/* * Knuth, TAOCP, Volume 2, 4.3.1: * w := product of u (len m) and v (len n) * w must be initialized to zero */ void _mpd_basemul(mpd_uint_t *w, const mpd_uint_t *u, const mpd_uint_t *v, mpd_size_t m, mpd_size_t n) { mpd_uint_t hi, lo; mpd_uint_t carry; mpd_size_t i, j; assert(m > 0 && n > 0); for (j=0; j < n; j++) { carry = 0; for (i=0; i < m; i++) { _mpd_mul_words(&hi, &lo, u[i], v[j]); lo = w[i+j] + lo; if (lo < w[i+j]) hi++; lo = carry + lo; if (lo < carry) hi++; _mpd_div_words_r(&carry, &w[i+j], hi, lo); } w[j+m] = carry; } }
/* w := product of u (len n) and v (single word) */ void _mpd_shortmul(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n, mpd_uint_t v) { mpd_uint_t hi, lo; mpd_uint_t carry = 0; mpd_size_t i; assert(n > 0); for (i=0; i < n; i++) { _mpd_mul_words(&hi, &lo, u[i], v); lo = carry + lo; if (lo < carry) hi++; _mpd_div_words_r(&carry, &w[i], hi, lo); } w[i] = carry; }
/* * Knuth, TAOCP Volume 2, 4.3.1, exercise 16: * w := quotient of u (len n) divided by a single word v */ mpd_uint_t _mpd_shortdiv(mpd_uint_t *w, const mpd_uint_t *u, mpd_size_t n, mpd_uint_t v) { mpd_uint_t hi, lo; mpd_uint_t rem = 0; mpd_size_t i; assert(n > 0); for (i=n-1; i != MPD_SIZE_MAX; i--) { _mpd_mul_words(&hi, &lo, rem, MPD_RADIX); lo = u[i] + lo; if (lo < u[i]) hi++; _mpd_div_words(&w[i], &rem, hi, lo, v); } return rem; }
/* * Knuth, TAOCP Volume 2, 4.3.1: * q, r := quotient and remainder of uconst (len nplusm) * divided by vconst (len n) * nplusm >= n * * If r is not NULL, r will contain the remainder. If r is NULL, the * return value indicates if there is a remainder: 1 for true, 0 for * false. A return value of -1 indicates an error. */ int _mpd_basedivmod(mpd_uint_t *q, mpd_uint_t *r, const mpd_uint_t *uconst, const mpd_uint_t *vconst, mpd_size_t nplusm, mpd_size_t n) { mpd_uint_t ustatic[MPD_MINALLOC_MAX]; mpd_uint_t vstatic[MPD_MINALLOC_MAX]; mpd_uint_t *u = ustatic; mpd_uint_t *v = vstatic; mpd_uint_t d, qhat, rhat, w2[2]; mpd_uint_t hi, lo, x; mpd_uint_t carry; mpd_size_t i, j, m; int retval = 0; assert(n > 1 && nplusm >= n); m = sub_size_t(nplusm, n); /* D1: normalize */ d = MPD_RADIX / (vconst[n-1] + 1); if (nplusm >= MPD_MINALLOC_MAX) { if ((u = mpd_alloc(nplusm+1, sizeof *u)) == NULL) { return -1; } } if (n >= MPD_MINALLOC_MAX) { if ((v = mpd_alloc(n+1, sizeof *v)) == NULL) { mpd_free(u); return -1; } } _mpd_shortmul(u, uconst, nplusm, d); _mpd_shortmul(v, vconst, n, d); /* D2: loop */ for (j=m; j != MPD_SIZE_MAX; j--) { /* D3: calculate qhat and rhat */ rhat = _mpd_shortdiv(w2, u+j+n-1, 2, v[n-1]); qhat = w2[1] * MPD_RADIX + w2[0]; while (1) { if (qhat < MPD_RADIX) { _mpd_singlemul(w2, qhat, v[n-2]); if (w2[1] <= rhat) { if (w2[1] != rhat || w2[0] <= u[j+n-2]) { break; } } } qhat -= 1; rhat += v[n-1]; if (rhat < v[n-1] || rhat >= MPD_RADIX) { break; } } /* D4: multiply and subtract */ carry = 0; for (i=0; i <= n; i++) { _mpd_mul_words(&hi, &lo, qhat, v[i]); lo = carry + lo; if (lo < carry) hi++; _mpd_div_words_r(&hi, &lo, hi, lo); x = u[i+j] - lo; carry = (u[i+j] < x); u[i+j] = carry ? x+MPD_RADIX : x; carry += hi; } q[j] = qhat; /* D5: test remainder */ if (carry) { q[j] -= 1; /* D6: add back */ (void)_mpd_baseadd(u+j, u+j, v, n+1, n); } } /* D8: unnormalize */ if (r != NULL) { _mpd_shortdiv(r, u, n, d); /* we are not interested in the return value here */ retval = 0; } else { retval = !_mpd_isallzero(u, n); } if (u != ustatic) mpd_free(u); if (v != vstatic) mpd_free(v); return retval; }