sexp sexp_bignum_normalize (sexp a) { sexp_uint_t *data; if ((! sexp_bignump(a)) || (sexp_bignum_hi(a)>1)) return a; data = sexp_bignum_data(a); if ((data[0] > SEXP_MAX_FIXNUM) && ! ((sexp_bignum_sign(a) == -1) && (data[0] == SEXP_MAX_FIXNUM+1))) return a; return sexp_make_fixnum((sexp_sint_t)data[0] * sexp_bignum_sign(a)); }
sexp sexp_double_to_ratio (sexp ctx, double f) { int sign, i; sexp_gc_var3(res, whole, scale); if (f == trunc(f)) return sexp_bignum_normalize(sexp_double_to_bignum(ctx, f)); sexp_gc_preserve3(ctx, res, whole, scale); whole = sexp_double_to_bignum(ctx, trunc(f)); res = sexp_fixnum_to_bignum(ctx, SEXP_ZERO); scale = SEXP_ONE; sign = (f < 0 ? -1 : 1); for (i=0, f=fabs(f-trunc(f)); f != trunc(f) && i < 15; i++) { res = sexp_bignum_fxmul(ctx, NULL, res, 10, 0); f = f * 10; res = sexp_bignum_fxadd(ctx, res, double_10s_digit(f)); f = f - trunc(f); scale = sexp_mul(ctx, scale, SEXP_TEN); } sexp_bignum_sign(res) = sign; res = sexp_bignum_normalize(res); scale = sexp_bignum_normalize(scale); res = sexp_make_ratio(ctx, res, scale); res = sexp_ratio_normalize(ctx, res, SEXP_FALSE); res = sexp_add(ctx, res, whole); sexp_gc_release3(ctx); return res; }
sexp sexp_make_integer (sexp ctx, sexp_lsint_t x) { sexp res; if ((SEXP_MIN_FIXNUM <= x) && (x <= SEXP_MAX_FIXNUM)) { res = sexp_make_fixnum(x); } else { res = sexp_make_bignum(ctx, 1); if (x < 0) { sexp_bignum_sign(res) = -1; sexp_bignum_data(res)[0] = -x; } else { sexp_bignum_sign(res) = 1; sexp_bignum_data(res)[0] = x; } } return res; }
sexp sexp_make_bignum (sexp ctx, sexp_uint_t len) { sexp_uint_t size = sexp_sizeof(bignum) + len*sizeof(sexp_uint_t); sexp res = sexp_alloc_tagged(ctx, size, SEXP_BIGNUM); sexp_bignum_length(res) = len; sexp_bignum_sign(res) = 1; return res; }
sexp sexp_bignum_mul (sexp ctx, sexp dst, sexp a, sexp b) { sexp_uint_t alen=sexp_bignum_hi(a), blen=sexp_bignum_hi(b), i, *bdata=sexp_bignum_data(b); sexp_gc_var2(c, d); if (alen < blen) return sexp_bignum_mul(ctx, dst, b, a); sexp_gc_preserve2(ctx, c, d); c = (dst ? dst : sexp_make_bignum(ctx, alen+blen+1)); d = sexp_make_bignum(ctx, alen+blen+1); for (i=0; i<blen; i++) { d = sexp_bignum_fxmul(ctx, d, a, bdata[i], i); c = sexp_bignum_add_digits(ctx, NULL, c, d); sexp_bignum_data(d)[i] = 0; } sexp_bignum_sign(c) = sexp_bignum_sign(a) * sexp_bignum_sign(b); sexp_gc_release2(ctx); return c; }
double sexp_bignum_to_double (sexp a) { double res = 0; sexp_sint_t i; sexp_uint_t *data=sexp_bignum_data(a); for (i=sexp_bignum_hi(a)-1; i>=0; i--) res = res * ((double)SEXP_UINT_T_MAX+1) + data[i]; return res * sexp_bignum_sign(a); }
sexp sexp_make_unsigned_integer (sexp ctx, sexp_luint_t x) { sexp res; if (x <= SEXP_MAX_FIXNUM) { res = sexp_make_fixnum(x); } else { res = sexp_make_bignum(ctx, 1); sexp_bignum_sign(res) = 1; sexp_bignum_data(res)[0] = x; } return res; }
sexp sexp_bignum_add_fixnum (sexp ctx, sexp a, sexp b) { sexp_gc_var1(c); sexp_gc_preserve1(ctx, c); c = sexp_copy_bignum(ctx, NULL, a, 0); if (sexp_bignum_sign(c) == sexp_fx_sign(b)) c = sexp_bignum_fxadd(ctx, c, sexp_unbox_fixnum(sexp_fx_abs(b))); else c = sexp_bignum_fxsub(ctx, c, sexp_unbox_fixnum(sexp_fx_abs(b))); sexp_gc_release1(ctx); return c; }
sexp sexp_write_bignum (sexp ctx, sexp a, sexp out, sexp_uint_t base) { int i, str_len, lg_base = log2i(base); char *data; sexp_gc_var2(b, str); sexp_gc_preserve2(ctx, b, str); b = sexp_copy_bignum(ctx, NULL, a, 0); sexp_bignum_sign(b) = 1; i = str_len = (sexp_bignum_length(b)*sizeof(sexp_uint_t)*8 + lg_base - 1) / lg_base + 1; str = sexp_make_string(ctx, sexp_make_fixnum(str_len), sexp_make_character(' ')); data = sexp_string_data(str); while (! sexp_bignum_zerop(b)) data[--i] = hex_digit(sexp_bignum_fxdiv(ctx, b, base, 0)); if (i == str_len) data[--i] = '0'; else if (sexp_bignum_sign(a) == -1) data[--i] = '-'; sexp_write_string(ctx, data + i, out); sexp_gc_release2(ctx); return SEXP_VOID; }
static sexp sexp_random_source_state_set (sexp ctx, sexp self, sexp_sint_t n, sexp rs, sexp state) { if (! sexp_random_source_p(rs)) return sexp_type_exception(ctx, self, rs_type_id, rs); else if (sexp_fixnump(state)) *sexp_random_data(rs) = sexp_unbox_fixnum(state); #if SEXP_USE_BIGNUMS else if (sexp_bignump(state)) *sexp_random_data(rs) = sexp_bignum_data(state)[0]*sexp_bignum_sign(state); #endif else return sexp_type_exception(ctx, self, SEXP_FIXNUM, state); return SEXP_VOID; }
sexp sexp_bignum_sub (sexp ctx, sexp dst, sexp a, sexp b) { sexp res; if (sexp_bignum_sign(a) == sexp_bignum_sign(b)) { res = sexp_bignum_sub_digits(ctx, dst, a, b); sexp_bignum_sign(res) = (sexp_bignum_compare_abs(a, b) >= 0 ? sexp_bignum_sign(a) : -sexp_bignum_sign(a)); } else { res = sexp_bignum_add_digits(ctx, dst, a, b); sexp_bignum_sign(res) = sexp_bignum_sign(a); } return res; }
sexp sexp_double_to_bignum (sexp ctx, double f) { int sign; sexp_gc_var3(res, scale, tmp); sexp_gc_preserve3(ctx, res, scale, tmp); res = sexp_fixnum_to_bignum(ctx, SEXP_ZERO); scale = sexp_fixnum_to_bignum(ctx, SEXP_ONE); sign = (f < 0 ? -1 : 1); for (f=fabs(f); f >= 1.0; f=trunc(f/10)) { tmp = sexp_bignum_fxmul(ctx, NULL, scale, double_10s_digit(f), 0); res = sexp_bignum_add(ctx, res, res, tmp); scale = sexp_bignum_fxmul(ctx, NULL, scale, 10, 0); } sexp_bignum_sign(res) = sign; sexp_gc_release3(ctx); return res; }
sexp sexp_read_bignum (sexp ctx, sexp in, sexp_uint_t init, signed char sign, sexp_uint_t base) { int c, digit; sexp_gc_var1(res); sexp_gc_preserve1(ctx, res); res = sexp_make_bignum(ctx, SEXP_INIT_BIGNUM_SIZE); sexp_bignum_sign(res) = sign; sexp_bignum_data(res)[0] = init; for (c=sexp_read_char(ctx, in); sexp_isxdigit(c); c=sexp_read_char(ctx, in)) { digit = digit_value(c); if ((digit < 0) || (digit >= base)) break; res = sexp_bignum_fxmul(ctx, res, res, base, 0); res = sexp_bignum_fxadd(ctx, res, digit); } if (c=='.' || c=='e' || c=='E') { if (base != 10) { res = sexp_read_error(ctx, "found non-base 10 float", SEXP_NULL, in); } else { if (c!='.') sexp_push_char(ctx, c, in); /* push the e back */ res = sexp_read_float_tail(ctx, in, sexp_bignum_to_double(res), (sign==-1)); } #if SEXP_USE_RATIOS } else if (c=='/') { res = sexp_bignum_normalize(res); res = sexp_make_ratio(ctx, res, SEXP_ONE); sexp_ratio_denominator(res) = sexp_read_number(ctx, in, 10); res = sexp_ratio_normalize(ctx, res, in); #endif #if SEXP_USE_COMPLEX } else if (c=='i' || c=='i' || c=='+' || c=='-') { sexp_push_char(ctx, c, in); res = sexp_bignum_normalize(res); res = sexp_read_complex_tail(ctx, in, res); #endif } else if ((c!=EOF) && ! sexp_is_separator(c)) { res = sexp_read_error(ctx, "invalid numeric syntax", sexp_make_character(c), in); } else { sexp_push_char(ctx, c, in); } sexp_gc_release1(ctx); return sexp_bignum_normalize(res); }
sexp sexp_bignum_quot_rem (sexp ctx, sexp *rem, sexp a, sexp b) { sexp res; sexp_gc_var4(k, i, a1, b1); sexp_gc_preserve4(ctx, k, i, a1, b1); a1 = sexp_copy_bignum(ctx, NULL, a, 0); sexp_bignum_sign(a1) = 1; b1 = sexp_copy_bignum(ctx, NULL, b, 0); sexp_bignum_sign(b1) = 1; k = sexp_copy_bignum(ctx, NULL, b1, 0); i = sexp_fixnum_to_bignum(ctx, SEXP_ONE); res = quot_step(ctx, rem, a1, b1, k, i); sexp_bignum_sign(res) = sexp_bignum_sign(a) * sexp_bignum_sign(b); if (sexp_bignum_sign(a) < 0) { sexp_negate_exact(*rem); } sexp_gc_release4(ctx); return res; }
sexp sexp_fixnum_to_bignum (sexp ctx, sexp a) { sexp res = sexp_make_bignum(ctx, 1); sexp_bignum_data(res)[0] = sexp_unbox_fixnum(sexp_fx_abs(a)); sexp_bignum_sign(res) = sexp_fx_sign(a); return res; }
sexp_sint_t sexp_bignum_compare (sexp a, sexp b) { if (sexp_bignum_sign(a) != sexp_bignum_sign(b)) return sexp_bignum_sign(a); return sexp_bignum_compare_abs(a, b); }