diff --git a/src/ca/bcit/comp1510/lab09/Complex.java b/src/ca/bcit/comp1510/lab09/Complex.java new file mode 100644 index 0000000..9b8b811 --- /dev/null +++ b/src/ca/bcit/comp1510/lab09/Complex.java @@ -0,0 +1,196 @@ +package ca.bcit.comp1510.lab09; + +/** + * Immutable Complex number type. + * @author blink + * @version 2025 + * @param re Real part of number. + * @param im Imaginary part of number. + */ +public record Complex(double re, double im) { + + /** Imaginary number I. */ + public static final Complex I = new Complex(0, 1); + + /** Complex number 0. */ + public static final Complex ZERO = new Complex(0, 0); + + /** Complex number 1. */ + public static final Complex ONE = new Complex(1, 0); + + /** + * Factory method for complex number in polar form. + * @param radius magnitude of number + * @param angle argument of number + * @return corresponding Complex number + */ + public static Complex polarComplex(double radius, double angle) { + + return new Complex(radius * Math.cos(angle), + radius * Math.sin(angle)); + } + + /** + * Returns absolute value of this. + * @return the absolute value + */ + public double abs() { + return Math.hypot(re, im); + } + + /** + * Returns argument of this, the angle with respect to + * the positive real axis in range -π to π. + * @return the argument, in radians + */ + public double arg() { + return Math.atan2(im, re); + } + + /** + * Returns conjugate value of this. + * @return the conjugate value + */ + public Complex conjugate() { + return new Complex(re, -im); + } + + /** + * Adds parameter to this complex number. + * @param op2 complex number to add + * @return sum of this + op2 + */ + public Complex add(Complex op2) { + return new Complex(re + op2.re, im + op2.im); + } + + /** + * real add. + * @param op2 real value to add + * @return result + */ + public Complex add(double op2) { + return new Complex(re + op2, im); + } + + /** + * Subtracts parameter from this complex number. + * @param op2 complex number to subtract + * @return difference of this - op2 + */ + public Complex subtract(Complex op2) { + return new Complex(re - op2.re, im - op2.im); + } + + /** + * real subtract. + * @param op2 real value to subtract + * @return result + */ + public Complex subtract(double op2) { + return new Complex(re - op2, im); + } + + /** + * Multiplies parameter with this complex number. + * @param op2 complex number to multiply + * @return product of this * op2 + */ + public Complex multiply(Complex op2) { + double realPart = re * op2.re - im * op2.im; + double imagPart = re * op2.im + im * op2.re; + return new Complex(realPart, imagPart); + } + + /** + * scalar multiply. + * @param op2 scalar value to multiply + * @return result + */ + public Complex multiply(double op2) { + return new Complex(re * op2, im * op2); + } + + /** + * Returns reciprocal of this complex number. + * @return 1 / this + */ + public Complex reciprocal() { + double denominator = re * re + im * im; + if (denominator == 0.0) { + throw new IllegalArgumentException( + "tried to take reciprocal of 0"); + } + return new Complex(re / denominator, -im / denominator); + } + + /** + * Divides parameter into this complex number. + * @param op2 complex number to divide + * @return quotient of this / op2 + */ + public Complex divide(Complex op2) { + if (op2.re == 0.0 && op2.im == 0.0) { + throw new IllegalArgumentException("Tried to divide by zero"); + } + return multiply(op2.reciprocal()); + } + + /** + * scalar divide. + * @param op2 scalar value to divide + * @return result + */ + public Complex divide(double op2) { + if (op2 == 0.0) { + throw new IllegalArgumentException("Tried to divide by 0.0"); + } + return new Complex(re / op2, im); + } + + /** + * Return complex square root of this. + * @return the square root + */ + public Complex sqrt() { + return polarComplex(Math.sqrt(this.abs()), this.arg() / 2.0); + } + + /** + * Return the exponential e to the power of this, + * where e is Euler's constant Math.E. + * @return e to the power this + */ + public Complex exp() { + return new Complex(Math.exp(re) * Math.cos(im), + Math.exp(re) * Math.sin(im)); + } + + /** + * Return the natural logarithm of this, + * with argument in range -π to π. + * @return e to the power this + */ + public Complex log() { + return new Complex(Math.log(abs()), arg()); + } + + /** + * Converts to string with special cases for real and imaginary + * values. + * @return String representation of the complex number + */ + public String toString() { + if (im == 0.0) { + return Double.toString(re); + } else if (re == 0.0) { + return Double.toString(im) + "i"; + } else if (im > 0) { + return Double.toString(re) + " + " + + Double.toString(im) + "i"; + } else { + return Double.toString(re) + " - " + + Double.toString(-im) + "i"; + } + } +} diff --git a/src/ca/bcit/comp1510/lab09/ComplexTester.java b/src/ca/bcit/comp1510/lab09/ComplexTester.java new file mode 100644 index 0000000..501b662 --- /dev/null +++ b/src/ca/bcit/comp1510/lab09/ComplexTester.java @@ -0,0 +1,113 @@ +package ca.bcit.comp1510.lab09; + +/** + * Driver to exercise the use of multiple Complex objects. Includes tests for + * divide by zero cases + * + * @author Lewis & Loftus 9e + * @author BCIT + * @version 2017 + */ +public class ComplexTester { + + /** 3 + 4 I real part. */ + private static final int TEST1R = 3; + + /** 3 + 4 I imaginary part. */ + private static final int TEST1I = 4; + + /** 1 + I real part. */ + private static final int TEST2R = 1; + + /** 1 + I imaginary part. */ + private static final int TEST2I = 1; + + /** + * Creates some complex number objects and performs various operations on + * them. + * + * @param args command-line arguments (unused) + */ + public static void main(String[] args) { + Complex z1 = new Complex(TEST1R, TEST1I); + Complex z2 = new Complex(TEST2R, TEST2I); + Complex z3; + Complex z4; + Complex z5; + Complex z6; + Complex z7; + System.out.println("First complex number: " + z1); + System.out.println("Second complex number: " + z2); + + if (z1.equals(z2)) { + System.out.println("z1 and z2 are equal."); + } else { + System.out.println("z1 and z2 are NOT equal."); + } + + z3 = z1.reciprocal(); + System.out.println("The reciprocal of z1 is: " + z3); + + z4 = z1.add(z2); + z5 = z1.subtract(z2); + z6 = z1.multiply(z2); + z7 = z1.divide(z2); + + System.out.println("z1 + z2: " + z4); + System.out.println("z1 - z2: " + z5); + System.out.println("z1 * z2: " + z6); + System.out.println("z1 / z2: " + z7); + System.out.println("One = " + Complex.ONE + "\nZero = " + + Complex.ZERO + "\nI = " + Complex.I + + "\nI * I = " + Complex.I.multiply(Complex.I)); + + testFunctions(z1, z2); + + testErrorCases(); + + } + + + // Groups together error test cases. + private static void testErrorCases() { + try { + Complex.ZERO.reciprocal(); + System.out.println("ZERO reciprocal test failed"); + } catch (IllegalArgumentException ex) { + System.out.println("ZERO reciprocal test worked"); + } + try { + Complex.ONE.divide(Complex.ZERO); + System.out.println("Divide by zero test failed"); + } catch (IllegalArgumentException ex) { + System.out.println("Divide by zero test worked"); + } + } + + // Test other Complex functions. + private static void testFunctions(Complex z1, Complex z2) { + final double piDiv4 = Math.PI / 4.0; + final Complex minusOne = Complex.ZERO.subtract(Complex.ONE); + final Complex pi = new Complex(0.0, Math.PI); + System.out.println("abs(" + z1 + ") = " + z1.abs() + + "\nabs(" + Complex.I + ") = " + Complex.I.abs() + + "\narg(" + z2 + ") = " + z2.arg() + + "\npi / 4 = " + piDiv4 + + "\narg(" + Complex.I + ") = " + Complex.I.arg() + + "\npi / 2 = " + Math.PI / 2.0 + + "\narg(" + minusOne + ") = " + minusOne.arg() + + "\npi / 2 = " + Math.PI + + "\nconjugate(" + z1 + ") = " + z1.conjugate() + + "\nexp(" + z1 + ") = " + z1.exp() + + "\nlog(" + z1 + ") = " + z1.log() + + "\nexp(log(" + z1 + ")) = " + z1.log().exp() + + "\nlog(exp(" + z1 + ")) = " + z1.exp().log() + + "\nexp(" + z2 + ") = " + z2.exp() + + "\nlog(" + z2 + ") = " + z2.log() + + "\nexp(log(" + z2 + ")) = " + z2.log().exp() + + "\nlog(exp(" + z2 + ")) = " + z2.exp().log() + + "\nexp(πi) + 1 = " + pi.exp().add(Complex.ONE) + + ); + } +} diff --git a/test/ca/bcit/comp1510/lab09/ComplexTest.java b/test/ca/bcit/comp1510/lab09/ComplexTest.java new file mode 100644 index 0000000..ae418e4 --- /dev/null +++ b/test/ca/bcit/comp1510/lab09/ComplexTest.java @@ -0,0 +1,114 @@ +package ca.bcit.comp1510.lab09; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.Test; + +class ComplexTest { + Complex c1 = new Complex(3, 4); + Complex c2 = new Complex(0, 1); + Complex c3 = new Complex(1, 1); + + @Test + void testPolarComplex() { + + } + + @Test + void testAbs() { + assertEquals(5, c1.abs()); + assertNotEquals(1, c1.abs()); + assertEquals(1, c2.abs()); + assertNotEquals(5, c2.abs()); + } + + @Test + void testArg() { + assertEquals(0.7853981633974483, c3.arg()); + assertNotEquals(0.03532358293, c3.arg()); + } + + @Test + void testConjugate() { + assertEquals(new Complex(3, -4), c1.conjugate()); + assertNotEquals(new Complex(3, 4), c1.conjugate()); + } + + @Test + void testAddComplex() { + assertEquals(new Complex(4, 5), c1.add(c3)); + assertNotEquals(new Complex(1, 3), c1.add(c3)); + } + + @Test + void testAddDouble() { + assertEquals(new Complex(8, 4), c1.add( 5.0)); + assertNotEquals(new Complex(3, 6), c2.add(4.0)); + } + + @Test + void testSubtractComplex() { + assertEquals(new Complex(2, 3), c1.subtract(c3)); + assertNotEquals(new Complex(4, 3), c1.subtract(c2)); + } + + @Test + void testSubtractDouble() { + assertEquals(new Complex(2, 4), c1.subtract(1.0)); + assertNotEquals(new Complex(4, 3), c1.subtract(3.0)); + } + + @Test + void testMultiplyComplex() { + assertEquals(new Complex(-1, 7), c1.multiply(c3)); + assertNotEquals(new Complex(4, 3), c1.multiply(c3)); + } + + @Test + void testMultiplyDouble() { + assertEquals(new Complex(6, 8), c1.multiply(2.0)); + assertNotEquals(new Complex(5, 6), c2.multiply(3)); + } + + @Test + void testReciprocal() { + assertEquals(new Complex(3.0/25, -4.0/25), c1.reciprocal()); + assertNotEquals(new Complex(3.0/25, -4.0/25), c2.reciprocal()); + } + + @Test + void testDivideComplex() { + assertEquals(new Complex(3.5, 0.5), c1.divide(c3)); + assertNotEquals(new Complex(-0.5, 3.5), c1.divide(c3)); + } + + @Test + void testDivideDouble() { + assertEquals(new Complex(1.5, 4), c1.divide(2.0)); + assertNotEquals(new Complex(2, 4), c1.divide(1.0)); + } + + @Test + void testSqrt() { + assertEquals(new Complex(2, 1), c1.sqrt()); + assertNotEquals(new Complex(1, 2), c1.sqrt()); + } + + @Test + void testExp() { + assertEquals(new Complex(-13.128783081462158, -15.200784463067954), c1.exp()); + assertNotEquals(new Complex(1, 2), c1.exp()); + } + + @Test + void testLog() { + assertEquals(new Complex(1.6094379124341003, 0.9272952180016122), c1.log()); + assertNotEquals(new Complex(1, 2), c1.log()); + } + + @Test + void testToString() { + assertEquals("3.0 + 4.0i", c1.toString()); + assertNotEquals("3 + 4i", c1.toString()); + } +} \ No newline at end of file