/* * 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 nucomp_div(VALUE self, VALUE other) { if (k_complex_p(other)) { get_dat2(self, other); if (TYPE(adat->real) == T_FLOAT || TYPE(adat->imag) == T_FLOAT || TYPE(bdat->real) == T_FLOAT || TYPE(bdat->imag) == T_FLOAT) { VALUE magn = m_hypot(bdat->real, bdat->imag); VALUE tmp = f_complex_new_bang2(CLASS_OF(self), f_div(bdat->real, magn), f_div(bdat->imag, magn)); return f_div(f_mul(self, f_conj(tmp)), magn); } return f_div(f_mul(self, f_conj(other)), f_abs2(other)); } if (k_numeric_p(other) && f_real_p(other)) { get_dat1(self); return f_complex_new2(CLASS_OF(self), f_div(dat->real, other), f_div(dat->imag, other)); } return rb_num_coerce_bin(self, other, '/'); }
static VALUE nucomp_coerce(VALUE self, VALUE other) { if (k_numeric_p(other) && f_real_p(other)) 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; }
inline static void nucomp_real_check(VALUE num) { if (!RB_TYPE_P(num, T_FIXNUM) && !RB_TYPE_P(num, T_BIGNUM) && !RB_TYPE_P(num, T_FLOAT) && !RB_TYPE_P(num, T_RATIONAL)) { if (!k_numeric_p(num) || !f_real_p(num)) rb_raise(rb_eTypeError, "not a real"); } }
inline static VALUE f_divide(VALUE self, VALUE other, VALUE (*func)(VALUE, VALUE), ID id) { if (k_complex_p(other)) { int flo; get_dat2(self, other); flo = (k_float_p(adat->real) || k_float_p(adat->imag) || k_float_p(bdat->real) || k_float_p(bdat->imag)); if (f_gt_p(f_abs(bdat->real), f_abs(bdat->imag))) { VALUE r, n; r = (*func)(bdat->imag, bdat->real); n = f_mul(bdat->real, f_add(ONE, f_mul(r, r))); if (flo) return f_complex_new2(CLASS_OF(self), (*func)(self, n), (*func)(f_negate(f_mul(self, r)), n)); return f_complex_new2(CLASS_OF(self), (*func)(f_add(adat->real, f_mul(adat->imag, r)), n), (*func)(f_sub(adat->imag, f_mul(adat->real, r)), n)); } else { VALUE r, n; r = (*func)(bdat->real, bdat->imag); n = f_mul(bdat->imag, f_add(ONE, f_mul(r, r))); if (flo) return f_complex_new2(CLASS_OF(self), (*func)(f_mul(self, r), n), (*func)(f_negate(self), n)); return f_complex_new2(CLASS_OF(self), (*func)(f_add(f_mul(adat->real, r), adat->imag), n), (*func)(f_sub(f_mul(adat->imag, r), adat->real), n)); } } if (k_numeric_p(other) && f_real_p(other)) { get_dat1(self); return f_complex_new2(CLASS_OF(self), (*func)(dat->real, other), (*func)(dat->imag, other)); } return rb_num_coerce_bin(self, other, id); }
static VALUE m_sin(VALUE x) { if (f_real_p(x)) return m_sin_bang(x); { get_dat1(x); return f_complex_new2(rb_cComplex, f_mul(m_sin_bang(dat->real), m_cosh_bang(dat->imag)), f_mul(m_cos_bang(dat->real), m_sinh_bang(dat->imag))); } }
inline static void nucomp_real_check(VALUE num) { switch (TYPE(num)) { case T_FIXNUM: case T_BIGNUM: case T_FLOAT: case T_RATIONAL: break; default: if (!k_numeric_p(num) || !f_real_p(num)) rb_raise(rb_eTypeError, "not a real"); } }
inline static VALUE nucomp_s_canonicalize_internal(VALUE klass, VALUE real, VALUE imag) { #ifdef CANON #define CL_CANON #ifdef CL_CANON if (k_exact_zero_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: * 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 m_sqrt(VALUE x) { if (f_real_p(x)) { if (f_positive_p(x)) return m_sqrt_bang(x); return f_complex_new2(rb_cComplex, ZERO, m_sqrt_bang(f_negate(x))); } else { get_dat1(x); if (f_negative_p(dat->imag)) return f_conj(m_sqrt(f_conj(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 nucomp_sub(VALUE self, VALUE other) { if (k_complex_p(other)) { VALUE real, imag; get_dat2(self, other); real = f_sub(adat->real, bdat->real); imag = f_sub(adat->imag, bdat->imag); 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_sub(dat->real, other), dat->imag); } return rb_num_coerce_bin(self, other, '-'); }
inline static VALUE f_addsub(VALUE self, VALUE other, VALUE (*func)(VALUE, VALUE), ID id) { if (k_complex_p(other)) { VALUE real, imag; get_dat2(self, other); real = (*func)(adat->real, bdat->real); imag = (*func)(adat->imag, bdat->imag); 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), (*func)(dat->real, other), dat->imag); } return rb_num_coerce_bin(self, other, id); }
/* * call-seq: * cmp ** numeric -> complex * * Performs exponentiation. * * Complex('i') ** 2 #=> (-1+0i) * Complex(-8) ** Rational(1, 3) #=> (1.0000000000000002+1.7320508075688772i) */ static VALUE nucomp_expt(VALUE self, VALUE other) { if (k_numeric_p(other) && k_exact_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); /* c14n */ if (k_complex_p(other)) { get_dat1(other); if (k_exact_zero_p(dat->imag)) other = dat->real; /* c14n */ } if (k_complex_p(other)) { VALUE r, theta, nr, ntheta; get_dat1(other); r = f_abs(self); theta = f_arg(self); nr = m_exp_bang(f_sub(f_mul(dat->real, m_log_bang(r)), f_mul(dat->imag, theta))); ntheta = f_add(f_mul(theta, dat->real), f_mul(dat->imag, m_log_bang(r))); return f_complex_polar(CLASS_OF(self), nr, ntheta); } if (k_fixnum_p(other)) { if (f_gt_p(other, ZERO)) { VALUE x, z; long n; x = self; z = x; n = FIX2LONG(other) - 1; while (n) { long q, r; while (1) { get_dat1(x); q = n / 2; r = n % 2; if (r) break; x = nucomp_s_new_internal(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 = q; } z = f_mul(z, x); n--; } return z; } return f_expt(f_reciprocal(self), f_negate(other)); } if (k_numeric_p(other) && f_real_p(other)) { VALUE r, theta; if (k_bignum_p(other)) rb_warn("in a**b, b may be too big"); r = f_abs(self); theta = f_arg(self); 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 nucomp_s_convert(int argc, VALUE *argv, VALUE klass) { VALUE a1, a2, backref; rb_scan_args(argc, argv, "11", &a1, &a2); if (NIL_P(a1) || (argc == 2 && NIL_P(a2))) rb_raise(rb_eTypeError, "can't convert nil into Complex"); backref = rb_backref_get(); rb_match_busy(backref); if (RB_TYPE_P(a1, T_STRING)) { a1 = string_to_c_strict(a1); } if (RB_TYPE_P(a2, T_STRING)) { a2 = string_to_c_strict(a2); } rb_backref_set(backref); if (RB_TYPE_P(a1, T_COMPLEX)) { { get_dat1(a1); if (k_exact_zero_p(dat->imag)) a1 = dat->real; } } if (RB_TYPE_P(a2, T_COMPLEX)) { { get_dat1(a2); if (k_exact_zero_p(dat->imag)) a2 = dat->real; } } if (RB_TYPE_P(a1, T_COMPLEX)) { if (argc == 1 || (k_exact_zero_p(a2))) return a1; } if (argc == 1) { if (k_numeric_p(a1) && !f_real_p(a1)) return a1; /* should raise exception for consistency */ if (!k_numeric_p(a1)) return rb_convert_type(a1, T_COMPLEX, "Complex", "to_c"); } 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 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); } }