1use core::{
4 fmt::{Display, Formatter},
5 mem,
6 ops::{Div, DivAssign, Rem, RemAssign},
7};
8use dashu_base::{DivRem, DivRemAssign};
9use num_modular::{DivExact, DivExactAssign, PreMulInv2by1, PreMulInv3by2};
10
11use crate::{
12 arch::word::{DoubleWord, Word},
13 buffer::Buffer,
14 div,
15 error::panic_divide_by_0,
16 helper_macros::debug_assert_zero,
17 math::{shl_dword, FastDivideNormalized2},
18 memory::MemoryAllocation,
19 primitive::{double_word, extend_word, shrink_dword},
20 repr::Repr,
21 repr::TypedRepr,
22 shift,
23 ubig::UBig,
24 IBig,
25};
26use alloc::boxed::Box;
27
28#[derive(Debug, PartialEq, Eq)]
29pub(crate) struct ConstSingleDivisor(pub(crate) PreMulInv2by1<Word>);
30
31#[derive(Debug, PartialEq, Eq)]
32pub(crate) struct ConstDoubleDivisor(pub(crate) PreMulInv3by2<Word, DoubleWord>);
33
34#[derive(Debug, PartialEq, Eq)]
35pub(crate) struct ConstLargeDivisor {
36 pub(crate) normalized_divisor: Box<[Word]>,
37 pub(crate) shift: u32,
38 pub(crate) fast_div_top: FastDivideNormalized2,
39}
40
41impl ConstSingleDivisor {
42 #[inline]
44 pub const fn new(n: Word) -> Self {
45 debug_assert!(n != 0);
46 Self(PreMulInv2by1::<Word>::new(n))
47 }
48
49 #[inline]
51 pub const fn divisor(&self) -> Word {
52 self.0.divisor() >> self.0.shift()
53 }
54
55 #[inline]
56 pub const fn normalized_divisor(&self) -> Word {
57 self.0.divisor()
58 }
59 pub const fn shift(&self) -> u32 {
60 self.0.shift()
61 }
62
63 #[inline]
65 pub const fn rem_word(&self, word: Word) -> Word {
66 if self.0.shift() == 0 {
67 self.0.divider().div_rem_1by1(word).1
68 } else {
69 self.0
70 .divider()
71 .div_rem_2by1(extend_word(word) << self.0.shift())
72 .1
73 }
74 }
75
76 #[inline]
78 pub const fn rem_dword(&self, dword: DoubleWord) -> Word {
79 if self.0.shift() == 0 {
80 self.0.divider().div_rem_2by1(dword).1
81 } else {
82 let (n0, n1, n2) = shl_dword(dword, self.0.shift());
83 let (_, r1) = self.0.divider().div_rem_2by1(double_word(n1, n2));
84 self.0.divider().div_rem_2by1(double_word(n0, r1)).1
85 }
86 }
87
88 pub fn rem_large(&self, words: &[Word]) -> Word {
90 let mut rem = div::fast_rem_by_normalized_word(words, *self.0.divider());
91 if self.0.shift() != 0 {
92 rem = self
93 .0
94 .divider()
95 .div_rem_2by1(extend_word(rem) << self.0.shift())
96 .1
97 }
98 rem
99 }
100}
101
102impl ConstDoubleDivisor {
103 #[inline]
105 pub const fn new(n: DoubleWord) -> Self {
106 debug_assert!(n > Word::MAX as DoubleWord);
107 Self(PreMulInv3by2::<Word, DoubleWord>::new(n))
108 }
109
110 #[inline]
112 pub const fn divisor(&self) -> DoubleWord {
113 self.0.divisor() >> self.0.shift()
114 }
115
116 #[inline]
117 pub const fn normalized_divisor(&self) -> DoubleWord {
118 self.0.divisor()
119 }
120 pub const fn shift(&self) -> u32 {
121 self.0.shift()
122 }
123
124 #[inline]
126 pub const fn rem_dword(&self, dword: DoubleWord) -> DoubleWord {
127 if self.0.shift() == 0 {
128 self.0.divider().div_rem_2by2(dword).1
129 } else {
130 let (n0, n1, n2) = shl_dword(dword, self.0.shift());
131 self.0.divider().div_rem_3by2(n0, double_word(n1, n2)).1
132 }
133 }
134
135 pub fn rem_large(&self, words: &[Word]) -> DoubleWord {
137 let mut rem = div::fast_rem_by_normalized_dword(words, *self.0.divider());
138 if self.0.shift() != 0 {
139 let (r0, r1, r2) = shl_dword(rem, self.0.shift());
140 rem = self.0.divider().div_rem_3by2(r0, double_word(r1, r2)).1
141 }
142 rem
143 }
144}
145
146impl ConstLargeDivisor {
147 pub fn new(mut n: Buffer) -> Self {
149 let (shift, fast_div_top) = crate::div::normalize(&mut n);
150 Self {
151 normalized_divisor: n.into_boxed_slice(),
152 shift,
153 fast_div_top,
154 }
155 }
156
157 pub fn divisor(&self) -> Buffer {
159 let mut buffer = Buffer::from(self.normalized_divisor.as_ref());
160 debug_assert_zero!(shift::shr_in_place(&mut buffer, self.shift));
161 buffer
162 }
163
164 #[inline]
166 pub fn rem_large(&self, mut words: Buffer) -> Buffer {
167 let carry = shift::shl_in_place(&mut words, self.shift);
169 words.push_resizing(carry);
170
171 let modulus = &self.normalized_divisor;
173 if words.len() >= modulus.len() {
174 let mut allocation =
175 MemoryAllocation::new(div::memory_requirement_exact(words.len(), modulus.len()));
176 let _overflow = div::div_rem_in_place(
177 &mut words,
178 modulus,
179 self.fast_div_top,
180 &mut allocation.memory(),
181 );
182 words.truncate(modulus.len());
183 }
184 words
185 }
186
187 #[inline]
189 pub fn rem_repr(&self, x: TypedRepr) -> Buffer {
190 match x {
191 TypedRepr::Small(dword) => {
192 let (lo, mid, hi) = shl_dword(dword, self.shift);
193 let mut buffer = Buffer::allocate_exact(self.normalized_divisor.len());
194 buffer.push(lo);
195 buffer.push(mid);
196 buffer.push(hi);
197
198 buffer
201 }
202 TypedRepr::Large(words) => self.rem_large(words),
203 }
204 }
205}
206
207#[derive(Debug, PartialEq, Eq)]
208pub(crate) enum ConstDivisorRepr {
209 Single(ConstSingleDivisor),
210 Double(ConstDoubleDivisor),
211 Large(ConstLargeDivisor),
212}
213
214#[derive(Debug, PartialEq, Eq)]
216pub struct ConstDivisor(pub(crate) ConstDivisorRepr);
217
218impl ConstDivisor {
219 pub fn new(n: UBig) -> ConstDivisor {
225 Self(match n.into_repr() {
226 TypedRepr::Small(0) => panic_divide_by_0(),
227 TypedRepr::Small(dword) => {
228 if let Some(word) = shrink_dword(dword) {
229 ConstDivisorRepr::Single(ConstSingleDivisor::new(word))
230 } else {
231 ConstDivisorRepr::Double(ConstDoubleDivisor::new(dword))
232 }
233 }
234 TypedRepr::Large(words) => ConstDivisorRepr::Large(ConstLargeDivisor::new(words)),
235 })
236 }
237
238 #[inline]
244 pub const fn from_word(word: Word) -> Self {
245 if word == 0 {
246 panic_divide_by_0()
247 }
248 Self(ConstDivisorRepr::Single(ConstSingleDivisor::new(word)))
249 }
250
251 #[inline]
257 pub const fn from_dword(dword: DoubleWord) -> Self {
258 if dword == 0 {
259 panic_divide_by_0()
260 }
261
262 Self(if let Some(word) = shrink_dword(dword) {
263 ConstDivisorRepr::Single(ConstSingleDivisor::new(word))
264 } else {
265 ConstDivisorRepr::Double(ConstDoubleDivisor::new(dword))
266 })
267 }
268
269 #[inline]
271 pub fn value(&self) -> UBig {
272 UBig(match &self.0 {
273 ConstDivisorRepr::Single(d) => Repr::from_word(d.divisor()),
274 ConstDivisorRepr::Double(d) => Repr::from_dword(d.divisor()),
275 ConstDivisorRepr::Large(d) => Repr::from_buffer(d.divisor()),
276 })
277 }
278}
279
280impl Display for ConstDivisor {
281 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
282 Display::fmt(&self.value(), f)
283 }
284}
285
286impl Div<&ConstDivisor> for UBig {
287 type Output = UBig;
288
289 #[inline]
290 fn div(self, rhs: &ConstDivisor) -> UBig {
291 UBig(self.into_repr() / &rhs.0)
292 }
293}
294impl Div<&ConstDivisor> for &UBig {
295 type Output = UBig;
296
297 #[inline]
298 fn div(self, rhs: &ConstDivisor) -> UBig {
299 UBig(self.clone().into_repr() / &rhs.0)
300 }
301}
302impl DivAssign<&ConstDivisor> for UBig {
303 #[inline]
304 fn div_assign(&mut self, rhs: &ConstDivisor) {
305 *self = mem::take(self) / rhs;
306 }
307}
308
309impl Rem<&ConstDivisor> for UBig {
310 type Output = UBig;
311
312 #[inline]
313 fn rem(self, rhs: &ConstDivisor) -> UBig {
314 UBig(self.into_repr() % &rhs.0)
315 }
316}
317impl Rem<&ConstDivisor> for &UBig {
318 type Output = UBig;
319
320 #[inline]
321 fn rem(self, rhs: &ConstDivisor) -> UBig {
322 UBig(self.repr() % &rhs.0)
323 }
324}
325impl RemAssign<&ConstDivisor> for UBig {
326 #[inline]
327 fn rem_assign(&mut self, rhs: &ConstDivisor) {
328 *self = mem::take(self) % rhs;
329 }
330}
331
332impl DivRem<&ConstDivisor> for UBig {
333 type OutputDiv = UBig;
334 type OutputRem = UBig;
335
336 #[inline]
337 fn div_rem(self, rhs: &ConstDivisor) -> (UBig, UBig) {
338 let (q, r) = self.into_repr().div_rem(&rhs.0);
339 (UBig(q), UBig(r))
340 }
341}
342impl DivRem<&ConstDivisor> for &UBig {
343 type OutputDiv = UBig;
344 type OutputRem = UBig;
345
346 #[inline]
347 fn div_rem(self, rhs: &ConstDivisor) -> (UBig, UBig) {
348 let (q, r) = self.clone().into_repr().div_rem(&rhs.0);
349 (UBig(q), UBig(r))
350 }
351}
352impl DivRemAssign<&ConstDivisor> for UBig {
353 type OutputRem = UBig;
354 #[inline]
355 fn div_rem_assign(&mut self, rhs: &ConstDivisor) -> UBig {
356 let (q, r) = mem::take(self).div_rem(rhs);
357 *self = q;
358 r
359 }
360}
361
362impl DivExact<UBig, ConstDivisor> for UBig {
370 type Output = UBig;
371
372 #[inline]
373 fn div_exact(self, d: UBig, pre: &ConstDivisor) -> Option<UBig> {
374 debug_assert_eq!(pre.value(), d, "the divisor must match the precomputed divisor");
375 let (q, r) = self.div_rem(pre);
376 if r.is_zero() {
377 Some(q)
378 } else {
379 None
380 }
381 }
382}
383
384impl DivExact<UBig, ConstDivisor> for &UBig {
385 type Output = UBig;
386
387 #[inline]
388 fn div_exact(self, d: UBig, pre: &ConstDivisor) -> Option<UBig> {
389 debug_assert_eq!(pre.value(), d, "the divisor must match the precomputed divisor");
390 let (q, r) = self.div_rem(pre);
391 if r.is_zero() {
392 Some(q)
393 } else {
394 None
395 }
396 }
397}
398
399impl DivExactAssign<UBig, ConstDivisor> for UBig {
400 #[inline]
401 fn div_exact_assign(&mut self, d: UBig, pre: &ConstDivisor) -> bool {
402 debug_assert_eq!(pre.value(), d, "the divisor must match the precomputed divisor");
403 let (q, r) = (&*self).div_rem(pre);
404 if r.is_zero() {
405 *self = q;
406 true
407 } else {
408 false
409 }
410 }
411}
412
413impl Div<&ConstDivisor> for IBig {
414 type Output = IBig;
415
416 #[inline]
417 fn div(self, rhs: &ConstDivisor) -> IBig {
418 let (sign, repr) = self.into_sign_repr();
419 IBig((repr / &rhs.0).with_sign(sign))
420 }
421}
422impl Div<&ConstDivisor> for &IBig {
423 type Output = IBig;
424
425 #[inline]
426 fn div(self, rhs: &ConstDivisor) -> IBig {
427 let (sign, repr) = self.clone().into_sign_repr();
428 IBig((repr / &rhs.0).with_sign(sign))
429 }
430}
431impl DivAssign<&ConstDivisor> for IBig {
432 #[inline]
433 fn div_assign(&mut self, rhs: &ConstDivisor) {
434 *self = mem::take(self) / rhs;
435 }
436}
437
438impl Rem<&ConstDivisor> for IBig {
439 type Output = IBig;
440
441 #[inline]
442 fn rem(self, rhs: &ConstDivisor) -> IBig {
443 let (sign, repr) = self.into_sign_repr();
444 IBig((repr % &rhs.0).with_sign(sign))
445 }
446}
447impl Rem<&ConstDivisor> for &IBig {
448 type Output = IBig;
449
450 #[inline]
451 fn rem(self, rhs: &ConstDivisor) -> IBig {
452 let (sign, repr) = self.as_sign_repr();
453 IBig((repr % &rhs.0).with_sign(sign))
454 }
455}
456impl RemAssign<&ConstDivisor> for IBig {
457 #[inline]
458 fn rem_assign(&mut self, rhs: &ConstDivisor) {
459 *self = mem::take(self) % rhs;
460 }
461}
462
463impl DivRem<&ConstDivisor> for IBig {
464 type OutputDiv = IBig;
465 type OutputRem = IBig;
466
467 #[inline]
468 fn div_rem(self, rhs: &ConstDivisor) -> (IBig, IBig) {
469 let (sign, repr) = self.into_sign_repr();
470 let (q, r) = repr.div_rem(&rhs.0);
471 (IBig(q.with_sign(sign)), IBig(r.with_sign(sign)))
472 }
473}
474impl DivRem<&ConstDivisor> for &IBig {
475 type OutputDiv = IBig;
476 type OutputRem = IBig;
477
478 #[inline]
479 fn div_rem(self, rhs: &ConstDivisor) -> (IBig, IBig) {
480 let (sign, repr) = self.clone().into_sign_repr();
481 let (q, r) = repr.div_rem(&rhs.0);
482 (IBig(q.with_sign(sign)), IBig(r.with_sign(sign)))
483 }
484}
485impl DivRemAssign<&ConstDivisor> for IBig {
486 type OutputRem = IBig;
487 #[inline]
488 fn div_rem_assign(&mut self, rhs: &ConstDivisor) -> IBig {
489 let (q, r) = mem::take(self).div_rem(rhs);
490 *self = q;
491 r
492 }
493}
494
495mod repr {
496 use super::*;
497 use crate::repr::{
498 Repr,
499 TypedRepr::{self, *},
500 TypedReprRef::{self, *},
501 };
502
503 impl Div<&ConstDivisorRepr> for TypedRepr {
504 type Output = Repr;
505 fn div(self, rhs: &ConstDivisorRepr) -> Repr {
506 match (self, rhs) {
507 (Small(dword), ConstDivisorRepr::Single(div)) => {
508 Repr::from_dword(div_rem_small_single(dword, div).0)
509 }
510 (Small(dword), ConstDivisorRepr::Double(div)) => {
511 Repr::from_word(div_rem_small_double(dword, div).0)
512 }
513 (Small(_), ConstDivisorRepr::Large(_)) => {
514 Repr::zero()
516 }
517 (Large(mut buffer), ConstDivisorRepr::Single(div)) => {
518 let _rem = div::fast_div_by_word_in_place(
519 &mut buffer,
520 div.0.shift(),
521 *div.0.divider(),
522 );
523 Repr::from_buffer(buffer)
524 }
525 (Large(mut buffer), ConstDivisorRepr::Double(div)) => {
526 let _rem = div::fast_div_by_dword_in_place(
527 &mut buffer,
528 div.0.shift(),
529 *div.0.divider(),
530 );
531 Repr::from_buffer(buffer)
532 }
533 (Large(mut buffer), ConstDivisorRepr::Large(div)) => {
534 let div_len = div.normalized_divisor.len();
535 if buffer.len() < div_len {
536 Repr::zero()
537 } else {
538 let mut allocation = MemoryAllocation::new(div::memory_requirement_exact(
539 buffer.len(),
540 div_len,
541 ));
542 let q_top = div::div_rem_unshifted_in_place(
543 &mut buffer,
544 &div.normalized_divisor,
545 div.shift,
546 div.fast_div_top,
547 &mut allocation.memory(),
548 );
549 buffer.erase_front(div_len);
550 buffer.push_resizing(q_top);
551 Repr::from_buffer(buffer)
552 }
553 }
554 }
555 }
556 }
557
558 impl Rem<&ConstDivisorRepr> for TypedRepr {
559 type Output = Repr;
560
561 fn rem(self, rhs: &ConstDivisorRepr) -> Repr {
562 match (self, rhs) {
563 (Small(dword), ConstDivisorRepr::Single(div)) => {
564 Repr::from_word(div.rem_dword(dword) >> div.0.shift())
565 }
566 (Small(dword), ConstDivisorRepr::Double(div)) => {
567 Repr::from_dword(div.rem_dword(dword) >> div.0.shift())
568 }
569 (Small(dword), ConstDivisorRepr::Large(_)) => {
570 Repr::from_dword(dword)
572 }
573 (Large(buffer), ConstDivisorRepr::Single(div)) => {
574 Repr::from_word(div.rem_large(&buffer) >> div.0.shift())
575 }
576 (Large(buffer), ConstDivisorRepr::Double(div)) => {
577 Repr::from_dword(div.rem_large(&buffer) >> div.0.shift())
578 }
579 (Large(buffer), ConstDivisorRepr::Large(div)) => rem_large_large(buffer, div),
580 }
581 }
582 }
583
584 impl<'l, 'r> Rem<&'r ConstDivisorRepr> for TypedReprRef<'l> {
585 type Output = Repr;
586
587 fn rem(self, rhs: &ConstDivisorRepr) -> Repr {
588 match (self, rhs) {
589 (RefSmall(dword), ConstDivisorRepr::Single(div)) => {
590 Repr::from_word(div.rem_dword(dword) >> div.0.shift())
591 }
592 (RefSmall(dword), ConstDivisorRepr::Double(div)) => {
593 Repr::from_dword(div.rem_dword(dword) >> div.0.shift())
594 }
595 (RefSmall(dword), ConstDivisorRepr::Large(_)) => {
596 Repr::from_dword(dword)
598 }
599 (RefLarge(words), ConstDivisorRepr::Single(div)) => {
600 Repr::from_word(div.rem_large(words) >> div.0.shift())
601 }
602 (RefLarge(words), ConstDivisorRepr::Double(div)) => {
603 Repr::from_dword(div.rem_large(words) >> div.0.shift())
604 }
605 (RefLarge(words), ConstDivisorRepr::Large(div)) => {
606 rem_large_large(words.into(), div)
607 }
608 }
609 }
610 }
611
612 impl DivRem<&ConstDivisorRepr> for TypedRepr {
613 type OutputDiv = Repr;
614 type OutputRem = Repr;
615
616 fn div_rem(self, rhs: &ConstDivisorRepr) -> (Repr, Repr) {
617 match (self, rhs) {
618 (Small(dword), ConstDivisorRepr::Single(div)) => {
619 let (q, r) = div_rem_small_single(dword, div);
620 (Repr::from_dword(q), Repr::from_word(r))
621 }
622 (Small(dword), ConstDivisorRepr::Double(div)) => {
623 let (q, r) = div_rem_small_double(dword, div);
624 (Repr::from_word(q), Repr::from_dword(r))
625 }
626 (Small(dword), ConstDivisorRepr::Large(_)) => {
627 (Repr::zero(), Repr::from_dword(dword))
629 }
630 (Large(mut buffer), ConstDivisorRepr::Single(div)) => {
631 let r = div::fast_div_by_word_in_place(
632 &mut buffer,
633 div.0.shift(),
634 *div.0.divider(),
635 );
636 (Repr::from_buffer(buffer), Repr::from_word(r))
637 }
638 (Large(mut buffer), ConstDivisorRepr::Double(div)) => {
639 let r = div::fast_div_by_dword_in_place(
640 &mut buffer,
641 div.0.shift(),
642 *div.0.divider(),
643 );
644 (Repr::from_buffer(buffer), Repr::from_dword(r))
645 }
646 (Large(mut buffer), ConstDivisorRepr::Large(div)) => {
647 let div_len = div.normalized_divisor.len();
648 if buffer.len() < div_len {
649 (Repr::zero(), Repr::from_buffer(buffer))
650 } else {
651 let mut allocation = MemoryAllocation::new(div::memory_requirement_exact(
652 buffer.len(),
653 div_len,
654 ));
655 let q_top = div::div_rem_unshifted_in_place(
656 &mut buffer,
657 &div.normalized_divisor,
658 div.shift,
659 div.fast_div_top,
660 &mut allocation.memory(),
661 );
662
663 let mut q = Buffer::from(&buffer[div_len..]);
664 q.push_resizing(q_top);
665 buffer.truncate(div_len);
666 debug_assert_zero!(shift::shr_in_place(&mut buffer, div.shift));
667 (Repr::from_buffer(q), Repr::from_buffer(buffer))
668 }
669 }
670 }
671 }
672 }
673
674 fn div_rem_small_single(lhs: DoubleWord, rhs: &ConstSingleDivisor) -> (DoubleWord, Word) {
675 let (lo, mid, hi) = shl_dword(lhs, rhs.0.shift());
676 let (q1, r1) = rhs.0.divider().div_rem_2by1(double_word(mid, hi));
677 let (q0, r0) = rhs.0.divider().div_rem_2by1(double_word(lo, r1));
678 (double_word(q0, q1), r0 >> rhs.0.shift())
679 }
680
681 fn div_rem_small_double(lhs: DoubleWord, rhs: &ConstDoubleDivisor) -> (Word, DoubleWord) {
682 let (lo, mid, hi) = shl_dword(lhs, rhs.0.shift());
683 let (q, r) = rhs.0.divider().div_rem_3by2(lo, double_word(mid, hi));
684 (q, r >> rhs.0.shift())
685 }
686
687 fn rem_large_large(mut lhs: Buffer, rhs: &ConstLargeDivisor) -> Repr {
688 let modulus = &rhs.normalized_divisor;
689
690 if lhs.len() >= modulus.len() {
692 let mut allocation =
693 MemoryAllocation::new(div::memory_requirement_exact(lhs.len(), modulus.len()));
694 let _qtop = div::div_rem_unshifted_in_place(
695 &mut lhs,
696 modulus,
697 rhs.shift,
698 rhs.fast_div_top,
699 &mut allocation.memory(),
700 );
701
702 lhs.truncate(modulus.len());
703 debug_assert_zero!(shift::shr_in_place(&mut lhs, rhs.shift));
704 }
705 Repr::from_buffer(lhs)
706 }
707}
708
709#[cfg(test)]
710mod tests {
711 use super::*;
712 use num_modular::{DivExact, DivExactAssign};
713
714 #[test]
718 fn test_div_exact_with_const_divisor() {
719 for d in [
720 UBig::from(1001u32), (UBig::ONE << 64) + 3u8, UBig::from(10u8).pow(50), ] {
724 let pre = ConstDivisor::new(d.clone());
725 for i in 1..5usize {
726 let n = d.clone().pow(i) * 7u8;
727 let (q, r) = (&n).div_rem(&d);
728 assert!(r.is_zero(), "d={d:?} i={i}");
729 assert_eq!(n.clone().div_exact(d.clone(), &pre), Some(q.clone()), "d={d:?} i={i}");
730
731 let mut m = n;
733 assert!(m.div_exact_assign(d.clone(), &pre), "d={d:?} i={i}");
734 assert_eq!(m, q, "d={d:?} i={i}");
735 }
736
737 let n = d.clone().pow(2) + 1u8;
739 assert_eq!(n.clone().div_exact(d.clone(), &pre), None, "d={d:?}");
740 let mut m = n;
741 let before = m.clone();
742 assert!(!m.div_exact_assign(d.clone(), &pre), "d={d:?}");
743 assert_eq!(m, before, "d={d:?}");
744 }
745
746 let d = UBig::from(1001u32);
748 let pre = ConstDivisor::new(d.clone());
749 let a = UBig::from(7u8) * &d;
750 assert_eq!((&a).div_exact(d.clone(), &pre), Some(UBig::from(7u8)));
751 assert_eq!(a, UBig::from(7u8) * d); }
753}