Avatar of a-glapinski

a-glapinski's solution

to Complex Numbers in the Kotlin Track

Published at Sep 09 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.

Setup

Go through the setup instructions for Kotlin to install the necessary dependencies:

https://exercism.io/tracks/kotlin/installation

Making the test suite pass

Execute the tests with:

$ gradlew test

Use gradlew.bat if you're on Windows

In the test suites all tests but the first have been skipped.

Once you get a test passing, you can enable the next one by removing the @Ignore annotation.

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.kt

import org.junit.Assert.assertEquals
import org.junit.Ignore
import org.junit.Test
import kotlin.math.*

class ComplexNumberTest {

    // Test helpers

    companion object {
        private const val DOUBLE_EQUALITY_TOLERANCE = 1e-15
    }

    private fun assertDoublesEqual(d1: Double, d2: Double) {
        assertEquals(d1, d2, DOUBLE_EQUALITY_TOLERANCE)
    }

    private fun assertComplexNumbersEqual(c1: ComplexNumber, c2: ComplexNumber) {
        assertDoublesEqual(c1.real, c2.real)
        assertDoublesEqual(c1.imag, c2.imag)
    }

    // Tests

    @Test
    fun testImaginaryUnitExhibitsDefiningProperty() {
        val expected = ComplexNumber(real = -1.0)
        val actual = ComplexNumber(imag = 1.0) * ComplexNumber(imag = 1.0)
        assertComplexNumbersEqual(expected, actual)
    }

    @Ignore
    @Test
    fun testAdditionWithPurelyRealNumbers() {
        val expected = ComplexNumber(real = 3.0)
        val actual = ComplexNumber(real = 1.0) + ComplexNumber(real = 2.0)
        assertComplexNumbersEqual(expected, actual)
    }

    @Ignore
    @Test
    fun testAdditionWithPurelyImaginaryNumbers() {
        val expected = ComplexNumber(imag = 3.0)
        val actual = ComplexNumber(imag = 1.0) + ComplexNumber(imag = 2.0)
        assertComplexNumbersEqual(expected, actual)
    }

    @Ignore
    @Test
    fun testAdditionWithRealAndImaginaryParts() {
        val expected = ComplexNumber(real = 4.0, imag = 6.0)
        val actual = ComplexNumber(real = 1.0, imag = 2.0) + ComplexNumber(real = 3.0, imag = 4.0)
        assertComplexNumbersEqual(expected, actual)
    }

    @Ignore
    @Test
    fun testSubtractionWithPurelyRealNumbers() {
        val expected = ComplexNumber(real = -1.0, imag = 0.0)
        val actual = ComplexNumber(real = 1.0, imag = 0.0) - ComplexNumber(real = 2.0, imag = 0.0)
        assertComplexNumbersEqual(expected, actual)
    }

    @Ignore
    @Test
    fun testSubtractionWithPurelyImaginaryNumbers() {
        val expected = ComplexNumber(imag = -1.0)
        val actual = ComplexNumber(imag = 1.0) - ComplexNumber(imag = 2.0)
        assertComplexNumbersEqual(expected, actual)
    }

    @Ignore
    @Test
    fun testSubtractionWithRealAndImaginaryParts() {
        val expected = ComplexNumber(real = -2.0, imag = -2.0)
        val actual = ComplexNumber(real = 1.0, imag = 2.0) - ComplexNumber(real = 3.0, imag = 4.0)
        assertComplexNumbersEqual(expected, actual)
    }

    @Ignore
    @Test
    fun testMultiplicationWithPurelyRealNumbers() {
        val expected = ComplexNumber(real = 2.0)
        val actual = ComplexNumber(real = 1.0) * ComplexNumber(real = 2.0)
        assertComplexNumbersEqual(expected, actual)
    }

    @Ignore
    @Test
    fun testMultiplicationWithPurelyImaginaryNumbers() {
        val expected = ComplexNumber(real = -2.0)
        val actual = ComplexNumber(imag = 1.0) * ComplexNumber(imag = 2.0)
        assertComplexNumbersEqual(expected, actual)
    }

    @Ignore
    @Test
    fun testMultiplicationWithRealAndImaginaryParts() {
        val expected = ComplexNumber(real = -5.0, imag = 10.0)
        val actual = ComplexNumber(real = 1.0, imag = 2.0) * ComplexNumber(real = 3.0, imag = 4.0)
        assertComplexNumbersEqual(expected, actual)
    }

    @Ignore
    @Test
    fun testDivisionWithPurelyRealNumbers() {
        val expected = ComplexNumber(real = 0.5)
        val actual = ComplexNumber(real = 1.0) / ComplexNumber(real = 2.0)
        assertComplexNumbersEqual(expected, actual)
    }

    @Ignore
    @Test
    fun testDivisionWithPurelyImaginaryNumbers() {
        val expected = ComplexNumber(real = 0.5)
        val actual = ComplexNumber(imag = 1.0) / ComplexNumber(imag = 2.0)
        assertComplexNumbersEqual(expected, actual)
    }

    @Ignore
    @Test
    fun testDivisionWithRealAndImaginaryParts() {
        val expected = ComplexNumber(real = 0.44, imag = 0.08)
        val actual = ComplexNumber(real = 1.0, imag = 2.0) / ComplexNumber(real = 3.0, imag = 4.0)
        assertComplexNumbersEqual(expected, actual)
    }

    @Ignore
    @Test
    fun testAbsoluteValueOfPositivePurelyRealNumber() {
        val expected = 5.0
        val actual = ComplexNumber(real = 5.0).abs
        assertDoublesEqual(expected, actual)
    }

    @Ignore
    @Test
    fun testAbsoluteValueOfNegativePurelyRealNumber() {
        val expected = 5.0
        val actual = ComplexNumber(real = -5.0).abs
        assertDoublesEqual(expected, actual)
    }

    @Ignore
    @Test
    fun testAbsoluteValueOfPurelyImaginaryNumberWithPositiveImaginaryPart() {
        val expected = 5.0
        val actual = ComplexNumber(imag = 5.0).abs
        assertDoublesEqual(expected, actual)
    }

    @Ignore
    @Test
    fun testAbsoluteValueOfPurelyImaginaryNumberWithNegativeImaginaryPart() {
        val expected = 5.0
        val actual = ComplexNumber(imag = -5.0).abs
        assertDoublesEqual(expected, actual)
    }

    @Ignore
    @Test
    fun testAbsoluteValueOfNumberWithRealAndImaginaryParts() {
        val expected = 5.0
        val actual = ComplexNumber(real = 3.0, imag = 4.0).abs
        assertDoublesEqual(expected, actual)
    }

    @Ignore
    @Test
    fun testConjugationOfPurelyRealNumber() {
        val expected = ComplexNumber(real = 5.0)
        val actual = ComplexNumber(real = 5.0).conjugate()
        assertComplexNumbersEqual(expected, actual)
    }

    @Ignore
    @Test
    fun testConjugationOfPurelyImaginaryNumber() {
        val expected = ComplexNumber(imag = -5.0)
        val actual = ComplexNumber(imag = 5.0).conjugate()
        assertComplexNumbersEqual(expected, actual)
    }

    @Ignore
    @Test
    fun testConjugationOfNumberWithRealAndImaginaryParts() {
        val expected = ComplexNumber(real = 1.0, imag = -1.0)
        val actual = ComplexNumber(real = 1.0, imag = 1.0).conjugate()
        assertComplexNumbersEqual(expected, actual)
    }

    @Ignore
    @Test
    fun testRealPartOfPurelyRealNumber() {
        val expected = 1.0
        val actual = ComplexNumber(real = 1.0).real
        assertDoublesEqual(expected, actual)
    }

    @Ignore
    @Test
    fun testRealPartOfPurelyImaginaryNumber() {
        val expected = 0.0
        val actual = ComplexNumber(imag = 1.0).real
        assertDoublesEqual(expected, actual)
    }

    @Ignore
    @Test
    fun testRealPartOfNumberWithRealAndImaginaryParts() {
        val expected = 1.0
        val actual = ComplexNumber(real = 1.0, imag = 2.0).real
        assertDoublesEqual(expected, actual)
    }

    @Ignore
    @Test
    fun testImaginaryPartOfPurelyRealNumber() {
        val expected = 0.0
        val actual = ComplexNumber(real = 1.0).imag
        assertDoublesEqual(expected, actual)
    }

    @Ignore
    @Test
    fun testImaginaryPartOfPurelyImaginaryNumber() {
        val expected = 1.0
        val actual = ComplexNumber(imag = 1.0).imag
        assertDoublesEqual(expected, actual)
    }

    @Ignore
    @Test
    fun testImaginaryPartOfNumberWithRealAndImaginaryParts() {
        val expected = 2.0
        val actual = ComplexNumber(real = 1.0, imag = 2.0).imag
        assertDoublesEqual(expected, actual)
    }

    @Ignore
    @Test
    fun testExponentialOfPurelyImaginaryNumber() {
        val expected = ComplexNumber(real = -1.0)
        val actual = exponential(ComplexNumber(imag = PI))
        assertComplexNumbersEqual(expected, actual)
    }

    @Ignore
    @Test
    fun testExponentialOfZero() {
        val expected = ComplexNumber(real = 1.0)
        val actual = exponential(ComplexNumber())
        assertComplexNumbersEqual(expected, actual)
    }

    @Ignore
    @Test
    fun testExponentialOfPurelyRealNumber() {
        val expected = ComplexNumber(real = E)
        val actual = exponential(ComplexNumber(real = 1.0))
        assertComplexNumbersEqual(expected, actual)
    }

    @Ignore
    @Test
    fun testExponentialOfANumberWithRealAndImaginaryPart() {
        val expected = ComplexNumber(real = -2.0)
        val actual = exponential(ComplexNumber(real = ln(2.0), imag = PI))
        assertComplexNumbersEqual(expected, actual)
    }

}
import kotlin.math.*

data class ComplexNumber(val real: Double = 0.0, val imag: Double = 0.0) {
    val abs by lazy { sqrt(real.squared() + imag.squared()) }

    operator fun plus(other: ComplexNumber) =
            ComplexNumber(this.real + other.real, this.imag + other.imag)

    operator fun minus(other: ComplexNumber) =
            ComplexNumber(this.real - other.real, this.imag - other.imag)

    operator fun times(other: ComplexNumber) = ComplexNumber(
            real = this.real * other.real - this.imag * other.imag,
            imag = this.imag * other.real + this.real * other.imag
    )

    operator fun div(other: ComplexNumber) = ComplexNumber(
            real = (this.real * other.real + this.imag * other.imag) / (other.real.squared() + other.imag.squared()),
            imag = (this.imag * other.real - this.real * other.imag) / (other.real.squared() + other.imag.squared())
    )

    fun conjugate() = this.copy(imag = -this.imag)

    private fun Double.squared() = this * this
}

fun exponential(complexNumber: ComplexNumber) = ComplexNumber(
        real = E.pow(complexNumber.real) * (cos(complexNumber.imag) + sin(complexNumber.imag))
)

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?