class Complex - Documentation for Ruby 3.5 (original) (raw)

A Complex object houses a pair of values, given when the object is created as either rectangular coordinates or polar coordinates.

Rectangular Coordinates

The rectangular coordinates of a complex number are called the real and imaginary parts; see Complex number definition.

You can create a Complex object from rectangular coordinates with:

Note that each of the stored parts may be a an instance one of the classes Complex, Float, Integer, or Rational; they may be retrieved:

The corresponding (computed) polar values may be retrieved:

Polar Coordinates

The polar coordinates of a complex number are called the absolute and argument parts; see Complex polar plane.

In this class, the argument part in expressed radians (not degrees).

You can create a Complex object from polar coordinates with:

Note that each of the stored parts may be a an instance one of the classes Complex, Float, Integer, or Rational; they may be retrieved:

The corresponding (computed) rectangular values may be retrieved:

What’s Here

First, what’s elsewhere:

Here, class Complex has methods for:

Creating Complex Objects

Querying

Comparing

Converting

Performing Complex Arithmetic

Working with JSON

These methods are provided by the JSON gem. To make these methods available:

require 'json/add/complex'

Constants

I

Equivalent to Complex.rect(0, 1):

Complex::I

Public Class Methods

Source

def self.json_create(object) Complex(object['r'], object['i']) end

See as_json.

Source

static VALUE nucomp_s_polar(int argc, VALUE *argv, VALUE klass) { VALUE abs, arg;

argc = rb_scan_args(argc, argv, "11", &abs, &arg);
abs = nucomp_real_check(abs);
if (argc == 2) {
    arg = nucomp_real_check(arg);
}
else {
    arg = ZERO;
}
return f_complex_polar_real(klass, abs, arg);

}

Returns a new Complex object formed from the arguments, each of which must be an instance of Numeric, or an instance of one of its subclasses: Complex, Float, Integer, Rational. Argument arg is given in radians; see Polar Coordinates:

Complex.polar(3)
Complex.polar(3, 2.0)
Complex.polar(-3, -2.0)

Source

static VALUE nucomp_s_new(int argc, VALUE *argv, VALUE klass) { VALUE real, imag;

switch (rb_scan_args(argc, argv, "11", &real, &imag)) {
  case 1:
    real = nucomp_real_check(real);
    imag = ZERO;
    break;
  default:
    real = nucomp_real_check(real);
    imag = nucomp_real_check(imag);
    break;
}

return nucomp_s_new_internal(klass, real, imag);

}

Returns a new Complex object formed from the arguments, each of which must be an instance of Numeric, or an instance of one of its subclasses: Complex, Float, Integer, Rational; see Rectangular Coordinates:

Complex.rect(3)
Complex.rect(3, Math::PI)
Complex.rect(-3, -Math::PI)

Complex.rectangular is an alias for Complex.rect.

Source

static VALUE nucomp_s_new(int argc, VALUE *argv, VALUE klass) { VALUE real, imag;

switch (rb_scan_args(argc, argv, "11", &real, &imag)) {
  case 1:
    real = nucomp_real_check(real);
    imag = ZERO;
    break;
  default:
    real = nucomp_real_check(real);
    imag = nucomp_real_check(imag);
    break;
}

return nucomp_s_new_internal(klass, real, imag);

}

Returns a new Complex object formed from the arguments, each of which must be an instance of Numeric, or an instance of one of its subclasses: Complex, Float, Integer, Rational; see Rectangular Coordinates:

Complex.rect(3)
Complex.rect(3, Math::PI)
Complex.rect(-3, -Math::PI)

Complex.rectangular is an alias for Complex.rect.

Public Instance Methods

Source

VALUE rb_complex_mul(VALUE self, VALUE other) { if (RB_TYPE_P(other, T_COMPLEX)) { VALUE real, imag; get_dat2(self, other);

    comp_mul(adat->real, adat->imag, bdat->real, bdat->imag, &real, &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_mul(dat->real, other),
                          f_mul(dat->imag, other));
}
return rb_num_coerce_bin(self, other, '*');

}

Returns the product of self and numeric:

Complex.rect(2, 3) * Complex.rect(2, 3)
Complex.rect(900) * Complex.rect(1)
Complex.rect(-2, 9) * Complex.rect(-9, 2) Complex.rect(9, 8) * 4
Complex.rect(20, 9) * 9.8

Source

VALUE rb_complex_pow(VALUE self, VALUE other) { if (k_numeric_p(other) && k_exact_zero_p(other)) return f_complex_new_bang1(CLASS_OF(self), ONE);

if (RB_TYPE_P(other, T_RATIONAL) && RRATIONAL(other)->den == LONG2FIX(1))
    other = RRATIONAL(other)->num; /* c14n */

if (RB_TYPE_P(other, T_COMPLEX)) {
    get_dat1(other);

    if (k_exact_zero_p(dat->imag))
        other = dat->real; /* c14n */
}

if (other == ONE) {
    get_dat1(self);
    return nucomp_s_new_internal(CLASS_OF(self), dat->real, dat->imag);
}

VALUE result = complex_pow_for_special_angle(self, other);
if (!UNDEF_P(result)) return result;

if (RB_TYPE_P(other, T_COMPLEX)) {
    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 (FIXNUM_P(other)) {
    long n = FIX2LONG(other);
    if (n == 0) {
        return nucomp_s_new_internal(CLASS_OF(self), ONE, ZERO);
    }
    if (n < 0) {
        self = f_reciprocal(self);
        other = rb_int_uminus(other);
        n = -n;
    }
    {
        get_dat1(self);
        VALUE xr = dat->real, xi = dat->imag, zr = xr, zi = xi;

        if (f_zero_p(xi)) {
            zr = rb_num_pow(zr, other);
        }
        else if (f_zero_p(xr)) {
            zi = rb_num_pow(zi, other);
            if (n & 2) zi = f_negate(zi);
            if (!(n & 1)) {
                VALUE tmp = zr;
                zr = zi;
                zi = tmp;
            }
        }
        else {
            while (--n) {
                long q, r;

                for (; q = n / 2, r = n % 2, r == 0; n = q) {
                    VALUE tmp = f_sub(f_mul(xr, xr), f_mul(xi, xi));
                    xi = f_mul(f_mul(TWO, xr), xi);
                    xr = tmp;
                }
                comp_mul(zr, zi, xr, xi, &zr, &zi);
            }
        }
        return nucomp_s_new_internal(CLASS_OF(self), zr, zi);
    }
}
if (k_numeric_p(other) && f_real_p(other)) {
    VALUE r, theta;

    if (RB_BIGNUM_TYPE_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);

}

Returns self raised to power numeric:

Complex.rect(0, 1) ** 2
Complex.rect(-8) ** Rational(1, 3)

Source

VALUE rb_complex_plus(VALUE self, VALUE other) { if (RB_TYPE_P(other, T_COMPLEX)) { VALUE real, imag;

    get_dat2(self, other);

    real = f_add(adat->real, bdat->real);
    imag = f_add(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_add(dat->real, other), dat->imag);
}
return rb_num_coerce_bin(self, other, '+');

}

Returns the sum of self and numeric:

Complex.rect(2, 3) + Complex.rect(2, 3)
Complex.rect(900) + Complex.rect(1)
Complex.rect(-2, 9) + Complex.rect(-9, 2) Complex.rect(9, 8) + 4
Complex.rect(20, 9) + 9.8

Source

VALUE rb_complex_minus(VALUE self, VALUE other) { if (RB_TYPE_P(other, T_COMPLEX)) { 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, '-');

}

Returns the difference of self and numeric:

Complex.rect(2, 3) - Complex.rect(2, 3)
Complex.rect(900) - Complex.rect(1)
Complex.rect(-2, 9) - Complex.rect(-9, 2) Complex.rect(9, 8) - 4
Complex.rect(20, 9) - 9.8

Source

VALUE rb_complex_uminus(VALUE self) { get_dat1(self); return f_complex_new2(CLASS_OF(self), f_negate(dat->real), f_negate(dat->imag)); }

Returns the negation of self, which is the negation of each of its parts:

-Complex.rect(1, 2)
-Complex.rect(-1, -2)

Source

VALUE rb_complex_div(VALUE self, VALUE other) { return f_divide(self, other, f_quo, id_quo); }

Returns the quotient of self and numeric:

Complex.rect(2, 3) / Complex.rect(2, 3)
Complex.rect(900) / Complex.rect(1)
Complex.rect(-2, 9) / Complex.rect(-9, 2) Complex.rect(9, 8) / 4
Complex.rect(20, 9) / 9.8

Source

static VALUE nucomp_cmp(VALUE self, VALUE other) { if (!k_numeric_p(other)) { return rb_num_coerce_cmp(self, other, idCmp); } if (!nucomp_real_p(self)) { return Qnil; } if (RB_TYPE_P(other, T_COMPLEX)) { if (nucomp_real_p(other)) { get_dat2(self, other); return rb_funcall(adat->real, idCmp, 1, bdat->real); } } else { get_dat1(self); if (f_real_p(other)) { return rb_funcall(dat->real, idCmp, 1, other); } else { return rb_num_coerce_cmp(dat->real, other, idCmp); } } return Qnil; }

Returns:

Examples:

Complex.rect(2) <=> 3
Complex.rect(2) <=> 2
Complex.rect(2) <=> 1
Complex.rect(2, 1) <=> 1
Complex.rect(1) <=> Complex.rect(1, 1) Complex.rect(1) <=> 'Foo'

Source

static VALUE nucomp_eqeq_p(VALUE self, VALUE other) { if (RB_TYPE_P(other, T_COMPLEX)) { get_dat2(self, other);

    return RBOOL(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 RBOOL(f_eqeq_p(dat->real, other) && f_zero_p(dat->imag));
}
return RBOOL(f_eqeq_p(other, self));

}

Returns true if self.real == object.real and self.imag == object.imag:

Complex.rect(2, 3) == Complex.rect(2.0, 3.0)

Source

VALUE rb_complex_abs(VALUE self) { get_dat1(self);

if (f_zero_p(dat->real)) {
    VALUE a = f_abs(dat->imag);
    if (RB_FLOAT_TYPE_P(dat->real) && !RB_FLOAT_TYPE_P(dat->imag))
        a = f_to_f(a);
    return a;
}
if (f_zero_p(dat->imag)) {
    VALUE a = f_abs(dat->real);
    if (!RB_FLOAT_TYPE_P(dat->real) && RB_FLOAT_TYPE_P(dat->imag))
        a = f_to_f(a);
    return a;
}
return rb_math_hypot(dat->real, dat->imag);

}

Returns the absolute value (magnitude) for self; see polar coordinates:

Complex.polar(-1, 0).abs

If self was created with rectangular coordinates, the returned value is computed, and may be inexact:

Complex.rectangular(1, 1).abs

Source

static VALUE nucomp_abs2(VALUE self) { get_dat1(self); return f_add(f_mul(dat->real, dat->real), f_mul(dat->imag, dat->imag)); }

Returns square of the absolute value (magnitude) for self; see polar coordinates:

Complex.polar(2, 2).abs2

If self was created with rectangular coordinates, the returned value is computed, and may be inexact:

Complex.rectangular(1.0/3, 1.0/3).abs2

Source

VALUE rb_complex_arg(VALUE self) { get_dat1(self); return rb_math_atan2(dat->imag, dat->real); }

Returns the argument (angle) for self in radians; see polar coordinates:

Complex.polar(3, Math::PI/2).arg

If self was created with rectangular coordinates, the returned value is computed, and may be inexact:

Complex.polar(1, 1.0/3).arg

Source

def as_json(*) { JSON.create_id => self.class.name, 'r' => real, 'i' => imag, } end

Methods Complex#as_json and Complex.json_create may be used to serialize and deserialize a Complex object; see Marshal.

Method Complex#as_json serializes self, returning a 2-element hash representing self:

require 'json/add/complex' x = Complex(2).as_json
y = Complex(2.0, 4).as_json

Method JSON.create deserializes such a hash, returning a Complex object:

Complex.json_create(x) Complex.json_create(y)

Source

Also aliased as: conj

Source

static VALUE nucomp_denominator(VALUE self) { get_dat1(self); return rb_lcm(f_denominator(dat->real), f_denominator(dat->imag)); }

Returns the denominator of self, which is the least common multiple of self.real.denominator and self.imag.denominator:

Complex.rect(Rational(1, 2), Rational(2, 3)).denominator

Note that n.denominator of a non-rational numeric is 1.

Related: Complex#numerator.

Source

static VALUE rb_complex_finite_p(VALUE self) { get_dat1(self);

return RBOOL(f_finite_p(dat->real) && f_finite_p(dat->imag));

}

Returns true if both self.real.finite? and self.imag.finite? are true, false otherwise:

Complex.rect(1, 1).finite?
Complex.rect(Float::INFINITY, 0).finite?

Related: Numeric#finite?, Float#finite?.

Source

static VALUE nucomp_hash(VALUE self) { return ST2FIX(rb_complex_hash(self)); }

Returns the integer hash value for self.

Two Complex objects created from the same values will have the same hash value (and will compare using eql?):

Complex.rect(1, 2).hash == Complex.rect(1, 2).hash

Returns the imaginary value for self:

Complex.rect(7).imag
Complex.rect(9, -4).imag

If self was created with polar coordinates, the returned value is computed, and may be inexact:

Complex.polar(1, Math::PI/4).imag

Source

Also aliased as: imag

Source

static VALUE rb_complex_infinite_p(VALUE self) { get_dat1(self);

if (!f_infinite_p(dat->real) && !f_infinite_p(dat->imag)) {
    return Qnil;
}
return ONE;

}

Returns 1 if either self.real.infinite? or self.imag.infinite? is true, nil otherwise:

Complex.rect(Float::INFINITY, 0).infinite? Complex.rect(1, 1).infinite?

Related: Numeric#infinite?, Float#infinite?.

Source

static VALUE nucomp_inspect(VALUE self) { VALUE s;

s = rb_usascii_str_new2("(");
f_format(self, s, rb_inspect);
rb_str_cat2(s, ")");

return s;

}

Returns a string representation of self:

Complex.rect(2).inspect
Complex.rect(-8, 6).inspect
Complex.rect(0, Rational(1, 2)).inspect
Complex.rect(0, Float::INFINITY).inspect
Complex.rect(Float::NAN, Float::NAN).inspect

Source

static VALUE nucomp_numerator(VALUE self) { VALUE cd;

get_dat1(self);

cd = nucomp_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->imag),
                            f_div(cd, f_denominator(dat->imag))));

}

Returns the Complex object created from the numerators of the real and imaginary parts of self, after converting each part to the lowest common denominator of the two:

c = Complex.rect(Rational(2, 3), Rational(3, 4)) c.numerator

In this example, the lowest common denominator of the two parts is 12; the two converted parts may be thought of as Rational(8, 12) and Rational(9, 12), whose numerators, respectively, are 8 and 9; so the returned value of c.numerator is Complex.rect(8, 9).

Related: Complex#denominator.

Source

static VALUE nucomp_polar(VALUE self) { return rb_assoc_new(f_abs(self), f_arg(self)); }

Returns the array [self.abs, self.arg]:

Complex.polar(1, 2).polar

See Polar Coordinates.

If self was created with rectangular coordinates, the returned value is computed, and may be inexact:

Complex.rect(1, 1).polar

Source

VALUE rb_complex_div(VALUE self, VALUE other) { return f_divide(self, other, f_quo, id_quo); }

Returns the quotient of self and numeric:

Complex.rect(2, 3) / Complex.rect(2, 3)
Complex.rect(900) / Complex.rect(1)
Complex.rect(-2, 9) / Complex.rect(-9, 2) Complex.rect(9, 8) / 4
Complex.rect(20, 9) / 9.8

Source

static VALUE nucomp_rationalize(int argc, VALUE *argv, VALUE self) { get_dat1(self);

rb_check_arity(argc, 0, 1);

if (!k_exact_zero_p(dat->imag)) {
   rb_raise(rb_eRangeError, "can't convert %"PRIsVALUE" into Rational",
            self);
}
return rb_funcallv(dat->real, id_rationalize, argc, argv);

}

Returns a Rational object whose value is exactly or approximately equivalent to that of self.real.

With no argument epsilon given, returns a Rational object whose value is exactly equal to that of self.real.rationalize:

Complex.rect(1, 0).rationalize
Complex.rect(1, Rational(0, 1)).rationalize Complex.rect(3.14159, 0).rationalize

With argument epsilon given, returns a Rational object whose value is exactly or approximately equal to that of self.real to the given precision:

Complex.rect(3.14159, 0).rationalize(0.1)
Complex.rect(3.14159, 0).rationalize(0.01)
Complex.rect(3.14159, 0).rationalize(0.001)
Complex.rect(3.14159, 0).rationalize(0.0001)
Complex.rect(3.14159, 0).rationalize(0.00001)
Complex.rect(3.14159, 0).rationalize(0.000001)
Complex.rect(3.14159, 0).rationalize(0.0000001)
Complex.rect(3.14159, 0).rationalize(0.00000001)
Complex.rect(3.14159, 0).rationalize(0.000000001)
Complex.rect(3.14159, 0).rationalize(0.0000000001) Complex.rect(3.14159, 0).rationalize(0.0)

Related: Complex#to_r.

Source

VALUE rb_complex_real(VALUE self) { get_dat1(self); return dat->real; }

Returns the real value for self:

Complex.rect(7).real
Complex.rect(9, -4).real

If self was created with polar coordinates, the returned value is computed, and may be inexact:

Complex.polar(1, Math::PI/4).real

Source

static VALUE nucomp_real_p_m(VALUE self) { return Qfalse; }

Returns false; for compatibility with Numeric#real?.

Returns a new Complex object formed from the arguments, each of which must be an instance of Numeric, or an instance of one of its subclasses: Complex, Float, Integer, Rational; see Rectangular Coordinates:

Complex.rect(3)
Complex.rect(3, Math::PI)
Complex.rect(-3, -Math::PI)

Complex.rectangular is an alias for Complex.rect.

Source

Also aliased as: rect, rect

Source

static VALUE nucomp_to_c(VALUE self) { return self; }

Returns self.

Source

static VALUE nucomp_to_f(VALUE self) { get_dat1(self);

if (!k_exact_zero_p(dat->imag)) {
    rb_raise(rb_eRangeError, "can't convert %"PRIsVALUE" into Float",
             self);
}
return f_to_f(dat->real);

}

Returns the value of self.real as a Float, if possible:

Complex.rect(1, 0).to_f
Complex.rect(1, Rational(0, 1)).to_f

Raises RangeError if self.imag is not exactly zero (either Integer(0) or Rational(0, _n_)).

Source

static VALUE nucomp_to_i(VALUE self) { get_dat1(self);

if (!k_exact_zero_p(dat->imag)) {
    rb_raise(rb_eRangeError, "can't convert %"PRIsVALUE" into Integer",
             self);
}
return f_to_i(dat->real);

}

Returns the value of self.real as an Integer, if possible:

Complex.rect(1, 0).to_i
Complex.rect(1, Rational(0, 1)).to_i

Raises RangeError if self.imag is not exactly zero (either Integer(0) or Rational(0, _n_)).

Source

def to_json(*args) as_json.to_json(*args) end

Returns a JSON string representing self:

require 'json/add/complex' puts Complex(2).to_json puts Complex(2.0, 4).to_json

Output:

{"json_class":"Complex","r":2,"i":0} {"json_class":"Complex","r":2.0,"i":4}

Source

static VALUE nucomp_to_r(VALUE self) { get_dat1(self);

if (RB_FLOAT_TYPE_P(dat->imag) && FLOAT_ZERO_P(dat->imag)) {
    /* Do nothing here */
}
else if (!k_exact_zero_p(dat->imag)) {
    VALUE imag = rb_check_convert_type_with_id(dat->imag, T_RATIONAL, "Rational", idTo_r);
    if (NIL_P(imag) || !k_exact_zero_p(imag)) {
        rb_raise(rb_eRangeError, "can't convert %"PRIsVALUE" into Rational",
                 self);
    }
}
return f_to_r(dat->real);

}

Returns the value of self.real as a Rational, if possible:

Complex.rect(1, 0).to_r
Complex.rect(1, Rational(0, 1)).to_r Complex.rect(1, 0.0).to_r

Raises RangeError if self.imag is not exactly zero (either Integer(0) or Rational(0, _n_)) and self.imag.to_r is not exactly zero.

Related: Complex#rationalize.

Source

static VALUE nucomp_to_s(VALUE self) { return f_format(self, rb_usascii_str_new2(""), rb_String); }

Returns a string representation of self:

Complex.rect(2).to_s
Complex.rect(-8, 6).to_s
Complex.rect(0, Rational(1, 2)).to_s
Complex.rect(0, Float::INFINITY).to_s
Complex.rect(Float::NAN, Float::NAN).to_s