/* Check divide and conquer division routine. */ void check_dc_div_qr_n (void) { mp_limb_t np[2*MAX_LIMBS]; mp_limb_t np2[2*MAX_LIMBS]; mp_limb_t rp[2*MAX_LIMBS+1]; mp_limb_t dp[MAX_LIMBS]; mp_limb_t qp[2*MAX_LIMBS]; mp_limb_t tp[DC_DIVAPPR_Q_N_ITCH(MAX_LIMBS)]; mp_limb_t dip, cy; mp_size_t rn, dn, qn; gmp_randstate_t rands; int i, j, s; gmp_randinit_default(rands); for (i = 0; i < ITERS; i++) { dn = (random() % (MAX_LIMBS - 5)) + 6; mpn_rrandom (np, rands, 2*dn); mpn_rrandom (dp, rands, dn); dp[dn-1] |= GMP_LIMB_HIGHBIT; MPN_COPY(np2, np, 2*dn); invert_1(dip, dp[dn - 1], dp[dn - 2]); qn = dn + 1; qp[qn - 1] = mpn_dc_div_qr_n(qp, np, dp, dn, dip, tp); MPN_NORMALIZE(qp, qn); if (qn) { if (qn >= dn) mpn_mul(rp, qp, qn, dp, dn); else mpn_mul(rp, dp, dn, qp, qn); rn = dn + qn; MPN_NORMALIZE(rp, rn); if (rn > 2*dn) { printf("failed: q*d has too many limbs\n"); abort(); } if (mpn_cmp(rp, np2, 2*dn) > 0) { printf("failed: remainder negative\n"); abort(); } mpn_sub(rp, np2, 2*dn, rp, rn); rn = 2*dn; MPN_NORMALIZE(rp, rn); } else { rn = 2*dn; MPN_COPY(rp, np, 2*dn); } s = (rn < dn) ? -1 : (rn > dn) ? 1 : mpn_cmp(rp, dp, dn); if (s >= 0) { printf ("failed:\n"); printf ("dn = %lu, qn = %lu, rn = %lu\n\n", dn, qn, rn); gmp_printf (" np: %Nx\n\n", np2, 2*dn); gmp_printf (" dp: %Nx\n\n", dp, dn); gmp_printf (" qp: %Nx\n\n", qp, qn); gmp_printf (" rp: %Nx\n\n", rp, rn); abort (); } if (mpn_cmp(rp, np, rn) != 0) { printf("failed: remainder does not match\n"); gmp_printf (" np: %Nx\n\n", np2, 2*dn); gmp_printf (" dp: %Nx\n\n", dp, dn); gmp_printf (" qp: %Nx\n\n", qp, qn); gmp_printf (" rp: %Nx\n\n", rp, rn); gmp_printf (" rp2: %Nx\n\n", np, rn); } } gmp_randclear(rands); }
mp_limb_t mpn_dc_div_qr (mp_ptr qp, mp_ptr np, mp_size_t nn, mp_srcptr dp, mp_size_t dn, mp_limb_t dinv) { mp_size_t qn; mp_limb_t qh, cy; mp_ptr tp; TMP_DECL; TMP_MARK; ASSERT (dn >= 6); /* to adhere to mpn_sb_div_qr's limits */ ASSERT (nn - dn >= 3); /* to adhere to mpn_sb_div_qr's limits */ ASSERT (dp[dn-1] & GMP_NUMB_HIGHBIT); tp = TMP_ALLOC_LIMBS (DC_DIVAPPR_Q_N_ITCH(dn)); qn = nn - dn; qp += qn; np += nn; dp += dn; if (qn > dn) { /* Reduce qn mod dn without division, optimizing small operations. */ do qn -= dn; while (qn > dn); qp -= qn; /* point at low limb of next quotient block */ np -= qn; /* point in the middle of partial remainder */ /* Perform the typically smaller block first. */ if (qn == 1) { mp_limb_t q, n2, n1, n0, d1, d0, d11, d01; /* Handle qh up front, for simplicity. */ qh = mpn_cmp (np - dn + 1, dp - dn, dn) >= 0; if (qh) ASSERT_NOCARRY (mpn_sub_n (np - dn + 1, np - dn + 1, dp - dn, dn)); /* A single iteration of schoolbook: One 3/2 division, followed by the bignum update and adjustment. */ n2 = np[0]; n1 = np[-1]; n0 = np[-2]; d1 = dp[-1]; d0 = dp[-2]; d01 = d0 + 1; d11 = d1 + (d01 < d0); ASSERT (n2 < d1 || (n2 == d1 && n1 <= d0)); if (UNLIKELY (n2 == d1) && n1 == d0) { q = GMP_NUMB_MASK; cy = mpn_submul_1 (np - dn, dp - dn, dn, q); ASSERT (cy == n2); } else { mpir_divrem32_preinv2 (q, n1, n0, n2, n1, n0, d11, d01, d1, d0, dinv); if (dn > 2) { mp_limb_t cy, cy1; cy = mpn_submul_1 (np - dn, dp - dn, dn - 2, q); cy1 = n0 < cy; n0 = (n0 - cy) & GMP_NUMB_MASK; cy = n1 < cy1; n1 = (n1 - cy1) & GMP_NUMB_MASK; np[-2] = n0; if (UNLIKELY (cy != 0)) { n1 += d1 + mpn_add_n (np - dn, np - dn, dp - dn, dn - 1); qh -= (q == 0); q = (q - 1) & GMP_NUMB_MASK; } } else np[-2] = n0; np[-1] = n1; } qp[0] = q; } else { /* Do a 2qn / qn division */ if (qn == 2) qh = mpn_divrem_2 (qp, 0L, np - 2, 4, dp - 2); /* FIXME: obsolete function. Use 5/3 division? */ else if (BELOW_THRESHOLD (qn, DC_DIV_QR_THRESHOLD)) qh = mpn_sb_div_qr (qp, np - qn, 2 * qn, dp - qn, qn, dinv); else qh = mpn_dc_div_qr_n (qp, np - qn, dp - qn, qn, dinv, tp); if (qn != dn) { if (qn > dn - qn) mpn_mul (tp, qp, qn, dp - dn, dn - qn); else mpn_mul (tp, dp - dn, dn - qn, qp, qn); cy = mpn_sub_n (np - dn, np - dn, tp, dn); if (qh != 0) cy += mpn_sub_n (np - dn + qn, np - dn + qn, dp - dn, dn - qn); while (cy != 0) { qh -= mpn_sub_1 (qp, qp, qn, 1); cy -= mpn_add_n (np - dn, np - dn, dp - dn, dn); } } } qn = nn - dn - qn; do { qp -= dn; np -= dn; ASSERT_NOCARRY(mpn_dc_div_qr_n (qp, np - dn, dp - dn, dn, dinv, tp)); qn -= dn; } while (qn > 0); } else { qp -= qn; /* point at low limb of next quotient block */ np -= qn; /* point in the middle of partial remainder */ if (BELOW_THRESHOLD (qn, DC_DIV_QR_THRESHOLD)) qh = mpn_sb_div_qr (qp, np - qn, 2 * qn, dp - qn, qn, dinv); else qh = mpn_dc_div_qr_n (qp, np - qn, dp - qn, qn, dinv, tp); if (qn != dn) { if (qn > dn - qn) mpn_mul (tp, qp, qn, dp - dn, dn - qn); else mpn_mul (tp, dp - dn, dn - qn, qp, qn); cy = mpn_sub_n (np - dn, np - dn, tp, dn); if (qh != 0) cy += mpn_sub_n (np - dn + qn, np - dn + qn, dp - dn, dn - qn); while (cy != 0) { qh -= mpn_sub_1 (qp, qp, qn, 1); cy -= mpn_add_n (np - dn, np - dn, dp - dn, dn); } } } TMP_FREE; return qh; }
/* Check divide and conquer division routine. */ void check_dc_divappr_q_n (void) { mp_limb_t tp[DC_DIVAPPR_Q_N_ITCH(MAX_LIMBS)]; mp_limb_t np[2*MAX_LIMBS]; mp_limb_t np2[2*MAX_LIMBS]; mp_limb_t rp[2*MAX_LIMBS]; mp_limb_t dp[MAX_LIMBS]; mp_limb_t qp[MAX_LIMBS]; mp_limb_t dip; mp_size_t nn, rn, dn, qn; gmp_randstate_t rands; int i, j, s; gmp_randinit_default(rands); for (i = 0; i < ITERS; i++) { dn = (random() % (MAX_LIMBS - 6)) + 6; nn = 2*dn; mpn_rrandom (np, rands, nn); mpn_rrandom (dp, rands, dn); dp[dn-1] |= GMP_LIMB_HIGHBIT; MPN_COPY(np2, np, nn); invert_1(dip, dp[dn - 1], dp[dn - 2]); qn = nn - dn + 1; qp[qn - 1] = mpn_dc_divappr_q_n(qp, np, dp, dn, dip, tp); MPN_NORMALIZE(qp, qn); if (qn) { if (qn >= dn) mpn_mul(rp, qp, qn, dp, dn); else mpn_mul(rp, dp, dn, qp, qn); rn = dn + qn; MPN_NORMALIZE(rp, rn); s = (rn < nn) ? -1 : (rn > nn) ? 1 : mpn_cmp(rp, np2, nn); if (s <= 0) { mpn_sub(rp, np2, nn, rp, rn); rn = nn; MPN_NORMALIZE(rp, rn); } else { mpn_sub(rp, rp, rn, np2, nn); MPN_NORMALIZE(rp, rn); } } else { rn = nn; MPN_COPY(rp, np, nn); } s = (rn < dn) ? -1 : (rn > dn) ? 1 : mpn_cmp(rp, dp, dn); if (s >= 0) { printf ("failed:\n"); printf ("nn = %lu, dn = %lu, qn = %lu, rn = %lu\n\n", nn, dn, qn, rn); gmp_printf (" np: %Nx\n\n", np2, nn); gmp_printf (" dp: %Nx\n\n", dp, dn); gmp_printf (" qp: %Nx\n\n", qp, qn); gmp_printf (" rp: %Nx\n\n", rp, rn); abort (); } } gmp_randclear(rands); }