Complex numbers (Scala)

From LiteratePrograms

Jump to: navigation, search
Other implementations: C++ | Java | Ruby | Scala


This article presents an set of classes for representing complex numbers in the Scala multiparadigm programming language.

Contents

Representation

We will represent a complex number with the abstract Complex class. This class includes accessors to query the complex number, as well as basic arithmetic functions.

We provide two concrete subclasses to construct a complex number either with its rectangular coordinates (RectComplex) or its polar coordinates (PolarComplex). Except for their constructor, which behaves differently, these classes share exactly the same behavior.

Note that these complex numbers are immutable.

<<ComplexNumber.scala>>=
package complex

class Complex {
  accessors
  arithmetic
  conjugate
}

rectangle initialization
polar initialization

Accessors

The accessors are defined in the Complex abstract base class.

<<accessors>>=
  val real:      double
  val imag:      double
  val magnitude: double
  val angle:     double  

Initialization

Initialization is done in the concrete subclasses.

<<rectangle initialization>>=
class RectComplex(r: double, i: double) extends Complex {
  val real = r
  val imag = i
  val magnitude = Math.sqrt(r*r + i*i)
  val angle = Math.atan(i / r)
}

<<polar initialization>>=
class PolarComplex(m: double, a: double) extends Complex {
  val real = m * Math.cos(a)
  val imag = m * Math.sin(a)
  val magnitude = m
  val angle = a
}

Arithmetic

Core arithmetic operations provided are addition, subtraction, multiplication, division. The arithmetic operations are defined in the abstract Complex base class.

<<arithmetic>>=
addition
subtraction
multiplication
division

Addition and Subtraction

Addition and subtraction are implemented using the coordinate representation:

<<addition>>=
  def +(that: Complex) = new RectComplex(real + that.real, imag + that.imag)
<<subtraction>>=
  def -(that: Complex) = new RectComplex(real - that.real, imag - that.imag)

Multiplication and Division

Multiplication and division are implemented using the polar representation:

<<multiplication>>=
  def *(that: Complex) = new PolarComplex(magnitude * that.magnitude, angle + that.angle)
<<division>>=
  def /(that: Complex) = new PolarComplex(magnitude / that.magnitude, angle - that.angle)

Complex Conjugate

<<conjugate>>=
  def conjugate = new RectComplex(real, -imag)

Test

Here where test our complex numbers. As you can see the code is much longer than our complex number implementation itself.

<<ComplexTestSuite.scala>>=
package complex

test initialization
test util
test rectangular constructor
test polar constructor
test addition
test subtraction
test multiplication
test division
test conjugate
test run

Test Initialization

<<test initialization>>=
  import scala.testing.SUnit._
  val tr = new TestResult
  val tolerance = 0.000001

Test Util

A util base class for the individual test objects.

<<test util>>=
  abstract class UtilTest(title: String) extends TestCase(title) with Assert {
    def assertInDelta(exp: double, found: double) = {
      val delta = Math.abs(exp - found)
      if (delta > tolerance) fail("Expected delta to be less than " + tolerance + ". Was " + delta)
    }
  }

Test Rectangular Constructor

<<test rectangular constructor>>=
  object RectConstructorTest extends UtilTest("Testing rectangular complex number constructor") {
    override def runTest = {
      val c = new RectComplex(1, 1)
      assertEquals(1, c.real)
      assertEquals(1, c.imag)
      assertInDelta(Math.sqrt(2.0), c.magnitude)
      assertInDelta(Math.Pi / 4, c.angle)
    }
  }

Test Polar Constructor

<<test polar constructor>>=
  object PolarConstructorTest extends UtilTest("Testing polar complex number constructor") {
    override def runTest = {
      val c = new PolarComplex(1, Math.Pi / 4)
      assertInDelta(Math.sqrt(2.0) / 2, c.real)
      assertInDelta(Math.sqrt(2.0) / 2, c.imag)
      assertEquals(1, c.magnitude)
      assertEquals(Math.Pi / 4, c.angle)
    }
  }

Test Addition

<<test addition>>=
  object AdditionTest extends UtilTest("Testing complex addition") {
    override def runTest = {
      val z1 = new RectComplex(1, 2)
      val z2 = new RectComplex(3, 4)
      val z3 = z1 + z2
      assertEquals(1 + 3, z3.real)
      assertEquals(2 + 4, z3.imag)
    }
  }

Test Subtraction

<<test subtraction>>=
  object SubractionTest extends UtilTest("Testing complex subtraction") {
    override def runTest = {
      val z1 = new RectComplex(1, 2)
      val z2 = new RectComplex(3, 4)
      val z3 = z1 - z2
      assertEquals(1 - 3, z3.real)
      assertEquals(2 - 4, z3.imag)
    }
  }

Test Multiplication

<<test multiplication>>=
  object MultiplicationTest extends UtilTest("Testing complex multiplication") {
    override def runTest = {
      val z1 = new PolarComplex(1, Math.Pi / 3)
      val z2 = new PolarComplex(3, Math.Pi / 6)
      val z3 = z1 * z2
      assertEquals(1 * 3, z3.magnitude)
      assertEquals(Math.Pi / 2, z3.angle)
    }
  }

Test Division

<<test division>>=
  object DivisionTest extends UtilTest("Testing complex division") {
    override def runTest = {
      val z1 = new PolarComplex(1, Math.Pi / 3)
      val z2 = new PolarComplex(2, Math.Pi / 6)
      val z3 = z1 / z2
      assertEquals(1.0 / 2, z3.magnitude)
      assertEquals(Math.Pi / 6, z3.angle)
    }
  }

Test Conjugate

<<test conjugate>>=
  object ConjugateTest extends UtilTest("Testing complex conjugate") {
    override def runTest = {
      val z = new RectComplex(1, 2).conjugate
      assertEquals(1, z.real)
      assertEquals(-2, z.imag)
    }
  }

Test Run

<<test run>>=
  new TestSuite(RectConstructorTest, 
                PolarConstructorTest, 
                AdditionTest,
                SubractionTest, 
                MultiplicationTest, 
                DivisionTest,
                ConjugateTest
                ).run(tr)
  for(f <- tr.failures) {
    println(f)
  }
Download code
Personal tools