~ chicken-core (chicken-5) e4dee9a9364cd2c86852508b1968fd1b702c3f0d
commit e4dee9a9364cd2c86852508b1968fd1b702c3f0d Author: Peter Bex <peter@more-magic.net> AuthorDate: Mon Apr 6 15:39:07 2015 +0200 Commit: Peter Bex <peter@more-magic.net> CommitDate: Sun May 31 14:55:25 2015 +0200 Clean up rat_times_{rat,integer}, integer_gcd and C_a_i_exact_to_inexact considerably. This is possible now that generic integer division is inlineable. diff --git a/runtime.c b/runtime.c index d8c92f42..bd322469 100644 --- a/runtime.c +++ b/runtime.c @@ -7379,8 +7379,7 @@ void C_ccall values_continuation(C_word c, C_word closure, C_word arg0, ...) static C_word rat_times_integer(C_word **ptr, C_word rat, C_word i) { - C_word ab[C_SIZEOF_FIX_BIGNUM*5+C_SIZEOF_BIGNUM_WRAPPER*4], *a = ab, - num, denom, d_big, gcd, gcd_big, i_big, a_div_g; + C_word ab[C_SIZEOF_FIX_BIGNUM * 2], *a = ab, num, denom, gcd, a_div_g; switch (i) { case C_fix(0): return C_fix(0); @@ -7399,24 +7398,20 @@ static C_word rat_times_integer(C_word **ptr, C_word rat, C_word i) /* With g = gcd(a, d) and a = x [Knuth, 4.5.1] */ gcd = C_s_a_u_i_integer_gcd(&a, 2, i, denom); - /* Ensure bignums to simplify logic */ - gcd_big = (gcd & C_FIXNUM_BIT) ? C_a_u_i_fix_to_big(&a, gcd) : gcd; - i_big = (i & C_FIXNUM_BIT) ? C_a_u_i_fix_to_big(&a, i) : i; - d_big = (denom & C_FIXNUM_BIT) ? C_a_u_i_fix_to_big(&a, denom) : denom; - /* Calculate a/g (= i/gcd), which will later be multiplied by y */ - bignum_divrem(&a, i_big, gcd_big, &a_div_g, NULL); + a_div_g = C_s_a_u_i_integer_quotient(&a, 2, i, gcd); if (a_div_g == C_fix(0)) { clear_buffer_object(ab, gcd); return C_fix(0); /* Save some work */ } /* Final numerator = a/g * c (= a_div_g * num) */ - num = C_s_a_u_i_integer_times(&a, 2, a_div_g, num); - num = move_buffer_object(ptr, ab, num); + num = C_s_a_u_i_integer_times(ptr, 2, a_div_g, num); - /* Calculate d/g (= denom/gcd). We already know |d| >= |g| */ - bignum_divrem(&a, d_big, gcd_big, &denom, NULL); + /* Final denominator = d/g (= denom/gcd) */ + denom = C_s_a_u_i_integer_quotient(ptr, 2, denom, gcd); + + num = move_buffer_object(ptr, ab, num); denom = move_buffer_object(ptr, ab, denom); clear_buffer_object(ab, gcd); @@ -7426,13 +7421,11 @@ static C_word rat_times_integer(C_word **ptr, C_word rat, C_word i) else return C_ratnum(ptr, num, denom); } -/* This is truly wretched */ static C_word rat_times_rat(C_word **ptr, C_word x, C_word y) { - C_word ab[C_SIZEOF_FIX_BIGNUM*10 + C_SIZEOF_BIGNUM_WRAPPER*8], *a = ab, + C_word ab[C_SIZEOF_FIX_BIGNUM * 6], *a = ab, num, denom, xnum, xdenom, ynum, ydenom, - xnum_big, xdenom_big, ynum_big, ydenom_big, - g1, g1_big, g2, g2_big, a_div_g1, b_div_g2, c_div_g2, d_div_g1; + g1, g2, a_div_g1, b_div_g2, c_div_g2, d_div_g1; xnum = C_block_item(x, 1); xdenom = C_block_item(x, 2); @@ -7445,34 +7438,27 @@ static C_word rat_times_rat(C_word **ptr, C_word x, C_word y) g1 = C_s_a_u_i_integer_gcd(&a, 2, xnum, ydenom); g2 = C_s_a_u_i_integer_gcd(&a, 2, ynum, xdenom); - /* Ensure bignums to simplify logic */ - g1_big = (g1 & C_FIXNUM_BIT) ? C_a_u_i_fix_to_big(&a, g1) : g1; - g2_big = (g2 & C_FIXNUM_BIT) ? C_a_u_i_fix_to_big(&a, g2) : g2; - xnum_big = (xnum & C_FIXNUM_BIT) ? C_a_u_i_fix_to_big(&a, xnum) : xnum; - xdenom_big = (xdenom & C_FIXNUM_BIT) ? C_a_u_i_fix_to_big(&a, xdenom) : xdenom; - ynum_big = (ynum & C_FIXNUM_BIT) ? C_a_u_i_fix_to_big(&a, ynum) : ynum; - ydenom_big = (ydenom & C_FIXNUM_BIT) ? C_a_u_i_fix_to_big(&a, ydenom) : ydenom; - /* Calculate a/g1 (= xnum/g1), which will later be multiplied by c/g2 */ - bignum_divrem(&a, xnum_big, g1_big, &a_div_g1, NULL); + a_div_g1 = C_s_a_u_i_integer_quotient(&a, 2, xnum, g1); /* Calculate c/g2 (= ynum/g2), which will later be multiplied by a/g1 */ - bignum_divrem(&a, ynum_big, g2_big, &c_div_g2, NULL); + c_div_g2 = C_s_a_u_i_integer_quotient(&a, 2, ynum, g2); /* Final numerator = a/g1 * c/g2 */ - num = C_s_a_u_i_integer_times(&a, 2, a_div_g1, c_div_g2); - num = move_buffer_object(ptr, ab, num); + num = C_s_a_u_i_integer_times(ptr, 2, a_div_g1, c_div_g2); /* Now, do the same for the denominator.... */ /* Calculate b/g2 (= xdenom/g2), which will later be multiplied by d/g1 */ - bignum_divrem(&a, xdenom_big, g2_big, &b_div_g2, NULL); + b_div_g2 = C_s_a_u_i_integer_quotient(&a, 2, xdenom, g2); /* Calculate d/g1 (= ydenom/g1), which will later be multiplied by b/g2 */ - bignum_divrem(&a, ydenom_big, g1_big, &d_div_g1, NULL); + d_div_g1 = C_s_a_u_i_integer_quotient(&a, 2, ydenom, g1); /* Final denominator = b/g2 * d/g1 */ - denom = C_s_a_u_i_integer_times(&a, 2, b_div_g2, d_div_g1); + denom = C_s_a_u_i_integer_times(ptr, 2, b_div_g2, d_div_g1); + + num = move_buffer_object(ptr, ab, num); denom = move_buffer_object(ptr, ab, denom); clear_buffer_object(ab, g1); @@ -7869,7 +7855,8 @@ static C_word bignum_plus_unsigned(C_word **ptr, C_word x, C_word y, C_word negp static C_word rat_plusmin_integer(C_word **ptr, C_word rat, C_word i, integer_plusmin_op plusmin_op) { - C_word ab[C_SIZEOF_FIX_BIGNUM*2], *a = ab, num, denom, tmp, res; + C_word ab[C_SIZEOF_FIX_BIGNUM+C_SIZEOF_BIGNUM(2)], *a = ab, + num, denom, tmp, res; if (i == C_fix(0)) return rat; @@ -7887,7 +7874,8 @@ static C_word rat_plusmin_integer(C_word **ptr, C_word rat, C_word i, integer_pl /* This is needed only for minus: plus is commutative but minus isn't. */ static C_word integer_minus_rat(C_word **ptr, C_word i, C_word rat) { - C_word ab[C_SIZEOF_FIX_BIGNUM*2], *a = ab, num, denom, tmp, res; + C_word ab[C_SIZEOF_FIX_BIGNUM+C_SIZEOF_BIGNUM(2)], *a = ab, + num, denom, tmp, res; num = C_block_item(rat, 1); denom = C_block_item(rat, 2); @@ -7903,46 +7891,40 @@ static C_word integer_minus_rat(C_word **ptr, C_word i, C_word rat) return C_ratnum(ptr, res, C_block_item(rat, 2)); } -/* This is completely braindead and ugly */ +/* This is pretty braindead and ugly */ static C_word rat_plusmin_rat(C_word **ptr, C_word x, C_word y, integer_plusmin_op plusmin_op) { - C_word ab[C_SIZEOF_FIX_BIGNUM*11 + C_SIZEOF_BIGNUM_WRAPPER*8], *a = ab, + C_word ab[C_SIZEOF_FIX_BIGNUM*6 + C_SIZEOF_BIGNUM(2)*2], *a = ab, xnum = C_block_item(x, 1), ynum = C_block_item(y, 1), xdenom = C_block_item(x, 2), ydenom = C_block_item(y, 2), xnorm, ynorm, tmp_r, g1, ydenom_g1, xdenom_g1, norm_sum, g2, len, - res_num, res_tmp_denom, res_denom; + res_num, res_denom; /* Knuth, 4.5.1. Start with g1 = gcd(xdenom, ydenom) */ g1 = C_s_a_u_i_integer_gcd(&a, 2, xdenom, ydenom); - if (g1 & C_FIXNUM_BIT) g1 = C_a_u_i_fix_to_big(&a, g1); - - if (xdenom & C_FIXNUM_BIT) xdenom = C_a_u_i_fix_to_big(&a, xdenom); - if (ydenom & C_FIXNUM_BIT) ydenom = C_a_u_i_fix_to_big(&a, ydenom); /* xnorm = xnum * (ydenom/g1) */ - bignum_divrem(&a, ydenom, g1, &ydenom_g1, NULL); + ydenom_g1 = C_s_a_u_i_integer_quotient(&a, 2, ydenom, g1); xnorm = C_s_a_u_i_integer_times(&a, 2, xnum, ydenom_g1); /* ynorm = ynum * (xdenom/g1) */ - bignum_divrem(&a, xdenom, g1, &xdenom_g1, NULL); + xdenom_g1 = C_s_a_u_i_integer_quotient(&a, 2, xdenom, g1); ynorm = C_s_a_u_i_integer_times(&a, 2, ynum, xdenom_g1); /* norm_sum = xnorm [+-] ynorm */ norm_sum = plusmin_op(&a, 2, xnorm, ynorm); /* g2 = gcd(norm_sum, g1) */ - g2 = C_s_a_u_i_integer_gcd(&a, 2, norm_sum, C_bignum_simplify(g1)); - if (g2 & C_FIXNUM_BIT) g2 = C_a_u_i_fix_to_big(&a, g2); - if (norm_sum & C_FIXNUM_BIT) norm_sum = C_a_u_i_fix_to_big(&a, norm_sum); + g2 = C_s_a_u_i_integer_gcd(&a, 2, norm_sum, g1); /* res_num = norm_sum / g2 */ - bignum_divrem(&a, norm_sum, g2, &res_num, NULL); + res_num = C_s_a_u_i_integer_quotient(ptr, 2, norm_sum, g2); if (res_num == C_fix(0)) { res_denom = C_fix(0); /* No need to calculate denom: we'll return 0 */ } else { /* res_denom = xdenom_g1 * (ydenom / g2) */ - bignum_divrem(&a, ydenom, g2, &res_tmp_denom, NULL); - res_denom = C_s_a_u_i_integer_times(&a, 2, xdenom_g1, res_tmp_denom); + C_word res_tmp_denom = C_s_a_u_i_integer_quotient(&a, 2, ydenom, g2); + res_denom = C_s_a_u_i_integer_times(ptr, 2, xdenom_g1, res_tmp_denom); /* Ensure they're allocated in the correct place */ res_num = move_buffer_object(ptr, ab, res_num); @@ -8784,7 +8766,7 @@ C_s_a_u_i_integer_remainder(C_word **ptr, C_word n, C_word x, C_word y) { C_word ab[C_SIZEOF_FIX_BIGNUM*2], *a = ab, r; if (y == C_fix(0)) C_div_by_zero_error("remainder"); - integer_divrem(ptr, x, y, NULL, &r); + integer_divrem(&a, x, y, NULL, &r); return move_buffer_object(ptr, ab, r); } @@ -8800,7 +8782,7 @@ C_s_a_u_i_integer_quotient(C_word **ptr, C_word n, C_word x, C_word y) { C_word ab[C_SIZEOF_FIX_BIGNUM*2], *a = ab, q; if (y == C_fix(0)) C_div_by_zero_error("quotient"); - integer_divrem(ptr, x, y, &q, NULL); + integer_divrem(&a, x, y, &q, NULL); return move_buffer_object(ptr, ab, q); } @@ -9851,13 +9833,6 @@ C_regparm C_word C_fcall C_bignum_simplify(C_word big) scan--; length = scan - start + 1; - /* Always trim the bignum, even before returning a fixnum. This - * benefits some code that uses bignums everywhere for simplicity. - * NOTE: This only works for stack-allocated or tmp bignums, not - * for scratchspace bignums! - */ - if (scan < last_digit && length > 0) /* Keep a minimum of 1 word */ - C_bignum_mutate_size(big, length); switch(length) { case 0: if (C_in_scratchspacep(C_internal_bignum_vector(big))) @@ -9874,6 +9849,7 @@ C_regparm C_word C_fcall C_bignum_simplify(C_word big) } /* FALLTHROUGH */ default: + if (scan < last_digit) C_bignum_mutate_size(big, length); return big; } } @@ -10217,7 +10193,7 @@ C_a_i_exact_to_inexact(C_word **ptr, int c, C_word n) * e_w in M2. TODO: What if b!=2 (ie, flonum-radix isn't 2)? */ e = integer_length_abs(num) - integer_length_abs(denom), - ab[C_SIZEOF_FIX_BIGNUM*6], *a = ab, tmp, q, r, len, + ab[C_SIZEOF_FIX_BIGNUM*5+C_SIZEOF_FLONUM], *a = ab, tmp, q, r, len, shift_amount, negp = C_i_integer_negativep(num); C_uword *d; double res, fraction; @@ -10241,51 +10217,27 @@ C_a_i_exact_to_inexact(C_word **ptr, int c, C_word n) clear_buffer_object(ab, num); /* "knows" shift creates fresh numbers */ num = tmp; - /* Ensure num and denom are bignums, for simplicity */ - if (num & C_FIXNUM_BIT) num = C_a_u_i_fix_to_big(&a, num); - if (denom & C_FIXNUM_BIT) denom = C_a_u_i_fix_to_big(&a, denom); - /* Now, calculate round(num/denom). We start with a quotient&remainder */ - switch(bignum_cmp_unsigned(num, denom)) { - case 0: /* q = 1, r = 0 */ - q = allocate_tmp_bignum(C_fix(1), C_SCHEME_FALSE, C_SCHEME_FALSE); - *(C_bignum_digits(q)) = 1; - r = allocate_tmp_bignum(C_fix(0), C_SCHEME_FALSE, C_SCHEME_FALSE); - break; - - case -1: /* q = 0, r = num */ - q = allocate_tmp_bignum(C_fix(0), C_SCHEME_FALSE, C_SCHEME_FALSE); - len = C_bignum_size(num) + 1; /* Ensure we can shift left by one */ - r = allocate_tmp_bignum(C_fix(len), C_SCHEME_FALSE, C_SCHEME_FALSE); - bignum_digits_destructive_copy(r, num); - d = C_bignum_digits(r); - d[len-1] = 0; /* Initialize most significant digit */ - bignum_digits_destructive_shift_left(d, d + len, 1); - break; - - case 1: - default: - len = C_bignum_size(num) + 1 - C_bignum_size(denom); - q = allocate_tmp_bignum(C_fix(len), C_SCHEME_FALSE, C_SCHEME_FALSE); - len = C_bignum_size(num) + 1; /* LEN */ - r = allocate_tmp_bignum(C_fix(len), C_SCHEME_FALSE, C_SCHEME_FALSE); - bignum_destructive_divide_full(num, denom, q, r, C_SCHEME_TRUE); - d = C_bignum_digits(r); - /* There should always be room to shift left by 1 because of LEN */ - assert(C_ilen(d[len-1]) < C_BIGNUM_DIGIT_LENGTH); - bignum_digits_destructive_shift_left(d, d + len, 1); - break; + integer_divrem(&a, num, denom, &q, &r); + + /* We multiply the remainder by two to simulate adding 1/2 for + * round. However, we don't do it if num = denom (q=1,r=0) */ + if (!((q == C_fix(1) || q == C_fix(-1)) && r == C_fix(0))) { + tmp = C_s_a_i_arithmetic_shift(&a, 2, r, C_fix(1)); + clear_buffer_object(ab, r); /* "knows" shift creates fresh numbers */ + r = tmp; } /* Now q is the quotient, but to "round" result we need to * adjust. This follows the semantics of the "round" procedure: - * Round away from zero on positive numbers (this is never - * negative). In case of exactly halfway, we round up if odd. + * Round away from zero on positive numbers (ignoring sign). In + * case of exactly halfway, we round up if odd. */ - fraction = C_bignum_to_double(q); - switch (basic_cmp(C_bignum_simplify(r), C_bignum_simplify(denom), "", 0)) { + tmp = C_a_i_exact_to_inexact(&a, 1, q); + fraction = fabs(C_flonum_magnitude(tmp)); + switch (basic_cmp(r, denom, "", 0)) { case C_fix(0): - if (*(C_bignum_digits(q)) & 1) fraction += 1.0; + if (C_truep(C_i_oddp(q))) fraction += 1.0; break; case C_fix(1): fraction += 1.0; @@ -10293,10 +10245,10 @@ C_a_i_exact_to_inexact(C_word **ptr, int c, C_word n) default: /* if r <= denom, we're done */ break; } - free_tmp_bignum(q); - free_tmp_bignum(r); clear_buffer_object(ab, num); clear_buffer_object(ab, denom); + clear_buffer_object(ab, q); + clear_buffer_object(ab, r); shift_amount = nmin(DBL_MANT_DIG-1, e - (DBL_MIN_EXP - DBL_MANT_DIG)); res = ldexp(fraction, e - shift_amount); @@ -10373,7 +10325,8 @@ C_inline void lehmer_gcd(C_word **ptr, C_word u, C_word v, C_word *x, C_word *y) { int i_even = 1, done = 0; C_word shift_amount = integer_length_abs(u) - (C_WORD_SIZE - 2), - uhat, vhat, qhat, ab[C_SIZEOF_FIX_BIGNUM*6], *a = ab, xnext, ynext, + ab[C_SIZEOF_BIGNUM(2)*2+C_SIZEOF_FIX_BIGNUM*2], *a = ab, + uhat, vhat, qhat, xnext, ynext, xprev = 1, yprev = 0, xcurr = 0, ycurr = 1; uhat = C_s_a_i_arithmetic_shift(&a, 2, u, C_fix(-shift_amount)); @@ -10428,94 +10381,53 @@ C_inline void lehmer_gcd(C_word **ptr, C_word u, C_word v, C_word *x, C_word *y) C_regparm C_word C_fcall C_s_a_u_i_integer_gcd(C_word **ptr, C_word n, C_word x, C_word y) { - C_word ab[2][C_SIZEOF_FIX_BIGNUM*4], *a, res, size, i = 0; + C_word ab[2][C_SIZEOF_BIGNUM(2) * 2], *a, newx, newy, size, i = 0; + + if (x & C_FIXNUM_BIT && y & C_FIXNUM_BIT) return C_i_fixnum_gcd(x, y); - /* Ensure loop invariant: abs(x) >= abs(y) */ - if (x & C_FIXNUM_BIT) { - if (y & C_FIXNUM_BIT) { - return C_i_fixnum_gcd(x, y); - } else { /* x is fixnum, y is bignum: swap */ - C_word tmp = y; - y = x; - x = tmp; - } - } else if (!(y & C_FIXNUM_BIT)) { /* Both are bignums: compare */ - switch (bignum_cmp_unsigned(x, y)) { - case -1: - { - C_word tmp = y; - y = x; - x = tmp; - break; - } - case 0: /* gcd(x, x) = abs(x); Try to reuse positive argument, if any */ - if (!C_bignum_negativep(x)) return x; - else return C_s_a_u_i_integer_abs(ptr, 1, y); - default: /* Do nothing: x > y */ - break; - } - } a = ab[i++]; x = C_s_a_u_i_integer_abs(&a, 1, x); y = C_s_a_u_i_integer_abs(&a, 1, y); + if (!C_truep(C_i_integer_greaterp(x, y))) { + newx = y; y = x; x = newx; /* Ensure loop invariant: abs(x) >= abs(y) */ + } + while(y != C_fix(0)) { assert(integer_length_abs(x) >= integer_length_abs(y)); /* x and y are stored in the same buffer, as well as a result */ a = ab[i++]; if (i == 2) i = 0; - if (x & C_FIXNUM_BIT) { - return C_i_fixnum_gcd(x, y); - } else if (y & C_FIXNUM_BIT) { - C_word absy = y & C_INT_SIGN_BIT ? -C_unfix(y) : C_unfix(y), - next_power = (C_uword)1 << (C_ilen(absy)-1); - - if (next_power == absy && C_fitsinfixnump(absy)) { - y = C_fix(*(C_bignum_digits(x)) & (next_power - 1)); - clear_buffer_object(ab[i], x); - x = C_fix(absy); - } else if (C_fitsinbignumhalfdigitp(absy)) { - y = C_fix(bignum_remainder_unsigned_halfdigit(x, absy)); - clear_buffer_object(ab[i], x); - x = C_fix(absy); - } else { - absy = C_a_u_i_fix_to_big(&a, y); - bignum_divrem(&a, x, absy, NULL, &res); - clear_buffer_object(ab[i], x); - x = y; - y = res; - } - } else { - C_word newx, newy; - - /* First, see if we should run a Lehmer step */ - if ((integer_length_abs(x) - integer_length_abs(y)) < C_HALF_WORD_SIZE) { - lehmer_gcd(&a, x, y, &newx, &newy); - clear_buffer_object(ab[i], x); - clear_buffer_object(ab[i], y); - x = newx; - y = newy; - if (x & C_FIXNUM_BIT) x = C_a_u_i_fix_to_big(&a, x); - if (y & C_FIXNUM_BIT) y = C_a_u_i_fix_to_big(&a, y); - a = ab[i++]; /* Ensure x and y get cleared correctly below */ - if (i == 2) i = 0; - } - - bignum_divrem(&a, x, y, NULL, &newy); + if (x & C_FIXNUM_BIT) return C_i_fixnum_gcd(x, y); + + /* First, see if we should run a Lehmer step */ + if ((integer_length_abs(x) - integer_length_abs(y)) < C_HALF_WORD_SIZE) { + lehmer_gcd(&a, x, y, &newx, &newy); + newx = move_buffer_object(&a, ab[i], newx); newy = move_buffer_object(&a, ab[i], newy); - newx = move_buffer_object(&a, ab[i], C_bignum_simplify(y)); clear_buffer_object(ab[i], x); + clear_buffer_object(ab[i], y); x = newx; y = newy; + a = ab[i++]; /* Ensure x and y get cleared correctly below */ + if (i == 2) i = 0; } + + newy = C_s_a_u_i_integer_remainder(&a, 2, x, y); + newy = move_buffer_object(&a, ab[i], newy); + newx = move_buffer_object(&a, ab[i], y); + clear_buffer_object(ab[i], x); + clear_buffer_object(ab[i], y); + x = newx; + y = newy; } - res = C_s_a_u_i_integer_abs(ptr, 1, x); - res = move_buffer_object(ptr, ab, res); + newx = C_s_a_u_i_integer_abs(ptr, 1, x); + newx = move_buffer_object(ptr, ab, newx); clear_buffer_object(ab, x); clear_buffer_object(ab, y); - return res; + return newx; }Trap