blob: 24cbd6756493c1dcdd39c0983ed3f552b48222a9 [file] [log] [blame]
/*
* 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.Assertions;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.function.BiFunction;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
/**
* Tests the standards defined by the C.99 standard for complex numbers
* defined in ISO/IEC 9899, Annex G.
*
* @see <a href="http://www.open-std.org/JTC1/SC22/WG14/www/standards">
* ISO/IEC 9899 - Programming languages - C</a>
*/
public class CStandardTest {
private static final double inf = Double.POSITIVE_INFINITY;
private static final double negInf = Double.NEGATIVE_INFINITY;
private static final double nan = Double.NaN;
private static final double max = Double.MAX_VALUE;
private static final double piOverFour = Math.PI / 4.0;
private static final double piOverTwo = Math.PI / 2.0;
private static final double threePiOverFour = 3.0 * Math.PI / 4.0;
private static final Complex oneZero = complex(1, 0);
private static final Complex zeroInf = complex(0, inf);
private static final Complex zeroNegInf = complex(0, negInf);
private static final Complex zeroNaN = complex(0, nan);
private static final Complex zeroPiTwo = complex(0.0, piOverTwo);
private static final Complex negZeroZero = complex(-0.0, 0);
private static final Complex negZeroNaN = complex(-0.0, nan);
private static final Complex infZero = complex(inf, 0);
private static final Complex infNaN = complex(inf, nan);
private static final Complex infInf = complex(inf, inf);
private static final Complex infPiTwo = complex(inf, piOverTwo);
private static final Complex infThreePiFour = complex(inf, threePiOverFour);
private static final Complex infPiFour = complex(inf, piOverFour);
private static final Complex infPi = complex(inf, Math.PI);
private static final Complex negInfInf = complex(negInf, inf);
private static final Complex negInfZero = complex(negInf, 0);
private static final Complex negInfNaN = complex(negInf, nan);
private static final Complex negInfPi = complex(negInf, Math.PI);
private static final Complex nanInf = complex(nan, inf);
private static final Complex nanNegInf = complex(nan, negInf);
private static final Complex nanZero = complex(nan, 0);
private static final Complex piTwoNaN = complex(piOverTwo, nan);
private static final Complex piNegInf = complex(Math.PI, negInf);
private static final Complex piTwoNegInf = complex(piOverTwo, negInf);
private static final Complex piTwoNegZero = complex(piOverTwo, -0.0);
private static final Complex threePiFourNegInf = complex(threePiOverFour, negInf);
private static final Complex piFourNegInf = complex(piOverFour, negInf);
private static final Complex NAN = complex(nan, nan);
private static final Complex maxMax = complex(max, max);
private static final Complex maxNan = complex(max, nan);
private static final Complex nanMax = complex(nan, max);
/** Finite numbers (positive and negative with zero). */
private static final double[] finite;
/** Positive finite numbers (with zero). */
private static final double[] positiveFinite;
/** Non-zero finite numbers (positive and negative). */
private static final double[] nonZeroFinite;
/** Non-zero positive finite numbers. */
private static final double[] nonZeroPositiveFinite;
/** Non-zero finite and infinite numbers (positive and negative). */
private static final double[] nonZero;
static {
// Choose a range that covers 2 * Math.PI.
// This is important for the functions that use cis(y).
final int size = 13;
finite = new double[size * 2];
positiveFinite = new double[size];
for (int i = 0; i < size; i++) {
final double v = i * 0.5;
finite[i * 2] = -v;
finite[i * 2 + 1] = v;
positiveFinite[i] = v;
}
// Copy without zero
nonZeroFinite = Arrays.copyOfRange(finite, 2, finite.length);
nonZeroPositiveFinite = Arrays.copyOfRange(positiveFinite, 1, positiveFinite.length);
nonZero = Arrays.copyOf(nonZeroFinite, nonZeroFinite.length + 2);
nonZero[nonZeroFinite.length] = Double.POSITIVE_INFINITY;
nonZero[nonZeroFinite.length + 1] = Double.NEGATIVE_INFINITY;
// Arrange numerically
Arrays.sort(finite);
Arrays.sort(nonZeroFinite);
Arrays.sort(nonZero);
}
/**
* The function type (e.g. odd or even).
*/
private enum FunctionType {
/** Odd: f(z) = -f(-z). */
ODD,
/** Even: f(z) = f(-z). */
EVEN,
/** Not Odd or Even. */
NONE;
}
/**
* An enum containing functionality to remove the sign from complex parts.
*/
private enum UnspecifiedSign {
/** Remove the sign from the real component. */
REAL {
@Override
Complex removeSign(Complex c) {
return negative(c.getReal()) ? complex(-c.getReal(), c.getImaginary()) : c;
}
},
/** Remove the sign from the imaginary component. */
IMAGINARY {
@Override
Complex removeSign(Complex c) {
return negative(c.getImaginary()) ? complex(c.getReal(), -c.getImaginary()) : c;
}
},
/** Remove the sign from the real and imaginary component. */
REAL_IMAGINARY {
@Override
Complex removeSign(Complex c) {
return IMAGINARY.removeSign(REAL.removeSign(c));
}
},
/** Do not remove the sign. */
NONE {
@Override
Complex removeSign(Complex c) {
return c;
}
};
/**
* Removes the sign from the complex real and or imaginary parts.
*
* @param c the complex
* @return the complex
*/
abstract Complex removeSign(Complex c);
/**
* Check that a value is negative. It must meet all the following conditions:
* <ul>
* <li>it is not {@code NaN},</li>
* <li>it is negative signed,</li>
* </ul>
*
* <p>Note: This is true for negative zero.</p>
*
* @param d Value.
* @return {@code true} if {@code d} is negative.
*/
private static boolean negative(double d) {
return d < 0 || Double.doubleToLongBits(d) == Double.doubleToLongBits(-0.0);
}
}
/**
* Assert the two complex numbers have equivalent real and imaginary components as
* defined by the {@code ==} operator.
*
* @param c1 the first complex (actual)
* @param c2 the second complex (expected)
*/
private static void assertComplex(Complex c1, Complex c2) {
// Use a delta of zero to allow comparison of -0.0 to 0.0
Assertions.assertEquals(c2.getReal(), c1.getReal(), 0.0, "real");
Assertions.assertEquals(c2.getImaginary(), c1.getImaginary(), 0.0, "imaginary");
}
/**
* Assert the operation on the two complex numbers.
*
* @param c1 the first complex
* @param c2 the second complex
* @param operation the operation
* @param operationName the operation name
* @param expected the expected
* @param expectedName the expected name
*/
private static void assertOperation(Complex c1, Complex c2,
BiFunction<Complex, Complex, Complex> operation, String operationName,
Predicate<Complex> expected, String expectedName) {
final Complex z = operation.apply(c1, c2);
Assertions.assertTrue(expected.test(z),
() -> String.format("%s expected: %s %s %s = %s", expectedName, c1, operationName, c2, 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) {
// Edge cases.
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, UnspecifiedSign.NONE);
}
}
}
// Random numbers
final UniformRandomProvider rng = RandomSource.create(RandomSource.SPLIT_MIX_64);
for (int i = 0; i < 100; i++) {
final double x = next(rng);
final double y = next(rng);
assertConjugateEquality(complex(x, y), operation, 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 operation
* @param sign the sign specification
*/
private static void assertConjugateEquality(Complex z,
UnaryOperator<Complex> operation, UnspecifiedSign sign) {
final Complex c1 = operation.apply(z.conj());
final Complex c2 = operation.apply(z).conj();
final Complex t1 = sign.removeSign(c1);
final Complex t2 = sign.removeSign(c2);
// Test for binary equality
if (!equals(t1.getReal(), t2.getReal()) ||
!equals(t1.getImaginary(), t2.getImaginary())) {
Assertions.fail(
String.format("Conjugate equality failed (z=%s). Expected: %s but was: %s (Unspecified sign = %s)",
z, c1, c2, sign));
}
}
/**
* Assert the operation on the complex number is odd or even.
*
* <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 operation the operation
* @param type the type
*/
private static void assertFunctionType(UnaryOperator<Complex> operation, FunctionType type) {
// Note: It may not be possible to satisfy the conjugate equality
// and be an odd/even function with regard to zero.
// The C99 standard allows for these cases to have unspecified sign.
// This test ignores parts that can result in unspecified signed results.
// The valid edge cases should be tested for each function separately.
if (type == FunctionType.NONE) {
return;
}
// Edge cases around zero.
final double[] parts = {-2, -1, -0.0, 0.0, 1, 2};
for (final double x : parts) {
for (final double y : parts) {
assertFunctionType(complex(x, y), operation, type, UnspecifiedSign.NONE);
}
}
// Random numbers
final UniformRandomProvider rng = RandomSource.create(RandomSource.SPLIT_MIX_64);
for (int i = 0; i < 100; i++) {
final double x = next(rng);
final double y = next(rng);
assertFunctionType(complex(x, y), operation, type, UnspecifiedSign.NONE);
}
}
/**
* Assert the operation on the complex number is odd or even.
*
* <pre>
* Odd : f(z) = -f(-z)
* Even: f(z) = f(-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 operation
* @param type the type (assumed to be ODD/EVEN)
* @param sign the sign specification
*/
private static void assertFunctionType(Complex z,
UnaryOperator<Complex> operation, FunctionType type, UnspecifiedSign sign) {
final Complex c1 = operation.apply(z);
Complex c2 = operation.apply(z.negate());
if (type == FunctionType.ODD) {
c2 = c2.negate();
}
final Complex t1 = sign.removeSign(c1);
final Complex t2 = sign.removeSign(c2);
// Test for binary equality
if (!equals(t1.getReal(), t2.getReal()) ||
!equals(t1.getImaginary(), t2.getImaginary())) {
Assertions.fail(
String.format("%s equality failed (z=%s, -z=%s). Expected: %s but was: %s (Unspecified sign = %s)",
type, z, z.negate(), c1, c2, sign));
new Exception().printStackTrace();
}
}
/**
* 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 operation
* @param expected the expected
*/
private static void assertComplex(Complex z,
UnaryOperator<Complex> operation, Complex expected) {
assertComplex(z, operation, 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 operation
* @param expected the expected
* @param sign the sign specification
*/
private static void assertComplex(Complex z,
UnaryOperator<Complex> operation, Complex expected, UnspecifiedSign sign) {
assertComplex(z, operation, 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 operation
* @param expected the expected
* @param type the type
*/
private static void assertComplex(Complex z,
UnaryOperator<Complex> operation, Complex expected,
FunctionType type) {
assertComplex(z, operation, 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 operation
* @param expected the expected
* @param type the type
* @param sign the sign specification
*/
private static void assertComplex(Complex z,
UnaryOperator<Complex> operation, 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 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)",
z, expected, c, sign));
}
if (!Double.isNaN(z.getImaginary())) {
assertConjugateEquality(z, operation, sign);
}
if (type != FunctionType.NONE) {
assertFunctionType(z, operation, type, sign);
// An odd/even function should satisfy the conjugate equality
// on the negated complex. This ensures testing the equalities
// hold for:
// (re, im) = (re, -im)
// (re, im) = (-re, -im) (even)
// = -(-re, -im) (odd)
// (-re, -im) = (-re, im)
if (!Double.isNaN(z.getImaginary())) {
assertConjugateEquality(z.negate(), operation, sign);
}
}
}
/**
* Returns {@code true} if the values are equal according to semantics of
* {@link Double#equals(Object)}.
*
* @param x Value
* @param y Value
* @return {@code Double.valueof(x).equals(Double.valueOf(y))}
*/
private static boolean equals(double x, double y) {
return Double.doubleToLongBits(x) == Double.doubleToLongBits(y);
}
/**
* Utility to create a Complex.
*
* @param real the real
* @param imaginary the imaginary
* @return the complex
*/
private static Complex complex(double real, double imaginary) {
return Complex.ofCartesian(real, imaginary);
}
/**
* Creates a list of Complex infinites.
*
* @return the list
*/
private static ArrayList<Complex> createInfinites() {
final double[] values = {0, 1, inf, negInf, nan};
return createCombinations(values, Complex::isInfinite);
}
/**
* Creates a list of Complex finites that are not zero.
*
* @return the list
*/
private static ArrayList<Complex> createNonZeroFinites() {
final double[] values = {-1, -0, 0, 1, Double.MAX_VALUE};
return createCombinations(values, c -> !CStandardTest.isZero(c));
}
/**
* Creates a list of Complex finites that are zero: [0,0], [-0,0], [0,-0], [-0,-0].
*
* @return the list
*/
private static ArrayList<Complex> createZeroFinites() {
final double[] values = {-0, 0};
return createCombinations(values, c -> true);
}
/**
* Creates a list of Complex NaNs.
*
* @return the list
*/
private static ArrayList<Complex> createNaNs() {
final double[] values = {0, 1, nan};
return createCombinations(values, Complex::isNaN);
}
/**
* Creates a list of Complex numbers as an all-vs-all combinations that pass the
* condition.
*
* @param values the values
* @param condition the condition
* @return the list
*/
private static ArrayList<Complex> createCombinations(final double[] values, Predicate<Complex> condition) {
final ArrayList<Complex> list = new ArrayList<>();
for (final double re : values) {
for (final double im : values) {
final Complex z = complex(re, im);
if (condition.test(z)) {
list.add(z);
}
}
}
return list;
}
/**
* Checks if the complex is zero.
*
* @param c the complex
* @return true if zero
*/
private static boolean isZero(Complex c) {
return Complex.equals(c, Complex.ZERO, 0);
}
/**
* ISO C Standard G.5 (4).
*/
@Test
public void testMultiply() {
final ArrayList<Complex> infinites = createInfinites();
final ArrayList<Complex> nonZeroFinites = createNonZeroFinites();
final ArrayList<Complex> zeroFinites = createZeroFinites();
// C.99 refers to non-zero finites.
// Standard multiplication of zero with infinites is not defined.
Assertions.assertEquals(nan, 0.0 * inf, "0 * inf");
Assertions.assertEquals(nan, 0.0 * negInf, "0 * -inf");
Assertions.assertEquals(nan, -0.0 * inf, "-0 * inf");
Assertions.assertEquals(nan, -0.0 * negInf, "-0 * -inf");
// "if one operand is an infinity and the other operand is a nonzero finite number or an
// infinity, then the result of the * operator is an infinity;"
for (final Complex z : infinites) {
for (final Complex w : infinites) {
assertOperation(z, w, Complex::multiply, "*", Complex::isInfinite, "Inf");
assertOperation(w, z, Complex::multiply, "*", Complex::isInfinite, "Inf");
}
for (final Complex w : nonZeroFinites) {
assertOperation(z, w, Complex::multiply, "*", Complex::isInfinite, "Inf");
assertOperation(w, z, Complex::multiply, "*", Complex::isInfinite, "Inf");
}
// C.99 refers to non-zero finites.
// Infer that Complex multiplication of zero with infinites is not defined.
for (final Complex w : zeroFinites) {
assertOperation(z, w, Complex::multiply, "*", Complex::isNaN, "NaN");
assertOperation(w, z, Complex::multiply, "*", Complex::isNaN, "NaN");
}
}
// ISO C Standard in Annex G is missing an explicit definition of how to handle NaNs.
// We will assume multiplication by (nan,nan) is not allowed.
// It is undefined how to multiply when a complex has only one NaN component.
// The reference implementation in Annex G allows it.
// The GNU g++ compiler computes:
// (1e300 + i 1e300) * (1e30 + i NAN) = inf + i inf
// Thus this is allowing some computations with NaN.
// Check multiply with (NaN,NaN) is not corrected
final double[] values = {0, 1, inf, negInf, nan};
for (final Complex z : createCombinations(values, c -> true)) {
assertOperation(z, NAN, Complex::multiply, "*", Complex::isNaN, "NaN");
assertOperation(NAN, z, Complex::multiply, "*", Complex::isNaN, "NaN");
}
// Test multiply cases which result in overflow are corrected to infinity
assertOperation(maxMax, maxMax, Complex::multiply, "*", Complex::isInfinite, "Inf");
assertOperation(maxNan, maxNan, Complex::multiply, "*", Complex::isInfinite, "Inf");
assertOperation(nanMax, maxNan, Complex::multiply, "*", Complex::isInfinite, "Inf");
assertOperation(maxNan, nanMax, Complex::multiply, "*", Complex::isInfinite, "Inf");
assertOperation(nanMax, nanMax, Complex::multiply, "*", Complex::isInfinite, "Inf");
}
/**
* ISO C Standard G.5 (4).
*/
@Test
public void testDivide() {
final ArrayList<Complex> infinites = createInfinites();
final ArrayList<Complex> nonZeroFinites = createNonZeroFinites();
final ArrayList<Complex> zeroFinites = createZeroFinites();
final ArrayList<Complex> nans = createNaNs();
final ArrayList<Complex> finites = new ArrayList<>(nonZeroFinites);
finites.addAll(zeroFinites);
// "if the first operand is an infinity and the second operand is a finite number, then the
// result of the / operator is an infinity;"
for (final Complex z : infinites) {
for (final Complex w : nonZeroFinites) {
assertOperation(z, w, Complex::divide, "/", Complex::isInfinite, "Inf");
}
for (final Complex w : zeroFinites) {
assertOperation(z, w, Complex::divide, "/", Complex::isInfinite, "Inf");
}
// Check inf/inf cannot be done
for (final Complex w : infinites) {
assertOperation(z, w, Complex::divide, "/", Complex::isNaN, "NaN");
}
}
// "if the first operand is a finite number and the second operand is an infinity, then the
// result of the / operator is a zero;"
for (final Complex z : finites) {
for (final Complex w : infinites) {
assertOperation(z, w, Complex::divide, "/", CStandardTest::isZero, "Zero");
}
}
// "if the first operand is a nonzero finite number or an infinity and the second operand is
// a zero, then the result of the / operator is an infinity."
for (final Complex w : zeroFinites) {
for (final Complex z : nonZeroFinites) {
assertOperation(z, w, Complex::divide, "/", Complex::isInfinite, "Inf");
}
for (final Complex z : infinites) {
assertOperation(z, w, Complex::divide, "/", Complex::isInfinite, "Inf");
}
}
// ISO C Standard in Annex G is missing an explicit definition of how to handle NaNs.
// The reference implementation does not correct for divide by NaN components unless
// infinite.
for (final Complex w : nans) {
for (final Complex z : finites) {
assertOperation(z, w, Complex::divide, "/", c -> NAN.equals(c), "(NaN,NaN)");
}
for (final Complex z : infinites) {
assertOperation(z, w, Complex::divide, "/", c -> NAN.equals(c), "(NaN,NaN)");
}
}
// Check (NaN,NaN) divide is not corrected for the edge case of divide by zero or infinite
for (final Complex w : zeroFinites) {
assertOperation(NAN, w, Complex::divide, "/", Complex::isNaN, "NaN");
}
for (final Complex w : infinites) {
assertOperation(NAN, w, Complex::divide, "/", Complex::isNaN, "NaN");
}
}
/**
* ISO C Standard G.6 (3).
*/
@Test
public void testSqrt1() {
assertComplex(complex(-2.0, 0.0).sqrt(), complex(0.0, Math.sqrt(2)));
assertComplex(complex(-2.0, -0.0).sqrt(), complex(0.0, -Math.sqrt(2)));
}
/**
* ISO C Standard G.6 (7).
*/
@Test
public void testImplicitTrig() {
final UniformRandomProvider rng = RandomSource.create(RandomSource.SPLIT_MIX_64);
for (int i = 0; i < 100; i++) {
final double re = next(rng);
final double im = next(rng);
final Complex z = complex(re, im);
final Complex iz = z.multiplyImaginary(1);
assertComplex(z.asin(), iz.asinh().multiplyImaginary(-1));
assertComplex(z.atan(), iz.atanh().multiplyImaginary(-1));
assertComplex(z.cos(), iz.cosh());
assertComplex(z.sin(), iz.sinh().multiplyImaginary(-1));
assertComplex(z.tan(), iz.tanh().multiplyImaginary(-1));
}
}
/**
* Create a number in the range {@code (-5,5)}.
*
* @param rng the random generator
* @return the number
*/
private static double next(UniformRandomProvider rng) {
return rng.nextDouble() * (rng.nextBoolean() ? -5 : 5);
}
/**
* ISO C Standard G.6 (6) for abs().
* Defined by ISO C Standard F.9.4.3 hypot function.
*/
@Test
public void testAbs() {
Assertions.assertEquals(inf, complex(inf, nan).abs());
Assertions.assertEquals(inf, complex(negInf, nan).abs());
final UniformRandomProvider rng = RandomSource.create(RandomSource.SPLIT_MIX_64);
for (int i = 0; i < 100; i++) {
final double x = next(rng);
final double y = next(rng);
Assertions.assertEquals(complex(x, y).abs(), complex(y, x).abs());
Assertions.assertEquals(complex(x, y).abs(), complex(x, -y).abs());
Assertions.assertEquals(Math.abs(x), complex(x, 0.0).abs());
Assertions.assertEquals(Math.abs(x), complex(x, -0.0).abs());
Assertions.assertEquals(inf, complex(inf, y).abs());
Assertions.assertEquals(inf, complex(negInf, y).abs());
}
}
/**
* ISO C Standard G.6.1.1.
*/
@Test
public void testAcos() {
final UnaryOperator<Complex> operation = Complex::acos;
assertConjugateEquality(operation);
assertComplex(Complex.ZERO, operation, piTwoNegZero);
assertComplex(negZeroZero, operation, piTwoNegZero);
assertComplex(zeroNaN, operation, piTwoNaN);
assertComplex(negZeroNaN, operation, piTwoNaN);
for (double x : finite) {
assertComplex(complex(x, inf), operation, piTwoNegInf);
}
for (double x : nonZeroFinite) {
assertComplex(complex(x, nan), operation, NAN);
}
for (double y : positiveFinite) {
assertComplex(complex(-inf, y), operation, piNegInf);
}
for (double y : positiveFinite) {
assertComplex(complex(inf, y), operation, zeroNegInf);
}
assertComplex(negInfInf, operation, threePiFourNegInf);
assertComplex(infInf, operation, piFourNegInf);
assertComplex(infNaN, operation, nanInf, UnspecifiedSign.IMAGINARY);
assertComplex(negInfNaN, operation, nanNegInf, UnspecifiedSign.IMAGINARY);
for (double y : finite) {
assertComplex(complex(nan, y), operation, NAN);
}
assertComplex(nanInf, operation, nanNegInf);
assertComplex(NAN, operation, NAN);
}
/**
* ISO C Standard G.6.2.1.
*/
@Test
public void testAcosh() {
final UnaryOperator<Complex> operation = Complex::acosh;
assertConjugateEquality(operation);
assertComplex(Complex.ZERO, operation, zeroPiTwo);
assertComplex(negZeroZero, operation, zeroPiTwo);
for (double x : finite) {
assertComplex(complex(x, inf), operation, infPiTwo);
}
for (double x : finite) {
assertComplex(complex(x, nan), operation, NAN);
}
for (double y : positiveFinite) {
assertComplex(complex(-inf, y), operation, infPi);
}
for (double y : positiveFinite) {
assertComplex(complex(inf, y), operation, infZero);
}
assertComplex(negInfInf, operation, infThreePiFour);
assertComplex(infInf, operation, infPiFour);
assertComplex(infNaN, operation, infNaN);
assertComplex(negInfNaN, operation, infNaN);
for (double y : finite) {
assertComplex(complex(nan, y), operation, NAN);
}
assertComplex(nanInf, operation, infNaN);
assertComplex(NAN, operation, NAN);
}
/**
* ISO C Standard G.6.2.2.
*/
@Test
public void testAsinh() {
final UnaryOperator<Complex> operation = Complex::asinh;
final FunctionType type = FunctionType.ODD;
assertConjugateEquality(operation);
assertFunctionType(operation, type);
assertComplex(Complex.ZERO, operation, Complex.ZERO, type);
for (double x : positiveFinite) {
assertComplex(complex(x, inf), operation, infPiTwo, type);
}
for (double x : finite) {
assertComplex(complex(x, nan), operation, NAN, type);
}
for (double y : positiveFinite) {
assertComplex(complex(inf, y), operation, infZero, type);
}
assertComplex(infInf, operation, infPiFour, type);
assertComplex(infNaN, operation, infNaN, type);
assertComplex(nanZero, operation, nanZero, type);
for (double y : nonZeroFinite) {
assertComplex(complex(nan, y), operation, NAN, type);
}
assertComplex(nanInf, operation, infNaN, type, UnspecifiedSign.REAL);
assertComplex(NAN, operation, NAN, type);
}
/**
* ISO C Standard G.6.2.3.
*/
@Test
public void testAtanh() {
final UnaryOperator<Complex> operation = Complex::atanh;
final FunctionType type = FunctionType.ODD;
assertConjugateEquality(operation);
assertFunctionType(operation, type);
assertComplex(Complex.ZERO, operation, Complex.ZERO, type);
assertComplex(zeroNaN, operation, zeroNaN, type);
assertComplex(oneZero, operation, infZero, type);
for (double x : positiveFinite) {
assertComplex(complex(x, inf), operation, zeroPiTwo, type);
}
for (double x : nonZeroFinite) {
assertComplex(complex(x, nan), operation, NAN, type);
}
for (double y : positiveFinite) {
assertComplex(complex(inf, y), operation, zeroPiTwo, type);
}
assertComplex(infInf, operation, zeroPiTwo, type);
assertComplex(infNaN, operation, zeroNaN, type);
assertComplex(nanZero, operation, NAN, type);
for (double y : finite) {
assertComplex(complex(nan, y), operation, NAN, type);
}
assertComplex(nanInf, operation, zeroPiTwo, type, UnspecifiedSign.REAL);
assertComplex(NAN, operation, NAN, type);
}
/**
* ISO C Standard G.6.2.4.
*/
@Test
public void testCosh() {
final UnaryOperator<Complex> operation = Complex::cosh;
final FunctionType type = FunctionType.EVEN;
assertConjugateEquality(operation);
assertFunctionType(operation, type);
assertComplex(Complex.ZERO, operation, Complex.ONE, type);
assertComplex(zeroInf, operation, nanZero, type, UnspecifiedSign.IMAGINARY);
assertComplex(zeroNaN, operation, nanZero, type, UnspecifiedSign.IMAGINARY);
for (double x : nonZeroFinite) {
assertComplex(complex(x, inf), operation, NAN, type);
}
for (double x : nonZeroFinite) {
assertComplex(complex(x, nan), operation, NAN, type);
}
assertComplex(infZero, operation, infZero, type);
for (double y : nonZeroFinite) {
assertComplex(complex(inf, y), operation, Complex.ofCis(y).multiply(inf), type);
}
assertComplex(infInf, operation, infNaN, type, UnspecifiedSign.REAL);
assertComplex(infNaN, operation, infNaN, type);
assertComplex(nanZero, operation, nanZero, type, UnspecifiedSign.IMAGINARY);
for (double y : nonZero) {
assertComplex(complex(nan, y), operation, NAN, type);
}
assertComplex(NAN, operation, NAN, type);
}
/**
* ISO C Standard G.6.2.5.
*/
@Test
public void testSinh() {
final UnaryOperator<Complex> operation = Complex::sinh;
final FunctionType type = FunctionType.ODD;
assertConjugateEquality(operation);
assertFunctionType(operation, type);
assertComplex(Complex.ZERO, operation, Complex.ZERO, type);
assertComplex(zeroInf, operation, zeroNaN, type, UnspecifiedSign.REAL);
assertComplex(zeroNaN, operation, zeroNaN, type, UnspecifiedSign.REAL);
for (double x : nonZeroPositiveFinite) {
assertComplex(complex(x, inf), operation, NAN, type);
}
for (double x : nonZeroFinite) {
assertComplex(complex(x, nan), operation, NAN, type);
}
assertComplex(infZero, operation, infZero, type);
// Note: Error in the ISO C99 reference to use positive finite y but the zero case is different
for (double y : nonZeroFinite) {
assertComplex(complex(inf, y), operation, Complex.ofCis(y).multiply(inf), type);
}
assertComplex(infInf, operation, infNaN, type, UnspecifiedSign.REAL);
assertComplex(infNaN, operation, infNaN, type, UnspecifiedSign.REAL);
assertComplex(nanZero, operation, nanZero, type);
for (double y : nonZero) {
assertComplex(complex(nan, y), operation, NAN, type);
}
assertComplex(NAN, operation, NAN, type);
}
/**
* ISO C Standard G.6.2.6.
*/
@Test
public void testTanh() {
final UnaryOperator<Complex> operation = Complex::tanh;
final FunctionType type = FunctionType.ODD;
assertConjugateEquality(operation);
assertFunctionType(operation, type);
assertComplex(Complex.ZERO, operation, Complex.ZERO, type);
for (double x : finite) {
assertComplex(complex(x, inf), operation, NAN, type);
}
for (double x : finite) {
assertComplex(complex(x, nan), operation, NAN, type);
}
for (double y : positiveFinite) {
assertComplex(complex(inf, y), operation, complex(1.0, Math.copySign(0, Math.sin(2 * y))), type);
}
assertComplex(infInf, operation, oneZero, type, UnspecifiedSign.IMAGINARY);
assertComplex(infNaN, operation, oneZero, type, UnspecifiedSign.IMAGINARY);
assertComplex(nanZero, operation, nanZero, type);
for (double y : nonZero) {
assertComplex(complex(nan, y), operation, NAN, type);
}
assertComplex(NAN, operation, NAN, type);
}
/**
* ISO C Standard G.6.3.1.
*/
@Test
public void testExp() {
final UnaryOperator<Complex> operation = Complex::exp;
assertConjugateEquality(operation);
assertComplex(Complex.ZERO, operation, oneZero);
assertComplex(negZeroZero, operation, oneZero);
for (double x : finite) {
assertComplex(complex(x, inf), operation, NAN);
}
for (double x : finite) {
assertComplex(complex(x, nan), operation, NAN);
}
assertComplex(infZero, operation, infZero);
for (double y : finite) {
assertComplex(complex(-inf, y), operation, Complex.ofCis(y).multiply(0.0));
}
for (double y : nonZeroFinite) {
assertComplex(complex(inf, y), operation, Complex.ofCis(y).multiply(inf));
}
assertComplex(negInfInf, operation, Complex.ZERO, UnspecifiedSign.REAL_IMAGINARY);
assertComplex(infInf, operation, infNaN, UnspecifiedSign.REAL);
assertComplex(negInfNaN, operation, Complex.ZERO, UnspecifiedSign.REAL_IMAGINARY);
assertComplex(infNaN, operation, infNaN, UnspecifiedSign.REAL);
assertComplex(nanZero, operation, nanZero);
for (double y : nonZero) {
assertComplex(complex(nan, y), operation, NAN);
}
assertComplex(NAN, operation, NAN);
}
/**
* ISO C Standard G.6.3.2.
*/
@Test
public void testLog() {
final UnaryOperator<Complex> operation = Complex::log;
assertConjugateEquality(operation);
assertComplex(negZeroZero, operation, negInfPi);
assertComplex(Complex.ZERO, operation, negInfZero);
for (double x : finite) {
assertComplex(complex(x, inf), operation, infPiTwo);
}
for (double x : finite) {
assertComplex(complex(x, nan), operation, NAN);
}
for (double y : positiveFinite) {
assertComplex(complex(-inf, y), operation, infPi);
}
for (double y : positiveFinite) {
assertComplex(complex(inf, y), operation, infZero);
}
assertComplex(negInfInf, operation, infThreePiFour);
assertComplex(infInf, operation, infPiFour);
assertComplex(negInfNaN, operation, infNaN);
assertComplex(infNaN, operation, infNaN);
for (double y : finite) {
assertComplex(complex(nan, y), operation, NAN);
}
assertComplex(nanInf, operation, infNaN);
assertComplex(NAN, operation, NAN);
}
/**
* Same edge cases as log() since the real component is divided by Math.log(10) which
* has no effect on infinite or nan.
*/
@Test
public void testLog10() {
final UnaryOperator<Complex> operation = Complex::log10;
assertConjugateEquality(operation);
assertComplex(negZeroZero, operation, negInfPi);
assertComplex(Complex.ZERO, operation, negInfZero);
for (double x : finite) {
assertComplex(complex(x, inf), operation, infPiTwo);
}
for (double x : finite) {
assertComplex(complex(x, nan), operation, NAN);
}
for (double y : positiveFinite) {
assertComplex(complex(-inf, y), operation, infPi);
}
for (double y : positiveFinite) {
assertComplex(complex(inf, y), operation, infZero);
}
assertComplex(negInfInf, operation, infThreePiFour);
assertComplex(infInf, operation, infPiFour);
assertComplex(negInfNaN, operation, infNaN);
assertComplex(infNaN, operation, infNaN);
for (double y : finite) {
assertComplex(complex(nan, y), operation, NAN);
}
assertComplex(nanInf, operation, infNaN);
assertComplex(NAN, operation, NAN);
}
/**
* ISO C Standard G.6.4.2.
*/
@Test
public void testSqrt() {
final UnaryOperator<Complex> operation = Complex::sqrt;
assertConjugateEquality(operation);
assertComplex(negZeroZero, operation, Complex.ZERO);
assertComplex(Complex.ZERO, operation, Complex.ZERO);
for (double x : finite) {
assertComplex(complex(x, inf), operation, infInf);
}
// Include infinity and nan for (x, inf).
assertComplex(infInf, operation, infInf);
assertComplex(negInfInf, operation, infInf);
assertComplex(nanInf, operation, infInf);
for (double x : finite) {
assertComplex(complex(x, nan), operation, NAN);
}
for (double y : positiveFinite) {
assertComplex(complex(-inf, y), operation, zeroInf);
}
for (double y : positiveFinite) {
assertComplex(complex(inf, y), operation, infZero);
}
assertComplex(negInfNaN, operation, nanInf, UnspecifiedSign.IMAGINARY);
assertComplex(infNaN, operation, infNaN);
for (double y : finite) {
assertComplex(complex(nan, y), operation, NAN);
}
assertComplex(NAN, operation, NAN);
}
}