/* * call-seq: * rat / numeric -> numeric_result * rat.quo(numeric) -> numeric_result * * Performs division. * * For example: * * Rational(2, 3) / Rational(2, 3) #=> (1/1) * Rational(900) / Rational(1) #=> (900/1) * Rational(-2, 9) / Rational(-9, 2) #=> (4/81) * Rational(9, 8) / 4 #=> (9/32) * Rational(20, 9) / 9.8 #=> 0.22675736961451246 */ static VALUE nurat_div(VALUE self, SEL sel, VALUE other) { switch (TYPE(other)) { case T_FIXNUM: case T_BIGNUM: if (f_zero_p(other)) rb_raise_zerodiv(); { get_dat1(self); return f_muldiv(self, dat->num, dat->den, other, ONE, '/'); } case T_FLOAT: return rb_funcall(f_to_f(self), '/', 1, other); case T_RATIONAL: if (f_zero_p(other)) rb_raise_zerodiv(); { get_dat2(self, other); if (f_one_p(self)) return f_rational_new_no_reduce2(CLASS_OF(self), bdat->den, bdat->num); return f_muldiv(self, adat->num, adat->den, bdat->num, bdat->den, '/'); } default: return rb_num_coerce_bin(self, other, '/'); } }
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 (FIX2LONG(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 */ }
/* * call-seq: * cmp * numeric -> complex * * Performs multiplication. * * Complex(2, 3) * Complex(2, 3) #=> (-5+12i) * Complex(900) * Complex(1) #=> (900+0i) * Complex(-2, 9) * Complex(-9, 2) #=> (0-85i) * Complex(9, 8) * 4 #=> (36+32i) * Complex(20, 9) * 9.8 #=> (196.0+88.2i) */ VALUE rb_nucomp_mul(VALUE self, VALUE other) { if (k_complex_p(other)) { VALUE real, imag; VALUE areal, aimag, breal, bimag; int arzero, aizero, brzero, bizero; get_dat2(self, other); arzero = !!f_zero_p(areal = adat->real); aizero = !!f_zero_p(aimag = adat->imag); brzero = !!f_zero_p(breal = bdat->real); bizero = !!f_zero_p(bimag = bdat->imag); real = f_sub(safe_mul(areal, breal, arzero, brzero), safe_mul(aimag, bimag, aizero, bizero)); imag = f_add(safe_mul(areal, bimag, arzero, bizero), safe_mul(aimag, breal, aizero, brzero)); return f_complex_new2(CLASS_OF(self), real, imag); } if (k_numeric_p(other) && f_real_p(other)) { get_dat1(self); return f_complex_new2(CLASS_OF(self), f_mul(dat->real, other), f_mul(dat->imag, other)); } return rb_num_coerce_bin(self, other, '*'); }
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"); { get_dat1(self); return f_muldiv(self, dat->num, dat->den, other, ONE, '/'); } case T_FLOAT: return rb_funcall(f_to_f(self), '/', 1, other); case T_RATIONAL: if (f_zero_p(other)) rb_raise(rb_eZeroDivError, "devided by zero"); { get_dat2(self, other); return f_muldiv(self, adat->num, adat->den, bdat->num, bdat->den, '/'); } default: return rb_num_coerce_bin(self, other, '/'); } }
inline static VALUE f_lcm(VALUE x, VALUE y) { if (f_zero_p(x) || f_zero_p(y)) return ZERO; return f_abs(f_mul(f_div(x, f_gcd(x, y)), y)); }
inline static VALUE f_gcd(VALUE x, VALUE y) { VALUE r = f_gcd_orig(x, y); if (f_nonzero_p(r)) { assert(f_zero_p(f_mod(x, r))); assert(f_zero_p(f_mod(y, r))); } return r; }
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); }
/* * call-seq: * rat.fdiv(numeric) -> float * * Performs division and returns the value as a float. * * For example: * * Rational(2, 3).fdiv(1) #=> 0.6666666666666666 * Rational(2, 3).fdiv(0.5) #=> 1.3333333333333333 * Rational(2).fdiv(3) #=> 0.6666666666666666 */ static VALUE nurat_fdiv(VALUE self, SEL sel, VALUE other) { if (f_zero_p(other)) return f_div(self, f_to_f(other)); return f_to_f(f_div(self, other)); }
static VALUE nurat_to_f(VALUE self) { VALUE num, den; int minus = 0; long nl, dl, ml, ne, de; int e; double f; { get_dat1(self); if (f_zero_p(dat->num)) return rb_float_new(0.0); num = dat->num; den = dat->den; } if (f_negative_p(num)) { num = f_negate(num); minus = 1; } nl = i_ilog2(num); dl = i_ilog2(den); ml = (long)(log(DBL_MAX) / log(2.0) - 1); /* should be a static */ ne = 0; if (nl > ml) { ne = nl - ml; num = f_rshift(num, LONG2NUM(ne)); } de = 0; if (dl > ml) { de = dl - ml; den = f_rshift(den, LONG2NUM(de)); } e = (int)(ne - de); if ((e > DBL_MAX_EXP) || (e < DBL_MIN_EXP)) { rb_warning("%s out of Float range", rb_obj_classname(self)); return rb_float_new(e > 0 ? HUGE_VAL : 0.0); } f = NUM2DBL(num) / NUM2DBL(den); if (minus) f = -f; f = ldexp(f, e); if (isinf(f) || isnan(f)) rb_warning("%s out of Float range", rb_obj_classname(self)); return rb_float_new(f); }
/* * call-seq: * cmp.abs -> real * cmp.magnitude -> real * * Returns the absolute part of its polar form. * * Complex(-1).abs #=> 1 * Complex(3.0, -4.0).abs #=> 5.0 */ static VALUE nucomp_abs(VALUE self) { get_dat1(self); if (f_zero_p(dat->real)) { VALUE a = f_abs(dat->imag); if (k_float_p(dat->real) && !k_float_p(dat->imag)) a = f_to_f(a); return a; } if (f_zero_p(dat->imag)) { VALUE a = f_abs(dat->real); if (!k_float_p(dat->real) && k_float_p(dat->imag)) a = f_to_f(a); return a; } return m_hypot(dat->real, dat->imag); }
static VALUE f_complex_polar(VALUE klass, VALUE x, VALUE y) { assert(!k_complex_p(x)); assert(!k_complex_p(y)); if (f_zero_p(x) || f_zero_p(y)) { if (canonicalization) return x; return nucomp_s_new_internal(klass, x, RFLOAT_0); } if (RB_FLOAT_TYPE_P(y)) { const double arg = RFLOAT_VALUE(y); if (arg == M_PI) { x = f_negate(x); if (canonicalization) return x; y = RFLOAT_0; } else if (arg == M_PI_2) { y = x; x = RFLOAT_0; } else if (arg == M_PI_2+M_PI) { y = f_negate(x); x = RFLOAT_0; } else if (RB_FLOAT_TYPE_P(x)) { const double abs = RFLOAT_VALUE(x); const double real = abs * cos(arg), imag = abs * sin(arg); x = DBL2NUM(real); if (canonicalization && imag == 0.0) return x; y = DBL2NUM(imag); } else { x = f_mul(x, DBL2NUM(cos(arg))); y = f_mul(y, DBL2NUM(sin(arg))); if (canonicalization && f_zero_p(y)) return x; } return nucomp_s_new_internal(klass, x, y); } return nucomp_s_canonicalize_internal(klass, f_mul(x, m_cos(y)), f_mul(x, m_sin(y))); }
static VALUE nurat_marshal_load(VALUE self, VALUE a) { get_dat1(self); dat->num = RARRAY_AT(a, 0); dat->den = RARRAY_AT(a, 1); if (f_zero_p(dat->den)) rb_raise(rb_eZeroDivError, "devided by zero"); return self; }
/* :nodoc: */ static VALUE nurat_marshal_load(VALUE self, SEL sel, VALUE a) { get_dat1(self); dat->num = RARRAY_PTR(a)[0]; dat->den = RARRAY_PTR(a)[1]; rb_copy_generic_ivar(self, a); if (f_zero_p(dat->den)) rb_raise_zerodiv(); return self; }
inline static VALUE nucomp_s_canonicalize_internal(VALUE klass, VALUE real, VALUE imag) { #ifdef CANON #define CL_CANON #ifdef CL_CANON if (f_zero_p(imag) && k_exact_p(imag) && canonicalization) return real; #else if (f_zero_p(imag) && canonicalization) return real; #endif #endif if (f_real_p(real) && f_real_p(imag)) return nucomp_s_new_internal(klass, real, imag); else if (f_real_p(real)) { get_dat1(imag); return nucomp_s_new_internal(klass, f_sub(real, dat->imag), f_add(ZERO, dat->real)); } else if (f_real_p(imag)) { get_dat1(real); return nucomp_s_new_internal(klass, dat->real, f_add(dat->imag, imag)); } else { get_dat2(real, imag); return nucomp_s_new_internal(klass, f_sub(adat->real, bdat->imag), f_add(adat->imag, bdat->real)); } }
/* * call-seq: * rat == object -> true or false * * Returns true if rat equals object numerically. * * For example: * * Rational(2, 3) == Rational(2, 3) #=> true * Rational(5) == 5 #=> true * Rational(0) == 0.0 #=> true * Rational('1/3') == 0.33 #=> false * Rational('1/2') == '1/2' #=> false */ static VALUE nurat_eqeq_p(VALUE self, SEL sel, VALUE other) { switch (TYPE(other)) { case T_FIXNUM: case T_BIGNUM: { get_dat1(self); if (f_zero_p(dat->num) && f_zero_p(other)) return Qtrue; if (!FIXNUM_P(dat->den)) return Qfalse; if (FIX2LONG(dat->den) != 1) return Qfalse; if (f_eqeq_p(dat->num, other)) return Qtrue; return Qfalse; } case T_FLOAT: return f_eqeq_p(f_to_f(self), other); case T_RATIONAL: { get_dat2(self, other); if (f_zero_p(adat->num) && f_zero_p(bdat->num)) return Qtrue; return f_boolcast(f_eqeq_p(adat->num, bdat->num) && f_eqeq_p(adat->den, bdat->den)); } default: return f_eqeq_p(other, self); } }
/* * call-seq: * cmp == object -> true or false * * Returns true if cmp equals object numerically. * * Complex(2, 3) == Complex(2, 3) #=> true * Complex(5) == 5 #=> true * Complex(0) == 0.0 #=> true * Complex('1/3') == 0.33 #=> false * Complex('1/2') == '1/2' #=> false */ static VALUE nucomp_eqeq_p(VALUE self, VALUE other) { if (k_complex_p(other)) { get_dat2(self, other); return f_boolcast(f_eqeq_p(adat->real, bdat->real) && f_eqeq_p(adat->imag, bdat->imag)); } if (k_numeric_p(other) && f_real_p(other)) { get_dat1(self); return f_boolcast(f_eqeq_p(dat->real, other) && f_zero_p(dat->imag)); } return f_eqeq_p(other, self); }
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; } return f_rational_new2(CLASS_OF(self), num, den); } case T_FLOAT: case T_RATIONAL: return f_expt(f_to_f(self), other); default: return rb_num_coerce_bin(self, other, id_expt); } }
static VALUE nucomp_s_convert(int argc, VALUE *argv, VALUE klass) { VALUE a1, a2, backref; rb_scan_args(argc, argv, "11", &a1, &a2); backref = rb_backref_get(); rb_match_busy(backref); 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; } rb_backref_set(backref); switch (TYPE(a1)) { case T_COMPLEX: { get_dat1(a1); if (k_exact_p(dat->imag) && f_zero_p(dat->imag)) a1 = dat->real; } } switch (TYPE(a2)) { case T_COMPLEX: { get_dat1(a2); if (k_exact_p(dat->imag) && f_zero_p(dat->imag)) a2 = dat->real; } } switch (TYPE(a1)) { case T_COMPLEX: if (argc == 1 || (k_exact_p(a2) && f_zero_p(a2))) return a1; } if (argc == 1) { if (k_numeric_p(a1) && !f_real_p(a1)) return a1; } else { if ((k_numeric_p(a1) && k_numeric_p(a2)) && (!f_real_p(a1) || !f_real_p(a2))) return f_add(a1, f_mul(a2, f_complex_new_bang2(rb_cComplex, ZERO, ONE))); } { VALUE argv2[2]; argv2[0] = a1; argv2[1] = a2; return nucomp_s_new(argc, argv2, klass); } }
static VALUE nucomp_expt(VALUE self, VALUE other) { if (k_exact_p(other) && 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? */ if (k_complex_p(other)) { 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->imag; 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 f_complex_polar(CLASS_OF(self), nr, ntheta); } if (k_integer_p(other)) { if (f_gt_p(other, ZERO)) { VALUE x, z, n; x = self; z = x; n = f_sub(other, ONE); while (f_nonzero_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->imag, dat->imag)), f_mul(f_mul(TWO, dat->real), dat->imag)); n = RARRAY_PTR(a)[0]; } z = f_mul(z, x); n = f_sub(n, ONE); } return z; } return f_expt(f_div(f_to_r(ONE), self), f_negate(other)); } if (k_numeric_p(other) && f_real_p(other)) { VALUE a, r, theta; a = f_polar(self); r = RARRAY_PTR(a)[0]; theta = RARRAY_PTR(a)[1]; return f_complex_polar(CLASS_OF(self), f_expt(r, other), f_mul(theta, other)); } return rb_num_coerce_bin(self, other, id_expt); }
static VALUE nurat_s_convert(int argc, VALUE *argv, VALUE klass) { VALUE a1, a2, backref; rb_scan_args(argc, argv, "11", &a1, &a2); switch (TYPE(a1)) { case T_COMPLEX: if (k_exact_p(RCOMPLEX(a1)->imag) && f_zero_p(RCOMPLEX(a1)->imag)) a1 = RCOMPLEX(a1)->real; } switch (TYPE(a2)) { case T_COMPLEX: if (k_exact_p(RCOMPLEX(a2)->imag) && f_zero_p(RCOMPLEX(a2)->imag)) a2 = RCOMPLEX(a2)->real; } backref = rb_backref_get(); rb_match_busy(backref); 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; } rb_backref_set(backref); switch (TYPE(a1)) { case T_RATIONAL: if (argc == 1 || (k_exact_p(a2) && f_one_p(a2))) return a1; } if (argc == 1) { if (k_numeric_p(a1) && !f_integer_p(a1)) return a1; } else { if ((k_numeric_p(a1) && k_numeric_p(a2)) && (!f_integer_p(a1) || !f_integer_p(a2))) 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_convert(int argc, VALUE *argv, VALUE klass) { VALUE a1, a2; if (rb_scan_args(argc, argv, "02", &a1, &a2) == 1) { a2 = ONE; } 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); } return nurat_s_new(klass, a1, a2); }