Avatar of remcopeereboom

remcopeereboom's solution

to Complex Numbers in the Ruby Track

Published at Aug 30 2018 · 0 comments
Instructions
Test suite
Solution

Note:

This exercise has changed since this solution was written.

A complex number is a number in the form a + b * i where a and b are real and i satisfies i^2 = -1.

a is called the real part and b is called the imaginary part of z. The conjugate of the number a + b * i is the number a - b * i. The absolute value of a complex number z = a + b * i is a real number |z| = sqrt(a^2 + b^2). The square of the absolute value |z|^2 is the result of multiplication of z by its complex conjugate.

The sum/difference of two complex numbers involves adding/subtracting their real and imaginary parts separately: (a + i * b) + (c + i * d) = (a + c) + (b + d) * i, (a + i * b) - (c + i * d) = (a - c) + (b - d) * i.

Multiplication result is by definition (a + i * b) * (c + i * d) = (a * c - b * d) + (b * c + a * d) * i.

The reciprocal of a non-zero complex number is 1 / (a + i * b) = a/(a^2 + b^2) - b/(a^2 + b^2) * i.

Dividing a complex number a + i * b by another c + i * d gives: (a + i * b) / (c + i * d) = (a * c + b * d)/(c^2 + d^2) + (b * c - a * d)/(c^2 + d^2) * i.

Exponent of a complex number can be expressed as exp(a + i * b) = exp(a) * exp(i * b), and the last term is given by Euler's formula exp(i * b) = cos(b) + i * sin(b).

Implement the following operations:

  • addition, subtraction, multiplication and division of two complex numbers,
  • conjugate, absolute value, exponent of a given complex number.

Assume the programming language you are using does not have an implementation of complex numbers.


For installation and learning resources, refer to the exercism help page.

For running the tests provided, you will need the Minitest gem. Open a terminal window and run the following command to install minitest:

gem install minitest

If you would like color output, you can require 'minitest/pride' in the test file, or note the alternative instruction, below, for running the test file.

Run the tests from the exercise directory using the following command:

ruby complex_numbers_test.rb

To include color from the command line:

ruby -r minitest/pride complex_numbers_test.rb

Source

Wikipedia https://en.wikipedia.org/wiki/Complex_number

Submitting Incomplete Solutions

It's possible to submit an incomplete solution so you can see how others have completed the exercise.

complex_numbers_test.rb

require 'minitest/autorun'
require_relative 'complex_numbers'

# Common test data version: 1.0.0 '117d062'
class ComplexNumberTest < Minitest::Test
  def test_imaginary_unit
    # skip
    expected = ComplexNumber.new(-1, 0)
    assert_equal expected, ComplexNumber.new(0, 1) * ComplexNumber.new(0, 1)
  end

  def test_add_purely_real_numbers
    skip
    expected = ComplexNumber.new(3, 0)
    assert_equal expected, ComplexNumber.new(1, 0) + ComplexNumber.new(2, 0)
  end

  def test_add_purely_imaginary_numbers
    skip
    expected = ComplexNumber.new(0, 3)
    assert_equal expected, ComplexNumber.new(0, 1) + ComplexNumber.new(0, 2)
  end

  def test_add_numbers_with_real_and_imaginary_part
    skip
    expected = ComplexNumber.new(4, 6)
    assert_equal expected, ComplexNumber.new(1, 2) + ComplexNumber.new(3, 4)
  end

  def test_subtract_purely_real_numbers
    skip
    expected = ComplexNumber.new(-1, 0)
    assert_equal expected, ComplexNumber.new(1, 0) - ComplexNumber.new(2, 0)
  end

  def test_subtract_purely_imaginary_numbers
    skip
    expected = ComplexNumber.new(0, -1)
    assert_equal expected, ComplexNumber.new(0, 1) - ComplexNumber.new(0, 2)
  end

  def test_subtract_numbers_with_real_and_imaginary_part
    skip
    expected = ComplexNumber.new(-2, -2)
    assert_equal expected, ComplexNumber.new(1, 2) - ComplexNumber.new(3, 4)
  end

  def test_multiply_purely_real_numbers
    skip
    expected = ComplexNumber.new(2, 0)
    assert_equal expected, ComplexNumber.new(1, 0) * ComplexNumber.new(2, 0)
  end

  def test_multiply_purely_imaginary_numbers
    skip
    expected = ComplexNumber.new(-2, 0)
    assert_equal expected, ComplexNumber.new(0, 1) * ComplexNumber.new(0, 2)
  end

  def test_multiply_numbers_with_real_and_imaginary_part
    skip
    expected = ComplexNumber.new(-5, 10)
    assert_equal expected, ComplexNumber.new(1, 2) * ComplexNumber.new(3, 4)
  end

  def test_divide_purely_real_numbers
    skip
    expected = ComplexNumber.new(0.5, 0)
    assert_equal expected, ComplexNumber.new(1, 0) / ComplexNumber.new(2, 0)
  end

  def test_divide_purely_imaginary_numbers
    skip
    expected = ComplexNumber.new(0.5, 0)
    assert_equal expected, ComplexNumber.new(0, 1) / ComplexNumber.new(0, 2)
  end

  def test_divide_numbers_with_real_and_imaginary_part
    skip
    expected = ComplexNumber.new(0.44, 0.08)
    assert_equal expected, ComplexNumber.new(1, 2) / ComplexNumber.new(3, 4)
  end

  def test_absolute_value_of_a_positive_purely_real_number
    skip
    expected = 5
    assert_equal expected, ComplexNumber.new(5, 0).abs
  end

  def test_absolute_value_of_a_negative_purely_real_number
    skip
    expected = 5
    assert_equal expected, ComplexNumber.new(-5, 0).abs
  end

  def test_absolute_value_of_a_purely_imaginary_number_with_positive_imaginary_part
    skip
    expected = 5
    assert_equal expected, ComplexNumber.new(0, 5).abs
  end

  def test_absolute_value_of_a_purely_imaginary_number_with_negative_imaginary_part
    skip
    expected = 5
    assert_equal expected, ComplexNumber.new(0, -5).abs
  end

  def test_absolute_value_of_a_number_with_real_and_imaginary_part
    skip
    expected = 5
    assert_equal expected, ComplexNumber.new(3, 4).abs
  end

  def test_conjugate_a_purely_real_number
    skip
    expected = ComplexNumber.new(5, 0)
    assert_equal expected, ComplexNumber.new(5, 0).conjugate
  end

  def test_conjugate_a_purely_imaginary_number
    skip
    expected = ComplexNumber.new(0, -5)
    assert_equal expected, ComplexNumber.new(0, 5).conjugate
  end

  def test_conjugate_a_number_with_real_and_imaginary_part
    skip
    expected = ComplexNumber.new(1, -1)
    assert_equal expected, ComplexNumber.new(1, 1).conjugate
  end

  def test_real_part_of_a_purely_real_number
    skip
    expected = 1
    assert_equal expected, ComplexNumber.new(1, 0).real
  end

  def test_real_part_of_a_purely_imaginary_number
    skip
    expected = 0
    assert_equal expected, ComplexNumber.new(0, 1).real
  end

  def test_real_part_of_a_number_with_real_and_imaginary_part
    skip
    expected = 1
    assert_equal expected, ComplexNumber.new(1, 2).real
  end

  def test_imaginary_part_of_a_purely_real_number
    skip
    expected = 0
    assert_equal expected, ComplexNumber.new(1, 0).imaginary
  end

  def test_imaginary_part_of_a_purely_imaginary_number
    skip
    expected = 1
    assert_equal expected, ComplexNumber.new(0, 1).imaginary
  end

  def test_imaginary_part_of_a_number_with_real_and_imaginary_part
    skip
    expected = 2
    assert_equal expected, ComplexNumber.new(1, 2).imaginary
  end

  def test_eulers_identityformula
    skip
    expected = ComplexNumber.new(-1, 0)
    assert_equal expected, ComplexNumber.new(0, Math::PI).exp
  end

  def test_exponential_of_0
    skip
    expected = ComplexNumber.new(1, 0)
    assert_equal expected, ComplexNumber.new(0, 0).exp
  end

  def test_exponential_of_a_purely_real_number
    skip
    expected = ComplexNumber.new(Math::E, 0)
    assert_equal expected, ComplexNumber.new(1, 0).exp
  end

  # Problems in exercism evolve over time, as we find better ways to ask
  # questions.
  # The version number refers to the version of the problem you solved,
  # not your solution.
  #
  # Define a constant named VERSION inside of the top level BookKeeping
  # module, which may be placed near the end of your file.
  #
  # In your file, it will look like this:
  #
  # module BookKeeping
  #   VERSION = 1 # Where the version number matches the one in the test.
  # end
  #
  # If you are curious, read more about constants on RubyDoc:
  # http://ruby-doc.org/docs/ruby-doc-bundle/UsersGuide/rg/constants.html

  def test_bookkeeping
    skip
    assert_equal 1, BookKeeping::VERSION
  end
end
# ComplexNumber
#
# An immutable number type representing complex numbers.
class ComplexNumber
  attr_reader :real, :imaginary

  # Initializes a new complex number with the given real and imaginary parts.
  # @param real [Integer, Real]
  # @param imaginary [Integer, Real]
  def initialize(real, imaginary = 0)
    @real = real
    @imaginary = imaginary
  end

  # Returns a new ComplexNumber that is the sum of self and the other
  # complex number.
  # @param other [ComplexNumber]
  # @return [ComplexNumber]
  def +(other)
    self.class.new(@real + other.real, @imaginary + other.imaginary)
  end

  # Returns a new ComplexNumber that is the difference of self and the other
  # complex number.
  # @param other [ComplexNumber]
  # @return [ComplexNumber]
  def -(other)
    self.class.new(@real - other.real, @imaginary - other.imaginary)
  end

  # Returns a new ComplexNumber that is the product of self and the other
  # complex number.
  # @param other [ComplexNumber]
  # @return [ComplexNumber]
  def *(other)
    self.class.new(@real * other.real - @imaginary * other.imaginary,
                   @imaginary * other.real + @real * other.imaginary)
  end

  # Returns a new ComplexNumber that is the product of self and the other
  # complex number.
  # @param other [ComplexNumber]
  # @return [ComplexNumber]
  def /(other)
    q = (other.real**2 + other.imaginary**2).to_f
    r = @real * other.real + @imaginary * other.imaginary
    i = @imaginary * other.real - @real * other.imaginary

    self.class.new(r / q, i / q)
  end

  # Is this number equal to other?
  # @return [Boolean]
  def ==(other)
    return false unless other.is_a? ComplexNumber
    (self - other).abs < 1e-12
  end

  # Returns the absolute value.
  # @return [Float]
  def abs
    Math.sqrt(@real**2 + @imaginary**2)
  end

  # Returns the square of the absolute value.
  # @return [Float]
  def abs2
    @real**2 + @imaginary**2
  end

  # Returns the complex conjugate.
  # @return [ComplexNumber]
  def conjugate
    self.class.new(@real, -@imaginary)
  end

  # Returns the e**self.
  # @return [ComplexNumber]
  def exp
    self.class.new(Math.exp(@real)) *
      self.class.new(Math.cos(@imaginary), Math.sin(@imaginary))
  end

  # Is this a real number?
  # @return [Boolean]
  def real?
    @imaginary.zero?
  end

  # Converts self to an Integer.
  # @raise [RangeError] if it has a non-zero imaginary part.
  # @return [Integer]
  def to_i
    fail RangeError, "Can't convert #{self} to an Integer." unless real?
    @real.to_i
  end

  # Converts self to a Float.
  # @raise [RangeError] if it has a non-zero imaginary part.
  # @return [Float]
  def to_f
    fail RangeError, "Can't convert #{self} to a Float." unless real?
    @real.to_f
  end

  # Converts self to a Rational.
  # @raise [RangeError] if it has a non-zero imaginary part.
  # @return [Rational]
  def to_r
    fail RangeError, "Can't convert #{self} to a Rational." unless real?
    @real.to_r
  end

  # Returns a string representation.
  # @return [String]
  def to_s
    "(#{@real} + #{@imaginary}i)"
  end
  alias inspect to_s
end

# BookKeeping for exercism test versions.
module BookKeeping
  VERSION = 1 # Test version
end

Community comments

Find this solution interesting? Ask the author a question to learn more.

What can you learn from this solution?

A huge amount can be learned from reading other people’s code. This is why we wanted to give exercism users the option of making their solutions public.

Here are some questions to help you reflect on this solution and learn the most from it.

  • What compromises have been made?
  • Are there new concepts here that you could read more about to improve your understanding?