Avatar of clementi

clementi's solution

to Complex Numbers in the Scala Track

Published at Oct 15 2019 · 0 comments
Instructions
Test suite
Solution

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.

Raising e to a complex exponent can be expressed as e^(a + i * b) = e^a * e^(i * b), the last term of which is given by Euler's formula e^(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.

The Scala exercises assume an SBT project scheme. The exercise solution source should be placed within the exercise directory/src/main/scala. The exercise unit tests can be found within the exercise directory/src/test/scala.

To run the tests simply run the command sbt test in the exercise directory.

Please see the learning and installation pages if you need any help.

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.

ComplexNumberTest.scala

import org.scalatest.{Matchers, FunSuite}

/** @version 1.3.0 */
class ComplexNumberTest extends FunSuite with Matchers {

  private val equalityEpsilon = 1e-15

  private def assertEquals(c1: ComplexNumber, c2: ComplexNumber) {
    c1.real should be (c2.real +- equalityEpsilon)
    c1.imaginary should be (c2.imaginary +- equalityEpsilon)
  }

  test("Real part of a purely real number") {
    ComplexNumber(real = 1).real should be (1.0 +- equalityEpsilon)
  }

  test("Real part of a purely imaginary number") {
    pending
    ComplexNumber(real = 0).real should be (0.0 +- equalityEpsilon)
  }

  test("Real part of a number with real and imaginary part") {
    pending
    ComplexNumber(real = 1).real should be (1.0 +- equalityEpsilon)
  }

  test("Imaginary part of a purely real number") {
    pending
    ComplexNumber(imaginary = 0).imaginary should be (0.0 +- equalityEpsilon)
  }

  test("Imaginary part of a purely imaginary number") {
    pending
    ComplexNumber(imaginary = 1).imaginary should be (1.0 +- equalityEpsilon)
  }

  test("Imaginary part of a number with real and imaginary part") {
    pending
    ComplexNumber(imaginary = 2).imaginary should be (2.0 +- equalityEpsilon)
  }

  test("Imaginary unit") {
    pending
    val result = ComplexNumber(0, 1) * ComplexNumber(0, 1)
    assertEquals(result, ComplexNumber(-1, 0))
  }

  test("Add purely real numbers") {
    pending
    val result = ComplexNumber(1, 0) + ComplexNumber(2, 0)
    assertEquals(result, ComplexNumber(3, 0))
  }

  test("Add purely imaginary numbers") {
    pending
    val result = ComplexNumber(0, 1) + ComplexNumber(0, 2)
    assertEquals(result, ComplexNumber(0, 3))
  }

  test("Add numbers with real and imaginary part") {
    pending
    val result = ComplexNumber(1, 2) + ComplexNumber(3, 4)
    assertEquals(result, ComplexNumber(4, 6))
  }

  test("Subtract purely real numbers") {
    pending
    val result = ComplexNumber(1, 0) - ComplexNumber(2, 0)
    assertEquals(result, ComplexNumber(-1, 0))
  }

  test("Subtract purely imaginary numbers") {
    pending
    val result = ComplexNumber(0, 1) - ComplexNumber(0, 2)
    assertEquals(result, ComplexNumber(0, -1))
  }

  test("Subtract numbers with real and imaginary part") {
    pending
    val result = ComplexNumber(1, 2) - ComplexNumber(3, 4)
    assertEquals(result, ComplexNumber(-2, -2))
  }

  test("Multiply purely real numbers") {
    pending
    val result = ComplexNumber(1, 0) * ComplexNumber(2, 0)
    assertEquals(result, ComplexNumber(2, 0))
  }

  test("Multiply purely imaginary numbers") {
    pending
    val result = ComplexNumber(0, 1) * ComplexNumber(0, 2)
    assertEquals(result, ComplexNumber(-2, 0))
  }

  test("Multiply numbers with real and imaginary part") {
    pending
    val result = ComplexNumber(1, 2) * ComplexNumber(3, 4)
    assertEquals(result, ComplexNumber(-5, 10))
  }

  test("Divide purely real numbers") {
    pending
    val result = ComplexNumber(1, 0) / ComplexNumber(2, 0)
    assertEquals(result, ComplexNumber(0.5, 0))
  }

  test("Divide purely imaginary numbers") {
    pending
    val result = ComplexNumber(0, 1) / ComplexNumber(0, 2)
    assertEquals(result, ComplexNumber(0.5, 0))
  }

  test("Divide numbers with real and imaginary part") {
    pending
    val result = ComplexNumber(1, 2) / ComplexNumber(3, 4)
    assertEquals(result, ComplexNumber(0.44, 0.08))
  }

  test("Absolute value of a positive purely real number") {
    pending
    ComplexNumber(5, 0).abs should be (5.0 +- equalityEpsilon)
  }

  test("Absolute value of a negative purely real number") {
    pending
    ComplexNumber(-5, 0).abs should be (5.0 +- equalityEpsilon)
  }

  test("Absolute value of a purely imaginary number with positive imaginary part") {
    pending
    ComplexNumber(0, 5).abs should be (5.0 +- equalityEpsilon)
  }

  test("Absolute value of a purely imaginary number with negative imaginary part") {
    pending
    ComplexNumber(0, -5).abs should be (5.0 +- equalityEpsilon)
  }

  test("Absolute value of a number with real and imaginary part") {
    pending
    ComplexNumber(3, 4).abs should be (5.0 +- equalityEpsilon)
  }

  test("Conjugate a purely real number") {
    pending
    val result = ComplexNumber(5, 0).conjugate
    assertEquals(result, ComplexNumber(5, 0))
  }

  test("Conjugate a purely imaginary number") {
    pending
    val result = ComplexNumber(0, 5).conjugate
    assertEquals(result, ComplexNumber(0, -5))
  }

  test("Conjugate a number with real and imaginary part") {
    pending
    val result = ComplexNumber(1, 1).conjugate
    assertEquals(result, ComplexNumber(1, -1))
  }

  test("Euler's identity/formula") {
    pending
    val result = ComplexNumber.exp(ComplexNumber(0, math.Pi))
    assertEquals(result, ComplexNumber(-1, 0))
  }

  test("Exponential of 0") {
    pending
    val result = ComplexNumber.exp(ComplexNumber(0, 0))
    assertEquals(result, ComplexNumber(1, 0))
  }

  test("Exponential of a purely real number") {
    pending
    val result = ComplexNumber.exp(ComplexNumber(1, 0))
    assertEquals(result, ComplexNumber(math.E, 0))
  }

  test("Exponential of a number with real and imaginary part") {
    pending
    val result = ComplexNumber.exp(ComplexNumber(math.log(2), math.Pi))
    assertEquals(result, ComplexNumber(-2, 0))
  }
}
package exercism

case class ComplexNumber(real: Double = 0, imaginary: Double = 0) {
  def +(that: ComplexNumber): ComplexNumber =
    ComplexNumber(real + that.real, imaginary + that.imaginary)

  def unary_- = ComplexNumber(-real, -imaginary)

  def -(that: ComplexNumber): ComplexNumber =
    this + (-that)

  def *(that: ComplexNumber): ComplexNumber =
    ComplexNumber(real * that.real - imaginary * that.imaginary, imaginary * that.real + real * that.imaginary)

  def recip: ComplexNumber = {
    val denominator = square(real) + square(imaginary)
    ComplexNumber(real / denominator, -imaginary / denominator)
  }

  def /(that: ComplexNumber): ComplexNumber = this * that.recip

  def abs: Double = Math.sqrt(square(real) + square(imaginary))

  def conjugate: ComplexNumber = ComplexNumber(real, -imaginary)

  private def square(x: Double) = x * x
}

object ComplexNumber {
  def exp(z: ComplexNumber): ComplexNumber = {
    val coefficient = Math.exp(z.real)
    ComplexNumber(coefficient * Math.cos(z.imaginary), coefficient * Math.sin(z.imaginary))
  }
}

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?