diff options
author | tadf <tadf@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2008-03-16 00:23:43 +0000 |
---|---|---|
committer | tadf <tadf@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2008-03-16 00:23:43 +0000 |
commit | 6125552c27b40a8da9e162af2655feca82ac16d3 (patch) | |
tree | 8f77bc1b34603f4ce939aa4b5a77f5e8303b7df4 | |
parent | 2694b2f937681526550b8aabf798f033fa557049 (diff) | |
download | ruby-6125552c27b40a8da9e162af2655feca82ac16d3.tar.gz |
both complex and rational are now builtin classes.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@15783 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r-- | ChangeLog | 40 | ||||
-rw-r--r-- | bignum.c | 21 | ||||
-rw-r--r-- | common.mk | 8 | ||||
-rw-r--r-- | complex.c | 1533 | ||||
-rw-r--r-- | configure.in | 2 | ||||
-rw-r--r-- | gc.c | 15 | ||||
-rw-r--r-- | include/ruby/intern.h | 20 | ||||
-rw-r--r-- | include/ruby/ruby.h | 21 | ||||
-rw-r--r-- | inits.c | 4 | ||||
-rw-r--r-- | lib/complex.rb | 620 | ||||
-rw-r--r-- | lib/mathn.rb | 9 | ||||
-rw-r--r-- | lib/rational.rb | 528 | ||||
-rw-r--r-- | numeric.c | 69 | ||||
-rw-r--r-- | rational.c | 1111 | ||||
-rw-r--r-- | test/ruby/test_complex.rb | 1017 | ||||
-rw-r--r-- | test/ruby/test_rational.rb | 971 |
16 files changed, 4938 insertions, 1051 deletions
@@ -1,3 +1,43 @@ +Sun Mar 16 08:51:41 2008 Tadayoshi Funaba <tadf@dotrb.org> + + * include/ruby/intern.h: added some declarations. + + * include/ruby/ruby.h: ditto. + + * common.mk: added some entries. + + * configure.in: added a check for signbit. + + * lib/complex.rb: nearly all of core definitions have been removed. + + * lib/rational.rb: ditto. + + * lib/mathn.rb: some trivial adjustments. + + * complex.c: new. + + * rational.c: ditto. + + * numeric.c (flo_{quo,rdiv}, fix_fdiv): added. + + * numeric.c ({num,int}_{numerator,denominator}): ditto. + + * bignum.c (rb_big_fdiv): ditto. + + * numeric.c (fix_{quo,pow}): now may yield rational number. + + * bignum.c (rb_big_{quo,pow}): ditto. + + * numeric.c (rb_{int,flo}_induced_from): now can accept rational. + + * gc.c (gc_mark_children, obj_free): now detects complex and rational. + + * inits.c (rb_call_inits): now calls Init_{Complex,Rational}. + + * test/ruby/test_complex.rb: new. + + * test/ruby/test_rational.rb: ditto. + Sat Mar 15 17:48:48 2008 Yukihiro Matsumoto <matz@ruby-lang.org> * encoding.c (rb_enc_associate_index): pass unnecessary enc_capable(). @@ -1902,6 +1902,12 @@ static VALUE big_shift(VALUE x, int n) static VALUE rb_big_quo(VALUE x, VALUE y) { + return rb_funcall(rb_rational_raw1(x), '/', 1, y); +} + +static VALUE +rb_big_fdiv(VALUE x, VALUE y) +{ double dx = big2dbl(x); double dy; @@ -1947,7 +1953,7 @@ rb_big_quo(VALUE x, VALUE y) break; default: - return rb_num_coerce_bin(x, y, rb_intern("quo")); + return rb_num_coerce_bin(x, y, rb_intern("fdiv")); } return DOUBLE2NUM(dx / dy); } @@ -2025,13 +2031,19 @@ rb_big_pow(VALUE x, VALUE y) break; case T_BIGNUM: + if (rb_funcall(y, '<', 1, INT2FIX(0))) + return rb_funcall(rb_rational_raw1(x), rb_intern("**"), 1, y); + rb_warn("in a**b, b may be too big"); d = rb_big2dbl(y); break; case T_FIXNUM: yy = FIX2LONG(y); - if (yy > 0) { + + if (yy < 0) + return rb_funcall(rb_rational_raw1(x), rb_intern("**"), 1, y); + else { VALUE z = 0; SIGNED_VALUE mask; const long BIGLEN_LIMIT = 1024*1024 / SIZEOF_BDIGITS; @@ -2050,7 +2062,7 @@ rb_big_pow(VALUE x, VALUE y) } return bignorm(z); } - d = (double)yy; + /* NOTREACHED */ break; default: @@ -2590,7 +2602,8 @@ Init_Bignum(void) rb_define_method(rb_cBignum, "modulo", rb_big_modulo, 1); rb_define_method(rb_cBignum, "remainder", rb_big_remainder, 1); rb_define_method(rb_cBignum, "quo", rb_big_quo, 1); - rb_define_method(rb_cBignum, "fdiv", rb_big_quo, 1); + rb_define_method(rb_cBignum, "rdiv", rb_big_quo, 1); + rb_define_method(rb_cBignum, "fdiv", rb_big_fdiv, 1); rb_define_method(rb_cBignum, "**", rb_big_pow, 1); rb_define_method(rb_cBignum, "&", rb_big_and, 1); rb_define_method(rb_cBignum, "|", rb_big_or, 1); @@ -23,6 +23,7 @@ COMMONOBJS = array.$(OBJEXT) \ bignum.$(OBJEXT) \ class.$(OBJEXT) \ compar.$(OBJEXT) \ + complex.$(OBJEXT) \ dir.$(OBJEXT) \ enum.$(OBJEXT) \ enumerator.$(OBJEXT) \ @@ -45,6 +46,7 @@ COMMONOBJS = array.$(OBJEXT) \ prec.$(OBJEXT) \ random.$(OBJEXT) \ range.$(OBJEXT) \ + rational.$(OBJEXT) \ re.$(OBJEXT) \ regcomp.$(OBJEXT) \ regenc.$(OBJEXT) \ @@ -424,6 +426,9 @@ class.$(OBJEXT): {$(VPATH)}class.c {$(VPATH)}ruby.h {$(VPATH)}config.h \ compar.$(OBJEXT): {$(VPATH)}compar.c {$(VPATH)}ruby.h {$(VPATH)}config.h \ {$(VPATH)}defines.h {$(VPATH)}missing.h {$(VPATH)}intern.h \ {$(VPATH)}st.h +complex.$(OBJEXT): {$(VPATH)}complex.c {$(VPATH)}ruby.h {$(VPATH)}config.h \ + {$(VPATH)}defines.h {$(VPATH)}missing.h {$(VPATH)}intern.h \ + {$(VPATH)}st.h dir.$(OBJEXT): {$(VPATH)}dir.c {$(VPATH)}ruby.h {$(VPATH)}config.h \ {$(VPATH)}defines.h {$(VPATH)}missing.h {$(VPATH)}intern.h \ {$(VPATH)}st.h {$(VPATH)}util.h @@ -530,6 +535,9 @@ random.$(OBJEXT): {$(VPATH)}random.c {$(VPATH)}ruby.h {$(VPATH)}config.h \ range.$(OBJEXT): {$(VPATH)}range.c {$(VPATH)}ruby.h {$(VPATH)}config.h \ {$(VPATH)}defines.h {$(VPATH)}missing.h {$(VPATH)}intern.h \ {$(VPATH)}st.h +rational.$(OBJEXT): {$(VPATH)}rational.c {$(VPATH)}ruby.h {$(VPATH)}config.h \ + {$(VPATH)}defines.h {$(VPATH)}missing.h {$(VPATH)}intern.h \ + {$(VPATH)}st.h re.$(OBJEXT): {$(VPATH)}re.c {$(VPATH)}ruby.h {$(VPATH)}config.h \ {$(VPATH)}defines.h {$(VPATH)}missing.h {$(VPATH)}intern.h \ {$(VPATH)}st.h {$(VPATH)}re.h {$(VPATH)}regex.h {$(VPATH)}oniguruma.h \ diff --git a/complex.c b/complex.c new file mode 100644 index 0000000000..9982db0c90 --- /dev/null +++ b/complex.c @@ -0,0 +1,1533 @@ +/* + nucomp_core.c: Coded by Tadayoshi Funaba 2008 + + This implementation is based on Keiju Ishitsuka's Complex library + which is written in ruby. +*/ + +#include "ruby.h" +#include <math.h> + +#define NDEBUG +#include <assert.h> + +#ifndef COMPLEX_NAME +#define COMPLEX_NAME "Complex" +#endif + +#define ZERO INT2FIX(0) +#define ONE INT2FIX(1) +#define TWO INT2FIX(2) + +VALUE rb_cComplex; + +static ID id_Unify, id_abs, id_abs2, id_arg, id_atan2_bang, id_cmp, + id_coerce, id_conjugate, id_convert, id_cos, id_denominator, id_divmod, + id_equal_p, id_exact_p, id_exp_bang, id_expt, id_floor, id_format, + id_hypot, id_idiv, id_inspect, id_log_bang, id_negate, id_new, id_new_bang, + id_numerator, id_polar, id_quo, id_scalar_p, id_sin, id_sqrt, id_to_f, + id_to_i, id_to_r, id_to_s, id_truncate; + +#define f_add(x,y) rb_funcall(x, '+', 1, y) +#define f_div(x,y) rb_funcall(x, '/', 1, y) +#define f_gt_p(x,y) rb_funcall(x, '>', 1, y) +#define f_lt_p(x,y) rb_funcall(x, '<', 1, y) +#define f_mod(x,y) rb_funcall(x, '%', 1, y) +#define f_mul(x,y) rb_funcall(x, '*', 1, y) +#define f_sub(x,y) rb_funcall(x, '-', 1, y) +#define f_xor(x,y) rb_funcall(x, '^', 1, y) + +#define f_abs(x) rb_funcall(x, id_abs, 0) +#define f_abs2(x) rb_funcall(x, id_abs2, 0) +#define f_arg(x) rb_funcall(x, id_arg, 0) +#define f_conjugate(x) rb_funcall(x, id_conjugate, 0) +#define f_denominator(x) rb_funcall(x, id_denominator, 0) +#define f_exact_p(x) rb_funcall(x, id_exact_p, 0) +#define f_floor(x) rb_funcall(x, id_floor, 0) +#define f_negate(x) rb_funcall(x, id_negate, 0) +#define f_numerator(x) rb_funcall(x, id_numerator, 0) +#define f_polar(x) rb_funcall(x, id_polar, 0) +#define f_scalar_p(x) rb_funcall(x, id_scalar_p, 0) +#define f_to_f(x) rb_funcall(x, id_to_f, 0) +#define f_to_i(x) rb_funcall(x, id_to_i, 0) +#define f_to_r(x) rb_funcall(x, id_to_r, 0) +#define f_to_s(x) rb_funcall(x, id_to_s, 0) +#define f_truncate(x) rb_funcall(x, id_truncate, 0) +#define f_cmp(x,y) rb_funcall(x, id_cmp, 1, y) +#define f_coerce(x,y) rb_funcall(x, id_coerce, 1, y) +#define f_divmod(x,y) rb_funcall(x, id_divmod, 1, y) +#define f_equal_p(x,y) rb_funcall(x, id_equal_p, 1, y) +#define f_expt(x,y) rb_funcall(x, id_expt, 1, y) +#define f_idiv(x,y) rb_funcall(x, id_idiv, 1, y) +#define f_inspect(x) rb_funcall(x, id_inspect, 0) +#define f_quo(x,y) rb_funcall(x, id_quo, 1, y) + +#if 0 +#define m_cos(x) rb_funcall(rb_mMath, id_cos, 1, x) +#define m_exp_bang(x) rb_funcall(rb_mMath, id_exp_bang, 1, x) +#define m_log_bang(x) rb_funcall(rb_mMath, id_log_bang, 1, x) +#define m_sin(x) rb_funcall(rb_mMath, id_sin, 1, x) +#define m_sqrt(x) rb_funcall(rb_mMath, id_sqrt, 1, x) +#define m_atan2_bang(x,y) rb_funcall(rb_mMath, id_atan2_bang, 2, x, y) +#define m_hypot(x,y) rb_funcall(rb_mMath, id_hypot, 2, x, y) +#endif + +#define f_negative_p(x) f_lt_p(x, ZERO) +#define f_zero_p(x) f_equal_p(x, ZERO) +#define f_one_p(x) f_equal_p(x, ONE) +#define f_kind_of_p(x,c) rb_obj_is_kind_of(x, c) +#define k_numeric_p(x) f_kind_of_p(x, rb_cNumeric) +#define k_integer_p(x) f_kind_of_p(x, rb_cInteger) +#define k_float_p(x) f_kind_of_p(x, rb_cFloat) +#define k_rational_p(x) f_kind_of_p(x, rb_cRational) +#define k_complex_p(x) f_kind_of_p(x, rb_cComplex) + +#define f_boolcast(x) ((x) ? Qtrue : Qfalse) + +inline static VALUE +f_generic_p(VALUE x) +{ + switch (TYPE(x)) { + case T_FIXNUM: + case T_BIGNUM: + case T_FLOAT: + case T_RATIONAL: + return Qtrue; + default: + return Qfalse; + } +} + +static VALUE +nucomp_s_generic_p(VALUE klass, VALUE x) +{ + return f_generic_p(x); +} + +#define get_dat1(x) \ + struct RComplex *dat;\ + dat = ((struct RComplex *)(x)) + +#define get_dat2(x,y) \ + struct RComplex *adat, *bdat;\ + adat = ((struct RComplex *)(x));\ + bdat = ((struct RComplex *)(y)) + +inline static VALUE +nucomp_s_new_internal(VALUE klass, VALUE real, VALUE image) +{ + NEWOBJ(obj, struct RComplex); + OBJSETUP(obj, klass, T_COMPLEX); + + obj->real = real; + obj->image = image; + + return (VALUE)obj; +} + +static VALUE +nucomp_s_alloc(VALUE klass) +{ + return nucomp_s_new_internal(klass, ZERO, ZERO); +} + +static VALUE +nucomp_s_new_bang(int argc, VALUE *argv, VALUE klass) +{ + VALUE real, image; + + switch (rb_scan_args(argc, argv, "11", &real, &image)) { + case 1: + if (!k_numeric_p(real)) + real = f_to_i(real); + image = ZERO; + break; + default: + if (!k_numeric_p(real)) + real = f_to_i(real); + if (!k_numeric_p(image)) + image = f_to_i(image); + break; + } + + return nucomp_s_new_internal(klass, real, image); +} + +inline static VALUE +f_complex_new_bang1(VALUE klass, VALUE x) +{ + return nucomp_s_new_internal(klass, x, ZERO); +} + +inline static VALUE +f_complex_new_bang2(VALUE klass, VALUE x, VALUE y) +{ + return nucomp_s_new_internal(klass, x, y); +} + +#define f_unify_p(klass) rb_const_defined(klass, id_Unify) + +inline static VALUE +nucomp_s_canonicalize_internal(VALUE klass, VALUE real, VALUE image) +{ +#define CL_CANON +#ifdef CL_CANON + if (f_zero_p(image) && f_unify_p(klass) && + !k_float_p(real) && !k_float_p(image)) + return real; +#else + if (f_zero_p(image) && f_unify_p(klass)) + return real; +#endif + else if (f_scalar_p(real) && f_scalar_p(image)) + return nucomp_s_new_internal(klass, real, image); + else if (f_scalar_p(real)) { + get_dat1(image); + + return nucomp_s_new_internal(klass, + f_sub(real, dat->image), + f_add(ZERO, dat->real)); + } else if (f_scalar_p(image)) { + get_dat1(real); + + return nucomp_s_new_internal(klass, + dat->real, + f_add(dat->image, image)); + } else { + get_dat2(real, image); + + return nucomp_s_new_internal(klass, + f_sub(adat->real, bdat->image), + f_add(adat->image, bdat->real)); + } +} + +static VALUE +nucomp_s_canonicalize(int argc, VALUE *argv, VALUE klass) +{ + VALUE real, image; + + switch (rb_scan_args(argc, argv, "11", &real, &image)) { + case 1: + image = ZERO; + break; + } + + switch (TYPE(real)) { + case T_FIXNUM: + case T_BIGNUM: + case T_FLOAT: + break; + default: + if (!k_rational_p(real)) + rb_raise(rb_eArgError, "not a real"); + } + + switch (TYPE(image)) { + case T_FIXNUM: + case T_BIGNUM: + case T_FLOAT: + break; + default: + if (!k_rational_p(image)) + rb_raise(rb_eArgError, "not a real"); + } + + return nucomp_s_canonicalize_internal(klass, real, image); +} + +static VALUE +nucomp_s_new(int argc, VALUE *argv, VALUE klass) +{ + VALUE real, image; + + switch (rb_scan_args(argc, argv, "11", &real, &image)) { + case 1: + image = ZERO; + break; + } + + switch (TYPE(real)) { + case T_FIXNUM: + case T_BIGNUM: + case T_FLOAT: + break; + default: + if (!k_rational_p(real)) + rb_raise(rb_eArgError, "not a real"); + } + + switch (TYPE(image)) { + case T_FIXNUM: + case T_BIGNUM: + case T_FLOAT: + break; + default: + if (!k_rational_p(image)) + rb_raise(rb_eArgError, "not a real"); + } + + return nucomp_s_canonicalize_internal(klass, real, image); +} + +inline static VALUE +f_complex_new1(VALUE klass, VALUE x) +{ + assert(!k_complex_p(x)); + return nucomp_s_canonicalize_internal(klass, x, ZERO); +} + +inline static VALUE +f_complex_new2(VALUE klass, VALUE x, VALUE y) +{ + assert(!k_complex_p(x)); + return nucomp_s_canonicalize_internal(klass, x, y); +} + +static VALUE +nucomp_f_complex(int argc, VALUE *argv, VALUE klass) +{ + return rb_funcall2(rb_cComplex, id_convert, argc, argv); +} + +#if 1 +/* the following code is copied from math.c */ + +#include <errno.h> + +#define Need_Float(x) (x) = rb_Float(x) +#define Need_Float2(x,y) do {\ + Need_Float(x);\ + Need_Float(y);\ +} while (0) + +static void +domain_check(double x, char *msg) +{ + while(1) { + if (errno) { + rb_sys_fail(msg); + } + if (isnan(x)) { +#if defined(EDOM) + errno = EDOM; +#elif defined(ERANGE) + errno = ERANGE; +#endif + continue; + } + break; + } +} + +static VALUE +m_cos_bang(VALUE x) +{ + Need_Float(x); + return DOUBLE2NUM(cos(RFLOAT_VALUE(x))); +} + +static VALUE m_cos_bang(VALUE); +static VALUE m_cosh_bang(VALUE); +static VALUE m_sin_bang(VALUE); +static VALUE m_sinh_bang(VALUE); + +static VALUE +m_cos(VALUE x) +{ + get_dat1(x); + + if (f_generic_p(x)) + return m_cos_bang(x); + else + return f_complex_new2(rb_cComplex, + f_mul(m_cos_bang(dat->real), + m_cosh_bang(dat->image)), + f_mul(f_negate(m_sin_bang(dat->real)), + m_sinh_bang(dat->image))); +} + +#ifndef HAVE_COSH +double +cosh(double x) +{ + return (exp(x) + exp(-x)) / 2; +} +#endif + +static VALUE +m_cosh_bang(VALUE x) +{ + Need_Float(x); + return DOUBLE2NUM(cosh(RFLOAT_VALUE(x))); +} + +static VALUE +m_exp_bang(VALUE x) +{ + Need_Float(x); + return DOUBLE2NUM(exp(RFLOAT_VALUE(x))); +} + +static VALUE +m_log_bang(VALUE x) +{ + double d; + + Need_Float(x); + errno = 0; + d = log(RFLOAT_VALUE(x)); + domain_check(d, "log"); + return DOUBLE2NUM(d); +} + +static VALUE +m_sin_bang(VALUE x) +{ + Need_Float(x); + return DOUBLE2NUM(sin(RFLOAT_VALUE(x))); +} + +static VALUE +m_sin(VALUE x) +{ + get_dat1(x); + + if (f_generic_p(x)) + return m_sin_bang(x); + else + return f_complex_new2(rb_cComplex, + f_mul(m_sin_bang(dat->real), + m_cosh_bang(dat->image)), + f_mul(m_cos_bang(dat->real), + m_sinh_bang(dat->image))); +} + +#ifndef HAVE_SINH +double +sinh(double x) +{ + return (exp(x) - exp(-x)) / 2; +} +#endif + +static VALUE +m_sinh_bang(VALUE x) +{ + Need_Float(x); + return DOUBLE2NUM(sinh(RFLOAT_VALUE(x))); +} + +static VALUE +m_sqrt_bang(VALUE x) +{ + double d; + + Need_Float(x); + errno = 0; + d = sqrt(RFLOAT_VALUE(x)); + domain_check(d, "sqrt"); + return DOUBLE2NUM(d); +} + +static VALUE +m_sqrt(VALUE x) +{ + if (f_generic_p(x)) { + if (!f_negative_p(x)) + return m_sqrt_bang(x); + else + return f_complex_new2(rb_cComplex, ZERO, m_sqrt_bang(f_negate(x))); + } else { + get_dat1(x); + + if (f_negative_p(dat->image)) + return f_conjugate(m_sqrt(f_conjugate(x))); + else { + VALUE a = f_abs(x); + return f_complex_new2(rb_cComplex, + m_sqrt_bang(f_div(f_add(a, dat->real), TWO)), + m_sqrt_bang(f_div(f_sub(a, dat->real), TWO))); + } + } +} + +static VALUE +m_atan2_bang(VALUE y, VALUE x) +{ + Need_Float2(y, x); + return DOUBLE2NUM(atan2(RFLOAT_VALUE(y), RFLOAT_VALUE(x))); +} + +static VALUE +m_hypot(VALUE x, VALUE y) +{ + Need_Float2(x, y); + return DOUBLE2NUM(hypot(RFLOAT_VALUE(x), RFLOAT_VALUE(y))); +} +#endif + +static VALUE +nucomp_s_polar(VALUE klass, VALUE abs, VALUE arg) +{ + return f_complex_new2(klass, + f_mul(abs, m_cos(arg)), + f_mul(abs, m_sin(arg))); +} + +static VALUE +nucomp_real(VALUE self) +{ + get_dat1(self); + return dat->real; +} + +static VALUE +nucomp_image(VALUE self) +{ + get_dat1(self); + return dat->image; +} + +static VALUE +nucomp_add(VALUE self, VALUE other) +{ + switch (TYPE(other)) { + case T_FIXNUM: + case T_BIGNUM: + case T_FLOAT: + case T_RATIONAL: + { + get_dat1(self); + + return f_complex_new2(CLASS_OF(self), + f_add(dat->real, other), dat->image); + } + case T_COMPLEX: + { + VALUE real, image; + + get_dat2(self, other); + + real = f_add(adat->real, bdat->real); + image = f_add(adat->image, bdat->image); + + return f_complex_new2(CLASS_OF(self), real, image); + } + default: + { + VALUE a = f_coerce(other, self); + return f_add(RARRAY_PTR(a)[0], RARRAY_PTR(a)[1]); + } + } +} + +static VALUE +nucomp_sub(VALUE self, VALUE other) +{ + switch (TYPE(other)) { + case T_FIXNUM: + case T_BIGNUM: + case T_FLOAT: + case T_RATIONAL: + { + get_dat1(self); + + return f_complex_new2(CLASS_OF(self), + f_sub(dat->real, other), dat->image); + } + case T_COMPLEX: + { + VALUE real, image; + + get_dat2(self, other); + + real = f_sub(adat->real, bdat->real); + image = f_sub(adat->image, bdat->image); + + return f_complex_new2(CLASS_OF(self), real, image); + } + default: + { + VALUE a = f_coerce(other, self); + return f_add(RARRAY_PTR(a)[0], RARRAY_PTR(a)[1]); + } + } +} + +static VALUE +nucomp_mul(VALUE self, VALUE other) +{ + switch (TYPE(other)) { + case T_FIXNUM: + case T_BIGNUM: + case T_FLOAT: + case T_RATIONAL: + { + get_dat1(self); + + return f_complex_new2(CLASS_OF(self), + f_mul(dat->real, other), + f_mul(dat->image, other)); + } + case T_COMPLEX: + { + VALUE real, image; + + get_dat2(self, other); + + real = f_sub(f_mul(adat->real, bdat->real), + f_mul(adat->image, bdat->image)); + image = f_add(f_mul(adat->real, bdat->image), + f_mul(adat->image, bdat->real)); + + return f_complex_new2(CLASS_OF(self), real, image); + } + default: + { + VALUE a = f_coerce(other, self); + return f_mul(RARRAY_PTR(a)[0], RARRAY_PTR(a)[1]); + } + } +} + +static VALUE +nucomp_div(VALUE self, VALUE other) +{ + switch (TYPE(other)) { + case T_FIXNUM: + case T_BIGNUM: + case T_FLOAT: + case T_RATIONAL: + { + get_dat1(self); + + return f_complex_new2(CLASS_OF(self), + f_div(dat->real, other), + f_div(dat->image, other)); + } + case T_COMPLEX: + return f_div(f_mul(self, f_conjugate(other)), f_abs2(other)); + default: + { + VALUE a = f_coerce(other, self); + return f_div(RARRAY_PTR(a)[0], RARRAY_PTR(a)[1]); + } + } +} + +static VALUE +nucomp_rdiv(VALUE self, VALUE other) +{ + get_dat1(self); + + return f_div(f_complex_new2(CLASS_OF(self), + f_to_r(dat->real), + f_to_r(dat->image)), other); +} + +static VALUE +nucomp_fdiv(VALUE self, VALUE other) +{ + get_dat1(self); + + return f_div(f_complex_new2(CLASS_OF(self), + f_to_f(dat->real), + f_to_f(dat->image)), other); +} + +static VALUE +nucomp_expt(VALUE self, VALUE other) +{ + if (f_zero_p(other)) + return f_complex_new_bang1(CLASS_OF(self), ONE); + + if (k_rational_p(other) && f_one_p(f_denominator(other))) + other = f_numerator(other); /* good? */ + + switch (TYPE(other)) { + case T_FIXNUM: + case T_BIGNUM: + if (f_gt_p(other, ZERO)) { + VALUE x, z, n; + + x = self; + z = x; + n = f_sub(other, ONE); + + while (!f_zero_p(n)) { + VALUE a; + + while (a = f_divmod(n, TWO), + f_zero_p(RARRAY_PTR(a)[1])) { + get_dat1(x); + + x = f_complex_new2(CLASS_OF(self), + f_sub(f_mul(dat->real, dat->real), + f_mul(dat->image, dat->image)), + f_mul(f_mul(TWO, dat->real), dat->image)); + n = RARRAY_PTR(a)[0]; + } + z = f_mul(z, x); + n = f_sub(n, ONE); + } + return z; + } else { + return f_expt(f_div(f_to_r(ONE), self), f_negate(other)); + } + case T_FLOAT: + case T_RATIONAL: + { + VALUE a, r, theta; + + a = f_polar(self); + r = RARRAY_PTR(a)[0]; + theta = RARRAY_PTR(a)[1]; + return nucomp_s_polar(CLASS_OF(self), f_expt(r, other), + f_mul(theta, other)); + } + case T_COMPLEX: + { + VALUE a, r, theta, ore, oim, nr, ntheta; + + get_dat1(other); + + a = f_polar(self); + r = RARRAY_PTR(a)[0]; + theta = RARRAY_PTR(a)[1]; + + ore = dat->real; + oim = dat->image; + nr = m_exp_bang(f_sub(f_mul(ore, m_log_bang(r)), + f_mul(oim, theta))); + ntheta = f_add(f_mul(theta, ore), f_mul(oim, m_log_bang(r))); + return nucomp_s_polar(CLASS_OF(self), nr, ntheta); + } + default: + { + VALUE a = f_coerce(other, self); + return f_div(RARRAY_PTR(a)[0], RARRAY_PTR(a)[1]); + } + } +} + +static VALUE +nucomp_equal_p(VALUE self, VALUE other) +{ + switch (TYPE(other)) { + case T_FIXNUM: + case T_BIGNUM: + case T_FLOAT: + case T_RATIONAL: + { + get_dat1(self); + + return f_boolcast(f_equal_p(dat->real, other) && f_zero_p(dat->image)); + } + case T_COMPLEX: + { + get_dat2(self, other); + + return f_boolcast(f_equal_p(adat->real, bdat->real) && + f_equal_p(adat->image, bdat->image)); + } + default: + return f_equal_p(other, self); + } +} + +static VALUE +nucomp_coerce(VALUE self, VALUE other) +{ + switch (TYPE(other)) { + case T_FIXNUM: + case T_BIGNUM: + case T_FLOAT: + case T_RATIONAL: + return rb_assoc_new(f_complex_new_bang1(CLASS_OF(self), other), self); + } + + rb_raise(rb_eTypeError, "%s can't be coerced into %s", + rb_obj_classname(other), rb_obj_classname(self)); + return Qnil; +} + +static VALUE +nucomp_abs(VALUE self) +{ + get_dat1(self); + return m_sqrt(f_add(f_mul(dat->real, dat->real), + f_mul(dat->image, dat->image))); +} + +static VALUE +nucomp_abs2(VALUE self) +{ + get_dat1(self); + return f_add(f_mul(dat->real, dat->real), + f_mul(dat->image, dat->image)); +} + +static VALUE +nucomp_arg(VALUE self) +{ + get_dat1(self); + return m_atan2_bang(dat->image, dat->real); +} + +static VALUE +nucomp_polar(VALUE self) +{ + return rb_assoc_new(f_abs(self), f_arg(self)); +} + +static VALUE +nucomp_conjugate(VALUE self) +{ + get_dat1(self); + return f_complex_new2(CLASS_OF(self), dat->real, f_negate(dat->image)); +} + +static VALUE +nucomp_real_p(VALUE self) +{ + return Qfalse; +} + +static VALUE +nucomp_complex_p(VALUE self) +{ + return Qtrue; +} + +static VALUE +nucomp_exact_p(VALUE self) +{ + get_dat1(self); + return f_boolcast(f_exact_p(dat->real) && f_exact_p(dat->image)); +} + +static VALUE +nucomp_inexact_p(VALUE self) +{ + return f_boolcast(!nucomp_exact_p(self)); +} + +inline static long +i_gcd(long x, long y) +{ + long b; + + if (x < 0) + x = -x; + if (y < 0) + y = -y; + + if (x == 0) + return y; + if (y == 0) + return x; + + b = 0; + while ((x & 1) == 0 && (y & 1) == 0) { + b += 1; + x >>= 1; + y >>= 1; + } + + while ((x & 1) == 0) + x >>= 1; + + while ((y & 1) == 0) + y >>= 1; + + while (x != y) { + if (y > x) { + long t; + t = x; + x = y; + y = t; + } + x -= y; + while ((x & 1) == 0) + x >>= 1; + } + + return x << b; +} + +inline static VALUE +f_gcd(VALUE x, VALUE y) +{ + VALUE z; + + if (FIXNUM_P(x) && FIXNUM_P(y)) + return LONG2NUM(i_gcd(FIX2LONG(x), FIX2LONG(y))); + + if (f_negative_p(x)) + x = f_negate(x); + if (f_negative_p(y)) + y = f_negate(y); + + if (f_zero_p(x)) + return y; + if (f_zero_p(y)) + return x; + + for (;;) { + if (FIXNUM_P(x)) { + if (FIX2INT(x) == 0) + return y; + if (FIXNUM_P(y)) + return LONG2NUM(i_gcd(FIX2LONG(x), FIX2LONG(y))); + } + z = x; + x = f_mod(y, x); + y = z; + } + /* NOTREACHED */ +} + +static VALUE +f_lcm(VALUE x, VALUE y) +{ + if (f_zero_p(x) || f_zero_p(y)) + return ZERO; + else + return f_abs(f_mul(f_div(x, f_gcd(x, y)), y)); +} + +static VALUE +nucomp_denominator(VALUE self) +{ + get_dat1(self); + return f_lcm(f_denominator(dat->real), f_denominator(dat->image)); +} + +static VALUE +nucomp_numerator(VALUE self) +{ + VALUE cd; + + get_dat1(self); + + cd = f_denominator(self); + return f_complex_new2(CLASS_OF(self), + f_mul(f_numerator(dat->real), + f_div(cd, f_denominator(dat->real))), + f_mul(f_numerator(dat->image), + f_div(cd, f_denominator(dat->image)))); +} + +static VALUE +nucomp_hash(VALUE self) +{ + get_dat1(self); + return f_xor(dat->real, dat->image); +} + +#ifndef HAVE_SIGNBIT +#ifdef signbit +#define HAVE_SIGNBIT 1 +#endif +#endif + +inline static VALUE +f_signbit(VALUE x) +{ + switch (TYPE(x)) { + case T_FLOAT: +#ifdef HAVE_SIGNBIT + return f_boolcast(signbit(RFLOAT_VALUE(x))); +#else + { + char s[2]; + + (void)snprintf(s, sizeof s, "%.0f", RFLOAT_VALUE(x)); + + return f_boolcast(s[0] == '-'); + } +#endif + } + return f_negative_p(x); +} + +inline static VALUE +f_tzero_p(VALUE x) +{ + return f_boolcast(f_zero_p(x) && !f_signbit(x)); +} + +inline static VALUE +f_tpositive_p(VALUE x) +{ + return f_boolcast(!f_signbit(x)); +} + +static VALUE +nucomp_to_s(VALUE self) +{ + VALUE s, rezero, impos; + + get_dat1(self); + + rezero = f_tzero_p(dat->real); + impos = f_tpositive_p(dat->image); + + if (rezero) + s = rb_str_new2(""); + else { + s = f_to_s(dat->real); + rb_str_concat(s, rb_str_new2(!impos ? "-" : "+")); + } + + if (k_rational_p(dat->image) && + !f_one_p(f_denominator(dat->image))) { + rb_str_concat(s, rb_str_new2("(")); + rb_str_concat(s, f_to_s(rezero ? dat->image : f_abs(dat->image))); + rb_str_concat(s, rb_str_new2(")i")); + } else { + rb_str_concat(s, f_to_s(rezero ? dat->image : f_abs(dat->image))); + rb_str_concat(s, rb_str_new2("i")); + } + + return s; +} + +static VALUE +nucomp_inspect(VALUE self) +{ + VALUE s; + + get_dat1(self); + + s = rb_str_new2("Complex("); + rb_str_concat(s, f_inspect(dat->real)); + rb_str_concat(s, rb_str_new2(", ")); + rb_str_concat(s, f_inspect(dat->image)); + rb_str_concat(s, rb_str_new2(")")); + + return s; +} + +static VALUE +nucomp_marshal_dump(VALUE self) +{ + get_dat1(self); + return rb_assoc_new(dat->real, dat->image); +} + +static VALUE +nucomp_marshal_load(VALUE self, VALUE a) +{ + get_dat1(self); + dat->real = RARRAY_PTR(a)[0]; + dat->image = RARRAY_PTR(a)[1]; + return self; +} + +/* --- */ + +VALUE +rb_complex_raw(VALUE x, VALUE y) +{ + return nucomp_s_new_internal(rb_cComplex, x, y); +} + +VALUE +rb_complex_new(VALUE x, VALUE y) +{ + return nucomp_s_canonicalize_internal(rb_cComplex, x, y); +} + +static VALUE nucomp_s_convert(int argc, VALUE *argv, VALUE klass); + +VALUE +rb_Complex(VALUE x, VALUE y) +{ + VALUE a[2]; + a[0] = x; + a[1] = y; + return nucomp_s_convert(2, a, rb_cComplex); +} + +static VALUE +nucomp_scalar_p(VALUE self) +{ + return Qfalse; +} + +static VALUE +nucomp_to_i(VALUE self) +{ + get_dat1(self); + + if (k_float_p(dat->image) || !f_zero_p(dat->image)) { + VALUE s = f_to_s(self); + rb_raise(rb_eRangeError, "can't convert %s into Integer", + StringValuePtr(s)); + } + return f_to_i(dat->real); +} + +static VALUE +nucomp_to_f(VALUE self) +{ + get_dat1(self); + + if (k_float_p(dat->image) || !f_zero_p(dat->image)) { + VALUE s = f_to_s(self); + rb_raise(rb_eRangeError, "can't convert %s into Integer", + StringValuePtr(s)); + } + return f_to_f(dat->real); +} + +static VALUE +nucomp_to_r(VALUE self) +{ + get_dat1(self); + + if (k_float_p(dat->image) || !f_zero_p(dat->image)) { + VALUE s = f_to_s(self); + rb_raise(rb_eRangeError, "can't convert %s into Integer", + StringValuePtr(s)); + } + return f_to_r(dat->real); +} + +static VALUE +nilclass_to_c(VALUE self) +{ + return rb_complex_new1(INT2FIX(0)); +} + +static VALUE +numeric_to_c(VALUE self) +{ + return rb_complex_new1(self); +} + +static VALUE comp_pat1, comp_pat2, a_slash, a_dot_and_an_e, + image_garbages_pat, null_string, underscores_pat, an_underscore; + +#define DIGITS "(?:\\d(?:_\\d|\\d)*)" +#define NUMERATOR "(?:" DIGITS "?\\.)?" DIGITS "(?:[eE][-+]?" DIGITS ")?" +#define DENOMINATOR "[-+]?" DIGITS +#define NUMBER "[-+]?" NUMERATOR "(?:\\/" DENOMINATOR ")?" +#define NUMBERNOS NUMERATOR "(?:\\/" DENOMINATOR ")?" +#define PATTERN1 "\\A(" NUMBER "|\\(" NUMBER "\\))[iIjJ]" +#define PATTERN2 "\\A(" NUMBER ")([-+](?:" NUMBERNOS "|\\(" NUMBER "\\))[iIjJ])?" + +static void +make_patterns(void) +{ + static char *comp_pat1_source = PATTERN1; + static char *comp_pat2_source = PATTERN2; + static char *image_garbages_pat_source = "[+\\(\\)iIjJ]"; + static char *underscores_pat_source = "_+"; + + comp_pat1 = rb_reg_new(comp_pat1_source, strlen(comp_pat1_source), 0); + rb_global_variable(&comp_pat1); + + comp_pat2 = rb_reg_new(comp_pat2_source, strlen(comp_pat2_source), 0); + rb_global_variable(&comp_pat2); + + a_slash = rb_str_new2("/"); + rb_global_variable(&a_slash); + + a_dot_and_an_e = rb_str_new2(".eE"); + rb_global_variable(&a_dot_and_an_e); + + image_garbages_pat = rb_reg_new(image_garbages_pat_source, + strlen(image_garbages_pat_source), 0); + rb_global_variable(&image_garbages_pat); + + null_string = rb_str_new2(""); + rb_global_variable(&null_string); + + underscores_pat = rb_reg_new(underscores_pat_source, + strlen(underscores_pat_source), 0); + rb_global_variable(&underscores_pat); + + an_underscore = rb_str_new2("_"); + rb_global_variable(&an_underscore); +} + +#define id_strip rb_intern("strip") +#define f_strip(x) rb_funcall(x, id_strip, 0) + +#define id_match rb_intern("match") +#define f_match(x,y) rb_funcall(x, id_match, 1, y) + +#define id_aref rb_intern("[]") +#define f_aref(x,y) rb_funcall(x, id_aref, 1, y) + +#define id_post_match rb_intern("post_match") +#define f_post_match(x) rb_funcall(x, id_post_match, 0) + +#define id_split rb_intern("split") +#define f_split(x,y) rb_funcall(x, id_split, 1, y) + +#define id_include_p rb_intern("include?") +#define f_include_p(x,y) rb_funcall(x, id_include_p, 1, y) + +#define id_count rb_intern("count") +#define f_count(x,y) rb_funcall(x, id_count, 1, y) + +#define id_gsub_bang rb_intern("gsub!") +#define f_gsub_bang(x,y,z) rb_funcall(x, id_gsub_bang, 2, y, z) + +static VALUE +string_to_c_internal(VALUE self) +{ + VALUE s; + + s = f_strip(self); + + if (RSTRING_LEN(s) == 0) + return rb_assoc_new(Qnil, self); + + { + VALUE m, sr, si, re, r, i; + + m = f_match(comp_pat1, s); + if (!NIL_P(m)) { + sr = Qnil; + si = f_aref(m, INT2FIX(1)); + re = f_post_match(m); + } + if (NIL_P(m)) { + m = f_match(comp_pat2, s); + if (NIL_P(m)) + return rb_assoc_new(Qnil, self); + sr = f_aref(m, INT2FIX(1)); + si = f_aref(m, INT2FIX(2)); + re = f_post_match(m); + } + r = INT2FIX(0); + i = INT2FIX(0); + if (!NIL_P(sr)) { + if (f_include_p(sr, a_slash)) + r = f_to_r(sr); + else if (f_gt_p(f_count(sr, a_dot_and_an_e), INT2FIX(0))) + r = f_to_f(sr); + else + r = f_to_i(sr); + } + if (!NIL_P(si)) { + f_gsub_bang(si, image_garbages_pat, null_string); + if (f_include_p(si, a_slash)) + i = f_to_r(si); + else if (f_gt_p(f_count(si, a_dot_and_an_e), INT2FIX(0))) + i = f_to_f(si); + else + i = f_to_i(si); + } + return rb_assoc_new(rb_complex_new2(r, i), re); + } +} + +static VALUE +string_to_c_strict(VALUE self) +{ + VALUE a = string_to_c_internal(self); + if (NIL_P(RARRAY_PTR(a)[0]) || RSTRING_LEN(RARRAY_PTR(a)[1]) > 0) { + VALUE s = f_inspect(self); + rb_raise(rb_eArgError, "invalid value for Complex: %s", + StringValuePtr(s)); + } + return RARRAY_PTR(a)[0]; +} + +#define id_gsub rb_intern("gsub") +#define f_gsub(x,y,z) rb_funcall(x, id_gsub, 2, y, z) + +static VALUE +string_to_c(VALUE self) +{ + VALUE s = f_gsub(self, underscores_pat, an_underscore); + VALUE a = string_to_c_internal(s); + if (!NIL_P(RARRAY_PTR(a)[0])) + return RARRAY_PTR(a)[0]; + return rb_complex_new1(INT2FIX(0)); +} + +static VALUE +nucomp_s_convert(int argc, VALUE *argv, VALUE klass) +{ + VALUE a1, a2; + + a1 = Qnil; + a2 = Qnil; + rb_scan_args(argc, argv, "02", &a1, &a2); + + switch (TYPE(a1)) { + case T_FIXNUM: + case T_BIGNUM: + case T_FLOAT: + break; + case T_STRING: + a1 = string_to_c_strict(a1); + break; + } + + switch (TYPE(a2)) { + case T_FIXNUM: + case T_BIGNUM: + case T_FLOAT: + break; + case T_STRING: + a2 = string_to_c_strict(a2); + break; + } + + switch (TYPE(a1)) { + case T_COMPLEX: + { + get_dat1(a1); + + if (!k_float_p(dat->image) && f_zero_p(dat->image)) + a1 = dat->real; + } + } + + switch (TYPE(a2)) { + case T_COMPLEX: + { + get_dat1(a2); + + if (!k_float_p(dat->image) && f_zero_p(dat->image)) + a2 = dat->real; + } + } + + switch (TYPE(a1)) { + case T_COMPLEX: + if (NIL_P(a2) || f_zero_p(a2)) + return a1; + } + + { + VALUE argv2[2]; + argv2[0] = a1; + argv2[1] = a2; + return nucomp_s_new(argc, argv2, klass); + } +} + +/* --- */ + +#define id_Complex rb_intern("Complex") + +static VALUE +numeric_re(VALUE self) +{ + return rb_Complex1(self); +} + +static VALUE +numeric_im(VALUE self) +{ + return rb_Complex2(ZERO, self); +} + +static VALUE +numeric_real(VALUE self) +{ + return self; +} + +static VALUE +numeric_image(VALUE self) +{ + return INT2FIX(0); +} + +#define id_PI rb_intern("PI") + +static VALUE +numeric_arg(VALUE self) +{ + if (!f_negative_p(self)) + return INT2FIX(0); + return rb_const_get(rb_mMath, id_PI); +} + +static VALUE +numeric_polar(VALUE self) +{ + return rb_assoc_new(f_abs(self), f_arg(self)); +} + +static VALUE +numeric_conjugate(VALUE self) +{ + return self; +} + +void +Init_Complex(void) +{ + assert(fprintf(stderr, "assert() is now active\n")); + + id_Unify = rb_intern("Unify"); + id_abs = rb_intern("abs"); + id_abs2 = rb_intern("abs2"); + id_arg = rb_intern("arg"); + id_atan2_bang = rb_intern("atan2!"); + id_cmp = rb_intern("<=>"); + id_coerce = rb_intern("coerce"); + id_conjugate = rb_intern("conjugate"); + id_convert = rb_intern("convert"); + id_cos = rb_intern("cos"); + id_denominator = rb_intern("denominator"); + id_divmod = rb_intern("divmod"); + id_equal_p = rb_intern("=="); + id_exact_p = rb_intern("exact?"); + id_exp_bang = rb_intern("exp!"); + id_expt = rb_intern("**"); + id_floor = rb_intern("floor"); + id_format = rb_intern("format"); + id_hypot = rb_intern("hypot"); + id_idiv = rb_intern("div"); + id_inspect = rb_intern("inspect"); + id_log_bang = rb_intern("log!"); + id_negate = rb_intern("-@"); + id_new = rb_intern("new"); + id_new_bang = rb_intern("new!"); + id_numerator = rb_intern("numerator"); + id_polar = rb_intern("polar"); + id_quo = rb_intern("quo"); + id_scalar_p = rb_intern("scalar?"); + id_sin = rb_intern("sin"); + id_sqrt = rb_intern("sqrt"); + id_to_f = rb_intern("to_f"); + id_to_i = rb_intern("to_i"); + id_to_r = rb_intern("to_r"); + id_to_s = rb_intern("to_s"); + id_truncate = rb_intern("truncate"); + + rb_cComplex = rb_define_class(COMPLEX_NAME, rb_cNumeric); + + rb_define_alloc_func(rb_cComplex, nucomp_s_alloc); + rb_funcall(rb_cComplex, rb_intern("private_class_method"), 1, + ID2SYM(rb_intern("allocate"))); + + rb_define_singleton_method(rb_cComplex, "generic?", nucomp_s_generic_p, 1); + + rb_define_singleton_method(rb_cComplex, "new!", nucomp_s_new_bang, -1); + rb_funcall(rb_cComplex, rb_intern("private_class_method"), 1, + ID2SYM(rb_intern("new!"))); + + rb_define_singleton_method(rb_cComplex, "new", nucomp_s_new, -1); + rb_funcall(rb_cComplex, rb_intern("private_class_method"), 1, + ID2SYM(rb_intern("new"))); + +#if 0 + rb_define_singleton_method(rb_cComplex, "rect", nucomp_s_new, -1); + rb_define_singleton_method(rb_cComplex, "rectangular", nucomp_s_new, -1); +#endif + rb_define_singleton_method(rb_cComplex, "polar", nucomp_s_polar, 2); + + rb_define_global_function(COMPLEX_NAME, nucomp_f_complex, -1); + + rb_undef_method(rb_cComplex, "<"); + rb_undef_method(rb_cComplex, "<="); + rb_undef_method(rb_cComplex, "<=>"); + rb_undef_method(rb_cComplex, ">"); + rb_undef_method(rb_cComplex, ">="); + rb_undef_method(rb_cComplex, "between?"); + rb_undef_method(rb_cComplex, "divmod"); + rb_undef_method(rb_cComplex, "floor"); + rb_undef_method(rb_cComplex, "ceil"); + rb_undef_method(rb_cComplex, "modulo"); + rb_undef_method(rb_cComplex, "round"); + rb_undef_method(rb_cComplex, "step"); + rb_undef_method(rb_cComplex, "truncate"); + +#if NUBY + rb_undef_method(rb_cComplex, "//"); +#endif + + rb_define_method(rb_cComplex, "real", nucomp_real, 0); + rb_define_method(rb_cComplex, "image", nucomp_image, 0); + rb_define_method(rb_cComplex, "imag", nucomp_image, 0); + + rb_define_method(rb_cComplex, "+", nucomp_add, 1); + rb_define_method(rb_cComplex, "-", nucomp_sub, 1); + rb_define_method(rb_cComplex, "*", nucomp_mul, 1); + rb_define_method(rb_cComplex, "/", nucomp_div, 1); + rb_define_method(rb_cComplex, "quo", nucomp_rdiv, 1); + rb_define_method(rb_cComplex, "rdiv", nucomp_rdiv, 1); + rb_define_method(rb_cComplex, "fdiv", nucomp_rdiv, 1); + rb_define_method(rb_cComplex, "**", nucomp_expt, 1); + + rb_define_method(rb_cComplex, "==", nucomp_equal_p, 1); + rb_define_method(rb_cComplex, "coerce", nucomp_coerce, 1); + + rb_define_method(rb_cComplex, "abs", nucomp_abs, 0); +#if 0 + rb_define_method(rb_cComplex, "magnitude", nucomp_abs, 0); +#endif + rb_define_method(rb_cComplex, "abs2", nucomp_abs2, 0); + rb_define_method(rb_cComplex, "arg", nucomp_arg, 0); + rb_define_method(rb_cComplex, "angle", nucomp_arg, 0); + rb_define_method(rb_cComplex, "polar", nucomp_polar, 0); + rb_define_method(rb_cComplex, "conjugate", nucomp_conjugate, 0); + rb_define_method(rb_cComplex, "conj", nucomp_conjugate, 0); +#if 0 + rb_define_method(rb_cComplex, "~", nucomp_conjugate, 0); /* gcc */ +#endif + +#if 0 + rb_define_method(rb_cComplex, "real?", nucomp_real_p, 0); + rb_define_method(rb_cComplex, "complex?", nucomp_complex_p, 0); + rb_define_method(rb_cComplex, "exact?", nucomp_exact_p, 0); + rb_define_method(rb_cComplex, "inexact?", nucomp_inexact_p, 0); +#endif + + rb_define_method(rb_cComplex, "numerator", nucomp_numerator, 0); + rb_define_method(rb_cComplex, "denominator", nucomp_denominator, 0); + + rb_define_method(rb_cComplex, "hash", nucomp_hash, 0); + + rb_define_method(rb_cComplex, "to_s", nucomp_to_s, 0); + rb_define_method(rb_cComplex, "inspect", nucomp_inspect, 0); + + rb_define_method(rb_cComplex, "marshal_dump", nucomp_marshal_dump, 0); + rb_define_method(rb_cComplex, "marshal_load", nucomp_marshal_load, 1); + + /* --- */ + + rb_define_method(rb_cComplex, "scalar?", nucomp_scalar_p, 0); + rb_define_method(rb_cComplex, "to_i", nucomp_to_i, 0); + rb_define_method(rb_cComplex, "to_f", nucomp_to_f, 0); + rb_define_method(rb_cComplex, "to_r", nucomp_to_r, 0); + rb_define_method(rb_cNilClass, "to_c", nilclass_to_c, 0); + rb_define_method(rb_cNumeric, "to_c", numeric_to_c, 0); + + make_patterns(); + + rb_define_method(rb_cString, "to_c", string_to_c, 0); + + rb_define_singleton_method(rb_cComplex, "convert", nucomp_s_convert, -1); + rb_funcall(rb_cComplex, rb_intern("private_class_method"), 1, + ID2SYM(rb_intern("convert"))); + + /* --- */ + + rb_define_method(rb_cNumeric, "re", numeric_re, 0); + rb_define_method(rb_cNumeric, "im", numeric_im, 0); + rb_define_method(rb_cNumeric, "real", numeric_real, 0); + rb_define_method(rb_cNumeric, "image", numeric_image, 0); + rb_define_method(rb_cNumeric, "imag", numeric_image, 0); + rb_define_method(rb_cNumeric, "arg", numeric_arg, 0); + rb_define_method(rb_cNumeric, "angle", numeric_arg, 0); + rb_define_method(rb_cNumeric, "polar", numeric_polar, 0); + rb_define_method(rb_cNumeric, "conjugate", numeric_conjugate, 0); + rb_define_method(rb_cNumeric, "conj", numeric_conjugate, 0); + + rb_define_const(rb_cComplex, "I", + f_complex_new_bang2(rb_cComplex, ZERO, ONE)); +} diff --git a/configure.in b/configure.in index 49918a31ca..253d4b8af1 100644 --- a/configure.in +++ b/configure.in @@ -693,7 +693,7 @@ AC_CHECK_FUNCS(fmod killpg wait4 waitpid fork spawnv syscall chroot fsync getcwd getpgrp setpgrp getpgid setpgid initgroups getgroups setgroups\ getpriority getrlimit setrlimit sysconf group_member\ dlopen sigprocmask sigaction sigsetjmp _setjmp vsnprintf snprintf\ - setsid telldir seekdir fchmod cosh sinh tanh log2 round\ + setsid telldir seekdir fchmod cosh sinh tanh log2 round signbit\ setuid setgid daemon select_large_fdset setenv unsetenv\ mktime timegm clock_gettime gettimeofday) AC_ARG_ENABLE(setreuid, @@ -127,6 +127,8 @@ typedef struct RVALUE { struct RFile file; struct RNode node; struct RMatch match; + struct RRational rational; + struct RComplex complex; } as; #ifdef GC_DEBUG char *file; @@ -1128,6 +1130,16 @@ gc_mark_children(VALUE ptr, int lev) } break; + case T_RATIONAL: + gc_mark(obj->as.rational.num, lev); + gc_mark(obj->as.rational.den, lev); + break; + + case T_COMPLEX: + gc_mark(obj->as.complex.real, lev); + gc_mark(obj->as.complex.image, lev); + break; + case T_STRUCT: { long len = RSTRUCT_LEN(obj); @@ -1369,6 +1381,9 @@ obj_free(VALUE obj) rb_io_fptr_finalize(RANY(obj)->as.file.fptr); } break; + case T_RATIONAL: + case T_COMPLEX: + break; case T_ICLASS: /* iClass shares table with the module */ break; diff --git a/include/ruby/intern.h b/include/ruby/intern.h index 2d29f44fc8..d51bc67636 100644 --- a/include/ruby/intern.h +++ b/include/ruby/intern.h @@ -117,6 +117,26 @@ VALUE rb_big_or(VALUE, VALUE); VALUE rb_big_xor(VALUE, VALUE); VALUE rb_big_lshift(VALUE, VALUE); VALUE rb_big_rshift(VALUE, VALUE); +/* rational.c */ +VALUE rb_rational_raw(VALUE, VALUE); +#define rb_rational_raw1(x) rb_rational_raw(x, INT2FIX(1)) +#define rb_rational_raw2(x,y) rb_rational_raw(x, y) +VALUE rb_rational_new(VALUE, VALUE); +#define rb_rational_new1(x) rb_rational_new(x, INT2FIX(1)) +#define rb_rational_new2(x,y) rb_rational_new(x, y) +VALUE rb_Rational(VALUE, VALUE); +#define rb_Rational1(x) rb_Rational(x, INT2FIX(1)) +#define rb_Rational2(x,y) rb_Rational(x, y) +/* complex.c */ +VALUE rb_complex_raw(VALUE, VALUE); +#define rb_complex_raw1(x) rb_complex_raw(x, INT2FIX(0)) +#define rb_complex_raw2(x,y) rb_complex_raw(x, y) +VALUE rb_complex_new(VALUE, VALUE); +#define rb_complex_new1(x) rb_complex_new(x, INT2FIX(0)) +#define rb_complex_new2(x,y) rb_complex_new(x, y) +VALUE rb_Complex(VALUE, VALUE); +#define rb_Complex1(x) rb_Complex(x, INT2FIX(0)) +#define rb_Complex2(x,y) rb_Complex(x, y) /* class.c */ VALUE rb_class_boot(VALUE); VALUE rb_class_new(VALUE); diff --git a/include/ruby/ruby.h b/include/ruby/ruby.h index 1876ea4d71..c289763841 100644 --- a/include/ruby/ruby.h +++ b/include/ruby/ruby.h @@ -273,6 +273,11 @@ enum ruby_value_type { RUBY_T_SYMBOL = 0x14, #define T_SYMBOL RUBY_T_SYMBOL + RUBY_T_RATIONAL = 0x15, +#define T_RATIONAL RUBY_T_RATIONAL + RUBY_T_COMPLEX = 0x16, +#define T_COMPLEX RUBY_T_COMPLEX + RUBY_T_VALUES = 0x1a, #define T_VALUES RUBY_T_VALUES RUBY_T_BLOCK = 0x1b, @@ -522,6 +527,18 @@ struct RFile { struct rb_io_t *fptr; }; +struct RRational { + struct RBasic basic; + VALUE num; + VALUE den; +}; + +struct RComplex { + struct RBasic basic; + VALUE real; + VALUE image; +}; + struct RData { struct RBasic basic; void (*dmark)(void*); @@ -622,6 +639,8 @@ struct RBignum { #define RSTRUCT(obj) (R_CAST(RStruct)(obj)) #define RBIGNUM(obj) (R_CAST(RBignum)(obj)) #define RFILE(obj) (R_CAST(RFile)(obj)) +#define RRATIONAL(obj) (R_CAST(RRational)(obj)) +#define RCOMPLEX(obj) (R_CAST(RComplex)(obj)) #define RVALUES(obj) (R_CAST(RValues)(obj)) #define FL_SINGLETON FL_USER0 @@ -851,6 +870,8 @@ RUBY_EXTERN VALUE rb_cNilClass; RUBY_EXTERN VALUE rb_cNumeric; RUBY_EXTERN VALUE rb_cProc; RUBY_EXTERN VALUE rb_cRange; +RUBY_EXTERN VALUE rb_cRational; +RUBY_EXTERN VALUE rb_cComplex; RUBY_EXTERN VALUE rb_cRegexp; RUBY_EXTERN VALUE rb_cStat; RUBY_EXTERN VALUE rb_cString; @@ -15,6 +15,7 @@ void Init_Array(void); void Init_Bignum(void); void Init_Binding(void); void Init_Comparable(void); +void Init_Complex(void); void Init_transcode(void); void Init_Dir(void); void Init_Enumerable(void); @@ -39,6 +40,7 @@ void Init_id(void); void Init_process(void); void Init_Random(void); void Init_Range(void); +void Init_Rational(void); void Init_Regexp(void); void Init_signal(void); void Init_String(void); @@ -96,5 +98,7 @@ rb_call_inits() Init_ISeq(); Init_Thread(); Init_Cont(); + Init_Rational(); + Init_Complex(); Init_version(); } diff --git a/lib/complex.rb b/lib/complex.rb index 9a621c033f..505b0120e3 100644 --- a/lib/complex.rb +++ b/lib/complex.rb @@ -1,473 +1,90 @@ -# -# complex.rb - -# $Release Version: 0.5 $ -# $Revision: 1.3 $ -# by Keiju ISHITSUKA(SHL Japan Inc.) -# -# ---- -# -# complex.rb implements the Complex class for complex numbers. Additionally, -# some methods in other Numeric classes are redefined or added to allow greater -# interoperability with Complex numbers. -# -# Complex numbers can be created in the following manner: -# - <tt>Complex(a, b)</tt> -# - <tt>Complex.polar(radius, theta)</tt> -# -# Additionally, note the following: -# - <tt>Complex::I</tt> (the mathematical constant <i>i</i>) -# - <tt>Numeric#im</tt> (e.g. <tt>5.im -> 0+5i</tt>) -# -# The following +Math+ module methods are redefined to handle Complex arguments. -# They will work as normal with non-Complex arguments. -# sqrt exp cos sin tan log log10 -# cosh sinh tanh acos asin atan atan2 acosh asinh atanh -# - - -# -# Numeric is a built-in class on which Fixnum, Bignum, etc., are based. Here -# some methods are added so that all number types can be treated to some extent -# as Complex numbers. -# -class Numeric - # - # Returns a Complex number <tt>(0,<i>self</i>)</tt>. - # - def im - Complex(0, self) - end - - # - # The real part of a complex number, i.e. <i>self</i>. - # - def real - self - end - - # - # The imaginary part of a complex number, i.e. 0. - # - def image - 0 - end - alias imag image - - # - # See Complex#arg. - # - def arg - if self >= 0 - return 0 - else - return Math::PI - end - end - alias angle arg - - # - # See Complex#polar. - # - def polar - return abs, arg - end - - # - # See Complex#conjugate (short answer: returns <i>self</i>). - # - def conjugate - self - end - alias conj conjugate -end - - -# -# Creates a Complex number. +a+ and +b+ should be Numeric. The result will be -# <tt>a+bi</tt>. -# -def Complex(a, b = 0) - if b == 0 and (a.kind_of?(Complex) or defined? Complex::Unify) - a - elsif a.scalar? and b.scalar? - # Don't delete for -0.0 - Complex.new(a, b) - else - Complex.new( a.real-b.imag, a.imag+b.real ) - end -end - -# -# The complex number class. See complex.rb for an overview. -# -class Complex < Numeric - @RCS_ID='-$Id: complex.rb,v 1.3 1998/07/08 10:05:28 keiju Exp keiju $-' - - undef step - undef <, <=, <=>, >, >= - undef between? - undef div, divmod, modulo - undef floor, truncate, ceil, round - - def scalar? - false - end - - def Complex.generic?(other) # :nodoc: - other.kind_of?(Integer) or - other.kind_of?(Float) or - (defined?(Rational) and other.kind_of?(Rational)) - end - - # - # Creates a +Complex+ number in terms of +r+ (radius) and +theta+ (angle). - # - def Complex.polar(r, theta) - Complex(r*Math.cos(theta), r*Math.sin(theta)) - end - - # - # Creates a +Complex+ number <tt>a</tt>+<tt>b</tt><i>i</i>. - # - def Complex.new!(a, b=0) - new(a,b) - end - - def initialize(a, b) - raise TypeError, "non numeric 1st arg `#{a.inspect}'" if !a.kind_of? Numeric - raise TypeError, "`#{a.inspect}' for 1st arg" if a.kind_of? Complex - raise TypeError, "non numeric 2nd arg `#{b.inspect}'" if !b.kind_of? Numeric - raise TypeError, "`#{b.inspect}' for 2nd arg" if b.kind_of? Complex - @real = a - @image = b - end - - # - # Addition with real or complex number. - # - def + (other) - if other.kind_of?(Complex) - re = @real + other.real - im = @image + other.image - Complex(re, im) - elsif Complex.generic?(other) - Complex(@real + other, @image) - else - x , y = other.coerce(self) - x + y - end - end - - # - # Subtraction with real or complex number. - # - def - (other) - if other.kind_of?(Complex) - re = @real - other.real - im = @image - other.image - Complex(re, im) - elsif Complex.generic?(other) - Complex(@real - other, @image) - else - x , y = other.coerce(self) - x - y - end - end - - # - # Multiplication with real or complex number. - # - def * (other) - if other.kind_of?(Complex) - re = @real*other.real - @image*other.image - im = @real*other.image + @image*other.real - Complex(re, im) - elsif Complex.generic?(other) - Complex(@real * other, @image * other) - else - x , y = other.coerce(self) - x * y - end - end - - # - # Division by real or complex number. - # - def / (other) - if other.kind_of?(Complex) - self*other.conjugate/other.abs2 - elsif Complex.generic?(other) - Complex(@real/other, @image/other) - else - x, y = other.coerce(self) - x/y - end - end +class Integer - def quo(other) - Complex(@real.quo(1), @image.quo(1)) / other - end - - # - # Raise this complex number to the given (real or complex) power. - # - def ** (other) - if other == 0 - return Complex(1) - end - if other.kind_of?(Complex) - r, theta = polar - ore = other.real - oim = other.image - nr = Math.exp!(ore*Math.log!(r) - oim * theta) - ntheta = theta*ore + oim*Math.log!(r) - Complex.polar(nr, ntheta) - elsif other.kind_of?(Integer) - if other > 0 - x = self - z = x - n = other - 1 - while n != 0 - while (div, mod = n.divmod(2) - mod == 0) - x = Complex(x.real*x.real - x.image*x.image, 2*x.real*x.image) - n = div - end - z *= x - n -= 1 - end - z - else - if defined? Rational - (Rational(1) / self) ** -other - else - self ** Float(other) - end - end - elsif Complex.generic?(other) - r, theta = polar - Complex.polar(r**other, theta*other) - else - x, y = other.coerce(self) - x**y - end - end - - # - # Remainder after division by a real or complex number. - # - -=begin - def % (other) - if other.kind_of?(Complex) - Complex(@real % other.real, @image % other.image) - elsif Complex.generic?(other) - Complex(@real % other, @image % other) - else - x , y = other.coerce(self) - x % y - end - end -=end - -#-- -# def divmod(other) -# if other.kind_of?(Complex) -# rdiv, rmod = @real.divmod(other.real) -# idiv, imod = @image.divmod(other.image) -# return Complex(rdiv, idiv), Complex(rmod, rmod) -# elsif Complex.generic?(other) -# Complex(@real.divmod(other), @image.divmod(other)) -# else -# x , y = other.coerce(self) -# x.divmod(y) -# end -# end -#++ - - # - # Absolute value (aka modulus): distance from the zero point on the complex - # plane. - # - def abs - Math.hypot(@real, @image) - end - - # - # Square of the absolute value. - # - def abs2 - @real*@real + @image*@image - end - - # - # Argument (angle from (1,0) on the complex plane). - # - def arg - Math.atan2!(@image, @real) - end - alias angle arg - - # - # Returns the absolute value _and_ the argument. - # - def polar - return abs, arg - end - - # - # Complex conjugate (<tt>z + z.conjugate = 2 * z.real</tt>). - # - def conjugate - Complex(@real, -@image) - end - alias conj conjugate - - # - # Test for numerical equality (<tt>a == a + 0<i>i</i></tt>). - # - def == (other) - if other.kind_of?(Complex) - @real == other.real and @image == other.image - elsif Complex.generic?(other) - @real == other and @image == 0 - else - other == self + def gcd(other) + min = self.abs + max = other.abs + while min > 0 + tmp = min + min = max % min + max = tmp end + max end - # - # Attempts to coerce +other+ to a Complex number. - # - def coerce(other) - if Complex.generic?(other) - return Complex.new!(other), self + def lcm(other) + if self.zero? or other.zero? + 0 else - super + (self.div(self.gcd(other)) * other).abs end end - # - # FIXME - # - def denominator - @real.denominator.lcm(@image.denominator) - end - - # - # FIXME - # - def numerator - cd = denominator - Complex(@real.numerator*(cd/@real.denominator), - @image.numerator*(cd/@image.denominator)) - end - - # - # Standard string representation of the complex number. - # - def to_s - if @real != 0 - if defined?(Rational) and @image.kind_of?(Rational) and @image.denominator != 1 - if @image >= 0 - @real.to_s+"+("+@image.to_s+")i" - else - @real.to_s+"-("+(-@image).to_s+")i" - end - else - if @image >= 0 - @real.to_s+"+"+@image.to_s+"i" - else - @real.to_s+"-"+(-@image).to_s+"i" - end - end + def gcdlcm(other) + gcd = self.gcd(other) + if self.zero? or other.zero? + [gcd, 0] else - if defined?(Rational) and @image.kind_of?(Rational) and @image.denominator != 1 - "("+@image.to_s+")i" - else - @image.to_s+"i" - end + [gcd, (self.div(gcd) * other).abs] end end - - # - # Returns a hash code for the complex number. - # - def hash - @real.hash ^ @image.hash - end - - # - # Returns "<tt>Complex(<i>real</i>, <i>image</i>)</tt>". - # - def inspect - sprintf("Complex(%s, %s)", @real.inspect, @image.inspect) - end - - - # - # +I+ is the imaginary number. It exists at point (0,1) on the complex plane. - # - I = Complex(0,1) - - # The real part of a complex number. - attr_reader :real - - # The imaginary part of a complex number. - attr_reader :image - alias imag image - -end - -class Integer - - unless defined?(1.numerator) - def numerator() self end - def denominator() 1 end - - def gcd(other) - min = self.abs - max = other.abs - while min > 0 - tmp = min - min = max % min - max = tmp - end - max - end - - def lcm(other) - if self.zero? or other.zero? - 0 - else - (self.div(self.gcd(other)) * other).abs - end - end - - end end module Math - alias sqrt! sqrt + alias exp! exp alias log! log alias log10! log10 - alias cos! cos + alias sqrt! sqrt + alias sin! sin + alias cos! cos alias tan! tan - alias cosh! cosh + alias sinh! sinh + alias cosh! cosh alias tanh! tanh - alias acos! acos + alias asin! asin + alias acos! acos alias atan! atan alias atan2! atan2 - alias acosh! acosh + alias asinh! asinh - alias atanh! atanh + alias acosh! acosh + alias atanh! atanh + + def exp(z) + if Complex.generic?(z) + exp!(z) + else + Complex(exp!(z.real) * cos!(z.image), + exp!(z.real) * sin!(z.image)) + end + end + + def log(*args) + z, b = args + if Complex.generic?(z) and z >= 0 and (b.nil? or b >= 0) + log!(*args) + else + r, theta = z.polar + a = Complex(log!(r.abs), theta) + if b + a /= log(b) + end + a + end + end + + def log10(z) + if Complex.generic?(z) + log10!(z) + else + log(z) / log!(10) + end + end - # Redefined to handle a Complex argument. def sqrt(z) if Complex.generic?(z) if z >= 0 @@ -481,41 +98,29 @@ module Math else r = z.abs x = z.real - Complex( sqrt!((r+x)/2), sqrt!((r-x)/2) ) + Complex(sqrt!((r + x) / 2), sqrt!((r - x) / 2)) end end end - - # Redefined to handle a Complex argument. - def exp(z) + + def sin(z) if Complex.generic?(z) - exp!(z) + sin!(z) else - Complex(exp!(z.real) * cos!(z.image), exp!(z.real) * sin!(z.image)) + Complex(sin!(z.real) * cosh!(z.image), + cos!(z.real) * sinh!(z.image)) end end - - # Redefined to handle a Complex argument. + def cos(z) if Complex.generic?(z) cos!(z) else - Complex(cos!(z.real)*cosh!(z.image), - -sin!(z.real)*sinh!(z.image)) + Complex(cos!(z.real) * cosh!(z.image), + -sin!(z.real) * sinh!(z.image)) end end - - # Redefined to handle a Complex argument. - def sin(z) - if Complex.generic?(z) - sin!(z) - else - Complex(sin!(z.real)*cosh!(z.image), - cos!(z.real)*sinh!(z.image)) - end - end - - # Redefined to handle a Complex argument. + def tan(z) if Complex.generic?(z) tan!(z) @@ -528,7 +133,8 @@ module Math if Complex.generic?(z) sinh!(z) else - Complex( sinh!(z.real)*cos!(z.image), cosh!(z.real)*sin!(z.image) ) + Complex(sinh!(z.real) * cos!(z.image), + cosh!(z.real) * sin!(z.image)) end end @@ -536,7 +142,8 @@ module Math if Complex.generic?(z) cosh!(z) else - Complex( cosh!(z.real)*cos!(z.image), sinh!(z.real)*sin!(z.image) ) + Complex(cosh!(z.real) * cos!(z.image), + sinh!(z.real) * sin!(z.image)) end end @@ -544,42 +151,23 @@ module Math if Complex.generic?(z) tanh!(z) else - sinh(z)/cosh(z) - end - end - - # Redefined to handle a Complex argument. - def log(z) - if Complex.generic?(z) and z >= 0 - log!(z) - else - r, theta = z.polar - Complex(log!(r.abs), theta) - end - end - - # Redefined to handle a Complex argument. - def log10(z) - if Complex.generic?(z) - log10!(z) - else - log(z)/log!(10) + sinh(z) / cosh(z) end end - def acos(z) + def asin(z) if Complex.generic?(z) and z >= -1 and z <= 1 - acos!(z) + asin!(z) else - -1.0.im * log( z + 1.0.im * sqrt(1.0-z*z) ) + -1.0.im * log(1.0.im * z + sqrt(1.0 - z * z)) end end - def asin(z) + def acos(z) if Complex.generic?(z) and z >= -1 and z <= 1 - asin!(z) + acos!(z) else - -1.0.im * log( 1.0.im * z + sqrt(1.0-z*z) ) + -1.0.im * log(z + 1.0.im * sqrt(1.0 - z * z)) end end @@ -587,7 +175,7 @@ module Math if Complex.generic?(z) atan!(z) else - 1.0.im * log( (1.0.im+z) / (1.0.im-z) ) / 2.0 + 1.0.im * log((1.0.im + z) / (1.0.im - z)) / 2.0 end end @@ -595,7 +183,7 @@ module Math if Complex.generic?(y) and Complex.generic?(x) atan2!(y,x) else - -1.0.im * log( (x+1.0.im*y) / sqrt(x*x+y*y) ) + -1.0.im * log((x + 1.0.im * y) / sqrt(x * x + y * y)) end end @@ -603,7 +191,7 @@ module Math if Complex.generic?(z) and z >= 1 acosh!(z) else - log( z + sqrt(z*z-1.0) ) + log(z + sqrt(z * z - 1.0)) end end @@ -611,7 +199,7 @@ module Math if Complex.generic?(z) asinh!(z) else - log( z + sqrt(1.0+z*z) ) + log(z + sqrt(1.0 + z * z)) end end @@ -619,49 +207,47 @@ module Math if Complex.generic?(z) and z >= -1 and z <= 1 atanh!(z) else - log( (1.0+z) / (1.0-z) ) / 2.0 + log((1.0 + z) / (1.0 - z)) / 2.0 end end - module_function :sqrt! - module_function :sqrt module_function :exp! module_function :exp module_function :log! module_function :log module_function :log10! module_function :log10 - module_function :cosh! - module_function :cosh - module_function :cos! - module_function :cos - module_function :sinh! - module_function :sinh + module_function :sqrt! + module_function :sqrt + module_function :sin! module_function :sin + module_function :cos! + module_function :cos module_function :tan! module_function :tan + + module_function :sinh! + module_function :sinh + module_function :cosh! + module_function :cosh module_function :tanh! module_function :tanh - module_function :acos! - module_function :acos + module_function :asin! module_function :asin + module_function :acos! + module_function :acos module_function :atan! module_function :atan module_function :atan2! module_function :atan2 - module_function :acosh! - module_function :acosh + module_function :asinh! module_function :asinh + module_function :acosh! + module_function :acosh module_function :atanh! module_function :atanh - -end -# Documentation comments: -# - source: original (researched from pickaxe) -# - a couple of fixme's -# - RDoc output for Bignum etc. is a bit short, with nothing but an -# (undocumented) alias. No big deal. +end diff --git a/lib/mathn.rb b/lib/mathn.rb index 724d37ea6f..f3be55eb6d 100644 --- a/lib/mathn.rb +++ b/lib/mathn.rb @@ -127,7 +127,7 @@ class Rational if other.kind_of?(Rational) other2 = other if self < 0 - return Complex.new!(self, 0) ** other + return Complex.__send__(:new!, self, 0) ** other elsif other == 0 return Rational(1,1) elsif self == 0 @@ -175,7 +175,7 @@ class Rational num = 1 den = 1 end - Rational.new!(num, den) + Rational(num, den) elsif other.kind_of?(Float) Float(self) ** other else @@ -187,7 +187,7 @@ class Rational def power2(other) if other.kind_of?(Rational) if self < 0 - return Complex(self, 0) ** other + return Complex.__send__(:new!, self, 0) ** other elsif other == 0 return Rational(1,1) elsif self == 0 @@ -219,7 +219,7 @@ class Rational num = 1 den = 1 end - Rational.new!(num, den) + Rational(num, den) elsif other.kind_of?(Float) Float(self) ** other else @@ -306,4 +306,3 @@ end class Complex Unify = true end - diff --git a/lib/rational.rb b/lib/rational.rb index 59588528ab..b12bf7ef38 100644 --- a/lib/rational.rb +++ b/lib/rational.rb @@ -1,469 +1,23 @@ -# -# rational.rb - -# $Release Version: 0.5 $ -# $Revision: 1.7 $ -# by Keiju ISHITSUKA(SHL Japan Inc.) -# -# Documentation by Kevin Jackson and Gavin Sinclair. -# -# When you <tt>require 'rational'</tt>, all interactions between numbers -# potentially return a rational result. For example: -# -# 1.quo(2) # -> 0.5 -# require 'rational' -# 1.quo(2) # -> Rational(1,2) -# -# See Rational for full documentation. -# - -# -# Creates a Rational number (i.e. a fraction). +a+ and +b+ should be Integers: -# -# Rational(1,3) # -> 1/3 -# -# Note: trying to construct a Rational with floating point or real values -# produces errors: -# -# Rational(1.1, 2.3) # -> NoMethodError -# -def Rational(a, b = 1) - if a.kind_of?(Rational) && b == 1 - a - else - Rational.reduce(a, b) - end -end - -# -# Rational implements a rational class for numbers. -# -# <em>A rational number is a number that can be expressed as a fraction p/q -# where p and q are integers and q != 0. A rational number p/q is said to have -# numerator p and denominator q. Numbers that are not rational are called -# irrational numbers.</em> (http://mathworld.wolfram.com/RationalNumber.html) -# -# To create a Rational Number: -# Rational(a,b) # -> a/b -# Rational.new!(a,b) # -> a/b -# -# Examples: -# Rational(5,6) # -> 5/6 -# Rational(5) # -> 5/1 -# -# Rational numbers are reduced to their lowest terms: -# Rational(6,10) # -> 3/5 -# -# But not if you use the unusual method "new!": -# Rational.new!(6,10) # -> 6/10 -# -# Division by zero is obviously not allowed: -# Rational(3,0) # -> ZeroDivisionError -# -class Rational < Numeric - @RCS_ID='-$Id: rational.rb,v 1.7 1999/08/24 12:49:28 keiju Exp keiju $-' - - # - # Reduces the given numerator and denominator to their lowest terms. Use - # Rational() instead. - # - def Rational.reduce(num, den = 1) - raise ZeroDivisionError, "denominator is zero" if den == 0 - - if den < 0 - num = -num - den = -den - end - gcd = num.gcd(den) - num = num.div(gcd) - den = den.div(gcd) - if den == 1 && defined?(Unify) - num - else - new!(num, den) - end - end - - # - # Implements the constructor. This method does not reduce to lowest terms or - # check for division by zero. Therefore #Rational() should be preferred in - # normal use. - # - def Rational.new!(num, den = 1) - new(num, den) - end - - private_class_method :new - - # - # This method is actually private. - # - def initialize(num, den) - if den < 0 - num = -num - den = -den - end - if num.kind_of?(Integer) and den.kind_of?(Integer) - @numerator = num - @denominator = den - else - @numerator = num.to_i - @denominator = den.to_i - end - end - - # - # Returns the addition of this value and +a+. - # - # Examples: - # r = Rational(3,4) # -> Rational(3,4) - # r + 1 # -> Rational(7,4) - # r + 0.5 # -> 1.25 - # - def + (a) - if a.kind_of?(Rational) - num = @numerator * a.denominator - num_a = a.numerator * @denominator - Rational(num + num_a, @denominator * a.denominator) - elsif a.kind_of?(Integer) - self + Rational.new!(a, 1) - elsif a.kind_of?(Float) - Float(self) + a - else - x, y = a.coerce(self) - x + y - end - end - - # - # Returns the difference of this value and +a+. - # subtracted. - # - # Examples: - # r = Rational(3,4) # -> Rational(3,4) - # r - 1 # -> Rational(-1,4) - # r - 0.5 # -> 0.25 - # - def - (a) - if a.kind_of?(Rational) - num = @numerator * a.denominator - num_a = a.numerator * @denominator - Rational(num - num_a, @denominator*a.denominator) - elsif a.kind_of?(Integer) - self - Rational.new!(a, 1) - elsif a.kind_of?(Float) - Float(self) - a - else - x, y = a.coerce(self) - x - y - end - end - - # - # Returns the product of this value and +a+. - # - # Examples: - # r = Rational(3,4) # -> Rational(3,4) - # r * 2 # -> Rational(3,2) - # r * 4 # -> Rational(3,1) - # r * 0.5 # -> 0.375 - # r * Rational(1,2) # -> Rational(3,8) - # - def * (a) - if a.kind_of?(Rational) - num = @numerator * a.numerator - den = @denominator * a.denominator - Rational(num, den) - elsif a.kind_of?(Integer) - self * Rational.new!(a, 1) - elsif a.kind_of?(Float) - Float(self) * a - else - x, y = a.coerce(self) - x * y - end - end - - # - # Returns the quotient of this value and +a+. - # r = Rational(3,4) # -> Rational(3,4) - # r / 2 # -> Rational(3,8) - # r / 2.0 # -> 0.375 - # r / Rational(1,2) # -> Rational(3,2) - # - def / (a) - if a.kind_of?(Rational) - num = @numerator * a.denominator - den = @denominator * a.numerator - Rational(num, den) - elsif a.kind_of?(Integer) - raise ZeroDivisionError, "division by zero" if a == 0 - self / Rational.new!(a, 1) - elsif a.kind_of?(Float) - Float(self) / a - else - x, y = a.coerce(self) - x / y - end - end - - # - # Returns this value raised to the given power. - # - # Examples: - # r = Rational(3,4) # -> Rational(3,4) - # r ** 2 # -> Rational(9,16) - # r ** 2.0 # -> 0.5625 - # r ** Rational(1,2) # -> 0.866025403784439 - # - def ** (other) - if other.kind_of?(Rational) - Float(self) ** other - elsif other.kind_of?(Integer) - if other > 0 - num = @numerator ** other - den = @denominator ** other - elsif other < 0 - num = @denominator ** -other - den = @numerator ** -other - elsif other == 0 - num = 1 - den = 1 - end - Rational.new!(num, den) - elsif other.kind_of?(Float) - Float(self) ** other - else - x, y = other.coerce(self) - x ** y - end - end - - def div(other) - (self / other).floor - end - - # - # Returns the remainder when this value is divided by +other+. - # - # Examples: - # r = Rational(7,4) # -> Rational(7,4) - # r % Rational(1,2) # -> Rational(1,4) - # r % 1 # -> Rational(3,4) - # r % Rational(1,7) # -> Rational(1,28) - # r % 0.26 # -> 0.19 - # - def % (other) - value = (self / other).floor - return self - other * value - end - - # - # Returns the quotient _and_ remainder. - # - # Examples: - # r = Rational(7,4) # -> Rational(7,4) - # r.divmod Rational(1,2) # -> [3, Rational(1,4)] - # - def divmod(other) - value = (self / other).floor - return value, self - other * value - end - - # - # Returns the absolute value. - # - def abs - if @numerator > 0 - self - else - Rational.new!(-@numerator, @denominator) - end - end - - # - # Returns +true+ iff this value is numerically equal to +other+. - # - # But beware: - # Rational(1,2) == Rational(4,8) # -> true - # Rational(1,2) == Rational.new!(4,8) # -> false - # - # Don't use Rational.new! - # - def == (other) - if other.kind_of?(Rational) - @numerator == other.numerator and @denominator == other.denominator - elsif other.kind_of?(Integer) - self == Rational.new!(other, 1) - elsif other.kind_of?(Float) - Float(self) == other - else - other == self - end - end - - # - # Standard comparison operator. - # - def <=> (other) - if other.kind_of?(Rational) - num = @numerator * other.denominator - num_a = other.numerator * @denominator - v = num - num_a - if v > 0 - return 1 - elsif v < 0 - return -1 - else - return 0 - end - elsif other.kind_of?(Integer) - return self <=> Rational.new!(other, 1) - elsif other.kind_of?(Float) - return Float(self) <=> other - elsif defined? other.coerce - x, y = other.coerce(self) - return x <=> y - else - return nil - end - end - - def coerce(other) - if other.kind_of?(Float) - return other, self.to_f - elsif other.kind_of?(Integer) - return Rational.new!(other, 1), self - else - super - end - end - - # - # Converts the rational to an Integer. Not the _nearest_ integer, the - # truncated integer. Study the following example carefully: - # Rational(+7,4).to_i # -> 1 - # Rational(-7,4).to_i # -> -2 - # (-1.75).to_i # -> -1 - # - # In other words: - # Rational(-7,4) == -1.75 # -> true - # Rational(-7,4).to_i == (-1.75).to_i # false - # - - def floor() - @numerator.div(@denominator) - end - - def ceil() - -((-@numerator).div(@denominator)) - end - - def truncate() - if @numerator < 0 - return -((-@numerator).div(@denominator)) - end - @numerator.div(@denominator) - end - - alias_method :to_i, :truncate - - def round() - if @numerator < 0 - num = -@numerator - num = num * 2 + @denominator - den = @denominator * 2 - -(num.div(den)) - else - num = @numerator * 2 + @denominator - den = @denominator * 2 - num.div(den) - end - end +class Fixnum - # - # Converts the rational to a Float. - # - def to_f - @numerator.quof(@denominator) - end + alias quof fdiv - # - # Returns a string representation of the rational number. - # - # Example: - # Rational(3,4).to_s # "3/4" - # Rational(8).to_s # "8" - # - def to_s - if @denominator == 1 - @numerator.to_s - else - @numerator.to_s+"/"+@denominator.to_s - end - end + alias power! ** + alias rpower ** - # - # Returns +self+. - # - def to_r - self - end +end - # - # Returns a reconstructable string representation: - # - # Rational(5,8).inspect # -> "Rational(5, 8)" - # - def inspect - sprintf("Rational(%s, %s)", @numerator.inspect, @denominator.inspect) - end +class Bignum - # - # Returns a hash code for the object. - # - def hash - @numerator.hash ^ @denominator.hash - end + alias quof fdiv - attr :numerator - attr :denominator + alias power! ** + alias rpower ** - private :initialize end class Integer - # - # In an integer, the value _is_ the numerator of its rational equivalent. - # Therefore, this method returns +self+. - # - def numerator - self - end - - # - # In an integer, the denominator is 1. Therefore, this method returns 1. - # - def denominator - 1 - end - # - # Returns a Rational representation of this integer. - # - def to_r - Rational(self, 1) - end - - # - # Returns the <em>greatest common denominator</em> of the two numbers (+self+ - # and +n+). - # - # Examples: - # 72.gcd 168 # -> 24 - # 19.gcd 36 # -> 1 - # - # The result is positive, no matter the sign of the arguments. - # def gcd(other) min = self.abs max = other.abs @@ -475,10 +29,6 @@ class Integer max end - # Examples: - # 6.lcm 7 # -> 42 - # 6.lcm 9 # -> 18 - # def lcm(other) if self.zero? or other.zero? 0 @@ -486,15 +36,7 @@ class Integer (self.div(self.gcd(other)) * other).abs end end - - # - # Returns the GCD _and_ the LCM (see #gcd and #lcm) of the two arguments - # (+self+ and +other+). This is more efficient than calculating them - # separately. - # - # Example: - # 6.gcdlcm 9 # -> [3, 18] - # + def gcdlcm(other) gcd = self.gcd(other) if self.zero? or other.zero? @@ -503,55 +45,5 @@ class Integer [gcd, (self.div(gcd) * other).abs] end end -end - -class Fixnum - alias quof quo - remove_method :quo - - # If Rational is defined, returns a Rational number instead of a Float. - def quo(other) - Rational.new!(self, 1) / other - end - alias rdiv quo - # Returns a Rational number if the result is in fact rational (i.e. +other+ < 0). - def rpower (other) - if other >= 0 - self.power!(other) - else - Rational.new!(self, 1)**other - end - end -end - -class Bignum - alias quof quo - remove_method :quo - - # If Rational is defined, returns a Rational number instead of a Float. - def quo(other) - Rational.new!(self, 1) / other - end - alias rdiv quo - - # Returns a Rational number if the result is in fact rational (i.e. +other+ < 0). - def rpower (other) - if other >= 0 - self.power!(other) - else - Rational.new!(self, 1)**other - end - end -end - -unless defined? 1.power! - class Fixnum - alias power! ** - alias ** rpower - end - class Bignum - alias power! ** - alias ** rpower - end end @@ -646,6 +646,17 @@ flo_div(VALUE x, VALUE y) } } +static VALUE +flo_quo(VALUE x, VALUE y) +{ + return rb_funcall(x, '/', 1, y); +} + +static VALUE +flo_rdiv(VALUE x, VALUE y) +{ + return rb_funcall(rb_Rational1(x), '/', 1, y); +} static void flodivmod(double x, double y, double *divp, double *modp) @@ -1699,6 +1710,17 @@ rb_num2ull(VALUE val) #endif /* HAVE_LONG_LONG */ +static VALUE +num_numerator(VALUE num) +{ + return rb_funcall(rb_Rational1(num), rb_intern("numerator"), 0); +} + +static VALUE +num_denominator(VALUE num) +{ + return rb_funcall(rb_Rational1(num), rb_intern("denominator"), 0); +} /* * Document-class: Integer @@ -1880,6 +1902,18 @@ int_chr(int argc, VALUE *argv, VALUE num) return str; } +static VALUE +int_numerator(VALUE num) +{ + return num; +} + +static VALUE +int_denominator(VALUE num) +{ + return INT2FIX(1); +} + /******************************************************************** * * Document-class: Fixnum @@ -1928,6 +1962,7 @@ rb_int_induced_from(VALUE klass, VALUE x) case T_BIGNUM: return x; case T_FLOAT: + case T_RATIONAL: return rb_funcall(x, id_to_i, 0); default: rb_raise(rb_eTypeError, "failed to convert %s into Integer", @@ -1948,6 +1983,7 @@ rb_flo_induced_from(VALUE klass, VALUE x) switch (TYPE(x)) { case T_FIXNUM: case T_BIGNUM: + case T_RATIONAL: return rb_funcall(x, rb_intern("to_f"), 0); case T_FLOAT: return x; @@ -2199,6 +2235,12 @@ fixdivmod(long x, long y, long *divp, long *modp) static VALUE fix_quo(VALUE x, VALUE y) { + return rb_funcall(rb_rational_raw1(x), '/', 1, y); +} + +static VALUE +fix_fdiv(VALUE x, VALUE y) +{ if (FIXNUM_P(y)) { return DOUBLE2NUM((double)FIX2LONG(x) / (double)FIX2LONG(y)); } @@ -2208,7 +2250,7 @@ fix_quo(VALUE x, VALUE y) case T_FLOAT: return DOUBLE2NUM((double)FIX2LONG(x) / RFLOAT_VALUE(y)); default: - return rb_num_coerce_bin(x, y, rb_intern("quo")); + return rb_num_coerce_bin(x, y, rb_intern("fdiv")); } } @@ -2392,6 +2434,9 @@ fix_pow(VALUE x, VALUE y) if (FIXNUM_P(y)) { long b = FIX2LONG(y); + if (b < 0) + return rb_funcall(rb_rational_raw1(x), rb_intern("**"), 1, y); + if (b == 0) return INT2FIX(1); if (b == 1) return x; if (a == 0) { @@ -2405,13 +2450,14 @@ fix_pow(VALUE x, VALUE y) else return INT2FIX(-1); } - if (b > 0) { - return int_pow(a, b); - } - return DOUBLE2NUM(pow((double)a, (double)b)); + return int_pow(a, b); } switch (TYPE(y)) { case T_BIGNUM: + + if (rb_funcall(y, '<', 1, INT2FIX(0))) + return rb_funcall(rb_rational_raw1(x), rb_intern("**"), 1, y); + if (a == 0) return INT2FIX(0); if (a == 1) return INT2FIX(1); if (a == -1) { @@ -3117,6 +3163,7 @@ Init_Numeric(void) rb_define_method(rb_cNumeric, "<=>", num_cmp, 1); rb_define_method(rb_cNumeric, "eql?", num_eql, 1); rb_define_method(rb_cNumeric, "quo", num_quo, 1); + rb_define_method(rb_cNumeric, "rdiv", num_quo, 1); rb_define_method(rb_cNumeric, "fdiv", num_quo, 1); rb_define_method(rb_cNumeric, "div", num_div, 1); rb_define_method(rb_cNumeric, "divmod", num_divmod, 1); @@ -3136,6 +3183,9 @@ Init_Numeric(void) rb_define_method(rb_cNumeric, "truncate", num_truncate, 0); rb_define_method(rb_cNumeric, "step", num_step, -1); + rb_define_method(rb_cNumeric, "numerator", num_numerator, 0); + rb_define_method(rb_cNumeric, "denominator", num_denominator, 0); + rb_cInteger = rb_define_class("Integer", rb_cNumeric); rb_undef_alloc_func(rb_cInteger); rb_undef_method(CLASS_OF(rb_cInteger), "new"); @@ -3163,6 +3213,9 @@ Init_Numeric(void) rb_define_singleton_method(rb_cFixnum, "induced_from", rb_fix_induced_from, 1); rb_define_singleton_method(rb_cInteger, "induced_from", rb_int_induced_from, 1); + rb_define_method(rb_cInteger, "numerator", int_numerator, 0); + rb_define_method(rb_cInteger, "denominator", int_denominator, 0); + rb_define_method(rb_cFixnum, "to_s", fix_to_s, -1); rb_define_method(rb_cFixnum, "id2name", fix_id2name, 0); @@ -3178,7 +3231,8 @@ Init_Numeric(void) rb_define_method(rb_cFixnum, "modulo", fix_mod, 1); rb_define_method(rb_cFixnum, "divmod", fix_divmod, 1); rb_define_method(rb_cFixnum, "quo", fix_quo, 1); - rb_define_method(rb_cFixnum, "fdiv", fix_quo, 1); + rb_define_method(rb_cFixnum, "rdiv", fix_quo, 1); + rb_define_method(rb_cFixnum, "fdiv", fix_fdiv, 1); rb_define_method(rb_cFixnum, "**", fix_pow, 1); rb_define_method(rb_cFixnum, "abs", fix_abs, 0); @@ -3233,6 +3287,9 @@ Init_Numeric(void) rb_define_method(rb_cFloat, "-", flo_minus, 1); rb_define_method(rb_cFloat, "*", flo_mul, 1); rb_define_method(rb_cFloat, "/", flo_div, 1); + rb_define_method(rb_cFloat, "quo", flo_quo, 1); + rb_define_method(rb_cFloat, "rdiv", flo_rdiv, 1); + rb_define_method(rb_cFloat, "fdiv", flo_quo, 1); rb_define_method(rb_cFloat, "%", flo_mod, 1); rb_define_method(rb_cFloat, "modulo", flo_mod, 1); rb_define_method(rb_cFloat, "divmod", flo_divmod, 1); diff --git a/rational.c b/rational.c new file mode 100644 index 0000000000..ba8583b310 --- /dev/null +++ b/rational.c @@ -0,0 +1,1111 @@ +/* + nurat_core.c: Coded by Tadayoshi Funaba 2008 + + This implementation is based on Keiju Ishitsuka's Rational library + which is written in ruby. +*/ + +#include "ruby.h" +#include <math.h> + +#define NDEBUG +#include <assert.h> + +#ifndef RATIONAL_NAME +#define RATIONAL_NAME "Rational" +#endif + +#define ZERO INT2FIX(0) +#define ONE INT2FIX(1) +#define TWO INT2FIX(2) + +VALUE rb_cRational; + +static ID id_Unify, id_cmp, id_coerce, id_convert, id_equal_p, id_expt, + id_floor, id_format,id_idiv, id_inspect, id_negate, id_new, id_new_bang, + id_to_f, id_to_i, id_to_s, id_truncate; + +#define f_add(x,y) rb_funcall(x, '+', 1, y) +#define f_div(x,y) rb_funcall(x, '/', 1, y) +#define f_gt_p(x,y) rb_funcall(x, '>', 1, y) +#define f_lt_p(x,y) rb_funcall(x, '<', 1, y) +#define f_mod(x,y) rb_funcall(x, '%', 1, y) +#define f_mul(x,y) rb_funcall(x, '*', 1, y) +#define f_sub(x,y) rb_funcall(x, '-', 1, y) +#define f_xor(x,y) rb_funcall(x, '^', 1, y) + +#define f_floor(x) rb_funcall(x, id_floor, 0) +#define f_inspect(x) rb_funcall(x, id_inspect, 0) +#define f_to_f(x) rb_funcall(x, id_to_f, 0) +#define f_to_i(x) rb_funcall(x, id_to_i, 0) +#define f_to_s(x) rb_funcall(x, id_to_s, 0) +#define f_truncate(x) rb_funcall(x, id_truncate, 0) +#define f_cmp(x,y) rb_funcall(x, id_cmp, 1, y) +#define f_coerce(x,y) rb_funcall(x, id_coerce, 1, y) +#define f_equal_p(x,y) rb_funcall(x, id_equal_p, 1, y) +#define f_expt(x,y) rb_funcall(x, id_expt, 1, y) +#define f_idiv(x,y) rb_funcall(x, id_idiv, 1, y) +#define f_negate(x) rb_funcall(x, id_negate, 0) + +#define f_negative_p(x) f_lt_p(x, ZERO) +#define f_zero_p(x) f_equal_p(x, ZERO) +#define f_one_p(x) f_equal_p(x, ONE) +#define f_kind_of_p(x,c) rb_obj_is_kind_of(x, c) +#define k_numeric_p(x) f_kind_of_p(x, rb_cNumeric) +#define k_integer_p(x) f_kind_of_p(x, rb_cInteger) +#define k_float_p(x) f_kind_of_p(x, rb_cFloat) +#define k_rational_p(x) f_kind_of_p(x, rb_cRational) + +#define f_boolcast(x) ((x) ? Qtrue : Qfalse) + +inline static long +i_gcd(long x, long y) +{ + long b; + + if (x < 0) + x = -x; + if (y < 0) + y = -y; + + if (x == 0) + return y; + if (y == 0) + return x; + + b = 0; + while ((x & 1) == 0 && (y & 1) == 0) { + b += 1; + x >>= 1; + y >>= 1; + } + + while ((x & 1) == 0) + x >>= 1; + + while ((y & 1) == 0) + y >>= 1; + + while (x != y) { + if (y > x) { + long t; + t = x; + x = y; + y = t; + } + x -= y; + while ((x & 1) == 0) + x >>= 1; + } + + return x << b; +} + +inline static VALUE +f_gcd(VALUE x, VALUE y) +{ + VALUE z; + + if (FIXNUM_P(x) && FIXNUM_P(y)) + return LONG2NUM(i_gcd(FIX2LONG(x), FIX2LONG(y))); + + if (f_negative_p(x)) + x = f_negate(x); + if (f_negative_p(y)) + y = f_negate(y); + + if (f_zero_p(x)) + return y; + if (f_zero_p(y)) + return x; + + for (;;) { + if (FIXNUM_P(x)) { + if (FIX2INT(x) == 0) + return y; + if (FIXNUM_P(y)) + return LONG2NUM(i_gcd(FIX2LONG(x), FIX2LONG(y))); + } + z = x; + x = f_mod(y, x); + y = z; + } + /* NOTREACHED */ +} + +#define get_dat1(x) \ + struct RRational *dat;\ + dat = ((struct RRational *)(x)) + +#define get_dat2(x,y) \ + struct RRational *adat, *bdat;\ + adat = ((struct RRational *)(x));\ + bdat = ((struct RRational *)(y)) + +inline static VALUE +nurat_s_new_internal(VALUE klass, VALUE num, VALUE den) +{ + NEWOBJ(obj, struct RRational); + OBJSETUP(obj, klass, T_RATIONAL); + + obj->num = num; + obj->den = den; + + return (VALUE)obj; +} + +static VALUE +nurat_s_alloc(VALUE klass) +{ + return nurat_s_new_internal(klass, ZERO, ONE); +} + +static VALUE +nurat_s_new_bang(int argc, VALUE *argv, VALUE klass) +{ + VALUE num, den; + + switch (rb_scan_args(argc, argv, "11", &num, &den)) { + case 1: + if (!k_integer_p(num)) + num = f_to_i(num); + den = ONE; + break; + default: + if (!k_integer_p(num)) + num = f_to_i(num); + if (!k_integer_p(den)) + den = f_to_i(den); + + if (f_negative_p(den)) { + num = f_negate(num); + den = f_negate(den); + } + break; + } + + return nurat_s_new_internal(klass, num, den); +} + +inline static VALUE +f_rational_new_bang1(VALUE klass, VALUE x) +{ + return nurat_s_new_internal(klass, x, ONE); +} + +inline static VALUE +f_rational_new_bang2(VALUE klass, VALUE x, VALUE y) +{ + assert(!f_negative_p(y)); + assert(!f_zero_p(y)); + return nurat_s_new_internal(klass, x, y); +} + +#define f_unify_p(klass) rb_const_defined(klass, id_Unify) + +inline static VALUE +nurat_s_canonicalize_internal(VALUE klass, VALUE num, VALUE den) +{ + VALUE gcd; + + switch (FIX2INT(f_cmp(den, ZERO))) { + case -1: + if (f_negative_p(den)) { + num = f_negate(num); + den = f_negate(den); + } + break; + case 0: + rb_raise(rb_eZeroDivError, "devided by zero"); + break; + } + + gcd = f_gcd(num, den); + num = f_idiv(num, gcd); + den = f_idiv(den, gcd); + + if (f_one_p(den) && f_unify_p(klass)) + return num; + else + return nurat_s_new_internal(klass, num, den); +} + +static VALUE +nurat_s_canonicalize(int argc, VALUE *argv, VALUE klass) +{ + VALUE num, den; + + switch (rb_scan_args(argc, argv, "11", &num, &den)) { + case 1: + den = ONE; + break; + } + + switch (TYPE(num)) { + case T_FIXNUM: + case T_BIGNUM: + break; + default: + rb_raise(rb_eArgError, "not an integer"); + } + + switch (TYPE(den)) { + case T_FIXNUM: + case T_BIGNUM: + break; + default: + rb_raise(rb_eArgError, "not an integer"); + } + + return nurat_s_canonicalize_internal(klass, num, den); +} + +static VALUE +nurat_s_new(int argc, VALUE *argv, VALUE klass) +{ + VALUE num, den; + + switch (rb_scan_args(argc, argv, "11", &num, &den)) { + case 1: + den = ONE; + break; + } + + switch (TYPE(num)) { + case T_FIXNUM: + case T_BIGNUM: + break; + default: + rb_raise(rb_eArgError, "not an integer"); + } + + switch (TYPE(den)) { + case T_FIXNUM: + case T_BIGNUM: + break; + default: + rb_raise(rb_eArgError, "not an integer"); + } + + return nurat_s_canonicalize_internal(klass, num, den); +} + +inline static VALUE +f_rational_new1(VALUE klass, VALUE x) +{ + assert(!k_rational_p(x)); + return nurat_s_canonicalize_internal(klass, x, ONE); +} + +inline static VALUE +f_rational_new2(VALUE klass, VALUE x, VALUE y) +{ + assert(!k_rational_p(x)); + assert(!k_rational_p(y)); + return nurat_s_canonicalize_internal(klass, x, y); +} + +static VALUE +nurat_f_rational(int argc, VALUE *argv, VALUE klass) +{ + return rb_funcall2(rb_cRational, id_convert, argc, argv); +} + +static VALUE +nurat_numerator(VALUE self) +{ + get_dat1(self); + return dat->num; +} + +static VALUE +nurat_denominator(VALUE self) +{ + get_dat1(self); + return dat->den; +} + +static VALUE +nurat_add(VALUE self, VALUE other) +{ + switch (TYPE(other)) { + case T_FIXNUM: + case T_BIGNUM: + return f_add(self, f_rational_new_bang1(CLASS_OF(self), other)); + case T_FLOAT: + return f_add(f_to_f(self), other); + case T_RATIONAL: + { + VALUE num1, num2; + + get_dat2(self, other); + + num1 = f_mul(adat->num, bdat->den); + num2 = f_mul(bdat->num, adat->den); + + return f_rational_new2(CLASS_OF(self), + f_add(num1, num2), + f_mul(adat->den, bdat->den)); + } + default: + { + VALUE a = f_coerce(other, self); + return f_add(RARRAY_PTR(a)[0], RARRAY_PTR(a)[1]); + } + } +} + +static VALUE +nurat_sub(VALUE self, VALUE other) +{ + switch (TYPE(other)) { + case T_FIXNUM: + case T_BIGNUM: + return f_sub(self, f_rational_new_bang1(CLASS_OF(self), other)); + case T_FLOAT: + return f_sub(f_to_f(self), other); + case T_RATIONAL: + { + VALUE num1, num2; + + get_dat2(self, other); + + num1 = f_mul(adat->num, bdat->den); + num2 = f_mul(bdat->num, adat->den); + + return f_rational_new2(CLASS_OF(self), + f_sub(num1, num2), + f_mul(adat->den, bdat->den)); + } + default: + { + VALUE a = f_coerce(other, self); + return f_sub(RARRAY_PTR(a)[0], RARRAY_PTR(a)[1]); + } + } +} + +static VALUE +nurat_mul(VALUE self, VALUE other) +{ + switch (TYPE(other)) { + case T_FIXNUM: + case T_BIGNUM: + return f_mul(self, f_rational_new_bang1(CLASS_OF(self), other)); + case T_FLOAT: + return f_mul(f_to_f(self), other); + case T_RATIONAL: + { + VALUE num, den; + + get_dat2(self, other); + + num = f_mul(adat->num, bdat->num); + den = f_mul(adat->den, bdat->den); + + return f_rational_new2(CLASS_OF(self), num, den); + } + default: + { + VALUE a = f_coerce(other, self); + return f_mul(RARRAY_PTR(a)[0], RARRAY_PTR(a)[1]); + } + } +} + +static VALUE +nurat_div(VALUE self, VALUE other) +{ + switch (TYPE(other)) { + case T_FIXNUM: + case T_BIGNUM: + if (f_zero_p(other)) + rb_raise(rb_eZeroDivError, "devided by zero"); + return f_div(self, f_rational_new_bang1(CLASS_OF(self), other)); + case T_FLOAT: + return f_div(f_to_f(self), other); + case T_RATIONAL: + { + VALUE num, den; + + get_dat2(self, other); + + num = f_mul(adat->num, bdat->den); + den = f_mul(adat->den, bdat->num); + + return f_rational_new2(CLASS_OF(self), num, den); + } + default: + { + VALUE a = f_coerce(other, self); + return f_div(RARRAY_PTR(a)[0], RARRAY_PTR(a)[1]); + } + } +} + +static VALUE +nurat_fdiv(VALUE self, VALUE other) +{ + return f_div(f_to_f(self), other); +} + +static VALUE +nurat_expt(VALUE self, VALUE other) +{ + if (f_zero_p(other)) + return f_rational_new_bang1(CLASS_OF(self), ONE); + + if (k_rational_p(other)) { + get_dat1(other); + + if (f_one_p(dat->den)) + other = dat->num; /* good? */ + } + + switch (TYPE(other)) { + case T_FIXNUM: + case T_BIGNUM: + { + VALUE num, den; + + get_dat1(self); + + switch (FIX2INT(f_cmp(other, ZERO))) { + case 1: + num = f_expt(dat->num, other); + den = f_expt(dat->den, other); + break; + case -1: + num = f_expt(dat->den, f_negate(other)); + den = f_expt(dat->num, f_negate(other)); + break; + default: + num = ONE; + den = ONE; + break; + } + if (f_negative_p(den)) { /* or use normal new */ + num = f_negate(num); + den = f_negate(den); + } + return f_rational_new_bang2(CLASS_OF(self), num, den); + } + case T_FLOAT: + case T_RATIONAL: + return f_expt(f_to_f(self), other); + default: + { + VALUE a = f_coerce(other, self); + return f_expt(RARRAY_PTR(a)[0], RARRAY_PTR(a)[1]); + } + } +} + +static VALUE +nurat_cmp(VALUE self, VALUE other) +{ + switch (TYPE(other)) { + case T_FIXNUM: + case T_BIGNUM: + return f_cmp(self, f_rational_new_bang1(CLASS_OF(self), other)); + case T_FLOAT: + return f_cmp(f_to_f(self), other); + case T_RATIONAL: + { + VALUE num1, num2; + + get_dat2(self, other); + + num1 = f_mul(adat->num, bdat->den); + num2 = f_mul(bdat->num, adat->den); + return f_cmp(f_sub(num1, num2), ZERO); + } + default: + { + VALUE a = f_coerce(other, self); + return f_cmp(RARRAY_PTR(a)[0], RARRAY_PTR(a)[1]); + } + } +} + +static VALUE +nurat_equal_p(VALUE self, VALUE other) +{ + switch (TYPE(other)) { + case T_FIXNUM: + case T_BIGNUM: + return f_equal_p(self, f_rational_new_bang1(CLASS_OF(self), other)); + case T_FLOAT: + return f_equal_p(f_to_f(self), other); + case T_RATIONAL: + { + get_dat2(self, other); + + return f_boolcast(f_equal_p(adat->num, bdat->num) && + f_equal_p(adat->den, bdat->den)); + } + default: + return f_equal_p(other, self); + } +} + +static VALUE +nurat_coerce(VALUE self, VALUE other) +{ + switch (TYPE(other)) { + case T_FIXNUM: + case T_BIGNUM: + return rb_assoc_new(f_rational_new_bang1(CLASS_OF(self), other), self); + case T_FLOAT: + return rb_assoc_new(other, f_to_f(self)); + } + + rb_raise(rb_eTypeError, "%s can't be coerced into %s", + rb_obj_classname(other), rb_obj_classname(self)); + return Qnil; +} + +static VALUE +nurat_idiv(VALUE self, VALUE other) +{ + return f_floor(f_div(self, other)); +} +static VALUE +nurat_mod(VALUE self, VALUE other) +{ + VALUE val = f_floor(f_div(self, other)); + return f_sub(self, f_mul(other, val)); +} + +static VALUE +nurat_divmod(VALUE self, VALUE other) +{ + VALUE val = f_floor(f_div(self, other)); + return rb_assoc_new(val, f_sub(self, f_mul(other, val))); +} + +static VALUE +nurat_quot(VALUE self, VALUE other) +{ + return f_truncate(f_div(self, other)); +} +static VALUE +nurat_rem(VALUE self, VALUE other) +{ + VALUE val = f_truncate(f_div(self, other)); + return f_sub(self, f_mul(other, val)); +} + +static VALUE +nurat_quotrem(VALUE self, VALUE other) +{ + VALUE val = f_truncate(f_div(self, other)); + return rb_assoc_new(val, f_sub(self, f_mul(other, val))); +} + +static VALUE +nurat_abs(VALUE self) +{ + if (!f_negative_p(self)) + return self; + else + return f_negate(self); +} + +static VALUE +nurat_true(VALUE self) +{ + return Qtrue; +} + +static VALUE +nurat_floor(VALUE self) +{ + get_dat1(self); + return f_idiv(dat->num, dat->den); +} + +static VALUE +nurat_ceil(VALUE self) +{ + get_dat1(self); + return f_negate(f_idiv(f_negate(dat->num), dat->den)); +} + +static VALUE +nurat_truncate(VALUE self) +{ + get_dat1(self); + if (f_negative_p(dat->num)) + return f_negate(f_idiv(f_negate(dat->num), dat->den)); + return f_idiv(dat->num, dat->den); +} + +static VALUE +nurat_round(VALUE self) +{ + get_dat1(self); + + if (f_negative_p(dat->num)) { + VALUE num, den; + + num = f_negate(dat->num); + num = f_add(f_mul(num, TWO), dat->den); + den = f_mul(dat->den, TWO); + return f_negate(f_idiv(num, den)); + } else { + VALUE num = f_add(f_mul(dat->num, TWO), dat->den); + VALUE den = f_mul(dat->den, TWO); + return f_idiv(num, den); + } +} + +static VALUE +nurat_to_f(VALUE self) +{ + get_dat1(self); + return f_div(f_to_f(dat->num), dat->den); /* enough? */ +} + +static VALUE +nurat_to_r(VALUE self) +{ + return self; +} + +static VALUE +nurat_hash(VALUE self) +{ + get_dat1(self); + return f_xor(dat->num, dat->den); +} + +static VALUE +nurat_to_s(VALUE self) +{ + get_dat1(self); + + if (f_one_p(dat->den)) + return f_to_s(dat->num); + else + return rb_funcall(rb_mKernel, id_format, 3, + rb_str_new2("%d/%d"), dat->num, dat->den); +} + +static VALUE +nurat_inspect(VALUE self) +{ + get_dat1(self); + return rb_funcall(rb_mKernel, id_format, 3, + rb_str_new2("Rational(%d, %d)"), dat->num, dat->den); +} + +static VALUE +nurat_marshal_dump(VALUE self) +{ + get_dat1(self); + return rb_assoc_new(dat->num, dat->den); +} + +static VALUE +nurat_marshal_load(VALUE self, VALUE a) +{ + get_dat1(self); + dat->num = RARRAY_PTR(a)[0]; + dat->den = RARRAY_PTR(a)[1]; + return self; +} + +/* --- */ + +VALUE +rb_rational_raw(VALUE x, VALUE y) +{ + return nurat_s_new_internal(rb_cRational, x, y); +} + +VALUE +rb_rational_new(VALUE x, VALUE y) +{ + return nurat_s_canonicalize_internal(rb_cRational, x, y); +} + +static VALUE nurat_s_convert(int argc, VALUE *argv, VALUE klass); + +VALUE +rb_Rational(VALUE x, VALUE y) +{ + VALUE a[2]; + a[0] = x; + a[1] = y; + return nurat_s_convert(2, a, rb_cRational); +} + +static VALUE +nilclass_to_r(VALUE self) +{ + return rb_rational_new1(INT2FIX(0)); +} + +static VALUE +integer_to_r(VALUE self) +{ + return rb_rational_new1(self); +} + +#include <float.h> + +static VALUE +float_decode(VALUE self) +{ + double f; + int n; + + f = frexp(RFLOAT_VALUE(self), &n); + f = ldexp(f, DBL_MANT_DIG); + n -= DBL_MANT_DIG; + return rb_assoc_new(f_to_i(rb_float_new(f)), INT2FIX(n)); +} + +static VALUE +float_to_r(VALUE self) +{ + VALUE a = float_decode(self); + return f_mul(RARRAY_PTR(a)[0], f_expt(INT2FIX(FLT_RADIX), RARRAY_PTR(a)[1])); +} + +static VALUE rat_pat, an_e_pat, a_dot_pat, underscores_pat, an_underscore; + +#define DIGITS "(?:\\d(?:_\\d|\\d)*)" +#define NUMERATOR "(?:" DIGITS "?\\.)?" DIGITS "(?:[eE][-+]?" DIGITS ")?" +#define DENOMINATOR "[-+]?" DIGITS +#define PATTERN "\\A([-+])?(" NUMERATOR ")(?:\\/(" DENOMINATOR "))?" + +static void +make_patterns(void) +{ + static char *rat_pat_source = PATTERN; + static char *an_e_pat_source = "[eE]"; + static char *a_dot_pat_source = "\\."; + static char *underscores_pat_source = "_+"; + + rat_pat = rb_reg_new(rat_pat_source, strlen(rat_pat_source), 0); + rb_global_variable(&rat_pat); + + an_e_pat = rb_reg_new(an_e_pat_source, strlen(an_e_pat_source), 0); + rb_global_variable(&an_e_pat); + + a_dot_pat = rb_reg_new(a_dot_pat_source, strlen(a_dot_pat_source), 0); + rb_global_variable(&a_dot_pat); + + underscores_pat = rb_reg_new(underscores_pat_source, + strlen(underscores_pat_source), 0); + rb_global_variable(&underscores_pat); + + an_underscore = rb_str_new2("_"); + rb_global_variable(&an_underscore); +} + +#define id_strip rb_intern("strip") +#define f_strip(x) rb_funcall(x, id_strip, 0) + +#define id_match rb_intern("match") +#define f_match(x,y) rb_funcall(x, id_match, 1, y) + +#define id_aref rb_intern("[]") +#define f_aref(x,y) rb_funcall(x, id_aref, 1, y) + +#define id_post_match rb_intern("post_match") +#define f_post_match(x) rb_funcall(x, id_post_match, 0) + +#define id_split rb_intern("split") +#define f_split(x,y) rb_funcall(x, id_split, 1, y) + +#include <ctype.h> + +static VALUE +string_to_r_internal(VALUE self) +{ + VALUE s, m; + + s = f_strip(self); + + if (RSTRING_LEN(s) == 0) + return rb_assoc_new(Qnil, self); + + m = f_match(rat_pat, s); + + if (!NIL_P(m)) { + VALUE v, ifp, exp, ip, fp; + VALUE si = f_aref(m, INT2FIX(1)); + VALUE nu = f_aref(m, INT2FIX(2)); + VALUE de = f_aref(m, INT2FIX(3)); + VALUE re = f_post_match(m); + + { + VALUE a; + + a = f_split(nu, an_e_pat); + ifp = RARRAY_PTR(a)[0]; + if (RARRAY_LEN(a) != 2) + exp = Qnil; + else + exp = RARRAY_PTR(a)[1]; + + a = f_split(ifp, a_dot_pat); + ip = RARRAY_PTR(a)[0]; + if (RARRAY_LEN(a) != 2) + fp = Qnil; + else + fp = RARRAY_PTR(a)[1]; + } + + v = rb_rational_new1(f_to_i(ip)); + + if (!NIL_P(fp)) { + char *p = StringValuePtr(fp); + long count = 0; + VALUE l; + + while (*p) { + if (isdigit(*p)) + count++; + p++; + } + + l = f_expt(INT2FIX(10), LONG2NUM(count)); + v = f_mul(v, l); + v = f_add(v, f_to_i(fp)); + v = f_div(v, l); + } + if (!NIL_P(exp)) + v = f_mul(v, f_expt(INT2FIX(10), f_to_i(exp))); + if (!NIL_P(si) && *StringValuePtr(si) == '-') + v = f_negate(v); + if (!NIL_P(de)) + v = f_div(v, f_to_i(de)); + + return rb_assoc_new(v, re); + } + return rb_assoc_new(Qnil, self); +} + +static VALUE +string_to_r_strict(VALUE self) +{ + VALUE a = string_to_r_internal(self); + if (NIL_P(RARRAY_PTR(a)[0]) || RSTRING_LEN(RARRAY_PTR(a)[1]) > 0) { + VALUE s = f_inspect(self); + rb_raise(rb_eArgError, "invalid value for Rational: %s", + StringValuePtr(s)); + } + return RARRAY_PTR(a)[0]; +} + +#define id_gsub rb_intern("gsub") +#define f_gsub(x,y,z) rb_funcall(x, id_gsub, 2, y, z) + +static VALUE +string_to_r(VALUE self) +{ + VALUE s = f_gsub(self, underscores_pat, an_underscore); + VALUE a = string_to_r_internal(s); + if (!NIL_P(RARRAY_PTR(a)[0])) + return RARRAY_PTR(a)[0]; + return rb_rational_new1(INT2FIX(0)); +} + +#define id_to_r rb_intern("to_r") +#define f_to_r(x) rb_funcall(x, id_to_r, 0) + +static VALUE +nurat_s_convert(int argc, VALUE *argv, VALUE klass) +{ + VALUE a1, a2; + + a1 = Qnil; + a2 = Qnil; + rb_scan_args(argc, argv, "02", &a1, &a2); + + switch (TYPE(a1)) { + case T_COMPLEX: + if (k_float_p(RCOMPLEX(a1)->image) || !f_zero_p(RCOMPLEX(a1)->image)) { + VALUE s = f_to_s(a1); + rb_raise(rb_eRangeError, "can't accept %s", + StringValuePtr(s)); + } + a1 = RCOMPLEX(a1)->real; + } + + switch (TYPE(a2)) { + case T_COMPLEX: + if (k_float_p(RCOMPLEX(a2)->image) || !f_zero_p(RCOMPLEX(a2)->image)) { + VALUE s = f_to_s(a2); + rb_raise(rb_eRangeError, "can't accept %s", + StringValuePtr(s)); + } + a2 = RCOMPLEX(a2)->real; + } + + switch (TYPE(a1)) { + case T_FIXNUM: + case T_BIGNUM: + break; + case T_FLOAT: + a1 = f_to_r(a1); + break; + case T_STRING: + a1 = string_to_r_strict(a1); + break; + } + + switch (TYPE(a2)) { + case T_FIXNUM: + case T_BIGNUM: + break; + case T_FLOAT: + a2 = f_to_r(a2); + break; + case T_STRING: + a2 = string_to_r_strict(a2); + break; + } + + switch (TYPE(a1)) { + case T_RATIONAL: + if (NIL_P(a2) || f_zero_p(a2)) + return a1; + else + return f_div(a1, a2); + } + + switch (TYPE(a2)) { + case T_RATIONAL: + return f_div(a1, a2); + } + + { + VALUE argv2[2]; + argv2[0] = a1; + argv2[1] = a2; + return nurat_s_new(argc, argv2, klass); + } +} + +static VALUE +nurat_s_induced_from(VALUE klass, VALUE n) +{ + return f_to_r(n); +} + +void +Init_Rational(void) +{ + assert(fprintf(stderr, "assert() is now active\n")); + + id_Unify = rb_intern("Unify"); + id_cmp = rb_intern("<=>"); + id_coerce = rb_intern("coerce"); + id_convert = rb_intern("convert"); + id_equal_p = rb_intern("=="); + id_expt = rb_intern("**"); + id_floor = rb_intern("floor"); + id_format = rb_intern("format"); + id_idiv = rb_intern("div"); + id_inspect = rb_intern("inspect"); + id_negate = rb_intern("-@"); + id_new = rb_intern("new"); + id_new_bang = rb_intern("new!"); + id_to_f = rb_intern("to_f"); + id_to_i = rb_intern("to_i"); + id_to_s = rb_intern("to_s"); + id_truncate = rb_intern("truncate"); + + rb_cRational = rb_define_class(RATIONAL_NAME, rb_cNumeric); + + rb_define_alloc_func(rb_cRational, nurat_s_alloc); + rb_funcall(rb_cRational, rb_intern("private_class_method"), 1, + ID2SYM(rb_intern("allocate"))); + + rb_define_singleton_method(rb_cRational, "new!", nurat_s_new_bang, -1); + rb_funcall(rb_cRational, rb_intern("private_class_method"), 1, + ID2SYM(rb_intern("new!"))); + + rb_define_singleton_method(rb_cRational, "new", nurat_s_new, -1); + rb_funcall(rb_cRational, rb_intern("private_class_method"), 1, + ID2SYM(rb_intern("new"))); + + rb_define_global_function(RATIONAL_NAME, nurat_f_rational, -1); + + rb_define_method(rb_cRational, "numerator", nurat_numerator, 0); + rb_define_method(rb_cRational, "denominator", nurat_denominator, 0); + + rb_define_method(rb_cRational, "+", nurat_add, 1); + rb_define_method(rb_cRational, "-", nurat_sub, 1); + rb_define_method(rb_cRational, "*", nurat_mul, 1); + rb_define_method(rb_cRational, "/", nurat_div, 1); + rb_define_method(rb_cRational, "fdiv", nurat_fdiv, 1); + rb_define_method(rb_cRational, "**", nurat_expt, 1); + + rb_define_method(rb_cRational, "<=>", nurat_cmp, 1); + rb_define_method(rb_cRational, "==", nurat_equal_p, 1); + rb_define_method(rb_cRational, "coerce", nurat_coerce, 1); + + rb_define_method(rb_cRational, "div", nurat_idiv, 1); +#if NUBY + rb_define_method(rb_cRational, "//", nurat_idiv, 1); +#endif + rb_define_method(rb_cRational, "modulo", nurat_mod, 1); + rb_define_method(rb_cRational, "%", nurat_mod, 1); + rb_define_method(rb_cRational, "divmod", nurat_divmod, 1); + +#if 0 + rb_define_method(rb_cRational, "quot", nurat_quot, 1); +#endif + rb_define_method(rb_cRational, "remainder", nurat_rem, 1); +#if 0 + rb_define_method(rb_cRational, "quotrem", nurat_quotrem, 1); +#endif + + rb_define_method(rb_cRational, "abs", nurat_abs, 0); + +#if 0 + rb_define_method(rb_cRational, "rational?", nurat_true, 0); + rb_define_method(rb_cRational, "exact?", nurat_true, 0); +#endif + + rb_define_method(rb_cRational, "floor", nurat_floor, 0); + rb_define_method(rb_cRational, "ceil", nurat_ceil, 0); + rb_define_method(rb_cRational, "truncate", nurat_truncate, 0); + rb_define_method(rb_cRational, "round", nurat_round, 0); + + rb_define_method(rb_cRational, "to_i", nurat_truncate, 0); + rb_define_method(rb_cRational, "to_f", nurat_to_f, 0); + rb_define_method(rb_cRational, "to_r", nurat_to_r, 0); + + rb_define_method(rb_cRational, "hash", nurat_hash, 0); + + rb_define_method(rb_cRational, "to_s", nurat_to_s, 0); + rb_define_method(rb_cRational, "inspect", nurat_inspect, 0); + + rb_define_method(rb_cRational, "marshal_dump", nurat_marshal_dump, 0); + rb_define_method(rb_cRational, "marshal_load", nurat_marshal_load, 1); + + /* --- */ + + rb_define_method(rb_cNilClass, "to_r", nilclass_to_r, 0); + rb_define_method(rb_cInteger, "to_r", integer_to_r, 0); + rb_define_method(rb_cFloat, "to_r", float_to_r, 0); + + make_patterns(); + + rb_define_method(rb_cString, "to_r", string_to_r, 0); + + rb_define_singleton_method(rb_cRational, "convert", nurat_s_convert, -1); + rb_funcall(rb_cRational, rb_intern("private_class_method"), 1, + ID2SYM(rb_intern("convert"))); + + rb_include_module(rb_cRational, rb_mPrecision); + rb_define_singleton_method(rb_cRational, "induced_from", + nurat_s_induced_from, 1); +} diff --git a/test/ruby/test_complex.rb b/test/ruby/test_complex.rb new file mode 100644 index 0000000000..48441d0dbd --- /dev/null +++ b/test/ruby/test_complex.rb @@ -0,0 +1,1017 @@ +require 'test/unit' + +class ComplexSub < Complex; end + +class Complex_Test < Test::Unit::TestCase + + def test_sub + c = ComplexSub.__send__(:new, 1) + cc = ComplexSub.__send__(:convert, 1) + if defined?(ComplexSub::Unify) + assert_instance_of(Fixnum, c) + assert_instance_of(Fixnum, cc) + else + assert_instance_of(ComplexSub, c) + assert_instance_of(ComplexSub, cc) + + c2 = c + 1 + assert_instance_of(ComplexSub, c2) + c2 = c - 1 + assert_instance_of(ComplexSub, c2) + + c3 = c - c2 + assert_instance_of(ComplexSub, c3) + + s = Marshal.dump(c) + c5 = Marshal.load(s) + assert_equal(c, c5) + assert_instance_of(ComplexSub, c5) + end + + end + + def test_eql_p + c = Complex(0) + c2 = Complex(0) + c3 = Complex(1) + + assert_equal(true, c.eql?(c2)) + assert_equal(false, c.eql?(c3)) + + if defined?(Complex::Unify) + assert_equal(true, c.eql?(0)) + else + assert_equal(false, c.eql?(0)) + end + end + + def test_hash + assert_instance_of(Fixnum, Complex(1,2).hash) + + h = {} + h[Complex(0)] = 0 + h[Complex(0,1)] = 1 + h[Complex(1,0)] = 2 + h[Complex(1,1)] = 3 + + assert_equal(4, h.size) + assert_equal(2, h[Complex(1,0)]) + + h[Complex(0,0)] = 9 + assert_equal(4, h.size) + end + + def test_freeze + c = Complex(1) + c.freeze + unless defined?(Complex::Unify) + assert_equal(true, c.frozen?) + end + assert_instance_of(String, c.to_s) + end + + def test_new_bang # no unify + assert_instance_of(Complex, Complex.__send__(:new!, 2,0)) + assert_equal([2,0], Complex.__send__(:new!, 2,0). + instance_eval{[real, image]}) + assert_equal([2,4], Complex.__send__(:new!, 2,4). + instance_eval{[real, image]}) + assert_equal([-2,4], Complex.__send__(:new!, -2,4). + instance_eval{[real, image]}) + assert_equal([2,-4], Complex.__send__(:new!, 2,-4). + instance_eval{[real, image]}) + assert_equal([-2,-4], Complex.__send__(:new!, -2,-4). + instance_eval{[real, image]}) + + assert_equal([2,0], Complex.__send__(:new!, Complex(2)). + instance_eval{[real, image]}) + assert_equal([2,3], Complex.__send__(:new!, Complex(2), Complex(3)). + instance_eval{[real, image]}) + assert_equal([2,3], Complex.__send__(:new!, 2, Complex(3)). + instance_eval{[real, image]}) + + assert_equal([1.1,0], Complex.__send__(:new!, 1.1). + instance_eval{[real, image]}) + assert_equal([-1.1,0], Complex.__send__(:new!, -1.1). + instance_eval{[real, image]}) + assert_equal([1,0], Complex.__send__(:new!, '1'). + instance_eval{[real, image]}) + assert_equal([0,0], Complex.__send__(:new!, nil). + instance_eval{[real, image]}) + end + + def test_new + if defined?(Complex::Unify) + assert_instance_of(Fixnum, Complex.__send__(:new, 2,0)) + else + assert_instance_of(Complex, Complex.__send__(:new, 2,0)) + assert_equal([2,0], Complex.__send__(:new, 2,0). instance_eval{[real, image]}) + end + assert_equal([2,4], Complex.__send__(:new, 2,4).instance_eval{[real, image]}) + assert_equal([-2,4], Complex.__send__(:new, -2,4).instance_eval{[real, image]}) + assert_equal([2,-4], Complex.__send__(:new, 2,-4).instance_eval{[real, image]}) + assert_equal([-2,-4], Complex.__send__(:new, -2,-4).instance_eval{[real, image]}) + + assert_raise(ArgumentError){Complex.__send__(:new, Complex(1,2),2)} + assert_raise(ArgumentError){Complex.__send__(:new, 2,Complex(1,2))} + assert_raise(ArgumentError){Complex.__send__(:new, Complex(1,2),Complex(1,2))} + + assert_raise(ArgumentError){Complex.__send__(:new, '1')} + assert_raise(ArgumentError){Complex.__send__(:new, nil)} +=begin + assert_raise(ArgumentError){Complex.__send__(:new, Complex(1))} +=end + end + + def test_conv + c = Complex(0,0) + assert_equal(Complex.__send__(:new, 0,0), c) + + c = Complex(2**32, 2**32) + assert_equal(Complex.__send__(:new, 2**32,2**32), c) + assert_equal([2**32,2**32], [c.real,c.image]) + + c = Complex(-2**32, 2**32) + assert_equal(Complex.__send__(:new, -2**32,2**32), c) + assert_equal([-2**32,2**32], [c.real,c.image]) + + c = Complex(2**32, -2**32) + assert_equal(Complex.__send__(:new, 2**32,-2**32), c) + assert_equal([2**32,-2**32], [c.real,c.image]) + + c = Complex(-2**32, -2**32) + assert_equal(Complex.__send__(:new, -2**32,-2**32), c) + assert_equal([-2**32,-2**32], [c.real,c.image]) + + c = Complex(Complex(1),0) + assert_equal(Complex.__send__(:new, 1,0), c) + + c = Complex(0,Complex(1)) + assert_equal(Complex.__send__(:new, 0,1), c) + + c = 5.re + assert_equal(Complex.__send__(:new, 5,0), c) + + c = Complex(1,2).re + assert_equal(Complex.__send__(:new, 1,2), c) + + c = 5.im + assert_equal(Complex.__send__(:new, 0,5), c) + + c = Complex(2,0).im + assert_equal(Complex.__send__(:new, 0,2), c) + assert_raise(ArgumentError){Complex(1,2).im} + + c = Complex::I + assert_equal(Complex.__send__(:new, 0,1), c) + + assert_equal(Complex.__send__(:new, 1),Complex(1)) + assert_equal(Complex.__send__(:new, 1),Complex('1')) + assert_raise(ArgumentError){Complex(nil)} + end + + def test_attr + c = Complex(4) + + assert_equal(4, c.real) + assert_equal(0, c.image) + + c = Complex(4,5) + + assert_equal(4, c.real) + assert_equal(5, c.image) + + c = Complex(-0.0,-0.0) + + assert_equal('-0.0', c.real.to_s) + assert_equal('-0.0', c.image.to_s) + + c = Complex.__send__(:new, 4) + + assert_equal(4, c.real) + assert_equal(0, c.image) + assert_equal(c.imag, c.image) + + c = Complex.__send__(:new, 4,5) + + assert_equal(4, c.real) + assert_equal(5, c.image) + assert_equal(c.imag, c.image) + + c = Complex.__send__(:new, -0.0,-0.0) + + assert_equal('-0.0', c.real.to_s) + assert_equal('-0.0', c.image.to_s) + assert_equal(c.imag.to_s, c.image.to_s) + + c = Complex.__send__(:new!, 4) + + assert_equal(4, c.real) + assert_equal(c.imag, c.image) + assert_equal(0, c.image) + + c = Complex.__send__(:new!, 4,5) + + assert_equal(4, c.real) + assert_equal(5, c.image) + assert_equal(c.imag, c.image) + + c = Complex.__send__(:new!, -0.0,-0.0) + + assert_equal('-0.0', c.real.to_s) + assert_equal('-0.0', c.image.to_s) + assert_equal(c.imag.to_s, c.image.to_s) + end + + def test_attr2 + c = Complex(1) + + if defined?(Complex::Unify) + assert_equal(true, c.scalar?) +=begin + assert_equal(true, c.finite?) + assert_equal(false, c.infinite?) + assert_equal(false, c.nan?) + assert_equal(true, c.integer?) + assert_equal(false, c.float?) + assert_equal(true, c.rational?) + assert_equal(true, c.real?) + assert_equal(false, c.complex?) + assert_equal(true, c.exact?) + assert_equal(false, c.inexact?) +=end + else + assert_equal(false, c.scalar?) +=begin + assert_equal(true, c.finite?) + assert_equal(false, c.infinite?) + assert_equal(false, c.nan?) + assert_equal(false, c.integer?) + assert_equal(false, c.float?) + assert_equal(false, c.rational?) + assert_equal(false, c.real?) + assert_equal(true, c.complex?) + assert_equal(true, c.exact?) + assert_equal(false, c.inexact?) +=end + end + +=begin + assert_equal(0, Complex(0).sign) + assert_equal(1, Complex(2).sign) + assert_equal(-1, Complex(-2).sign) +=end + + assert_equal(true, Complex(0).zero?) + assert_equal(true, Complex(0,0).zero?) + assert_equal(false, Complex(1,0).zero?) + assert_equal(false, Complex(0,1).zero?) + assert_equal(false, Complex(1,1).zero?) + + assert_equal(nil, Complex(0).nonzero?) + assert_equal(nil, Complex(0,0).nonzero?) + assert_equal(Complex(1,0), Complex(1,0).nonzero?) + assert_equal(Complex(0,1), Complex(0,1).nonzero?) + assert_equal(Complex(1,1), Complex(1,1).nonzero?) + end + + def test_uplus + assert_equal(Complex(1), +Complex(1)) + assert_equal(Complex(-1), +Complex(-1)) + assert_equal(Complex(1,1), +Complex(1,1)) + assert_equal(Complex(-1,1), +Complex(-1,1)) + assert_equal(Complex(1,-1), +Complex(1,-1)) + assert_equal(Complex(-1,-1), +Complex(-1,-1)) + end + + def test_negate + assert_equal(Complex(-1), -Complex(1)) + assert_equal(Complex(1), -Complex(-1)) + assert_equal(Complex(-1,-1), -Complex(1,1)) + assert_equal(Complex(1,-1), -Complex(-1,1)) + assert_equal(Complex(-1,1), -Complex(1,-1)) + assert_equal(Complex(1,1), -Complex(-1,-1)) + +=begin + assert_equal(0, Complex(0).negate) + assert_equal(-2, Complex(2).negate) + assert_equal(2, Complex(-2).negate) +=end + end + + def test_add + c = Complex(1,2) + c2 = Complex(2,3) + + assert_equal(Complex(3,5), c + c2) + + assert_equal(Complex(3,2), c + 2) + assert_equal(Complex(3.0,2), c + 2.0) + + if defined?(Rational) + assert_equal(Complex(Rational(3,1),Rational(2)), c + Rational(2)) + assert_equal(Complex(Rational(5,3),Rational(2)), c + Rational(2,3)) + end + end + + def test_sub + c = Complex(1,2) + c2 = Complex(2,3) + + assert_equal(Complex(-1,-1), c - c2) + + assert_equal(Complex(-1,2), c - 2) + assert_equal(Complex(-1.0,2), c - 2.0) + + if defined?(Rational) + assert_equal(Complex(Rational(-1,1),Rational(2)), c - Rational(2)) + assert_equal(Complex(Rational(1,3),Rational(2)), c - Rational(2,3)) + end + end + + def test_mul + c = Complex(1,2) + c2 = Complex(2,3) + + assert_equal(Complex(-4,7), c * c2) + + assert_equal(Complex(2,4), c * 2) + assert_equal(Complex(2.0,4.0), c * 2.0) + + if defined?(Rational) + assert_equal(Complex(Rational(2,1),Rational(4)), c * Rational(2)) + assert_equal(Complex(Rational(2,3),Rational(4,3)), c * Rational(2,3)) + end + + end + + def test_div + c = Complex(1,2) + c2 = Complex(2,3) + + if defined?(Complex::Unify) + assert_equal(Complex(Rational(8,13),Rational(1,13)), c / c2) + else + assert_equal(Complex(0,0), c / c2) + end + + c = Complex(1.0,2.0) + c2 = Complex(2.0,3.0) + + r = c / c2 + assert_in_delta(0.615, r.real, 0.001) + assert_in_delta(0.076, r.image, 0.001) + + c = Complex(1,2) + c2 = Complex(2,3) + + if defined?(Complex::Unify) + assert_equal(Complex(Rational(1,2),1), c / 2) + else + assert_equal(Complex(0,1), c / 2) + end + assert_equal(Complex(0.5,1.0), c / 2.0) + + if defined?(Rational) + assert_equal(Complex(Rational(1,2),Rational(1)), c / Rational(2)) + assert_equal(Complex(Rational(3,2),Rational(3)), c / Rational(2,3)) + end + end + + def test_quo + c = Complex(1,2) + c2 = Complex(2,3) + + if defined?(Rational) + assert_equal(Complex(Rational(8,13),Rational(1,13)), c.quo(c2)) + else + r = c.quo(c2) + assert_in_delta(0.615, r.real, 0.001) + assert_in_delta(0.076, r.image, 0.001) + end + + c = Complex(1.0,2.0) + c2 = Complex(2.0,3.0) + + r = c.quo(c2) + assert_in_delta(0.615, r.real, 0.001) + assert_in_delta(0.076, r.image, 0.001) + + c = Complex(1,2) + c2 = Complex(2,3) + + if defined?(Rational) + assert_equal(Complex(Rational(1,2),1), c.quo(2)) + else + assert_equal(Complex(0.5,1.0), c.quo(2)) + end + assert_equal(Complex(0.5,1.0), c.quo(2.0)) + + if defined?(Rational) + assert_equal(Complex(Rational(1,2),Rational(1)), c / Rational(2)) + assert_equal(Complex(Rational(3,2),Rational(3)), c / Rational(2,3)) + end + end + + def test_rdiv + if defined?(Rational) && !Rational.instance_variable_get('@RCS_ID') + c = Complex(1,2) + c2 = Complex(2,3) + + assert_equal(Complex(Rational(8,13),Rational(1,13)), c.rdiv(c2)) + + c = Complex(1.0,2.0) + c2 = Complex(2.0,3.0) + + r = c.rdiv(c2) + assert_in_delta(0.615, r.real, 0.001) + assert_in_delta(0.076, r.image, 0.001) + + c = Complex(1,2) + c2 = Complex(2,3) + + assert_equal(Complex(Rational(1,2),1), c.rdiv(2)) + + assert_equal(Complex(Rational(1,2),Rational(1)), c / Rational(2)) + assert_equal(Complex(Rational(3,2),Rational(3)), c / Rational(2,3)) + end + end + + def test_fdiv + c = Complex(1,2) + c2 = Complex(2,3) + + r = c.fdiv(c2) + assert_in_delta(0.615, r.real, 0.001) + assert_in_delta(0.076, r.image, 0.001) + + c = Complex(1.0,2.0) + c2 = Complex(2.0,3.0) + + r = c.fdiv(c2) + assert_in_delta(0.615, r.real, 0.001) + assert_in_delta(0.076, r.image, 0.001) + + c = Complex(1,2) + c2 = Complex(2,3) + + assert_equal(Complex(0.5,1.0), c.fdiv(2)) + assert_equal(Complex(0.5,1.0), c.fdiv(2.0)) + end + + def test_expt + c = Complex(1,2) + c2 = Complex(2,3) + + r = c ** c2 + assert_in_delta(-0.015, r.real, 0.001) + assert_in_delta(-0.179, r.image, 0.001) + + assert_equal(Complex(-3,4), c ** 2) + if defined?(Rational) && !Rational.instance_variable_get('@RCS_ID') + assert_equal(Complex(Rational(-3,25),Rational(-4,25)), c ** -2) + else + r = c ** -2 + assert_in_delta(-0.12, r.real, 0.001) + assert_in_delta(-0.16, r.image, 0.001) + end + r = c ** 2.0 + assert_in_delta(-3.0, r.real, 0.001) + assert_in_delta(4.0, r.image, 0.001) + + r = c ** -2.0 + assert_in_delta(-0.12, r.real, 0.001) + assert_in_delta(-0.16, r.image, 0.001) + + if defined?(Rational) && !Rational.instance_variable_get('@RCS_ID') + assert_equal(Complex(-3,4), c ** Rational(2)) +#=begin + assert_equal(Complex(Rational(-3,25),Rational(-4,25)), + c ** Rational(-2)) # why failed? +#=end + + r = c ** Rational(2,3) + assert_in_delta(1.264, r.real, 0.001) + assert_in_delta(1.150, r.image, 0.001) + + r = c ** Rational(-2,3) + assert_in_delta(0.432, r.real, 0.001) + assert_in_delta(-0.393, r.image, 0.001) + end + end + + def test_cmp + assert_raise(NoMethodError){1 <=> Complex(1,1)} + assert_raise(NoMethodError){Complex(1,1) <=> 1} + assert_raise(NoMethodError){Complex(1,1) <=> Complex(1,1)} + end + + def test_equal + assert(Complex(1,0) == Complex(1)) + assert(Complex(1,0) == Complex.__send__(:new, 1)) + assert(Complex(1,0) == Complex.__send__(:new, 1,0)) + assert(Complex(1,0) == Complex.__send__(:new!, 1)) + assert(Complex(1,0) == Complex.__send__(:new!, 1,0)) + + assert(Complex(-1,0) == Complex(-1)) + assert(Complex(-1,0) == Complex.__send__(:new, -1)) + assert(Complex(-1,0) == Complex.__send__(:new, -1,0)) + assert(Complex(-1,0) == Complex.__send__(:new!, -1)) + assert(Complex(-1,0) == Complex.__send__(:new!, -1,0)) + + assert_equal(false, Complex(2,1) == Complex(1)) + assert_equal(true, Complex(2,1) != Complex(1)) + assert_equal(false, Complex(1) == nil) + assert_equal(false, Complex(1) == '') + end + + def test_math + c = Complex(1,2) + + assert_in_delta(2.236, c.abs, 0.001) + assert_equal(5, c.abs2) + + assert_equal(c.abs, Math.sqrt(c * c.conj)) + assert_equal(c.abs, Math.sqrt(c.real**2 + c.image**2)) + assert_equal(c.abs2, c * c.conj) + assert_equal(c.abs2, c.real**2 + c.image**2) + + assert_in_delta(1.107, c.arg, 0.001) + assert_in_delta(1.107, c.angle, 0.001) + + r = c.polar + assert_in_delta(2.236, r[0], 0.001) + assert_in_delta(1.107, r[1], 0.001) + assert_equal(Complex(1,-2), c.conjugate) + assert_equal(Complex(1,-2), c.conj) +# assert_equal(Complex(1,-2), ~c) +# assert_equal(5, c * ~c) + + assert_equal(Complex(1,2), c.numerator) + assert_equal(1, c.denominator) + end + + def test_to_s + c = Complex(1,2) + + assert_instance_of(String, c.to_s) + assert_equal('1+2i', c.to_s) + + assert_equal('2i', Complex(0,2).to_s) + assert_equal('-2i', Complex(0,-2).to_s) + assert_equal('1+2i', Complex(1,2).to_s) + assert_equal('-1+2i', Complex(-1,2).to_s) + assert_equal('-1-2i', Complex(-1,-2).to_s) + assert_equal('1-2i', Complex(1,-2).to_s) + assert_equal('-1-2i', Complex(-1,-2).to_s) + + assert_equal('2.0i', Complex(0,2.0).to_s) + assert_equal('-2.0i', Complex(0,-2.0).to_s) + assert_equal('1.0+2.0i', Complex(1.0,2.0).to_s) + assert_equal('-1.0+2.0i', Complex(-1.0,2.0).to_s) + assert_equal('-1.0-2.0i', Complex(-1.0,-2.0).to_s) + assert_equal('1.0-2.0i', Complex(1.0,-2.0).to_s) + assert_equal('-1.0-2.0i', Complex(-1.0,-2.0).to_s) + + if defined?(Rational) + assert_equal('2i', Complex(0,Rational(2)).to_s) + assert_equal('-2i', Complex(0,Rational(-2)).to_s) + assert_equal('1+2i', Complex(1,Rational(2)).to_s) + assert_equal('-1+2i', Complex(-1,Rational(2)).to_s) + assert_equal('-1-2i', Complex(-1,Rational(-2)).to_s) + assert_equal('1-2i', Complex(1,Rational(-2)).to_s) + assert_equal('-1-2i', Complex(-1,Rational(-2)).to_s) + + assert_equal('(2/3)i', Complex(0,Rational(2,3)).to_s) + assert_equal('(-2/3)i', Complex(0,Rational(-2,3)).to_s) + assert_equal('1+(2/3)i', Complex(1,Rational(2,3)).to_s) + assert_equal('-1+(2/3)i', Complex(-1,Rational(2,3)).to_s) + assert_equal('-1-(2/3)i', Complex(-1,Rational(-2,3)).to_s) + assert_equal('1-(2/3)i', Complex(1,Rational(-2,3)).to_s) + assert_equal('-1-(2/3)i', Complex(-1,Rational(-2,3)).to_s) + end + end + + def test_inspect + c = Complex(1,2) + + assert_instance_of(String, c.inspect) + assert_equal('Complex(1, 2)', c.inspect) + end + + def test_marshal + c = Complex(1,2) + + s = Marshal.dump(c) + c2 = Marshal.load(s) + assert_equal(c, c2) + assert_instance_of(Complex, c2) + + if defined?(Rational) + c = Complex(Rational(1,2),Rational(2,3)) + + s = Marshal.dump(c) + c2 = Marshal.load(s) + assert_equal(c, c2) + assert_instance_of(Complex, c2) + end + end + + def test_parse + assert_equal(Complex(0), ''.to_c) + assert_equal(Complex(0), ' '.to_c) + assert_equal(Complex(5), '5'.to_c) + assert_equal(Complex(-5), '-5'.to_c) + assert_equal(Complex(5,3), '5+3i'.to_c) + assert_equal(Complex(-5,3), '-5+3i'.to_c) + assert_equal(Complex(5,-3), '5-3i'.to_c) + assert_equal(Complex(-5,-3), '-5-3i'.to_c) + assert_equal(Complex(0,3), '3i'.to_c) + assert_equal(Complex(0,-3), '-3i'.to_c) + + assert_equal(Complex(5,3), '5+3I'.to_c) + assert_equal(Complex(5,3), '5+3j'.to_c) + assert_equal(Complex(5,3), '5+3J'.to_c) + assert_equal(Complex(0,3), '3I'.to_c) + assert_equal(Complex(0,3), '3j'.to_c) + assert_equal(Complex(0,3), '3J'.to_c) + + assert_equal(Complex(5.0), '5.0'.to_c) + assert_equal(Complex(-5.0), '-5.0'.to_c) + assert_equal(Complex(5.0,3.0), '5.0+3.0i'.to_c) + assert_equal(Complex(-5.0,3.0), '-5.0+3.0i'.to_c) + assert_equal(Complex(5.0,-3.0), '5.0-3.0i'.to_c) + assert_equal(Complex(-5.0,-3.0), '-5.0-3.0i'.to_c) + assert_equal(Complex(0.0,3.0), '3.0i'.to_c) + assert_equal(Complex(0.0,-3.0), '-3.0i'.to_c) + + assert_equal(Complex(5.0), '5e0'.to_c) + assert_equal(Complex(-5.0), '-5e0'.to_c) + assert_equal(Complex(5.0,3.0), '5e0+3e0i'.to_c) + assert_equal(Complex(-5.0,3.0), '-5e0+3e0i'.to_c) + assert_equal(Complex(5.0,-3.0), '5e0-3e0i'.to_c) + assert_equal(Complex(-5.0,-3.0), '-5e0-3e0i'.to_c) + assert_equal(Complex(0.0,3.0), '3e0i'.to_c) + assert_equal(Complex(0.0,-3.0), '-3e0i'.to_c) + + assert_equal(Complex(5), Complex('5')) + assert_equal(Complex(-5), Complex('-5')) + assert_equal(Complex(5,3), Complex('5+3i')) + assert_equal(Complex(-5,3), Complex('-5+3i')) + assert_equal(Complex(5,-3), Complex('5-3i')) + assert_equal(Complex(-5,-3), Complex('-5-3i')) + assert_equal(Complex(0,3), Complex('3i')) + assert_equal(Complex(0,-3), Complex('-3i')) + + assert_equal(Complex(5,3), Complex('5+3I')) + assert_equal(Complex(5,3), Complex('5+3j')) + assert_equal(Complex(5,3), Complex('5+3J')) + assert_equal(Complex(0,3), Complex('3I')) + assert_equal(Complex(0,3), Complex('3j')) + assert_equal(Complex(0,3), Complex('3J')) + + assert_equal(Complex(5.0), Complex('5.0')) + assert_equal(Complex(-5.0), Complex('-5.0')) + assert_equal(Complex(5.0,3.0), Complex('5.0+3.0i')) + assert_equal(Complex(-5.0,3.0), Complex('-5.0+3.0i')) + assert_equal(Complex(5.0,-3.0), Complex('5.0-3.0i')) + assert_equal(Complex(-5.0,-3.0), Complex('-5.0-3.0i')) + assert_equal(Complex(0.0,3.0), Complex('3.0i')) + assert_equal(Complex(0.0,-3.0), Complex('-3.0i')) + + assert_equal(Complex(5.0), Complex('5e0')) + assert_equal(Complex(-5.0), Complex('-5e0')) + assert_equal(Complex(5.0,3.0), Complex('5e0+3e0i')) + assert_equal(Complex(-5.0,3.0), Complex('-5e0+3e0i')) + assert_equal(Complex(5.0,-3.0), Complex('5e0-3e0i')) + assert_equal(Complex(-5.0,-3.0), Complex('-5e0-3e0i')) + assert_equal(Complex(0.0,3.0), Complex('3e0i')) + assert_equal(Complex(0.0,-3.0), Complex('-3e0i')) + + assert_equal(Complex(0), '_'.to_c) + assert_equal(Complex(0), '_5'.to_c) + assert_equal(Complex(5), '5_'.to_c) + assert_equal(Complex(5), '5x'.to_c) + assert_equal(Complex(5), '5+_3i'.to_c) + assert_equal(Complex(5), '5+3_i'.to_c) + assert_equal(Complex(5,3), '5+3i_'.to_c) + assert_equal(Complex(5,3), '5+3ix'.to_c) + assert_raise(ArgumentError){ Complex('')} + assert_raise(ArgumentError){ Complex('_')} + assert_raise(ArgumentError){ Complex('_5')} + assert_raise(ArgumentError){ Complex('5_')} + assert_raise(ArgumentError){ Complex('5x')} + assert_raise(ArgumentError){ Complex('5+_3i')} + assert_raise(ArgumentError){ Complex('5+3_i')} + assert_raise(ArgumentError){ Complex('5+3i_')} + assert_raise(ArgumentError){ Complex('5+3ix')} + + if defined?(Rational) && defined?(''.to_r) + assert_equal(Complex(Rational(1,5)), '1/5'.to_c) + assert_equal(Complex(Rational(-1,5)), '-1/5'.to_c) + assert_equal(Complex(Rational(1,5),3), '1/5+3i'.to_c) + assert_equal(Complex(Rational(1,5),-3), '1/5-3i'.to_c) + assert_equal(Complex(Rational(-1,5),3), '-1/5+3i'.to_c) + assert_equal(Complex(Rational(-1,5),-3), '-1/5-3i'.to_c) + assert_equal(Complex(Rational(1,5),Rational(3,2)), '1/5+3/2i'.to_c) + assert_equal(Complex(Rational(1,5),Rational(-3,2)), '1/5-3/2i'.to_c) + assert_equal(Complex(Rational(-1,5),Rational(3,2)), '-1/5+3/2i'.to_c) + assert_equal(Complex(Rational(-1,5),Rational(-3,2)), '-1/5-3/2i'.to_c) + assert_equal(Complex(Rational(1,5),Rational(3,2)), '1/5+(3/2)i'.to_c) + assert_equal(Complex(Rational(1,5),Rational(-3,2)), '1/5-(3/2)i'.to_c) + assert_equal(Complex(Rational(-1,5),Rational(3,2)), '-1/5+(3/2)i'.to_c) + assert_equal(Complex(Rational(-1,5),Rational(-3,2)), '-1/5-(3/2)i'.to_c) + end + + end + + def test_respond + c = Complex(1,1) + assert_equal(false, c.respond_to?(:<)) + assert_equal(false, c.respond_to?(:<=)) + assert_equal(false, c.respond_to?(:<=>)) + assert_equal(false, c.respond_to?(:>)) + assert_equal(false, c.respond_to?(:>=)) + assert_equal(false, c.respond_to?(:between?)) +# assert_equal(false, c.respond_to?(:div)) # ? + assert_equal(false, c.respond_to?(:divmod)) + assert_equal(false, c.respond_to?(:floor)) + assert_equal(false, c.respond_to?(:ceil)) + assert_equal(false, c.respond_to?(:modulo)) + assert_equal(false, c.respond_to?(:round)) + assert_equal(false, c.respond_to?(:step)) + assert_equal(false, c.respond_to?(:tunrcate)) + + assert_equal(false, c.respond_to?(:positive?)) + assert_equal(false, c.respond_to?(:negative?)) +# assert_equal(false, c.respond_to?(:sign)) + + assert_equal(false, c.respond_to?(:quotient)) + assert_equal(false, c.respond_to?(:quot)) + assert_equal(false, c.respond_to?(:quotrem)) + + assert_equal(false, c.respond_to?(:gcd)) + assert_equal(false, c.respond_to?(:lcm)) + assert_equal(false, c.respond_to?(:gcdlcm)) + end + + def test_to_i + assert_equal(3, Complex(3).to_i) + assert_equal(3, Integer(Complex(3))) + assert_raise(RangeError){Complex(3,2).to_i} + assert_raise(RangeError){Integer(Complex(3,2))} + end + + def test_to_f + assert_equal(3.0, Complex(3).to_f) + assert_equal(3.0, Float(Complex(3))) + assert_raise(RangeError){Complex(3,2).to_f} + assert_raise(RangeError){Float(Complex(3,2))} + end + + def test_to_r + if defined?(Rational) && !Rational.instance_variable_get('@RCS_ID') + assert_equal(Rational(3), Complex(3).to_r) + assert_equal(Rational(3), Rational(Complex(3))) + assert_raise(RangeError){Complex(3,2).to_r} + assert_raise(RangeError){Rational(Complex(3,2))} + end + end + + def test_to_c + c = nil.to_c + assert_equal([0,0] , [c.real, c.image]) + + c = 0.to_c + assert_equal([0,0] , [c.real, c.image]) + + c = 1.to_c + assert_equal([1,0] , [c.real, c.image]) + + c = 1.1.to_c + assert_equal([1.1, 0], [c.real, c.image]) + + if defined?(Rational) + c = Rational(1,2).to_c + assert_equal([Rational(1,2), 0], [c.real, c.image]) + end + + c = Complex(1,2).to_c + assert_equal([1, 2], [c.real, c.image]) + end + + def test_prec + assert_equal(nil, Complex < Precision) + end + + def test_supp + assert_equal(true, 1.scalar?) + assert_equal(true, 1.1.scalar?) + + assert_equal(1, 1.real) + assert_equal(0, 1.image) + assert_equal(0, 1.imag) + + assert_equal(1.1, 1.1.real) + assert_equal(0, 1.1.image) + assert_equal(0, 1.1.imag) + + assert_equal(0, 1.arg) + assert_equal(0, 1.angle) + + assert_equal(0, 1.0.arg) + assert_equal(0, 1.0.angle) + + assert_equal(Math::PI, -1.arg) + assert_equal(Math::PI, -1.angle) + + assert_equal(Math::PI, -1.0.arg) + assert_equal(Math::PI, -1.0.angle) + + assert_equal([1,0], 1.polar) + assert_equal([1, Math::PI], -1.polar) + + assert_equal([1.0,0], 1.0.polar) + assert_equal([1.0, Math::PI], -1.0.polar) + + assert_equal(1, 1.conjugate) + assert_equal(-1, -1.conjugate) + assert_equal(1, 1.conj) + assert_equal(-1, -1.conj) + + assert_equal(1.1, 1.1.conjugate) + assert_equal(-1.1, -1.1.conjugate) + assert_equal(1.1, 1.1.conj) + assert_equal(-1.1, -1.1.conj) + + assert_equal(1, 1.numerator) + assert_equal(9, 9.numerator) + assert_equal(1, 1.denominator) + assert_equal(1, 9.denominator) + + if defined?(Rational) && !Rational.instance_variable_get('@RCS_ID') + assert_equal(1.0, 1.0.numerator) + assert_equal(9.0, 9.0.numerator) + assert_equal(1.0, 1.0.denominator) + assert_equal(1.0, 9.0.denominator) + end + +=begin + if defined?(Rational) && !Rational.instance_variable_get('@RCS_ID') + assert_equal(Rational(1,9), 9.reciprocal) + assert_equal(Rational(1,9), 9.0.reciprocal) + assert_equal(Rational(1,9), 9.inverse) + assert_equal(Rational(1,9), 9.0.inverse) + end +=end + + if defined?(Rational) + assert_equal(Rational(1,2), 1.quo(2)) + assert_equal(Rational(5000000000), 10000000000.quo(2)) + assert_equal(0.5, 1.0.quo(2)) + assert_equal(Rational(1,4), Rational(1,2).quo(2)) + assert_equal(Complex(Rational(1,2),Rational(1)), Complex(1,2).quo(2)) + else + assert_equal(0.5, 1.quo(2)) + assert_equal(5000000000.0, 10000000000.quo(2)) + assert_equal(0.5, 1.0.quo(2)) + assert_equal(Complex(0.5,1.0), Complex(1,2).quo(2)) + end + + if defined?(Rational) && !Rational.instance_variable_get('@RCS_ID') + assert_equal(Rational(1,2), 1.rdiv(2)) + assert_equal(Rational(5000000000), 10000000000.rdiv(2)) + assert_equal(Rational(1,2), 1.0.rdiv(2)) + assert_equal(Rational(1,4), Rational(1,2).rdiv(2)) + assert_equal(Complex(Rational(1,2),Rational(1)), Complex(1,2).quo(2)) + end + + assert_equal(0.5, 1.fdiv(2)) + assert_equal(5000000000.0, 10000000000.fdiv(2)) + assert_equal(0.5, 1.0.fdiv(2)) + if defined?(Rational) + assert_equal(0.25, Rational(1,2).fdiv(2)) + end + assert_equal(Complex(0.5,1.0), Complex(1,2).quo(2)) + + unless $".grep(/complex/).empty? + assert_equal(Complex(0,2), Math.sqrt(-4.0)) +# assert_equal(true, Math.sqrt(-4.0).inexact?) + assert_equal(Complex(0,2), Math.sqrt(-4)) +# assert_equal(true, Math.sqrt(-4).exact?) + if defined?(Rational) + assert_equal(Complex(0,2), Math.sqrt(Rational(-4))) +# assert_equal(true, Math.sqrt(Rational(-4)).exact?) + end + + assert_equal(Complex(0,3), Math.sqrt(-9.0)) +# assert_equal(true, Math.sqrt(-9.0).inexact?) + assert_equal(Complex(0,3), Math.sqrt(-9)) +# assert_equal(true, Math.sqrt(-9).exact?) + if defined?(Rational) + assert_equal(Complex(0,3), Math.sqrt(Rational(-9))) +# assert_equal(true, Math.sqrt(Rational(-9)).exact?) + end + + c = Math.sqrt(Complex(1, 2)) + assert_in_delta(1.272, c.real, 0.001) + assert_in_delta(0.786, c.image, 0.001) + + c = Math.sqrt(-9) + assert_in_delta(0.0, c.real, 0.001) + assert_in_delta(3.0, c.image, 0.001) + + c = Math.exp(Complex(1, 2)) + assert_in_delta(-1.131, c.real, 0.001) + assert_in_delta(2.471, c.image, 0.001) + + c = Math.sin(Complex(1, 2)) + assert_in_delta(3.165, c.real, 0.001) + assert_in_delta(1.959, c.image, 0.001) + + c = Math.cos(Complex(1, 2)) + assert_in_delta(2.032, c.real, 0.001) + assert_in_delta(-3.051, c.image, 0.001) + + c = Math.tan(Complex(1, 2)) + assert_in_delta(0.033, c.real, 0.001) + assert_in_delta(1.014, c.image, 0.001) + + c = Math.sinh(Complex(1, 2)) + assert_in_delta(-0.489, c.real, 0.001) + assert_in_delta(1.403, c.image, 0.001) + + c = Math.cosh(Complex(1, 2)) + assert_in_delta(-0.642, c.real, 0.001) + assert_in_delta(1.068, c.image, 0.001) + + c = Math.tanh(Complex(1, 2)) + assert_in_delta(1.166, c.real, 0.001) + assert_in_delta(-0.243, c.image, 0.001) + + c = Math.log(Complex(1, 2)) + assert_in_delta(0.804, c.real, 0.001) + assert_in_delta(1.107, c.image, 0.001) + + c = Math.log(Complex(1, 2), Math::E) + assert_in_delta(0.804, c.real, 0.001) + assert_in_delta(1.107, c.image, 0.001) + + c = Math.log(-1) + assert_in_delta(0.0, c.real, 0.001) + assert_in_delta(Math::PI, c.image, 0.001) + + c = Math.log(8, 2) + assert_in_delta(3.0, c.real, 0.001) + assert_in_delta(0.0, c.image, 0.001) + + c = Math.log(-8, -2) + assert_in_delta(1.092, c.real, 0.001) + assert_in_delta(-0.420, c.image, 0.001) + + c = Math.log10(Complex(1, 2)) + assert_in_delta(0.349, c.real, 0.001) + assert_in_delta(0.480, c.image, 0.001) + + c = Math.asin(Complex(1, 2)) + assert_in_delta(0.427, c.real, 0.001) + assert_in_delta(1.528, c.image, 0.001) + + c = Math.acos(Complex(1, 2)) + assert_in_delta(1.143, c.real, 0.001) + assert_in_delta(-1.528, c.image, 0.001) + + c = Math.atan(Complex(1, 2)) + assert_in_delta(1.338, c.real, 0.001) + assert_in_delta(0.402, c.image, 0.001) + + c = Math.atan2(Complex(1, 2), 1) + assert_in_delta(1.338, c.real, 0.001) + assert_in_delta(0.402, c.image, 0.001) + + c = Math.asinh(Complex(1, 2)) + assert_in_delta(1.469, c.real, 0.001) + assert_in_delta(1.063, c.image, 0.001) + + c = Math.acosh(Complex(1, 2)) + assert_in_delta(1.528, c.real, 0.001) + assert_in_delta(1.143, c.image, 0.001) + + c = Math.atanh(Complex(1, 2)) + assert_in_delta(0.173, c.real, 0.001) + assert_in_delta(1.178, c.image, 0.001) + end + + end + + def test_fixed_bug + if defined?(Rational) && !Rational.instance_variable_get('@RCS_ID') + assert_equal(Complex(1), 1 ** Complex(1)) + end + assert_equal('-1.0-0.0i', Complex(-1.0, -0.0).to_s) + end + + def test_known_bug + end + +end diff --git a/test/ruby/test_rational.rb b/test/ruby/test_rational.rb new file mode 100644 index 0000000000..bafe995a26 --- /dev/null +++ b/test/ruby/test_rational.rb @@ -0,0 +1,971 @@ +require 'test/unit' + +class RationalSub < Rational; end + +class Rational_Test < Test::Unit::TestCase + + def test_sub + c = RationalSub.__send__(:new, 1) + cc = RationalSub.__send__(:convert, 1) + if defined?(RationalSub::Unify) + assert_instance_of(Fixnum, c) + assert_instance_of(Fixnum, cc) + else + assert_instance_of(RationalSub, c) + assert_instance_of(RationalSub, cc) + + c2 = c + 1 + assert_instance_of(RationalSub, c2) + c2 = c - 1 + assert_instance_of(RationalSub, c2) + + c3 = c - c2 + assert_instance_of(RationalSub, c3) + + s = Marshal.dump(c) + c5 = Marshal.load(s) + assert_equal(c, c5) + assert_instance_of(RationalSub, c5) + end + end + + def test_hash + assert_instance_of(Fixnum, Rational(1,2).hash) + + h = {} + h[Rational(0)] = 0 + h[Rational(1,1)] = 1 + h[Rational(2,1)] = 2 + h[Rational(3,1)] = 3 + + assert_equal(4, h.size) + assert_equal(2, h[Rational(2,1)]) + + h[Rational(0,1)] = 9 + assert_equal(4, h.size) + end + + def test_freeze + c = Rational(1) + c.freeze + unless defined?(Rational::Unify) + assert_equal(true, c.frozen?) + end + assert_instance_of(String, c.to_s) + end + + def test_new_bang # no unify & no reduce + assert_instance_of(Rational, Rational.__send__(:new!, 2,1)) + assert_equal([2,1], Rational.__send__(:new!, 2,1). + instance_eval{[numerator, denominator]}) + assert_equal([2,4], Rational.__send__(:new!, 2,4). + instance_eval{[numerator, denominator]}) + assert_equal([-2,4], Rational.__send__(:new!, -2,4). + instance_eval{[numerator, denominator]}) + assert_equal([-2,4], Rational.__send__(:new!, 2,-4). + instance_eval{[numerator, denominator]}) + assert_equal([2,4], Rational.__send__(:new!, -2,-4). + instance_eval{[numerator, denominator]}) + + # to_i + assert_equal([2,1], Rational.__send__(:new!, Rational(2)). + instance_eval{[numerator, denominator]}) + assert_equal([2,3], Rational.__send__(:new!, Rational(2), Rational(3)). + instance_eval{[numerator, denominator]}) + assert_equal([2,3], Rational.__send__(:new!, 2, Rational(3)). + instance_eval{[numerator, denominator]}) + + assert_equal([1,1], Rational.__send__(:new!, 1.1). + instance_eval{[numerator, denominator]}) + assert_equal([-1,1], Rational.__send__(:new!, -1.1). + instance_eval{[numerator, denominator]}) + assert_equal([1,1], Rational.__send__(:new!, '1'). + instance_eval{[numerator, denominator]}) + assert_equal([0,1], Rational.__send__(:new!, nil). + instance_eval{[numerator, denominator]}) + end + +=begin + def test_reduce + if defined?(Rational::Unify) + assert_instance_of(Fixnum, Rational.__send__(:reduce, 2,1)) + else + assert_instance_of(Rational, Rational.__send__(:reduce, 2,1)) + assert_instance_of(Rational, Rational.__send__(:reduce, 2,1)) + end + assert_equal([2,1], Rational.__send__(:reduce, 2,1). + instance_eval{[numerator, denominator]}) + assert_equal([1,2], Rational.__send__(:reduce, 2,4). + instance_eval{[numerator, denominator]}) + assert_equal([-1,2], Rational.__send__(:reduce, -2,4). + instance_eval{[numerator, denominator]}) + assert_equal([-1,2], Rational.__send__(:reduce, 2,-4). + instance_eval{[numerator, denominator]}) + assert_equal([1,2], Rational.__send__(:reduce, -2,-4). + instance_eval{[numerator, denominator]}) + + assert_raise(ArgumentError){Rational.__send__(:reduce, Rational(1,2),2)} + assert_raise(ArgumentError){Rational.__send__(:reduce, 2,Rational(1,2))} + assert_raise(ArgumentError){Rational. + __send__(:reduce, Rational(1,2),Rational(1,2))} + + assert_raise(ArgumentError){Rational.__send__(:reduce, 1.1)} + assert_raise(ArgumentError){Rational.__send__(:reduce, -1.1)} + assert_raise(ArgumentError){Rational.__send__(:reduce, '1')} + assert_raise(ArgumentError){Rational.__send__(:reduce, nil)} + end +=end + + def test_new + if defined?(Rational::Unify) + assert_instance_of(Fixnum, Rational.__send__(:new, 2,1)) + else + assert_instance_of(Rational, Rational.__send__(:new, 2,1)) + assert_equal([2,1], Rational.__send__(:new, 2,1). + instance_eval{[numerator, denominator]}) + end + assert_equal([1,2], Rational.__send__(:new, 2,4). + instance_eval{[numerator, denominator]}) + assert_equal([-1,2], Rational.__send__(:new, -2,4). + instance_eval{[numerator, denominator]}) + assert_equal([-1,2], Rational.__send__(:new, 2,-4). + instance_eval{[numerator, denominator]}) + assert_equal([1,2], Rational.__send__(:new, -2,-4). + instance_eval{[numerator, denominator]}) + + assert_raise(ArgumentError){Rational.__send__(:new, Rational(1,2),2)} + assert_raise(ArgumentError){Rational.__send__(:new, 2,Rational(1,2))} + assert_raise(ArgumentError){Rational.__send__(:new, Rational(1,2),Rational(1,2))} + + assert_raise(ArgumentError){Rational.__send__(:new, 1.1)} + assert_raise(ArgumentError){Rational.__send__(:new, -1.1)} + assert_raise(ArgumentError){Rational.__send__(:new, '1')} + assert_raise(ArgumentError){Rational.__send__(:new, nil)} +=begin + assert_raise(ArgumentError){Rational.__send__(:new, Rational(1))} + if defined?(Complex) + assert_raise(ArgumentError){Rational.__send__(:new, Complex(1))} + end +=end + end + + def test_conv + c = Rational(0,1) + assert_equal(Rational.__send__(:new, 0,1), c) + + c = Rational(2**32, 2**32) + assert_equal(Rational.__send__(:new, 2**32,2**32), c) + assert_equal([1,1], [c.numerator,c.denominator]) + + c = Rational(-2**32, 2**32) + assert_equal(Rational.__send__(:new, -2**32,2**32), c) + assert_equal([-1,1], [c.numerator,c.denominator]) + + c = Rational(2**32, -2**32) + assert_equal(Rational.__send__(:new, 2**32,-2**32), c) + assert_equal([-1,1], [c.numerator,c.denominator]) + + c = Rational(-2**32, -2**32) + assert_equal(Rational.__send__(:new, -2**32,-2**32), c) + assert_equal([1,1], [c.numerator,c.denominator]) + + c = Rational(Rational(1,2),2) + assert_equal(Rational.__send__(:new, 1,4), c) + + c = Rational(2,Rational(1,2)) + assert_equal(Rational.__send__(:new, 4), c) + + c = Rational(Rational(1,2),Rational(1,2)) + assert_equal(Rational.__send__(:new, 1), c) + + assert_equal(Rational.__send__(:new, 1),Rational(1)) + assert_equal(1.1.to_r,Rational(1.1)) + assert_equal(Rational.__send__(:new, 1),Rational('1')) + assert_raise(ArgumentError){Rational(nil)} + end + + def test_attr + c = Rational(4) + + assert_equal(4, c.numerator) + assert_equal(1, c.denominator) + + c = Rational(4,5) + + assert_equal(4, c.numerator) + assert_equal(5, c.denominator) + + c = Rational.__send__(:new, 4) + + assert_equal(4, c.numerator) + assert_equal(1, c.denominator) + + c = Rational.__send__(:new, 4,5) + + assert_equal(4, c.numerator) + assert_equal(5, c.denominator) + + c = Rational.__send__(:new!, 4) + + assert_equal(4, c.numerator) + assert_equal(1, c.denominator) + + c = Rational.__send__(:new!, 4,5) + + assert_equal(4, c.numerator) + assert_equal(5, c.denominator) + end + + def test_attr2 + c = Rational(1) + + if defined?(Rational::Unify) + assert_equal(true, c.scalar?) +=begin + assert_equal(true, c.finite?) + assert_equal(false, c.infinite?) + assert_equal(false, c.nan?) + assert_equal(true, c.integer?) + assert_equal(false, c.float?) + assert_equal(true, c.rational?) + assert_equal(true, c.real?) + assert_equal(false, c.complex?) + assert_equal(true, c.exact?) + assert_equal(false, c.inexact?) +=end + else + assert_equal(true, c.scalar?) +=begin + assert_equal(true, c.finite?) + assert_equal(false, c.infinite?) + assert_equal(false, c.nan?) + assert_equal(false, c.integer?) + assert_equal(false, c.float?) + assert_equal(true, c.rational?) + assert_equal(true, c.real?) + assert_equal(false, c.complex?) + assert_equal(true, c.exact?) + assert_equal(false, c.inexact?) +=end + end + +=begin + assert_equal(true, Rational(0).positive?) + assert_equal(true, Rational(1).positive?) + assert_equal(false, Rational(-1).positive?) + assert_equal(false, Rational(0).negative?) + assert_equal(false, Rational(1).negative?) + assert_equal(true, Rational(-1).negative?) + + assert_equal(0, Rational(0).sign) + assert_equal(1, Rational(2).sign) + assert_equal(-1, Rational(-2).sign) +=end + + assert_equal(true, Rational(0).zero?) + assert_equal(true, Rational(0,1).zero?) + assert_equal(false, Rational(1,1).zero?) + + assert_equal(nil, Rational(0).nonzero?) + assert_equal(nil, Rational(0,1).nonzero?) + assert_equal(Rational(1,1), Rational(1,1).nonzero?) + end + + def test_uplus + assert_equal(Rational(1), +Rational(1)) + assert_equal(Rational(-1), +Rational(-1)) + assert_equal(Rational(1,1), +Rational(1,1)) + assert_equal(Rational(-1,1), +Rational(-1,1)) + assert_equal(Rational(-1,1), +Rational(1,-1)) + assert_equal(Rational(1,1), +Rational(-1,-1)) + end + + def test_negate + assert_equal(Rational(-1), -Rational(1)) + assert_equal(Rational(1), -Rational(-1)) + assert_equal(Rational(-1,1), -Rational(1,1)) + assert_equal(Rational(1,1), -Rational(-1,1)) + assert_equal(Rational(1,1), -Rational(1,-1)) + assert_equal(Rational(-1,1), -Rational(-1,-1)) + +=begin + assert_equal(0, Rational(0).negate) + assert_equal(-2, Rational(2).negate) + assert_equal(2, Rational(-2).negate) +=end + end + + def test_add + c = Rational(1,2) + c2 = Rational(2,3) + + assert_equal(Rational(7,6), c + c2) + + assert_equal(Rational(5,2), c + 2) + assert_equal(2.5, c + 2.0) + end + + def test_sub + c = Rational(1,2) + c2 = Rational(2,3) + + assert_equal(Rational(-1,6), c - c2) + + assert_equal(Rational(-3,2), c - 2) + assert_equal(-1.5, c - 2.0) + end + + def test_mul + c = Rational(1,2) + c2 = Rational(2,3) + + assert_equal(Rational(1,3), c * c2) + + assert_equal(Rational(1,1), c * 2) + assert_equal(1.0, c * 2.0) + end + + def test_div + c = Rational(1,2) + c2 = Rational(2,3) + + assert_equal(Rational(3,4), c / c2) + + assert_equal(Rational(1,4), c / 2) + assert_equal(0.25, c / 2.0) + end + + def assert_eql(exp, act, *args) + unless Array === exp + exp = [exp] + end + unless Array === act + act = [act] + end + exp.zip(act).each do |e, a| + na = [e, a] + args + assert_equal(*na) + na = [e.class, a] + args + assert_instance_of(*na) + end + end + + def test_idiv + c = Rational(1,2) + c2 = Rational(2,3) + + assert_eql(0, c.div(c2)) + assert_eql(0, c.div(2)) + assert_eql(0, c.div(2.0)) + + c = Rational(301,100) + c2 = Rational(7,5) + + assert_equal(2, c.div(c2)) + assert_equal(-3, c.div(-c2)) + assert_equal(-3, (-c).div(c2)) + assert_equal(2, (-c).div(-c2)) + + c = Rational(301,100) + c2 = Rational(2) + + assert_equal(1, c.div(c2)) + assert_equal(-2, c.div(-c2)) + assert_equal(-2, (-c).div(c2)) + assert_equal(1, (-c).div(-c2)) + + unless defined?(Rational::Unify) + c = Rational(11) + c2 = Rational(3) + + assert_equal(3, c.div(c2)) + assert_equal(-4, c.div(-c2)) + assert_equal(-4, (-c).div(c2)) + assert_equal(3, (-c).div(-c2)) + end + end + + def test_divmod + c = Rational(1,2) + c2 = Rational(2,3) + + assert_eql([0, Rational(1,2)], c.divmod(c2)) + assert_eql([0, Rational(1,2)], c.divmod(2)) + assert_eql([0, 0.5], c.divmod(2.0)) + + c = Rational(301,100) + c2 = Rational(7,5) + + assert_equal([2, Rational(21,100)], c.divmod(c2)) + assert_equal([-3, Rational(-119,100)], c.divmod(-c2)) + assert_equal([-3, Rational(119,100)], (-c).divmod(c2)) + assert_equal([2, Rational(-21,100)], (-c).divmod(-c2)) + + c = Rational(301,100) + c2 = Rational(2) + + assert_equal([1, Rational(101,100)], c.divmod(c2)) + assert_equal([-2, Rational(-99,100)], c.divmod(-c2)) + assert_equal([-2, Rational(99,100)], (-c).divmod(c2)) + assert_equal([1, Rational(-101,100)], (-c).divmod(-c2)) + + unless defined?(Rational::Unify) + c = Rational(11) + c2 = Rational(3) + + assert_equal([3,2], c.divmod(c2)) + assert_equal([-4,-1], c.divmod(-c2)) + assert_equal([-4,1], (-c).divmod(c2)) + assert_equal([3,-2], (-c).divmod(-c2)) + end + end + +=begin + def test_quot + c = Rational(1,2) + c2 = Rational(2,3) + + assert_eql(0, c.quot(c2)) + assert_eql(0, c.quot(2)) + assert_eql(0, c.quot(2.0)) + + c = Rational(301,100) + c2 = Rational(7,5) + + assert_equal(2, c.quot(c2)) + assert_equal(-2, c.quot(-c2)) + assert_equal(-2, (-c).quot(c2)) + assert_equal(2, (-c).quot(-c2)) + + c = Rational(301,100) + c2 = Rational(2) + + assert_equal(1, c.quot(c2)) + assert_equal(-1, c.quot(-c2)) + assert_equal(-1, (-c).quot(c2)) + assert_equal(1, (-c).quot(-c2)) + + unless defined?(Rational::Unify) + c = Rational(11) + c2 = Rational(3) + + assert_equal(3, c.quot(c2)) + assert_equal(-3, c.quot(-c2)) + assert_equal(-3, (-c).quot(c2)) + assert_equal(3, (-c).quot(-c2)) + end + end + + def test_quotrem + c = Rational(1,2) + c2 = Rational(2,3) + + assert_eql([0, Rational(1,2)], c.quotrem(c2)) + assert_eql([0, Rational(1,2)], c.quotrem(2)) + assert_eql([0, 0.5], c.quotrem(2.0)) + + c = Rational(301,100) + c2 = Rational(7,5) + + assert_equal([2, Rational(21,100)], c.quotrem(c2)) + assert_equal([-2, Rational(21,100)], c.quotrem(-c2)) + assert_equal([-2, Rational(-21,100)], (-c).quotrem(c2)) + assert_equal([2, Rational(-21,100)], (-c).quotrem(-c2)) + + c = Rational(301,100) + c2 = Rational(2) + + assert_equal([1, Rational(101,100)], c.quotrem(c2)) + assert_equal([-1, Rational(101,100)], c.quotrem(-c2)) + assert_equal([-1, Rational(-101,100)], (-c).quotrem(c2)) + assert_equal([1, Rational(-101,100)], (-c).quotrem(-c2)) + + unless defined?(Rational::Unify) + c = Rational(11) + c2 = Rational(3) + + assert_equal([3,2], c.quotrem(c2)) + assert_equal([-3,2], c.quotrem(-c2)) + assert_equal([-3,-2], (-c).quotrem(c2)) + assert_equal([3,-2], (-c).quotrem(-c2)) + end + end +=end + + def test_quo + c = Rational(1,2) + c2 = Rational(2,3) + + assert_equal(Rational(3,4), c.quo(c2)) + + assert_equal(Rational(1,4), c.quo(2)) + assert_equal(Rational(0.25), c.quo(2.0)) + end + + def test_rdiv + c = Rational(1,2) + c2 = Rational(2,3) + + assert_equal(Rational(3,4), c.rdiv(c2)) + + assert_equal(Rational(1,4), c.rdiv(2)) + assert_equal(Rational(0.25), c.rdiv(2.0)) + end + + def test_fdiv + c = Rational(1,2) + c2 = Rational(2,3) + + assert_equal(0.75, c.fdiv(c2)) + + assert_equal(0.25, c.fdiv(2)) + assert_equal(0.25, c.fdiv(2.0)) + end + + def test_expt + c = Rational(1,2) + c2 = Rational(2,3) + + r = c ** c2 + assert_in_delta(0.6299, r, 0.001) + + assert_equal(Rational(1,4), c ** 2) + assert_equal(Rational(4), c ** -2) + assert_equal(Rational(1,4), (-c) ** 2) + assert_equal(Rational(4), (-c) ** -2) + + assert_equal(0.25, c ** 2.0) + assert_equal(4.0, c ** -2.0) + + assert_equal(Rational(1,4), c ** Rational(2)) + assert_equal(Rational(4), c ** Rational(-2)) + + assert_equal(Rational(1), 0 ** Rational(0)) + assert_equal(Rational(1), Rational(0) ** 0) + assert_equal(Rational(1), Rational(0) ** Rational(0)) + + # p ** p + x = 2 ** Rational(2) + assert_equal(Rational(4), x) + unless defined?(Rational::Unify) + assert_instance_of(Rational, x) + end + assert_equal(4, x.numerator) + assert_equal(1, x.denominator) + + x = Rational(2) ** 2 + assert_equal(Rational(4), x) + unless defined?(Rational::Unify) + assert_instance_of(Rational, x) + end + assert_equal(4, x.numerator) + assert_equal(1, x.denominator) + + x = Rational(2) ** Rational(2) + assert_equal(Rational(4), x) + unless defined?(Rational::Unify) + assert_instance_of(Rational, x) + end + assert_equal(4, x.numerator) + assert_equal(1, x.denominator) + + # -p ** p + x = (-2) ** Rational(2) + assert_equal(Rational(4), x) + unless defined?(Rational::Unify) + assert_instance_of(Rational, x) + end + assert_equal(4, x.numerator) + assert_equal(1, x.denominator) + + x = Rational(-2) ** 2 + assert_equal(Rational(4), x) + unless defined?(Rational::Unify) + assert_instance_of(Rational, x) + end + assert_equal(4, x.numerator) + assert_equal(1, x.denominator) + + x = Rational(-2) ** Rational(2) + assert_equal(Rational(4), x) + unless defined?(Rational::Unify) + assert_instance_of(Rational, x) + end + assert_equal(4, x.numerator) + assert_equal(1, x.denominator) + + # p ** -p + x = 2 ** Rational(-2) + assert_equal(Rational(1,4), x) + assert_instance_of(Rational, x) + assert_equal(1, x.numerator) + assert_equal(4, x.denominator) + + x = Rational(2) ** -2 + assert_equal(Rational(1,4), x) + assert_instance_of(Rational, x) + assert_equal(1, x.numerator) + assert_equal(4, x.denominator) + + x = Rational(2) ** Rational(-2) + assert_equal(Rational(1,4), x) + assert_instance_of(Rational, x) + assert_equal(1, x.numerator) + assert_equal(4, x.denominator) + + # -p ** -p + x = (-2) ** Rational(-2) + assert_equal(Rational(1,4), x) + assert_instance_of(Rational, x) + assert_equal(1, x.numerator) + assert_equal(4, x.denominator) + + x = Rational(-2) ** -2 + assert_equal(Rational(1,4), x) + assert_instance_of(Rational, x) + assert_equal(1, x.numerator) + assert_equal(4, x.denominator) + + x = Rational(-2) ** Rational(-2) + assert_equal(Rational(1,4), x) + assert_instance_of(Rational, x) + assert_equal(1, x.numerator) + assert_equal(4, x.denominator) + end + + def test_cmp + assert_equal(-1, Rational(-1) <=> Rational(0)) + assert_equal(0, Rational(0) <=> Rational(0)) + assert_equal(+1, Rational(+1) <=> Rational(0)) + + assert_equal(-1, Rational(-1) <=> 0) + assert_equal(0, Rational(0) <=> 0) + assert_equal(+1, Rational(+1) <=> 0) + + assert_equal(-1, Rational(-1) <=> 0.0) + assert_equal(0, Rational(0) <=> 0.0) + assert_equal(+1, Rational(+1) <=> 0.0) + + assert_equal(-1, Rational(1,2) <=> Rational(2,3)) + assert_equal(0, Rational(2,3) <=> Rational(2,3)) + assert_equal(+1, Rational(2,3) <=> Rational(1,2)) + + f = 2**30-1 + b = 2**30 + + assert_equal(0, Rational(f) <=> Rational(f)) + assert_equal(-1, Rational(f) <=> Rational(b)) + assert_equal(+1, Rational(b) <=> Rational(f)) + assert_equal(0, Rational(b) <=> Rational(b)) + + assert_equal(-1, Rational(f-1) <=> Rational(f)) + assert_equal(+1, Rational(f) <=> Rational(f-1)) + assert_equal(-1, Rational(b-1) <=> Rational(b)) + assert_equal(+1, Rational(b) <=> Rational(b-1)) + + assert_equal(false, Rational(0) < Rational(0)) + assert_equal(true, Rational(0) <= Rational(0)) + assert_equal(true, Rational(0) >= Rational(0)) + assert_equal(false, Rational(0) > Rational(0)) + end + + def test_equal + assert(Rational(1,1) == Rational(1)) + assert(Rational(1,1) == Rational.__send__(:new, 1)) + assert(Rational(1,1) == Rational.__send__(:new, 1,1)) + assert(Rational(1,1) == Rational.__send__(:new!, 1)) + assert(Rational(1,1) == Rational.__send__(:new!, 1,1)) + + assert(Rational(-1,1) == Rational(-1)) + assert(Rational(-1,1) == Rational.__send__(:new, -1)) + assert(Rational(-1,1) == Rational.__send__(:new, -1,1)) + assert(Rational(-1,1) == Rational.__send__(:new!, -1)) + assert(Rational(-1,1) == Rational.__send__(:new!, -1,1)) + + assert_equal(false, Rational(2,1) == Rational(1)) + assert_equal(true, Rational(2,1) != Rational(1)) + assert_equal(false, Rational(1) == nil) + assert_equal(false, Rational(1) == '') + end + + def test_unify + if defined?(Rational::Unify) + assert_instance_of(Fixnum, Rational(1,2) + Rational(1,2)) + assert_instance_of(Fixnum, Rational(1,2) - Rational(1,2)) + assert_instance_of(Fixnum, Rational(1,2) * 2) + assert_instance_of(Fixnum, Rational(1,2) / Rational(1,2)) + assert_instance_of(Fixnum, Rational(1,2).div(Rational(1,2))) + assert_instance_of(Fixnum, Rational(1,2).quo(Rational(1,2))) + assert_instance_of(Fixnum, Rational(1,2) ** -2) + end + end + + def test_to_s + c = Rational(1,2) + + assert_instance_of(String, c.to_s) + assert_equal('1/2', c.to_s) + + assert_equal('0', Rational(0,2).to_s) + assert_equal('0', Rational(0,-2).to_s) + assert_equal('1/2', Rational(1,2).to_s) + assert_equal('-1/2', Rational(-1,2).to_s) + assert_equal('1/2', Rational(-1,-2).to_s) + assert_equal('-1/2', Rational(1,-2).to_s) + assert_equal('1/2', Rational(-1,-2).to_s) + end + + def test_inspect + c = Rational(1,2) + + assert_instance_of(String, c.inspect) + assert_equal('Rational(1, 2)', c.inspect) + end + + def test_marshal + c = Rational(1,2) + + s = Marshal.dump(c) + c2 = Marshal.load(s) + assert_equal(c, c2) + assert_instance_of(Rational, c2) + end + + def test_parse + assert_equal(Rational(0), ''.to_r) + assert_equal(Rational(0), ' '.to_r) + assert_equal(Rational(5), '5'.to_r) + assert_equal(Rational(-5), '-5'.to_r) + assert_equal(Rational(5,3), '5/3'.to_r) + assert_equal(Rational(-5,3), '-5/3'.to_r) + assert_equal(Rational(5,-3), '5/-3'.to_r) + assert_equal(Rational(-5,-3), '-5/-3'.to_r) + + assert_equal(Rational(5), '5.0'.to_r) + assert_equal(Rational(-5), '-5.0'.to_r) + assert_equal(Rational(5,3), '5.0/3'.to_r) + assert_equal(Rational(-5,3), '-5.0/3'.to_r) + assert_equal(Rational(5,-3), '5.0/-3'.to_r) + assert_equal(Rational(-5,-3), '-5.0/-3'.to_r) + + assert_equal(Rational(5), '5e0'.to_r) + assert_equal(Rational(-5), '-5e0'.to_r) + assert_equal(Rational(5,3), '5e0/3'.to_r) + assert_equal(Rational(-5,3), '-5e0/3'.to_r) + assert_equal(Rational(5,-3), '5e0/-3'.to_r) + assert_equal(Rational(-5,-3), '-5e0/-3'.to_r) + + assert_equal(Rational(33,100), '0.33'.to_r) + assert_equal(Rational(-33,100), '-0.33'.to_r) + assert_equal(Rational(-33,100), '-0.3_3'.to_r) + + assert_equal(Rational(1,2), '5e-1'.to_r) + assert_equal(Rational(50), '5e+1'.to_r) + assert_equal(Rational(1,2), '5.0e-1'.to_r) + assert_equal(Rational(50), '5.0e+1'.to_r) + assert_equal(Rational(50), '5e1'.to_r) + assert_equal(Rational(50), '5E1'.to_r) + assert_equal(Rational(500), '5e2'.to_r) + assert_equal(Rational(5000), '5e3'.to_r) + assert_equal(Rational(500000000000), '5e1_1'.to_r) + + assert_equal(Rational(5), Rational('5')) + assert_equal(Rational(-5), Rational('-5')) + assert_equal(Rational(5,3), Rational('5/3')) + assert_equal(Rational(-5,3), Rational('-5/3')) + assert_equal(Rational(5,-3), Rational('5/-3')) + assert_equal(Rational(-5,-3), Rational('-5/-3')) + + assert_equal(Rational(33,100), Rational('0.33')) + assert_equal(Rational(-33,100), Rational('-0.33')) + assert_equal(Rational(-33,100), Rational('-0.3_3')) + + assert_equal(Rational(1,2), Rational('5e-1')) + assert_equal(Rational(50), Rational('5e1')) + assert_equal(Rational(50), Rational('5E1')) + assert_equal(Rational(500), Rational('5e2')) + assert_equal(Rational(5000), Rational('5e3')) + assert_equal(Rational(500000000000), Rational('5e1_1')) + + assert_equal(Rational(5.0), Rational('5.0')) + assert_equal(Rational(-5.0), Rational('-5.0')) + assert_equal(Rational(5.0,3), Rational('5.0/3')) + assert_equal(Rational(-5.0,3), Rational('-5.0/3')) + assert_equal(Rational(5.0,-3), Rational('5.0/-3')) + assert_equal(Rational(-5.0,-3), Rational('-5.0/-3')) + + assert_equal(Rational(0), '_'.to_r) + assert_equal(Rational(0), '_5'.to_r) + assert_equal(Rational(5), '5_'.to_r) + assert_equal(Rational(5), '5x'.to_r) + assert_equal(Rational(5), '5/_3'.to_r) + assert_equal(Rational(5,3), '5/3_'.to_r) + assert_equal(Rational(5,3), '5/3.3'.to_r) + assert_equal(Rational(5,3), '5/3x'.to_r) + assert_raise(ArgumentError){ Rational('')} + assert_raise(ArgumentError){ Rational('_')} + assert_raise(ArgumentError){ Rational('_5')} + assert_raise(ArgumentError){ Rational('5_')} + assert_raise(ArgumentError){ Rational('5x')} + assert_raise(ArgumentError){ Rational('5/_3')} + assert_raise(ArgumentError){ Rational('5/3_')} + assert_raise(ArgumentError){ Rational('5/3.3')} + assert_raise(ArgumentError){ Rational('5/3x')} + end + +=begin + def test_reciprocal + assert_equal(Rational(1,9), Rational(9,1).reciprocal) + assert_equal(Rational(9,1), Rational(1,9).reciprocal) + assert_equal(Rational(-1,9), Rational(-9,1).reciprocal) + assert_equal(Rational(-9,1), Rational(-1,9).reciprocal) + end +=end + + def test_to_i + assert_equal(1, Rational(3,2).to_i) + assert_equal(1, Integer(Rational(3,2))) + end + + def test_to_f + assert_equal(1.5, Rational(3,2).to_f) + assert_equal(1.5, Float(Rational(3,2))) + end + + def test_to_c + if defined?(Complex) && !Complex.instance_variable_get('@RCS_ID') + if defined?(Rational::Unify) + assert_equal(Rational(3,2), Rational(3,2).to_c) + assert_equal(Rational(3,2), Complex(Rational(3,2))) + else + assert_equal(Complex(Rational(3,2)), Rational(3,2).to_c) + assert_equal(Complex(Rational(3,2)), Complex(Rational(3,2))) + end + end + end + + def test_to_r + c = nil.to_r + assert_equal([0,1] , [c.numerator, c.denominator]) + + c = 0.to_r + assert_equal([0,1] , [c.numerator, c.denominator]) + + c = 1.to_r + assert_equal([1,1] , [c.numerator, c.denominator]) + + c = 1.1.to_r + assert_equal([2476979795053773, 2251799813685248], + [c.numerator, c.denominator]) + + c = Rational(1,2).to_r + assert_equal([1,2] , [c.numerator, c.denominator]) + + if defined?(Complex) + if Complex.instance_variable_get('@RCS_ID') + assert_raise(NoMethodError){Complex(1,2).to_r} + else + assert_raise(RangeError){Complex(1,2).to_r} + end + end + end + + def test_prec + assert_equal(true, Rational < Precision) + + c = Rational(3,2) + + assert_eql(1, c.prec(Integer)) + assert_eql(1.5, c.prec(Float)) + assert_eql(c, c.prec(Rational)) + end + + def test_supp + assert_equal(true, 1.scalar?) + assert_equal(true, 1.1.scalar?) + + if defined?(Complex) + assert_equal(1, 1.real) + assert_equal(0, 1.image) + assert_equal(0, 1.imag) + + assert_equal(1.1, 1.1.real) + assert_equal(0, 1.1.image) + assert_equal(0, 1.1.imag) + + assert_equal(0, 1.arg) + assert_equal(0, 1.angle) + + assert_equal(0, 1.0.arg) + assert_equal(0, 1.0.angle) + + assert_equal(Math::PI, -1.arg) + assert_equal(Math::PI, -1.angle) + + assert_equal(Math::PI, -1.0.arg) + assert_equal(Math::PI, -1.0.angle) + + assert_equal([1,0], 1.polar) + assert_equal([1, Math::PI], -1.polar) + + assert_equal([1.0,0], 1.0.polar) + assert_equal([1.0, Math::PI], -1.0.polar) + + assert_equal(1, 1.conjugate) + assert_equal(-1, -1.conjugate) + assert_equal(1, 1.conj) + assert_equal(-1, -1.conj) + + assert_equal(1.1, 1.1.conjugate) + assert_equal(-1.1, -1.1.conjugate) + assert_equal(1.1, 1.1.conj) + assert_equal(-1.1, -1.1.conj) + end + + assert_equal(1, 1.numerator) + assert_equal(9, 9.numerator) + assert_equal(1, 1.denominator) + assert_equal(1, 9.denominator) + + assert_equal(1.0, 1.0.numerator) + assert_equal(9.0, 9.0.numerator) + assert_equal(1.0, 1.0.denominator) + assert_equal(1.0, 9.0.denominator) + +=begin + assert_equal(Rational(1,9), 9.reciprocal) + assert_equal(Rational(1,9), 9.0.reciprocal) + assert_equal(Rational(1,9), 9.inverse) + assert_equal(Rational(1,9), 9.0.inverse) +=end + + assert_equal(Rational(1,2), 1.quo(2)) + assert_equal(Rational(5000000000), 10000000000.quo(2)) + assert_equal(0.5, 1.0.quo(2)) + assert_equal(Rational(1,4), Rational(1,2).quo(2)) + + assert_equal(Rational(1,2), 1.rdiv(2)) + assert_equal(Rational(5000000000), 10000000000.rdiv(2)) + assert_equal(Rational(1,2), 1.0.rdiv(2)) + assert_equal(Rational(1,4), Rational(1,2).rdiv(2)) + + assert_equal(0.5, 1.fdiv(2)) + assert_equal(5000000000.0, 10000000000.fdiv(2)) + assert_equal(0.5, 1.0.fdiv(2)) + assert_equal(0.25, Rational(1,2).fdiv(2)) + end + + def test_fixed_bug + if defined?(Rational::Unify) + assert_instance_of(Fixnum, Rational(1,2) ** 0) # mathn's bug + end + end + +=begin + def test_known_bug + n = Float::MAX.to_i * 2 + assert_equal(1.0, Rational(n + 2, n + 1).to_f, '[ruby-dev:33852]') + end +=end + +end |