From dcaf4083d55b270b2850e1ec3f8ae89ad01265f2 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Thu, 7 Apr 2022 16:45:21 +0900 Subject: [PATCH] src/bigint.c: implement multi-precision integer. To enable multi-precision integer support, you need to link `mruby-bigint` mrbgem. The gem itself is empty but it turns on the "bigint" support. --- include/mruby/bigint.h | 117 ++ include/mruby/internal.h | 34 + include/mruby/value.h | 6 +- mrbgems/mruby-bigint/README-fgmp.md | 154 ++ mrbgems/mruby-bigint/README.md | 9 + mrbgems/mruby-bigint/mrbgem.rake | 6 + mrbgems/mruby-numeric-ext/src/numeric_ext.c | 79 +- mrbgems/mruby-os-memsize/src/memsize.c | 17 +- src/array.c | 2 +- src/bigint.c | 1410 +++++++++++++++++++ src/gc.c | 7 + src/load.c | 2 +- src/numeric.c | 179 ++- src/numops.c | 15 + src/object.c | 6 + src/string.c | 37 +- src/vm.c | 26 +- 17 files changed, 2065 insertions(+), 41 deletions(-) create mode 100644 include/mruby/bigint.h create mode 100644 mrbgems/mruby-bigint/README-fgmp.md create mode 100644 mrbgems/mruby-bigint/README.md create mode 100644 mrbgems/mruby-bigint/mrbgem.rake create mode 100644 src/bigint.c diff --git a/include/mruby/bigint.h b/include/mruby/bigint.h new file mode 100644 index 000000000..142a11d30 --- /dev/null +++ b/include/mruby/bigint.h @@ -0,0 +1,117 @@ +/** +** @file mruby/bigint.h - Multi-precision, Integer +** +** See Copyright Notice in mruby.h +*/ +#ifdef MRB_USE_BIGINT + +#ifndef MRUBY_BIGINT_H +#define MRUBY_BIGINT_H +/* + * FREE GMP - a public domain implementation of a subset of the + * gmp library + * + * I hearby place the file in the public domain. + * + * Do whatever you want with this code. Change it. Sell it. Claim you + * wrote it. + * Bugs, complaints, flames, rants: please send email to + * Mark Henderson + * I'm already aware that fgmp is considerably slower than gmp + * + * CREDITS: + * Paul Rouse - generic bug fixes, mpz_sqrt and + * mpz_sqrtrem, and modifications to get fgmp to compile on a system + * with int and long of different sizes (specifically MS-DOS,286 compiler) + * Also see the file "notes" included with the fgmp distribution, for + * more credits. + * + * VERSION 1.0 - beta 5 + */ + +#include + +#ifdef MRB_32BIT +/* + * The values below are for 32 bit machines (i.e. machines with a + * 32 bit long type) + * You'll need to change them, if you're using something else + * If DIGITBITS is odd, see the comment at the top of mpz_sqrtrem + */ +typedef int32_t mp_limb; +#define LMAX 0x3fffffffL +#define LC 0xc0000000L +#define OVMASK 0x2 +#define CMASK (LMAX+1) +#define HLMAX 0x7fffL +#define HCMASK (HLMAX + 1) +#define HIGH(x) (((x) & 0x3fff8000L) >> 15) +#define LOW(x) ((x) & 0x7fffL) + +#else +/* 64 bit long type */ +typedef int64_t mp_limb; +#define LMAX 0x3fffffffffffffffL +#define LC 0xc000000000000000L +#define OVMASK 0x2 +#define CMASK (LMAX+1) +#define HLMAX 0x7fffffffL +#define HCMASK (HLMAX + 1) +#define HIGH(x) (((x) & 0x3fffffff80000000L) >> 31) +#define LOW(x) ((x) & 0x7fffffffL) +#endif + +typedef struct _mpz_t { + mp_limb *p; + short sn; + size_t sz; +} mpz_t; + +struct RBigint { + MRB_OBJECT_HEADER; + mpz_t mp; +}; +#define RBIGINT(v) ((struct RBigint*)mrb_ptr(v)) + +#define iabs(x) ((x>0) ? (x) : (-x)) +#define imax(x,y) ((x>y)?x:y) +#define LONGBITS (sizeof(mp_limb)*8) +#define DIGITBITS (LONGBITS-2) +#define HALFDIGITBITS ((LONGBITS-2)/2) + +#define hd(x,i) (((size_t)(i)>=2*((x)->sz))? 0:(((i)%2) ? HIGH((x)->p[(i)/2]) \ + : LOW((x)->p[(i)/2]))) +#define dg(x,i) (((size_t)(i) < (x)->sz) ? ((x)->p)[i] : 0) + +#define RBIGINT(v) ((struct RBigint*)mrb_ptr(v)) + +mrb_value mrb_bint_new_int(mrb_state *mrb, mrb_int x); +mrb_value mrb_bint_add(mrb_state *mrb, mrb_value x, mrb_value y); +mrb_value mrb_bint_sub(mrb_state *mrb, mrb_value x, mrb_value y); +mrb_value mrb_bint_mul(mrb_state *mrb, mrb_value x, mrb_value y); +mrb_value mrb_bint_div(mrb_state *mrb, mrb_value x, mrb_value y); +mrb_value mrb_bint_divmod(mrb_state *mrb, mrb_value x, mrb_value y); +mrb_value mrb_bint_add_ii(mrb_state *mrb, mrb_int x, mrb_int y); +mrb_value mrb_bint_sub_ii(mrb_state *mrb, mrb_int x, mrb_int y); +mrb_value mrb_bint_mul_ii(mrb_state *mrb, mrb_int x, mrb_int y); +mrb_value mrb_bint_div_ii(mrb_state *mrb, mrb_int x, mrb_int y); +mrb_value mrb_bint_mod(mrb_state *mrb, mrb_value x, mrb_value y); +mrb_value mrb_bint_rem(mrb_state *mrb, mrb_value x, mrb_value y); +mrb_value mrb_bint_pow(mrb_state *mrb, mrb_value x, mrb_value y); +mrb_value mrb_bint_powm(mrb_state *mrb, mrb_value x, mrb_int y, mrb_value z); +mrb_value mrb_bint_and(mrb_state *mrb, mrb_value x, mrb_value y); +mrb_value mrb_bint_or(mrb_state *mrb, mrb_value x, mrb_value y); +mrb_value mrb_bint_xor(mrb_state *mrb, mrb_value x, mrb_value y); +mrb_value mrb_bint_rev(mrb_state *mrb, mrb_value x); +mrb_value mrb_bint_lshift(mrb_state *mrb, mrb_value x, mrb_int width); +mrb_value mrb_bint_rshift(mrb_state *mrb, mrb_value x, mrb_int width); +mrb_value mrb_bint_to_s(mrb_state *mrb, mrb_value x, mrb_int base); +#ifndef MRB_NO_FLOAT +mrb_float mrb_bint_as_float(mrb_state *mrb, mrb_value x); +#endif +mrb_int mrb_bint_as_int(mrb_state *mrb, mrb_value x); +mrb_int mrb_bint_cmp(mrb_state *mrb, mrb_value x, mrb_value y); +void mrb_gc_free_bint(mrb_state *mrb, struct RBasic *x); +#endif + +#endif /* MRB_USE_BIGINT */ diff --git a/include/mruby/internal.h b/include/mruby/internal.h index b86931ad9..9ce87c8aa 100644 --- a/include/mruby/internal.h +++ b/include/mruby/internal.h @@ -83,6 +83,8 @@ mrb_int mrb_div_int(mrb_state *mrb, mrb_int x, mrb_int y); mrb_value mrb_int_add(mrb_state *mrb, mrb_value x, mrb_value y); mrb_value mrb_int_sub(mrb_state *mrb, mrb_value x, mrb_value y); mrb_value mrb_int_mul(mrb_state *mrb, mrb_value x, mrb_value y); +void mrb_int_zerodiv(mrb_state *mrb); +void mrb_int_overflow(mrb_state *mrb, const char *reason); #ifdef MRB_USE_COMPLEX mrb_value mrb_complex_new(mrb_state *mrb, mrb_float x, mrb_float y); @@ -154,4 +156,36 @@ void mrb_gc_free_iv(mrb_state*, struct RObject*); /* VM */ mrb_int mrb_ci_bidx(mrb_callinfo *ci); +#ifdef MRB_USE_BIGINT +mrb_value mrb_bint_new_int(mrb_state *mrb, mrb_int x); +mrb_value mrb_bint_new_str(mrb_state *mrb, const char *x, mrb_int len, mrb_int base); +mrb_value mrb_as_bint(mrb_state *mrb, mrb_value x); +mrb_value mrb_bint_add(mrb_state *mrb, mrb_value x, mrb_value y); +mrb_value mrb_bint_sub(mrb_state *mrb, mrb_value x, mrb_value y); +mrb_value mrb_bint_mul(mrb_state *mrb, mrb_value x, mrb_value y); +mrb_value mrb_bint_div(mrb_state *mrb, mrb_value x, mrb_value y); +mrb_value mrb_bint_divmod(mrb_state *mrb, mrb_value x, mrb_value y); +mrb_value mrb_bint_add_ii(mrb_state *mrb, mrb_int x, mrb_int y); +mrb_value mrb_bint_sub_ii(mrb_state *mrb, mrb_int x, mrb_int y); +mrb_value mrb_bint_mul_ii(mrb_state *mrb, mrb_int x, mrb_int y); +mrb_value mrb_bint_div_ii(mrb_state *mrb, mrb_int x, mrb_int y); +mrb_value mrb_bint_mod(mrb_state *mrb, mrb_value x, mrb_value y); +mrb_value mrb_bint_rem(mrb_state *mrb, mrb_value x, mrb_value y); +mrb_value mrb_bint_pow(mrb_state *mrb, mrb_value x, mrb_value y); +mrb_value mrb_bint_powm(mrb_state *mrb, mrb_value x, mrb_int y, mrb_value z); +mrb_value mrb_bint_and(mrb_state *mrb, mrb_value x, mrb_value y); +mrb_value mrb_bint_or(mrb_state *mrb, mrb_value x, mrb_value y); +mrb_value mrb_bint_xor(mrb_state *mrb, mrb_value x, mrb_value y); +mrb_value mrb_bint_rev(mrb_state *mrb, mrb_value x); +mrb_value mrb_bint_lshift(mrb_state *mrb, mrb_value x, mrb_int width); +mrb_value mrb_bint_rshift(mrb_state *mrb, mrb_value x, mrb_int width); +mrb_value mrb_bint_to_s(mrb_state *mrb, mrb_value x, mrb_int base); +#ifndef MRB_NO_FLOAT +mrb_float mrb_bint_as_float(mrb_state *mrb, mrb_value x); +#endif +mrb_int mrb_bint_as_int(mrb_state *mrb, mrb_value x); +mrb_int mrb_bint_cmp(mrb_state *mrb, mrb_value x, mrb_value y); +void mrb_gc_free_bint(mrb_state *mrb, struct RBasic *x); +#endif + #endif /* MRUBY_INTERNAL_H */ diff --git a/include/mruby/value.h b/include/mruby/value.h index ad439334b..d3a41cefd 100644 --- a/include/mruby/value.h +++ b/include/mruby/value.h @@ -154,7 +154,8 @@ static const unsigned int IEEE754_INFINITY_BITS_SINGLE = 0x7F800000; f(MRB_TT_ISTRUCT, struct RIStruct, "istruct") \ f(MRB_TT_BREAK, struct RBreak, "break") \ f(MRB_TT_COMPLEX, struct RComplex, "Complex") \ - f(MRB_TT_RATIONAL, struct RRational, "Rational") + f(MRB_TT_RATIONAL, struct RRational, "Rational") \ + f(MRB_TT_BIGINT, struct RBigint, "Integer") enum mrb_vtype { #define MRB_VTYPE_DEFINE(tt, type, name) tt, @@ -300,6 +301,9 @@ struct RCptr { #define mrb_bool(o) (mrb_type(o) != MRB_TT_FALSE) #endif #define mrb_test(o) mrb_bool(o) +#ifndef mrb_bigint_p +#define mrb_bigint_p(o) (mrb_type(o) == MRB_TT_BIGINT) +#endif /** * Returns a float in Ruby. diff --git a/mrbgems/mruby-bigint/README-fgmp.md b/mrbgems/mruby-bigint/README-fgmp.md new file mode 100644 index 000000000..3e5400dd5 --- /dev/null +++ b/mrbgems/mruby-bigint/README-fgmp.md @@ -0,0 +1,154 @@ +# fgmp + +FGMP is a public domain implementation of a subset of the GNU gmp library with the same API. + +WELCOME TO FGMP. + +FGMP is a public domain implementation of a subset of the GNU gmp library +with the same API. + +For instance, you can link the following trivial program with either +this code, or libgmp.a and get the same results. + +``` C +#include +#include "gmp.h" +main() +{ + MP_INT a; MP_INT b; MP_INT c; + + mpz_init_set_ui(&a,1); mpz_init_set_ui(&b,2); mpz_init(&c); + mpz_add(&c,&a,&b); + printf("\n%s\n", mpz_get_str(NULL,10,&c)); +} +``` + +FGMP is really in the public domain. You can do whatever you want with +it. + +I wrote FGMP so that we would all have access to a (truly free) +implementation of this subset of the API of GNU libgmp. I encourage +everyone to distribute this as widely as possible. + +If you need more documentation, I suggest you look at the file +gmp.texi which is included with the GNU gmp library. + +You can send me bug reports, implementations of missing functions, flames +and rants by Email. + +Any submissions of new code to be integrated into fgmp must also be +placed in the public domain (For the particularly dense, you can +release a new fgmp yourself under different licensing terms. This +is a condition for including a submission in a release of FGMP that +I personally prepare). + +Mark Henderson + +# This is the fifth BETA release. 1.0b5 + +I hearby place this file and all of FGMP in the public domain. + +Thanks to Paul Rouse for changes to get fgmp +to work on a 286 MS-DOS compiler, the functions mpz_sqrt and +mpz_sqrtrem, plus other general bug fixes. + +Thanks also to Erick Gallesio for a fix +to mpz_init_set_str + +Define B64 if your "long" type is 64 bits. Otherwise we assume 32 +bit longs. (The 64 bit version hasn't been tested enough) + +``` +Platforms: +Linux 0.99 (gcc) +IBM RS6000/AIX 3.2 (IBM xlc compiler and gcc 2.3) +Sun OS 4.1, Sun 3/4 +DEC Alpha OSF/1 (only lightly tested, 64 bit longs do make a difference, + thanks to DEC for providing access via axposf.pa.dec.com). Define B64 + for this platform +MS-DOS 286 C compiler (see credits above) +``` + +# Some differences between gmp and fgmp + +1. fgmp is considerably slower than gmp +2. fgmp does not implement the following: + all mpq_* + internal mpn_* functions + mpz_perfect_square_p + mpz_inp_raw, mpz_out_raw + mp_set_memory_functions, mpz_out_str, mpz_inp_str +3. fgmp implements the following in addition to the routines in GNU gmp. + `int mpz_jacobi(MP_INT *a, MP_INT *b)` + - finds the jacobi symbol (a/b) +4. mpz_sizeinbase often overestimates the exact value + +5. To convert your gmp based program to fgmp (subject to the +above) + +- recompile your source. Make sure to include the gmp.h file included + with fgmp rather than that included with gmp. (The point is to recompile + all files which include gmp.h) +- link with gmp.o instead of libgmp.a + +Here's a complete sorted list of function implemented in fgmp: + +``` +_mpz_realloc +mpz_abs +mpz_add +mpz_add_ui +mpz_and +mpz_clear +mpz_cmp +mpz_cmp_si +mpz_cmp_ui +mpz_div +mpz_div_2exp +mpz_div_ui +mpz_divmod +mpz_divmod_ui +mpz_fac_ui +mpz_gcd +mpz_gcdext +mpz_get_si +mpz_get_str +mpz_get_ui +mpz_init +mpz_init_set +mpz_init_set_si +mpz_init_set_str +mpz_init_set_ui +mpz_jacobi +mpz_mdiv +mpz_mdiv_ui +mpz_mdivmod +mpz_mdivmod_ui +mpz_mmod +mpz_mmod_ui +mpz_mod +mpz_mod_2exp +mpz_mod_ui +mpz_mul +mpz_mul_2exp +mpz_mul_ui +mpz_neg +mpz_or +mpz_pow_ui +mpz_powm +mpz_powm_ui +mpz_probab_prime_p +mpz_random +mpz_random2 +mpz_set +mpz_set_si +mpz_set_str +mpz_set_ui +mpz_size +mpz_sizeinbase +mpz_sqrt +mpz_sqrtrem +mpz_sub +mpz_sub_ui +mpz_xor +``` diff --git a/mrbgems/mruby-bigint/README.md b/mrbgems/mruby-bigint/README.md new file mode 100644 index 000000000..bf15b7f24 --- /dev/null +++ b/mrbgems/mruby-bigint/README.md @@ -0,0 +1,9 @@ +# Multi-precision Integer extension for mruby + +This extension uses fgmp, which is a public domain implementation of a subset of the GNU gmp library by Mark Henderson +But it's heavily modified to fit with mruby. You can get the original source code from +You can read the original README for fgmp in + +## Implementation + +This mrbgem is only to turn on `MRB_USE_BIGINT` flag. The implementation resides in `include/mruby/bigint.h` and `src/bigint.c`. diff --git a/mrbgems/mruby-bigint/mrbgem.rake b/mrbgems/mruby-bigint/mrbgem.rake new file mode 100644 index 000000000..528a7ee70 --- /dev/null +++ b/mrbgems/mruby-bigint/mrbgem.rake @@ -0,0 +1,6 @@ +MRuby::Gem::Specification.new('mruby-bigint') do |spec| + spec.license = 'MIT' + spec.author = 'mruby developers' + spec.summary = 'Integer class extension to multiple-precision' + spec.build.defines << "MRB_USE_BIGINT" +end diff --git a/mrbgems/mruby-numeric-ext/src/numeric_ext.c b/mrbgems/mruby-numeric-ext/src/numeric_ext.c index 4220f9207..1010d8fa7 100644 --- a/mrbgems/mruby-numeric-ext/src/numeric_ext.c +++ b/mrbgems/mruby-numeric-ext/src/numeric_ext.c @@ -1,9 +1,40 @@ #include #include +#include #include -void mrb_int_zerodiv(mrb_state *mrb); -void mrb_int_overflow(mrb_state *mrb, const char *reason); +#ifdef MRB_USE_BIGINT +#include + +static mrb_value +bint_allbits(mrb_state *mrb, mrb_value x, mrb_value y) +{ + y = mrb_as_bint(mrb, y); + x = mrb_bint_and(mrb, x, y); + if (mrb_bint_cmp(mrb, x, y) == 0) return mrb_true_value(); + return mrb_false_value(); +} + +static mrb_value +bint_anybits(mrb_state *mrb, mrb_value x, mrb_value y) +{ + y = mrb_as_bint(mrb, y); + x = mrb_bint_and(mrb, x, y); + if (mrb_bint_cmp(mrb, x, mrb_fixnum_value(0)) != 0) + return mrb_true_value(); + return mrb_false_value(); +} + +static mrb_value +bint_nobits(mrb_state *mrb, mrb_value x, mrb_value y) +{ + y = mrb_as_bint(mrb, y); + x = mrb_bint_and(mrb, x, y); + if (mrb_bint_cmp(mrb, x, mrb_fixnum_value(0)) == 0) + return mrb_true_value(); + return mrb_false_value(); +} +#endif /* * call-seq: @@ -14,6 +45,9 @@ void mrb_int_overflow(mrb_state *mrb, const char *reason); static mrb_value int_allbits(mrb_state *mrb, mrb_value self) { +#ifdef MRB_USE_BIGINT + return bint_allbits(mrb, self, mrb_get_arg1(mrb)); +#endif mrb_int n, m; mrb_get_args(mrb, "i", &m); @@ -30,6 +64,9 @@ int_allbits(mrb_state *mrb, mrb_value self) static mrb_value int_anybits(mrb_state *mrb, mrb_value self) { +#ifdef MRB_USE_BIGINT + return bint_anybits(mrb, self, mrb_get_arg1(mrb)); +#endif mrb_int n, m; mrb_get_args(mrb, "i", &m); @@ -46,6 +83,9 @@ int_anybits(mrb_state *mrb, mrb_value self) static mrb_value int_nobits(mrb_state *mrb, mrb_value self) { +#ifdef MRB_USE_BIGINT + return bint_nobits(mrb, self, mrb_get_arg1(mrb)); +#endif mrb_int n, m; mrb_get_args(mrb, "i", &m); @@ -53,6 +93,10 @@ int_nobits(mrb_state *mrb, mrb_value self) return mrb_bool_value((n & m) == 0); } +#ifndef MRB_NO_FLOAT +static mrb_value flo_remainder(mrb_state *mrb, mrb_value self); +#endif + /* * call-seq: * num.remainder(numeric) -> real @@ -67,6 +111,14 @@ int_remainder(mrb_state *mrb, mrb_value x) mrb_value y = mrb_get_arg1(mrb); mrb_int a, b; +#ifdef MRB_USE_BIGINT + if (mrb_bigint_p(x)) { + if (mrb_integer_p(y) || mrb_bigint_p(y)) { + return mrb_bint_rem(mrb, x, y); + } + return flo_remainder(mrb, mrb_float_value(mrb, mrb_as_float(mrb, x))); + } +#endif a = mrb_integer(x); if (mrb_integer_p(y)) { b = mrb_integer(y); @@ -77,11 +129,7 @@ int_remainder(mrb_state *mrb, mrb_value x) #ifdef MRB_NO_FLOAT mrb_raise(mrb, E_TYPE_ERROR, "non integer remainder"); #else - mrb_float n = (mrb_float)a; - mrb_float m = mrb_as_float(mrb, y); - - if (isinf(m)) return mrb_float_value(mrb, n); - return mrb_float_value(mrb, n-m*trunc(n/m)); + return flo_remainder(mrb, mrb_float_value(mrb, mrb_as_float(mrb, x))); #endif } @@ -100,14 +148,25 @@ mrb_value mrb_int_pow(mrb_state *mrb, mrb_value x); static mrb_value int_powm(mrb_state *mrb, mrb_value x) { + mrb_value m; mrb_int base, exp, mod, result = 1; if (mrb_get_argc(mrb) == 1) { return mrb_int_pow(mrb, x); } - mrb_get_args(mrb, "ii", &exp, &mod); - if (exp < 0) mrb_raise(mrb, E_TYPE_ERROR, "int.pow(n,m): n must be positive"); - if (mod < 0) mrb_raise(mrb, E_TYPE_ERROR, "int.pow(n,m): m must be positive"); + mrb_get_args(mrb, "io", &exp, &m); + if (exp < 0) mrb_raise(mrb, E_ARGUMENT_ERROR, "int.pow(n,m): n must be positive"); +#ifdef MRB_USE_BIGINT + if (mrb_bigint_p(x)) { + return mrb_bint_powm(mrb, x, exp, m); + } + if (mrb_bigint_p(m)) { + return mrb_bint_powm(mrb, mrb_bint_new_int(mrb, mrb_integer(x)), exp, m); + } +#endif + if (!mrb_integer_p(m)) mrb_raise(mrb, E_TYPE_ERROR, "int.pow(n,m): m must be integer"); + mod = mrb_integer(m); + if (mod < 0) mrb_raise(mrb, E_ARGUMENT_ERROR, "int.pow(n,m): m must be positive"); if (mod == 0) mrb_int_zerodiv(mrb); if (mod == 1) return mrb_fixnum_value(0); base = mrb_integer(x); diff --git a/mrbgems/mruby-os-memsize/src/memsize.c b/mrbgems/mruby-os-memsize/src/memsize.c index d4ade914f..121cbb060 100644 --- a/mrbgems/mruby-os-memsize/src/memsize.c +++ b/mrbgems/mruby-os-memsize/src/memsize.c @@ -128,23 +128,24 @@ os_memsize_of_object(mrb_state* mrb, mrb_value obj) case MRB_TT_INTEGER: if (mrb_immediate_p(obj)) break; - case MRB_TT_RATIONAL: + case MRB_TT_RATIONAL: #if defined(MRB_USE_RATIONAL) #if defined(MRB_INT64) && defined(MRB_32BIT) - size += sizeof(mrb_int)*2; + size += sizeof(mrb_int)*2; #endif - size += mrb_objspace_page_slot_size(); + size += mrb_objspace_page_slot_size(); #endif - break; + break; - case MRB_TT_COMPLEX: + case MRB_TT_COMPLEX: #if defined(MRB_USE_COMPLEX) #if defined(MRB_32BIT) && !defined(MRB_USE_FLOAT32) - size += sizeof(mrb_float)*2; + size += sizeof(mrb_float)*2; #endif - size += mrb_objspace_page_slot_size(); + size += mrb_objspace_page_slot_size(); #endif - break; + break; + case MRB_TT_BIGINT: case MRB_TT_DATA: case MRB_TT_ISTRUCT: size += mrb_objspace_page_slot_size(); diff --git a/src/array.c b/src/array.c index 29ac2f8b3..6d096a61d 100644 --- a/src/array.c +++ b/src/array.c @@ -847,7 +847,7 @@ ary_subseq(mrb_state *mrb, struct RArray *a, mrb_int beg, mrb_int len) return mrb_ary_new_from_values(mrb, len, ARY_PTR(a)+beg); } ary_make_shared(mrb, a); - b = MRB_OBJ_ALLOC(mrb, MRB_TT_ARRAY, mrb->array_class); + b = MRB_OBJ_ALLOC(mrb, MRB_TT_ARRAY, mrb->array_class); b->as.heap.ptr = a->as.heap.ptr + beg; b->as.heap.len = len; b->as.heap.aux.shared = a->as.heap.aux.shared; diff --git a/src/bigint.c b/src/bigint.c new file mode 100644 index 000000000..136c0c78b --- /dev/null +++ b/src/bigint.c @@ -0,0 +1,1410 @@ +/** +** @file mruby/bigint.h - Multi-precision, Integer +** +** See Copyright Notice in mruby.h +*/ +#ifdef MRB_USE_BIGINT + +#include +#include +#include +#include +#include +#include +#include +#include + +static void +mpz_init(mrb_state *mrb, mpz_t *s) +{ + s->p = (mp_limb*)mrb_malloc(mrb, sizeof(mp_limb)*2); + (s->p)[0] = 0; + (s->p)[1] = 0; + s->sn=0; + s->sz=2; +} + +#if 0 +static void +mpz_init_set(mrb_state *mrb, mpz_t *s, mpz_t *t) +{ + int i; + s->p = (mp_limb*)mrb_malloc(mrb, sizeof(mp_limb) * t->sz); + for (i=0;i < t->sz ; i++) + (s->p)[i] = (t->p)[i]; + + s->sn = t->sn; + s->sz = t->sz; +} +#endif + +static void +mpz_set_int(mrb_state *mrb, mpz_t *y, long v) +{ + for (size_t i=1; isz; i++) + y->p[i] = 0; + if (v < 0) { + y->sn = -1; + y->p[0] = (-v) & LMAX; + y->p[1] = ((-v) & LC) >> DIGITBITS; + } + else if (v > 0) { + y->sn = 1; + y->p[0] = v & LMAX; + y->p[1] = (v & LC) >> DIGITBITS; + } + else { + y->sn=0; + y->p[0] = 0; + y->p[1] = 0; + } +} + +static void +mpz_init_set_int(mrb_state *mrb, mpz_t *y, mrb_int v) +{ + size_t u; + + y->p = (mp_limb*)mrb_malloc(mrb, sizeof(mp_limb)*2); + if (v < 0) { + y->sn = -1; + u = -v; + } + else if (v > 0) { + y->sn = 1; + u = v; + } + else { + y->sn=0; + u = 0; + } + y->p[0] = u & LMAX; + y->p[1] = (u & LC) >> DIGITBITS; + y -> sz = 2; +} + +static void +mpz_clear(mrb_state *mrb, mpz_t *s) +{ + if (s->p) + mrb_free(mrb, s->p); + s->p=NULL; + s->sn=0; + s->sz=0; +} + +static void +mpz_realloc(mrb_state *mrb, mpz_t *x, size_t size) +{ + if (size > 1 && x->sz < size) { + x->p=(mp_limb*)mrb_realloc(mrb,x->p,size * sizeof(mp_limb)); + for (size_t i=x->sz; ip)[i] = 0; + x->sz = size; + } +} + +static size_t +digits(mpz_t *x) +{ + int i; + for (i = (x->sz) - 1; i>=0 && (x->p)[i] == 0 ; i--) + ; + return i+1; +} + +/* y = x */ +static void +mpz_set(mrb_state *mrb, mpz_t *y, mpz_t *x) +{ + size_t i,k = x->sz; + if (y->sz < k) { + k=digits(x); + mpz_realloc(mrb, y, (size_t)k); + } + if (y->sz > x->sz) { + mpz_clear(mrb, y); + mpz_init(mrb, y); + mpz_realloc(mrb, y, (size_t)(x->sz)); + } + + for (i=0;i < k; i++) + (y->p)[i] = (x->p)[i]; + + for (;isz;i++) + (y->p)[i] = 0; + + y->sn = x->sn; +} + +/* z = x + y, without regard for sign */ +static void +uadd(mrb_state *mrb, mpz_t *z, mpz_t *x, mpz_t *y) +{ + mp_limb c; + size_t i; + mpz_t *t; + + if (y->sz < x->sz) { + t=x; x=y; y=t; + } + + /* now y->sz >= x->sz */ + + mpz_realloc(mrb, z, (size_t)((y->sz)+1)); + + c=0; + for (i=0; isz; i++) { + if (( z->p[i] = y->p[i] + x->p[i] + c ) & CMASK) { + c=1; + (z->p[i]) &=LMAX; + } + else + c=0; + } + for (;isz; i++) { + if ((z->p[i] = (y->p[i] + c)) & CMASK) + z->p[i]=0; + else + c=0; + } + (z->p)[y->sz]=c; +} + +/* z = y - x, ignoring sign */ +/* precondition: abs(y) >= abs(x) */ +static void +usub(mrb_state *mrb, mpz_t *z, mpz_t *y, mpz_t *x) +{ + mp_limb b,m; + mpz_realloc(mrb, z, (size_t)(y->sz)); + b=0; + for (size_t i=0;isz;i++) { + m=((y->p)[i]-b)-dg(x,i); + if (m < 0) { + b = 1; + m = LMAX + 1 + m; + } + else + b = 0; + z->p[i] = m; + } +} + +/* compare abs(x) and abs(y) */ +static int +ucmp(mpz_t *y, mpz_t *x) +{ + int i; + for (i=imax(x->sz,y->sz)-1;i>=0;i--) { + if (dg(y,i) < dg(x,i)) + return (-1); + else if (dg(y,i) > dg(x,i)) + return 1; + } + return 0; +} + +static int +uzero(mpz_t *x) +{ + for (size_t i=0; i < x->sz; i++) + if ((x->p)[i] != 0) + return 0; + return 1; +} + +static void +zero(mpz_t *x) +{ + x->sn=0; + for (size_t i=0;isz;i++) + (x->p)[i] = 0; +} + +/* z = x + y */ +static void +mpz_add(mrb_state *mrb, mpz_t *zz, mpz_t *x, mpz_t *y) +{ + int mg; + mpz_t z; + if (x->sn == 0) { + mpz_set(mrb, zz, y); + return; + } + if (y->sn == 0) { + mpz_set(mrb, zz, x); + return; + } + mpz_init(mrb, &z); + + if (x->sn > 0 && y->sn > 0) { + uadd(mrb, &z, x, y); + z.sn = 1; + } + else if (x->sn < 0 && y->sn < 0) { + uadd(mrb, &z, x, y); + z.sn = -1; + } + else { + /* signs differ */ + if ((mg = ucmp(x,y)) == 0) { + zero(&z); + } + else if (mg > 0) { /* abs(y) < abs(x) */ + usub(mrb, &z, x, y); + z.sn = (x->sn > 0 && y->sn < 0) ? 1 : (-1); + } + else { /* abs(y) > abs(x) */ + usub(mrb, &z, y, x); + z.sn = (x->sn < 0 && y->sn > 0) ? 1 : (-1); + } + } + mpz_set(mrb,zz,&z); + mpz_clear(mrb,&z); +} + +/* z = x - y -- just use mpz_add - I'm lazy */ +static void +mpz_sub(mrb_state *mrb, mpz_t *z, mpz_t *x, mpz_t *y) +{ + mpz_t u; + mpz_init(mrb, &u); + mpz_set(mrb, &u, y); + u.sn = -(u.sn); + mpz_add(mrb, z, x, &u); + mpz_clear(mrb, &u); +} + +/* x = y - n */ +static void +mpz_sub_int(mrb_state *mrb, mpz_t *x, mpz_t *y, mrb_int n) +{ + mpz_t z; + mpz_init_set_int(mrb, &z, n); + mpz_sub(mrb, x, y, &z); + mpz_clear(mrb, &z); +} + +/* w = u * v */ +static void +mpz_mul(mrb_state *mrb, mpz_t *ww, mpz_t *u, mpz_t *v) +{ + size_t i,j; + mp_limb t0,t1,t2,t3; + mp_limb cc; + mpz_t w; + + mpz_init(mrb, &w); + mpz_realloc(mrb, &w, (size_t)(u->sz + v->sz)); + for (j=0; j < 2*u->sz; j++) { + cc = (mp_limb)0; + t3 = hd(u,j); + for (i=0; i < 2*v->sz; i++) { + t0 = t3 * hd(v,i); + t1 = HIGH(t0); t0 = LOW(t0); + if ((i+j)%2) + t2 = HIGH(w.p[(i+j)/2]); + else + t2 = LOW(w.p[(i+j)/2]); + t2 += cc; + if (t2 & HCMASK) { + cc = 1; t2&=HLMAX; + } + else + cc = 0; + t2 += t0; + if (t2 & HCMASK) { + cc++ ; t2&=HLMAX; + } + cc+=t1; + if ((i+j)%2) + w.p[(i+j)/2] = LOW(w.p[(i+j)/2]) | + (t2 << HALFDIGITBITS); + else + w.p[(i+j)/2] = (HIGH(w.p[(i+j)/2]) << HALFDIGITBITS) | t2; + } + if (cc) { + if ((j+i)%2) + w.p[(i+j)/2] += cc << HALFDIGITBITS; + else + w.p[(i+j)/2] += cc; + } + } + w.sn = (u->sn) * (v->sn); + mpz_set(mrb, ww, &w); + mpz_clear(mrb, &w); +} + +static void +mpz_mul_int(mrb_state *mrb, mpz_t *x, mpz_t *y, mrb_int n) +{ + mpz_t z; + mpz_init_set_int(mrb,&z,n); + mpz_mul(mrb,x,y,&z); + mpz_clear(mrb,&z); +} + +/* number of leading zero bits in digit */ +static int +lzb(mp_limb a) +{ + mp_limb i; size_t j=0; + + for (i = ((mp_limb)1 << (DIGITBITS-1)); i && !(a&i) ; j++,i>>=1) + ; + return j; +} + +/* c1 = a>>n */ +/* n must be < DIGITBITS */ +static void +urshift(mrb_state *mrb, mpz_t *c1, mpz_t *a, int n) +{ + mp_limb cc = 0; + mrb_assert(n < DIGITBITS); + if (n == 0) + mpz_set(mrb, c1, a); + else { + mpz_t c; size_t i; + mp_limb rm = (((mp_limb)1<sz)); + for (i=a->sz-1;; i--) { + c.p[i] = ((a->p[i] >> n) | cc) & LMAX; + cc = (a->p[i] & rm) << (DIGITBITS - n); + if (i == 0) break; + } + mpz_set(mrb,c1,&c); + mpz_clear(mrb,&c); + } +} + +/* c1 = a<sz + 1)); + for (i=0; isz; i++) { + c.p[i] = (((mrb_uint)a->p[i] << n) | cc) & LMAX; + cc = (a->p[i] & rm) >> (DIGITBITS -n); + } + c.p[i] = cc; + mpz_set(mrb,c1,&c); + mpz_clear(mrb,&c); + } +} + +/* internal routine to compute x/y and x%y ignoring signs */ +static void +udiv(mrb_state *mrb, mpz_t *qq, mpz_t *rr, mpz_t *xx, mpz_t *yy) +{ + mpz_t q, x, y, r; + int ns,f,xd,yd,i,j,ccc=0; + mp_limb zz,z,qhat,b,u,m; + + if (uzero(yy)) + return; + mpz_init(mrb,&q); mpz_init(mrb,&x);mpz_init(mrb,&y);mpz_init(mrb,&r); + mpz_realloc(mrb,&x,(size_t)((xx->sz)+1)); + yd = digits(yy); + ns = lzb(yy->p[yd-1]); + ulshift(mrb,&x,xx,ns); + ulshift(mrb,&y,yy,ns); + xd = digits(&x); + mpz_realloc(mrb,&q,(size_t)xd); + xd*=2; yd*=2; + z = hd(&y,yd-1); + for (j=(xd-yd);j>=0;j--) { + if (z == LMAX) + qhat = hd(&x,j+yd); + else { + qhat = ((hd(&x,j+yd)<< HALFDIGITBITS) + hd(&x,j+yd-1)) / (z+1); + } + b = 0; zz=0; + if (qhat) { + for (i=0; i < yd; i++) { + zz = qhat * hd(&y,i); + u = hd(&x,i+j); + u-=b; + if (u<0) { + b=1; u+=HLMAX+1; + } + else + b=0; + u-=LOW(zz); + if (u < 0) { + b++; + u+=HLMAX+1; + } + b+=HIGH(zz); + if ((i+j)%2) + x.p[(i+j)/2] = LOW(x.p[(i+j)/2]) | (u << HALFDIGITBITS); + else + x.p[(i+j)/2] = (HIGH(x.p[(i+j)/2]) << HALFDIGITBITS) | u; + } + if (b) { + if ((j+i)%2) + x.p[(i+j)/2] -= b << HALFDIGITBITS; + else + x.p[(i+j)/2] -= b; + } + } + for(;;zz++) { + f=1; + if (!hd(&x,j+yd)) { + for(i=yd-1; i>=0; i--) { + if (hd(&x,j+i) > hd(&y,i)) { + f=1; + break; + } + if (hd(&x,j+i) < hd(&y,i)) { + f=0; + break; + } + } + } + if (!f) + break; + qhat++; + ccc++; + b=0; + for (i=0;isz)); + zero(&r); + urshift(mrb,&r,&x,ns); + mpz_set(mrb,rr,&r); + mpz_set(mrb,qq,&q); + mpz_clear(mrb,&x); mpz_clear(mrb,&y); + mpz_clear(mrb,&q); mpz_clear(mrb,&r); +} + +static void +mpz_mdiv(mrb_state *mrb, mpz_t *q, mpz_t *x, mpz_t *y) +{ + mpz_t r; + short sn1 = x->sn, sn2 = y->sn, qsign; + mpz_init(mrb,&r); + udiv(mrb,q,&r,x,y); + qsign = q->sn = sn1*sn2; + if (uzero(q)) + q->sn = 0; + /* now if r != 0 and q < 0 we need to round q towards -inf */ + if (!uzero(&r) && qsign < 0) + mpz_sub_int(mrb,q,q,1); + mpz_clear(mrb,&r); +} + +static void +mpz_mmod(mrb_state *mrb, mpz_t *r, mpz_t *x, mpz_t *y) +{ + mpz_t q; + short sn1 = x->sn, sn2 = y->sn; + mpz_init(mrb, &q); + if (sn1 == 0) { + zero(r); + return; + } + udiv(mrb,&q,r,x,y); + if (uzero(r)) { + r->sn = 0; + return; + } + q.sn = sn1*sn2; + if (q.sn > 0) + r->sn = sn1; + else if (sn1 < 0 && sn2 > 0) { + r->sn = 1; + mpz_sub(mrb,r,y,r); + } + else { + r->sn = 1; + mpz_add(mrb,r,y,r); + } +} + +static void +mpz_mdivmod(mrb_state *mrb, mpz_t *q, mpz_t *r, mpz_t *x, mpz_t *y) +{ + short sn1 = x->sn, sn2 = y->sn, qsign; + if (sn1 == 0) { + zero(q); + zero(r); + return; + } + udiv(mrb,q,r,x,y); + qsign = q->sn = sn1*sn2; + if (uzero(r)) { + /* q != 0, since q=r=0 would mean x=0, which was tested above */ + r->sn = 0; + return; + } + if (q->sn > 0) + r->sn = sn1; + else if (sn1 < 0 && sn2 > 0) { + r->sn = 1; + mpz_sub(mrb,r,y,r); + } + else { + r->sn = 1; + mpz_add(mrb,r,y,r); + } + if (uzero(q)) + q->sn = 0; + /* now if r != 0 and q < 0 we need to round q towards -inf */ + if (!uzero(r) && qsign < 0) + mpz_sub_int(mrb,q,q,1); +} + +static void +mpz_mod(mrb_state *mrb, mpz_t *r, mpz_t *x, mpz_t *y) +{ + mpz_t q; + short sn = x->sn; + mpz_init(mrb, &q); + if (x->sn == 0) { + zero(r); + return; + } + udiv(mrb,&q,r,x,y); + r->sn = sn; + if (uzero(r)) + r->sn = 0; + mpz_clear(mrb,&q); +} + +static mrb_int +mpz_cmp(mrb_state *mrb, mpz_t *x, mpz_t *y) +{ + int abscmp; + if (x->sn < 0 && y->sn > 0) + return (-1); + if (x->sn > 0 && y->sn < 0) + return 1; + abscmp=ucmp(x,y); + if (x->sn >=0 && y->sn >=0) + return abscmp; + return (-abscmp); // if (x->sn <=0 && y->sn <=0) +} + +/* 2<=base <=36 - this overestimates the optimal value, which is OK */ +static int +mpz_sizeinbase(mpz_t *x, int base) +{ + int i,j; + int bits = digits(x) * DIGITBITS; + mrb_assert(2 <= base && base <= 36); + for (j=0,i=1; i<=base;i*=2,j++) + ; + return ((bits)/(j-1)+1); +} + +static int +mpz_init_set_str(mrb_state *mrb, mpz_t *x, const char *s, mrb_int len, mrb_int base) +{ + mrb_int i; + mrb_int retval = 0; + mpz_t t,m,bb; + short sn; + unsigned int k; + mpz_init(mrb,x); + mpz_init_set_int(mrb,&m,1); + mpz_init(mrb,&t); + zero(x); + if (*s == '-') { + sn = -1; s++; + } + else if (base < 0) { /* trick: negative if base < 0 */ + sn = -1; base = -base; + } + else + sn = 1; + mpz_init_set_int(mrb,&bb, base); + for (i = len-1; i>=0; i--) { + if (s[i]=='_') continue; + if (s[i] >= '0' && s[i] <= '9') + k = (unsigned int)s[i] - (unsigned int)'0'; + else if (s[i] >= 'A' && s[i] <= 'Z') + k = (unsigned int)s[i] - (unsigned int)'A'+10; + else if (s[i] >= 'a' && s[i] <= 'z') + k = (unsigned int)s[i] - (unsigned int)'a'+10; + else { + retval = (-1); + break; + } + if (k >= base) { + retval = (-1); + break; + } + mpz_mul_int(mrb,&t,&m,(mrb_int)k); + mpz_add(mrb,x,x,&t); + mpz_mul(mrb,&m,&m,&bb); + } + if (x->sn) + x->sn = sn; + mpz_clear(mrb,&m); + mpz_clear(mrb,&bb); + mpz_clear(mrb,&t); + return retval; +} + +static char* +mpz_get_str(mrb_state *mrb, char *s, int sz, int base, mpz_t *x) +{ + mpz_t xx,q,r,bb; + char *p,*t,*ps; + mp_limb d; + mrb_assert(2 <= base && base <= 36); + if (uzero(x)) { + *s='0'; + *(s+1)='\0'; + return s; + } + t = (char*)mrb_malloc(mrb, sz+2); + mpz_init(mrb,&xx); mpz_init(mrb,&q); mpz_init(mrb,&r); + mpz_init_set_int(mrb,&bb,base); + mpz_set(mrb,&xx,x); + ps = s; + if (x->sn < 0) { + *ps++= '-'; + xx.sn = 1; + } + p = t; + while (!uzero(&xx)) { + udiv(mrb,&xx,&r,&xx,&bb); + d = r.p[0]; + if (d < 10) + *p++ = (char)(r.p[0] + '0'); + else + *p++ = (char)(r.p[0] + -10 + 'a'); + } + + p--; + for (;p>=t;p--,ps++) + *ps = *p; + *ps='\0'; + + mrb_free(mrb,t); + mpz_clear(mrb,&xx); mpz_clear(mrb,&q); mpz_clear(mrb,&r); mpz_clear(mrb,&bb); + return s; +} + +static int +mpz_get_int(mpz_t *y, mrb_int *v) +{ + mrb_int i; + + if (y->sn == 0) { + i = 0; + } + else if (y->sz > 2 || y->p[1] > 1) { + return FALSE; + } + else { + i = (y->sn * (y->p[0] | (y->p[1] & 1) << DIGITBITS)); + } + *v = i; + return TRUE; +} + +static void +mpz_mul_2exp(mrb_state *mrb, mpz_t *z, mpz_t *x, mrb_int e) +{ + short sn = x->sn; + if (e==0) + mpz_set(mrb,z,x); + else { + size_t i; + long digs = (e / DIGITBITS); + size_t bs = (e % (DIGITBITS)); + mpz_t y; + + mpz_init(mrb, &y); + mpz_realloc(mrb, &y,(size_t)((x->sz)+digs)); + for (i=digs;i<((x->sz) + digs);i++) + (y.p)[i] = (x->p)[i - digs]; + if (bs) { + ulshift(mrb,z,&y,bs); + } + else { + mpz_set(mrb,z,&y); + } + z->sn = sn; + mpz_clear(mrb,&y); + } +} + +static void +mpz_div_2exp(mrb_state *mrb, mpz_t *z, mpz_t *x, mrb_int e) +{ + short sn = x->sn; + if (e==0) + mpz_set(mrb,z,x); + else { + size_t i; + long digs = (e / DIGITBITS); + size_t bs = (e % (DIGITBITS)); + mpz_t y; + + mpz_init(mrb,&y); + mpz_realloc(mrb,&y,(size_t)((x->sz) - digs)); + for (i=0; i < (x->sz - digs); i++) + (y.p)[i] = (x->p)[i+digs]; + if (bs) { + urshift(mrb,z,&y,bs); + } + else { + mpz_set(mrb,z,&y); + } + if (uzero(z)) + z->sn = 0; + else + z->sn = sn; + mpz_clear(mrb,&y); + } +} + +static void +mpz_neg(mrb_state *mrb, mpz_t *x, mpz_t *y) +{ + if (x!=y) + mpz_set(mrb,x,y); + x->sn = -(y->sn); +} + +static void +mpz_and(mrb_state *mrb, mpz_t *z, mpz_t *x, mpz_t *y) /* not the most efficient way to do this */ +{ + size_t i,sz; + sz = imax(x->sz, y->sz); + mpz_realloc(mrb,z,(size_t)sz); + for (i=0; i < sz; i++) + (z->p)[i] = dg(x,i) & dg(y,i); + if (x->sn < 0 && y->sn < 0) + z->sn = (-1); + else + z->sn = 1; + if (uzero(z)) + z->sn = 0; +} + +static void +mpz_or(mrb_state *mrb, mpz_t *z, mpz_t *x, mpz_t *y) /* not the most efficient way to do this */ +{ + size_t i,sz; + sz = imax(x->sz, y->sz); + mpz_realloc(mrb,z,(size_t)sz); + for (i=0; i < sz; i++) + (z->p)[i] = dg(x,i) | dg(y,i); + if (x->sn < 0 || y->sn < 0) + z->sn = (-1); + else + z->sn = 1; + if (uzero(z)) + z->sn = 0; +} + +static void +mpz_xor(mrb_state *mrb, mpz_t *z, mpz_t *x, mpz_t *y) /* not the most efficient way to do this */ +{ + size_t i,sz; + sz = imax(x->sz, y->sz); + mpz_realloc(mrb,z,(size_t)sz); + for (i=0; i < sz; i++) + (z->p)[i] = dg(x,i) ^ dg(y,i); + if ((x->sn <= 0 && y->sn > 0) || (x->sn > 0 && y->sn <=0)) + z->sn = (-1); + else + z->sn = 1; + if (uzero(z)) + z->sn = 0; +} + +static void +mpz_pow(mrb_state *mrb, mpz_t *zz, mpz_t *x, mrb_int e) +{ + mpz_t t; + size_t mask = (1UL<< (LONGBITS-1)); + + if (e==0) { + mpz_set_int(mrb, zz, 1L); + return; + } + + mpz_init(mrb,&t); + mpz_set(mrb,&t,x); + for (;!(mask &e); mask>>=1) + ; + mask>>=1; + for (;mask!=0; mask>>=1) { + mpz_mul(mrb,&t,&t,&t); + if (e & mask) + mpz_mul(mrb,&t,&t,x); + } + mpz_set(mrb,zz,&t); + mpz_clear(mrb,&t); +} + +#define lowdigit(x) (((x)->p)[0]) + +struct is { + mp_limb v; + struct is *next; +}; + +static void +push(mrb_state *mrb, mp_limb i, struct is **sp) +{ + struct is *tmp; + tmp = *sp; + *sp = (struct is*)mrb_malloc(mrb, sizeof(struct is)); + (*sp)->v = i; + (*sp)->next=tmp; +} + +static mp_limb +pop(mrb_state *mrb, struct is **sp) +{ + struct is *tmp; + mp_limb i; + if (!(*sp)) + return (-1); + tmp = *sp; + *sp = (*sp)->next; + i = tmp->v; + tmp->v = 0; + mrb_free(mrb,tmp); + return i; +} + +static void +mpz_powm(mrb_state *mrb, mpz_t *zz, mpz_t *x, mrb_int ex, mpz_t *n) +{ + mpz_t t, e; + struct is *stack = NULL; + int k,i; + + if (ex == 0) { + mpz_set_int(mrb,zz,1); + return; + } + + if (ex < 0) { + return; + } + mpz_init(mrb,&e); + mpz_init(mrb,&t); + mpz_set_int(mrb,&e,ex); + + for (k=0;!uzero(&e);k++,mpz_div_2exp(mrb,&e,&e,1)) + push(mrb,lowdigit(&e) & 1,&stack); + k--; + i=pop(mrb,&stack); + + mpz_mod(mrb,&t,x,n); /* t=x%n */ + + for (i=k-1;i>=0;i--) { + mpz_mul(mrb,&t,&t,&t); + mpz_mod(mrb,&t,&t,n); + if (pop(mrb,&stack)) { + mpz_mul(mrb,&t,&t,x); + mpz_mod(mrb,&t,&t,n); + } + } + mpz_set(mrb,zz,&t); + mpz_clear(mrb,&t); + mpz_clear(mrb,&e); +} + +/* --- mruby functions --- */ +static struct RBigint* +bint_new(mrb_state *mrb) +{ + struct RBigint *b = MRB_OBJ_ALLOC(mrb, MRB_TT_BIGINT, mrb->integer_class); + mpz_init(mrb, &b->mp); + return b; +} + +static struct RBigint* +bint_new_int(mrb_state *mrb, mrb_int x) +{ + struct RBigint *b = MRB_OBJ_ALLOC(mrb, MRB_TT_BIGINT, mrb->integer_class); + mpz_init_set_int(mrb, &b->mp, x); + return b; +} + +mrb_value +mrb_bint_new(mrb_state *mrb) +{ + struct RBigint *b = bint_new(mrb); + return mrb_obj_value(b); +} + +mrb_value +mrb_bint_new_int(mrb_state *mrb, mrb_int x) +{ + struct RBigint *b = bint_new_int(mrb, x); + return mrb_obj_value(b); +} + +mrb_value +mrb_bint_new_str(mrb_state *mrb, const char *x, mrb_int len, mrb_int base) +{ + struct RBigint *b = MRB_OBJ_ALLOC(mrb, MRB_TT_BIGINT, mrb->integer_class); + mrb_assert(2 <= iabs(base) && iabs(base) <= 36); + mpz_init_set_str(mrb, &b->mp, x, len, base); + return mrb_obj_value(b); +} + +static mrb_value +bint_norm(mrb_state *mrb, struct RBigint *b) +{ + mrb_int i; + + if (mpz_get_int(&b->mp, &i)) { + return mrb_int_value(mrb, i); + } + return mrb_obj_value(b); +} + +void +mrb_gc_free_bint(mrb_state *mrb, struct RBasic *x) +{ + struct RBigint *b = (struct RBigint*)x; + mpz_clear(mrb, &b->mp); +} + +#ifndef MRB_NO_FLOAT +mrb_float +mrb_bint_as_float(mrb_state *mrb, mrb_value self) +{ + struct RBigint *b = RBIGINT(self); + mpz_t *i = &b->mp; + mp_limb *d = i->p + i->sz; + mrb_float val = 0; + + while (d-- > i->p) { + val = val * (LMAX+1) + *d; + } + + if (i->sn < 0) { + val = -val; + } + return val; +} +#endif + +mrb_value +mrb_as_bint(mrb_state *mrb, mrb_value x) +{ + if (mrb_bigint_p(x)) return x; + return mrb_bint_new_int(mrb, mrb_as_int(mrb, x)); +} + +mrb_int +mrb_bint_as_int(mrb_state *mrb, mrb_value x) +{ + struct RBigint *b = RBIGINT(x); + mrb_int i; + + if (!mpz_get_int(&b->mp, &i)) { + mrb_raise(mrb, E_RANGE_ERROR, "integer overflow"); + } + return i; +} + +mrb_value +mrb_bint_add(mrb_state *mrb, mrb_value x, mrb_value y) +{ +#ifndef MRB_NO_FLOAT + if (mrb_float_p(y)) { + mrb_float v1 = mrb_bint_as_float(mrb, x); + mrb_float v2 = mrb_float(y); + return mrb_float_value(mrb,v1+v2); + } +#endif + y = mrb_as_bint(mrb, y); + struct RBigint *b = RBIGINT(x); + struct RBigint *b2 = RBIGINT(y); + struct RBigint *b3 = bint_new(mrb); + mpz_add(mrb, &b3->mp, &b->mp, &b2->mp); + return bint_norm(mrb, b3); +} + +mrb_value +mrb_bint_sub(mrb_state *mrb, mrb_value x, mrb_value y) +{ +#ifndef MRB_NO_FLOAT + if (mrb_float_p(y)) { + mrb_float v1 = mrb_bint_as_float(mrb, x); + mrb_float v2 = mrb_float(y); + return mrb_float_value(mrb,v1-v2); + } +#endif + y = mrb_as_bint(mrb, y); + struct RBigint *b = RBIGINT(x); + struct RBigint *b2 = RBIGINT(y); + struct RBigint *b3 = bint_new(mrb); + mpz_sub(mrb, &b3->mp, &b->mp, &b2->mp); + return bint_norm(mrb, b3); +} + +mrb_value +mrb_bint_mul(mrb_state *mrb, mrb_value x, mrb_value y) +{ +#ifndef MRB_NO_FLOAT + if (mrb_float_p(y)) { + mrb_float v1 = mrb_bint_as_float(mrb, x); + mrb_float v2 = mrb_float(y); + return mrb_float_value(mrb,v1*v2); + } +#endif + y = mrb_as_bint(mrb, y); + struct RBigint *b = RBIGINT(x); + struct RBigint *b2 = RBIGINT(y); + struct RBigint *b3 = bint_new(mrb); + mpz_mul(mrb, &b3->mp, &b->mp, &b2->mp); + return bint_norm(mrb, b3); +} + +mrb_value +mrb_bint_div(mrb_state *mrb, mrb_value x, mrb_value y) +{ +#ifndef MRB_NO_FLOAT + if (mrb_float_p(y)) { + mrb_float v1 = mrb_bint_as_float(mrb, x); + mrb_float v2 = mrb_float(y); + return mrb_float_value(mrb,v1*v2); + } +#endif + y = mrb_as_bint(mrb, y); + struct RBigint *b = RBIGINT(x); + struct RBigint *b2 = RBIGINT(y); + struct RBigint *b3 = bint_new(mrb); + mpz_mdiv(mrb, &b3->mp, &b->mp, &b2->mp); + return bint_norm(mrb, b3); +} + +mrb_value +mrb_bint_add_ii(mrb_state *mrb, mrb_int x, mrb_int y) +{ + struct RBigint *b = bint_new(mrb); + mpz_t z1, z2; + + mpz_init_set_int(mrb,&z1,x); + mpz_init_set_int(mrb,&z2,y); + mpz_add(mrb,&b->mp,&z1,&z2); + mpz_clear(mrb,&z1); + mpz_clear(mrb,&z2); + return mrb_obj_value(b); +} + +mrb_value +mrb_bint_sub_ii(mrb_state *mrb, mrb_int x, mrb_int y) +{ + struct RBigint *b = bint_new(mrb); + mpz_t z1, z2; + + mpz_init_set_int(mrb,&z1,x); + mpz_init_set_int(mrb,&z2,y); + mpz_sub(mrb,&b->mp,&z1,&z2); + mpz_clear(mrb,&z1); + mpz_clear(mrb,&z2); + return mrb_obj_value(b); +} + +mrb_value +mrb_bint_mul_ii(mrb_state *mrb, mrb_int x, mrb_int y) +{ + struct RBigint *b = bint_new(mrb); + mpz_t z1, z2; + + mpz_init_set_int(mrb,&z1,x); + mpz_init_set_int(mrb,&z2,y); + mpz_mul(mrb,&b->mp,&z1,&z2); + mpz_clear(mrb,&z1); + mpz_clear(mrb,&z2); + return mrb_obj_value(b); +} + +mrb_value +mrb_bint_div_ii(mrb_state *mrb, mrb_int x, mrb_int y) +{ + struct RBigint *b = bint_new(mrb); + mpz_t z1, z2; + + mpz_init_set_int(mrb,&z1,x); + mpz_init_set_int(mrb,&z2,y); + mpz_mdiv(mrb,&b->mp,&z1,&z2); + mpz_clear(mrb,&z1); + mpz_clear(mrb,&z2); + return mrb_obj_value(b); +} + +mrb_value +mrb_bint_idiv(mrb_state *mrb, mrb_value x, mrb_value y) +{ + return mrb_nil_value(); +} + +mrb_value +mrb_bint_mod(mrb_state *mrb, mrb_value x, mrb_value y) +{ +#ifndef MRB_NO_FLOAT + if (mrb_float_p(y)) { + mrb_float v1 = mrb_bint_as_float(mrb, x); + mrb_float v2 = mrb_float(y); + return mrb_float_value(mrb,fmod(v1,v2)); + } +#endif + y = mrb_as_bint(mrb, y); + struct RBigint *b = RBIGINT(x); + struct RBigint *b2 = RBIGINT(y); + struct RBigint *b3 = bint_new(mrb); + mpz_mmod(mrb, &b3->mp, &b->mp, &b2->mp); + return bint_norm(mrb, b3); +} + +mrb_value +mrb_bint_rem(mrb_state *mrb, mrb_value x, mrb_value y) +{ + /* called from mrbgems/mruby-numeric-ext/src/numeric_ext.c */ + /* y should not be float */ + struct RBigint *b = RBIGINT(x); + struct RBigint *b2 = RBIGINT(y); + struct RBigint *b3 = bint_new(mrb); + y = mrb_as_bint(mrb, y); + mpz_mod(mrb, &b3->mp, &b->mp, &b2->mp); + return bint_norm(mrb, b3); +} + +mrb_value +mrb_bint_divmod(mrb_state *mrb, mrb_value x, mrb_value y) +{ + /* called from src/numeric.c */ + /* y should not be float */ + y = mrb_as_bint(mrb, y); + struct RBigint *b = RBIGINT(x); + struct RBigint *b2 = RBIGINT(y); + struct RBigint *b3 = bint_new(mrb); + struct RBigint *b4 = bint_new(mrb); + mpz_mdivmod(mrb, &b3->mp, &b4->mp, &b->mp, &b2->mp); + x = bint_norm(mrb, b3); + y = bint_norm(mrb, b4); + return mrb_assoc_new(mrb, x, y); +} + +mrb_int +mrb_bint_cmp(mrb_state *mrb, mrb_value x, mrb_value y) +{ +#ifndef MRB_NO_FLOAT + if (mrb_float_p(y)) { + mrb_float v1 = mrb_bint_as_float(mrb, x); + mrb_float v2 = mrb_float(y); + if (v1 == v2) return 0; + if (v1 > v2) return 1; + return -1; + } +#endif + if (!mrb_bigint_p(y)) { + if (!mrb_integer_p(y)) return -2; /* type mismatch */ + y = mrb_bint_new_int(mrb, mrb_integer(y)); + } + struct RBigint *b = RBIGINT(x); + struct RBigint *b2 = RBIGINT(y); + return mpz_cmp(mrb, &b->mp, &b2->mp); +} + +mrb_value +mrb_bint_pow(mrb_state *mrb, mrb_value x, mrb_value y) +{ + struct RBigint *b = RBIGINT(x); + switch (mrb_type(y)) { + case MRB_TT_INTEGER: + { + struct RBigint *b3 = bint_new(mrb); + mpz_pow(mrb, &b3->mp, &b->mp, mrb_integer(y)); + return mrb_obj_value(b3); + } + case MRB_TT_BIGINT: + mrb_raise(mrb, E_TYPE_ERROR, "too big power"); + default: + mrb_raisef(mrb, E_TYPE_ERROR, "%v cannot be convert to integer", y); + } + return mrb_nil_value(); +} + +mrb_value +mrb_bint_powm(mrb_state *mrb, mrb_value x, mrb_int exp, mrb_value mod) +{ + struct RBigint *b = RBIGINT(x); + switch (mrb_type(mod)) { + case MRB_TT_INTEGER: + { + struct RBigint *b2 = bint_new(mrb); + struct RBigint *b3 = bint_new_int(mrb, mrb_integer(mod)); + mpz_powm(mrb, &b2->mp, &b->mp, exp, &b3->mp); + return mrb_obj_value(b3); + } + case MRB_TT_BIGINT: + { + struct RBigint *b2 = bint_new(mrb); + struct RBigint *b3 = RBIGINT(mod); + mpz_powm(mrb, &b2->mp, &b->mp, exp, &b3->mp); + return bint_norm(mrb, b3); + } + mrb_raise(mrb, E_TYPE_ERROR, "too big power"); + default: + mrb_raisef(mrb, E_TYPE_ERROR, "%v cannot be convert to integer", mod); + } + return mrb_nil_value(); +} + +mrb_value +mrb_bint_to_s(mrb_state *mrb, mrb_value x, mrb_int base) +{ + struct RBigint *b = RBIGINT(x); + mrb_int len = mpz_sizeinbase(&b->mp, base); + mrb_value str = mrb_str_new(mrb, NULL, len+2); + mpz_get_str(mrb, RSTRING_PTR(str), len, base, &b->mp); + RSTR_SET_LEN(RSTRING(str), strlen(RSTRING_PTR(str))); + return str; +} + +mrb_value +mrb_bint_and(mrb_state *mrb, mrb_value x, mrb_value y) +{ + struct RBigint *b1 = RBIGINT(x); + struct RBigint *b3 = bint_new(mrb); + +#ifndef MRB_NO_FLOAT + if (mrb_float_p(y)) { + mpz_t z; + mpz_init_set_int(mrb, &z, (mrb_int)mrb_float(y)); + mpz_and(mrb, &b3->mp, &b1->mp, &z); + mpz_clear(mrb, &z); + return bint_norm(mrb, b3); + } +#endif + y = mrb_as_bint(mrb, y); + struct RBigint *b2 = RBIGINT(y); + mpz_and(mrb, &b3->mp, &b1->mp, &b2->mp); + return bint_norm(mrb, b3); +} + +mrb_value +mrb_bint_or(mrb_state *mrb, mrb_value x, mrb_value y) +{ + struct RBigint *b1 = RBIGINT(x); + struct RBigint *b3 = bint_new(mrb); + +#ifndef MRB_NO_FLOAT + if (mrb_float_p(y)) { + mpz_t z; + mpz_init_set_int(mrb, &z, (mrb_int)mrb_float(y)); + mpz_or(mrb, &b3->mp, &b1->mp, &z); + mpz_clear(mrb, &z); + return bint_norm(mrb, b3); + } +#endif + y = mrb_as_bint(mrb, y); + struct RBigint *b2 = RBIGINT(y); + mpz_or(mrb, &b3->mp, &b1->mp, &b2->mp); + return bint_norm(mrb, b3); +} + +mrb_value +mrb_bint_xor(mrb_state *mrb, mrb_value x, mrb_value y) +{ + struct RBigint *b3 = bint_new(mrb); + struct RBigint *b1 = RBIGINT(x); + +#ifndef MRB_NO_FLOAT + if (mrb_float_p(y)) { + mpz_t z; + mpz_init_set_int(mrb, &z, (mrb_int)mrb_float(y)); + mpz_xor(mrb, &b3->mp, &b1->mp, &z); + mpz_clear(mrb, &z); + return bint_norm(mrb, b3); + } +#endif + y = mrb_as_bint(mrb, y); + struct RBigint *b2 = RBIGINT(y); + mpz_xor(mrb, &b3->mp, &b1->mp, &b2->mp); + return bint_norm(mrb, b3); +} + +mrb_value +mrb_bint_rev(mrb_state *mrb, mrb_value x) +{ + struct RBigint *b1 = RBIGINT(x); + struct RBigint *b2 = bint_new(mrb); + + mpz_neg(mrb, &b2->mp, &b1->mp); + mpz_sub_int(mrb, &b2->mp, &b2->mp, 1); + return bint_norm(mrb, b2); +} + +mrb_value +mrb_bint_lshift(mrb_state *mrb, mrb_value x, mrb_int width) +{ + struct RBigint *b = RBIGINT(x); + struct RBigint *b2 = bint_new(mrb); + if (width < 0) { + mpz_div_2exp(mrb, &b2->mp, &b->mp, -width); + } + else { + mpz_mul_2exp(mrb, &b2->mp, &b->mp, width); + } + return mrb_obj_value(b2); +} + +mrb_value +mrb_bint_rshift(mrb_state *mrb, mrb_value x, mrb_int width) +{ + struct RBigint *b = RBIGINT(x); + struct RBigint *b2 = bint_new(mrb); + if (width < 0) { + mpz_mul_2exp(mrb, &b2->mp, &b->mp, -width); + } + else { + mpz_div_2exp(mrb, &b2->mp, &b->mp, width); + } + return bint_norm(mrb, b2); +} + +void mpz_abs(mpz_t *, mpz_t *); +void mpz_gcd(mpz_t *, mpz_t *, mpz_t *); + +#endif /* MRB_USE_BIGINT */ diff --git a/src/gc.c b/src/gc.c index c978b6131..99fc5e40b 100644 --- a/src/gc.c +++ b/src/gc.c @@ -555,6 +555,7 @@ mrb_obj_alloc(mrb_state *mrb, enum mrb_vtype ttype, struct RClass *cls) ttype != MRB_TT_SCLASS && ttype != MRB_TT_ICLASS && ttype != MRB_TT_ENV && + ttype != MRB_TT_BIGINT && ttype != tt) { mrb_raisef(mrb, E_TYPE_ERROR, "allocation failure of %C", cls); } @@ -909,6 +910,12 @@ obj_free(mrb_state *mrb, struct RBasic *obj, int end) break; #endif +#ifdef MRB_USE_BIGINT + case MRB_TT_BIGINT: + mrb_gc_free_bint(mrb, obj); + break; +#endif + default: break; } diff --git a/src/load.c b/src/load.c index 4ed270569..b02650b98 100644 --- a/src/load.c +++ b/src/load.c @@ -166,7 +166,7 @@ read_irep_record_1(mrb_state *mrb, const uint8_t *bin, const uint8_t *end, size_ pool_data_len = bin_to_uint8(src); /* pool data length */ src += sizeof(uint8_t); if (src + pool_data_len > end) return FALSE; - { + { char *p; pool[i].tt = IREP_TT_BIGINT; p = (char*)mrb_malloc(mrb, pool_data_len+2); diff --git a/src/numeric.c b/src/numeric.c index 781a06b4b..aa6550d7a 100644 --- a/src/numeric.c +++ b/src/numeric.c @@ -45,6 +45,18 @@ mrb_int_zerodiv(mrb_state *mrb) mrb_value mrb_int_pow(mrb_state *mrb, mrb_value x) { +#ifdef MRB_USE_BIGINT + if (mrb_bigint_p(x)) { + mrb_value y = mrb_get_arg1(mrb); + +#ifndef MRB_NO_FLOAT + if (mrb_float_p(y)) { + return mrb_float_value(mrb, pow(mrb_bint_as_float(mrb, x), mrb_float(y))); + } +#endif + return mrb_bint_pow(mrb, x, y); + } +#endif mrb_int base = mrb_integer(x); mrb_int result = 1; mrb_int exp; @@ -79,7 +91,11 @@ mrb_int_pow(mrb_state *mrb, mrb_value x) exp >>= 1; if (exp == 0) break; if (mrb_int_mul_overflow(base, base, &base)) { +#ifdef MRB_USE_BIGINT + return mrb_bint_pow(mrb, mrb_bint_new_int(mrb, mrb_integer(x)), y); +#else mrb_int_overflow(mrb, "power"); +#endif } } return mrb_int_value(mrb, result); @@ -121,13 +137,28 @@ static mrb_value int_div(mrb_state *mrb, mrb_value x) { mrb_value y = mrb_get_arg1(mrb); +#ifdef MRB_USE_BIGINT + if (mrb_bigint_p(x)) { + return mrb_bint_div(mrb, x, y); + } +#endif mrb_int a = mrb_integer(x); if (mrb_integer_p(y)) { - mrb_int div = mrb_div_int(mrb, a, mrb_integer(y)); + mrb_int b = mrb_integer(y); +#ifdef MRB_USE_BIGINT + if(a == MRB_INT_MIN && b == -1) { + return mrb_bint_mul_ii(mrb, a, b); + } +#endif + mrb_int div = mrb_div_int(mrb, a, b); return mrb_int_value(mrb, div); } switch (mrb_type(y)) { +#ifdef MRB_USE_BIGINT + case MRB_TT_BIGINT: + return mrb_bint_div(mrb, mrb_bint_new_int(mrb, a), y); +#endif #ifdef MRB_USE_RATIONAL case MRB_TT_RATIONAL: return mrb_rational_div(mrb, mrb_rational_new(mrb, a, 1), y); @@ -163,6 +194,11 @@ int_div(mrb_state *mrb, mrb_value x) static mrb_value int_idiv(mrb_state *mrb, mrb_value x) { +#ifdef MRB_USE_BIGINT + if (mrb_bigint_p(x)) { + return mrb_bint_div(mrb, x, mrb_get_arg1(mrb)); + } +#endif mrb_int y; mrb_get_args(mrb, "i", &y); @@ -182,6 +218,11 @@ int_quo(mrb_state *mrb, mrb_value x) if (y == 0) { mrb_int_zerodiv(mrb); } +#ifdef MRB_USE_BIGINT + if (mrb_bigint_p(x)) { + return mrb_float_value(mrb, mrb_bint_as_float(mrb, x) / y); + } +#endif return mrb_float_value(mrb, mrb_integer(x) / y); #endif #else @@ -502,6 +543,11 @@ int_eql(mrb_state *mrb, mrb_value x) { mrb_value y = mrb_get_arg1(mrb); +#ifdef MRB_USE_BIGINT + if (mrb_bigint_p(x)) { + return mrb_bool_value(mrb_bint_cmp(mrb, x, y) == 0); + } +#endif if (!mrb_integer_p(y)) return mrb_false_value(); return mrb_bool_value(mrb_integer(x) == mrb_integer(y)); } @@ -1038,16 +1084,25 @@ mrb_int_mul(mrb_state *mrb, mrb_value x, mrb_value y) if (a == 0) return x; b = mrb_integer(y); if (mrb_int_mul_overflow(a, b, &c)) { +#ifdef MRB_USE_BIGINT + x = mrb_bint_new_int(mrb, a); + return mrb_bint_mul(mrb, x, y); +#else mrb_int_overflow(mrb, "multiplication"); +#endif } return mrb_int_value(mrb, c); } switch (mrb_type(y)) { -#if defined(MRB_USE_RATIONAL) +#ifdef MRB_USE_BIGINT + case MRB_TT_BIGINT: + return mrb_bint_mul(mrb, y, x); +#endif +#ifdef MRB_USE_RATIONAL case MRB_TT_RATIONAL: return mrb_rational_mul(mrb, y, x); #endif -#if defined(MRB_USE_COMPLEX) +#ifdef MRB_USE_COMPLEX case MRB_TT_COMPLEX: return mrb_complex_mul(mrb, y, x); #endif @@ -1075,6 +1130,11 @@ int_mul(mrb_state *mrb, mrb_value x) { mrb_value y = mrb_get_arg1(mrb); +#ifdef MRB_USE_BIGINT + if (mrb_bigint_p(x)) { + return mrb_bint_mul(mrb, x, y); + } +#endif return mrb_int_mul(mrb, x, y); } @@ -1115,6 +1175,11 @@ int_mod(mrb_state *mrb, mrb_value x) mrb_value y = mrb_get_arg1(mrb); mrb_int a, b; +#ifdef MRB_USE_BIGINT + if (mrb_bigint_p(x)) { + return mrb_bint_mod(mrb, x, y); + } +#endif a = mrb_integer(x); if (mrb_integer_p(y)) { b = mrb_integer(y); @@ -1151,6 +1216,17 @@ int_divmod(mrb_state *mrb, mrb_value x) { mrb_value y = mrb_get_arg1(mrb); +#ifdef MRB_USE_BIGINT + if (mrb_bigint_p(x)) { +#ifndef MRB_NO_FLOAT + if (mrb_float_p(y)) { + mrb_float f = mrb_bint_as_float(mrb, x); + return flo_divmod(mrb, mrb_float_value(mrb, f)); + } +#endif + return mrb_bint_divmod(mrb, x, y); + } +#endif if (mrb_integer_p(y)) { mrb_int div, mod; @@ -1206,6 +1282,10 @@ int_equal(mrb_state *mrb, mrb_value x) case MRB_TT_FLOAT: return mrb_bool_value((mrb_float)mrb_integer(x) == mrb_float(y)); #endif +#ifdef MRB_USE_BIGINT + case MRB_TT_BIGINT: + return mrb_bool_value(mrb_bint_cmp(mrb, y, x) == 0); +#endif #ifdef MRB_USE_RATIONAL case MRB_TT_RATIONAL: return mrb_bool_value(mrb_equal(mrb, y, x)); @@ -1235,6 +1315,11 @@ int_rev(mrb_state *mrb, mrb_value num) { mrb_int val = mrb_integer(num); +#ifdef MRB_USE_BIGINT + if (mrb_bigint_p(num)) { + mrb_bint_rev(mrb, num); + } +#endif return mrb_int_value(mrb, ~val); } @@ -1265,6 +1350,11 @@ int_and(mrb_state *mrb, mrb_value x) { mrb_value y = mrb_get_arg1(mrb); +#ifdef MRB_USE_BIGINT + if (mrb_bigint_p(x)) { + return mrb_bint_and(mrb, x, y); + } +#endif bit_op(x, y, and, &); } @@ -1281,6 +1371,11 @@ int_or(mrb_state *mrb, mrb_value x) { mrb_value y = mrb_get_arg1(mrb); +#ifdef MRB_USE_BIGINT + if (mrb_bigint_p(x)) { + return mrb_bint_or(mrb, x, y); + } +#endif bit_op(x, y, or, |); } @@ -1297,6 +1392,11 @@ int_xor(mrb_state *mrb, mrb_value x) { mrb_value y = mrb_get_arg1(mrb); +#ifdef MRB_USE_BIGINT + if (mrb_bigint_p(x)) { + return mrb_bint_xor(mrb, x, y); + } +#endif bit_op(x, y, or, ^); } @@ -1355,10 +1455,20 @@ int_lshift(mrb_state *mrb, mrb_value x) if (width == 0) { return x; } + if (width == MRB_INT_MIN) mrb_int_overflow(mrb, "bit shift"); +#ifdef MRB_USE_BIGINT + if (mrb_bigint_p(x)) { + return mrb_bint_lshift(mrb, x, width); + } +#endif val = mrb_integer(x); if (val == 0) return x; if (!mrb_num_shift(mrb, val, width, &val)) { +#ifdef MRB_USE_BIGINT + return mrb_bint_lshift(mrb, mrb_bint_new_int(mrb, val), width); +#else mrb_int_overflow(mrb, "bit shift"); +#endif } return mrb_int_value(mrb, val); } @@ -1380,11 +1490,20 @@ int_rshift(mrb_state *mrb, mrb_value x) if (width == 0) { return x; } + if (width == MRB_INT_MIN) mrb_int_overflow(mrb, "bit shift"); +#ifdef MRB_USE_BIGINT + if (mrb_bigint_p(x)) { + return mrb_bint_rshift(mrb, x, width); + } +#endif val = mrb_integer(x); if (val == 0) return x; - if (width == MRB_INT_MIN) mrb_int_overflow(mrb, "bit shift"); if (!mrb_num_shift(mrb, val, -width, &val)) { +#ifdef MRB_USE_BIGINT + return mrb_bint_rshift(mrb, mrb_bint_new_int(mrb, val), width); +#else mrb_int_overflow(mrb, "bit shift"); +#endif } return mrb_int_value(mrb, val); } @@ -1402,6 +1521,11 @@ int_rshift(mrb_state *mrb, mrb_value x) static mrb_value int_to_f(mrb_state *mrb, mrb_value num) { +#ifdef MRB_USE_BIGINT + if (mrb_bigint_p(num)) { + return mrb_float_value(mrb, mrb_bint_as_float(mrb, num)); + } +#endif return mrb_float_value(mrb, (mrb_float)mrb_integer(num)); } @@ -1454,16 +1578,25 @@ mrb_int_add(mrb_state *mrb, mrb_value x, mrb_value y) if (a == 0) return y; b = mrb_integer(y); if (mrb_int_add_overflow(a, b, &c)) { +#ifdef MRB_USE_BIGINT + x = mrb_bint_new_int(mrb, a); + return mrb_bint_add(mrb, x, y); +#else mrb_int_overflow(mrb, "addition"); +#endif } return mrb_int_value(mrb, c); } switch (mrb_type(y)) { -#if defined(MRB_USE_RATIONAL) +#ifdef MRB_USE_BIGINT + case MRB_TT_BIGINT: + return mrb_bint_add(mrb, y, x); +#endif +#ifdef MRB_USE_RATIONAL case MRB_TT_RATIONAL: return mrb_rational_add(mrb, y, x); #endif -#if defined(MRB_USE_COMPLEX) +#ifdef MRB_USE_COMPLEX case MRB_TT_COMPLEX: return mrb_complex_add(mrb, y, x); #endif @@ -1490,6 +1623,11 @@ int_add(mrb_state *mrb, mrb_value self) { mrb_value other = mrb_get_arg1(mrb); +#ifdef MRB_USE_BIGINT + if (mrb_bigint_p(self)) { + return mrb_bint_add(mrb, self, other); + } +#endif return mrb_int_add(mrb, self, other); } @@ -1504,16 +1642,25 @@ mrb_int_sub(mrb_state *mrb, mrb_value x, mrb_value y) b = mrb_integer(y); if (mrb_int_sub_overflow(a, b, &c)) { +#ifdef MRB_USE_BIGINT + x = mrb_bint_new_int(mrb, a); + return mrb_bint_sub(mrb, x, y); +#else mrb_int_overflow(mrb, "subtraction"); +#endif } return mrb_int_value(mrb, c); } switch (mrb_type(y)) { -#if defined(MRB_USE_RATIONAL) +#ifdef MRB_USE_BIGINT + case MRB_TT_BIGINT: + return mrb_bint_sub(mrb, mrb_bint_new_int(mrb, a), y); +#endif +#ifdef MRB_USE_RATIONAL case MRB_TT_RATIONAL: return mrb_rational_sub(mrb, mrb_rational_new(mrb, a, 1), y); #endif -#if defined(MRB_USE_COMPLEX) +#ifdef MRB_USE_COMPLEX case MRB_TT_COMPLEX: return mrb_complex_sub(mrb, mrb_complex_new(mrb, (mrb_float)a, 0), y); #endif @@ -1541,6 +1688,11 @@ int_sub(mrb_state *mrb, mrb_value self) { mrb_value other = mrb_get_arg1(mrb); +#ifdef MRB_USE_BIGINT + if (mrb_bigint_p(self)) { + return mrb_bint_sub(mrb, self, other); + } +#endif return mrb_int_sub(mrb, self, other); } @@ -1615,6 +1767,11 @@ int_to_s(mrb_state *mrb, mrb_value self) mrb_int base = 10; mrb_get_args(mrb, "|i", &base); +#ifdef MRB_USE_BIGINT + if (mrb_bigint_p(self)) { + return mrb_bint_to_s(mrb, self, base); + } +#endif return mrb_integer_to_str(mrb, self, base); } @@ -1622,6 +1779,12 @@ int_to_s(mrb_state *mrb, mrb_value self) static mrb_int cmpnum(mrb_state *mrb, mrb_value v1, mrb_value v2) { +#ifdef MRB_USE_BIGINT + if (mrb_bigint_p(v2)) { + return mrb_bint_cmp(mrb, v1, v2); + } +#endif + #ifdef MRB_NO_FLOAT mrb_int x, y; #else diff --git a/src/numops.c b/src/numops.c index 6bfe508e2..ff8adc2d6 100644 --- a/src/numops.c +++ b/src/numops.c @@ -12,6 +12,11 @@ MRB_API mrb_value mrb_num_add(mrb_state *mrb, mrb_value x, mrb_value y) { +#ifdef MRB_USE_BIGINT + if (mrb_bigint_p(x)) { + return mrb_bint_add(mrb, x, y); + } +#endif if (mrb_integer_p(x)) { return mrb_int_add(mrb, x, y); } @@ -41,6 +46,11 @@ mrb_num_add(mrb_state *mrb, mrb_value x, mrb_value y) MRB_API mrb_value mrb_num_sub(mrb_state *mrb, mrb_value x, mrb_value y) { +#ifdef MRB_USE_BIGINT + if (mrb_bigint_p(x)) { + return mrb_bint_sub(mrb, x, y); + } +#endif if (mrb_integer_p(x)) { return mrb_int_sub(mrb, x, y); } @@ -70,6 +80,11 @@ mrb_num_sub(mrb_state *mrb, mrb_value x, mrb_value y) MRB_API mrb_value mrb_num_mul(mrb_state *mrb, mrb_value x, mrb_value y) { +#ifdef MRB_USE_BIGINT + if (mrb_bigint_p(x)) { + return mrb_bint_mul(mrb, x, y); + } +#endif if (mrb_integer_p(x)) { return mrb_int_mul(mrb, x, y); } diff --git a/src/object.c b/src/object.c index b430f2057..d03bfcfea 100644 --- a/src/object.c +++ b/src/object.c @@ -9,6 +9,7 @@ #include #include #include +#include #include MRB_API mrb_bool @@ -547,6 +548,11 @@ mrb_ensure_float_type(mrb_state *mrb, mrb_value val) return mrb_complex_to_f(mrb, val); #endif +#ifdef MRB_USE_BIGINT + case MRB_TT_BIGINT: + return mrb_float_value(mrb, mrb_bint_as_float(mrb, val)); +#endif + default: mrb_raisef(mrb, E_TYPE_ERROR, "%Y cannot be converted to Float", val); /* not reached */ diff --git a/src/string.c b/src/string.c index 0273f74e3..2ab45e289 100644 --- a/src/string.c +++ b/src/string.c @@ -2150,11 +2150,24 @@ mrb_str_split_m(mrb_state *mrb, mrb_value str) return result; } +static mrb_bool +trailingbad(const char *str, const char *p, const char *pend) +{ + if (p == str) return TRUE; /* no number */ + if (*(p - 1) == '_') return TRUE; /* trailing '_' */ + while (p= base) break; + p3++; + } + if (badcheck && trailingbad(str, p, pend)) goto bad; + return mrb_bint_new_str(mrb, p2, (mrb_int)(p3-p2), sign ? base : -base); +#endif mrb_raisef(mrb, E_RANGE_ERROR, "string (%l) too big for integer", str, pend-str); } n += c; } val = (mrb_int)n; - if (badcheck) { - if (p == str) goto bad; /* no number */ - if (*(p - 1) == '_') goto bad; /* trailing '_' */ - while (p