aherbert commented on code in PR #117:
URL: https://github.com/apache/commons-numbers/pull/117#discussion_r922646042
##########
commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/Complex.java:
##########
@@ -3297,7 +2951,7 @@ private static boolean equals(double x, double y) {
* @return {@code true} if {@code d} is negative.
*/
private static boolean negative(double d) {
Review Comment:
Add a TODO note to this method to remove it in future refactoring. IIUC it
is only used in one place if all the functions are moved to ComplexFunctions
(the remaining call site is in the ofPolar constructor).
##########
commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/ComplexUnaryOperator.java:
##########
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.numbers.complex;
+
+/**
+ * Represents a complex operation that accepts a complex number of type
ComplexDouble and produces a ComplexDouble result.
+ * @param <R> Generic
+ */
+@FunctionalInterface
+public interface ComplexUnaryOperator<R> {
+
+ /**
+ * Represents an operator that accepts real and imaginary parts of a
complex number and a complex constructor to produce and return the result.
+ * @param r real
+ * @param i imaginary
+ * @param out Constructor
+ * @return the object created by the supplied constructor.
+ */
+ R apply(double r, double i, ComplexConstructor<R> out);
+
Review Comment:
Remove trailing line
##########
commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/Complex.java:
##########
@@ -2905,224 +2776,7 @@ private static Complex atanh(final double real, final
double imaginary,
* @return {@code x^2 + y^2 - 1}.
*/
private static double x2y2m1(double x, double y) {
Review Comment:
This method is an internal method used in atanh. It should not be refactored
within this PR and should not be public in ComplexFunctions.
##########
commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/CStandardTest.java:
##########
@@ -300,6 +300,91 @@ private static void assertConjugateEquality(Complex z,
}
}
+ /**
+ * Assert the operation on the complex number satisfies the conjugate
equality.
+ *
+ * <pre>
+ * op(conj(z)) = conj(op(z))
+ * </pre>
+ *
+ * <p>The results must be binary equal. This includes the sign of zero.
+ *
+ * <h2>ISO C99 equalities</h2>
+ *
+ * <p>Note that this method currently enforces the conjugate equalities
for some cases
+ * where the sign of the real/imaginary parts are unspecified in ISO C99.
This is
+ * allowed (since they are unspecified). The sign specification is
appropriately
+ * handled during testing of odd/even functions. There are some functions
where it
+ * is not possible to satisfy the conjugate equality and also the odd/even
rule.
+ * The compromise made here is to satisfy only one and the other is
allowed to fail
+ * only on the sign of the output result. Known functions where this
applies are:
+ *
+ * <ul>
+ * <li>asinh(NaN, inf)
+ * <li>atanh(NaN, inf)
+ * <li>cosh(NaN, 0.0)
+ * <li>sinh(inf, inf)
+ * <li>sinh(inf, nan)
+ * </ul>
+ *
+ * @param operation the operation
+ */
+ private static void assertConjugateEquality(UnaryOperator<Complex>
operation, ComplexUnaryOperator<ComplexNumber> operation2) {
Review Comment:
each argument on a new line
##########
commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/CStandardTest.java:
##########
@@ -508,6 +593,146 @@ private static void assertComplex(Complex z,
}
}
+ /**
+ * Assert the operation on the complex number is equal to the expected
value.
+ * If the imaginary part is not NaN the operation must also satisfy the
conjugate equality.
+ *
+ * <pre>
+ * op(conj(z)) = conj(op(z))
+ * </pre>
+ *
+ * <p>The results must be binary equal. This includes the sign of zero.
+ * @param z the complex
+ * @param operation the Complex operation
+ * @param expected the expected
+ * @param operation2 the ComplexFunctions operation
+ */
+ private static void assertComplex(Complex z,
+ UnaryOperator<Complex> operation,
Complex expected,
+ ComplexUnaryOperator<ComplexNumber>
operation2) {
+ assertComplex(z, operation, operation2, expected, FunctionType.NONE,
UnspecifiedSign.NONE);
+ }
+
+ /**
+ * Assert the operation on the complex number is equal to the expected
value. If the
+ * imaginary part is not NaN the operation must also satisfy the conjugate
equality.
+ *
+ * <pre>
+ * op(conj(z)) = conj(op(z))
+ * </pre>
+ *
+ * <p>The results must be binary equal. This includes the sign of zero.
+ * @param z the complex
+ * @param operation the Complex operation
+ * @param operation2 the ComplexFunctions operation
+ * @param expected the expected
+ * @param sign the sign specification
+ */
+ private static void assertComplex(Complex z,
+ UnaryOperator<Complex> operation,
+ ComplexUnaryOperator<ComplexNumber>
operation2,
+ Complex expected, UnspecifiedSign sign) {
Review Comment:
sign on a new line
##########
commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/ComplexFunctionsTest.java:
##########
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.numbers.complex;
+
+import org.apache.commons.rng.UniformRandomProvider;
+import org.apache.commons.rng.simple.RandomSource;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests for {@link Complex} and {@link ComplexFunctions}.
+ *
+ * <p>Note: The ISO C99 math functions are not fully tested in this class. See
also:
+ *
+ * <ul>
+ * <li>{@link CStandardTest} for a test of the ISO C99 standards including
special case handling.
+ * <li>{@link CReferenceTest} for a test of the output using standard finite
value against an
+ * ISO C99 compliant reference implementation.
+ * <li>{@link ComplexEdgeCaseTest} for a test of extreme edge case finite
values for real and/or
+ * imaginary parts that can create intermediate overflow or underflow.
+ * </ul>
+ */
+class ComplexFunctionsTest {
Review Comment:
I think this is not required. The test here should be in ComplexTest. That
should be updated to test both the operation in Complex and ComplexFunctions
using the new dual operation assertions in TestUtils.
##########
commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/TestUtils.java:
##########
@@ -387,4 +389,94 @@ private static String preprocessTestData(String line,
TestDataFlagOption option,
}
return line;
}
+
+ /**
+ * Assert the unary complex operation on the complex number is equal to
the expected value.
+ * No delta.
Review Comment:
Remove this useless comment. Remove the comment about conjugate equality as
this is not tested.
##########
commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/TestUtils.java:
##########
@@ -387,4 +389,94 @@ private static String preprocessTestData(String line,
TestDataFlagOption option,
}
return line;
}
+
+ /**
+ * Assert the unary complex operation on the complex number is equal to
the expected value.
+ * No delta.
+ * If the imaginary part is not NaN the operation must also satisfy the
conjugate equality.
+ *
+ * <pre>
+ * op(conj(z)) = conj(op(z))
+ * </pre>
+ *
+ * <p>The results must be binary equal. This includes the sign of zero.
+ * @param c the complex
+ * @param operation1 the operation
+ * @param operation2 the second operation
+ * @param expected the expected
+ * @param name the operation name
+ */
+ static void assertComplexUnary(Complex c,
+ UnaryOperator<Complex> operation1,
ComplexUnaryOperator<ComplexNumber> operation2,
+ Complex expected, String name) {
+ assertComplexUnary(c, operation1, operation2, expected, name, 0.0D,
0.0D);
+ }
+
+ /**
+ * Assert the unary complex operation on the complex number is equal to
the expected value.
+ * If the imaginary part is not NaN the operation must also satisfy the
conjugate equality.
+ *
+ * <pre>
+ * op(conj(z)) = conj(op(z))
+ * </pre>
+ *
+ * <p>The results must be binary equal. This includes the sign of zero.
+ * @param c the complex
+ * @param operation1 the operation
+ * @param operation2 the second operation
+ * @param expected the expected
+ * @param name the operation name
+ * @param delta delta
+ */
+ static void assertComplexUnary(Complex c,
+ UnaryOperator<Complex> operation1,
ComplexUnaryOperator<ComplexNumber> operation2,
+ Complex expected, String name, double
delta) {
+ assertComplexUnary(c, operation1, operation2, expected, name, delta,
delta);
+ }
+
+ /**
+ * Assert the unary complex operation on the complex number is equal to
the expected value.
+ * If the imaginary part is not NaN the operation must also satisfy the
conjugate equality.
+ *
+ * <pre>
+ * op(conj(z)) = conj(op(z))
+ * </pre>
+ *
+ * <p>The results must be binary equal. This includes the sign of zero.
+ * @param c the complex
+ * @param operation1 the operation
+ * @param operation2 the second operation
+ * @param expected the expected
+ * @param name the operation name
+ * @param deltaReal real delta
+ * @param deltaImaginary imaginary delta
+ */
+ static void assertComplexUnary(Complex c,
+ UnaryOperator<Complex> operation1,
ComplexUnaryOperator<ComplexNumber> operation2,
+ Complex expected, String name, double
deltaReal, double deltaImaginary) {
+ final Complex result1 = operation1.apply(c);
+ final ComplexNumber result2 = operation2.apply(c.getReal(),
c.getImaginary(), ComplexNumber::new);
Review Comment:
double space after comma
##########
commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/TestUtils.java:
##########
@@ -387,4 +389,94 @@ private static String preprocessTestData(String line,
TestDataFlagOption option,
}
return line;
}
+
+ /**
+ * Assert the unary complex operation on the complex number is equal to
the expected value.
+ * No delta.
+ * If the imaginary part is not NaN the operation must also satisfy the
conjugate equality.
+ *
+ * <pre>
+ * op(conj(z)) = conj(op(z))
+ * </pre>
+ *
+ * <p>The results must be binary equal. This includes the sign of zero.
+ * @param c the complex
+ * @param operation1 the operation
+ * @param operation2 the second operation
+ * @param expected the expected
+ * @param name the operation name
+ */
+ static void assertComplexUnary(Complex c,
+ UnaryOperator<Complex> operation1,
ComplexUnaryOperator<ComplexNumber> operation2,
+ Complex expected, String name) {
+ assertComplexUnary(c, operation1, operation2, expected, name, 0.0D,
0.0D);
+ }
+
+ /**
+ * Assert the unary complex operation on the complex number is equal to
the expected value.
+ * If the imaginary part is not NaN the operation must also satisfy the
conjugate equality.
+ *
+ * <pre>
+ * op(conj(z)) = conj(op(z))
+ * </pre>
+ *
+ * <p>The results must be binary equal. This includes the sign of zero.
+ * @param c the complex
+ * @param operation1 the operation
+ * @param operation2 the second operation
+ * @param expected the expected
+ * @param name the operation name
+ * @param delta delta
+ */
+ static void assertComplexUnary(Complex c,
+ UnaryOperator<Complex> operation1,
ComplexUnaryOperator<ComplexNumber> operation2,
+ Complex expected, String name, double
delta) {
+ assertComplexUnary(c, operation1, operation2, expected, name, delta,
delta);
+ }
+
+ /**
+ * Assert the unary complex operation on the complex number is equal to
the expected value.
+ * If the imaginary part is not NaN the operation must also satisfy the
conjugate equality.
+ *
+ * <pre>
+ * op(conj(z)) = conj(op(z))
+ * </pre>
+ *
+ * <p>The results must be binary equal. This includes the sign of zero.
+ * @param c the complex
+ * @param operation1 the operation
+ * @param operation2 the second operation
+ * @param expected the expected
+ * @param name the operation name
+ * @param deltaReal real delta
+ * @param deltaImaginary imaginary delta
+ */
+ static void assertComplexUnary(Complex c,
+ UnaryOperator<Complex> operation1,
ComplexUnaryOperator<ComplexNumber> operation2,
+ Complex expected, String name, double
deltaReal, double deltaImaginary) {
+ final Complex result1 = operation1.apply(c);
+ final ComplexNumber result2 = operation2.apply(c.getReal(),
c.getImaginary(), ComplexNumber::new);
+
+ assertEquals(() -> c + "." + name + "(): real", expected.real(),
result1.real(), deltaReal);
+ assertEquals(() -> c + "." + name + "(): imaginary", expected.imag(),
result1.imag(), deltaImaginary);
+
+ assertEquals(() -> "ComplexFunctions." + name + "(" + c + "): real",
result1.real(), result2.getReal(), deltaReal);
+ assertEquals(() -> "ComplexFunctions." + name + "(" + c + "):
imaginary", result1.imag(), result2.getImaginary(), deltaImaginary);
+ }
+
+ /**
+ * Assert the two numbers are equal within the provided units of least
precision.
+ * The maximum count of numbers allowed between the two values is {@code
maxUlps - 1}.
Review Comment:
There is no maxUlps. Signs are not checked that they match.
Currently this method has no extra checking over Assertions.assertEquals. So
remove it unless your intention is to do extra checking using ULPs or signs.
##########
commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/ComplexFunctions.java:
##########
@@ -0,0 +1,813 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.numbers.complex;
+
+import java.util.function.DoubleUnaryOperator;
+
+/**
+ * <p>This class is all the Complex arithmetics. All the arithmetics that uses
1 or 2 Complex
+ * number to produce a Complex result.
+ * All arithmetic will create a new instance for the result.</p>
+ */
+public final class ComplexFunctions {
+
+ /** The bit representation of {@code -0.0}. */
+ static final long NEGATIVE_ZERO_LONG_BITS = Double.doubleToLongBits(-0.0);
+ /** Exponent offset in IEEE754 representation. */
+ static final int EXPONENT_OFFSET = 1023;
+
+ /** Mask to remove the sign bit from a long. */
+ static final long UNSIGN_MASK = 0x7fff_ffff_ffff_ffffL;
+ /** Mask to extract the 52-bit mantissa from a long representation of a
double. */
+ static final long MANTISSA_MASK = 0x000f_ffff_ffff_ffffL;
+
+ /** Natural logarithm of 2 (ln(2)). */
+ private static final double LN_2 = Math.log(2);
+ /** {@code 1/2}. */
+ private static final double HALF = 0.5;
+ /** Base 10 logarithm of 10 divided by 2 (log10(e)/2). */
+ private static final double LOG_10E_O_2 = Math.log10(Math.E) / 2;
+ /** Base 10 logarithm of 2 (log10(2)). */
+ private static final double LOG10_2 = Math.log10(2);
+ /** {@code sqrt(2)}. */
+ private static final double ROOT2 = 1.4142135623730951;
+
+ /** The multiplier used to split the double value into hi and low parts.
This must be odd
+ * and a value of 2^s + 1 in the range {@code p/2 <= s <= p-1} where p is
the number of
+ * bits of precision of the floating point number. Here {@code s = 27}.*/
+ private static final double MULTIPLIER = 1.34217729E8;
+
+ /** 54 shifted 20-bits to align with the exponent of the upper 32-bits of
a double. */
+ private static final int EXP_54 = 0x36_00000;
+ /** Represents an exponent of 500 in unbiased form shifted 20-bits to
align with the upper 32-bits of a double. */
+ private static final int EXP_500 = 0x5f3_00000;
+ /** Represents an exponent of 1024 in unbiased form (infinite or nan)
+ * shifted 20-bits to align with the upper 32-bits of a double. */
+ private static final int EXP_1024 = 0x7ff_00000;
+ /** Represents an exponent of -500 in unbiased form shifted 20-bits to
align with the upper 32-bits of a double. */
+ private static final int EXP_NEG_500 = 0x20b_00000;
+ /** 2^600. */
+ private static final double TWO_POW_600 = 0x1.0p+600;
+ /** 2^-600. */
+ private static final double TWO_POW_NEG_600 = 0x1.0p-600;
+
+ /**
+ * Private constructor for utility class.
+ */
+ private ComplexFunctions() {
+
+ }
+
+ /**
+ * Returns the absolute value of the complex number.
+ * <pre>abs(x + i y) = sqrt(x^2 + y^2)</pre>
+ *
+ * <p>This should satisfy the special cases of the hypot function in ISO
C99 F.9.4.3:
+ * "The hypot functions compute the square root of the sum of the squares
of x and y,
+ * without undue overflow or underflow."
+ *
+ * <ul>
+ * <li>hypot(x, y), hypot(y, x), and hypot(x, −y) are equivalent.
+ * <li>hypot(x, ±0) is equivalent to |x|.
+ * <li>hypot(±∞, y) returns +∞, even if y is a NaN.
+ * </ul>
+ *
+ * <p>This method is called by all methods that require the absolute value
of the complex
+ * number, e.g. abs(), sqrt() and log().
+ *
+ * @param real real part of complex number
+ * @param imaginary imaginary part of complex number
+ * @return The absolute value.
+ */
+ public static double abs(double real, double imaginary) {
+ // Specialised implementation of hypot.
+ // See NUMBERS-143
+ return hypot(real, imaginary);
+ }
+
+ /**
+ * Returns the argument of this 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 value returned is between \( -\pi \) (not inclusive)
+ * and \( \pi \) (inclusive), with negative values returned for numbers
with
+ * negative imaginary parts.
+ *
+ * <p>If either real or imaginary part (or both) is NaN, then the result
is NaN.
+ * Infinite parts are handled as {@linkplain Math#atan2} handles them,
+ * essentially treating finite parts as zero in the presence of an
+ * infinite coordinate and returning a multiple of \( \frac{\pi}{4} \)
depending on
+ * the signs of the infinite parts.
+ *
+ * <p>This code follows the
+ * <a href="http://www.iso-9899.info/wiki/The_Standard">ISO C
Standard</a>, Annex G,
+ * in calculating the returned value using the {@code atan2(y, x)} method
for complex
+ * \( x + iy \).
+ *
+ * @param r real part of complex number
+ * @param i imaginary part of complex number
+ * @return The argument of this complex number.
+ * @see Math#atan2(double, double)
+ */
+ public static double arg(double r, double i) {
Review Comment:
This should be called by Complex for consistency.
##########
commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/ComplexUnaryOperator.java:
##########
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.numbers.complex;
+
+/**
+ * Represents a complex operation that accepts a complex number of type
ComplexDouble and produces a ComplexDouble result.
+ * @param <R> Generic
+ */
+@FunctionalInterface
+public interface ComplexUnaryOperator<R> {
+
+ /**
+ * Represents an operator that accepts real and imaginary parts of a
complex number and a complex constructor to produce and return the result.
+ * @param r real
Review Comment:
Javadoc style for [numbers] is to avoid using `The` for all arguments and
have a trailing period `.` The argument descriptions should be improved. Please
try and wrap the javadoc to 100 characters. Look through the rest of Complex
for reference.
```
... Real part \( a \) of the complex number \(a +ib \).
... Imaginary part \( b \) of the complex number \(a +ib \).
... Terminating consumer for the complex result, used to construct a result
of type {@code R}.
```
##########
commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/CStandardTest.java:
##########
@@ -300,6 +300,91 @@ private static void assertConjugateEquality(Complex z,
}
}
+ /**
+ * Assert the operation on the complex number satisfies the conjugate
equality.
+ *
+ * <pre>
+ * op(conj(z)) = conj(op(z))
+ * </pre>
+ *
+ * <p>The results must be binary equal. This includes the sign of zero.
+ *
+ * <h2>ISO C99 equalities</h2>
+ *
+ * <p>Note that this method currently enforces the conjugate equalities
for some cases
+ * where the sign of the real/imaginary parts are unspecified in ISO C99.
This is
+ * allowed (since they are unspecified). The sign specification is
appropriately
+ * handled during testing of odd/even functions. There are some functions
where it
+ * is not possible to satisfy the conjugate equality and also the odd/even
rule.
+ * The compromise made here is to satisfy only one and the other is
allowed to fail
+ * only on the sign of the output result. Known functions where this
applies are:
+ *
+ * <ul>
+ * <li>asinh(NaN, inf)
+ * <li>atanh(NaN, inf)
+ * <li>cosh(NaN, 0.0)
+ * <li>sinh(inf, inf)
+ * <li>sinh(inf, nan)
+ * </ul>
+ *
+ * @param operation the operation
+ */
+ private static void assertConjugateEquality(UnaryOperator<Complex>
operation, ComplexUnaryOperator<ComplexNumber> operation2) {
+ // Edge cases. Inf/NaN are specifically handled in the C99 test cases
+ // but are repeated here to enforce the conjugate equality even when
the C99
+ // standard does not specify a sign. This may be revised in the future.
+ final double[] parts = {Double.NEGATIVE_INFINITY, -1, -0.0, 0.0, 1,
+ Double.POSITIVE_INFINITY, Double.NaN};
+ for (final double x : parts) {
+ for (final double y : parts) {
+ // No conjugate for imaginary NaN
+ if (!Double.isNaN(y)) {
+ assertConjugateEquality(complex(x, y), operation,
operation2, UnspecifiedSign.NONE);
+ }
+ }
+ }
+ // Random numbers
+ final UniformRandomProvider rng = RandomSource.SPLIT_MIX_64.create();
+ for (int i = 0; i < 100; i++) {
+ final double x = next(rng);
+ final double y = next(rng);
+ assertConjugateEquality(complex(x, y), operation, operation2,
UnspecifiedSign.NONE);
+ }
+ }
+
+ /**
+ * Assert the operation on the complex number satisfies the conjugate
equality.
+ *
+ * <pre>
+ * op(conj(z)) = conj(op(z))
+ * </pre>
+ *
+ * <p>The results must be binary equal; the sign of the complex number is
first processed
+ * using the provided sign specification.
+ *
+ * @param z the complex number
+ * @param operation the Complex operation
+ * @param operation2 the ComplexFunctions operation
+ * @param sign the sign specification
+ */
+ private static void assertConjugateEquality(Complex z,
+ UnaryOperator<Complex>
operation, ComplexUnaryOperator<ComplexNumber> operation2,
+ UnspecifiedSign sign) {
+ final Complex c1 = operation.apply(z.conj());
+ final Complex c2 = operation.apply(z).conj();
+ final ComplexNumber c3 = operation2.apply(z.conj().getReal(),
z.conj().getImaginary(), ComplexNumber::new);
Review Comment:
You do not test `c3` is binary equal to `c1`. You do not test it at all.
I would move the usage of operation2 below the test code for operation1. The
first part on operation1 will test the conjugate equality.
Then operation2 must output the same as `c1` and `c2` (when applied to the
conjugate), i.e. `c1 == c3` and `c2 == c4`.
##########
commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/CReferenceTest.java:
##########
@@ -145,6 +145,34 @@ static void assertEquals(Supplier<String> msg, double
expected, double actual, l
}
}
+ /**
+ * Assert the operation on the complex number is equal to the expected
value.
+ *
+ * <p>The results are considered equal within the provided units of least
+ * precision. The maximum count of numbers allowed between the two values
is
+ * {@code maxUlps - 1}.
+ *
+ * <p>Numbers must have the same sign. Thus -0.0 and 0.0 are never equal.
+ *
+ * @param c Input number.
+ * @param name the operation name
+ * @param operation1 the Complex operation
+ * @param operation2 the ComplexFunctions operation
+ * @param expected Expected result.
+ * @param maxUlps the maximum units of least precision between the two
values
+ */
+ static void assertComplex(Complex c,
+ String name, UnaryOperator<Complex> operation1,
ComplexUnaryOperator<ComplexNumber> operation2,
+ Complex expected, long maxUlps) {
+ final Complex z = operation1.apply(c);
+
+ final ComplexNumber y = operation2.apply(c.getReal(),
c.getImaginary(), ComplexNumber::new);
Review Comment:
double space after `, `
##########
commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/CReferenceTest.java:
##########
@@ -145,6 +145,34 @@ static void assertEquals(Supplier<String> msg, double
expected, double actual, l
}
}
+ /**
+ * Assert the operation on the complex number is equal to the expected
value.
+ *
+ * <p>The results are considered equal within the provided units of least
+ * precision. The maximum count of numbers allowed between the two values
is
+ * {@code maxUlps - 1}.
+ *
+ * <p>Numbers must have the same sign. Thus -0.0 and 0.0 are never equal.
+ *
+ * @param c Input number.
+ * @param name the operation name
+ * @param operation1 the Complex operation
+ * @param operation2 the ComplexFunctions operation
+ * @param expected Expected result.
+ * @param maxUlps the maximum units of least precision between the two
values
+ */
+ static void assertComplex(Complex c,
+ String name, UnaryOperator<Complex> operation1,
ComplexUnaryOperator<ComplexNumber> operation2,
+ Complex expected, long maxUlps) {
+ final Complex z = operation1.apply(c);
+
+ final ComplexNumber y = operation2.apply(c.getReal(),
c.getImaginary(), ComplexNumber::new);
+ assertEquals(() -> c + "." + name + "(): real", expected.real(),
z.real(), maxUlps);
+ assertEquals(() -> c + "." + name + "(): imaginary", expected.imag(),
z.imag(), maxUlps);
+
+ assertEquals(() -> c + "." + name + "(): real", z.real(), y.getReal(),
maxUlps);
Review Comment:
Message here should reflect the static API :
```
() -> name + "(" + c + "): real"
```
The assertion should not use the `maxUlps`. The methods should produce the
exact same result
##########
commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/ComplexFunctions.java:
##########
@@ -0,0 +1,813 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.numbers.complex;
+
+import java.util.function.DoubleUnaryOperator;
+
+/**
+ * <p>This class is all the Complex arithmetics. All the arithmetics that uses
1 or 2 Complex
+ * number to produce a Complex result.
+ * All arithmetic will create a new instance for the result.</p>
+ */
+public final class ComplexFunctions {
+
+ /** The bit representation of {@code -0.0}. */
+ static final long NEGATIVE_ZERO_LONG_BITS = Double.doubleToLongBits(-0.0);
+ /** Exponent offset in IEEE754 representation. */
+ static final int EXPONENT_OFFSET = 1023;
+
+ /** Mask to remove the sign bit from a long. */
+ static final long UNSIGN_MASK = 0x7fff_ffff_ffff_ffffL;
+ /** Mask to extract the 52-bit mantissa from a long representation of a
double. */
+ static final long MANTISSA_MASK = 0x000f_ffff_ffff_ffffL;
+
+ /** Natural logarithm of 2 (ln(2)). */
+ private static final double LN_2 = Math.log(2);
+ /** {@code 1/2}. */
+ private static final double HALF = 0.5;
+ /** Base 10 logarithm of 10 divided by 2 (log10(e)/2). */
+ private static final double LOG_10E_O_2 = Math.log10(Math.E) / 2;
+ /** Base 10 logarithm of 2 (log10(2)). */
+ private static final double LOG10_2 = Math.log10(2);
+ /** {@code sqrt(2)}. */
+ private static final double ROOT2 = 1.4142135623730951;
+
+ /** The multiplier used to split the double value into hi and low parts.
This must be odd
+ * and a value of 2^s + 1 in the range {@code p/2 <= s <= p-1} where p is
the number of
+ * bits of precision of the floating point number. Here {@code s = 27}.*/
+ private static final double MULTIPLIER = 1.34217729E8;
+
+ /** 54 shifted 20-bits to align with the exponent of the upper 32-bits of
a double. */
+ private static final int EXP_54 = 0x36_00000;
+ /** Represents an exponent of 500 in unbiased form shifted 20-bits to
align with the upper 32-bits of a double. */
+ private static final int EXP_500 = 0x5f3_00000;
+ /** Represents an exponent of 1024 in unbiased form (infinite or nan)
+ * shifted 20-bits to align with the upper 32-bits of a double. */
+ private static final int EXP_1024 = 0x7ff_00000;
+ /** Represents an exponent of -500 in unbiased form shifted 20-bits to
align with the upper 32-bits of a double. */
+ private static final int EXP_NEG_500 = 0x20b_00000;
+ /** 2^600. */
+ private static final double TWO_POW_600 = 0x1.0p+600;
+ /** 2^-600. */
+ private static final double TWO_POW_NEG_600 = 0x1.0p-600;
+
+ /**
+ * Private constructor for utility class.
+ */
+ private ComplexFunctions() {
+
+ }
+
+ /**
+ * Returns the absolute value of the complex number.
+ * <pre>abs(x + i y) = sqrt(x^2 + y^2)</pre>
+ *
+ * <p>This should satisfy the special cases of the hypot function in ISO
C99 F.9.4.3:
+ * "The hypot functions compute the square root of the sum of the squares
of x and y,
+ * without undue overflow or underflow."
+ *
+ * <ul>
+ * <li>hypot(x, y), hypot(y, x), and hypot(x, −y) are equivalent.
+ * <li>hypot(x, ±0) is equivalent to |x|.
+ * <li>hypot(±∞, y) returns +∞, even if y is a NaN.
+ * </ul>
+ *
+ * <p>This method is called by all methods that require the absolute value
of the complex
+ * number, e.g. abs(), sqrt() and log().
+ *
+ * @param real real part of complex number
+ * @param imaginary imaginary part of complex number
+ * @return The absolute value.
+ */
+ public static double abs(double real, double imaginary) {
+ // Specialised implementation of hypot.
+ // See NUMBERS-143
+ return hypot(real, imaginary);
+ }
+
+ /**
+ * Returns the argument of this 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 value returned is between \( -\pi \) (not inclusive)
+ * and \( \pi \) (inclusive), with negative values returned for numbers
with
+ * negative imaginary parts.
+ *
+ * <p>If either real or imaginary part (or both) is NaN, then the result
is NaN.
+ * Infinite parts are handled as {@linkplain Math#atan2} handles them,
+ * essentially treating finite parts as zero in the presence of an
+ * infinite coordinate and returning a multiple of \( \frac{\pi}{4} \)
depending on
+ * the signs of the infinite parts.
+ *
+ * <p>This code follows the
+ * <a href="http://www.iso-9899.info/wiki/The_Standard">ISO C
Standard</a>, Annex G,
+ * in calculating the returned value using the {@code atan2(y, x)} method
for complex
+ * \( x + iy \).
+ *
+ * @param r real part of complex number
+ * @param i imaginary part of complex number
+ * @return The argument of this complex number.
+ * @see Math#atan2(double, double)
+ */
+ public static double arg(double r, double i) {
+ // Delegate
+ return Math.atan2(i, r);
+ }
+
+ /**
+ * Returns {@code true} if either real or imaginary component of the
complex number is infinite.
+ *
+ * <p>Note: A complex number with at least one infinite part is regarded
+ * as an infinity (even if its other part is a NaN).
+ * @param real real part of complex number
+ * @param imaginary imaginary part of complex number
+ * @return {@code true} if this instance contains an infinite value.
+ * @see Double#isInfinite(double)
+ */
+ private static boolean isInfinite(double real, double imaginary) {
+ return Double.isInfinite(real) || Double.isInfinite(imaginary);
+ }
+
+ /**
+ * Returns the
+ * <a href="http://mathworld.wolfram.com/NaturalLogarithm.html">
+ * natural logarithm</a> of this complex number using its real and
imaginary parts.
Review Comment:
References to `this complex number` should be updated to `the complex number`
##########
commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/ComplexUnaryOperator.java:
##########
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.numbers.complex;
+
+/**
+ * Represents a complex operation that accepts a complex number of type
ComplexDouble and produces a ComplexDouble result.
Review Comment:
This javadoc requires improvement. ComplexDouble does not exist. For example:
```
* 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.
*
* <p>This is a functional interface whose functional method is
* {@link #apply(double, double, ComplexConstructor)}.
*
* @param <R> The type of the complex result
* @since 1.1
```
##########
commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/CStandardTest.java:
##########
@@ -508,6 +593,146 @@ private static void assertComplex(Complex z,
}
}
+ /**
+ * Assert the operation on the complex number is equal to the expected
value.
+ * If the imaginary part is not NaN the operation must also satisfy the
conjugate equality.
+ *
+ * <pre>
+ * op(conj(z)) = conj(op(z))
+ * </pre>
+ *
+ * <p>The results must be binary equal. This includes the sign of zero.
+ * @param z the complex
+ * @param operation the Complex operation
+ * @param expected the expected
+ * @param operation2 the ComplexFunctions operation
+ */
+ private static void assertComplex(Complex z,
+ UnaryOperator<Complex> operation,
Complex expected,
Review Comment:
expected on a new line. It should be after operation2.
For consistency rename operation to operation1. Do this for all methods that
have the two operations.
##########
commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/CStandardTest.java:
##########
@@ -300,6 +300,91 @@ private static void assertConjugateEquality(Complex z,
}
}
+ /**
+ * Assert the operation on the complex number satisfies the conjugate
equality.
+ *
+ * <pre>
+ * op(conj(z)) = conj(op(z))
+ * </pre>
+ *
+ * <p>The results must be binary equal. This includes the sign of zero.
+ *
+ * <h2>ISO C99 equalities</h2>
+ *
+ * <p>Note that this method currently enforces the conjugate equalities
for some cases
+ * where the sign of the real/imaginary parts are unspecified in ISO C99.
This is
+ * allowed (since they are unspecified). The sign specification is
appropriately
+ * handled during testing of odd/even functions. There are some functions
where it
+ * is not possible to satisfy the conjugate equality and also the odd/even
rule.
+ * The compromise made here is to satisfy only one and the other is
allowed to fail
+ * only on the sign of the output result. Known functions where this
applies are:
+ *
+ * <ul>
+ * <li>asinh(NaN, inf)
+ * <li>atanh(NaN, inf)
+ * <li>cosh(NaN, 0.0)
+ * <li>sinh(inf, inf)
+ * <li>sinh(inf, nan)
+ * </ul>
+ *
+ * @param operation the operation
+ */
+ private static void assertConjugateEquality(UnaryOperator<Complex>
operation, ComplexUnaryOperator<ComplexNumber> operation2) {
+ // Edge cases. Inf/NaN are specifically handled in the C99 test cases
+ // but are repeated here to enforce the conjugate equality even when
the C99
+ // standard does not specify a sign. This may be revised in the future.
+ final double[] parts = {Double.NEGATIVE_INFINITY, -1, -0.0, 0.0, 1,
+ Double.POSITIVE_INFINITY, Double.NaN};
+ for (final double x : parts) {
+ for (final double y : parts) {
+ // No conjugate for imaginary NaN
+ if (!Double.isNaN(y)) {
+ assertConjugateEquality(complex(x, y), operation,
operation2, UnspecifiedSign.NONE);
+ }
+ }
+ }
+ // Random numbers
+ final UniformRandomProvider rng = RandomSource.SPLIT_MIX_64.create();
+ for (int i = 0; i < 100; i++) {
+ final double x = next(rng);
+ final double y = next(rng);
+ assertConjugateEquality(complex(x, y), operation, operation2,
UnspecifiedSign.NONE);
+ }
+ }
+
+ /**
+ * Assert the operation on the complex number satisfies the conjugate
equality.
+ *
+ * <pre>
+ * op(conj(z)) = conj(op(z))
+ * </pre>
+ *
+ * <p>The results must be binary equal; the sign of the complex number is
first processed
+ * using the provided sign specification.
+ *
+ * @param z the complex number
+ * @param operation the Complex operation
+ * @param operation2 the ComplexFunctions operation
+ * @param sign the sign specification
+ */
+ private static void assertConjugateEquality(Complex z,
+ UnaryOperator<Complex>
operation, ComplexUnaryOperator<ComplexNumber> operation2,
Review Comment:
Each argument on a new line
##########
commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/CReferenceTest.java:
##########
@@ -227,7 +255,25 @@ private static void assertBiOperation(String name,
/**
* Assert the operation using the data loaded from test resources.
*
- * @param testData Test data resource name.
+ * @param name the operation name
+ * @param operation1 the Complex operation
+ * @param operation2 the ComplexFunctions operation
+ * @param maxUlps the maximum units of least precision between the two
values
+ */
+ private static void assertOperation(String name,
+ UnaryOperator<Complex> operation1,
Review Comment:
Here either compact the trailing arguments or put `maxUlps` on a new line
with the same alignment. For only 4 arguments I would do the later.
##########
commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/CReferenceTest.java:
##########
@@ -145,6 +145,34 @@ static void assertEquals(Supplier<String> msg, double
expected, double actual, l
}
}
+ /**
+ * Assert the operation on the complex number is equal to the expected
value.
+ *
+ * <p>The results are considered equal within the provided units of least
+ * precision. The maximum count of numbers allowed between the two values
is
+ * {@code maxUlps - 1}.
+ *
+ * <p>Numbers must have the same sign. Thus -0.0 and 0.0 are never equal.
+ *
+ * @param c Input number.
+ * @param name the operation name
+ * @param operation1 the Complex operation
+ * @param operation2 the ComplexFunctions operation
+ * @param expected Expected result.
+ * @param maxUlps the maximum units of least precision between the two
values
+ */
+ static void assertComplex(Complex c,
Review Comment:
In the case of many arguments I would wrap the trailing line with an indent
of 8 characters.
##########
commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/CStandardTest.java:
##########
@@ -508,6 +593,146 @@ private static void assertComplex(Complex z,
}
}
+ /**
+ * Assert the operation on the complex number is equal to the expected
value.
+ * If the imaginary part is not NaN the operation must also satisfy the
conjugate equality.
+ *
+ * <pre>
+ * op(conj(z)) = conj(op(z))
+ * </pre>
+ *
+ * <p>The results must be binary equal. This includes the sign of zero.
+ * @param z the complex
+ * @param operation the Complex operation
+ * @param expected the expected
+ * @param operation2 the ComplexFunctions operation
+ */
+ private static void assertComplex(Complex z,
+ UnaryOperator<Complex> operation,
Complex expected,
+ ComplexUnaryOperator<ComplexNumber>
operation2) {
+ assertComplex(z, operation, operation2, expected, FunctionType.NONE,
UnspecifiedSign.NONE);
+ }
+
+ /**
+ * Assert the operation on the complex number is equal to the expected
value. If the
+ * imaginary part is not NaN the operation must also satisfy the conjugate
equality.
+ *
+ * <pre>
+ * op(conj(z)) = conj(op(z))
+ * </pre>
+ *
+ * <p>The results must be binary equal. This includes the sign of zero.
+ * @param z the complex
+ * @param operation the Complex operation
+ * @param operation2 the ComplexFunctions operation
+ * @param expected the expected
+ * @param sign the sign specification
+ */
+ private static void assertComplex(Complex z,
+ UnaryOperator<Complex> operation,
+ ComplexUnaryOperator<ComplexNumber>
operation2,
+ Complex expected, UnspecifiedSign sign) {
+ assertComplex(z, operation, operation2, expected, FunctionType.NONE,
sign);
+ }
+
+ /**
+ * Assert the operation on the complex number is equal to the expected
value. If the
+ * imaginary part is not NaN the operation must also satisfy the conjugate
equality.
+ *
+ * <pre>
+ * op(conj(z)) = conj(op(z))
+ * </pre>
+ *
+ * <p>If the function type is ODD/EVEN the operation must satisfy the
function type equality.
+ *
+ * <pre>
+ * Odd : f(z) = -f(-z)
+ * Even: f(z) = f(-z)
+ * </pre>
+ *
+ * <p>The results must be binary equal. This includes the sign of zero.
+ * @param z the complex
+ * @param operation the Complex operation
+ * @param operation2 the ComplexFunctions operation
+ * @param expected the expected
+ * @param type the type
+ */
+ private static void assertComplex(Complex z,
+ UnaryOperator<Complex> operation,
+ ComplexUnaryOperator<ComplexNumber>
operation2,
+ Complex expected,
+ FunctionType type) {
+ assertComplex(z, operation, operation2, expected, type,
UnspecifiedSign.NONE);
+ }
+
+ /**
+ * Assert the operation on the complex number is equal to the expected
value. If the
+ * imaginary part is not NaN the operation must also satisfy the conjugate
equality.
+ *
+ * <pre>
+ * op(conj(z)) = conj(op(z))
+ * </pre>
+ *
+ * <p>If the function type is ODD/EVEN the operation must satisfy the
function type equality.
+ *
+ * <pre>
+ * Odd : f(z) = -f(-z)
+ * Even: f(z) = f(-z)
+ * </pre>
+ *
+ * <p>An ODD/EVEN function is also tested that the conjugate equalities
hold with {@code -z}.
+ * This effectively enumerates testing: (re, im); (re, -im); (-re, -im);
and (-re, im).
+ *
+ * <p>The results must be binary equal; the sign of the complex number is
first processed
+ * using the provided sign specification.
+ *
+ * @param z the complex
+ * @param operation the Complex operation
+ * @param operation2 the ComplexFunctions operation
+ * @param expected the expected
+ * @param type the type
+ * @param sign the sign specification
+ */
+ private static void assertComplex(Complex z,
Review Comment:
Compact the arguments here to a list, wrapped to about 100 chars, with an
indent of 8 for new lines.
##########
commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/ComplexFunctions.java:
##########
@@ -0,0 +1,813 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.numbers.complex;
+
+import java.util.function.DoubleUnaryOperator;
+
+/**
+ * <p>This class is all the Complex arithmetics. All the arithmetics that uses
1 or 2 Complex
+ * number to produce a Complex result.
+ * All arithmetic will create a new instance for the result.</p>
+ */
+public final class ComplexFunctions {
+
+ /** The bit representation of {@code -0.0}. */
+ static final long NEGATIVE_ZERO_LONG_BITS = Double.doubleToLongBits(-0.0);
+ /** Exponent offset in IEEE754 representation. */
+ static final int EXPONENT_OFFSET = 1023;
+
+ /** Mask to remove the sign bit from a long. */
+ static final long UNSIGN_MASK = 0x7fff_ffff_ffff_ffffL;
+ /** Mask to extract the 52-bit mantissa from a long representation of a
double. */
+ static final long MANTISSA_MASK = 0x000f_ffff_ffff_ffffL;
+
+ /** Natural logarithm of 2 (ln(2)). */
+ private static final double LN_2 = Math.log(2);
+ /** {@code 1/2}. */
+ private static final double HALF = 0.5;
+ /** Base 10 logarithm of 10 divided by 2 (log10(e)/2). */
+ private static final double LOG_10E_O_2 = Math.log10(Math.E) / 2;
+ /** Base 10 logarithm of 2 (log10(2)). */
+ private static final double LOG10_2 = Math.log10(2);
+ /** {@code sqrt(2)}. */
+ private static final double ROOT2 = 1.4142135623730951;
+
+ /** The multiplier used to split the double value into hi and low parts.
This must be odd
+ * and a value of 2^s + 1 in the range {@code p/2 <= s <= p-1} where p is
the number of
+ * bits of precision of the floating point number. Here {@code s = 27}.*/
+ private static final double MULTIPLIER = 1.34217729E8;
+
+ /** 54 shifted 20-bits to align with the exponent of the upper 32-bits of
a double. */
+ private static final int EXP_54 = 0x36_00000;
+ /** Represents an exponent of 500 in unbiased form shifted 20-bits to
align with the upper 32-bits of a double. */
+ private static final int EXP_500 = 0x5f3_00000;
+ /** Represents an exponent of 1024 in unbiased form (infinite or nan)
+ * shifted 20-bits to align with the upper 32-bits of a double. */
+ private static final int EXP_1024 = 0x7ff_00000;
+ /** Represents an exponent of -500 in unbiased form shifted 20-bits to
align with the upper 32-bits of a double. */
+ private static final int EXP_NEG_500 = 0x20b_00000;
+ /** 2^600. */
+ private static final double TWO_POW_600 = 0x1.0p+600;
+ /** 2^-600. */
+ private static final double TWO_POW_NEG_600 = 0x1.0p-600;
+
+ /**
+ * Private constructor for utility class.
+ */
+ private ComplexFunctions() {
+
+ }
+
+ /**
+ * Returns the absolute value of the complex number.
+ * <pre>abs(x + i y) = sqrt(x^2 + y^2)</pre>
+ *
+ * <p>This should satisfy the special cases of the hypot function in ISO
C99 F.9.4.3:
+ * "The hypot functions compute the square root of the sum of the squares
of x and y,
+ * without undue overflow or underflow."
+ *
+ * <ul>
+ * <li>hypot(x, y), hypot(y, x), and hypot(x, −y) are equivalent.
+ * <li>hypot(x, ±0) is equivalent to |x|.
+ * <li>hypot(±∞, y) returns +∞, even if y is a NaN.
+ * </ul>
+ *
+ * <p>This method is called by all methods that require the absolute value
of the complex
+ * number, e.g. abs(), sqrt() and log().
+ *
+ * @param real real part of complex number
+ * @param imaginary imaginary part of complex number
+ * @return The absolute value.
+ */
+ public static double abs(double real, double imaginary) {
+ // Specialised implementation of hypot.
+ // See NUMBERS-143
+ return hypot(real, imaginary);
+ }
+
+ /**
+ * Returns the argument of this 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 value returned is between \( -\pi \) (not inclusive)
+ * and \( \pi \) (inclusive), with negative values returned for numbers
with
+ * negative imaginary parts.
+ *
+ * <p>If either real or imaginary part (or both) is NaN, then the result
is NaN.
+ * Infinite parts are handled as {@linkplain Math#atan2} handles them,
+ * essentially treating finite parts as zero in the presence of an
+ * infinite coordinate and returning a multiple of \( \frac{\pi}{4} \)
depending on
+ * the signs of the infinite parts.
+ *
+ * <p>This code follows the
+ * <a href="http://www.iso-9899.info/wiki/The_Standard">ISO C
Standard</a>, Annex G,
+ * in calculating the returned value using the {@code atan2(y, x)} method
for complex
+ * \( x + iy \).
+ *
+ * @param r real part of complex number
+ * @param i imaginary part of complex number
+ * @return The argument of this complex number.
+ * @see Math#atan2(double, double)
+ */
+ public static double arg(double r, double i) {
+ // Delegate
+ return Math.atan2(i, r);
+ }
+
+ /**
+ * Returns {@code true} if either real or imaginary component of the
complex number is infinite.
+ *
+ * <p>Note: A complex number with at least one infinite part is regarded
+ * as an infinity (even if its other part is a NaN).
+ * @param real real part of complex number
+ * @param imaginary imaginary part of complex number
+ * @return {@code true} if this instance contains an infinite value.
+ * @see Double#isInfinite(double)
+ */
+ private static boolean isInfinite(double real, double imaginary) {
+ return Double.isInfinite(real) || Double.isInfinite(imaginary);
+ }
+
+ /**
+ * Returns the
+ * <a href="http://mathworld.wolfram.com/NaturalLogarithm.html">
+ * natural logarithm</a> of this complex number using its real and
imaginary parts.
+ *
+ * <p>The natural logarithm of \( z \) is unbounded along the real axis and
+ * in the range \( [-\pi, \pi] \) along the imaginary axis. The imaginary
part of the
+ * natural logarithm has a branch cut along the negative real axis \(
(-infty,0] \).
+ * Special cases:
+ *
+ * <ul>
+ * <li>{@code z.conj().log() == z.log().conj()}.
+ * <li>If {@code z} is −0 + i0, returns −∞ + iπ ("divide-by-zero"
floating-point operation).
+ * <li>If {@code z} is +0 + i0, returns −∞ + i0 ("divide-by-zero"
floating-point operation).
+ * <li>If {@code z} is x + i∞ for finite x, returns +∞ + iπ/2.
+ * <li>If {@code z} is x + iNaN for finite x, returns NaN + iNaN
("invalid" floating-point operation).
+ * <li>If {@code z} is −∞ + iy for finite positive-signed y, returns +∞ +
iπ.
+ * <li>If {@code z} is +∞ + iy for finite positive-signed y, returns +∞ +
i0.
+ * <li>If {@code z} is −∞ + i∞, returns +∞ + i3π/4.
+ * <li>If {@code z} is +∞ + i∞, returns +∞ + iπ/4.
+ * <li>If {@code z} is ±∞ + iNaN, returns +∞ + iNaN.
+ * <li>If {@code z} is NaN + iy for finite y, returns NaN + iNaN
("invalid" floating-point operation).
+ * <li>If {@code z} is NaN + i∞, returns +∞ + iNaN.
+ * <li>If {@code z} is NaN + iNaN, returns NaN + iNaN.
+ * </ul>
+ *
+ * <p>Implements the formula:
+ *
+ * <p>\[ \ln(z) = \ln |z| + i \arg(z) \]
+ *
+ * <p>where \( |z| \) is the absolute and \( \arg(z) \) is the argument.
+ *
+ * <p>The implementation is based on the method described in:</p>
+ * <blockquote>
+ * T E Hull, Thomas F Fairgrieve and Ping Tak Peter Tang (1994)
+ * Implementing complex elementary functions using exception handling.
+ * ACM Transactions on Mathematical Software, Vol 20, No 2, pp 215-244.
+ * </blockquote>
+ *
+ * @param real real part of complex number
+ * @param imaginary imaginary part of complex number
+ * @param constructor Constructor
+ * @param <R> generic
+ * @return The natural logarithm of this complex number.
+ * @see Math#log(double)
+ * @see <a
href="http://functions.wolfram.com/ElementaryFunctions/Log/">Log</a>
+ */
+ public static <R> R log(double real, double imaginary,
ComplexConstructor<R> constructor) {
+ return log(Math::log, HALF, LN_2, real, imaginary, constructor);
+ }
+
+ /**
+ * Returns the base 10
+ * <a href="http://mathworld.wolfram.com/CommonLogarithm.html">
+ * common logarithm</a> of this complex number using its real and
imaginary parts.
+ *
+ * <p>The common logarithm of \( z \) is unbounded along the real axis and
+ * in the range \( [-\pi, \pi] \) along the imaginary axis. The imaginary
part of the
+ * common logarithm has a branch cut along the negative real axis \(
(-infty,0] \).
+ * Special cases are as defined in the log:
+ *
+ * <p>Implements the formula:
+ *
+ * <p>\[ \log_{10}(z) = \log_{10} |z| + i \arg(z) \]
+ *
+ * <p>where \( |z| \) is the absolute and \( \arg(z) \) is the argument.
+ *
+ * @param real real part of complex number
+ * @param imaginary imaginary part of complex number
+ * @param constructor Constructor
+ * @param <R> generic
+ * @return The base 10 logarithm of this complex number.
+ * @see Math#log10(double)
+ */
+ public static <R> R log10(double real, double imaginary,
ComplexConstructor<R> constructor) {
+ return log(Math::log10, LOG_10E_O_2, LOG10_2, real, imaginary,
constructor);
+ }
+
+ /**
+ * Returns the logarithm of complex number using its real and
+ * imaginary parts and the provided function.
+ * Implements the formula:
+ *
+ * <pre>
+ * log(x + i y) = log(|x + i y|) + i arg(x + i y)</pre>
+ *
+ * <p>Warning: The argument {@code logOf2} must be equal to {@code log(2)}
using the
+ * provided log function otherwise scaling using powers of 2 in the case
of overflow
+ * will be incorrect. This is provided as an internal optimisation.
+ *
+ * @param log Log function.
+ * @param logOfeOver2 The log function applied to e, then divided by 2.
+ * @param logOf2 The log function applied to 2.
+ * @param real real part of complex number
+ * @param imaginary imaginary part of complex number
+ * @param constructor Constructor for the returned complex.
+ * @param <R> generic
+ * @return The logarithm of this complex number.
+ */
+ private static <R> R log(DoubleUnaryOperator log, double logOfeOver2,
double logOf2,
+ double real, double imaginary,
ComplexConstructor<R> constructor) {
Review Comment:
Fix the trailing argument indentation
##########
commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/ComplexNumber.java:
##########
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.numbers.complex;
+
+/**
+ * Cartesian representation of a complex number. The complex number is
expressed
+ * in the form \( a + ib \) where \( a \) and \( b \) are real numbers and \(
i \)
+ * is the imaginary unit which satisfies the equation \( i^2 = -1 \). For the
+ * complex number \( a + ib \), \( a \) is called the <em>real part</em> and
+ * \( b \) is called the <em>imaginary part</em>.
+ */
+class ComplexNumber {
+
+ private final double real;
Review Comment:
Comment these for completeness.
##########
commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/TestUtils.java:
##########
@@ -387,4 +389,94 @@ private static String preprocessTestData(String line,
TestDataFlagOption option,
}
return line;
}
+
+ /**
+ * Assert the unary complex operation on the complex number is equal to
the expected value.
+ * No delta.
+ * If the imaginary part is not NaN the operation must also satisfy the
conjugate equality.
+ *
+ * <pre>
+ * op(conj(z)) = conj(op(z))
+ * </pre>
+ *
+ * <p>The results must be binary equal. This includes the sign of zero.
+ * @param c the complex
+ * @param operation1 the operation
+ * @param operation2 the second operation
+ * @param expected the expected
+ * @param name the operation name
+ */
+ static void assertComplexUnary(Complex c,
+ UnaryOperator<Complex> operation1,
ComplexUnaryOperator<ComplexNumber> operation2,
+ Complex expected, String name) {
+ assertComplexUnary(c, operation1, operation2, expected, name, 0.0D,
0.0D);
Review Comment:
Please apply my recommendations from the previous PR. This method should be
name `assertSame`. It should not call `assertEquals` with a delta as that will
fail to detect `-0.0` is not equal to `0.0`. The javadoc is correct about
binary equality. The implementation is wrong.
##########
commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/TestUtils.java:
##########
@@ -387,4 +389,94 @@ private static String preprocessTestData(String line,
TestDataFlagOption option,
}
return line;
}
+
+ /**
+ * Assert the unary complex operation on the complex number is equal to
the expected value.
+ * No delta.
+ * If the imaginary part is not NaN the operation must also satisfy the
conjugate equality.
+ *
+ * <pre>
+ * op(conj(z)) = conj(op(z))
+ * </pre>
+ *
+ * <p>The results must be binary equal. This includes the sign of zero.
+ * @param c the complex
+ * @param operation1 the operation
+ * @param operation2 the second operation
+ * @param expected the expected
+ * @param name the operation name
+ */
+ static void assertComplexUnary(Complex c,
+ UnaryOperator<Complex> operation1,
ComplexUnaryOperator<ComplexNumber> operation2,
+ Complex expected, String name) {
+ assertComplexUnary(c, operation1, operation2, expected, name, 0.0D,
0.0D);
+ }
+
+ /**
+ * Assert the unary complex operation on the complex number is equal to
the expected value.
+ * If the imaginary part is not NaN the operation must also satisfy the
conjugate equality.
Review Comment:
Wrong
##########
commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/TestUtils.java:
##########
@@ -387,4 +389,94 @@ private static String preprocessTestData(String line,
TestDataFlagOption option,
}
return line;
}
+
+ /**
+ * Assert the unary complex operation on the complex number is equal to
the expected value.
+ * No delta.
+ * If the imaginary part is not NaN the operation must also satisfy the
conjugate equality.
+ *
+ * <pre>
+ * op(conj(z)) = conj(op(z))
+ * </pre>
+ *
+ * <p>The results must be binary equal. This includes the sign of zero.
+ * @param c the complex
+ * @param operation1 the operation
Review Comment:
the operation on the Complex object
the operation on the complex real and imaginary parts
##########
commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/TestUtils.java:
##########
@@ -387,4 +389,94 @@ private static String preprocessTestData(String line,
TestDataFlagOption option,
}
return line;
}
+
+ /**
+ * Assert the unary complex operation on the complex number is equal to
the expected value.
+ * No delta.
+ * If the imaginary part is not NaN the operation must also satisfy the
conjugate equality.
+ *
+ * <pre>
+ * op(conj(z)) = conj(op(z))
+ * </pre>
+ *
+ * <p>The results must be binary equal. This includes the sign of zero.
+ * @param c the complex
+ * @param operation1 the operation
+ * @param operation2 the second operation
+ * @param expected the expected
+ * @param name the operation name
+ */
+ static void assertComplexUnary(Complex c,
+ UnaryOperator<Complex> operation1,
ComplexUnaryOperator<ComplexNumber> operation2,
+ Complex expected, String name) {
+ assertComplexUnary(c, operation1, operation2, expected, name, 0.0D,
0.0D);
+ }
+
+ /**
+ * Assert the unary complex operation on the complex number is equal to
the expected value.
+ * If the imaginary part is not NaN the operation must also satisfy the
conjugate equality.
+ *
+ * <pre>
+ * op(conj(z)) = conj(op(z))
+ * </pre>
+ *
+ * <p>The results must be binary equal. This includes the sign of zero.
+ * @param c the complex
+ * @param operation1 the operation
+ * @param operation2 the second operation
+ * @param expected the expected
+ * @param name the operation name
+ * @param delta delta
+ */
+ static void assertComplexUnary(Complex c,
+ UnaryOperator<Complex> operation1,
ComplexUnaryOperator<ComplexNumber> operation2,
+ Complex expected, String name, double
delta) {
+ assertComplexUnary(c, operation1, operation2, expected, name, delta,
delta);
+ }
+
+ /**
+ * Assert the unary complex operation on the complex number is equal to
the expected value.
+ * If the imaginary part is not NaN the operation must also satisfy the
conjugate equality.
Review Comment:
Wrong
##########
commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/TestUtils.java:
##########
@@ -387,4 +389,94 @@ private static String preprocessTestData(String line,
TestDataFlagOption option,
}
return line;
}
+
+ /**
+ * Assert the unary complex operation on the complex number is equal to
the expected value.
+ * No delta.
+ * If the imaginary part is not NaN the operation must also satisfy the
conjugate equality.
+ *
+ * <pre>
+ * op(conj(z)) = conj(op(z))
+ * </pre>
+ *
+ * <p>The results must be binary equal. This includes the sign of zero.
+ * @param c the complex
+ * @param operation1 the operation
+ * @param operation2 the second operation
+ * @param expected the expected
+ * @param name the operation name
+ */
+ static void assertComplexUnary(Complex c,
+ UnaryOperator<Complex> operation1,
ComplexUnaryOperator<ComplexNumber> operation2,
+ Complex expected, String name) {
+ assertComplexUnary(c, operation1, operation2, expected, name, 0.0D,
0.0D);
+ }
+
+ /**
+ * Assert the unary complex operation on the complex number is equal to
the expected value.
+ * If the imaginary part is not NaN the operation must also satisfy the
conjugate equality.
+ *
+ * <pre>
+ * op(conj(z)) = conj(op(z))
+ * </pre>
+ *
+ * <p>The results must be binary equal. This includes the sign of zero.
Review Comment:
Wrong
##########
commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/CStandardTest.java:
##########
@@ -508,6 +593,146 @@ private static void assertComplex(Complex z,
}
}
+ /**
+ * Assert the operation on the complex number is equal to the expected
value.
+ * If the imaginary part is not NaN the operation must also satisfy the
conjugate equality.
+ *
+ * <pre>
+ * op(conj(z)) = conj(op(z))
+ * </pre>
+ *
+ * <p>The results must be binary equal. This includes the sign of zero.
+ * @param z the complex
+ * @param operation the Complex operation
+ * @param expected the expected
+ * @param operation2 the ComplexFunctions operation
+ */
+ private static void assertComplex(Complex z,
+ UnaryOperator<Complex> operation,
Complex expected,
+ ComplexUnaryOperator<ComplexNumber>
operation2) {
+ assertComplex(z, operation, operation2, expected, FunctionType.NONE,
UnspecifiedSign.NONE);
+ }
+
+ /**
+ * Assert the operation on the complex number is equal to the expected
value. If the
+ * imaginary part is not NaN the operation must also satisfy the conjugate
equality.
+ *
+ * <pre>
+ * op(conj(z)) = conj(op(z))
+ * </pre>
+ *
+ * <p>The results must be binary equal. This includes the sign of zero.
+ * @param z the complex
+ * @param operation the Complex operation
+ * @param operation2 the ComplexFunctions operation
+ * @param expected the expected
+ * @param sign the sign specification
+ */
+ private static void assertComplex(Complex z,
+ UnaryOperator<Complex> operation,
+ ComplexUnaryOperator<ComplexNumber>
operation2,
+ Complex expected, UnspecifiedSign sign) {
+ assertComplex(z, operation, operation2, expected, FunctionType.NONE,
sign);
+ }
+
+ /**
+ * Assert the operation on the complex number is equal to the expected
value. If the
+ * imaginary part is not NaN the operation must also satisfy the conjugate
equality.
+ *
+ * <pre>
+ * op(conj(z)) = conj(op(z))
+ * </pre>
+ *
+ * <p>If the function type is ODD/EVEN the operation must satisfy the
function type equality.
+ *
+ * <pre>
+ * Odd : f(z) = -f(-z)
+ * Even: f(z) = f(-z)
+ * </pre>
+ *
+ * <p>The results must be binary equal. This includes the sign of zero.
+ * @param z the complex
Review Comment:
`@param` has a leading double space
##########
commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/CStandardTest.java:
##########
@@ -508,6 +593,146 @@ private static void assertComplex(Complex z,
}
}
+ /**
+ * Assert the operation on the complex number is equal to the expected
value.
+ * If the imaginary part is not NaN the operation must also satisfy the
conjugate equality.
+ *
+ * <pre>
+ * op(conj(z)) = conj(op(z))
+ * </pre>
+ *
+ * <p>The results must be binary equal. This includes the sign of zero.
+ * @param z the complex
+ * @param operation the Complex operation
+ * @param expected the expected
+ * @param operation2 the ComplexFunctions operation
+ */
+ private static void assertComplex(Complex z,
+ UnaryOperator<Complex> operation,
Complex expected,
+ ComplexUnaryOperator<ComplexNumber>
operation2) {
+ assertComplex(z, operation, operation2, expected, FunctionType.NONE,
UnspecifiedSign.NONE);
+ }
+
+ /**
+ * Assert the operation on the complex number is equal to the expected
value. If the
+ * imaginary part is not NaN the operation must also satisfy the conjugate
equality.
+ *
+ * <pre>
+ * op(conj(z)) = conj(op(z))
+ * </pre>
+ *
+ * <p>The results must be binary equal. This includes the sign of zero.
+ * @param z the complex
+ * @param operation the Complex operation
+ * @param operation2 the ComplexFunctions operation
+ * @param expected the expected
+ * @param sign the sign specification
+ */
+ private static void assertComplex(Complex z,
+ UnaryOperator<Complex> operation,
+ ComplexUnaryOperator<ComplexNumber>
operation2,
+ Complex expected, UnspecifiedSign sign) {
+ assertComplex(z, operation, operation2, expected, FunctionType.NONE,
sign);
+ }
+
+ /**
+ * Assert the operation on the complex number is equal to the expected
value. If the
+ * imaginary part is not NaN the operation must also satisfy the conjugate
equality.
+ *
+ * <pre>
+ * op(conj(z)) = conj(op(z))
+ * </pre>
+ *
+ * <p>If the function type is ODD/EVEN the operation must satisfy the
function type equality.
+ *
+ * <pre>
+ * Odd : f(z) = -f(-z)
+ * Even: f(z) = f(-z)
+ * </pre>
+ *
+ * <p>The results must be binary equal. This includes the sign of zero.
+ * @param z the complex
+ * @param operation the Complex operation
+ * @param operation2 the ComplexFunctions operation
+ * @param expected the expected
+ * @param type the type
+ */
+ private static void assertComplex(Complex z,
+ UnaryOperator<Complex> operation,
+ ComplexUnaryOperator<ComplexNumber>
operation2,
+ Complex expected,
+ FunctionType type) {
+ assertComplex(z, operation, operation2, expected, type,
UnspecifiedSign.NONE);
+ }
+
+ /**
+ * Assert the operation on the complex number is equal to the expected
value. If the
+ * imaginary part is not NaN the operation must also satisfy the conjugate
equality.
+ *
+ * <pre>
+ * op(conj(z)) = conj(op(z))
+ * </pre>
+ *
+ * <p>If the function type is ODD/EVEN the operation must satisfy the
function type equality.
+ *
+ * <pre>
+ * Odd : f(z) = -f(-z)
+ * Even: f(z) = f(-z)
+ * </pre>
+ *
+ * <p>An ODD/EVEN function is also tested that the conjugate equalities
hold with {@code -z}.
+ * This effectively enumerates testing: (re, im); (re, -im); (-re, -im);
and (-re, im).
+ *
+ * <p>The results must be binary equal; the sign of the complex number is
first processed
+ * using the provided sign specification.
+ *
+ * @param z the complex
+ * @param operation the Complex operation
+ * @param operation2 the ComplexFunctions operation
+ * @param expected the expected
+ * @param type the type
+ * @param sign the sign specification
+ */
+ private static void assertComplex(Complex z,
+ UnaryOperator<Complex> operation,
ComplexUnaryOperator<ComplexNumber> operation2, Complex expected,
+ FunctionType type, UnspecifiedSign sign)
{
+ // Developer note: Set the sign specification to UnspecifiedSign.NONE
+ // to see which equalities fail. They should be for input defined
+ // in ISO C99 with an unspecified output sign, e.g.
+ // sign = UnspecifiedSign.NONE
+
+ // Test the operation
+ final Complex c = operation.apply(z);
+ final ComplexNumber c1 = operation2.apply(z.getReal(),
z.getImaginary(), ComplexNumber::new);
Review Comment:
As before, where are you testing `c1`? Make it clear that operation2 is
separate. It must match operation1 exactly.
##########
commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/TestUtils.java:
##########
@@ -387,4 +389,94 @@ private static String preprocessTestData(String line,
TestDataFlagOption option,
}
return line;
}
+
+ /**
+ * Assert the unary complex operation on the complex number is equal to
the expected value.
+ * No delta.
+ * If the imaginary part is not NaN the operation must also satisfy the
conjugate equality.
+ *
+ * <pre>
+ * op(conj(z)) = conj(op(z))
+ * </pre>
+ *
+ * <p>The results must be binary equal. This includes the sign of zero.
+ * @param c the complex
+ * @param operation1 the operation
+ * @param operation2 the second operation
+ * @param expected the expected
+ * @param name the operation name
+ */
+ static void assertComplexUnary(Complex c,
+ UnaryOperator<Complex> operation1,
ComplexUnaryOperator<ComplexNumber> operation2,
+ Complex expected, String name) {
+ assertComplexUnary(c, operation1, operation2, expected, name, 0.0D,
0.0D);
+ }
+
+ /**
+ * Assert the unary complex operation on the complex number is equal to
the expected value.
+ * If the imaginary part is not NaN the operation must also satisfy the
conjugate equality.
+ *
+ * <pre>
+ * op(conj(z)) = conj(op(z))
+ * </pre>
+ *
+ * <p>The results must be binary equal. This includes the sign of zero.
+ * @param c the complex
+ * @param operation1 the operation
+ * @param operation2 the second operation
+ * @param expected the expected
+ * @param name the operation name
+ * @param delta delta
+ */
+ static void assertComplexUnary(Complex c,
+ UnaryOperator<Complex> operation1,
ComplexUnaryOperator<ComplexNumber> operation2,
+ Complex expected, String name, double
delta) {
+ assertComplexUnary(c, operation1, operation2, expected, name, delta,
delta);
+ }
+
+ /**
+ * Assert the unary complex operation on the complex number is equal to
the expected value.
+ * If the imaginary part is not NaN the operation must also satisfy the
conjugate equality.
+ *
+ * <pre>
+ * op(conj(z)) = conj(op(z))
+ * </pre>
+ *
+ * <p>The results must be binary equal. This includes the sign of zero.
+ * @param c the complex
+ * @param operation1 the operation
+ * @param operation2 the second operation
+ * @param expected the expected
+ * @param name the operation name
+ * @param deltaReal real delta
+ * @param deltaImaginary imaginary delta
+ */
+ static void assertComplexUnary(Complex c,
Review Comment:
assertEquals
##########
commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/TestUtils.java:
##########
@@ -387,4 +389,94 @@ private static String preprocessTestData(String line,
TestDataFlagOption option,
}
return line;
}
+
+ /**
+ * Assert the unary complex operation on the complex number is equal to
the expected value.
+ * No delta.
+ * If the imaginary part is not NaN the operation must also satisfy the
conjugate equality.
+ *
+ * <pre>
+ * op(conj(z)) = conj(op(z))
+ * </pre>
+ *
+ * <p>The results must be binary equal. This includes the sign of zero.
+ * @param c the complex
+ * @param operation1 the operation
+ * @param operation2 the second operation
+ * @param expected the expected
+ * @param name the operation name
+ */
+ static void assertComplexUnary(Complex c,
+ UnaryOperator<Complex> operation1,
ComplexUnaryOperator<ComplexNumber> operation2,
+ Complex expected, String name) {
+ assertComplexUnary(c, operation1, operation2, expected, name, 0.0D,
0.0D);
+ }
+
+ /**
+ * Assert the unary complex operation on the complex number is equal to
the expected value.
+ * If the imaginary part is not NaN the operation must also satisfy the
conjugate equality.
+ *
+ * <pre>
+ * op(conj(z)) = conj(op(z))
+ * </pre>
+ *
+ * <p>The results must be binary equal. This includes the sign of zero.
+ * @param c the complex
+ * @param operation1 the operation
+ * @param operation2 the second operation
+ * @param expected the expected
+ * @param name the operation name
+ * @param delta delta
+ */
+ static void assertComplexUnary(Complex c,
+ UnaryOperator<Complex> operation1,
ComplexUnaryOperator<ComplexNumber> operation2,
+ Complex expected, String name, double
delta) {
+ assertComplexUnary(c, operation1, operation2, expected, name, delta,
delta);
+ }
+
+ /**
+ * Assert the unary complex operation on the complex number is equal to
the expected value.
+ * If the imaginary part is not NaN the operation must also satisfy the
conjugate equality.
+ *
+ * <pre>
+ * op(conj(z)) = conj(op(z))
+ * </pre>
+ *
+ * <p>The results must be binary equal. This includes the sign of zero.
Review Comment:
Wrong
##########
commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/TestUtils.java:
##########
@@ -387,4 +389,94 @@ private static String preprocessTestData(String line,
TestDataFlagOption option,
}
return line;
}
+
+ /**
+ * Assert the unary complex operation on the complex number is equal to
the expected value.
+ * No delta.
+ * If the imaginary part is not NaN the operation must also satisfy the
conjugate equality.
+ *
+ * <pre>
+ * op(conj(z)) = conj(op(z))
+ * </pre>
+ *
+ * <p>The results must be binary equal. This includes the sign of zero.
+ * @param c the complex
+ * @param operation1 the operation
+ * @param operation2 the second operation
+ * @param expected the expected
+ * @param name the operation name
+ */
+ static void assertComplexUnary(Complex c,
+ UnaryOperator<Complex> operation1,
ComplexUnaryOperator<ComplexNumber> operation2,
+ Complex expected, String name) {
+ assertComplexUnary(c, operation1, operation2, expected, name, 0.0D,
0.0D);
+ }
+
+ /**
+ * Assert the unary complex operation on the complex number is equal to
the expected value.
+ * If the imaginary part is not NaN the operation must also satisfy the
conjugate equality.
+ *
+ * <pre>
+ * op(conj(z)) = conj(op(z))
+ * </pre>
+ *
+ * <p>The results must be binary equal. This includes the sign of zero.
+ * @param c the complex
+ * @param operation1 the operation
+ * @param operation2 the second operation
+ * @param expected the expected
+ * @param name the operation name
+ * @param delta delta
+ */
+ static void assertComplexUnary(Complex c,
+ UnaryOperator<Complex> operation1,
ComplexUnaryOperator<ComplexNumber> operation2,
+ Complex expected, String name, double
delta) {
+ assertComplexUnary(c, operation1, operation2, expected, name, delta,
delta);
+ }
+
+ /**
+ * Assert the unary complex operation on the complex number is equal to
the expected value.
+ * If the imaginary part is not NaN the operation must also satisfy the
conjugate equality.
+ *
+ * <pre>
+ * op(conj(z)) = conj(op(z))
+ * </pre>
+ *
+ * <p>The results must be binary equal. This includes the sign of zero.
+ * @param c the complex
+ * @param operation1 the operation
+ * @param operation2 the second operation
+ * @param expected the expected
+ * @param name the operation name
+ * @param deltaReal real delta
+ * @param deltaImaginary imaginary delta
+ */
+ static void assertComplexUnary(Complex c,
+ UnaryOperator<Complex> operation1,
ComplexUnaryOperator<ComplexNumber> operation2,
+ Complex expected, String name, double
deltaReal, double deltaImaginary) {
+ final Complex result1 = operation1.apply(c);
+ final ComplexNumber result2 = operation2.apply(c.getReal(),
c.getImaginary(), ComplexNumber::new);
+
+ assertEquals(() -> c + "." + name + "(): real", expected.real(),
result1.real(), deltaReal);
+ assertEquals(() -> c + "." + name + "(): imaginary", expected.imag(),
result1.imag(), deltaImaginary);
+
+ assertEquals(() -> "ComplexFunctions." + name + "(" + c + "): real",
result1.real(), result2.getReal(), deltaReal);
Review Comment:
The `ComplexFunctions.` is a big assumption. This TestUtils class knows
nothing of what operation2 is using. Drop the term from the message.
This function should test ComplexNumber is **exactly** the same as Complex.
It should not test ComplexNumber with expected:
```Java
Assertions.assertEquals(() -> ... , result1.real(), result2.getReal());
Assertions.assertEquals(() -> ... , result1.imag(), result2.getImaginary());
```
##########
commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/TestUtils.java:
##########
@@ -387,4 +389,94 @@ private static String preprocessTestData(String line,
TestDataFlagOption option,
}
return line;
}
+
+ /**
+ * Assert the unary complex operation on the complex number is equal to
the expected value.
+ * No delta.
+ * If the imaginary part is not NaN the operation must also satisfy the
conjugate equality.
+ *
+ * <pre>
+ * op(conj(z)) = conj(op(z))
+ * </pre>
+ *
+ * <p>The results must be binary equal. This includes the sign of zero.
+ * @param c the complex
+ * @param operation1 the operation
+ * @param operation2 the second operation
+ * @param expected the expected
+ * @param name the operation name
+ */
+ static void assertComplexUnary(Complex c,
+ UnaryOperator<Complex> operation1,
ComplexUnaryOperator<ComplexNumber> operation2,
+ Complex expected, String name) {
+ assertComplexUnary(c, operation1, operation2, expected, name, 0.0D,
0.0D);
+ }
+
+ /**
+ * Assert the unary complex operation on the complex number is equal to
the expected value.
+ * If the imaginary part is not NaN the operation must also satisfy the
conjugate equality.
+ *
+ * <pre>
+ * op(conj(z)) = conj(op(z))
+ * </pre>
+ *
+ * <p>The results must be binary equal. This includes the sign of zero.
+ * @param c the complex
+ * @param operation1 the operation
+ * @param operation2 the second operation
+ * @param expected the expected
+ * @param name the operation name
+ * @param delta delta
+ */
+ static void assertComplexUnary(Complex c,
Review Comment:
assertEquals
##########
commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/ComplexFunctions.java:
##########
@@ -0,0 +1,813 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.numbers.complex;
+
+import java.util.function.DoubleUnaryOperator;
+
+/**
+ * <p>This class is all the Complex arithmetics. All the arithmetics that uses
1 or 2 Complex
+ * number to produce a Complex result.
+ * All arithmetic will create a new instance for the result.</p>
+ */
+public final class ComplexFunctions {
+
+ /** The bit representation of {@code -0.0}. */
+ static final long NEGATIVE_ZERO_LONG_BITS = Double.doubleToLongBits(-0.0);
+ /** Exponent offset in IEEE754 representation. */
+ static final int EXPONENT_OFFSET = 1023;
+
+ /** Mask to remove the sign bit from a long. */
+ static final long UNSIGN_MASK = 0x7fff_ffff_ffff_ffffL;
+ /** Mask to extract the 52-bit mantissa from a long representation of a
double. */
+ static final long MANTISSA_MASK = 0x000f_ffff_ffff_ffffL;
+
+ /** Natural logarithm of 2 (ln(2)). */
+ private static final double LN_2 = Math.log(2);
+ /** {@code 1/2}. */
+ private static final double HALF = 0.5;
+ /** Base 10 logarithm of 10 divided by 2 (log10(e)/2). */
+ private static final double LOG_10E_O_2 = Math.log10(Math.E) / 2;
+ /** Base 10 logarithm of 2 (log10(2)). */
+ private static final double LOG10_2 = Math.log10(2);
+ /** {@code sqrt(2)}. */
+ private static final double ROOT2 = 1.4142135623730951;
+
+ /** The multiplier used to split the double value into hi and low parts.
This must be odd
+ * and a value of 2^s + 1 in the range {@code p/2 <= s <= p-1} where p is
the number of
+ * bits of precision of the floating point number. Here {@code s = 27}.*/
+ private static final double MULTIPLIER = 1.34217729E8;
+
+ /** 54 shifted 20-bits to align with the exponent of the upper 32-bits of
a double. */
+ private static final int EXP_54 = 0x36_00000;
+ /** Represents an exponent of 500 in unbiased form shifted 20-bits to
align with the upper 32-bits of a double. */
+ private static final int EXP_500 = 0x5f3_00000;
+ /** Represents an exponent of 1024 in unbiased form (infinite or nan)
+ * shifted 20-bits to align with the upper 32-bits of a double. */
+ private static final int EXP_1024 = 0x7ff_00000;
+ /** Represents an exponent of -500 in unbiased form shifted 20-bits to
align with the upper 32-bits of a double. */
+ private static final int EXP_NEG_500 = 0x20b_00000;
+ /** 2^600. */
+ private static final double TWO_POW_600 = 0x1.0p+600;
+ /** 2^-600. */
+ private static final double TWO_POW_NEG_600 = 0x1.0p-600;
+
+ /**
+ * Private constructor for utility class.
+ */
+ private ComplexFunctions() {
+
Review Comment:
Remove extra line
##########
src/main/resources/checkstyle/checkstyle-suppressions.xml:
##########
@@ -21,6 +21,7 @@
<suppressions>
<suppress checks="Indentation"
files=".*[/\\]combinatorics[/\\]Factorial\.java" />
<suppress checks="ParameterNumber"
files=".*[/\\]core[/\\]LinearCombination[s]?\.java" />
+ <suppress checks="FileLengthCheck"
files=".*[/\\]ComplexFunctions(Test)?\.java" />
Review Comment:
Not required
##########
commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/ComplexConstructor.java:
##########
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.numbers.complex;
+
+/**
+ * Define a constructor for a Complex.
+ * @param <R> Generic
+ */
+@FunctionalInterface
+public interface ComplexConstructor<R> {
+
+ /**
+ * Represents a function that accepts real and imaginary part of complex
number and returns an object.
+ * @param real real part
+ * @param imaginary imaginary part
+ * @return R
+ */
+ R apply(double real, double imaginary);
+
Review Comment:
Remove empty line
##########
commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/ComplexFunctions.java:
##########
@@ -0,0 +1,813 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.numbers.complex;
+
+import java.util.function.DoubleUnaryOperator;
+
+/**
+ * <p>This class is all the Complex arithmetics. All the arithmetics that uses
1 or 2 Complex
Review Comment:
Please improve the documentation. Start with the documentation in Complex
and modify to change from an OO representation of a complex number to a
representation as two parts.
##########
commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/ComplexFunctions.java:
##########
@@ -0,0 +1,813 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.numbers.complex;
+
+import java.util.function.DoubleUnaryOperator;
+
+/**
+ * <p>This class is all the Complex arithmetics. All the arithmetics that uses
1 or 2 Complex
+ * number to produce a Complex result.
+ * All arithmetic will create a new instance for the result.</p>
+ */
+public final class ComplexFunctions {
+
+ /** The bit representation of {@code -0.0}. */
+ static final long NEGATIVE_ZERO_LONG_BITS = Double.doubleToLongBits(-0.0);
+ /** Exponent offset in IEEE754 representation. */
+ static final int EXPONENT_OFFSET = 1023;
+
+ /** Mask to remove the sign bit from a long. */
+ static final long UNSIGN_MASK = 0x7fff_ffff_ffff_ffffL;
+ /** Mask to extract the 52-bit mantissa from a long representation of a
double. */
+ static final long MANTISSA_MASK = 0x000f_ffff_ffff_ffffL;
+
+ /** Natural logarithm of 2 (ln(2)). */
+ private static final double LN_2 = Math.log(2);
+ /** {@code 1/2}. */
+ private static final double HALF = 0.5;
+ /** Base 10 logarithm of 10 divided by 2 (log10(e)/2). */
+ private static final double LOG_10E_O_2 = Math.log10(Math.E) / 2;
+ /** Base 10 logarithm of 2 (log10(2)). */
+ private static final double LOG10_2 = Math.log10(2);
+ /** {@code sqrt(2)}. */
+ private static final double ROOT2 = 1.4142135623730951;
+
+ /** The multiplier used to split the double value into hi and low parts.
This must be odd
+ * and a value of 2^s + 1 in the range {@code p/2 <= s <= p-1} where p is
the number of
+ * bits of precision of the floating point number. Here {@code s = 27}.*/
+ private static final double MULTIPLIER = 1.34217729E8;
+
+ /** 54 shifted 20-bits to align with the exponent of the upper 32-bits of
a double. */
+ private static final int EXP_54 = 0x36_00000;
+ /** Represents an exponent of 500 in unbiased form shifted 20-bits to
align with the upper 32-bits of a double. */
+ private static final int EXP_500 = 0x5f3_00000;
+ /** Represents an exponent of 1024 in unbiased form (infinite or nan)
+ * shifted 20-bits to align with the upper 32-bits of a double. */
+ private static final int EXP_1024 = 0x7ff_00000;
+ /** Represents an exponent of -500 in unbiased form shifted 20-bits to
align with the upper 32-bits of a double. */
+ private static final int EXP_NEG_500 = 0x20b_00000;
+ /** 2^600. */
+ private static final double TWO_POW_600 = 0x1.0p+600;
+ /** 2^-600. */
+ private static final double TWO_POW_NEG_600 = 0x1.0p-600;
+
+ /**
+ * Private constructor for utility class.
+ */
+ private ComplexFunctions() {
+
+ }
+
+ /**
+ * Returns the absolute value of the complex number.
+ * <pre>abs(x + i y) = sqrt(x^2 + y^2)</pre>
+ *
+ * <p>This should satisfy the special cases of the hypot function in ISO
C99 F.9.4.3:
+ * "The hypot functions compute the square root of the sum of the squares
of x and y,
+ * without undue overflow or underflow."
+ *
+ * <ul>
+ * <li>hypot(x, y), hypot(y, x), and hypot(x, −y) are equivalent.
+ * <li>hypot(x, ±0) is equivalent to |x|.
+ * <li>hypot(±∞, y) returns +∞, even if y is a NaN.
+ * </ul>
+ *
+ * <p>This method is called by all methods that require the absolute value
of the complex
+ * number, e.g. abs(), sqrt() and log().
+ *
+ * @param real real part of complex number
+ * @param imaginary imaginary part of complex number
+ * @return The absolute value.
+ */
+ public static double abs(double real, double imaginary) {
+ // Specialised implementation of hypot.
+ // See NUMBERS-143
+ return hypot(real, imaginary);
+ }
+
+ /**
+ * Returns the argument of this 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 value returned is between \( -\pi \) (not inclusive)
+ * and \( \pi \) (inclusive), with negative values returned for numbers
with
+ * negative imaginary parts.
+ *
+ * <p>If either real or imaginary part (or both) is NaN, then the result
is NaN.
+ * Infinite parts are handled as {@linkplain Math#atan2} handles them,
+ * essentially treating finite parts as zero in the presence of an
+ * infinite coordinate and returning a multiple of \( \frac{\pi}{4} \)
depending on
+ * the signs of the infinite parts.
+ *
+ * <p>This code follows the
+ * <a href="http://www.iso-9899.info/wiki/The_Standard">ISO C
Standard</a>, Annex G,
+ * in calculating the returned value using the {@code atan2(y, x)} method
for complex
+ * \( x + iy \).
+ *
+ * @param r real part of complex number
+ * @param i imaginary part of complex number
+ * @return The argument of this complex number.
+ * @see Math#atan2(double, double)
+ */
+ public static double arg(double r, double i) {
+ // Delegate
+ return Math.atan2(i, r);
+ }
+
+ /**
+ * Returns {@code true} if either real or imaginary component of the
complex number is infinite.
+ *
+ * <p>Note: A complex number with at least one infinite part is regarded
+ * as an infinity (even if its other part is a NaN).
+ * @param real real part of complex number
+ * @param imaginary imaginary part of complex number
+ * @return {@code true} if this instance contains an infinite value.
+ * @see Double#isInfinite(double)
+ */
+ private static boolean isInfinite(double real, double imaginary) {
+ return Double.isInfinite(real) || Double.isInfinite(imaginary);
+ }
+
+ /**
+ * Returns the
+ * <a href="http://mathworld.wolfram.com/NaturalLogarithm.html">
+ * natural logarithm</a> of this complex number using its real and
imaginary parts.
+ *
+ * <p>The natural logarithm of \( z \) is unbounded along the real axis and
+ * in the range \( [-\pi, \pi] \) along the imaginary axis. The imaginary
part of the
+ * natural logarithm has a branch cut along the negative real axis \(
(-infty,0] \).
+ * Special cases:
+ *
+ * <ul>
+ * <li>{@code z.conj().log() == z.log().conj()}.
+ * <li>If {@code z} is −0 + i0, returns −∞ + iπ ("divide-by-zero"
floating-point operation).
+ * <li>If {@code z} is +0 + i0, returns −∞ + i0 ("divide-by-zero"
floating-point operation).
+ * <li>If {@code z} is x + i∞ for finite x, returns +∞ + iπ/2.
+ * <li>If {@code z} is x + iNaN for finite x, returns NaN + iNaN
("invalid" floating-point operation).
+ * <li>If {@code z} is −∞ + iy for finite positive-signed y, returns +∞ +
iπ.
+ * <li>If {@code z} is +∞ + iy for finite positive-signed y, returns +∞ +
i0.
+ * <li>If {@code z} is −∞ + i∞, returns +∞ + i3π/4.
+ * <li>If {@code z} is +∞ + i∞, returns +∞ + iπ/4.
+ * <li>If {@code z} is ±∞ + iNaN, returns +∞ + iNaN.
+ * <li>If {@code z} is NaN + iy for finite y, returns NaN + iNaN
("invalid" floating-point operation).
+ * <li>If {@code z} is NaN + i∞, returns +∞ + iNaN.
+ * <li>If {@code z} is NaN + iNaN, returns NaN + iNaN.
+ * </ul>
+ *
+ * <p>Implements the formula:
+ *
+ * <p>\[ \ln(z) = \ln |z| + i \arg(z) \]
+ *
+ * <p>where \( |z| \) is the absolute and \( \arg(z) \) is the argument.
+ *
+ * <p>The implementation is based on the method described in:</p>
+ * <blockquote>
+ * T E Hull, Thomas F Fairgrieve and Ping Tak Peter Tang (1994)
+ * Implementing complex elementary functions using exception handling.
+ * ACM Transactions on Mathematical Software, Vol 20, No 2, pp 215-244.
+ * </blockquote>
+ *
+ * @param real real part of complex number
+ * @param imaginary imaginary part of complex number
+ * @param constructor Constructor
+ * @param <R> generic
+ * @return The natural logarithm of this complex number.
+ * @see Math#log(double)
+ * @see <a
href="http://functions.wolfram.com/ElementaryFunctions/Log/">Log</a>
+ */
+ public static <R> R log(double real, double imaginary,
ComplexConstructor<R> constructor) {
+ return log(Math::log, HALF, LN_2, real, imaginary, constructor);
+ }
+
+ /**
+ * Returns the base 10
+ * <a href="http://mathworld.wolfram.com/CommonLogarithm.html">
+ * common logarithm</a> of this complex number using its real and
imaginary parts.
+ *
+ * <p>The common logarithm of \( z \) is unbounded along the real axis and
+ * in the range \( [-\pi, \pi] \) along the imaginary axis. The imaginary
part of the
+ * common logarithm has a branch cut along the negative real axis \(
(-infty,0] \).
+ * Special cases are as defined in the log:
+ *
+ * <p>Implements the formula:
+ *
+ * <p>\[ \log_{10}(z) = \log_{10} |z| + i \arg(z) \]
+ *
+ * <p>where \( |z| \) is the absolute and \( \arg(z) \) is the argument.
+ *
+ * @param real real part of complex number
+ * @param imaginary imaginary part of complex number
+ * @param constructor Constructor
+ * @param <R> generic
+ * @return The base 10 logarithm of this complex number.
+ * @see Math#log10(double)
+ */
+ public static <R> R log10(double real, double imaginary,
ComplexConstructor<R> constructor) {
+ return log(Math::log10, LOG_10E_O_2, LOG10_2, real, imaginary,
constructor);
+ }
+
+ /**
+ * Returns the logarithm of complex number using its real and
+ * imaginary parts and the provided function.
+ * Implements the formula:
+ *
+ * <pre>
+ * log(x + i y) = log(|x + i y|) + i arg(x + i y)</pre>
+ *
+ * <p>Warning: The argument {@code logOf2} must be equal to {@code log(2)}
using the
+ * provided log function otherwise scaling using powers of 2 in the case
of overflow
+ * will be incorrect. This is provided as an internal optimisation.
+ *
+ * @param log Log function.
+ * @param logOfeOver2 The log function applied to e, then divided by 2.
+ * @param logOf2 The log function applied to 2.
+ * @param real real part of complex number
+ * @param imaginary imaginary part of complex number
+ * @param constructor Constructor for the returned complex.
+ * @param <R> generic
+ * @return The logarithm of this complex number.
+ */
+ private static <R> R log(DoubleUnaryOperator log, double logOfeOver2,
double logOf2,
+ double real, double imaginary,
ComplexConstructor<R> constructor) {
+ // Handle NaN
+ if (Double.isNaN(real) || Double.isNaN(imaginary)) {
+ // Return NaN unless infinite
+ if (isInfinite(real, imaginary)) {
+ return constructor.apply(Double.POSITIVE_INFINITY, Double.NaN);
+ }
+ return constructor.apply(Double.NaN, Double.NaN);
+ }
+
+ // Returns the real part:
+ // log(sqrt(x^2 + y^2))
+ // log(x^2 + y^2) / 2
+
+ // Compute with positive values
+ double x = Math.abs(real);
+ double y = Math.abs(imaginary);
+
+ // Find the larger magnitude.
+ if (x < y) {
+ final double tmp = x;
+ x = y;
+ y = tmp;
+ }
+
+ if (x == 0) {
+ // Handle zero: raises the ‘‘divide-by-zero’’ floating-point
exception.
+ return constructor.apply(Double.NEGATIVE_INFINITY,
+ negative(real) ? Math.copySign(Math.PI, imaginary) :
imaginary);
+ }
+
+ double re;
+
+ // This alters the implementation of Hull et al (1994) which used a
standard
+ // precision representation of |z|: sqrt(x*x + y*y).
+ // This formula should use the same definition of the magnitude
returned
+ // by Complex.abs() which is a high precision computation with scaling.
+ // The checks for overflow thus only require ensuring the output of |z|
+ // will not overflow or underflow.
+
+ if (x > HALF && x < ROOT2) {
+ // x^2+y^2 close to 1. Use log1p(x^2+y^2 - 1) / 2.
+ re = Math.log1p(x2y2m1(x, y)) * logOfeOver2;
+ } else {
+ // Check for over/underflow in |z|
+ // When scaling:
+ // log(a / b) = log(a) - log(b)
+ // So initialize the result with the log of the scale factor.
+ re = 0;
+ if (x > Double.MAX_VALUE / 2) {
+ // Potential overflow.
+ if (isPosInfinite(x)) {
+ // Handle infinity
+ return constructor.apply(x, arg(real, imaginary));
+ }
+ // Scale down.
+ x /= 2;
+ y /= 2;
+ // log(2)
+ re = logOf2;
+ } else if (y < Double.MIN_NORMAL) {
+ // Potential underflow.
+ if (y == 0) {
+ // Handle real only number
+ return constructor.apply(log.applyAsDouble(x), arg(real,
imaginary));
+ }
+ // Scale up sub-normal numbers to make them normal by scaling
by 2^54,
+ // i.e. more than the mantissa digits.
+ x *= 0x1.0p54;
+ y *= 0x1.0p54;
+ // log(2^-54) = -54 * log(2)
+ re = -54 * logOf2;
+ }
+ re += log.applyAsDouble(abs(x, y));
+ }
+
+ // All ISO C99 edge cases for the imaginary are satisfied by the Math
library.
+ return constructor.apply(re, arg(real, imaginary));
+ }
+
+ /**
+ * Compute {@code x^2 + y^2 - 1} in high precision.
+ * Assumes that the values x and y can be multiplied without overflow; that
+ * {@code x >= y}; and both values are positive.
+ *
+ * @param x the x value
+ * @param y the y value
+ * @return {@code x^2 + y^2 - 1}.
+ */
+ public static double x2y2m1(double x, double y) {
Review Comment:
Should not be public. This has been refactored too early. It is not needed
for this PR.
##########
commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/ComplexConstructor.java:
##########
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.numbers.complex;
+
+/**
+ * Define a constructor for a Complex.
Review Comment:
This functional interface requires more details. It should highlight which
is the function method. See my example for the ComplexUnaryOperator.
All new public files should have `@since 1.1` tags.
##########
commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/CStandardTest.java:
##########
@@ -508,6 +593,146 @@ private static void assertComplex(Complex z,
}
}
+ /**
+ * Assert the operation on the complex number is equal to the expected
value.
+ * If the imaginary part is not NaN the operation must also satisfy the
conjugate equality.
+ *
+ * <pre>
+ * op(conj(z)) = conj(op(z))
+ * </pre>
+ *
+ * <p>The results must be binary equal. This includes the sign of zero.
+ * @param z the complex
+ * @param operation the Complex operation
+ * @param expected the expected
+ * @param operation2 the ComplexFunctions operation
+ */
+ private static void assertComplex(Complex z,
+ UnaryOperator<Complex> operation,
Complex expected,
+ ComplexUnaryOperator<ComplexNumber>
operation2) {
+ assertComplex(z, operation, operation2, expected, FunctionType.NONE,
UnspecifiedSign.NONE);
+ }
+
+ /**
+ * Assert the operation on the complex number is equal to the expected
value. If the
+ * imaginary part is not NaN the operation must also satisfy the conjugate
equality.
+ *
+ * <pre>
+ * op(conj(z)) = conj(op(z))
+ * </pre>
+ *
+ * <p>The results must be binary equal. This includes the sign of zero.
+ * @param z the complex
+ * @param operation the Complex operation
+ * @param operation2 the ComplexFunctions operation
+ * @param expected the expected
+ * @param sign the sign specification
+ */
+ private static void assertComplex(Complex z,
+ UnaryOperator<Complex> operation,
+ ComplexUnaryOperator<ComplexNumber>
operation2,
+ Complex expected, UnspecifiedSign sign) {
+ assertComplex(z, operation, operation2, expected, FunctionType.NONE,
sign);
+ }
+
+ /**
+ * Assert the operation on the complex number is equal to the expected
value. If the
+ * imaginary part is not NaN the operation must also satisfy the conjugate
equality.
+ *
+ * <pre>
+ * op(conj(z)) = conj(op(z))
+ * </pre>
+ *
+ * <p>If the function type is ODD/EVEN the operation must satisfy the
function type equality.
+ *
+ * <pre>
+ * Odd : f(z) = -f(-z)
+ * Even: f(z) = f(-z)
+ * </pre>
+ *
+ * <p>The results must be binary equal. This includes the sign of zero.
+ * @param z the complex
+ * @param operation the Complex operation
+ * @param operation2 the ComplexFunctions operation
+ * @param expected the expected
+ * @param type the type
+ */
+ private static void assertComplex(Complex z,
+ UnaryOperator<Complex> operation,
+ ComplexUnaryOperator<ComplexNumber>
operation2,
+ Complex expected,
+ FunctionType type) {
+ assertComplex(z, operation, operation2, expected, type,
UnspecifiedSign.NONE);
+ }
+
+ /**
+ * Assert the operation on the complex number is equal to the expected
value. If the
+ * imaginary part is not NaN the operation must also satisfy the conjugate
equality.
+ *
+ * <pre>
+ * op(conj(z)) = conj(op(z))
+ * </pre>
+ *
+ * <p>If the function type is ODD/EVEN the operation must satisfy the
function type equality.
+ *
+ * <pre>
+ * Odd : f(z) = -f(-z)
+ * Even: f(z) = f(-z)
+ * </pre>
+ *
+ * <p>An ODD/EVEN function is also tested that the conjugate equalities
hold with {@code -z}.
+ * This effectively enumerates testing: (re, im); (re, -im); (-re, -im);
and (-re, im).
+ *
+ * <p>The results must be binary equal; the sign of the complex number is
first processed
+ * using the provided sign specification.
+ *
+ * @param z the complex
+ * @param operation the Complex operation
+ * @param operation2 the ComplexFunctions operation
+ * @param expected the expected
+ * @param type the type
+ * @param sign the sign specification
+ */
+ private static void assertComplex(Complex z,
+ UnaryOperator<Complex> operation,
ComplexUnaryOperator<ComplexNumber> operation2, Complex expected,
+ FunctionType type, UnspecifiedSign sign)
{
+ // Developer note: Set the sign specification to UnspecifiedSign.NONE
+ // to see which equalities fail. They should be for input defined
+ // in ISO C99 with an unspecified output sign, e.g.
+ // sign = UnspecifiedSign.NONE
+
+ // Test the operation
+ final Complex c = operation.apply(z);
+ final ComplexNumber c1 = operation2.apply(z.getReal(),
z.getImaginary(), ComplexNumber::new);
+ final Complex t1 = sign.removeSign(c);
+ final Complex t2 = sign.removeSign(expected);
+ if (!equals(t1.getReal(), t2.getReal()) ||
+ !equals(t1.getImaginary(), t2.getImaginary())) {
+ Assertions.fail(
+ String.format("Operation failed (z=%s). Expected: %s but was:
%s (Unspecified sign = %s)",
Review Comment:
This formatting message includes c1 as an argument but the string format
does not require it.
##########
commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/ComplexConstructor.java:
##########
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.numbers.complex;
+
+/**
+ * Define a constructor for a Complex.
+ * @param <R> Generic
+ */
+@FunctionalInterface
+public interface ComplexConstructor<R> {
+
+ /**
+ * Represents a function that accepts real and imaginary part of complex
number and returns an object.
+ * @param real real part
+ * @param imaginary imaginary part
+ * @return R
Review Comment:
the object encapsulating the complex result
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]