From 3db12e8b236ac8f88db8eb4690d10e4a3b8dbcd4 Mon Sep 17 00:00:00 2001 From: matz Date: Fri, 16 Jan 1998 12:13:05 +0000 Subject: Initial revision git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@2 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- numeric.c | 1154 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1154 insertions(+) create mode 100644 numeric.c (limited to 'numeric.c') diff --git a/numeric.c b/numeric.c new file mode 100644 index 0000000000..242d13c14e --- /dev/null +++ b/numeric.c @@ -0,0 +1,1154 @@ +/************************************************ + + numeric.c - + + $Author$ + $Date$ + created at: Fri Aug 13 18:33:09 JST 1993 + + Copyright (C) 1993-1996 Yukihiro Matsumoto + +************************************************/ + +#include "ruby.h" +#include +#if defined (HAVE_STRING_H) +# include +#else +# include +#endif + +static ID coerce; +static ID to_i; + +VALUE cNumeric; +VALUE cFloat; +VALUE cInteger; +VALUE cFixnum; + +VALUE eZeroDiv; + +ID rb_frame_last_func(); +VALUE float_new(); +double big2dbl(); + +void +num_zerodiv() +{ + Raise(eZeroDiv, "divided by 0"); +} + +static VALUE +num_coerce(x, y) + VALUE x, y; +{ + return assoc_new(f_float(x,x),f_float(y,y)); +} + +VALUE +num_coerce_bin(x, y) + VALUE x, y; +{ + VALUE ary; + + ary = rb_funcall(y, coerce, 1, x); + if (TYPE(ary) != T_ARRAY || RARRAY(ary)->len != 2) { + TypeError("coerce must return [x, y]"); + } + + x = RARRAY(ary)->ptr[0]; + y = RARRAY(ary)->ptr[1]; + + return rb_funcall(x, rb_frame_last_func(), 1, y); +} + +static VALUE +num_uplus(num) + VALUE num; +{ + return num; +} + +static VALUE +num_uminus(num) + VALUE num; +{ + VALUE ary, x, y; + + ary = rb_funcall(num, coerce, 1, INT2FIX(0)); + if (TYPE(ary) != T_ARRAY || RARRAY(ary)->len != 2) { + TypeError("coerce must return [x, y]"); + } + + x = RARRAY(ary)->ptr[0]; + y = RARRAY(ary)->ptr[1]; + + return rb_funcall(x, '-', 1, y); +} + +static VALUE +num_divmod(x, y) + VALUE x, y; +{ + VALUE div, mod; + + div = rb_funcall(x, '/', 1, y); + if (TYPE(div) == T_FLOAT) { + double d = floor(RFLOAT(div)->value); + + if (RFLOAT(div)->value > d) { + div = float_new(d); + } + } + mod = rb_funcall(x, '%', 1, y); + return assoc_new(div, mod); +} + +static VALUE +num_int_p(num) + VALUE num; +{ + return FALSE; +} + +static VALUE +num_chr(num) + VALUE num; +{ + char c; + int i = NUM2INT(num); + + if (i < 0 || 0xff < i) + Fail("%d out of char range", i); + c = i; + return str_new(&c, 1); +} + +static VALUE +num_abs(num) + VALUE num; +{ + if (RTEST(rb_funcall(num, '<', 1, INT2FIX(0)))) { + return rb_funcall(num, rb_intern("-@"), 0); + } + return num; +} + +VALUE +float_new(d) + double d; +{ + NEWOBJ(flt, struct RFloat); + OBJSETUP(flt, cFloat, T_FLOAT); + + flt->value = d; + return (VALUE)flt; +} + +static VALUE +flo_to_s(flt) + struct RFloat *flt; +{ + char buf[32]; + + sprintf(buf, "%g", flt->value); + if (strchr(buf, '.') == 0) { + int len = strlen(buf); + char *ind = strchr(buf, 'e'); + + if (ind) { + memmove(ind+2, ind, len-(ind-buf)+1); + ind[0] = '.'; + ind[1] = '0'; + } else { + strcat(buf, ".0"); + } + } + + return str_new2(buf); +} + +static VALUE +flo_coerce(x, y) + VALUE x, y; +{ + return assoc_new(f_float(x, y), x); +} + +static VALUE +flo_uminus(flt) + struct RFloat *flt; +{ + return float_new(-flt->value); +} + +static VALUE +flo_plus(x, y) + struct RFloat *x, *y; +{ + switch (TYPE(y)) { + case T_FIXNUM: + return float_new(x->value + (double)FIX2INT(y)); + case T_BIGNUM: + return float_new(x->value + big2dbl(y)); + case T_FLOAT: + return float_new(x->value + y->value); + case T_STRING: + return str_plus(obj_as_string(x), y); + default: + return num_coerce_bin(x, y); + } +} + +static VALUE +flo_minus(x, y) + struct RFloat *x, *y; +{ + switch (TYPE(y)) { + case T_FIXNUM: + return float_new(x->value - (double)FIX2INT(y)); + case T_BIGNUM: + return float_new(x->value - big2dbl(y)); + case T_FLOAT: + return float_new(x->value - y->value); + default: + return num_coerce_bin(x, y); + } +} + +static VALUE +flo_mul(x, y) + struct RFloat *x, *y; +{ + switch (TYPE(y)) { + case T_FIXNUM: + return float_new(x->value * (double)FIX2INT(y)); + case T_BIGNUM: + return float_new(x->value * big2dbl(y)); + case T_FLOAT: + return float_new(x->value * y->value); + case T_STRING: + return str_times(y, INT2FIX((int)x->value)); + default: + return num_coerce_bin(x, y); + } +} + +static VALUE +flo_div(x, y) + struct RFloat *x, *y; +{ + int f_y; + double d; + + switch (TYPE(y)) { + case T_FIXNUM: + f_y = FIX2INT(y); + if (f_y == 0) num_zerodiv(); + return float_new(x->value / (double)f_y); + case T_BIGNUM: + d = big2dbl(y); + if (d == 0.0) num_zerodiv(); + return float_new(x->value / d); + case T_FLOAT: + if (y->value == 0.0) num_zerodiv(); + return float_new(x->value / y->value); + default: + return num_coerce_bin(x, y); + } +} + +static VALUE +flo_mod(x, y) + struct RFloat *x, *y; +{ + double value; + + switch (TYPE(y)) { + case T_FIXNUM: + value = (double)FIX2INT(y); + break; + case T_BIGNUM: + value = big2dbl(y); + break; + case T_FLOAT: + value = y->value; + break; + default: + return num_coerce_bin(x, y); + } +#ifdef HAVE_FMOD + value = fmod(x->value, value); +#else + { + double value1 = x->value; + double value2; + + modf(value1/value, &value2); + value = value1 - value2 * value; + } +#endif + + return float_new(value); +} + +VALUE +flo_pow(x, y) + struct RFloat *x, *y; +{ + switch (TYPE(y)) { + case T_FIXNUM: + return float_new(pow(x->value, (double)FIX2INT(y))); + case T_BIGNUM: + return float_new(pow(x->value, big2dbl(y))); + case T_FLOAT: + return float_new(pow(x->value, y->value)); + default: + return num_coerce_bin(x, y); + } +} + +static VALUE +num_eql(x, y) + VALUE x, y; +{ + if (TYPE(x) != TYPE(y)) return FALSE; + + return rb_equal(x, y); +} + + +static VALUE +num_equal(x, y) + VALUE x, y; +{ + return rb_equal(y, x); +} + +static VALUE +flo_eq(x, y) + struct RFloat *x, *y; +{ + switch (TYPE(y)) { + case T_FIXNUM: + if (x->value == FIX2INT(y)) return TRUE; + return FALSE; + case T_BIGNUM: + return (x->value == big2dbl(y))?TRUE:FALSE; + case T_FLOAT: + return (x->value == y->value)?TRUE:FALSE; + default: + return num_equal(x, y); + } +} + +static VALUE +flo_hash(num) + struct RFloat *num; +{ + double d; + char *c; + int i, hash; + + d = num->value; + c = (char*)&d; + for (hash=0, i=0; ivalue; + switch (TYPE(y)) { + case T_FIXNUM: + b = (double)FIX2INT(y); + break; + + case T_BIGNUM: + b = big2dbl(y); + break; + + case T_FLOAT: + b = y->value; + break; + + default: + return num_coerce_bin(x, y); + } + if (a == b) return INT2FIX(0); + if (a > b) return INT2FIX(1); + return INT2FIX(-1); +} + +static VALUE +flo_eql(x, y) + struct RFloat *x, *y; +{ + if (TYPE(y) == T_FLOAT) { + if (x->value == y->value) return TRUE; + } +} + +static VALUE +flo_to_i(num) + struct RFloat *num; +{ + double f = num->value; + int val; + + if (!FIXABLE(f)) { + return dbl2big(f); + } + val = f; + return INT2FIX(val); +} + +static VALUE +flo_to_f(num) + VALUE num; +{ + return num; +} + +static VALUE +flo_abs(flt) + struct RFloat *flt; +{ + double val = fabs(flt->value); + return float_new(val); +} + +static VALUE +to_integer(val) + VALUE val; +{ + return rb_funcall(val, to_i, 0); +} + +static VALUE +fail_to_integer(val) + VALUE val; +{ + TypeError("failed to convert %s into Integer", + rb_class2name(CLASS_OF(val))); +} + +int +num2int(val) + VALUE val; +{ + if (NIL_P(val)) return 0; + + switch (TYPE(val)) { + case T_FIXNUM: + return FIX2INT(val); + + case T_FLOAT: + if (RFLOAT(val)->value <= (double) LONG_MAX + && RFLOAT(val)->value >= (double) LONG_MIN) { + return (int)(RFLOAT(val)->value); + } + else { + Fail("float %g out of rang of integer", RFLOAT(val)->value); + } + + case T_BIGNUM: + return big2int(val); + + default: + val = rb_rescue(to_integer, val, fail_to_integer, val); + return NUM2INT(val); + } +} + +VALUE +num2fix(val) + VALUE val; +{ + int v; + + if (NIL_P(val)) return INT2FIX(0); + switch (TYPE(val)) { + case T_FIXNUM: + return val; + + case T_FLOAT: + case T_BIGNUM: + default: + v = num2int(val); + if (!FIXABLE(v)) + Fail("integer %d out of range of Fixnum", v); + return INT2FIX(v); + } +} + +static VALUE +int_int_p(num) + VALUE num; +{ + return TRUE; +} + +static VALUE +int_succ(num) + VALUE num; +{ + return rb_funcall(num, '+', 1, INT2FIX(1)); +} + +static VALUE +fix_uminus(num) + VALUE num; +{ + return int2inum(-FIX2INT(num)); +} + +VALUE +fix2str(x, base) + VALUE x; + int base; +{ + char fmt[3], buf[12]; + + fmt[0] = '%'; fmt[2] = '\0'; + if (base == 10) fmt[1] = 'd'; + else if (base == 16) fmt[1] = 'x'; + else if (base == 8) fmt[1] = 'o'; + else Fatal("fixnum cannot treat base %d", base); + + sprintf(buf, fmt, FIX2INT(x)); + return str_new2(buf); +} + +VALUE +fix_to_s(in) + VALUE in; +{ + return fix2str(in, 10); +} + +static VALUE +fix_plus(x, y) + VALUE x; + struct RFloat *y; +{ + switch (TYPE(y)) { + case T_FIXNUM: + { + int a, b, c; + VALUE r; + + a = FIX2INT(x); + b = FIX2INT(y); + c = a + b; + r = INT2FIX(c); + + if (FIX2INT(r) != c) { + r = big_plus(int2big(a), int2big(b)); + } + return r; + } + case T_FLOAT: + return float_new((double)FIX2INT(x) + y->value); + default: + return num_coerce_bin(x, y); + } +} + +static VALUE +fix_minus(x, y) + VALUE x; + struct RFloat *y; +{ + switch (TYPE(y)) { + case T_FIXNUM: + { + int a, b, c; + VALUE r; + + a = FIX2INT(x); + b = FIX2INT(y); + c = a - b; + r = INT2FIX(c); + + if (FIX2INT(r) != c) { + r = big_minus(int2big(a), int2big(b)); + } + return r; + } + case T_FLOAT: + return float_new((double)FIX2INT(x) - y->value); + default: + return num_coerce_bin(x, y); + } +} + +static VALUE +fix_mul(x, y) + VALUE x; + struct RFloat *y; +{ + switch (TYPE(y)) { + case T_FIXNUM: + { + int a, b, c; + VALUE r; + + a = FIX2INT(x); + if (a == 0) return x; + + b = FIX2INT(y); + c = a * b; + r = INT2FIX(c); + + if (FIX2INT(r) != c || c/a != b) { + r = big_mul(int2big(a), int2big(b)); + } + return r; + } + case T_FLOAT: + return float_new((double)FIX2INT(x) * y->value); + default: + return num_coerce_bin(x, y); + } +} + +static VALUE +fix_div(x, y) + VALUE x; + struct RFloat *y; +{ + int i; + + if (TYPE(y) == T_FIXNUM) { + i = FIX2INT(y); + if (i == 0) num_zerodiv(); + i = FIX2INT(x)/i; + return INT2FIX(i); + } + return num_coerce_bin(x, y); +} + +static VALUE +fix_mod(x, y) + VALUE x, y; +{ + int i; + + if (TYPE(y) == T_FIXNUM) { + i = FIX2INT(y); + if (i == 0) num_zerodiv(); + i = FIX2INT(x)%i; + return INT2FIX(i); + } + return num_coerce_bin(x, y); +} + +static VALUE +fix_pow(x, y) + VALUE x, y; +{ + if (FIXNUM_P(y)) { + int a, b; + + b = FIX2INT(y); + if (b == 0) return INT2FIX(1); + a = FIX2INT(x); + if (b > 0) { + return big_pow(int2big(a), y); + } + return float_new(pow((double)a, (double)b)); + } + else if (NIL_P(y)) { + return INT2FIX(1); + } + return num_coerce_bin(x, y); +} + +static VALUE +fix_equal(x, y) + VALUE x, y; +{ + if (FIXNUM_P(y)) { + return (FIX2INT(x) == FIX2INT(y))?TRUE:FALSE; + } + else { + return num_equal(x, y); + } +} + +static VALUE +fix_cmp(x, y) + VALUE x, y; +{ + if (FIXNUM_P(y)) { + int a = FIX2INT(x), b = FIX2INT(y); + + if (a == b) return INT2FIX(0); + if (a > b) return INT2FIX(1); + return INT2FIX(-1); + } + else { + return num_coerce_bin(x, y); + } +} + +static VALUE +fix_gt(x, y) + VALUE x, y; +{ + if (FIXNUM_P(y)) { + int a = FIX2INT(x), b = FIX2INT(y); + + if (a > b) return TRUE; + return FALSE; + } + else { + return num_coerce_bin(x, y); + } +} + +static VALUE +fix_ge(x, y) + VALUE x, y; +{ + if (FIXNUM_P(y)) { + int a = FIX2INT(x), b = FIX2INT(y); + + if (a >= b) return TRUE; + return FALSE; + } + else { + return num_coerce_bin(x, y); + } +} + +static VALUE +fix_lt(x, y) + VALUE x, y; +{ + if (FIXNUM_P(y)) { + int a = FIX2INT(x), b = FIX2INT(y); + + if (a < b) return TRUE; + return FALSE; + } + else { + return num_coerce_bin(x, y); + } +} + +static VALUE +fix_le(x, y) + VALUE x, y; +{ + if (FIXNUM_P(y)) { + int a = FIX2INT(x), b = FIX2INT(y); + + if (a <= b) return TRUE; + return FALSE; + } + else { + return num_coerce_bin(x, y); + } +} + +static VALUE +fix_rev(num) + VALUE num; +{ + unsigned long val = FIX2UINT(num); + + val = ~val; + return INT2FIX(val); +} + +static VALUE +fix_and(x, y) + VALUE x, y; +{ + long val; + + if (TYPE(y) == T_BIGNUM) { + return big_and(y, x); + } + val = NUM2INT(x) & NUM2INT(y); + return int2inum(val); +} + +static VALUE +fix_or(x, y) + VALUE x, y; +{ + long val; + + if (TYPE(y) == T_BIGNUM) { + return big_or(y, x); + } + val = NUM2INT(x) | NUM2INT(y); + return INT2FIX(val); +} + +static VALUE +fix_xor(x, y) + VALUE x, y; +{ + long val; + + if (TYPE(y) == T_BIGNUM) { + return big_xor(y, x); + } + val = NUM2INT(x) ^ NUM2INT(y); + return INT2FIX(val); +} + +static VALUE +fix_lshift(x, y) + VALUE x, y; +{ + long val, width; + + val = NUM2INT(x); + width = NUM2INT(y); + if (width > (sizeof(VALUE)*CHAR_BIT-1) + || (unsigned)val>>(sizeof(VALUE)*CHAR_BIT-1-width) > 0) { + return big_lshift(int2big(val), y); + } + val = val << width; + return int2inum(val); +} + +static VALUE +fix_rshift(x, y) + VALUE x, y; +{ + long i, val; + + i = NUM2INT(y); + if (y < 32) { + val = RSHIFT(FIX2INT(x), i); + return INT2FIX(val); + } + + return INT2FIX(0); +} + +static VALUE +fix_aref(fix, idx) + VALUE fix, idx; +{ + unsigned long val = FIX2INT(fix); + int i = FIX2INT(idx); + + if (i < 0 || sizeof(VALUE)*CHAR_BIT-1 < i) + return INT2FIX(0); + if (val & (1<', 1, to))) break; + rb_yield(i); + i = rb_funcall(i, '+', 1, INT2FIX(1)); + } + return from; +} + +static VALUE +num_downto(from, to) + VALUE from, to; +{ + VALUE i = from; + + for (;;) { + if (RTEST(rb_funcall(i, '<', 1, to))) break; + rb_yield(i); + i = rb_funcall(i, '-', 1, INT2FIX(1)); + } + return from; +} + +static VALUE +num_step(from, to, step) + VALUE from, to, step; +{ + VALUE i = from; + ID cmp; + + if (step == INT2FIX(0)) { + IndexError("step cannot be 0"); + } + + if (RTEST(rb_funcall(step, '>', 1, INT2FIX(0)))) { + cmp = '>'; + } + else { + cmp = '<'; + } + for (;;) { + if (RTEST(rb_funcall(i, cmp, 1, to))) break; + rb_yield(i); + i = rb_funcall(i, '+', 1, step); + } + return from; +} + +static VALUE +num_dotimes(num) + VALUE num; +{ + VALUE i = INT2FIX(0); + + for (;;) { + if (!RTEST(rb_funcall(i, '<', 1, num))) break; + rb_yield(i); + i = rb_funcall(i, '+', 1, INT2FIX(1)); + } + return num; +} + +VALUE +fix_upto(from, to) + VALUE from, to; +{ + int i, end; + + if (!FIXNUM_P(to)) return num_upto(from, to); + end = FIX2INT(to); + for (i = FIX2INT(from); i <= end; i++) { + rb_yield(INT2FIX(i)); + } + + return from; +} + +static VALUE +fix_downto(from, to) + VALUE from, to; +{ + int i, end; + + if (!FIXNUM_P(to)) return num_downto(from, to); + end = FIX2INT(to); + for (i=FIX2INT(from); i >= end; i--) { + rb_yield(INT2FIX(i)); + } + + return from; +} + +static VALUE +fix_step(from, to, step) + VALUE from, to, step; +{ + int i, end, diff; + + if (!FIXNUM_P(to) || !FIXNUM_P(step)) + return num_step(from, to, step); + + end = FIX2INT(to); + diff = FIX2INT(step); + + if (diff == 0) { + ArgError("step cannot be 0"); + } + else if (diff > 0) { + for (i=FIX2INT(from); i <= end; i+=diff) { + rb_yield(INT2FIX(i)); + } + } + else { + for (i=FIX2INT(from); i >= end; i+=diff) { + rb_yield(INT2FIX(i)); + } + } + return from; +} + +static VALUE +fix_dotimes(num) + VALUE num; +{ + int i, end; + + end = FIX2INT(num); + for (i=0; i", fix_cmp, 1); + rb_define_method(cFixnum, ">", fix_gt, 1); + rb_define_method(cFixnum, ">=", fix_ge, 1); + rb_define_method(cFixnum, "<", fix_lt, 1); + rb_define_method(cFixnum, "<=", fix_le, 1); + + rb_define_method(cFixnum, "~", fix_rev, 0); + rb_define_method(cFixnum, "&", fix_and, 1); + rb_define_method(cFixnum, "|", fix_or, 1); + rb_define_method(cFixnum, "^", fix_xor, 1); + rb_define_method(cFixnum, "[]", fix_aref, 1); + + rb_define_method(cFixnum, "<<", fix_lshift, 1); + rb_define_method(cFixnum, ">>", fix_rshift, 1); + + rb_define_method(cFixnum, "to_i", fix_to_i, 0); + rb_define_method(cFixnum, "to_f", fix_to_f, 0); + + rb_define_method(cFixnum, "succ", fix_succ, 0); + rb_define_method(cFixnum, "size", fix_size, 0); + + rb_define_method(cFixnum, "upto", fix_upto, 1); + rb_define_method(cFixnum, "downto", fix_downto, 1); + rb_define_method(cFixnum, "step", fix_step, 2); + rb_define_method(cFixnum, "times", fix_dotimes, 0); + + cFloat = rb_define_class("Float", cNumeric); + + rb_undef_method(CLASS_OF(cFloat), "new"); + + rb_define_method(cFloat, "to_s", flo_to_s, 0); + rb_define_method(cFloat, "coerce", flo_coerce, 1); + rb_define_method(cFloat, "-@", flo_uminus, 0); + rb_define_method(cFloat, "+", flo_plus, 1); + rb_define_method(cFloat, "-", flo_minus, 1); + rb_define_method(cFloat, "*", flo_mul, 1); + rb_define_method(cFloat, "/", flo_div, 1); + rb_define_method(cFloat, "%", flo_mod, 1); + rb_define_method(cFloat, "**", flo_pow, 1); + rb_define_method(cFloat, "==", flo_eq, 1); + rb_define_method(cFloat, "<=>", flo_cmp, 1); + rb_define_method(cFloat, "eql?", flo_eql, 1); + rb_define_method(cFloat, "hash", flo_hash, 0); + rb_define_method(cFloat, "to_i", flo_to_i, 0); + rb_define_method(cFloat, "to_f", flo_to_f, 0); + rb_define_method(cFloat, "abs", flo_abs, 0); +} -- cgit v1.2.3