Skip to content

Commit bddb431

Browse files
committed
update division alghorithm
1 parent 6d5caf3 commit bddb431

File tree

1 file changed

+84
-23
lines changed

1 file changed

+84
-23
lines changed

library/core/src/num/bignum.rs

Lines changed: 84 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -336,41 +336,102 @@ macro_rules! define_bignum {
336336
(self, borrow)
337337
}
338338

339-
/// Divide self by another bignum, overwriting `q` with the quotient and `r` with the
340-
/// remainder.
339+
/// Knuth’s Algorithm D implementation
341340
pub fn div_rem(&self, d: &$name, q: &mut $name, r: &mut $name) {
342-
// Stupid slow base-2 long division taken from
343-
// https://en.wikipedia.org/wiki/Division_algorithm
344-
// FIXME use a greater base ($ty) for the long division.
345341
assert!(!d.is_zero());
346342
let digitbits = <$ty>::BITS as usize;
343+
347344
for digit in &mut q.base[..] {
348345
*digit = 0;
349346
}
350347
for digit in &mut r.base[..] {
351348
*digit = 0;
352349
}
353-
r.size = d.size;
354-
q.size = 1;
355-
let mut q_is_zero = true;
356-
let end = self.bit_length();
357-
for i in (0..end).rev() {
358-
r.mul_pow2(1);
359-
r.base[0] |= self.get_bit(i) as $ty;
360-
if &*r >= d {
361-
r.sub(d);
362-
// Set bit `i` of q to 1.
363-
let digit_idx = i / digitbits;
364-
let bit_idx = i % digitbits;
365-
if q_is_zero {
366-
q.size = digit_idx + 1;
367-
q_is_zero = false;
350+
351+
if d.size == 1 {
352+
let mut tmp = self.clone();
353+
let (_q, rem) = tmp.div_rem_small(d.base[0]);
354+
*q = tmp;
355+
r.base[0] = rem;
356+
r.size = 1;
357+
return;
358+
}
359+
360+
if self.is_zero() {
361+
q.size = 1;
362+
r.size = 1;
363+
return;
364+
}
365+
366+
q.size = self.size;
367+
r.size = 1;
368+
r.base[0] = 0;
369+
370+
for i in (0..self.size).rev() {
371+
r.mul_pow2(digitbits);
372+
373+
r.base[0] = self.base[i];
374+
375+
while r.size > 1 && r.base[r.size - 1] == 0 {
376+
r.size -= 1;
377+
}
378+
379+
if &*r < d {
380+
q.base[i] = 0;
381+
continue;
382+
}
383+
384+
let r_top = r.base[r.size - 1] as u128;
385+
let r_next = if r.size >= 2 { r.base[r.size - 2] as u128 } else { 0u128 };
386+
let v = (r_top << digitbits) | r_next;
387+
let u = d.base[d.size - 1] as u128;
388+
389+
// initial estimate
390+
let mut qhat = (v / u) as $ty;
391+
let base_limit = if digitbits == 64 {
392+
// avoid (1u128<<64) overflow: handle separately
393+
u128::MAX as u128
394+
} else {
395+
(1u128 << digitbits) - 1
396+
};
397+
if (qhat as u128) > base_limit {
398+
qhat = base_limit as $ty;
399+
}
400+
401+
let mut prod = d.clone();
402+
if qhat != 0 {
403+
prod.mul_small(qhat);
404+
} else {
405+
prod.size = 1;
406+
prod.base[0] = 0;
407+
}
408+
409+
while &prod > &*r {
410+
qhat = qhat - 1;
411+
// we know prod >= d because qhat was at least 1 when prod > r >= d
412+
prod.sub(d);
413+
}
414+
415+
// Set quotient digit
416+
q.base[i] = qhat;
417+
418+
if qhat != 0 {
419+
r.sub(&prod);
420+
while r.size > 1 && r.base[r.size - 1] == 0 {
421+
r.size -= 1;
368422
}
369-
q.base[digit_idx] |= 1 << bit_idx;
370423
}
371424
}
372-
debug_assert!(q.base[q.size..].iter().all(|&d| d == 0));
373-
debug_assert!(r.base[r.size..].iter().all(|&d| d == 0));
425+
426+
let mut qsz = q.size;
427+
while qsz > 1 && q.base[qsz - 1] == 0 {
428+
qsz -= 1;
429+
}
430+
q.size = qsz;
431+
432+
if r.size == 0 {
433+
r.size = 1;
434+
}
374435
}
375436
}
376437

0 commit comments

Comments
 (0)