diff --git a/DIRECTORY.md b/DIRECTORY.md index a48e01f4ac0..da264de1943 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -282,6 +282,7 @@ * [CircularConvolutionFFT](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/CircularConvolutionFFT.java) * [CollatzConjecture](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/CollatzConjecture.java) * [Combinations](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/Combinations.java) + * [ComplexNumberUtil](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/ComplexNumberUtil.java) * [Convolution](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/Convolution.java) * [ConvolutionFFT](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/ConvolutionFFT.java) * [CrossCorrelation](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/maths/CrossCorrelation.java) @@ -726,10 +727,12 @@ * [CeilTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/CeilTest.java) * [CollatzConjectureTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/CollatzConjectureTest.java) * [CombinationsTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/CombinationsTest.java) + * [ComplexNumberUtilTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/ComplexNumberUtilTest.java) * [CrossCorrelationTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/CrossCorrelationTest.java) * [DigitalRootTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/DigitalRootTest.java) * [DistanceFormulaTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/DistanceFormulaTest.java) * [DudeneyNumberTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/DudeneyNumberTest.java) + * [FactorialRecursionTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/FactorialRecursionTest.java) * [FactorialTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/FactorialTest.java) * [FastInverseSqrtTests](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/FastInverseSqrtTests.java) * [FFTTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/FFTTest.java) @@ -762,7 +765,9 @@ * [MillerRabinPrimalityCheckTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/MillerRabinPrimalityCheckTest.java) * [MinValueTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/MinValueTest.java) * [MobiusFunctionTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/MobiusFunctionTest.java) + * [ModeTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/ModeTest.java) * [NthUglyNumberTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/NthUglyNumberTest.java) + * [NumberOfDigitsTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/NumberOfDigitsTest.java) * [PalindromeNumberTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/PalindromeNumberTest.java) * [ParseIntegerTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/ParseIntegerTest.java) * [PascalTriangleTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/maths/PascalTriangleTest.java) @@ -853,6 +858,7 @@ * [BogoSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/BogoSortTest.java) * [BubbleSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/BubbleSortTest.java) * [BucketSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/BucketSortTest.java) + * [CircleSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/CircleSortTest.java) * [CocktailShakerSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/CocktailShakerSortTest.java) * [CombSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/CombSortTest.java) * [DualPivotQuickSortTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/sorts/DualPivotQuickSortTest.java) diff --git a/src/main/java/com/thealgorithms/maths/ComplexNumberUtil.java b/src/main/java/com/thealgorithms/maths/ComplexNumberUtil.java new file mode 100644 index 00000000000..e16fd37451f --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/ComplexNumberUtil.java @@ -0,0 +1,264 @@ +package com.thealgorithms.maths; + +public class ComplexNumberUtil { + + public static class ComplexNumber { + public final double REAL; + public final double IMAGINARY; + + public ComplexNumber(double real, double imaginary) { + REAL = real; + IMAGINARY = imaginary; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof ComplexNumber num) { + return this.REAL == num.REAL && this.IMAGINARY == num.IMAGINARY; + } + + return super.equals(obj); + } + + @Override + public String toString() { + return REAL + ((IMAGINARY < 0) ? (" - " + Math.abs(IMAGINARY)) : (" + " + IMAGINARY)) + "i"; + } + } + + public static final ComplexNumber ZERO = new ComplexNumber(0, 0); + public static final ComplexNumber ONE = new ComplexNumber(1, 0); + public static final ComplexNumber TWO = new ComplexNumber(2, 0); + public static final ComplexNumber PLUS_I = new ComplexNumber(0, 1); + public static final ComplexNumber MINUS_I = new ComplexNumber(0, -1); + + /** + * add two complex numbers + * @param num1 the first complex number + * @param num2 the second complex number + * @return the sum of num1 and num2 + */ + public static ComplexNumber add(ComplexNumber num1, ComplexNumber num2) { + return new ComplexNumber(num1.REAL + num2.REAL, num1.IMAGINARY + num2.IMAGINARY); + } + + /** + * Subtracts the second complex number from the first complex number. + * + * @param num1 the first complex number + * @param num2 the second complex number + * @return the result of subtracting num2 from num1 + */ + public static ComplexNumber subtract(ComplexNumber num1, ComplexNumber num2) { + return new ComplexNumber(num1.REAL - num2.REAL, num1.IMAGINARY - num2.IMAGINARY); + } + + /** + * Multiplies two complex numbers. + * + * @param num1 the first complex number + * @param num2 the second complex number + * @return the product of num1 and num2 + * @link ... + */ + public static ComplexNumber multiply(ComplexNumber num1, ComplexNumber num2) { + return new ComplexNumber(num1.REAL * num2.REAL - num1.IMAGINARY * num2.IMAGINARY, num1.REAL * num2.IMAGINARY + num1.IMAGINARY * num2.REAL); + } + + /** + * Divides the first complex number by the second complex number. + * + * @param num1 the numerator complex number + * @param num2 the denominator complex number + * @return the result of dividing num1 by num2 + * @throws RuntimeException if the divisor (num2) is zero + * @link ... + */ + public static ComplexNumber divide(ComplexNumber num1, ComplexNumber num2) { + final double divisor = num2.REAL * num2.REAL + num2.IMAGINARY * num2.IMAGINARY; + if (divisor == 0) { + throw new RuntimeException("Cannot divide by zero"); + } + + return new ComplexNumber((num1.REAL * num2.REAL + num1.IMAGINARY * num2.IMAGINARY) / divisor, (num1.IMAGINARY * num2.REAL - num1.REAL * num2.IMAGINARY) / divisor); + } + + /** + * Computes the absolute value (magnitude) of a complex number. + * + * @param num the complex number + * @return the absolute value of num + * @link ... + */ + public static double abs(ComplexNumber num) { + return Math.sqrt(num.REAL * num.REAL + num.IMAGINARY * num.IMAGINARY); + } + + /** + * Computes the exponential function of a complex number. + * + * @param num the complex number + * @return e raised to the power of num + * @link ... + */ + public static ComplexNumber exp(ComplexNumber num) { + final double coefficient = Math.exp(num.REAL); + return new ComplexNumber(coefficient * Math.cos(num.IMAGINARY), coefficient * Math.sin(num.IMAGINARY)); + } + + /** + * Computes the principal value of natural logarithm of a complex number. + * + * @param num the complex number + * @return the natural logarithm of num + * @throws RuntimeException if num is zero + * @link ... + */ + public static ComplexNumber log(ComplexNumber num) { + if (num.equals(ZERO)) { + throw new RuntimeException("Cannot take the logarithm of zero"); + } + + return new ComplexNumber(Math.log(abs(num)), Math.atan2(num.IMAGINARY, num.REAL)); + } + + /** + * Computes the power of a complex number raised to another complex number. + * + * @param num1 the base complex number + * @param num2 the exponent complex number + * @return num1 raised to the power of num2 + * link ... + */ + public static ComplexNumber pow(ComplexNumber num1, ComplexNumber num2) { + if (num1.equals(ZERO)) { + return num2.equals(ZERO) ? ONE : ZERO; + } + + return exp(multiply(ln(num1), num2)); + } + + /** + * Computes the principal square root of a complex number. + * + * @param num the complex number + * @return the square root of num + */ + public static ComplexNumber sqrt(ComplexNumber num) { + return pow(num, new ComplexNumber(0.5, 0)); + } + + /** + * Computes the sine of a complex number. + * + * @param num the complex number + * @return the sine of num + * @link ...(Euler's_formula) + */ + public static ComplexNumber sin(ComplexNumber num) { + ComplexNumber exp1 = exp(multiply(num, PLUS_I)); + ComplexNumber exp2 = exp(multiply(num, MINUS_I)); + return divide(subtract(exp1, exp2), multiply(new ComplexNumber(2, 0), PLUS_I)); + } + + /** + * Computes the cosine of a complex number. + * + * @param num the complex number + * @return the cosine of num + * @link ...(Euler's_formula) + */ + public static ComplexNumber cos(ComplexNumber num) { + ComplexNumber exp1 = exp(multiply(num, PLUS_I)); + ComplexNumber exp2 = exp(multiply(num, MINUS_I)); + return divide(add(exp1, exp2), TWO); + } + + /** + * Computes the tangent of a complex number. + * + * @param num the complex number + * @return the tangent of num + * @throws RuntimeException if num.real = pi*(n+0.5) + * @link ... + */ + public static ComplexNumber tan(ComplexNumber num) { + if (num.REAL % Math.PI == Math.PI / 2) { + throw new RuntimeException("Cannot take the tan of a number where the real part can be expressed as pi*(n+0.5)"); + } + + return divide(sin(num), cos(num)); + } + + /** + * Computes the cotangent of a complex number. + * + * @param num the complex number + * @return the cotangent of num + * @throws RuntimeException if num.real = pi*n + * @link ... + */ + public static ComplexNumber cot(ComplexNumber num) { + if (num.REAL % Math.PI == 0) { + throw new RuntimeException("Cannot take the cot of number with real part dividable by pi"); + } + + return divide(cos(num), sin(num)); + } + + /** + * Computes the arcsine of a complex number. + * + * @param num the complex number + * @return the arcsine of num + * @link ... + */ + public static ComplexNumber arcsin(ComplexNumber num) { + ComplexNumber temp = sqrt(subtract(ONE, multiply(num, num))); + return multiply(MINUS_I, ln(add(multiply(PLUS_I, num), temp))); + } + + /** + * Computes the arccosine of a complex number. + * + * @param num the complex number + * @return the arccosine of num + * @link ... + */ + public static ComplexNumber arccos(ComplexNumber num) { + ComplexNumber temp = sqrt(subtract(ONE, multiply(num, num))); + return multiply(MINUS_I, ln(add(num, multiply(temp, PLUS_I)))); + } + + /** + * Computes the arctangent of a complex number. + * + * @param num the complex number + * @return the arctangent of num + * @throws RuntimeException if num=i or num=-i + * @link ... + */ + public static ComplexNumber arctan(ComplexNumber num) { + if (num.equals(PLUS_I) || num.equals(MINUS_I)) { + throw new RuntimeException("Cannot take the arctan of " + num); + } + + return multiply(divide(MINUS_I, TWO), ln(divide(subtract(PLUS_I, num), add(PLUS_I, num)))); + } + + /** + * Computes the arccotangent of a complex number. + * + * @param num the complex number + * @return the arccotangent of num + * @throws RuntimeException if num=i or num=-i + * @link ... + */ + public static ComplexNumber arccot(ComplexNumber num) { + if (num.equals(PLUS_I) || num.equals(MINUS_I)) { + throw new RuntimeException("Cannot take the arccot of " + num); + } + + return multiply(divide(MINUS_I, TWO), ln(divide(add(num, PLUS_I), subtract(num, PLUS_I)))); + } +} diff --git a/src/test/java/com/thealgorithms/maths/ComplexNumberUtilTest.java b/src/test/java/com/thealgorithms/maths/ComplexNumberUtilTest.java new file mode 100644 index 00000000000..7f58ce61b80 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/ComplexNumberUtilTest.java @@ -0,0 +1,224 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import com.thealgorithms.maths.ComplexNumberUtil.ComplexNumber; +import org.junit.jupiter.api.Test; + +public class ComplexNumberUtilTest { + + private boolean checkIfEqual(ComplexNumber expected, ComplexNumber actual) { + final double LIM = 0.0001; + if (Math.abs(actual.REAL - expected.REAL) > LIM || Math.abs(actual.IMAGINARY - expected.IMAGINARY) > LIM) { + fail("Expected " + expected + " but got " + actual); + } + + return true; + } + + @Test + public void testComplexNumberConstructor() { + ComplexNumber c = new ComplexNumber(3.0, 4.0); + assertEquals(3.0, c.REAL); + assertEquals(4.0, c.IMAGINARY); + } + + @Test + public void testEquals1() { + ComplexNumber c1 = new ComplexNumber(1, 2); + ComplexNumber c2 = new ComplexNumber(1, 2); + ComplexNumber c3 = new ComplexNumber(2, 1); + + assertTrue(checkIfEqual(c1, c2)); + assertNotEquals(c1, c3); + } + + @Test + public void testEquals2() { + ComplexNumber c1 = new ComplexNumber(1, 2); + assertNotEquals(c1, 2); + } + + @Test + public void testToString() { + ComplexNumber c = new ComplexNumber(3, -2); + assertEquals("3.0 - 2.0i", c.toString()); + } + + @Test + public void testAdd() { + ComplexNumber c1 = new ComplexNumber(1, 1); + ComplexNumber c2 = new ComplexNumber(2, 3); + ComplexNumber result = ComplexNumberUtil.add(c1, c2); + checkIfEqual(new ComplexNumber(3, 4), result); + } + + @Test + public void testSubtract() { + ComplexNumber c1 = new ComplexNumber(5, 5); + ComplexNumber c2 = new ComplexNumber(3, 2); + ComplexNumber result = ComplexNumberUtil.subtract(c1, c2); + checkIfEqual(new ComplexNumber(2, 3), result); + } + + @Test + public void testMultiply() { + ComplexNumber c1 = new ComplexNumber(1, 1); + ComplexNumber c2 = new ComplexNumber(2, 3); + ComplexNumber result = ComplexNumberUtil.multiply(c1, c2); + checkIfEqual(new ComplexNumber(-1, 5), result); + } + + @Test + public void testDivide() { + ComplexNumber c1 = new ComplexNumber(1, 1); + ComplexNumber c2 = new ComplexNumber(2, 3); + ComplexNumber result = ComplexNumberUtil.divide(c1, c2); + checkIfEqual(new ComplexNumber(0.38461538, -0.07692307), result); + } + + @Test + public void testDivideByZero() { + ComplexNumber c1 = new ComplexNumber(1, 1); + ComplexNumber c2 = new ComplexNumber(0, 0); + assertThrows(RuntimeException.class, () -> ComplexNumberUtil.divide(c1, c2)); + } + + @Test + public void testAbs() { + ComplexNumber c = new ComplexNumber(3, 4); + double result = ComplexNumberUtil.abs(c); + assertEquals(5.0, result, 0.001); + } + + @Test + public void testExp() { + ComplexNumber c = new ComplexNumber(1, Math.PI); + ComplexNumber result = ComplexNumberUtil.exp(c); + checkIfEqual(new ComplexNumber(-2.718281828459045, 0), result); + } + + @Test + public void testLn() { + ComplexNumber c = new ComplexNumber(5, 5); + ComplexNumber result = ComplexNumberUtil.ln(c); + checkIfEqual(new ComplexNumber(Math.log(5 * Math.sqrt(2)), Math.PI / 4), result); + } + + @Test + public void testLnOfZero() { + assertThrows(RuntimeException.class, () -> ComplexNumberUtil.ln(ComplexNumberUtil.ZERO)); + } + + @Test + public void testPow1() { + ComplexNumber c1 = new ComplexNumber(0, 0); + ComplexNumber c2 = new ComplexNumber(0, 0); + ComplexNumber result = ComplexNumberUtil.pow(c1, c2); + checkIfEqual(ComplexNumberUtil.ONE, result); + } + + @Test + public void testPow2() { + ComplexNumber c1 = new ComplexNumber(-3, 4); + ComplexNumber c2 = new ComplexNumber(2, -5); + ComplexNumber result = ComplexNumberUtil.pow(c1, c2); + ComplexNumber expected = new ComplexNumber(-1428309.3755404, 738159.21728509); + checkIfEqual(expected, result); + } + + @Test + public void testSqrt() { + ComplexNumber c = new ComplexNumber(-2, 3); + ComplexNumber result = ComplexNumberUtil.sqrt(c); + checkIfEqual(new ComplexNumber(0.8959774, 1.6741492), result); + } + + @Test + public void testSin() { + ComplexNumber c = new ComplexNumber(1, 1); + ComplexNumber result = ComplexNumberUtil.sin(c); + ComplexNumber expected = new ComplexNumber(1.2984575814159773, 0.6349639147847361); + checkIfEqual(expected, result); + } + + @Test + public void testCos() { + ComplexNumber c = new ComplexNumber(1, 1); + ComplexNumber result = ComplexNumberUtil.cos(c); + ComplexNumber expected = new ComplexNumber(0.8337300251311491, -0.9888977057628651); + checkIfEqual(expected, result); + } + + @Test + public void testTan() { + ComplexNumber c = new ComplexNumber(1, 1); + ComplexNumber result = ComplexNumberUtil.tan(c); + ComplexNumber expected = new ComplexNumber(0.2717525853195117, 1.0839233273386948); + checkIfEqual(expected, result); + } + + @Test + public void testTanOutOfRange() { + assertThrows(RuntimeException.class, () -> ComplexNumberUtil.tan(new ComplexNumber(Math.PI * 2.5, 0))); + } + + @Test + public void testCot() { + ComplexNumber c = new ComplexNumber(1, 1); + ComplexNumber result = ComplexNumberUtil.cot(c); + ComplexNumber expected = new ComplexNumber(0.21762156185440268, -0.8680141428959249); + checkIfEqual(expected, result); + } + + @Test + public void testCotOutOfRange() { + assertThrows(RuntimeException.class, () -> ComplexNumberUtil.cot(new ComplexNumber(Math.PI * 3, 0))); + } + + @Test + public void testArcsin() { + ComplexNumber c = new ComplexNumber(0.5, 0); + ComplexNumber result = ComplexNumberUtil.arcsin(c); + ComplexNumber expected = new ComplexNumber(0.5235987755982989, 0); + checkIfEqual(expected, result); + } + + @Test + public void testArccos() { + ComplexNumber c = new ComplexNumber(0.5, 0); + ComplexNumber result = ComplexNumberUtil.arccos(c); + ComplexNumber expected = new ComplexNumber(1.0471975511965979, 0); + checkIfEqual(expected, result); + } + + @Test + public void testArctan() { + ComplexNumber c = new ComplexNumber(1, 1); + ComplexNumber result = ComplexNumberUtil.arctan(c); + ComplexNumber expected = new ComplexNumber(1.0172219678978514, 0.40235947810852507); + checkIfEqual(expected, result); + } + + @Test + public void testArctanOfI() { + assertThrows(RuntimeException.class, () -> ComplexNumberUtil.arctan(ComplexNumberUtil.PLUS_I)); + } + + @Test + public void testArccot() { + ComplexNumber c = new ComplexNumber(1, 1); + ComplexNumber result = ComplexNumberUtil.arccot(c); + ComplexNumber expected = new ComplexNumber(0.5535743588970452, -0.40235947810852507); + checkIfEqual(expected, result); + } + + @Test + public void testArccotOfI() { + assertThrows(RuntimeException.class, () -> ComplexNumberUtil.arccot(ComplexNumberUtil.PLUS_I)); + } +}