This is an automated email from the ASF dual-hosted git repository. aherbert pushed a commit to branch complex-gsoc-2022 in repository https://gitbox.apache.org/repos/asf/commons-numbers.git
The following commit(s) were added to refs/heads/complex-gsoc-2022 by this push: new 9510b535 NUMBERS-188: Refactor complex scalar binary functions to static methods 9510b535 is described below commit 9510b535cb466171d686d846dc8d8c2c9b4c6433 Author: sumanth-rajkumar <53705316+sumanth-rajku...@users.noreply.github.com> AuthorDate: Thu Jul 28 05:33:25 2022 -0400 NUMBERS-188: Refactor complex scalar binary functions to static methods Refactored binary methods accepting a scalar double as the second argument. --- .../numbers/complex/ComplexBinaryOperator.java | 8 +- .../commons/numbers/complex/ComplexFunctions.java | 334 ++++++++++++++++++++- ...aryOperator.java => ComplexScalarFunction.java} | 23 +- .../numbers/complex/ComplexUnaryOperator.java | 5 +- .../commons/numbers/complex/CStandardTest.java | 48 +-- .../commons/numbers/complex/ComplexTest.java | 327 +++++++++++--------- .../apache/commons/numbers/complex/TestUtils.java | 66 ++++ 7 files changed, 623 insertions(+), 188 deletions(-) diff --git a/commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/ComplexBinaryOperator.java b/commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/ComplexBinaryOperator.java index 6714e78e..d30ee297 100644 --- a/commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/ComplexBinaryOperator.java +++ b/commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/ComplexBinaryOperator.java @@ -38,10 +38,10 @@ public interface ComplexBinaryOperator<R> { * * @param real1 Real part \( a \) of the first complex number \( (a +ib) \). * @param imaginary1 Imaginary part \( b \) of the first complex number \( (a +ib) \). - * @param real2 Real part \( a \) of the second complex number \( (a +ib) \). - * @param imaginary2 Imaginary part \( b \) of the second complex number \( (a +ib) \). - * @param out Consumer for the complex result. + * @param real2 Real part \( c \) of the second complex number \( (c +id) \). + * @param imaginary2 Imaginary part \( d \) of the second complex number \( (c +id) \). + * @param action Consumer for the complex result. * @return the object returned by the provided consumer. */ - R apply(double real1, double imaginary1, double real2, double imaginary2, ComplexSink<R> out); + R apply(double real1, double imaginary1, double real2, double imaginary2, ComplexSink<R> action); } diff --git a/commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/ComplexFunctions.java b/commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/ComplexFunctions.java index 1b7445f4..1917aa1b 100644 --- a/commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/ComplexFunctions.java +++ b/commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/ComplexFunctions.java @@ -228,7 +228,7 @@ public final class ComplexFunctions { * Returns the argument of the complex number. * * <p>The argument is the angle phi between the positive real axis and - * the point representing this number in the complex plane. + * the point representing the number in the complex plane. * The value returned is between \( -\pi \) (not inclusive) * and \( \pi \) (inclusive), with negative values returned for numbers with * negative imaginary parts. @@ -406,8 +406,8 @@ public final class ComplexFunctions { * * @param real1 Real part \( a \) of the first complex number \( (a +ib) \). * @param imaginary1 Imaginary part \( b \) of the first complex number \( (a +ib) \). - * @param real2 Real part \( a \) of the second complex number \( (a +ib) \). - * @param imaginary2 Imaginary part \( b \) of the second complex number \( (a +ib) \). + * @param real2 Real part \( c \) of the second complex number \( (c +id) \). + * @param imaginary2 Imaginary part \( d \) of the second complex number \( (a +id) \). * @param action Consumer for the addition result. * @param <R> the return type of the supplied action. * @return the object returned by the supplied action. @@ -420,6 +420,58 @@ public final class ComplexFunctions { imaginary1 + imaginary2); } + /** + * Computes the result of the addition of a complex number and a real number. + * Implements the formula: + * + * <p>\[ (a + i b) + c = (a + c) + i b \] + * + * <p>This method is included for compatibility with ISO C99 which defines arithmetic between + * real-only and complex numbers.</p> + * + * <p>Note: This method preserves the sign of the imaginary component \( b \) if it is {@code -0.0}. + * The sign would be lost if adding \( (c + i 0) \) using + * {@link #add(double, double, double, double, ComplexSink) add(real, imaginary, addend, 0, action)} since + * {@code -0.0 + 0.0 = 0.0}. + * + * @param real Real part \( a \) of the complex number \( (a +ib) \). + * @param imaginary Imaginary part \( b \) of the complex number \( (a +ib) \). + * @param addend Value to be added to the complex number. + * @param action Consumer for the addition result. + * @param <R> the return type of the supplied action. + * @return the object returned by the supplied action. + * @see #add(double, double, double, double, ComplexSink) + */ + public static <R> R add(double real, double imaginary, double addend, ComplexSink<R> action) { + return action.apply(real + addend, imaginary); + } + + /** + * Computes the result of the addition of a complex number and an imaginary number. + * Implements the formula: + * + * <p>\[ (a + i b) + i d = a + i (b + d) \] + * + * <p>This method is included for compatibility with ISO C99 which defines arithmetic between + * imaginary-only and complex numbers.</p> + * + * <p>Note: This method preserves the sign of the real component \( a \) if it is {@code -0.0}. + * The sign would be lost if adding \( (0 + i d) \) using + * {@link #add(double, double, double, double, ComplexSink) add(real, imaginary, 0, addend, action)} since + * {@code -0.0 + 0.0 = 0.0}. + * + * @param real Real part \( a \) of the complex number \( (a +ib) \). + * @param imaginary Imaginary part \( b \) of the complex number \( (a +ib) \). + * @param addend Value to be added to the complex number. + * @param action Consumer for the addition result. + * @param <R> the return type of the supplied action. + * @return the object returned by the supplied action. + * @see #add(double, double, double, double, ComplexSink) + */ + public static <R> R addImaginary(double real, double imaginary, double addend, ComplexSink<R> action) { + return action.apply(real, imaginary + addend); + } + /** * Returns a {@code Object} whose value is {@code (real1 - real2, imaginary1 - imaginary2)}. * Implements the formula: @@ -428,8 +480,8 @@ public final class ComplexFunctions { * * @param real1 Real part \( a \) of the first complex number \( (a +ib) \). * @param imaginary1 Imaginary part \( b \) of the first complex number \( (a +ib) \). - * @param real2 Real part \( a \) of the second complex number \( (a +ib) \). - * @param imaginary2 Imaginary part \( b \) of the second complex number \( (a +ib) \). + * @param real2 Real part \( c \) of the second complex number \( (c +id) \). + * @param imaginary2 Imaginary part \( d \) of the second complex number \( (c +id) \). * @param action Consumer for the subtraction result. * @param <R> the return type of the supplied action. * @return the object returned by the supplied action. @@ -442,6 +494,99 @@ public final class ComplexFunctions { imaginary1 - imaginary2); } + /** + * Returns a {@code Object} whose value is {@code (real - subtrahend, imaginary)}, + * with {@code subtrahend} interpreted as a real number. + * Implements the formula: + * + * <p>\[ (a + i b) - c = (a - c) + i b \] + * + * <p>This method is included for compatibility with ISO C99 which defines arithmetic between + * real-only and complex numbers.</p> + * + * @param real Real part \( a \) of the complex number \( (a +ib) \). + * @param imaginary Imaginary part \( b \) of the complex number \( (a +ib) \). + * @param subtrahend Value to be subtracted from the complex number. + * @param action Consumer for the subtraction result. + * @param <R> the return type of the supplied action. + * @return the object returned by the supplied action. + * @see #subtract(double, double, double, double, ComplexSink) + */ + public static <R> R subtract(double real, double imaginary, double subtrahend, ComplexSink<R> action) { + return action.apply(real - subtrahend, imaginary); + } + + /** + * Computes the result of the subtraction of an imaginary number from a complex number. + * Implements the formula: + * + * <p>\[ (a + i b) - i d = a + i (b - d) \] + * + * <p>This method is included for compatibility with ISO C99 which defines arithmetic between + * imaginary-only and complex numbers.</p> + * + * @param real Real part \( a \) of the complex number \( (a +ib) \). + * @param imaginary Imaginary part \( b \) of the complex number \( (a +ib) \). + * @param subtrahend Value to be subtracted from the complex number. + * @param action Consumer for the subtraction result. + * @param <R> the return type of the supplied action. + * @return the object returned by the supplied action. + * @see #subtract(double, double, double, double, ComplexSink) + */ + public static <R> R subtractImaginary(double real, double imaginary, double subtrahend, ComplexSink<R> action) { + return action.apply(real, imaginary - subtrahend); + } + + /** + * Computes the result of the subtraction of a complex number from a real number. + * Implements the formula: + * \[ c - (a + i b) = (c - a) - i b \] + * + * <p>This method is included for compatibility with ISO C99 which defines arithmetic between + * real-only and complex numbers.</p> + * + * <p>Note: This method inverts the sign of the imaginary component \( b \) if it is {@code 0.0}. + * The sign would not be inverted if subtracting from \( c + i 0 \) using + * {@link #subtract(double, double, double, double, ComplexSink) subtract(minuend, 0, real, imaginary, action)} since + * {@code 0.0 - 0.0 = 0.0}. + * + * @param minuend Value the complex number is to be subtracted from. + * @param real Real part \( a \) of the complex number \( (a +ib) \). + * @param imaginary Imaginary part \( b \) of the complex number \( (a +ib) \). + * @param action Consumer for the subtraction result. + * @param <R> the return type of the supplied action. + * @return the object returned by the supplied action. + * @see #subtract(double, double, double, double, ComplexSink) + */ + public static <R> R realSubtract(double minuend, double real, double imaginary, ComplexSink<R> action) { + return action.apply(minuend - real, -imaginary); + } + + /** + * Computes the result of the subtraction of a complex number from an imaginary number. + * Implements the formula: + * \[ i d - (a + i b) = -a + i (d - b) \] + * + * <p>This method is included for compatibility with ISO C99 which defines arithmetic between + * imaginary-only and complex numbers.</p> + * + * <p>Note: This method inverts the sign of the real component \( a \) if it is {@code 0.0}. + * The sign would not be inverted if subtracting from \( 0 + i d \) using + * {@link #subtract(double, double, double, double, ComplexSink) subtract(0, minuend, real, imaginary, action)} since + * {@code 0.0 - 0.0 = 0.0}. + * + * @param minuend Value the complex number is to be subtracted from. + * @param real Real part \( a \) of the complex number \( (a +ib) \). + * @param imaginary Imaginary part \( b \) of the complex number \( (a +ib) \). + * @param action Consumer for the subtraction result. + * @param <R> the return type of the supplied action. + * @return the object returned by the supplied action. + * @see #subtract(double, double, double, double, ComplexSink) + */ + public static <R> R imaginarySubtract(double minuend, double real, double imaginary, ComplexSink<R> action) { + return action.apply(-real, minuend - imaginary); + } + /** * Returns a {@code Object} whose value is {@code (real1 + i*imaginary1) * (real2 + i*imaginary2))}. * Implements the formula: @@ -453,7 +598,7 @@ public final class ComplexFunctions { * @param real1 Real part \( a \) of the first complex number \( (a +ib) \). * @param imaginary1 Imaginary part \( b \) of the first complex number \( (a +ib) \). * @param real2 Real part \( a \) of the second complex number \( (a +ib) \). - * @param imaginary2 Imaginary part \( b \) of the second complex number \( (a +ib) \). + * @param imaginary2 Imaginary part \( d \) of the second complex number \( (c +id) \). * @param action Consumer for the multiplication result. * @param <R> the return type of the supplied action. * @return the object returned by the supplied action. @@ -570,6 +715,70 @@ public final class ComplexFunctions { return Double.isNaN(value) ? Math.copySign(0.0, value) : value; } + /** + * Computes the result of the multiplication of a complex number and a real number. + * Implements the formula: + * + * <p>\[ (a + i b) c = (ac) + i (bc) \] + * + * <p>This method is included for compatibility with ISO C99 which defines arithmetic between + * real-only and complex numbers.</p> + * + * <p>Note: This method should be preferred over using + * {@link #multiply(double, double, double, double, ComplexSink) multiply(a, b, factor, 0, action)}. Multiplication + * can generate signed zeros if either the complex has zeros for the real + * and/or imaginary component, or if the factor is zero. The summation of signed zeros + * in {@link #multiply(double, double, double, double, ComplexSink)} may create zeros in the result that differ in sign + * from the equivalent call to multiply by a real-only number. + * + * @param real Real part \( a \) of the complex number \( (a +ib) \). + * @param imaginary Imaginary part \( b \) of the complex number \( (a +ib) \). + * @param factor Value to be multiplied by the complex number. + * @param action Consumer for the multiplication result. + * @param <R> the return type of the supplied action. + * @return the object returned by the supplied action + * @see #multiply(double, double, double, double, ComplexSink) + */ + public static <R> R multiply(double real, double imaginary, double factor, ComplexSink<R> action) { + return action.apply(real * factor, imaginary * factor); + } + + /** + * Computes the result of the multiplication of a complex number and an imaginary number. + * Implements the formula: + * + * <p>\[ (a + i b) id = (-bd) + i (ad) \] + * + * <p>This method can be used to compute the multiplication of the complex number \( z \) + * by \( i \) using a factor with magnitude 1.0. This should be used in preference to + * {@link #multiply(double, double, double, double, ComplexSink) multiply(Complex.I)} with or without {@link #negate(double, double, ComplexSink) negation}:</p> + * + * \[ \begin{aligned} + * iz &= (-b + i a) \\ + * -iz &= (b - i a) \end{aligned} \] + * + * <p>This method is included for compatibility with ISO C99 which defines arithmetic between + * imaginary-only and complex numbers.</p> + * + * <p>Note: This method should be preferred over using + * {@link #multiply(double, double, double, double, ComplexSink) multiply(a, b, 0, factor, action)}. Multiplication + * can generate signed zeros if either the complex has zeros for the real + * and/or imaginary component, or if the factor is zero. The summation of signed zeros + * in {@link #multiply(double, double, double, double, ComplexSink)} may create zeros in the result that differ in sign + * from the equivalent call to multiply by an imaginary-only number. + * + * @param real Real part \( a \) of the complex number \( (a +ib) \). + * @param imaginary Imaginary part \( b \) of the complex number \( (a +ib) \). + * @param factor Value to be multiplied by the complex number. + * @param action Consumer for the multiplication result. + * @param <R> the return type of the supplied action. + * @return the object returned by the supplied action. + * @see #multiply(double, double, double, double, ComplexSink) + */ + public static <R> R multiplyImaginary(double real, double imaginary, double factor, ComplexSink<R> action) { + return action.apply(-imaginary * factor, real * factor); + } + /** * Returns a {@code Object} whose value is {@code (real1 + i*imaginary1)/(real2 + i*imaginary2)}. * Implements the formula: @@ -579,18 +788,18 @@ public final class ComplexFunctions { * <p>Re-calculates NaN result values to recover infinities as specified in C99 standard G.5.1. * * <p>Note: In the event of divide by zero this method produces the same result - * as dividing by a real-only zero using (add divide reference) + * as dividing by a real-only zero using {@link #divide(double, double, double, ComplexSink)}. * * @param real1 Real part \( a \) of the first complex number \( (a +ib) \). * @param imaginary1 Imaginary part \( b \) of the first complex number \( (a +ib) \). - * @param real2 Real part \( a \) of the second complex number \( (a +ib) \). - * @param imaginary2 Imaginary part \( b \) of the second complex number \( (a +ib) \). + * @param real2 Real part \( c \) of the second complex number \( (c +id) \). + * @param imaginary2 Imaginary part \( d \) of the second complex number \( (c +id) \). * @param action Consumer for the division result. * @param <R> the return type of the supplied action. * @return the object returned by the supplied action. * @see <a href="http://mathworld.wolfram.com/ComplexDivision.html">Complex Division</a> + * @see #divide(double, double, double, ComplexSink) */ - //TODO - add divide reference once moved to ComplexFunctions public static <R> R divide(double real1, double imaginary1, double real2, double imaginary2, ComplexSink<R> action) { @@ -651,6 +860,71 @@ public final class ComplexFunctions { return action.apply(x, y); } + /** + * Computes the result of the division of a complex number by a real number. + * Implements the formula: + * + * <p>\[ \frac{a + i b}{c} = \frac{a}{c} + i \frac{b}{c} \] + * + * <p>This method is included for compatibility with ISO C99 which defines arithmetic between + * real-only and complex numbers.</p> + * + * <p>Note: This method should be preferred over using + * {@link #divide(double, double, double, double, ComplexSink) divide(a, b, divisor, 0, action)}. Division + * can generate signed zeros if the complex has zeros for the real + * and/or imaginary component, or the divisor is infinite. The summation of signed zeros + * in {@link #divide(double, double, double, double, ComplexSink)} may create zeros in the result that differ in sign + * from the equivalent call to divide by a real-only number. + * + * @param real Real part \( a \) of the complex number \( (a +ib) \). + * @param imaginary Imaginary part \( b \) of the complex number \( (a +ib) \). + * @param divisor Value by which the complex number is to be divided. + * @param action Consumer for the division result. + * @param <R> the return type of the supplied action. + * @return the object returned by the supplied action. + * @see #divide(double, double, double, double, ComplexSink) + */ + public static <R> R divide(double real, double imaginary, double divisor, ComplexSink<R> action) { + return action.apply(real / divisor, imaginary / divisor); + } + + /** + * Computes the result of the division of a complex number by an imaginary number. + * Implements the formula: + * + * <p>\[ \frac{a + i b}{id} = \frac{b}{d} - i \frac{a}{d} \] + * + * <p>This method is included for compatibility with ISO C99 which defines arithmetic between + * imaginary-only and complex numbers.</p> + * + * <p>Note: This method should be preferred over using + * {@link #divide(double, double, double, double, ComplexSink) divide(a, b, 0, divisor, action)}. Division + * can generate signed zeros if the complex has zeros for the real + * and/or imaginary component, or the divisor is infinite. The summation of signed zeros + * in {@link #divide(double, double, double, double, ComplexSink)} may create zeros in the result that differ in sign + * from the equivalent call to divide by an imaginary-only number. + * + * <p>Warning: This method will generate a different result from + * {@link #divide(double, double, double, double, ComplexSink) divide(a, b, 0, divisor, action)} if the divisor is zero. + * In this case the divide method using a zero-valued Complex will produce the same result + * as dividing by a real-only zero. The output from dividing by imaginary zero will create + * infinite and NaN values in the same component parts as the output from + * {@code divide(real, imaginary, Complex.ZERO, action).multiplyImaginary(real, imaginary, 1, action)}, however the sign + * of some infinite values may be negated. + * + * @param real Real part \( a \) of the complex number \( (a +ib) \). + * @param imaginary Imaginary part \( b \) of the complex number \( (a +ib) \). + * @param divisor Value by which the complex number is to be divided. + * @param action Consumer for the division result. + * @param <R> the return type of the supplied action. + * @return the object returned by the supplied action. + * @see #divide(double, double, double, double, ComplexSink) + * @see #divide(double, double, double, ComplexSink) + */ + public static <R> R divideImaginary(double real, double imaginary, double divisor, ComplexSink<R> action) { + return action.apply(imaginary / divisor, -real / divisor); + } + /** * Returns the * <a href="http://mathworld.wolfram.com/ExponentialFunction.html"> @@ -945,8 +1219,8 @@ public final class ComplexFunctions { * * @param real1 Real part \( a \) of the first complex number \( (a +ib) \). * @param imaginary1 Imaginary part \( b \) of the first complex number \( (a +ib) \). - * @param real2 Real part \( a \) of the second complex number \( (a +ib) \). - * @param imaginary2 Imaginary part \( b \) of the second complex number \( (a +ib) \). + * @param real2 Real part \( c \) of the second complex number \( (c +id) \). + * @param imaginary2 Imaginary part \( d \) of the second complex number \( (c +id) \). * @param action Consumer for the power result. * @param <R> the return type of the supplied action. * @return the object returned by the supplied action. @@ -971,6 +1245,42 @@ public final class ComplexFunctions { return log(real1, imaginary1, (x, y) -> multiply(x, y, real2, imaginary2, (a, b) -> exp(a, b, action))); } + /** + * Returns the complex power of the complex number raised to the power of {@code x}, + * with {@code x} interpreted as a real number. + * Implements the formula: + * + * <p>\[ z^x = e^{x \ln(z)} \] + * + * <p>If the complex number is zero then this method returns zero if {@code x} is positive; + * otherwise it returns NaN + iNaN. + * + * @param real Real part \( a \) of the complex number \( (a +ib) \). + * @param imaginary Imaginary part \( b \) of the complex number \( (a +ib) \). + * @param x The exponent to which the complex number is to be raised. + * @param action Consumer for the power result. + * @param <R> the return type of the supplied action. + * @return the object returned by the supplied action. + * @see #log(double, double, ComplexSink) + * @see #multiply(double, double, double, ComplexSink) + * @see #exp(double, double, ComplexSink) + * @see #pow(double, double, double, double, ComplexSink) + * @see <a href="http://functions.wolfram.com/ElementaryFunctions/Power/">Power</a> + */ + public static <R> R pow(double real, double imaginary, double x, ComplexSink<R> action) { + if (real == 0 && + imaginary == 0) { + // This value is zero. Test the other. + if (x > 0) { + // 0 raised to positive number is 0 + return action.apply(0, 0); + } + // 0 raised to anything else is NaN + return action.apply(Double.NaN, Double.NaN); + } + return log(real, imaginary, (y, z) -> multiply(y, z, x, (a, b) -> exp(a, b, action))); + } + /** * Returns the * <a href="http://mathworld.wolfram.com/SquareRoot.html"> diff --git a/commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/ComplexUnaryOperator.java b/commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/ComplexScalarFunction.java similarity index 58% copy from commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/ComplexUnaryOperator.java copy to commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/ComplexScalarFunction.java index a9bae229..b339f5a3 100644 --- a/commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/ComplexUnaryOperator.java +++ b/commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/ComplexScalarFunction.java @@ -18,27 +18,30 @@ package org.apache.commons.numbers.complex; /** - * Represents a unary operation on a Cartesian form of a complex number \( a + ib \) - * where \( a \) and \( b \) are real numbers represented as two {@code double} - * parts. The operation creates a complex number result; the result is supplied - * to a terminating consumer function which may return an object representation - * of the complex result. + * Represents a binary operation on a Cartesian form of a complex number \( a + ib \) + * and a {@code double} scalar operand, where \( a \) and \( b \) are real numbers represented as two {@code double} + * The operation creates a complex number result; the result is supplied to a terminating consumer function + * which may return an object representation of the complex result. * * <p>This is a functional interface whose functional method is - * {@link #apply(double, double, ComplexSink)}. + * {@link #apply(double, double, double, ComplexSink)}. * * @param <R> The type of the complex result * @since 1.1 */ @FunctionalInterface -public interface ComplexUnaryOperator<R> { +public interface ComplexScalarFunction<R> { /** - * Represents an operator that accepts real and imaginary parts of a complex number and supplies the complex result to the provided consumer. + * Represents a binary function that accepts a Complex number's real and imaginary parts + * and a double operand to produce a Complex result. + * The complex result is supplied to the provided consumer. + * * @param real Real part \( a \) of the complex number \( (a +ib) \). * @param imaginary Imaginary part \( b \) of the complex number \( (a +ib) \). - * @param out Consumer for the complex result. + * @param operand Scalar operand. + * @param action Consumer for the complex result. * @return the object returned by the provided consumer. */ - R apply(double real, double imaginary, ComplexSink<R> out); + R apply(double real, double imaginary, double operand, ComplexSink<R> action); } diff --git a/commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/ComplexUnaryOperator.java b/commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/ComplexUnaryOperator.java index a9bae229..dbfc53d6 100644 --- a/commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/ComplexUnaryOperator.java +++ b/commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/ComplexUnaryOperator.java @@ -35,10 +35,11 @@ public interface ComplexUnaryOperator<R> { /** * Represents an operator that accepts real and imaginary parts of a complex number and supplies the complex result to the provided consumer. + * * @param real Real part \( a \) of the complex number \( (a +ib) \). * @param imaginary Imaginary part \( b \) of the complex number \( (a +ib) \). - * @param out Consumer for the complex result. + * @param action Consumer for the complex result. * @return the object returned by the provided consumer. */ - R apply(double real, double imaginary, ComplexSink<R> out); + R apply(double real, double imaginary, ComplexSink<R> action); } diff --git a/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/CStandardTest.java b/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/CStandardTest.java index 620e3a92..b739c359 100644 --- a/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/CStandardTest.java +++ b/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/CStandardTest.java @@ -980,27 +980,27 @@ class CStandardTest { final double re = next(rng); final double im = next(rng); final Complex z = complex(re, im); - final Complex iz = z.multiplyImaginary(1); + final Complex iz = TestUtils.assertSame(z, 1, "multiplyImaginary", Complex::multiplyImaginary, ComplexFunctions::multiplyImaginary); - Complex actual = TestUtils.assertSame(z, "asin", Complex::asin, ComplexFunctions::asin); - Complex expected = TestUtils.assertSame(iz, "asinh", Complex::asinh, ComplexFunctions::asinh); - assertComplex(actual, expected.multiplyImaginary(-1)); + Complex c1 = TestUtils.assertSame(z, "asin", Complex::asin, ComplexFunctions::asin); + Complex c2 = TestUtils.assertSame(iz, "asinh", Complex::asinh, ComplexFunctions::asinh); + assertComplex(c1, TestUtils.assertSame(c2, -1, "multiplyImaginary", Complex::multiplyImaginary, ComplexFunctions::multiplyImaginary)); - actual = TestUtils.assertSame(z, "atan", Complex::atan, ComplexFunctions::atan); - expected = TestUtils.assertSame(iz, "atanh", Complex::atanh, ComplexFunctions::atanh); - assertComplex(actual, expected.multiplyImaginary(-1)); + c1 = TestUtils.assertSame(z, "atan", Complex::atan, ComplexFunctions::atan); + c2 = TestUtils.assertSame(iz, "atanh", Complex::atanh, ComplexFunctions::atanh); + assertComplex(c1, TestUtils.assertSame(c2, -1, "multiplyImaginary", Complex::multiplyImaginary, ComplexFunctions::multiplyImaginary)); - actual = TestUtils.assertSame(z, "cos", Complex::cos, ComplexFunctions::cos); - expected = TestUtils.assertSame(iz, "cosh", Complex::cosh, ComplexFunctions::cosh); - assertComplex(actual, expected); + c1 = TestUtils.assertSame(z, "cos", Complex::cos, ComplexFunctions::cos); + c2 = TestUtils.assertSame(iz, "cosh", Complex::cosh, ComplexFunctions::cosh); + assertComplex(c1, c2); - actual = TestUtils.assertSame(z, "sin", Complex::sin, ComplexFunctions::sin); - expected = TestUtils.assertSame(iz, "sinh", Complex::sinh, ComplexFunctions::sinh); - assertComplex(actual, expected.multiplyImaginary(-1)); + c1 = TestUtils.assertSame(z, "sin", Complex::sin, ComplexFunctions::sin); + c2 = TestUtils.assertSame(iz, "sinh", Complex::sinh, ComplexFunctions::sinh); + assertComplex(c1, TestUtils.assertSame(c2, -1, "multiplyImaginary", Complex::multiplyImaginary, ComplexFunctions::multiplyImaginary)); - actual = TestUtils.assertSame(z, "tan", Complex::tan, ComplexFunctions::tan); - expected = TestUtils.assertSame(iz, "tanh", Complex::tanh, ComplexFunctions::tanh); - assertComplex(actual, expected.multiplyImaginary(-1)); + c1 = TestUtils.assertSame(z, "tan", Complex::tan, ComplexFunctions::tan); + c2 = TestUtils.assertSame(iz, "tanh", Complex::tanh, ComplexFunctions::tanh); + assertComplex(c1, TestUtils.assertSame(c2, -1, "multiplyImaginary", Complex::multiplyImaginary, ComplexFunctions::multiplyImaginary)); } } @@ -1056,8 +1056,8 @@ class CStandardTest { {1.40905821964671, 1.4090583434236112}, {1.912164268932753, 1.9121638616231227}}) { final Complex z = complex(pair[0], pair[1]); - assertAbs(z.abs(), z.multiplyImaginary(1)); - Assertions.assertEquals(z.abs(), z.multiplyImaginary(1).abs(), "Expected |z| == |iz|"); + final Complex iz = TestUtils.assertSame(z, 1, "multiplyImaginary", Complex::multiplyImaginary, ComplexFunctions::multiplyImaginary); + Assertions.assertEquals(z.abs(), iz.abs(), "Expected |z| == |iz|"); } // Test with a range of numbers. @@ -1267,7 +1267,8 @@ class CStandardTest { } assertComplex(infZero, name, operation1, operation2, infZero, type); for (final double y : nonZeroFinite) { - assertComplex(complex(inf, y), name, operation1, operation2, Complex.ofCis(y).multiply(inf), type); + final Complex expected = TestUtils.assertSame(Complex.ofCis(y), inf, "multiply", Complex::multiply, ComplexFunctions::multiply); + assertComplex(complex(inf, y), name, operation1, operation2, expected, type); } assertComplex(infInf, name, operation1, operation2, infNaN, type, UnspecifiedSign.REAL); assertComplex(infNaN, name, operation1, operation2, infNaN, type); @@ -1302,7 +1303,8 @@ class CStandardTest { assertComplex(infZero, name, operation1, operation2, infZero, type); // Note: Error in the ISO C99 reference to use positive finite y but the zero case is different for (final double y : nonZeroFinite) { - assertComplex(complex(inf, y), name, operation1, operation2, Complex.ofCis(y).multiply(inf), type); + final Complex expected = TestUtils.assertSame(Complex.ofCis(y), inf, "multiply", Complex::multiply, ComplexFunctions::multiply); + assertComplex(complex(inf, y), name, operation1, operation2, expected, type); } assertComplex(infInf, name, operation1, operation2, infNaN, type, UnspecifiedSign.REAL); assertComplex(infNaN, name, operation1, operation2, infNaN, type, UnspecifiedSign.REAL); @@ -1369,10 +1371,12 @@ class CStandardTest { } assertComplex(infZero, name, operation1, operation2, infZero); for (final double y : finite) { - assertComplex(complex(-inf, y), name, operation1, operation2, Complex.ofCis(y).multiply(0.0)); + final Complex expected = TestUtils.assertSame(Complex.ofCis(y), 0.0, "multiply", Complex::multiply, ComplexFunctions::multiply); + assertComplex(complex(-inf, y), name, operation1, operation2, expected); } for (final double y : nonZeroFinite) { - assertComplex(complex(inf, y), name, operation1, operation2, Complex.ofCis(y).multiply(inf)); + final Complex expected = TestUtils.assertSame(Complex.ofCis(y), inf, "multiply", Complex::multiply, ComplexFunctions::multiply); + assertComplex(complex(inf, y), name, operation1, operation2, expected); } assertComplex(negInfInf, name, operation1, operation2, Complex.ZERO, UnspecifiedSign.REAL_IMAGINARY); assertComplex(infInf, name, operation1, operation2, infNaN, UnspecifiedSign.REAL); diff --git a/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/ComplexTest.java b/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/ComplexTest.java index 02653eb6..3d7410e0 100644 --- a/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/ComplexTest.java +++ b/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/ComplexTest.java @@ -553,44 +553,47 @@ class ComplexTest { void testAddReal() { final Complex x = Complex.ofCartesian(3.0, 4.0); final double y = 5.0; - final Complex z = x.add(y); + final Complex z = TestUtils.assertSame(x, y, "add", Complex::add, ComplexFunctions::add); Assertions.assertEquals(8.0, z.getReal()); Assertions.assertEquals(4.0, z.getImaginary()); // Equivalent - Assertions.assertEquals(z, x.add(ofReal(y))); + final Complex actual = TestUtils.assertSame(x, ofReal(y), "add", Complex::add, ComplexFunctions::add); + Assertions.assertEquals(z, actual); } @Test void testAddRealNaN() { final Complex x = Complex.ofCartesian(3.0, 4.0); final double y = nan; - final Complex z = x.add(y); + final Complex z = TestUtils.assertSame(x, y, "add", Complex::add, ComplexFunctions::add); Assertions.assertEquals(nan, z.getReal()); Assertions.assertEquals(4.0, z.getImaginary()); // Equivalent - Assertions.assertEquals(z, x.add(ofReal(y))); + final Complex actual = TestUtils.assertSame(x, ofReal(y), "add", Complex::add, ComplexFunctions::add); + Assertions.assertEquals(z, actual); } @Test void testAddRealInf() { final Complex x = Complex.ofCartesian(3.0, 4.0); final double y = inf; - final Complex z = x.add(y); + final Complex z = TestUtils.assertSame(x, y, "add", Complex::add, ComplexFunctions::add); Assertions.assertEquals(inf, z.getReal()); Assertions.assertEquals(4.0, z.getImaginary()); // Equivalent - Assertions.assertEquals(z, x.add(ofReal(y))); + final Complex actual = TestUtils.assertSame(x, ofReal(y), "add", Complex::add, ComplexFunctions::add); + Assertions.assertEquals(z, actual); } @Test void testAddRealWithNegZeroImaginary() { final Complex x = Complex.ofCartesian(3.0, -0.0); final double y = 5.0; - final Complex z = x.add(y); + final Complex z = TestUtils.assertSame(x, y, "add", Complex::add, ComplexFunctions::add); Assertions.assertEquals(8.0, z.getReal()); Assertions.assertEquals(-0.0, z.getImaginary(), "Expected sign preservation"); // Sign-preservation is a problem: -0.0 + 0.0 == 0.0 - final Complex z2 = x.add(ofReal(y)); + final Complex z2 = TestUtils.assertSame(x, ofReal(y), "add", Complex::add, ComplexFunctions::add); Assertions.assertEquals(8.0, z2.getReal()); Assertions.assertEquals(0.0, z2.getImaginary(), "Expected no-sign preservation"); } @@ -599,44 +602,47 @@ class ComplexTest { void testAddImaginary() { final Complex x = Complex.ofCartesian(3.0, 4.0); final double y = 5.0; - final Complex z = x.addImaginary(y); + final Complex z = TestUtils.assertSame(x, y, "addImaginary", Complex::addImaginary, ComplexFunctions::addImaginary); Assertions.assertEquals(3.0, z.getReal()); Assertions.assertEquals(9.0, z.getImaginary()); // Equivalent - Assertions.assertEquals(z, x.add(ofImaginary(y))); + final Complex actual = TestUtils.assertSame(x, ofImaginary(y), "add", Complex::add, ComplexFunctions::add); + Assertions.assertEquals(z, actual); } @Test void testAddImaginaryNaN() { final Complex x = Complex.ofCartesian(3.0, 4.0); final double y = nan; - final Complex z = x.addImaginary(y); + final Complex z = TestUtils.assertSame(x, y, "addImaginary", Complex::addImaginary, ComplexFunctions::addImaginary); Assertions.assertEquals(3.0, z.getReal()); Assertions.assertEquals(nan, z.getImaginary()); // Equivalent - Assertions.assertEquals(z, x.add(ofImaginary(y))); + final Complex actual = TestUtils.assertSame(x, ofImaginary(y), "add", Complex::add, ComplexFunctions::add); + Assertions.assertEquals(z, actual); } @Test void testAddImaginaryInf() { final Complex x = Complex.ofCartesian(3.0, 4.0); final double y = inf; - final Complex z = x.addImaginary(y); + final Complex z = TestUtils.assertSame(x, y, "addImaginary", Complex::addImaginary, ComplexFunctions::addImaginary); Assertions.assertEquals(3.0, z.getReal()); Assertions.assertEquals(inf, z.getImaginary()); // Equivalent - Assertions.assertEquals(z, x.add(ofImaginary(y))); + final Complex actual = TestUtils.assertSame(x, ofImaginary(y), "add", Complex::add, ComplexFunctions::add); + Assertions.assertEquals(z, actual); } @Test void testAddImaginaryWithNegZeroReal() { final Complex x = Complex.ofCartesian(-0.0, 4.0); final double y = 5.0; - final Complex z = x.addImaginary(y); + final Complex z = TestUtils.assertSame(x, y, "addImaginary", Complex::addImaginary, ComplexFunctions::addImaginary); Assertions.assertEquals(-0.0, z.getReal(), "Expected sign preservation"); Assertions.assertEquals(9.0, z.getImaginary()); // Sign-preservation is a problem: -0.0 + 0.0 == 0.0 - final Complex z2 = x.add(ofImaginary(y)); + final Complex z2 = TestUtils.assertSame(x, ofImaginary(y), "add", Complex::add, ComplexFunctions::add); Assertions.assertEquals(0.0, z2.getReal(), "Expected no-sign preservation"); Assertions.assertEquals(9.0, z2.getImaginary()); } @@ -667,178 +673,194 @@ class ComplexTest { void testSubtractReal() { final Complex x = Complex.ofCartesian(3.0, 4.0); final double y = 5.0; - final Complex z = x.subtract(y); + final Complex z = TestUtils.assertSame(x, y, "subtract", Complex::subtract, ComplexFunctions::subtract); Assertions.assertEquals(-2.0, z.getReal()); Assertions.assertEquals(4.0, z.getImaginary()); // Equivalent - Assertions.assertEquals(z, x.subtract(ofReal(y))); + final Complex actual = TestUtils.assertSame(x, ofReal(y), "subtract", Complex::subtract, ComplexFunctions::subtract); + Assertions.assertEquals(z, actual); } @Test void testSubtractRealNaN() { final Complex x = Complex.ofCartesian(3.0, 4.0); final double y = nan; - final Complex z = x.subtract(y); + final Complex z = TestUtils.assertSame(x, y, "subtract", Complex::subtract, ComplexFunctions::subtract); Assertions.assertEquals(nan, z.getReal()); Assertions.assertEquals(4.0, z.getImaginary()); // Equivalent - Assertions.assertEquals(z, x.subtract(ofReal(y))); + final Complex actual = TestUtils.assertSame(x, ofReal(y), "subtract", Complex::subtract, ComplexFunctions::subtract); + Assertions.assertEquals(z, actual); } @Test void testSubtractRealInf() { final Complex x = Complex.ofCartesian(3.0, 4.0); final double y = inf; - final Complex z = x.subtract(y); + final Complex z = TestUtils.assertSame(x, y, "subtract", Complex::subtract, ComplexFunctions::subtract); Assertions.assertEquals(-inf, z.getReal()); Assertions.assertEquals(4.0, z.getImaginary()); // Equivalent - Assertions.assertEquals(z, x.subtract(ofReal(y))); + final Complex actual = TestUtils.assertSame(x, ofReal(y), "subtract", Complex::subtract, ComplexFunctions::subtract); + Assertions.assertEquals(z, actual); } @Test void testSubtractRealWithNegZeroImaginary() { final Complex x = Complex.ofCartesian(3.0, -0.0); final double y = 5.0; - final Complex z = x.subtract(y); + final Complex z = TestUtils.assertSame(x, y, "subtract", Complex::subtract, ComplexFunctions::subtract); Assertions.assertEquals(-2.0, z.getReal()); Assertions.assertEquals(-0.0, z.getImaginary()); // Equivalent // Sign-preservation is not a problem: -0.0 - 0.0 == -0.0 - Assertions.assertEquals(z, x.subtract(ofReal(y))); + final Complex actual = TestUtils.assertSame(x, ofReal(y), "subtract", Complex::subtract, ComplexFunctions::subtract); + Assertions.assertEquals(z, actual); } @Test void testSubtractImaginary() { final Complex x = Complex.ofCartesian(3.0, 4.0); final double y = 5.0; - final Complex z = x.subtractImaginary(y); + final Complex z = TestUtils.assertSame(x, y, "subtractImaginary", Complex::subtractImaginary, ComplexFunctions::subtractImaginary); Assertions.assertEquals(3.0, z.getReal()); Assertions.assertEquals(-1.0, z.getImaginary()); // Equivalent - Assertions.assertEquals(z, x.subtract(ofImaginary(y))); + final Complex actual = TestUtils.assertSame(x, ofImaginary(y), "subtract", Complex::subtract, ComplexFunctions::subtract); + Assertions.assertEquals(z, actual); } @Test void testSubtractImaginaryNaN() { final Complex x = Complex.ofCartesian(3.0, 4.0); final double y = nan; - final Complex z = x.subtractImaginary(y); + final Complex z = TestUtils.assertSame(x, y, "subtractImaginary", Complex::subtractImaginary, ComplexFunctions::subtractImaginary); Assertions.assertEquals(3.0, z.getReal()); Assertions.assertEquals(nan, z.getImaginary()); // Equivalent - Assertions.assertEquals(z, x.subtract(ofImaginary(y))); + final Complex actual = TestUtils.assertSame(x, ofImaginary(y), "subtract", Complex::subtract, ComplexFunctions::subtract); + Assertions.assertEquals(z, actual); } @Test void testSubtractImaginaryInf() { final Complex x = Complex.ofCartesian(3.0, 4.0); final double y = inf; - final Complex z = x.subtractImaginary(y); + final Complex z = TestUtils.assertSame(x, y, "subtractImaginary", Complex::subtractImaginary, ComplexFunctions::subtractImaginary); Assertions.assertEquals(3.0, z.getReal()); Assertions.assertEquals(-inf, z.getImaginary()); // Equivalent - Assertions.assertEquals(z, x.subtract(ofImaginary(y))); + final Complex actual = TestUtils.assertSame(x, ofImaginary(y), "subtract", Complex::subtract, ComplexFunctions::subtract); + Assertions.assertEquals(z, actual); } @Test void testSubtractImaginaryWithNegZeroReal() { final Complex x = Complex.ofCartesian(-0.0, 4.0); final double y = 5.0; - final Complex z = x.subtractImaginary(y); + final Complex z = TestUtils.assertSame(x, y, "subtractImaginary", Complex::subtractImaginary, ComplexFunctions::subtractImaginary); Assertions.assertEquals(-0.0, z.getReal()); Assertions.assertEquals(-1.0, z.getImaginary()); // Equivalent // Sign-preservation is not a problem: -0.0 - 0.0 == -0.0 - Assertions.assertEquals(z, x.subtract(ofImaginary(y))); + final Complex actual = TestUtils.assertSame(x, ofImaginary(y), "subtract", Complex::subtract, ComplexFunctions::subtract); + Assertions.assertEquals(z, actual); } @Test void testSubtractFromReal() { final Complex x = Complex.ofCartesian(3.0, 4.0); final double y = 5.0; - final Complex z = x.subtractFrom(y); + final Complex z = TestUtils.assertSame(x, y, "subtractFrom", Complex::subtractFrom, TestUtils::subtractFrom); Assertions.assertEquals(2.0, z.getReal()); Assertions.assertEquals(-4.0, z.getImaginary()); // Equivalent - Assertions.assertEquals(z, ofReal(y).subtract(x)); + final Complex actual = TestUtils.assertSame(ofReal(y), x, "subtract", Complex::subtract, ComplexFunctions::subtract); + Assertions.assertEquals(z, actual); } @Test void testSubtractFromRealNaN() { final Complex x = Complex.ofCartesian(3.0, 4.0); final double y = nan; - final Complex z = x.subtractFrom(y); + final Complex z = TestUtils.assertSame(x, y, "subtractFrom", Complex::subtractFrom, TestUtils::subtractFrom); Assertions.assertEquals(nan, z.getReal()); Assertions.assertEquals(-4.0, z.getImaginary()); // Equivalent - Assertions.assertEquals(z, ofReal(y).subtract(x)); + final Complex actual = TestUtils.assertSame(ofReal(y), x, "subtract", Complex::subtract, ComplexFunctions::subtract); + Assertions.assertEquals(z, actual); } @Test void testSubtractFromRealInf() { final Complex x = Complex.ofCartesian(3.0, 4.0); final double y = inf; - final Complex z = x.subtractFrom(y); + final Complex z = TestUtils.assertSame(x, y, "subtractFrom", Complex::subtractFrom, TestUtils::subtractFrom); Assertions.assertEquals(inf, z.getReal()); Assertions.assertEquals(-4.0, z.getImaginary()); // Equivalent - Assertions.assertEquals(z, ofReal(y).subtract(x)); + final Complex actual = TestUtils.assertSame(ofReal(y), x, "subtract", Complex::subtract, ComplexFunctions::subtract); + Assertions.assertEquals(z, actual); } @Test void testSubtractFromRealWithPosZeroImaginary() { final Complex x = Complex.ofCartesian(3.0, 0.0); final double y = 5.0; - final Complex z = x.subtractFrom(y); + final Complex z = TestUtils.assertSame(x, y, "subtractFrom", Complex::subtractFrom, TestUtils::subtractFrom); Assertions.assertEquals(2.0, z.getReal()); Assertions.assertEquals(-0.0, z.getImaginary(), "Expected sign inversion"); // Sign-inversion is a problem: 0.0 - 0.0 == 0.0 - Assertions.assertNotEquals(z, ofReal(y).subtract(x)); + final Complex actual = TestUtils.assertSame(ofReal(y), x, "subtract", Complex::subtract, ComplexFunctions::subtract); + Assertions.assertNotEquals(z, actual); } @Test void testSubtractFromImaginary() { final Complex x = Complex.ofCartesian(3.0, 4.0); final double y = 5.0; - final Complex z = x.subtractFromImaginary(y); + final Complex z = TestUtils.assertSame(x, y, "subtractFromImaginary", Complex::subtractFromImaginary, TestUtils::subtractFromImaginary); Assertions.assertEquals(-3.0, z.getReal()); Assertions.assertEquals(1.0, z.getImaginary()); // Equivalent - Assertions.assertEquals(z, ofImaginary(y).subtract(x)); + final Complex actual = TestUtils.assertSame(ofImaginary(y), x, "subtract", Complex::subtract, ComplexFunctions::subtract); + Assertions.assertEquals(z, actual); } @Test void testSubtractFromImaginaryNaN() { final Complex x = Complex.ofCartesian(3.0, 4.0); final double y = nan; - final Complex z = x.subtractFromImaginary(y); + final Complex z = TestUtils.assertSame(x, y, "subtractFromImaginary", Complex::subtractFromImaginary, TestUtils::subtractFromImaginary); Assertions.assertEquals(-3.0, z.getReal()); Assertions.assertEquals(nan, z.getImaginary()); // Equivalent - Assertions.assertEquals(z, ofImaginary(y).subtract(x)); + final Complex actual = TestUtils.assertSame(ofImaginary(y), x, "subtract", Complex::subtract, ComplexFunctions::subtract); + Assertions.assertEquals(z, actual); } @Test void testSubtractFromImaginaryInf() { final Complex x = Complex.ofCartesian(3.0, 4.0); final double y = inf; - final Complex z = x.subtractFromImaginary(y); + final Complex z = TestUtils.assertSame(x, y, "subtractFromImaginary", Complex::subtractFromImaginary, TestUtils::subtractFromImaginary); Assertions.assertEquals(-3.0, z.getReal()); Assertions.assertEquals(inf, z.getImaginary()); // Equivalent - Assertions.assertEquals(z, ofImaginary(y).subtract(x)); + final Complex actual = TestUtils.assertSame(ofImaginary(y), x, "subtract", Complex::subtract, ComplexFunctions::subtract); + Assertions.assertEquals(z, actual); } @Test void testSubtractFromImaginaryWithPosZeroReal() { final Complex x = Complex.ofCartesian(0.0, 4.0); final double y = 5.0; - final Complex z = x.subtractFromImaginary(y); + final Complex z = TestUtils.assertSame(x, y, "subtractFromImaginary", Complex::subtractFromImaginary, TestUtils::subtractFromImaginary); Assertions.assertEquals(-0.0, z.getReal(), "Expected sign inversion"); Assertions.assertEquals(1.0, z.getImaginary()); // Sign-inversion is a problem: 0.0 - 0.0 == 0.0 - Assertions.assertNotEquals(z, ofImaginary(y).subtract(x)); + final Complex actual = TestUtils.assertSame(ofImaginary(y), x, "subtract", Complex::subtract, ComplexFunctions::subtract); + Assertions.assertNotEquals(z, actual); } @Test @@ -872,62 +894,68 @@ class ComplexTest { void testMultiplyReal() { final Complex x = Complex.ofCartesian(3.0, 4.0); final double y = 2.0; - Complex z = x.multiply(y); + Complex z = TestUtils.assertSame(x, y, "multiply", Complex::multiply, ComplexFunctions::multiply); Assertions.assertEquals(6.0, z.getReal()); Assertions.assertEquals(8.0, z.getImaginary()); // Equivalent - Assertions.assertEquals(z, x.multiply(ofReal(y))); + Complex actual = TestUtils.assertSame(x, ofReal(y), "multiply", Complex::multiply, ComplexFunctions::multiply); + Assertions.assertEquals(z, actual); - z = x.multiply(-y); + z = TestUtils.assertSame(x, -y, "multiply", Complex::multiply, ComplexFunctions::multiply); Assertions.assertEquals(-6.0, z.getReal()); Assertions.assertEquals(-8.0, z.getImaginary()); // Equivalent - Assertions.assertEquals(z, x.multiply(ofReal(-y))); + actual = TestUtils.assertSame(x, ofReal(-y), "multiply", Complex::multiply, ComplexFunctions::multiply); + Assertions.assertEquals(z, actual); } @Test void testMultiplyRealNaN() { final Complex x = Complex.ofCartesian(3.0, 4.0); final double y = nan; - final Complex z = x.multiply(y); + final Complex z = TestUtils.assertSame(x, y, "multiply", Complex::multiply, ComplexFunctions::multiply); Assertions.assertEquals(nan, z.getReal()); Assertions.assertEquals(nan, z.getImaginary()); // Equivalent - Assertions.assertEquals(z, x.multiply(ofReal(y))); + final Complex actual = TestUtils.assertSame(x, ofReal(y), "multiply", Complex::multiply, ComplexFunctions::multiply); + Assertions.assertEquals(z, actual); } @Test void testMultiplyRealInf() { final Complex x = Complex.ofCartesian(3.0, 4.0); final double y = inf; - Complex z = x.multiply(y); + Complex z = TestUtils.assertSame(x, y, "multiply", Complex::multiply, ComplexFunctions::multiply); Assertions.assertEquals(inf, z.getReal()); Assertions.assertEquals(inf, z.getImaginary()); // Equivalent - Assertions.assertEquals(z, x.multiply(ofReal(y))); + Complex actual = TestUtils.assertSame(x, ofReal(y), "multiply", Complex::multiply, ComplexFunctions::multiply); + Assertions.assertEquals(z, actual); - z = x.multiply(-y); + z = TestUtils.assertSame(x, -y, "multiply", Complex::multiply, ComplexFunctions::multiply); Assertions.assertEquals(-inf, z.getReal()); Assertions.assertEquals(-inf, z.getImaginary()); // Equivalent - Assertions.assertEquals(z, x.multiply(ofReal(-y))); + actual = TestUtils.assertSame(x, ofReal(-y), "multiply", Complex::multiply, ComplexFunctions::multiply); + Assertions.assertEquals(z, actual); } @Test void testMultiplyRealZero() { final Complex x = Complex.ofCartesian(3.0, 4.0); final double y = 0.0; - Complex z = x.multiply(y); + Complex z = TestUtils.assertSame(x, y, "multiply", Complex::multiply, ComplexFunctions::multiply); Assertions.assertEquals(0.0, z.getReal()); Assertions.assertEquals(0.0, z.getImaginary()); // Equivalent - Assertions.assertEquals(z, x.multiply(ofReal(y))); + Complex actual = TestUtils.assertSame(x, ofReal(y), "multiply", Complex::multiply, ComplexFunctions::multiply); + Assertions.assertEquals(z, actual); - z = x.multiply(-y); + z = TestUtils.assertSame(x, -y, "multiply", Complex::multiply, ComplexFunctions::multiply); Assertions.assertEquals(-0.0, z.getReal()); Assertions.assertEquals(-0.0, z.getImaginary()); // Sign-preservation is a problem for imaginary: 0.0 - -0.0 == 0.0 - final Complex z2 = x.multiply(ofReal(-y)); + final Complex z2 = TestUtils.assertSame(x, ofReal(-y), "multiply", Complex::multiply, ComplexFunctions::multiply); Assertions.assertEquals(-0.0, z2.getReal()); Assertions.assertEquals(0.0, z2.getImaginary(), "Expected no sign preservation"); } @@ -936,64 +964,69 @@ class ComplexTest { void testMultiplyImaginary() { final Complex x = Complex.ofCartesian(3.0, 4.0); final double y = 2.0; - Complex z = x.multiplyImaginary(y); + Complex z = TestUtils.assertSame(x, y, "multiplyImaginary", Complex::multiplyImaginary, ComplexFunctions::multiplyImaginary); Assertions.assertEquals(-8.0, z.getReal()); Assertions.assertEquals(6.0, z.getImaginary()); // Equivalent - Assertions.assertEquals(z, x.multiply(ofImaginary(y))); + Complex actual = TestUtils.assertSame(x, ofImaginary(y), "multiply", Complex::multiply, ComplexFunctions::multiply); + Assertions.assertEquals(z, actual); - z = x.multiplyImaginary(-y); + z = TestUtils.assertSame(x, -y, "multiplyImaginary", Complex::multiplyImaginary, ComplexFunctions::multiplyImaginary); Assertions.assertEquals(8.0, z.getReal()); Assertions.assertEquals(-6.0, z.getImaginary()); // Equivalent - Assertions.assertEquals(z, x.multiply(ofImaginary(-y))); + actual = TestUtils.assertSame(x, ofImaginary(-y), "multiply", Complex::multiply, ComplexFunctions::multiply); + Assertions.assertEquals(z, actual); } @Test void testMultiplyImaginaryNaN() { final Complex x = Complex.ofCartesian(3.0, 4.0); final double y = nan; - final Complex z = x.multiplyImaginary(y); + final Complex z = TestUtils.assertSame(x, y, "multiplyImaginary", Complex::multiplyImaginary, ComplexFunctions::multiplyImaginary); Assertions.assertEquals(nan, z.getReal()); Assertions.assertEquals(nan, z.getImaginary()); // Equivalent - Assertions.assertEquals(z, x.multiply(ofImaginary(y))); + final Complex actual = TestUtils.assertSame(x, ofImaginary(y), "multiply", Complex::multiply, ComplexFunctions::multiply); + Assertions.assertEquals(z, actual); } @Test void testMultiplyImaginaryInf() { final Complex x = Complex.ofCartesian(3.0, 4.0); final double y = inf; - Complex z = x.multiplyImaginary(y); + Complex z = TestUtils.assertSame(x, y, "multiplyImaginary", Complex::multiplyImaginary, ComplexFunctions::multiplyImaginary); Assertions.assertEquals(-inf, z.getReal()); Assertions.assertEquals(inf, z.getImaginary()); // Equivalent - Assertions.assertEquals(z, x.multiply(ofImaginary(y))); + Complex actual = TestUtils.assertSame(x, ofImaginary(y), "multiply", Complex::multiply, ComplexFunctions::multiply); + Assertions.assertEquals(z, actual); - z = x.multiplyImaginary(-y); + z = TestUtils.assertSame(x, -y, "multiplyImaginary", Complex::multiplyImaginary, ComplexFunctions::multiplyImaginary); Assertions.assertEquals(inf, z.getReal()); Assertions.assertEquals(-inf, z.getImaginary()); // Equivalent - Assertions.assertEquals(z, x.multiply(ofImaginary(-y))); + actual = TestUtils.assertSame(x, ofImaginary(-y), "multiply", Complex::multiply, ComplexFunctions::multiply); + Assertions.assertEquals(z, actual); } @Test void testMultiplyImaginaryZero() { final Complex x = Complex.ofCartesian(3.0, 4.0); final double y = 0.0; - Complex z = x.multiplyImaginary(y); + Complex z = TestUtils.assertSame(x, y, "multiplyImaginary", Complex::multiplyImaginary, ComplexFunctions::multiplyImaginary); Assertions.assertEquals(-0.0, z.getReal()); Assertions.assertEquals(0.0, z.getImaginary()); // Sign-preservation is a problem for real: 0.0 - -0.0 == 0.0 - Complex z2 = x.multiply(ofImaginary(y)); + Complex z2 = TestUtils.assertSame(x, ofImaginary(y), "multiply", Complex::multiply, ComplexFunctions::multiply); Assertions.assertEquals(0.0, z2.getReal(), "Expected no sign preservation"); Assertions.assertEquals(0.0, z2.getImaginary()); - z = x.multiplyImaginary(-y); + z = TestUtils.assertSame(x, -y, "multiplyImaginary", Complex::multiplyImaginary, ComplexFunctions::multiplyImaginary); Assertions.assertEquals(0.0, z.getReal()); Assertions.assertEquals(-0.0, z.getImaginary()); // Sign-preservation is a problem for imaginary: -0.0 - 0.0 == 0.0 - z2 = x.multiply(ofImaginary(-y)); + z2 = TestUtils.assertSame(x, ofImaginary(-y), "multiply", Complex::multiply, ComplexFunctions::multiply); Assertions.assertEquals(0.0, z2.getReal()); Assertions.assertEquals(0.0, z2.getImaginary(), "Expected no sign preservation"); } @@ -1004,11 +1037,11 @@ class ComplexTest { for (final double a : parts) { for (final double b : parts) { final Complex c = Complex.ofCartesian(a, b); - final Complex x = c.multiplyImaginary(1.0); + final Complex x = TestUtils.assertSame(c, 1.0, "multiplyImaginary", Complex::multiplyImaginary, ComplexFunctions::multiplyImaginary); // Check verses algebra solution Assertions.assertEquals(-b, x.getReal()); Assertions.assertEquals(a, x.getImaginary()); - final Complex z = c.multiply(Complex.I); + final Complex z = TestUtils.assertSame(c, Complex.I, "multiply", Complex::multiply, ComplexFunctions::multiply); Assertions.assertEquals(x, z); } } @@ -1022,12 +1055,12 @@ class ComplexTest { for (final double a : parts) { for (final double b : parts) { final Complex c = Complex.ofCartesian(a, b); - final Complex x = c.multiplyImaginary(-1.0); + final Complex x = TestUtils.assertSame(c, -1.0, "multiplyImaginary", Complex::multiplyImaginary, ComplexFunctions::multiplyImaginary); // Check verses algebra solution Assertions.assertEquals(b, x.getReal()); Assertions.assertEquals(-a, x.getImaginary()); for (final Complex negI : negIs) { - final Complex z = c.multiply(negI); + final Complex z = TestUtils.assertSame(c, negI, "multiply", Complex::multiply, ComplexFunctions::multiply); Assertions.assertEquals(x, z); } } @@ -1040,11 +1073,11 @@ class ComplexTest { for (final double a : zeros) { for (final double b : zeros) { final Complex c = Complex.ofCartesian(a, b); - final Complex x = c.multiplyImaginary(1.0); + final Complex x = TestUtils.assertSame(c, 1.0, "multiplyImaginary", Complex::multiplyImaginary, ComplexFunctions::multiplyImaginary); // Check verses algebra solution Assertions.assertEquals(-b, x.getReal()); Assertions.assertEquals(a, x.getImaginary()); - final Complex z = c.multiply(Complex.I); + final Complex z = TestUtils.assertSame(c, Complex.I, "multiply", Complex::multiply, ComplexFunctions::multiply); // Does not work when imaginary part is +0.0. if (Double.compare(b, 0.0) == 0) { // (-0.0, 0.0).multiply( (0,1) ) => (-0.0, 0.0) expected (-0.0,-0.0) @@ -1069,12 +1102,13 @@ class ComplexTest { for (final double a : zeros) { for (final double b : zeros) { final Complex c = Complex.ofCartesian(a, b); - final Complex x = c.multiplyImaginary(-1.0); + final Complex x = TestUtils.assertSame(c, -1.0, "multiplyImaginary", Complex::multiplyImaginary, ComplexFunctions::multiplyImaginary); // Check verses algebra solution Assertions.assertEquals(b, x.getReal()); Assertions.assertEquals(-a, x.getImaginary()); - final Complex z = c.multiply(negI); - final Complex z2 = c.multiply(Complex.I).negate(); + final Complex z = TestUtils.assertSame(c, negI, "multiply", Complex::multiply, ComplexFunctions::multiply); + final Complex ci = TestUtils.assertSame(c, Complex.I, "multiply", Complex::multiply, ComplexFunctions::multiply); + final Complex z2 = TestUtils.assertSame(ci, "negate", Complex::negate, ComplexFunctions::negate); // Does not work when imaginary part is -0.0. if (Double.compare(b, -0.0) == 0) { // (-0.0,-0.0).multiply( (-0.0,-1) ) => ( 0.0, 0.0) expected (-0.0, 0.0) @@ -1145,126 +1179,138 @@ class ComplexTest { void testDivideReal() { final Complex x = Complex.ofCartesian(3.0, 4.0); final double y = 2.0; - Complex z = x.divide(y); + Complex z = TestUtils.assertSame(x, y, "divide", Complex::divide, ComplexFunctions::divide); Assertions.assertEquals(1.5, z.getReal()); Assertions.assertEquals(2.0, z.getImaginary()); // Equivalent - Assertions.assertEquals(z, x.divide(ofReal(y))); + Complex actual = TestUtils.assertSame(x, ofReal(y), "divide", Complex::divide, ComplexFunctions::divide); + Assertions.assertEquals(z, actual); - z = x.divide(-y); + z = TestUtils.assertSame(x, -y, "divide", Complex::divide, ComplexFunctions::divide); Assertions.assertEquals(-1.5, z.getReal()); Assertions.assertEquals(-2.0, z.getImaginary()); // Equivalent - Assertions.assertEquals(z, x.divide(ofReal(-y))); + actual = TestUtils.assertSame(x, ofReal(-y), "divide", Complex::divide, ComplexFunctions::divide); + Assertions.assertEquals(z, actual); } @Test void testDivideRealNaN() { final Complex x = Complex.ofCartesian(3.0, 4.0); final double y = nan; - final Complex z = x.divide(y); + final Complex z = TestUtils.assertSame(x, y, "divide", Complex::divide, ComplexFunctions::divide); Assertions.assertEquals(nan, z.getReal()); Assertions.assertEquals(nan, z.getImaginary()); // Equivalent - Assertions.assertEquals(z, x.divide(ofReal(y))); + final Complex actual = TestUtils.assertSame(x, ofReal(y), "divide", Complex::divide, ComplexFunctions::divide); + Assertions.assertEquals(z, actual); } @Test void testDivideRealInf() { final Complex x = Complex.ofCartesian(3.0, 4.0); final double y = inf; - Complex z = x.divide(y); + Complex z = TestUtils.assertSame(x, y, "divide", Complex::divide, ComplexFunctions::divide); Assertions.assertEquals(0.0, z.getReal()); Assertions.assertEquals(0.0, z.getImaginary()); // Equivalent - Assertions.assertEquals(z, x.divide(ofReal(y))); + Complex actual = TestUtils.assertSame(x, ofReal(y), "divide", Complex::divide, ComplexFunctions::divide); + Assertions.assertEquals(z, actual); - z = x.divide(-y); + z = TestUtils.assertSame(x, -y, "divide", Complex::divide, ComplexFunctions::divide); Assertions.assertEquals(-0.0, z.getReal()); Assertions.assertEquals(-0.0, z.getImaginary()); // Equivalent - Assertions.assertEquals(z, x.divide(ofReal(-y))); + actual = TestUtils.assertSame(x, ofReal(-y), "divide", Complex::divide, ComplexFunctions::divide); + Assertions.assertEquals(z, actual); } @Test void testDivideRealZero() { final Complex x = Complex.ofCartesian(3.0, 4.0); final double y = 0.0; - Complex z = x.divide(y); + Complex z = TestUtils.assertSame(x, y, "divide", Complex::divide, ComplexFunctions::divide); Assertions.assertEquals(inf, z.getReal()); Assertions.assertEquals(inf, z.getImaginary()); // Equivalent - Assertions.assertEquals(z, x.divide(ofReal(y))); + Complex actual = TestUtils.assertSame(x, ofReal(y), "divide", Complex::divide, ComplexFunctions::divide); + Assertions.assertEquals(z, actual); - z = x.divide(-y); + z = TestUtils.assertSame(x, -y, "divide", Complex::divide, ComplexFunctions::divide); Assertions.assertEquals(-inf, z.getReal()); Assertions.assertEquals(-inf, z.getImaginary()); // Equivalent - Assertions.assertEquals(z, x.divide(ofReal(-y))); + actual = TestUtils.assertSame(x, ofReal(-y), "divide", Complex::divide, ComplexFunctions::divide); + Assertions.assertEquals(z, actual); } @Test void testDivideImaginary() { final Complex x = Complex.ofCartesian(3.0, 4.0); final double y = 2.0; - Complex z = x.divideImaginary(y); + Complex z = TestUtils.assertSame(x, y, "divideImaginary", Complex::divideImaginary, ComplexFunctions::divideImaginary); Assertions.assertEquals(2.0, z.getReal()); Assertions.assertEquals(-1.5, z.getImaginary()); // Equivalent - Assertions.assertEquals(z, x.divide(ofImaginary(y))); + Complex actual = TestUtils.assertSame(x, ofImaginary(y), "divide", Complex::divide, ComplexFunctions::divide); + Assertions.assertEquals(z, actual); - z = x.divideImaginary(-y); + z = TestUtils.assertSame(x, -y, "divideImaginary", Complex::divideImaginary, ComplexFunctions::divideImaginary); Assertions.assertEquals(-2.0, z.getReal()); Assertions.assertEquals(1.5, z.getImaginary()); // Equivalent - Assertions.assertEquals(z, x.divide(ofImaginary(-y))); + actual = TestUtils.assertSame(x, ofImaginary(-y), "divide", Complex::divide, ComplexFunctions::divide); + Assertions.assertEquals(z, actual); } @Test void testDivideImaginaryNaN() { final Complex x = Complex.ofCartesian(3.0, 4.0); final double y = nan; - final Complex z = x.divideImaginary(y); + final Complex z = TestUtils.assertSame(x, y, "divideImaginary", Complex::divideImaginary, ComplexFunctions::divideImaginary); Assertions.assertEquals(nan, z.getReal()); Assertions.assertEquals(nan, z.getImaginary()); // Equivalent - Assertions.assertEquals(z, x.divide(ofImaginary(y))); + final Complex actual = TestUtils.assertSame(x, ofImaginary(y), "divide", Complex::divide, ComplexFunctions::divide); + Assertions.assertEquals(z, actual); } @Test void testDivideImaginaryInf() { final Complex x = Complex.ofCartesian(3.0, 4.0); final double y = inf; - Complex z = x.divideImaginary(y); + Complex z = TestUtils.assertSame(x, y, "divideImaginary", Complex::divideImaginary, ComplexFunctions::divideImaginary); Assertions.assertEquals(0.0, z.getReal()); Assertions.assertEquals(-0.0, z.getImaginary()); // Equivalent - Assertions.assertEquals(z, x.divide(ofImaginary(y))); + Complex actual = TestUtils.assertSame(x, ofImaginary(y), "divide", Complex::divide, ComplexFunctions::divide); + Assertions.assertEquals(z, actual); - z = x.divideImaginary(-y); + z = TestUtils.assertSame(x, -y, "divideImaginary", Complex::divideImaginary, ComplexFunctions::divideImaginary); Assertions.assertEquals(-0.0, z.getReal()); Assertions.assertEquals(0.0, z.getImaginary()); // Equivalent - Assertions.assertEquals(z, x.divide(ofImaginary(-y))); + actual = TestUtils.assertSame(x, ofImaginary(-y), "divide", Complex::divide, ComplexFunctions::divide); + Assertions.assertEquals(z, actual); } @Test void testDivideImaginaryZero() { final Complex x = Complex.ofCartesian(3.0, 4.0); final double y = 0.0; - Complex z = x.divideImaginary(y); + Complex z = TestUtils.assertSame(x, y, "divideImaginary", Complex::divideImaginary, ComplexFunctions::divideImaginary); Assertions.assertEquals(inf, z.getReal()); Assertions.assertEquals(-inf, z.getImaginary()); // Sign-preservation is a problem for imaginary: 0.0 - -0.0 == 0.0 - Complex z2 = x.divide(ofImaginary(y)); + Complex z2 = TestUtils.assertSame(x, ofImaginary(y), "divide", Complex::divide, ComplexFunctions::divide); Assertions.assertEquals(inf, z2.getReal()); Assertions.assertEquals(inf, z2.getImaginary(), "Expected no sign preservation"); - z = x.divideImaginary(-y); + z = TestUtils.assertSame(x, -y, "divideImaginary", Complex::divideImaginary, ComplexFunctions::divideImaginary); Assertions.assertEquals(-inf, z.getReal()); Assertions.assertEquals(inf, z.getImaginary()); // Sign-preservation is a problem for real: 0.0 + -0.0 == 0.0 - z2 = x.divide(ofImaginary(-y)); + z2 = TestUtils.assertSame(x, ofImaginary(-y), "divide", Complex::divide, ComplexFunctions::divide); Assertions.assertEquals(inf, z2.getReal(), "Expected no sign preservation"); Assertions.assertEquals(inf, z2.getImaginary()); } @@ -1304,22 +1350,22 @@ class ComplexTest { // and the javadoc in Complex does not break down the actual cases. // 16: (x,-0.0) + x - assertSignedZeroArithmetic("addReal", Complex::add, ComplexTest::ofReal, Complex::add, + assertSignedZeroArithmetic("addReal", Complex::add, ComplexFunctions::add, ComplexTest::ofReal, Complex::add, ComplexFunctions::add, 0b1111000000000000111100000000000011110000000000001111L); // 16: (-0.0,x) + x - assertSignedZeroArithmetic("addImaginary", Complex::addImaginary, ComplexTest::ofImaginary, Complex::add, + assertSignedZeroArithmetic("addImaginary", Complex::addImaginary, ComplexFunctions::addImaginary, ComplexTest::ofImaginary, Complex::add, ComplexFunctions::add, 0b1111111111111111L); // 0: - assertSignedZeroArithmetic("subtractReal", Complex::subtract, ComplexTest::ofReal, Complex::subtract, 0); + assertSignedZeroArithmetic("subtractReal", Complex::subtract, ComplexFunctions::subtract, ComplexTest::ofReal, Complex::subtract, ComplexFunctions::subtract, 0); // 0: - assertSignedZeroArithmetic("subtractImaginary", Complex::subtractImaginary, ComplexTest::ofImaginary, - Complex::subtract, 0); + assertSignedZeroArithmetic("subtractImaginary", Complex::subtractImaginary, ComplexFunctions::subtractImaginary, ComplexTest::ofImaginary, + Complex::subtract, ComplexFunctions::subtract, 0); // 16: x - (x,+0.0) - assertSignedZeroArithmetic("subtractFromReal", Complex::subtractFrom, ComplexTest::ofReal, - (y, z) -> z.subtract(y), 0b11110000000000001111000000000000111100000000000011110000L); + assertSignedZeroArithmetic("subtractFromReal", Complex::subtractFrom, TestUtils::subtractFrom, ComplexTest::ofReal, + (y, z) -> z.subtract(y), (a, b, c, d, action) -> ComplexFunctions.subtract(c, d, a, b, action), 0b11110000000000001111000000000000111100000000000011110000L); // 16: x - (+0.0,x) - assertSignedZeroArithmetic("subtractFromImaginary", Complex::subtractFromImaginary, ComplexTest::ofImaginary, - (y, z) -> z.subtract(y), 0b11111111111111110000000000000000L); + assertSignedZeroArithmetic("subtractFromImaginary", Complex::subtractFromImaginary, TestUtils::subtractFromImaginary, ComplexTest::ofImaginary, + (y, z) -> z.subtract(y), (a, b, c, d, action) -> ComplexFunctions.subtract(c, d, a, b, action), 0b11111111111111110000000000000000L); // 4: (-0.0,-x) * +x // 4: (+0.0,-0.0) * x // 4: (+0.0,x) * -x @@ -1329,7 +1375,7 @@ class ComplexTest { // 2: (+y,-x) * -0.0 // 2: (+x,-y) * +0.0 // 2: (+x,+y) * -0.0 - assertSignedZeroArithmetic("multiplyReal", Complex::multiply, ComplexTest::ofReal, Complex::multiply, + assertSignedZeroArithmetic("multiplyReal", Complex::multiply, ComplexFunctions::multiply, ComplexTest::ofReal, Complex::multiply, ComplexFunctions::multiply, 0b1001101011011000000100000001000010111010111110000101000001010L); // 4: (-0.0,+x) * +x // 2: (+0.0,-0.0) * -x @@ -1340,31 +1386,33 @@ class ComplexTest { // 2: (+0.0,+/-y) * -/+0 // 2: (+y,+/-0.0) * +/-y (sign 0.0 matches sign y) // 2: (+y,+x) * +0.0 - assertSignedZeroArithmetic("multiplyImaginary", Complex::multiplyImaginary, ComplexTest::ofImaginary, - Complex::multiply, 0b11000110110101001000000010000001110001111101011010000010100000L); + assertSignedZeroArithmetic("multiplyImaginary", Complex::multiplyImaginary, ComplexFunctions::multiplyImaginary, ComplexTest::ofImaginary, + Complex::multiply, ComplexFunctions::multiply, 0b11000110110101001000000010000001110001111101011010000010100000L); // 2: (-0.0,0) / +y // 2: (+0.0,+x) / -y // 2: (-x,0) / -y // 1: (-0.0,+y) / +y // 1: (-y,+0.0) / -y - assertSignedZeroArithmetic("divideReal", Complex::divide, ComplexTest::ofReal, Complex::divide, + assertSignedZeroArithmetic("divideReal", Complex::divide, ComplexFunctions::divide, ComplexTest::ofReal, Complex::divide, ComplexFunctions::divide, 0b100100001000000010000001000000011001000L); // DivideImaginary has its own test as the result is not always equal ignoring the // sign. } - private static void assertSignedZeroArithmetic(String name, BiFunction<Complex, Double, Complex> doubleOperation, - DoubleFunction<Complex> doubleToComplex, BiFunction<Complex, Complex, Complex> complexOperation, - long expectedFailures) { + private static void assertSignedZeroArithmetic(String name, BiFunction<Complex, Double, Complex> doubleOperation1, + ComplexScalarFunction<ComplexNumber> doubleOperation2, + DoubleFunction<Complex> doubleToComplex, BiFunction<Complex, Complex, Complex> complexOperation1, + ComplexBinaryOperator<ComplexNumber> complexOperation2, + long expectedFailures) { // With an operation on zero or non-zero arguments final double[] arguments = {-0.0, 0.0, -2, 3}; for (final double a : arguments) { for (final double b : arguments) { final Complex c = Complex.ofCartesian(a, b); for (final double arg : arguments) { - final Complex y = doubleOperation.apply(c, arg); - final Complex z = complexOperation.apply(c, doubleToComplex.apply(arg)); + final Complex y = TestUtils.assertSame(c, arg, name, doubleOperation1, doubleOperation2); + final Complex z = TestUtils.assertSame(c, doubleToComplex.apply(arg), name, complexOperation1, complexOperation2); final boolean expectedFailure = (expectedFailures & 0x1) == 1; expectedFailures >>>= 1; // Check the same answer. Sign is allowed to be different for zero. @@ -1406,7 +1454,7 @@ class ComplexTest { for (final double b : arguments) { final Complex c = Complex.ofCartesian(a, b); for (final double arg : arguments) { - final Complex y = c.divideImaginary(arg); + final Complex y = TestUtils.assertSame(c, arg, "divideImaginary", Complex::divideImaginary, ComplexFunctions::divideImaginary); Complex z = TestUtils.assertSame(c, ofImaginary(arg), "divide", Complex::divide, ComplexFunctions::divide); final boolean expectedFailure = (expectedFailures & 0x1) == 1; expectedFailures >>>= 1; @@ -1415,7 +1463,7 @@ class ComplexTest { if (arg == 0) { // Same result if multiplied by I. The sign may not match so // optionally ignore the sign of the infinity. - z = z.multiplyImaginary(1); + z = TestUtils.assertSame(z, 1, "multiplyImaginary", Complex::multiplyImaginary, ComplexFunctions::multiplyImaginary); final double ya = expectedFailure ? Math.abs(y.getReal()) : y.getReal(); final double yb = expectedFailure ? Math.abs(y.getImaginary()) : y.getImaginary(); final double za = expectedFailure ? Math.abs(z.getReal()) : z.getReal(); @@ -1458,7 +1506,8 @@ class ComplexTest { final double yDouble = 5.0; final Complex yComplex = ofReal(yDouble); final Complex expected = TestUtils.assertSame(x, yComplex, "pow", Complex::pow, ComplexFunctions::pow); - Assertions.assertEquals(expected, x.pow(yDouble)); + final Complex actual = TestUtils.assertSame(x, yDouble, "pow", Complex::pow, ComplexFunctions::pow); + Assertions.assertEquals(expected, actual); } @Test @@ -1491,7 +1540,7 @@ class ComplexTest { void testPowScalerRealZero() { // Hits the edge case when real == 0 but imaginary != 0 final Complex x = Complex.ofCartesian(0, 1); - final Complex c = x.pow(2); + final Complex c = TestUtils.assertSame(x, 2, "pow", Complex::pow, ComplexFunctions::pow); // Answer from g++ Assertions.assertEquals(-1, c.getReal()); Assertions.assertEquals(1.2246467991473532e-16, c.getImaginary()); @@ -1505,7 +1554,7 @@ class ComplexTest { } private static void assertPowScalarZeroBase(double exp, Complex expected) { - final Complex c = Complex.ZERO.pow(exp); + final Complex c = TestUtils.assertSame(Complex.ZERO, exp, "pow", Complex::pow, ComplexFunctions::pow); Assertions.assertEquals(expected, c); } @@ -1515,7 +1564,8 @@ class ComplexTest { final double yDouble = 5.0; final Complex yComplex = ofReal(yDouble); final Complex expected = TestUtils.assertSame(x, yComplex, "pow", Complex::pow, ComplexFunctions::pow); - Assertions.assertEquals(expected, x.pow(yDouble)); + final Complex actual = TestUtils.assertSame(x, yDouble, "pow", Complex::pow, ComplexFunctions::pow); + Assertions.assertEquals(expected, actual); } @Test @@ -1524,7 +1574,8 @@ class ComplexTest { final double yDouble = Double.NaN; final Complex yComplex = ofReal(yDouble); final Complex expected = TestUtils.assertSame(x, yComplex, "pow", Complex::pow, ComplexFunctions::pow); - Assertions.assertEquals(expected, x.pow(yDouble)); + final Complex actual = TestUtils.assertSame(x, yDouble, "pow", Complex::pow, ComplexFunctions::pow); + Assertions.assertEquals(expected, actual); } @Test diff --git a/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/TestUtils.java b/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/TestUtils.java index e67bec5f..cefe109b 100644 --- a/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/TestUtils.java +++ b/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/TestUtils.java @@ -457,6 +457,30 @@ public final class TestUtils { return z; } + /** + * Assert the operation on the complex number and double operand is <em>exactly</em> equal to the operation on + * complex real and imaginary parts and double operand. + * + * @param c Input complex number. + * @param operand Scalar operand. + * @param name Operation name. + * @param operation1 Operation on the Complex object and double operand. + * @param operation2 Operation on the complex real and imaginary parts and double operand. + * @return Result complex number from the given operation. + */ + public static Complex assertSame(Complex c, + double operand, + String name, + BiFunction<Complex, Double, Complex> operation1, + ComplexScalarFunction<ComplexNumber> operation2) { + final Complex z = operation1.apply(c, operand); + // Test operation2 produces the exact same result + final ComplexNumber z2 = operation2.apply(c.real(), c.imag(), operand, ComplexNumber::new); + Assertions.assertEquals(z.real(), z2.getReal(), () -> "Scalar operator mismatch: " + name + " real"); + Assertions.assertEquals(z.imag(), z2.getImaginary(), () -> "Scalar operator mismatch: " + name + " imaginary"); + return z; + } + /** * Assert the operation on the complex number is <em>exactly</em> equal to the operation on * complex real and imaginary parts. @@ -498,4 +522,46 @@ public final class TestUtils { Assertions.assertEquals(b1, b2, () -> "Predicate mismatch: " + name); return b1; } + + /** + * Computes the result of the subtraction of a complex number from an imaginary number. + * Implements the formula: + * \[ i d - (a + i b) = -a + i (d - b) \] + * + * <p>This method is a helper to replicate the method signature of the object-orientated + * API in Complex (i.e. the complex argument is first) using the equivalent static API + * function in ComplexFunctions. + * + * @param real Real part \( a \) of the complex number \( (a +ib) \). + * @param imaginary Imaginary part \( b \) of the complex number \( (a +ib) \). + * @param minuend Value the complex number is to be subtracted from. + * @param action Consumer for the subtraction result. + * @param <R> the return type of the supplied action. + * @return the object returned by the supplied action. + */ + public static <R> R subtractFromImaginary(double real, double imaginary, double minuend, ComplexSink<R> action) { + // Call the equivalent static API function + return ComplexFunctions.imaginarySubtract(minuend, real, imaginary, action); + } + + /** + * Computes the result of the subtraction of a complex number from a real number. + * Implements the formula: + * \[ c - (a + i b) = (c - a) - i b \] + * + * <p>This method is a helper to replicate the method signature of the object-orientated + * API in Complex (i.e. the complex argument is first) using the equivalent static API + * function in ComplexFunctions. + * + * @param real Real part \( a \) of the complex number \( (a +ib) \). + * @param imaginary Imaginary part \( b \) of the complex number \( (a +ib) \). + * @param minuend Value the complex number is to be subtracted from. + * @param action Consumer for the subtraction result. + * @param <R> the return type of the supplied action. + * @return the object returned by the supplied action. + */ + public static <R> R subtractFrom(double real, double imaginary, double minuend, ComplexSink<R> action) { + // Call the equivalent static API function + return ComplexFunctions.realSubtract(minuend, real, imaginary, action); + } }