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 &amp;= (-b + i a) \\
+     *   -iz &amp;= (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);
+    }
 }

Reply via email to