/bignum.c
C | 3671 lines | 2882 code | 423 blank | 366 comment | 673 complexity | f19243715966d2979a1b5e60b3efd5ed MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause, LGPL-2.1
Large files files are truncated, but you can click here to view the full file
- /*
- * This file is covered by the Ruby license. See COPYING for more details.
- *
- * Copyright (C) 2007-2011, Apple Inc. All rights reserved.
- * Copyright (C) 1993-2007 Yukihiro Matsumoto
- */
- #include "macruby_internal.h"
- #include "objc.h"
- #include "encoding.h"
- #include <math.h>
- #include <float.h>
- #include <ctype.h>
- #ifdef HAVE_IEEEFP_H
- #include <ieeefp.h>
- #endif
- #include <assert.h>
- VALUE rb_cBignum;
- #if defined __MINGW32__
- #define USHORT _USHORT
- #endif
- #define BDIGITS(x) (RBIGNUM_DIGITS(x))
- #define BITSPERDIG (SIZEOF_BDIGITS*CHAR_BIT)
- #define BIGRAD ((BDIGIT_DBL)1 << BITSPERDIG)
- #define DIGSPERLONG (SIZEOF_LONG/SIZEOF_BDIGITS)
- #if HAVE_LONG_LONG
- # define DIGSPERLL (SIZEOF_LONG_LONG/SIZEOF_BDIGITS)
- #endif
- #define BIGUP(x) ((BDIGIT_DBL)(x) << BITSPERDIG)
- #define BIGDN(x) RSHIFT(x,BITSPERDIG)
- #define BIGLO(x) ((BDIGIT)((x) & (BIGRAD-1)))
- #define BDIGMAX ((BDIGIT)-1)
- #define BIGZEROP(x) (RBIGNUM_LEN(x) == 0 || \
- (BDIGITS(x)[0] == 0 && \
- (RBIGNUM_LEN(x) == 1 || bigzero_p(x))))
- static int
- bigzero_p(VALUE x)
- {
- long i;
- BDIGIT *ds = BDIGITS(x);
- for (i = RBIGNUM_LEN(x) - 1; 0 <= i; i--) {
- if (ds[i]) return 0;
- }
- return 1;
- }
- int
- rb_bigzero_p(VALUE x)
- {
- return BIGZEROP(x);
- }
- int
- rb_cmpint(VALUE val, VALUE a, VALUE b)
- {
- if (NIL_P(val)) {
- rb_cmperr(a, b);
- }
- if (FIXNUM_P(val)) {
- long l = FIX2LONG(val);
- if (l > 0) return 1;
- if (l < 0) return -1;
- return 0;
- }
- if (TYPE(val) == T_BIGNUM) {
- if (BIGZEROP(val)) return 0;
- if (RBIGNUM_SIGN(val)) return 1;
- return -1;
- }
- if (RTEST(rb_funcall(val, '>', 1, INT2FIX(0)))) return 1;
- if (RTEST(rb_funcall(val, '<', 1, INT2FIX(0)))) return -1;
- return 0;
- }
- #define RBIGNUM_SET_LEN(b,l) \
- ((RBASIC(b)->flags & RBIGNUM_EMBED_FLAG) ? \
- (RBASIC(b)->flags = (RBASIC(b)->flags & ~RBIGNUM_EMBED_LEN_MASK) | \
- ((l) << RBIGNUM_EMBED_LEN_SHIFT)) : \
- (RBIGNUM(b)->as.heap.len = (l)))
- static void
- rb_big_realloc(VALUE big, long len)
- {
- BDIGIT *ds;
- if (RBASIC(big)->flags & RBIGNUM_EMBED_FLAG) {
- if (RBIGNUM_EMBED_LEN_MAX < len) {
- ds = ALLOC_N(BDIGIT, len);
- MEMCPY(ds, RBIGNUM(big)->as.ary, BDIGIT, RBIGNUM_EMBED_LEN_MAX);
- RBIGNUM(big)->as.heap.len = RBIGNUM_LEN(big);
- GC_WB(&RBIGNUM(big)->as.heap.digits, ds);
- RBASIC(big)->flags &= ~RBIGNUM_EMBED_FLAG;
- }
- }
- else {
- if (len <= RBIGNUM_EMBED_LEN_MAX) {
- ds = RBIGNUM(big)->as.heap.digits;
- RBASIC(big)->flags |= RBIGNUM_EMBED_FLAG;
- RBIGNUM_SET_LEN(big, len);
- if (ds) {
- MEMCPY(RBIGNUM(big)->as.ary, ds, BDIGIT, len);
- xfree(ds);
- }
- }
- else {
- if (RBIGNUM_LEN(big) == 0) {
- GC_WB(&RBIGNUM(big)->as.heap.digits, ALLOC_N(BDIGIT, len));
- }
- else {
- void *tmp = ruby_xrealloc(RBIGNUM(big)->as.heap.digits, sizeof(BDIGIT) * len);
- if (tmp != RBIGNUM(big)->as.heap.digits) {
- GC_WB(&RBIGNUM(big)->as.heap.digits, tmp);
- }
- }
- }
- }
- }
- void
- rb_big_resize(VALUE big, long len)
- {
- rb_big_realloc(big, len);
- RBIGNUM_SET_LEN(big, len);
- }
- static VALUE
- bignew_1(VALUE klass, long len, int sign)
- {
- NEWOBJ(big, struct RBignum);
- OBJSETUP(big, klass, T_BIGNUM);
- RBIGNUM_SET_SIGN(big, sign?1:0);
- if (len <= RBIGNUM_EMBED_LEN_MAX) {
- RBASIC(big)->flags |= RBIGNUM_EMBED_FLAG;
- RBIGNUM_SET_LEN(big, len);
- }
- else {
- rb_big_resize((VALUE)big, len);
- }
- return (VALUE)big;
- }
- #define bignew(len,sign) bignew_1(rb_cBignum,len,sign)
- VALUE
- rb_bignum_new_retained(const char *str)
- {
- VALUE v = rb_cstr2inum(str, 10);
- GC_RETAIN(v);
- return v;
- }
- VALUE
- rb_big_new(long len, int sign)
- {
- return bignew(len, sign != 0);
- }
- VALUE
- rb_big_clone(VALUE x)
- {
- long len = RBIGNUM_LEN(x);
- VALUE z = bignew_1(CLASS_OF(x), len, RBIGNUM_SIGN(x));
- MEMCPY(BDIGITS(z), BDIGITS(x), BDIGIT, len);
- return z;
- }
- /* modify a bignum by 2's complement */
- static void
- get2comp(VALUE x)
- {
- long i = RBIGNUM_LEN(x);
- BDIGIT *ds = BDIGITS(x);
- BDIGIT_DBL num;
- if (!i) return;
- while (i--) ds[i] = ~ds[i];
- i = 0; num = 1;
- do {
- num += ds[i];
- ds[i++] = BIGLO(num);
- num = BIGDN(num);
- } while (i < RBIGNUM_LEN(x));
- if (num != 0) {
- rb_big_resize(x, RBIGNUM_LEN(x)+1);
- ds = BDIGITS(x);
- ds[RBIGNUM_LEN(x)-1] = 1;
- }
- }
- void
- rb_big_2comp(VALUE x) /* get 2's complement */
- {
- get2comp(x);
- }
- static inline VALUE
- bigtrunc(VALUE x)
- {
- long len = RBIGNUM_LEN(x);
- BDIGIT *ds = BDIGITS(x);
- if (len == 0) return x;
- while (--len && !ds[len]);
- if (RBIGNUM_LEN(x) > len+1) {
- rb_big_resize(x, len+1);
- }
- return x;
- }
- static inline VALUE
- bigfixize(VALUE x)
- {
- long len = RBIGNUM_LEN(x);
- BDIGIT *ds = BDIGITS(x);
- if (len == 0) return INT2FIX(0);
- if ((size_t)(len*SIZEOF_BDIGITS) <= sizeof(long)) {
- long num = 0;
- #if 2*SIZEOF_BDIGITS > SIZEOF_LONG
- num = (long)ds[0];
- #else
- while (len--) {
- num = (long)(BIGUP(num) + ds[len]);
- }
- #endif
- if (num >= 0) {
- if (RBIGNUM_SIGN(x)) {
- if (POSFIXABLE(num)) return LONG2FIX(num);
- }
- else {
- if (NEGFIXABLE(-num)) return LONG2FIX(-num);
- }
- }
- }
- return x;
- }
- static VALUE
- bignorm(VALUE x)
- {
- if (!FIXNUM_P(x) && TYPE(x) == T_BIGNUM) {
- x = bigfixize(bigtrunc(x));
- }
- return x;
- }
- VALUE
- rb_big_norm(VALUE x)
- {
- return bignorm(x);
- }
- VALUE
- rb_uint2big(VALUE n)
- {
- BDIGIT_DBL num = n;
- long i = 0;
- BDIGIT *digits;
- VALUE big;
- big = bignew(DIGSPERLONG, 1);
- digits = BDIGITS(big);
- while (i < DIGSPERLONG) {
- digits[i++] = BIGLO(num);
- num = BIGDN(num);
- }
- i = DIGSPERLONG;
- while (--i && !digits[i]) ;
- RBIGNUM_SET_LEN(big, i+1);
- return big;
- }
- VALUE
- rb_int2big(SIGNED_VALUE n)
- {
- long neg = 0;
- VALUE big;
- if (n < 0) {
- n = -n;
- neg = 1;
- }
- big = rb_uint2big(n);
- if (neg) {
- RBIGNUM_SET_SIGN(big, 0);
- }
- return big;
- }
- #if SIZEOF_LONG % SIZEOF_BDIGITS != 0
- # error unexpected SIZEOF_LONG : SIZEOF_BDIGITS ratio
- #endif
- /*
- * buf is an array of long integers.
- * buf is ordered from least significant word to most significant word.
- * buf[0] is the least significant word and
- * buf[num_longs-1] is the most significant word.
- * This means words in buf is little endian.
- * However each word in buf is native endian.
- * (buf[i]&1) is the least significant bit and
- * (buf[i]&(1<<(SIZEOF_LONG*CHAR_BIT-1))) is the most significant bit
- * for each 0 <= i < num_longs.
- * So buf is little endian at whole on a little endian machine.
- * But buf is mixed endian on a big endian machine.
- */
- void
- rb_big_pack(VALUE val, unsigned long *buf, long num_longs)
- {
- val = rb_to_int(val);
- if (num_longs == 0)
- return;
- if (FIXNUM_P(val)) {
- long i;
- long tmp = FIX2LONG(val);
- buf[0] = (unsigned long)tmp;
- tmp = tmp < 0 ? ~0L : 0;
- for (i = 1; i < num_longs; i++)
- buf[i] = (unsigned long)tmp;
- return;
- }
- else {
- long len = RBIGNUM_LEN(val);
- BDIGIT *ds = BDIGITS(val), *dend = ds + len;
- long i, j;
- for (i = 0; i < num_longs && ds < dend; i++) {
- unsigned long l = 0;
- for (j = 0; j < DIGSPERLONG && ds < dend; j++, ds++) {
- l |= ((unsigned long)*ds << (j * BITSPERDIG));
- }
- buf[i] = l;
- }
- for (; i < num_longs; i++)
- buf[i] = 0;
- if (RBIGNUM_NEGATIVE_P(val)) {
- for (i = 0; i < num_longs; i++) {
- buf[i] = ~buf[i];
- }
- for (i = 0; i < num_longs; i++) {
- buf[i]++;
- if (buf[i] != 0)
- return;
- }
- }
- }
- }
- /* See rb_big_pack comment for endianness of buf. */
- VALUE
- rb_big_unpack(unsigned long *buf, long num_longs)
- {
- while (2 <= num_longs) {
- if (buf[num_longs-1] == 0 && (long)buf[num_longs-2] >= 0)
- num_longs--;
- else if (buf[num_longs-1] == ~0UL && (long)buf[num_longs-2] < 0)
- num_longs--;
- else
- break;
- }
- if (num_longs == 0)
- return INT2FIX(0);
- else if (num_longs == 1)
- return LONG2NUM((long)buf[0]);
- else {
- VALUE big;
- BDIGIT *ds;
- long len = num_longs * DIGSPERLONG;
- long i;
- big = bignew(len, 1);
- ds = BDIGITS(big);
- for (i = 0; i < num_longs; i++) {
- unsigned long d = buf[i];
- #if SIZEOF_LONG == SIZEOF_BDIGITS
- *ds++ = d;
- #else
- int j;
- for (j = 0; j < DIGSPERLONG; j++) {
- *ds++ = BIGLO(d);
- d = BIGDN(d);
- }
- #endif
- }
- if ((long)buf[num_longs-1] < 0) {
- get2comp(big);
- RBIGNUM_SET_SIGN(big, 0);
- }
- return bignorm(big);
- }
- }
- #define QUAD_SIZE 8
- #ifdef HAVE_LONG_LONG
- void
- rb_quad_pack(char *buf, VALUE val)
- {
- LONG_LONG q;
- val = rb_to_int(val);
- if (FIXNUM_P(val)) {
- q = FIX2LONG(val);
- }
- else {
- long len = RBIGNUM_LEN(val);
- BDIGIT *ds;
- if (len > SIZEOF_LONG_LONG/SIZEOF_BDIGITS) {
- len = SIZEOF_LONG_LONG/SIZEOF_BDIGITS;
- }
- ds = BDIGITS(val);
- q = 0;
- while (len--) {
- q = BIGUP(q);
- q += ds[len];
- }
- if (!RBIGNUM_SIGN(val)) q = -q;
- }
- memcpy(buf, (char*)&q, SIZEOF_LONG_LONG);
- }
- VALUE
- rb_quad_unpack(const char *buf, int sign)
- {
- BDIGIT_DBL q;
- long neg = 0;
- long i;
- BDIGIT *digits;
- VALUE big;
- memcpy(&q, buf, SIZEOF_LONG_LONG);
- if (sign) {
- if (FIXABLE((LONG_LONG)q)) return LONG2FIX((LONG_LONG)q);
- if ((LONG_LONG)q < 0) {
- q = -(LONG_LONG)q;
- neg = 1;
- }
- }
- else {
- if (POSFIXABLE(q)) return LONG2FIX(q);
- }
- i = 0;
- big = bignew(DIGSPERLL, 1);
- digits = BDIGITS(big);
- while (i < DIGSPERLL) {
- digits[i++] = BIGLO(q);
- q = BIGDN(q);
- }
- i = DIGSPERLL;
- while (i-- && !digits[i]) ;
- RBIGNUM_SET_LEN(big, i+1);
- if (neg) {
- RBIGNUM_SET_SIGN(big, 0);
- }
- return bignorm(big);
- }
- #else
- static int
- quad_buf_complement(char *buf, size_t len)
- {
- size_t i;
- for (i = 0; i < len; i++)
- buf[i] = ~buf[i];
- for (i = 0; i < len; i++) {
- buf[i]++;
- if (buf[i] != 0)
- return 0;
- }
- return 1;
- }
- void
- rb_quad_pack(char *buf, VALUE val)
- {
- long len;
- memset(buf, 0, QUAD_SIZE);
- val = rb_to_int(val);
- if (FIXNUM_P(val)) {
- val = rb_int2big(FIX2LONG(val));
- }
- len = RBIGNUM_LEN(val) * SIZEOF_BDIGITS;
- if (len > QUAD_SIZE) {
- len = QUAD_SIZE;
- }
- memcpy(buf, (char*)BDIGITS(val), len);
- if (RBIGNUM_NEGATIVE_P(val)) {
- quad_buf_complement(buf, QUAD_SIZE);
- }
- }
- #define BNEG(b) (RSHIFT(((BDIGIT*)b)[QUAD_SIZE/SIZEOF_BDIGITS-1],BITSPERDIG-1) != 0)
- VALUE
- rb_quad_unpack(const char *buf, int sign)
- {
- VALUE big = bignew(QUAD_SIZE/SIZEOF_BDIGITS, 1);
- memcpy((char*)BDIGITS(big), buf, QUAD_SIZE);
- if (sign && BNEG(buf)) {
- char *tmp = (char*)BDIGITS(big);
- RBIGNUM_SET_SIGN(big, 0);
- quad_buf_complement(tmp, QUAD_SIZE);
- }
- return bignorm(big);
- }
- #endif
- VALUE
- rb_cstr_to_inum(const char *str, int base, int badcheck)
- {
- const char *s = str;
- char *end;
- char sign = 1, nondigit = 0;
- int c;
- BDIGIT_DBL num;
- long len, blen = 1;
- long i;
- VALUE z;
- BDIGIT *zds;
- #undef ISDIGIT
- #define ISDIGIT(c) ('0' <= (c) && (c) <= '9')
- #define conv_digit(c) \
- (!ISASCII(c) ? -1 : \
- ISDIGIT(c) ? ((c) - '0') : \
- ISLOWER(c) ? ((c) - 'a' + 10) : \
- ISUPPER(c) ? ((c) - 'A' + 10) : \
- -1)
- if (!str) {
- if (badcheck) goto bad;
- return INT2FIX(0);
- }
- while (ISSPACE(*str)) str++;
- if (str[0] == '+') {
- str++;
- }
- else if (str[0] == '-') {
- str++;
- sign = 0;
- }
- if (str[0] == '+' || str[0] == '-') {
- if (badcheck) goto bad;
- return INT2FIX(0);
- }
- if (base <= 0) {
- if (str[0] == '0') {
- switch (str[1]) {
- case 'x': case 'X':
- base = 16;
- break;
- case 'b': case 'B':
- base = 2;
- break;
- case 'o': case 'O':
- base = 8;
- break;
- case 'd': case 'D':
- base = 10;
- break;
- default:
- base = 8;
- }
- }
- else if (base < -1) {
- base = -base;
- }
- else {
- base = 10;
- }
- }
- switch (base) {
- case 2:
- len = 1;
- if (str[0] == '0' && (str[1] == 'b'||str[1] == 'B')) {
- str += 2;
- }
- break;
- case 3:
- len = 2;
- break;
- case 8:
- if (str[0] == '0' && (str[1] == 'o'||str[1] == 'O')) {
- str += 2;
- }
- case 4: case 5: case 6: case 7:
- len = 3;
- break;
- case 10:
- if (str[0] == '0' && (str[1] == 'd'||str[1] == 'D')) {
- str += 2;
- }
- case 9: case 11: case 12: case 13: case 14: case 15:
- len = 4;
- break;
- case 16:
- len = 4;
- if (str[0] == '0' && (str[1] == 'x'||str[1] == 'X')) {
- str += 2;
- }
- break;
- default:
- if (base < 2 || 36 < base) {
- rb_raise(rb_eArgError, "invalid radix %d", base);
- }
- if (base <= 32) {
- len = 5;
- }
- else {
- len = 6;
- }
- break;
- }
- if (*str == '0') { /* squeeze preceding 0s */
- int us = 0;
- while ((c = *++str) == '0' || c == '_') {
- if (c == '_') {
- if (++us >= 2)
- break;
- } else
- us = 0;
- }
- if (!(c = *str) || ISSPACE(c)) --str;
- }
- c = *str;
- c = conv_digit(c);
- if (c < 0 || c >= base) {
- if (badcheck) goto bad;
- return INT2FIX(0);
- }
- len *= strlen(str)*sizeof(char);
- if ((size_t)len <= (sizeof(long)*CHAR_BIT)) {
- unsigned long val = STRTOUL(str, &end, base);
- if (str < end && *end == '_') goto bigparse;
- if (badcheck) {
- if (end == str) goto bad; /* no number */
- while (*end && ISSPACE(*end)) end++;
- if (*end) goto bad; /* trailing garbage */
- }
- if (POSFIXABLE(val)) {
- if (sign) return LONG2FIX(val);
- else {
- long result = -(long)val;
- return LONG2FIX(result);
- }
- }
- else {
- VALUE big = rb_uint2big(val);
- RBIGNUM_SET_SIGN(big, sign);
- return bignorm(big);
- }
- }
- bigparse:
- len = (len/BITSPERDIG)+1;
- if (badcheck && *str == '_') goto bad;
- z = bignew(len, sign);
- zds = BDIGITS(z);
- for (i=len;i--;) zds[i]=0;
- while ((c = *str++) != 0) {
- if (c == '_') {
- if (nondigit) {
- if (badcheck) goto bad;
- break;
- }
- nondigit = c;
- continue;
- }
- else if ((c = conv_digit(c)) < 0) {
- break;
- }
- if (c >= base) break;
- nondigit = 0;
- i = 0;
- num = c;
- for (;;) {
- while (i<blen) {
- num += (BDIGIT_DBL)zds[i]*base;
- zds[i++] = BIGLO(num);
- num = BIGDN(num);
- }
- if (num) {
- blen++;
- continue;
- }
- break;
- }
- }
- if (badcheck) {
- str--;
- if (s+1 < str && str[-1] == '_') goto bad;
- while (*str && ISSPACE(*str)) str++;
- if (*str) {
- bad:
- rb_invalid_str(s, "Integer");
- }
- }
- return bignorm(z);
- }
- VALUE
- rb_str_to_inum(VALUE str, int base, int badcheck)
- {
- const char *s;
- long len;
- StringValue(str);
- str_check_ascii_compatible(str);
- if (badcheck) {
- s = StringValueCStr(str);
- }
- else {
- s = RSTRING_PTR(str);
- }
- if (s) {
- len = RSTRING_LEN(str);
- if (s[len]) { /* no sentinel somehow */
- char *p = ALLOCA_N(char, len+1);
- MEMCPY(p, s, char, len);
- p[len] = '\0';
- s = p;
- }
- }
- return rb_cstr_to_inum(s, base, badcheck);
- }
- #if HAVE_LONG_LONG
- VALUE
- rb_ull2big(unsigned long long n)
- {
- BDIGIT_DBL num = n;
- long i = 0;
- BDIGIT *digits;
- VALUE big;
- big = bignew(DIGSPERLL, 1);
- digits = BDIGITS(big);
- while (i < DIGSPERLL) {
- digits[i++] = BIGLO(num);
- num = BIGDN(num);
- }
- i = DIGSPERLL;
- while (i-- && !digits[i]) ;
- RBIGNUM_SET_LEN(big, i+1);
- return big;
- }
- VALUE
- rb_ll2big(long long n)
- {
- long neg = 0;
- VALUE big;
- if (n < 0) {
- n = -n;
- neg = 1;
- }
- big = rb_ull2big(n);
- if (neg) {
- RBIGNUM_SET_SIGN(big, 0);
- }
- return big;
- }
- #endif /* HAVE_LONG_LONG */
- VALUE
- rb_cstr2inum(const char *str, int base)
- {
- return rb_cstr_to_inum(str, base, base==0);
- }
- VALUE
- rb_str2inum(VALUE str, int base)
- {
- return rb_str_to_inum(str, base, base==0);
- }
- const char ruby_digitmap[] = "0123456789abcdefghijklmnopqrstuvwxyz";
- static VALUE bigsqr(VALUE x);
- static void bigdivmod(VALUE x, VALUE y, volatile VALUE *divp, volatile VALUE *modp);
- #define POW2_P(x) (((x)&((x)-1))==0)
- static inline int
- ones(register unsigned long x)
- {
- #if SIZEOF_LONG == 8
- # define MASK_55 0x5555555555555555UL
- # define MASK_33 0x3333333333333333UL
- # define MASK_0f 0x0f0f0f0f0f0f0f0fUL
- #else
- # define MASK_55 0x55555555UL
- # define MASK_33 0x33333333UL
- # define MASK_0f 0x0f0f0f0fUL
- #endif
- x -= (x >> 1) & MASK_55;
- x = ((x >> 2) & MASK_33) + (x & MASK_33);
- x = ((x >> 4) + x) & MASK_0f;
- x += (x >> 8);
- x += (x >> 16);
- #if SIZEOF_LONG == 8
- x += (x >> 32);
- #endif
- return (int)(x & 0x7f);
- #undef MASK_0f
- #undef MASK_33
- #undef MASK_55
- }
- static inline unsigned long
- next_pow2(register unsigned long x)
- {
- x |= x >> 1;
- x |= x >> 2;
- x |= x >> 4;
- x |= x >> 8;
- x |= x >> 16;
- #if SIZEOF_LONG == 8
- x |= x >> 32;
- #endif
- return x + 1;
- }
- static inline int
- floor_log2(register unsigned long x)
- {
- x |= x >> 1;
- x |= x >> 2;
- x |= x >> 4;
- x |= x >> 8;
- x |= x >> 16;
- #if SIZEOF_LONG == 8
- x |= x >> 32;
- #endif
- return (int)ones(x) - 1;
- }
- static inline int
- ceil_log2(register unsigned long x)
- {
- return floor_log2(x) + !POW2_P(x);
- }
- #define LOG2_KARATSUBA_DIGITS 7
- #define KARATSUBA_DIGITS (1L<<LOG2_KARATSUBA_DIGITS)
- #define MAX_BIG2STR_TABLE_ENTRIES 64
- static VALUE big2str_power_cache[35][MAX_BIG2STR_TABLE_ENTRIES];
- static void
- power_cache_init(void)
- {
- int i, j;
- for (i = 0; i < 35; ++i) {
- for (j = 0; j < MAX_BIG2STR_TABLE_ENTRIES; ++j) {
- big2str_power_cache[i][j] = Qnil;
- }
- }
- }
- static inline VALUE
- power_cache_get_power0(int base, int i)
- {
- if (NIL_P(big2str_power_cache[base - 2][i])) {
- big2str_power_cache[base - 2][i] =
- i == 0 ? rb_big_pow(rb_int2big(base), INT2FIX(KARATSUBA_DIGITS))
- : bigsqr(power_cache_get_power0(base, i - 1));
- GC_RETAIN(big2str_power_cache[base - 2][i]);
- }
- return big2str_power_cache[base - 2][i];
- }
- static VALUE
- power_cache_get_power(int base, long n1, long* m1)
- {
- int i, m;
- long j;
- VALUE t;
- if (n1 <= KARATSUBA_DIGITS)
- rb_bug("n1 > KARATSUBA_DIGITS");
- m = ceil_log2(n1);
- if (m1) *m1 = 1 << m;
- i = m - LOG2_KARATSUBA_DIGITS;
- if (i >= MAX_BIG2STR_TABLE_ENTRIES)
- i = MAX_BIG2STR_TABLE_ENTRIES - 1;
- t = power_cache_get_power0(base, i);
- j = KARATSUBA_DIGITS*(1 << i);
- while (n1 > j) {
- t = bigsqr(t);
- j *= 2;
- }
- return t;
- }
- /* big2str_muraken_find_n1
- *
- * Let a natural number x is given by:
- * x = 2^0 * x_0 + 2^1 * x_1 + ... + 2^(B*n_0 - 1) * x_{B*n_0 - 1},
- * where B is BITSPERDIG (i.e. BDIGITS*CHAR_BIT) and n_0 is
- * RBIGNUM_LEN(x).
- *
- * Now, we assume n_1 = min_n \{ n | 2^(B*n_0/2) <= b_1^(n_1) \}, so
- * it is realized that 2^(B*n_0) <= {b_1}^{2*n_1}, where b_1 is a
- * given radix number. And then, we have n_1 <= (B*n_0) /
- * (2*log_2(b_1)), therefore n_1 is given by ceil((B*n_0) /
- * (2*log_2(b_1))).
- */
- static long
- big2str_find_n1(VALUE x, int base)
- {
- static const double log_2[] = {
- 1.0, 1.58496250072116, 2.0,
- 2.32192809488736, 2.58496250072116, 2.8073549220576,
- 3.0, 3.16992500144231, 3.32192809488736,
- 3.4594316186373, 3.58496250072116, 3.70043971814109,
- 3.8073549220576, 3.90689059560852, 4.0,
- 4.08746284125034, 4.16992500144231, 4.24792751344359,
- 4.32192809488736, 4.39231742277876, 4.4594316186373,
- 4.52356195605701, 4.58496250072116, 4.64385618977472,
- 4.70043971814109, 4.75488750216347, 4.8073549220576,
- 4.85798099512757, 4.90689059560852, 4.95419631038688,
- 5.0, 5.04439411935845, 5.08746284125034,
- 5.12928301694497, 5.16992500144231
- };
- long bits;
- if (base < 2 || 36 < base)
- rb_bug("invalid radix %d", base);
- if (FIXNUM_P(x)) {
- bits = (SIZEOF_LONG*CHAR_BIT - 1)/2 + 1;
- }
- else if (BIGZEROP(x)) {
- return 0;
- }
- else if (RBIGNUM_LEN(x) >= LONG_MAX/BITSPERDIG) {
- rb_raise(rb_eRangeError, "bignum too big to convert into `string'");
- }
- else {
- bits = BITSPERDIG*RBIGNUM_LEN(x);
- }
- return (long)ceil(bits/log_2[base - 2]);
- }
- static long
- big2str_orig(VALUE x, int base, char* ptr, long len, long hbase, int trim)
- {
- long i = RBIGNUM_LEN(x), j = len;
- BDIGIT* ds = BDIGITS(x);
- while (i && j > 0) {
- long k = i;
- BDIGIT_DBL num = 0;
- while (k--) { /* x / hbase */
- num = BIGUP(num) + ds[k];
- ds[k] = (BDIGIT)(num / hbase);
- num %= hbase;
- }
- if (trim && ds[i-1] == 0) i--;
- #if defined(__LP64__) && (MAC_OS_X_VERSION_MAX_ALLOWED >= 1060)
- k = SIZEOF_BDIGITS/2;
- #else
- k = SIZEOF_BDIGITS;
- #endif
- while (k--) {
- ptr[--j] = ruby_digitmap[num % base];
- num /= base;
- if (j <= 0) break;
- if (trim && i == 0 && num == 0) break;
- }
- }
- if (trim) {
- while (j < len && ptr[j] == '0') j++;
- MEMMOVE(ptr, ptr + j, char, len - j);
- len -= j;
- }
- return len;
- }
- static long
- big2str_karatsuba(VALUE x, int base, char* ptr,
- long n1, long len, long hbase, int trim)
- {
- long lh, ll, m1;
- VALUE b, q, r;
- if (FIXNUM_P(x)) {
- VALUE str = rb_fix2str(x, base);
- const char* str_ptr = RSTRING_PTR(str);
- long str_len = RSTRING_LEN(str);
- if (trim) {
- if (FIX2INT(x) == 0) return 0;
- MEMCPY(ptr, str_ptr, char, str_len);
- return str_len;
- }
- else {
- memset(ptr, '0', len - str_len);
- MEMCPY(ptr + len - str_len, str_ptr, char, str_len);
- return len;
- }
- }
- if (BIGZEROP(x)) {
- if (trim) return 0;
- else {
- memset(ptr, '0', len);
- return len;
- }
- }
- if (n1 <= KARATSUBA_DIGITS) {
- return big2str_orig(x, base, ptr, len, hbase, trim);
- }
- b = power_cache_get_power(base, n1, &m1);
- bigdivmod(x, b, &q, &r);
- lh = big2str_karatsuba(q, base, ptr, (len - m1)/2,
- len - m1, hbase, trim);
- rb_big_resize(q, 0);
- ll = big2str_karatsuba(r, base, ptr + lh, m1/2,
- m1, hbase, !lh && trim);
- rb_big_resize(r, 0);
- return lh + ll;
- }
- VALUE
- rb_big2str0(VALUE x, int base, int trim)
- {
- int off;
- VALUE xx;
- long n1, n2, len, hbase;
- char* ptr;
- if (FIXNUM_P(x)) {
- return rb_fix2str(x, base);
- }
- if (BIGZEROP(x)) {
- return rb_usascii_str_new2("0");
- }
- if (base < 2 || 36 < base)
- rb_raise(rb_eArgError, "invalid radix %d", base);
- n2 = big2str_find_n1(x, base);
- n1 = (n2 + 1) / 2;
- ptr = (char *)alloca(n2 + 1); /* plus one for sign */
- ptr[0] = RBIGNUM_SIGN(x) ? '+' : '-';
- hbase = base*base;
- #if SIZEOF_BDIGITS > 2
- hbase *= hbase;
- #endif
- off = !(trim && RBIGNUM_SIGN(x)); /* erase plus sign if trim */
- xx = rb_big_clone(x);
- RBIGNUM_SET_SIGN(xx, 1);
- if (n1 <= KARATSUBA_DIGITS) {
- len = off + big2str_orig(xx, base, ptr + off, n2, hbase, trim);
- }
- else {
- len = off + big2str_karatsuba(xx, base, ptr + off, n1,
- n2, hbase, trim);
- }
- rb_big_resize(xx, 0);
- ptr[len] = '\0';
- return rb_usascii_str_new2(ptr);
- }
- VALUE
- rb_big2str(VALUE x, int base)
- {
- return rb_big2str0(x, base, 1);
- }
- /*
- * call-seq:
- * big.to_s(base=10) -> string
- *
- * Returns a string containing the representation of <i>big</i> radix
- * <i>base</i> (2 through 36).
- *
- * 12345654321.to_s #=> "12345654321"
- * 12345654321.to_s(2) #=> "1011011111110110111011110000110001"
- * 12345654321.to_s(8) #=> "133766736061"
- * 12345654321.to_s(16) #=> "2dfdbbc31"
- * 78546939656932.to_s(36) #=> "rubyrules"
- */
- static VALUE
- rb_big_to_s(VALUE x, SEL sel, int argc, VALUE *argv)
- {
- int base;
- if (argc == 0) base = 10;
- else {
- VALUE b;
- rb_scan_args(argc, argv, "01", &b);
- base = NUM2INT(b);
- }
- return rb_big2str(x, base);
- }
- static VALUE
- big2ulong(VALUE x, const char *type, int check)
- {
- long len = RBIGNUM_LEN(x);
- BDIGIT_DBL num;
- BDIGIT *ds;
- if (len > DIGSPERLONG) {
- if (check)
- rb_raise(rb_eRangeError, "bignum too big to convert into `%s'", type);
- len = DIGSPERLONG;
- }
- ds = BDIGITS(x);
- num = 0;
- while (len--) {
- num = BIGUP(num);
- num += ds[len];
- }
- return (VALUE)num;
- }
- VALUE
- rb_big2ulong_pack(VALUE x)
- {
- VALUE num = big2ulong(x, "unsigned long", FALSE);
- if (!RBIGNUM_SIGN(x)) {
- return (VALUE)(-(SIGNED_VALUE)num);
- }
- return num;
- }
- VALUE
- rb_big2ulong(VALUE x)
- {
- VALUE num = big2ulong(x, "unsigned long", TRUE);
- if (!RBIGNUM_SIGN(x)) {
- if ((SIGNED_VALUE)num < 0) {
- rb_raise(rb_eRangeError, "bignum out of range of unsigned long");
- }
- return (VALUE)(-(SIGNED_VALUE)num);
- }
- return num;
- }
- SIGNED_VALUE
- rb_big2long(VALUE x)
- {
- VALUE num = big2ulong(x, "long", TRUE);
- if ((SIGNED_VALUE)num < 0 &&
- (RBIGNUM_SIGN(x) || (SIGNED_VALUE)num != LONG_MIN)) {
- rb_raise(rb_eRangeError, "bignum too big to convert into `long'");
- }
- if (!RBIGNUM_SIGN(x)) return -(SIGNED_VALUE)num;
- return num;
- }
- #if HAVE_LONG_LONG
- static unsigned LONG_LONG
- big2ull(VALUE x, const char *type)
- {
- long len = RBIGNUM_LEN(x);
- BDIGIT_DBL num;
- BDIGIT *ds;
- if (len > SIZEOF_LONG_LONG/SIZEOF_BDIGITS)
- rb_raise(rb_eRangeError, "bignum too big to convert into `%s'", type);
- ds = BDIGITS(x);
- num = 0;
- while (len--) {
- num = BIGUP(num);
- num += ds[len];
- }
- return num;
- }
- unsigned LONG_LONG
- rb_big2ull(VALUE x)
- {
- unsigned LONG_LONG num = big2ull(x, "unsigned long long");
- if (!RBIGNUM_SIGN(x)) return -num;
- return num;
- }
- LONG_LONG
- rb_big2ll(VALUE x)
- {
- unsigned LONG_LONG num = big2ull(x, "long long");
- // This check is disabled because this function might be called from
- // compiler stubs which will pass negative values as long long times
- // for certain Cocoa APIs (generally fixed values like -1 or -2).
- #if 0
- if ((LONG_LONG)num < 0 && (RBIGNUM_SIGN(x)
- || (LONG_LONG)num != LLONG_MIN)) {
- rb_raise(rb_eRangeError, "bignum too big to convert into `long long'");
- }
- #endif
- if (!RBIGNUM_SIGN(x)) return -(LONG_LONG)num;
- return num;
- }
- #endif /* HAVE_LONG_LONG */
- static VALUE
- dbl2big(double d)
- {
- long i = 0;
- BDIGIT c;
- BDIGIT *digits;
- VALUE z;
- double u = (d < 0)?-d:d;
- if (isinf(d)) {
- rb_raise(rb_eFloatDomainError, d < 0 ? "-Infinity" : "Infinity");
- }
- if (isnan(d)) {
- rb_raise(rb_eFloatDomainError, "NaN");
- }
- while (!POSFIXABLE(u) || 0 != (long)u) {
- u /= (double)(BIGRAD);
- i++;
- }
- z = bignew(i, d>=0);
- digits = BDIGITS(z);
- while (i--) {
- u *= BIGRAD;
- c = (BDIGIT)u;
- u -= c;
- digits[i] = c;
- }
- return z;
- }
- VALUE
- rb_dbl2big(double d)
- {
- return bignorm(dbl2big(d));
- }
- static int
- nlz(BDIGIT x)
- {
- BDIGIT y;
- int n = BITSPERDIG;
- #if BITSPERDIG > 64
- y = x >> 64; if (y) {n -= 64; x = y;}
- #endif
- #if BITSPERDIG > 32
- y = x >> 32; if (y) {n -= 32; x = y;}
- #endif
- #if BITSPERDIG > 16
- y = x >> 16; if (y) {n -= 16; x = y;}
- #endif
- y = x >> 8; if (y) {n -= 8; x = y;}
- y = x >> 4; if (y) {n -= 4; x = y;}
- y = x >> 2; if (y) {n -= 2; x = y;}
- y = x >> 1; if (y) {return n - 2;}
- return n - x;
- }
- static double
- big2dbl(VALUE x)
- {
- double d = 0.0;
- long i = (bigtrunc(x), RBIGNUM_LEN(x)), lo = 0, bits;
- BDIGIT *ds = BDIGITS(x), dl;
- if (i) {
- bits = i * BITSPERDIG - nlz(ds[i-1]);
- if (bits > DBL_MANT_DIG+DBL_MAX_EXP) {
- d = HUGE_VAL;
- }
- else {
- if (bits > DBL_MANT_DIG+1)
- lo = (bits -= DBL_MANT_DIG+1) / BITSPERDIG;
- else
- bits = 0;
- while (--i > lo) {
- d = ds[i] + BIGRAD*d;
- }
- dl = ds[i];
- if (bits && (dl & (1UL << (bits %= BITSPERDIG)))) {
- int carry = dl & ~(~(BDIGIT)0 << bits);
- if (!carry) {
- while (i-- > 0) {
- if ((carry = ds[i]) != 0) break;
- }
- }
- if (carry) {
- dl &= (BDIGIT)~0 << bits;
- dl += (BDIGIT)1 << bits;
- if (!dl) d += 1;
- }
- }
- d = dl + BIGRAD*d;
- if (lo) {
- if (lo > INT_MAX / BITSPERDIG)
- d = HUGE_VAL;
- else if (lo < INT_MIN / BITSPERDIG)
- d = 0.0;
- else
- d = ldexp(d, (int)(lo * BITSPERDIG));
- }
- }
- }
- if (!RBIGNUM_SIGN(x)) d = -d;
- return d;
- }
- double
- rb_big2dbl(VALUE x)
- {
- double d = big2dbl(x);
- if (isinf(d)) {
- rb_warning("Bignum out of Float range");
- if (d < 0.0)
- d = -HUGE_VAL;
- else
- d = HUGE_VAL;
- }
- return d;
- }
- /*
- * call-seq:
- * big.to_f -> float
- *
- * Converts <i>big</i> to a <code>Float</code>. If <i>big</i> doesn't
- * fit in a <code>Float</code>, the result is infinity.
- *
- */
- static VALUE
- rb_big_to_f(VALUE x, SEL sel)
- {
- return DBL2NUM(rb_big2dbl(x));
- }
- /*
- * call-seq:
- * big <=> numeric -> -1, 0, +1 or nil
- *
- * Comparison---Returns -1, 0, or +1 depending on whether <i>big</i> is
- * less than, equal to, or greater than <i>numeric</i>. This is the
- * basis for the tests in <code>Comparable</code>.
- *
- */
- VALUE
- rb_big_cmp(VALUE x, VALUE y)
- {
- long xlen = RBIGNUM_LEN(x);
- BDIGIT *xds, *yds;
- switch (TYPE(y)) {
- case T_FIXNUM:
- y = rb_int2big(FIX2LONG(y));
- break;
- case T_BIGNUM:
- break;
- case T_FLOAT:
- {
- double a = RFLOAT_VALUE(y);
- if (isinf(a)) {
- if (a > 0.0) return INT2FIX(-1);
- else return INT2FIX(1);
- }
- return rb_dbl_cmp(rb_big2dbl(x), a);
- }
- default:
- return rb_num_coerce_cmp(x, y, rb_intern("<=>"));
- }
- if (RBIGNUM_SIGN(x) > RBIGNUM_SIGN(y)) return INT2FIX(1);
- if (RBIGNUM_SIGN(x) < RBIGNUM_SIGN(y)) return INT2FIX(-1);
- if (xlen < RBIGNUM_LEN(y))
- return (RBIGNUM_SIGN(x)) ? INT2FIX(-1) : INT2FIX(1);
- if (xlen > RBIGNUM_LEN(y))
- return (RBIGNUM_SIGN(x)) ? INT2FIX(1) : INT2FIX(-1);
- xds = BDIGITS(x);
- yds = BDIGITS(y);
- while(xlen-- && (xds[xlen]==yds[xlen]));
- if (-1 == xlen) return INT2FIX(0);
- return (xds[xlen] > yds[xlen]) ?
- (RBIGNUM_SIGN(x) ? INT2FIX(1) : INT2FIX(-1)) :
- (RBIGNUM_SIGN(x) ? INT2FIX(-1) : INT2FIX(1));
- }
- static VALUE
- rb_big_cmp_imp(VALUE x, SEL sel, VALUE y)
- {
- return rb_big_cmp(x, y);
- }
- static VALUE
- big_op(VALUE x, VALUE y, int op)
- {
- VALUE rel;
- int n;
- switch (TYPE(y)) {
- case T_FIXNUM:
- case T_BIGNUM:
- rel = rb_big_cmp(x, y);
- break;
- case T_FLOAT:
- {
- double a = RFLOAT_VALUE(y);
- if (isinf(a)) {
- if (a > 0.0) rel = INT2FIX(-1);
- else rel = INT2FIX(1);
- break;
- }
- rel = rb_dbl_cmp(rb_big2dbl(x), a);
- break;
- }
- default:
- {
- ID id = 0;
- switch (op) {
- case 0: id = '>'; break;
- case 1: id = rb_intern(">="); break;
- case 2: id = '<'; break;
- case 3: id = rb_intern("<="); break;
- }
- return rb_num_coerce_relop(x, y, id);
- }
- }
- if (NIL_P(rel)) return Qfalse;
- n = FIX2INT(rel);
- switch (op) {
- case 0: return n > 0 ? Qtrue : Qfalse;
- case 1: return n >= 0 ? Qtrue : Qfalse;
- case 2: return n < 0 ? Qtrue : Qfalse;
- case 3: return n <= 0 ? Qtrue : Qfalse;
- }
- return Qundef;
- }
- /*
- * call-seq:
- * big > real -> true or false
- *
- * Returns <code>true</code> if the value of <code>big</code> is
- * greater than that of <code>real</code>.
- */
- static VALUE
- big_gt(VALUE x, SEL sel, VALUE y)
- {
- return big_op(x, y, 0);
- }
- /*
- * call-seq:
- * big >= real -> true or false
- *
- * Returns <code>true</code> if the value of <code>big</code> is
- * greater than or equal to that of <code>real</code>.
- */
- static VALUE
- big_ge(VALUE x, SEL sel, VALUE y)
- {
- return big_op(x, y, 1);
- }
- /*
- * call-seq:
- * big < real -> true or false
- *
- * Returns <code>true</code> if the value of <code>big</code> is
- * less than that of <code>real</code>.
- */
- static VALUE
- big_lt(VALUE x, SEL sel, VALUE y)
- {
- return big_op(x, y, 2);
- }
- /*
- * call-seq:
- * big <= real -> true or false
- *
- * Returns <code>true</code> if the value of <code>big</code> is
- * less than or equal to that of <code>real</code>.
- */
- static VALUE
- big_le(VALUE x, SEL sel, VALUE y)
- {
- return big_op(x, y, 3);
- }
- /*
- * call-seq:
- * big == obj -> true or false
- *
- * Returns <code>true</code> only if <i>obj</i> has the same value
- * as <i>big</i>. Contrast this with <code>Bignum#eql?</code>, which
- * requires <i>obj</i> to be a <code>Bignum</code>.
- *
- * 68719476736 == 68719476736.0 #=> true
- */
- VALUE
- rb_big_eq_imp(VALUE x, SEL sel, VALUE y)
- {
- return rb_big_eq(x, y);
- }
- VALUE
- rb_big_eq(VALUE x, VALUE y)
- {
- switch (TYPE(y)) {
- case T_FIXNUM:
- y = rb_int2big(FIX2LONG(y));
- break;
- case T_BIGNUM:
- break;
- case T_FLOAT:
- {
- volatile double a, b;
- a = RFLOAT_VALUE(y);
- if (isnan(a)) return Qfalse;
- b = rb_big2dbl(x);
- return (a == b)?Qtrue:Qfalse;
- }
- default:
- return rb_equal(y, x);
- }
- if (RBIGNUM_SIGN(x) != RBIGNUM_SIGN(y)) return Qfalse;
- if (RBIGNUM_LEN(x) != RBIGNUM_LEN(y)) return Qfalse;
- if (MEMCMP(BDIGITS(x),BDIGITS(y),BDIGIT,RBIGNUM_LEN(y)) != 0) return Qfalse;
- return Qtrue;
- }
- /*
- * call-seq:
- * big.eql?(obj) -> true or false
- *
- * Returns <code>true</code> only if <i>obj</i> is a
- * <code>Bignum</code> with the same value as <i>big</i>. Contrast this
- * with <code>Bignum#==</code>, which performs type conversions.
- *
- * 68719476736.eql?(68719476736.0) #=> false
- */
- static VALUE
- rb_big_eql(VALUE x, SEL sel, VALUE y)
- {
- if (TYPE(y) != T_BIGNUM) return Qfalse;
- if (RBIGNUM_SIGN(x) != RBIGNUM_SIGN(y)) return Qfalse;
- if (RBIGNUM_LEN(x) != RBIGNUM_LEN(y)) return Qfalse;
- if (MEMCMP(BDIGITS(x),BDIGITS(y),BDIGIT,RBIGNUM_LEN(y)) != 0) return Qfalse;
- return Qtrue;
- }
- /*
- * call-seq:
- * -big -> integer
- *
- * Unary minus (returns an integer whose value is 0-big)
- */
- VALUE
- rb_big_uminus(VALUE x)
- {
- VALUE z = rb_big_clone(x);
- RBIGNUM_SET_SIGN(z, !RBIGNUM_SIGN(x));
- return bignorm(z);
- }
- static VALUE
- rb_big_uminus_imp(VALUE x, SEL sel)
- {
- return rb_big_uminus(x);
- }
- /*
- * call-seq:
- * ~big -> integer
- *
- * Inverts the bits in big. As Bignums are conceptually infinite
- * length, the result acts as if it had an infinite number of one
- * bits to the left. In hex representations, this is displayed
- * as two periods to the left of the digits.
- *
- * sprintf("%X", ~0x1122334455) #=> "..FEEDDCCBBAA"
- */
- static VALUE
- rb_big_neg(VALUE x, SEL sel)
- {
- VALUE z = rb_big_clone(x);
- BDIGIT *ds;
- long i;
- if (!RBIGNUM_SIGN(x)) get2comp(z);
- ds = BDIGITS(z);
- i = RBIGNUM_LEN(x);
- if (!i) return INT2FIX(~(SIGNED_VALUE)0);
- while (i--) {
- ds[i] = ~ds[i];
- }
- RBIGNUM_SET_SIGN(z, !RBIGNUM_SIGN(z));
- if (RBIGNUM_SIGN(x)) get2comp(z);
- return bignorm(z);
- }
- static void
- bigsub_core(BDIGIT *xds, long xn, BDIGIT *yds, long yn, BDIGIT *zds, long zn)
- {
- BDIGIT_DBL_SIGNED num;
- long i;
- for (i = 0, num = 0; i < yn; i++) {
- num += (BDIGIT_DBL_SIGNED)xds[i] - yds[i];
- zds[i] = BIGLO(num);
- num = BIGDN(num);
- }
- while (num && i < xn) {
- num += xds[i];
- zds[i++] = BIGLO(num);
- num = BIGDN(num);
- }
- while (i < xn) {
- zds[i] = xds[i];
- i++;
- }
- assert(i <= zn);
- while (i < zn) {
- zds[i++] = 0;
- }
- }
- static VALUE
- bigsub(VALUE x, VALUE y)
- {
- VALUE z = 0;
- long i = RBIGNUM_LEN(x);
- BDIGIT *xds, *yds;
- /* if x is larger than y, swap */
- if (RBIGNUM_LEN(x) < RBIGNUM_LEN(y)) {
- z = x; x = y; y = z; /* swap x y */
- }
- else if (RBIGNUM_LEN(x) == RBIGNUM_LEN(y)) {
- xds = BDIGITS(x);
- yds = BDIGITS(y);
- while (i > 0) {
- i--;
- if (xds[i] > yds[i]) {
- break;
- }
- if (xds[i] < yds[i]) {
- z = x; x = y; y = z; /* swap x y */
- break;
- }
- }
- }
- z = bignew(RBIGNUM_LEN(x), z==0);
- bigsub_core(BDIGITS(x), RBIGNUM_LEN(x),
- BDIGITS(y), RBIGNUM_LEN(y),
- BDIGITS(z), RBIGNUM_LEN(z));
- return z;
- }
- static VALUE bigadd_int(VALUE x, long y);
- static VALUE
- bigsub_int(VALUE x, long y0)
- {
- VALUE z;
- BDIGIT *xds, *zds;
- long xn;
- BDIGIT_DBL_SIGNED num;
- long i, y;
- y = y0;
- xds = BDIGITS(x);
- xn = RBIGNUM_LEN(x);
- z = bignew(xn, RBIGNUM_SIGN(x));
- zds = BDIGITS(z);
- #if SIZEOF_BDIGITS == SIZEOF_LONG
- num = (BDIGIT_DBL_SIGNED)xds[0] - y;
- if (xn == 1 && num < 0) {
- RBIGNUM_SET_SIGN(z, !RBIGNUM_SIGN(x));
- zds[0] = (BDIGIT)-num;
- return bignorm(z);
- }
- zds[0] = BIGLO(num);
- num = BIGDN(num);
- i = 1;
- #else
- num = 0;
- for (i=0; i<(int)(sizeof(y)/sizeof(BDIGIT)); i++) {
- num += (BDIGIT_DBL_SIGNED)xds[i] - BIGLO(y);
- zds[i] = BIGLO(num);
- num = BIGDN(num);
- y = BIGDN(y);
- }
- #endif
- while (num && i < xn) {
- num += xds[i];
- zds[i++] = BIGLO(num);
- num = BIGDN(num);
- }
- while (i < xn) {
- zds[i] = xds[i];
- i++;
- }
- if (num < 0) {
- z = bigsub(x, rb_int2big(y0));
- }
- return bignorm(z);
- }
- static VALUE
- bigadd_int(VALUE x, long y)
- {
- VALUE z;
- BDIGIT *xds, *zds;
- long xn, zn;
- BDIGIT_DBL num;
- long i;
- xds = BDIGITS(x);
- xn = RBIGNUM_LEN(x);
- if (xn < 2) {
- zn = 3;
- }
- else {
- zn = xn + 1;
- }
- z = bignew(zn, RBIGNUM_SIGN(x));
- zds = BDIGITS(z);
- #if SIZEOF_BDIGITS == SIZEOF_LONG
- num = (BDIGIT_DBL)xds[0] + y;
- zds[0] = BIGLO(num);
- num = BIGDN(num);
- i = 1;
- #else
- num = 0;
- for (i=0; i<(int)(sizeof(y)/sizeof(BDIGIT)); i++) {
- num += (BDIGIT_DBL)xds[i] + BIGLO(y);
- zds[i] = BIGLO(num);
- num = BIGDN(num);
- y = BIGDN(y);
- }
- #endif
- while (num && i < xn) {
- num += xds[i];
- zds[i++] = BIGLO(num);
- num = BIGDN(num);
- }
- if (num) zds[i++] = (BDIGIT)num;
- else while (i < xn) {
- zds[i] = xds[i];
- i++;
- }
- assert(i <= zn);
- while (i < zn) {
- zds[i++] = 0;
- }
- return bignorm(z);
- }
- static void
- bigadd_core(BDIGIT *xds, long xn, BDIGIT *yds, long yn, BDIGIT *zds, long zn)
- {
- BDIGIT_DBL num = 0;
- long i;
- if (xn > yn) {
- BDIGIT *tds;
- tds = xds; xds = yds; yds = tds;
- i = xn; xn = yn; yn = i;
- }
- i = 0;
- while (i < xn) {
- num += (BDIGIT_DBL)xds[i] + yds[i];
- zds[i++] = BIGLO(num);
- num = BIGDN(num);
- }
- while (num && i < yn) {
- num += yds[i];
- zds[i++] = BIGLO(num);
- num = BIGDN(num);
- }
- while (i < yn) {
- zds[i] = yds[i];
- i++;
- }
- if (num) zds[i++] = (BDIGIT)num;
- assert(i <= zn);
- while (i < zn) {
- zds[i++] = 0;
- }
- }
- static VALUE
- bigadd(VALUE x, VALUE y, int sign)
- {
- VALUE z;
- long len;
- sign = (sign == RBIGNUM_SIGN(y));
- if (RBIGNUM_SIGN(x) != sign) {
- if (sign) return bigsub(y, x);
- return bigsub(x, y);
- }
- if (RBIGNUM_LEN(x) > RBIGNUM_LEN(y)) {
- len = RBIGNUM_LEN(x) + 1;
- }
- else {
- len = RBIGNUM_LEN(y) + 1;
- }
- z = bignew(len, sign);
- bigadd_core(BDIGITS(x), RBIGNUM_LEN(x),
- BDIGITS(y), RBIGNUM_LEN(y),
- BDIGITS(z), RBIGNUM_LEN(z));
- return z;
- }
- /*
- * call-seq:
- * big + other -> Numeric
- *
- * Adds big and other, returning the result.
- */
- static VALUE
- rb_big_plus_imp(VALUE x, SEL sel, VALUE y)
- {
- long n;
- switch (TYPE(y)) {
- case T_FIXNUM:
- n = FIX2LONG(y);
- if ((n > 0) != RBIGNUM_SIGN(x)) {
- if (n < 0) {
- n = -n;
- }
- return bigsub_int(x, n);
- }
- if (n < 0) {
- n = -n;
- }
- return bigadd_int(x, n);
- case T_BIGNUM:
- return bignorm(bigadd(x, y, 1));
- case T_FLOAT:
- return DBL2NUM(rb_big2dbl(x) + RFLOAT_VALUE(y));
- default:
- return rb_num_coerce_bin(x, y, '+');
- }
- }
- VALUE
- rb_big_plus(VALUE x, VALUE y)
- {
- return rb_big_plus_imp(x, 0, y);
- }
- /*
- * call-seq:
- * big - other -> Numeric
- *
- * Subtracts other from big, returning the result.
- */
- VALUE
- rb_big_minus(VALUE x, VALUE y)
- {
- long n;
- switch (TYPE(y)) {
- case T_FIXNUM:
- n = FIX2LONG(y);
- if ((n > 0) != RBIGNUM_SIGN(x)) {
- if (n < 0) {
- n = -n;
- }
- return bigadd_int(x, n);
- }
- if (n < 0) {
- n = -n;
- }
- return bigsub_int(x, n);
- case T_BIGNUM:
- return bignorm(bigadd(x, y, 0));
- case T_FLOAT:
- return DBL2NUM(rb_big2dbl(x) - RFLOAT_VALUE(y));
- default:
- return rb_num_coerce_bin(x, y, '-');
- }
- }
- static VALUE
- rb_big_minus_imp(VALUE x, SEL sel, VALUE y)
- {
- return rb_big_minus(x, y);
- }
- static long
- big_real_len(VALUE x)
- {
- long i = RBIGNUM_LEN(x);
- BDIGIT *xds = BDIGITS(x);
- while (--i && !xds[i]);
- return i + 1;
- }
- static VALUE
- bigmul1_single(VALUE x, VALUE y)
- {
- BDIGIT_DBL n;
- VALUE z = bignew(2, RBIGNUM_SIGN(x)==RBIGNUM_SIGN(y));
- BDIGIT *xds, *yds, *zds;
- xds = BDIGITS(x);
- yds = BDIGITS(y);
- zds = BDIGITS(z);
- n = (BDIGIT_DBL)xds[0] * yds[0];
- zds[0] = BIGLO(n);
- zds[1] = (BDIGIT)BIGDN(n);
- return z;
- }
- static VALUE
- bigmul1_normal(VALUE x, VALUE y)
- {
- long xl = RBIGNUM_LEN(x), yl = RBIGNUM_LEN(y), i, j = xl + yl + 1;
- BDIGIT_DBL n = 0;
- VALUE z = bignew(j, RBIGNUM_SIGN(x)==RBIGNUM_SIGN(y));
- BDIGIT *xds, *yds, *zds;
- xds = BDIGITS(x);
- yds = BDIGITS(y);
- zds = BDIGITS(z);
- while (j--) zds[j] = 0;
- for (i = 0; i < xl; i++) {
- BDIGIT_DBL dd;
- dd = xds[i];
- if (dd == 0) continue;
- n = 0;
- for (j = 0; j < yl; j++) {
- BDIGIT_DBL ee = n + (BDIGIT_DBL)dd * yds[j];
- n = zds[i + j] + ee;
- if (ee) zds[i + j] = BIGLO(n);
- n = BIGDN(n);
- }
- if (n) {
- zds[i + j] = (BDIGIT)n;
- }
- }
- //rb_thread_check_ints();
- return z;
- }
- static VALUE bigmul0(VALUE x, VALUE y);
- /* balancing multiplication by slicing larger argument */
- static VALUE
- bigmul1_balance(VALUE x, VALUE y)
- {
- VALUE z, t1, t2;
- long i, xn, yn, r, n;
- BDIGIT *yds, *zds, *t1ds;
- xn = RBIGNUM_LEN(x);
- yn = RBIGNUM_LEN(y);
- assert(2 * xn <= yn);
- z = bignew(xn + yn, RBIGNUM_SIGN(x)==RBIGNUM_SIGN(y));
- t1 = bignew(xn, 1);
- yds = BDIGITS(y);
- zds = BDIGITS(z);
- t1ds = BDIGITS(t1);
- for (i = 0; i < xn + yn; i++) zds[i] = 0;
- n = 0;
- while (yn > 0) {
- r = xn > yn ? yn : xn;
- MEMCPY(t1ds, yds + n, BDIGIT, r);
- RBIGNUM_SET_LEN(t1, r);
- t2 = bigmul0(x, t1);
- bigadd_core(zds + n, RBIGNUM_LEN(z) - n,
- BDIGITS(t2), big_real_len(t2),
- zds + n, RBIGNUM_LEN(z) - n);
- yn -= r;
- n += r;
- }
- return z;
- }
- /* split a bignum into high and low bignums */
- static void
- big_split(VALUE v, long n, volatile VALUE *ph, volatile VALUE *pl)
- {
- long hn = 0, ln = RBIGNUM_LEN(v);
- VALUE h, l;
- BDIGIT *vds = BDIGITS(v);
- if (ln > n) {
- hn = ln - n;
- ln = n;
- }
- while (--hn && !vds[hn + ln]);
- h = bignew(hn += 2, 1);
- MEMCPY(BDIGITS(h), vds + ln, BDIGIT, hn - 1);
- BDIGITS(h)[hn - 1] = 0; /* margin for carry */
- while (--ln && !vds[ln]);
- l = bignew(ln += 2, 1);
- MEMCPY(BDIGITS(l), vds, BDIGIT, ln - 1);
- BDIGITS(l)[ln - 1] = 0; /* margin for carry */
- *pl = l;
- *ph = h;
- }
- /* multiplication by karatsuba method */
- static VALUE
- bigmul1_karatsuba(VALUE x, VALUE y)
- {
- long i, n, xn, yn, t1n, t2n;
- VALUE xh, xl, yh, yl, z, t1, t2, t3;
- BDIGIT *zds;
- xn = RBIGNUM_LEN(x);
- yn = RBIGNUM_LEN(y);
- n = yn / 2;
- big_split(x, n, &xh, &xl);
- if (x == y) {
- yh = xh; yl = xl;
- }
- else big_split(y, n, &yh, &yl);
- /* x = xh * b + xl
- * y = yh * b + yl
- *
- * Karatsuba method:
- * x * y = z2 * b^2 + z1 * b + z0
- * where
- * z2 = xh * yh
- * z0 = xl * yl
- * z1 = (xh + xl) * (yh + yl) - z2 - z0
- *
- * ref: http://en.wikipedia.org/wiki/Karatsuba_algorithm
- */
- /* allocate a result bignum */
- z = bignew(xn + yn, RBIGNUM_SIGN(x)==RBIGNUM_SIGN(y));
- zds = BDIGITS(z);
- /* t1 <- xh * yh */
- t1 = bigmul0(xh, yh);
- t1n = big_real_len(t1);
- /* copy t1 into high bytes of the result (z2) */
- MEMCPY(zds + 2 * n, BDIGITS(t1), BDIGIT, t1n);
- for (i = 2 * n + t1n; i < xn + yn; i++) zds[i] = 0;
- if (!BIGZEROP(xl) && !BIGZEROP(yl)) {
- /* t2 <- xl * yl */
- t2 = bigmul0(xl, yl);
- t2n = big_real_len(t2);
- /* copy t2 into low bytes of the result (z0) */
- MEMCPY(zds, BDIGITS(t2), BDIGIT, t2n);
- for (i = t2n; i < 2 * n; i++) zds[i] = 0;
- }
- else {
- t2 = Qundef;
- t2n = 0;
- /* copy 0 into low bytes of the result (z0) */
- for (i = 0; i < 2 * n; i++) zds[i] = 0;
- }
- /* xh <- xh + xl */
- if (RBIGNUM_LEN(xl) > RBIGNUM_LEN(xh)) {
- t3 = xl; xl = xh; xh = t3;
- }
- /* xh has a margin for carry */
- bigadd_core(BDIGITS(xh), RBIGNUM_LEN(xh),
- BDIGITS(xl), RBIGNUM_LEN(xl),
- BDIGITS(xh), RBIGNUM_LEN(xh));
- /* yh <- yh + yl */
- if (x != y) {
- if (RBIGNUM_LEN(yl) > RBIGNUM_LEN(yh)) {
- t3 = yl; yl = yh; yh = t3;
- }
- /* yh has a margin for carry */
- bigadd_core(BDIGITS(yh), RBIGNUM_LEN(yh),
- BDIGITS(yl), RBIGNUM_LEN(yl),
- BDIGITS(yh), RBIGNUM_LEN(yh));
- }
- else yh = xh;
- /* t3 <- xh * yh */
- t3 = bigmul0(xh, yh);
- i = xn + yn - n;
- /* subtract t1 from t3 */
- bigsub_core(BDIGITS(t3), big_real_len(t3), BDIGITS(t1), t1n, BDIGITS(t3), big_real_len(t3));
- /* subtract t2 from t3; t3 is now the middle term of the product */
- if (t2 != Qundef) bigsub_core(BDIGITS(t3), big_real_len(t3), BDIGITS(t2), t2n, BDIGITS(t3), big_real_len(t3));
- /* add t3 to middle bytes of the result (z1) */
- bigadd_core(zds + n, i, BDIGITS(t3), big_real_len(t3), zds + n, i);
- return z;
- }
- /* efficient squaring (2 times faster than normal multiplication)
- * ref: Handbook of Applied Cryptography, Algorithm 14.16
- * http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf
- */
- static VALUE
- bigsqr_fast(VALUE x)
- {
- long len = RBIGNUM_LEN(x), i, j;
- VALUE z = bignew(2 * len + 1, 1);
- BDIGIT *xds = BDIGITS(x), *zds = BDIGITS(z);
- BDIGIT_DBL c, v, w;
- for (i = 2 * len + 1; i--; ) zds[i] = 0;
- for (i = 0; i < len; i++) {
- v = (BDIGIT_DBL)xds[i];
- if (!v) continue;
- c = (BDIGIT_DBL)zds[i + i] + v * v;
- zds[i + i] = BIGLO(c);
- c = BIGDN(c);
- v *= 2;
- for (j = i + 1; j < len; j++) {
- w = (BDIGIT_DBL)xds[j];
- c += (BDIGIT_DBL)zds[i + j] + BIGLO(v) * w;
- zds[i + j] = BIGLO(c);
- c = BIGDN(c);
- if (BIGDN(v)) c += w;
- }
- if (c) {
- c += (BDIGIT_DBL)zds[i + len];
- zds[i + len] = BIGLO(c);
- c = BIGDN(c);
- }
- if (c) zds[i + len + 1] += (BDIGIT)c;
- }
- return z;
- }
- #define KARATSUBA_MUL_DIGITS 70
- /* determine whether a bignum is sparse or not by random sampling */
- static inline VALUE
- big_sparse_p(VALUE x)
- {
- long c = 0, n = RBIGNUM_LEN(x);
- unsigned long rb_rand_internal(unsigned long i);
- if ( BDIGITS(x)[rb_rand_internal(n / 2) + n / 4]) c++;
- if (c <= 1 && BDIGITS(x)[rb_rand_internal(n / 2) + n / 4]) c++;
- if (c <= 1 && BDIGITS(x)[rb_rand_internal(n / 2) + n / 4]) c++;
- return (c <= 1) ? Qtrue : Qfalse;
- }
- #if 0
- static void
- dump_bignum(VALUE x)
- {
- long i;
- printf("0x0");
- for (i = RBIGNUM_LEN(x); i--; ) {
- printf("_%08x", BDIGITS(x)[i]);
- }
- puts("");
- }
- #endif
- static VALUE
- bigmul0(VALUE x, VALUE y)
- {
- long xn, yn;
- xn = RBIGNUM_LEN(x);
- yn = RBIGNUM_LEN(y);
- /* make sure that y is longer than x */
- if (xn > yn) {
- VALUE t;
- long tn;
- t = x; x = y; y = t;
- tn = xn; xn = yn; yn = tn;
- }
- assert(xn <= yn);
- /* normal multiplication when x is small */
- if (xn < KARATSUBA_MUL_DIGITS) {
- normal:
- if (x == y) return bigsqr_fast(x);
- if (xn == 1 && yn == 1) return bigmul1_single(x, y);
- return bigmul1_normal(x, y);
- }
- /* normal multiplication when x or y is a sparse bignum */
- if (big_sparse_p(x)) goto normal;
- if (big_sparse_p(y)) return bigmul1_normal(y, x);
- /* balance multiplication by slicing y when x is much smaller than y */
- if (2 * xn <= yn) return bigmul1_balance(x, y);
- /* multiplication by karatsuba method */
- return bigmul1_karatsuba(x, y);
- }
- /*
- * call-seq:
- * big * other -> Numeric
- *
- * Multiplies big and other, returning the result.
- */
- VALUE
- rb_big_mul(VALUE x, VALUE y)
- {
- switch (TYPE(y)) {
- case T_FIXNUM:
- y = rb_int2big(FIX2LONG(y));
- break;
- case T_BIGNUM:
- break;
- case T_FLOAT:
- return DBL2NUM(rb_big2dbl(x) * RFLOAT_VALUE(y));
- default:
- return rb_num_coerce_bin(x, y, '*');
- }
- return bignorm(bigmul0(x, y));
- }
- static VALUE
- rb_big_mul_imp(VALUE x, SEL sel, VALUE y)
- {
- return bignorm(rb_big_mul(x, y));
- }
- struct big_div_struct {
- long nx, ny;
- BDIGIT *yds, *zds;
- VALUE stop;
- };
- static VALUE
- bigdivrem1(void *ptr)
- {
- struct big_div_struct *bds = (struct big_div_struct*)ptr;
- long nx = bds->nx, ny = bds->ny;
- long i, j, nyzero;
- BDIGIT *yds = bds->yds, *zds = bds->zds;
- BDIGIT_DBL t2;
- BDIGIT_DBL_SIGNED num;
- BDIGIT q;
- j = nx==ny?nx+1:nx;
- for (nyzero = 0; !yds[nyzero]; nyzero++);
- do {
- if (bds->stop) return Qnil;
- if (zds[j] == yds[ny-1]) q = (BDIGIT)BIGRAD-1;
- else q = (BDIGIT)((BIGUP(zds[j]) + zds[j-1])/yds[ny-1]);
- if (q) {
- i = nyzero; num = 0; t2 = 0;
- do { /* multiply and subtract */
- BDIGIT_DBL ee;
- t2 += (BDIGIT_DBL)yds[i] * q;
- ee = num - BIGLO(t2);
- num = (BDIGIT_DBL)zds[j - ny + i] + ee;
- if (ee) zds[j - ny + i] = BIGLO(num);
- num = BIGDN(num);
- t2 = BIGDN(t2);
- } while (++i < ny);
- num += zds[j - ny + i] - t2;/* borrow from high digit; don't update */
- while (num) { /* "add back" required */
- i = 0; num = 0; q--;
- do {
- BDIGIT_DBL ee = num + yds[i];
- num = (BDIGIT_DBL)zds[j - ny + i] + ee;
- if (ee) zds[j - ny + i] = BIGLO(num);
- num = BIGDN(num);
- } while (++i < ny);
- num--;
- }
- }
- zds[j] = q;
- } while (--j >= ny);
- return Qnil;
- }
- #if 0
- static void
- rb_big_stop(void *ptr)
- {
- VALUE *stop = (VALUE*)ptr;
- *stop = Qtrue;
- }
- #endif
- static VALUE
- bigdivrem(VALUE x, VALUE y, volatile VALUE *divp, volatile VALUE *modp)
- {
- struct big_div_struct bds;
- long nx = RBIGNUM_LEN(x), ny = RBIGNUM_LEN(y);
- long i, j;
- VALUE z, yy, zz;
- BDIGIT *xds, *yds, *zds, *tds;
- BDIGIT_DBL t2;
- BDIGIT dd, q;
- if (BIGZEROP(y)) rb_num_zerodiv();
- xds = BDIGITS(x);
- yds = BDIGITS(y);
- if (nx < ny || (nx == ny && xds[nx - 1] < yds[ny - 1])) {
- if (divp) *divp = rb_int2big(0);
- if (modp) *modp = x;
- return Qnil;
- }
- if (ny == 1) {
- dd = yds[0];
- z = rb_big_clone(x);
- zds = BDIGITS(z);
- t2 = 0; i = nx;
- while (i--) {
- t2 = BIGUP(t2) + zds[i];
- zds[i] = (BDIGIT)(t2 / dd);
- t2 %= dd;
- }
- RBIGNUM_SET_SIGN(z, R…
Large files files are truncated, but you can click here to view the full file