int main (int argc, char **argv) { mp_ptr ap, bp, rp, refp; gmp_randstate_ptr rands; int test; TMP_DECL; TMP_MARK; tests_start (); rands = RANDS; ap = TMP_ALLOC_LIMBS (MAX_N); bp = TMP_ALLOC_LIMBS (MAX_N); rp = TMP_ALLOC_LIMBS (MAX_N + 2); refp = TMP_ALLOC_LIMBS (MAX_N + 2); for (test = 0; test < COUNT; test++) { mp_size_t an, bn, rn; unsigned size_log; size_log = 1 + gmp_urandomm_ui (rands, SIZE_LOG); an = 1 + gmp_urandomm_ui(rands, 1L << size_log); size_log = 1 + gmp_urandomm_ui (rands, SIZE_LOG); bn = 1 + gmp_urandomm_ui(rands, 1L << size_log); /* Make sure an >= bn */ if (an < bn) MP_SIZE_T_SWAP (an, bn); mpn_random2 (ap, an); mpn_random2 (bp, bn); refmpn_mulmid (refp, ap, an, bp, bn); mpn_mulmid (rp, ap, an, bp, bn); rn = an + 3 - bn; if (mpn_cmp (refp, rp, rn)) { printf ("ERROR in test %d, an = %d, bn = %d, rn = %d\n", test, (int) an, (int) bn, (int) rn); printf("a: "); mpn_dump (ap, an); printf("b: "); mpn_dump (bp, bn); printf("r: "); mpn_dump (rp, rn); printf("ref: "); mpn_dump (refp, rn); abort(); } } TMP_FREE; tests_end (); return 0; }
mp_limb_t mpn_dc_divappr_q_n (mp_ptr qp, mp_ptr np, mp_srcptr dp, mp_size_t n, mp_limb_t dip, mp_limb_t d1ip, mp_ptr tp) { mp_limb_t qh, cy; mp_ptr q_hi; mp_size_t m; mp_limb_t ret = 0; ASSERT (n >= 6); /* if the top n limbs of np are >= dp, high limb of quotient is 1 */ if (mpn_cmp(np + n, dp, n) >= 0) { ret = 1; mpn_sub_n(np + n, np + n, dp, n); } /* top n limbs of np are now < dp */ m = (n + 1) / 2; q_hi = qp + n - m; /* FIXME: we could probably avoid this copy if we could guarantee that sb_div_appr_q/dc_divappr_q_n did not destroy the "bottom half" of N */ MPN_COPY (tp, np, 2*n); /* estimate high m+1 limbs of quotient, using a 2*m by m division the quotient may be computed 1 too large as it is approximate, moreover, even computed precisely it may be two too large due to the truncation we've done to a 2*m by m division... */ if (m < DC_DIVAPPR_Q_N_THRESHOLD) qh = mpn_sb_divappr_q (q_hi, tp + 2*n - 2*m, 2*m, dp + n - m, m, dip, d1ip); else qh = mpn_dc_divappr_q_n (q_hi, tp + 2*n - 2*m, dp + n - m, m, dip, d1ip, tp + 2*n); /* we therefore decrease the estimate by 3... */ qh -= mpn_sub_1 (q_hi, q_hi, m, (mp_limb_t) 3); /* ensuring it doesn't become negative */ if (qh & GMP_NUMB_HIGHBIT) { MPN_ZERO (q_hi, m); qh = 0; } /* note qh is now always zero as the quotient we have is definitely correct or up to two too small, and we already normalised np */ ASSERT (qh == 0); /* we know that {np+n-m, n+m} = q_hi * D + e0, where 0 <= e0 < C*B^n, where C is a small positive constant. Estimate q_hi * D using middle product, developing one additional limb, i.e. develop n - m + 3 limbs. The bottom limb is meaningless and the next limb may be too small by up to some small multiple of n, but recall n << B. */ mpn_mulmid (tp, dp, n, q_hi + 1, m - 2); /* do some parts of the middle product "manually": */ tp[n - m + 2] += mpn_addmul_1 (tp, dp + m - 2, n - m + 2, q_hi[0]); mpn_addmul_1 (tp + 1, dp, n - m + 2, q_hi[m-1]); /* subtract that estimate from N. We note the limb at np + n - 2 is then meaningless, and the next limb mght be too large by a small amount, i.e. the bottom n limbs of np are now possibly too large by a quantity much less than dp */ mpn_sub_n (np + n - 2, np + n - 2, tp, n - m + 3); /* recursively divide to obtain low half of quotient, developing one more limb than we would need if everything had been exact. As this extra limb is out by only a small amount, rounding the remaining limbs based on its value and discarding the extra limb results in a quotient which is at most 1 too large */ if (n - m + 2 < DC_DIVAPPR_Q_N_THRESHOLD) cy = mpn_sb_divappr_q (tp, np + m - 3, 2*n - 2*m + 4, dp + m - 2, n - m + 2, dip, d1ip); else cy = mpn_dc_divappr_q_n (tp, np + m - 3, dp + m - 2, n - m + 2, dip, d1ip, tp + n - m + 2); /* FIXME: The only reason this copy happens is that we elected to develop one extra quotient limb in the second recursive quotient. */ MPN_COPY (qp, tp + 1, n - m); /* Construct final quotient from low and hi parts... */ ret += mpn_add_1 (qp + n - m, qp + n - m, m, tp[n-m+1]); ret += mpn_add_1 (qp + n - m + 1, qp + n - m + 1, m - 1, cy); if (tp[0] >= GMP_NUMB_HIGHBIT) ret += mpn_add_1 (qp, qp, n, 1); /* ...rounding quotient up */ /* As the final quotient may be 1 too large, we may have ret == 2 (it is very unlikely, but can be relatively easily triggered at random when dp = 0x80000...0000), then Q must be 2000.... and we should return instead 1ffff.... */ if (ret == 2) { ret -= mpn_sub_1 (qp, qp, n, 1); ASSERT (ret == 1); } return ret; }
mp_limb_t mpn_dc_divappr_q (mp_ptr qp, mp_ptr np, mp_size_t nn, mp_srcptr dp, mp_size_t dn, mp_limb_t dinv) { mp_size_t q_orig, qn, sh, sl, i; mp_limb_t qh, cy, cy2; mp_ptr tp; TMP_DECL; ASSERT (dn >= 6); ASSERT (nn >= dn + 3); ASSERT (dp[dn-1] & GMP_NUMB_HIGHBIT); qn = nn - dn; if (qn + 1 < dn) { dp += dn - (qn + 1); dn = qn + 1; } q_orig = qn; qh = mpn_cmp(np + nn - dn, dp, dn) >= 0; if (qh != 0) mpn_sub_n(np + nn - dn, np + nn - dn, dp, dn); np += nn - dn - qn; nn = dn + qn; /* Reduce until dn - 1 >= qn */ while (dn - 1 < qn) { sh = MIN(dn, qn - dn + 1); if (sh <= DC_DIV_QR_THRESHOLD) cy2 = mpn_sb_div_qr(qp + qn - sh, np + nn - dn - sh, dn + sh, dp, dn, dinv); else cy2 = mpn_dc_div_qr(qp + qn - sh, np + nn - dn - sh, dn + sh, dp, dn, dinv); qn -= sh; nn -= sh; } cy = np[nn - 1]; /* split into two parts */ sh = qn/2; sl = qn - sh; /* Rare case where truncation ruins normalisation */ if (cy > dp[dn - 1] || (cy == dp[dn - 1] && mpn_cmp(np + nn - qn, dp + dn - qn, qn - 1) >= 0)) { __divappr_helper(qp, np + nn - qn - 2, dp + dn - qn - 1, qn); return qh; } if (mpn_cmp(np + sl + dn - 1, dp + dn - sh - 1, sh + 1) >= 0) __divappr_helper(qp + sl, np + dn + sl - 2, dp + dn - sh - 1, sh); else { if (sh < SB_DIVAPPR_Q_CUTOFF) mpn_sb_divappr_q(qp + sl, np + sl, dn + sh, dp, dn, dinv); else mpn_dc_divappr_q(qp + sl, np + sl, dn + sh, dp, dn, dinv); } cy = np[nn - sh]; TMP_MARK; tp = TMP_ALLOC_LIMBS(sl + 2); mpn_mulmid(tp, dp + dn - qn - 1, qn - 1, qp + sl, sh); cy -= mpn_sub_n(np + nn - qn - 2, np + nn - qn - 2, tp, sl + 2); TMP_FREE; while ((mp_limb_signed_t) cy < 0) { qh -= mpn_sub_1(qp + sl, qp + sl, q_orig - sl, 1); /* ensure quotient is not too big */ /* correct remainder, noting that "digits" of quotient aren't base B but in base varying with truncation, thus correction needs fixup */ cy += mpn_add_n(np + nn - qn - 2, np + nn - qn - 2, dp + dn - sl - 2, sl + 2); for (i = 0; i < sh - 1 && qp[sl + i] == ~CNST_LIMB(0); i++) cy += mpn_add_1(np + nn - qn - 2, np + nn - qn - 2, sl + 2, dp[dn - sl - 3 - i]); } if (cy != 0) /* special case: unable to canonicalise */ __divappr_helper(qp, np + nn - qn - 2, dp + dn - sl - 1, sl); else { if (mpn_cmp(np + dn - 1, dp + dn - sl - 1, sl + 1) >= 0) __divappr_helper(qp, np + dn - 2, dp + dn - sl - 1, sl); else { if (sl < SB_DIVAPPR_Q_CUTOFF) mpn_sb_divappr_q(qp, np, dn + sl, dp, dn, dinv); else mpn_dc_divappr_q(qp, np, dn + sl, dp, dn, dinv); } } return qh; }