summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog40
-rw-r--r--bignum.c21
-rw-r--r--common.mk8
-rw-r--r--complex.c1533
-rw-r--r--configure.in2
-rw-r--r--gc.c15
-rw-r--r--include/ruby/intern.h20
-rw-r--r--include/ruby/ruby.h21
-rw-r--r--inits.c4
-rw-r--r--lib/complex.rb620
-rw-r--r--lib/mathn.rb9
-rw-r--r--lib/rational.rb528
-rw-r--r--numeric.c69
-rw-r--r--rational.c1111
-rw-r--r--test/ruby/test_complex.rb1017
-rw-r--r--test/ruby/test_rational.rb971
16 files changed, 4938 insertions, 1051 deletions
diff --git a/ChangeLog b/ChangeLog
index 3b419bc00f..f67a8c269e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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().
diff --git a/bignum.c b/bignum.c
index 1f38816f6d..d06bf6a6c0 100644
--- a/bignum.c
+++ b/bignum.c
@@ -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);
diff --git a/common.mk b/common.mk
index cf0055dd0e..86cef49ad3 100644
--- a/common.mk
+++ b/common.mk
@@ -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,
diff --git a/gc.c b/gc.c
index 22f2327416..f54048718f 100644
--- a/gc.c
+++ b/gc.c
@@ -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;
diff --git a/inits.c b/inits.c
index bfb6ee9b84..d031fcf08f 100644
--- a/inits.c
+++ b/inits.c
@@ -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
diff --git a/numeric.c b/numeric.c
index b5aa2d2371..fc2310b94d 100644
--- a/numeric.c
+++ b/numeric.c
@@ -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