This is an automated email from the ASF dual-hosted git repository. aherbert pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-numbers.git
commit 7775ba7b3188906c077be853bebaa01274565bd0 Author: aherbert <[email protected]> AuthorDate: Thu Nov 7 11:04:57 2019 +0000 [NUMBERS-78] Increase test coverage. Added more tests using the C.99 standard. Fixed checkstyle. Use final. --- .../apache/commons/numbers/complex/Complex.java | 391 ++++++++++----- .../commons/numbers/complex/CStandardTest.java | 250 +++++++--- .../commons/numbers/complex/ComplexTest.java | 547 +++++++++++++-------- .../apache/commons/numbers/complex/TestUtils.java | 53 +- 4 files changed, 822 insertions(+), 419 deletions(-) diff --git a/commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/Complex.java b/commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/Complex.java index c47b510..083f774 100644 --- a/commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/Complex.java +++ b/commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/Complex.java @@ -25,18 +25,19 @@ import org.apache.commons.numbers.core.Precision; /** * Representation of a Complex number, i.e. a number which has both a * real and imaginary part. - * <p> - * Implementations of arithmetic operations handle {@code NaN} and + * + * <p>Implementations of arithmetic operations handle {@code NaN} and * infinite values according to the rules for {@link java.lang.Double}, i.e. * {@link #equals} is an equivalence relation for all instances that have * a {@code NaN} in either real or imaginary part, e.g. the following are - * considered equal: + * considered equal:</p> * <ul> * <li>{@code 1 + NaNi}</li> * <li>{@code NaN + i}</li> * <li>{@code NaN + NaNi}</li> - * </ul><p> - * Note that this contradicts the IEEE-754 standard for floating + * </ul> + * + * <p>Note that this contradicts the IEEE-754 standard for floating * point numbers (according to which the test {@code x == x} must fail if * {@code x} is {@code NaN}). The method * {@link org.apache.commons.numbers.core.Precision#equals(double,double,int) @@ -44,6 +45,11 @@ import org.apache.commons.numbers.core.Precision; * IEEE-754 while this class conforms with the standard behavior for Java * object types.</p> * + * <p>Arithmetic in this class conforms to the C.99 standard for complex numbers + * defined in ISO/IEC 9899, Annex G.<p> + * + * @see <a href="http://www.open-std.org/JTC1/SC22/WG14/www/standards"> + * ISO/IEC 9899 - Programming languages - C</a> */ public final class Complex implements Serializable { /** The square root of -1, a.k.a. "i". */ @@ -52,8 +58,10 @@ public final class Complex implements Serializable { public static final Complex ONE = new Complex(1, 0); /** A complex number representing zero. */ public static final Complex ZERO = new Complex(0, 0); - /** A complex number representing "NaN + NaN i" */ + /** A complex number representing "NaN + NaN i". */ private static final Complex NAN = new Complex(Double.NaN, Double.NaN); + /** 3*π/4. */ + private static final double PI_3_OVER_4 = 0.75 * Math.PI; /** π/2. */ private static final double PI_OVER_2 = 0.5 * Math.PI; /** π/4. */ @@ -111,9 +119,9 @@ public final class Complex implements Serializable { /** * Creates a Complex from its polar representation. * - * If {@code r} is infinite and {@code theta} is finite, infinite or NaN + * <p>If {@code r} is infinite and {@code theta} is finite, infinite or NaN * values may be returned in parts of the result, following the rules for - * double arithmetic. + * double arithmetic.</p> * * <pre> * Examples: @@ -127,9 +135,12 @@ public final class Complex implements Serializable { * @param r the modulus of the complex number to create * @param theta the argument of the complex number to create * @return {@code Complex} + * @throws IllegalArgumentException if {@code r} is non-positive */ public static Complex ofPolar(double r, double theta) { - checkNotNegative(r); + if (r <= 0) { + throw new IllegalArgumentException("Non-positive polar modulus argument: " + r); + } return new Complex(r * Math.cos(theta), r * Math.sin(theta)); } @@ -173,13 +184,13 @@ public final class Complex implements Serializable { final double re; try { re = Double.parseDouble(elements[0]); - } catch (NumberFormatException ex) { + } catch (final NumberFormatException ex) { throw new ComplexParsingException("Could not parse real part" + elements[0]); } final double im; try { im = Double.parseDouble(elements[1]); - } catch (NumberFormatException ex) { + } catch (final NumberFormatException ex) { throw new ComplexParsingException("Could not parse imaginary part" + elements[1]); } @@ -187,25 +198,41 @@ public final class Complex implements Serializable { } /** - * Returns true if either real or imaginary component of the Complex - * is NaN. + * Returns true if either real or imaginary component of the Complex is NaN and the + * Complex is not infinite. * - * @return {@code true} is this instance contains NaN. + * @return {@code true} if this instance contains NaN and no infinite parts. + * @see Double#isNaN(double) + * @see #isInfinite() */ public boolean isNaN() { - return Double.isNaN(real) || - Double.isNaN(imaginary); + if (Double.isNaN(real) || Double.isNaN(imaginary)) { + return !isInfinite(); + } + return false; } /** - * Returns true if either real or imaginary component of the Complex - * is infinite. + * Returns true if either real or imaginary component of the Complex is infinite. + * + * <p>Note: A complex or imaginary value with at least one infinite part is regarded + * as an infinity (even if its other part is a NaN).</p> * * @return {@code true} if this instance contains an infinite value. + * @see Double#isInfinite(double) */ public boolean isInfinite() { - return Double.isInfinite(real) || - Double.isInfinite(imaginary); + return Double.isInfinite(real) || Double.isInfinite(imaginary); + } + + /** + * Returns true if both real and imaginary component of the Complex are finite. + * + * @return {@code true} if this instance contains finite values. + * @see Double#isFinite(double) + */ + public boolean isFinite() { + return Double.isFinite(real) && Double.isFinite(imaginary); } /** @@ -223,9 +250,8 @@ public final class Complex implements Serializable { if (Double.isInfinite(real) || Double.isInfinite(imaginary)) { return new Complex(Double.POSITIVE_INFINITY, 0); - } else { - return this; } + return this; } /** @@ -237,16 +263,18 @@ public final class Complex implements Serializable { * @return the absolute value. */ public double abs() { - if (Math.abs(real) < Math.abs(imaginary)) { - final double q = real / imaginary; - return Math.abs(imaginary) * Math.sqrt(1 + q * q); - } else { - if (real == 0) { - return Math.abs(imaginary); - } - final double q = imaginary / real; - return Math.abs(real) * Math.sqrt(1 + q * q); - } + // Delegate + return Math.hypot(real, imaginary); + + //if (Math.abs(real) < Math.abs(imaginary)) { + // final double q = real / imaginary; + // return Math.abs(imaginary) * Math.sqrt(1 + q * q); + //} + //if (real == 0) { + // return Math.abs(imaginary); + //} + //final double q = imaginary / real; + //return Math.abs(real) * Math.sqrt(1 + q * q); } /** @@ -290,6 +318,7 @@ public final class Complex implements Serializable { /** * Returns the conjugate of this complex number * (C++11 grammar). + * * @return the conjugate of this complex object. * @see #conjugate() */ @@ -319,21 +348,20 @@ public final class Complex implements Serializable { * @return {@code this / divisor}. */ public Complex divide(Complex divisor) { - double a = real; double b = imaginary; double c = divisor.getReal(); double d = divisor.getImaginary(); int ilogbw = 0; - double logbw = Math.log(Math.max(Math.abs(c), Math.abs(d))) / Math.log(2); + final double logbw = Math.log(Math.max(Math.abs(c), Math.abs(d))) / Math.log(2); if (!Double.isInfinite(logbw)) { ilogbw = (int)logbw; c = Math.scalb(c, -ilogbw); d = Math.scalb(d, -ilogbw); } - double denom = c*c + d*d; - double x = Math.scalb( (a*c + b*d) / denom, -ilogbw); - double y = Math.scalb( (b*c - a*d) / denom, -ilogbw); + final double denom = c * c + d * d; + double x = Math.scalb((a * c + b * d) / denom, -ilogbw); + double y = Math.scalb((b * c - a * d) / denom, -ilogbw); if (Double.isNaN(x) && Double.isNaN(y)) { if ((denom == 0.0) && (!Double.isNaN(a) || !Double.isNaN(b))) { @@ -343,19 +371,17 @@ public final class Complex implements Serializable { !Double.isInfinite(c) && !Double.isInfinite(d)) { a = Math.copySign(Double.isInfinite(a) ? 1.0 : 0.0, a); b = Math.copySign(Double.isInfinite(b) ? 1.0 : 0.0, b); - x = Double.POSITIVE_INFINITY * (a*c + b*d); - y = Double.POSITIVE_INFINITY * (b*c - a*d); + x = Double.POSITIVE_INFINITY * (a * c + b * d); + y = Double.POSITIVE_INFINITY * (b * c - a * d); } else if (Double.isInfinite(logbw) && !Double.isInfinite(a) && !Double.isInfinite(b)) { c = Math.copySign(Double.isInfinite(c) ? 1.0 : 0.0, c); d = Math.copySign(Double.isInfinite(d) ? 1.0 : 0.0, d); - x = 0.0 * (a*c + b*d); - y = 0.0 * (b*c - a*d); + x = 0.0 * (a * c + b * d); + y = 0.0 * (b * c - a * d); } } return new Complex(x, y); - - } /** @@ -386,16 +412,15 @@ public final class Complex implements Serializable { scaleQ = scale * q; } return new Complex(scaleQ, -scale); - } else { - final double q = imaginary / real; - final double scale = 1. / (imaginary * q + real); - double scaleQ = 0; - if (q != 0 && - scale != 0) { - scaleQ = scale * q; - } - return new Complex(scale, -scaleQ); } + final double q = imaginary / real; + final double scale = 1. / (imaginary * q + real); + double scaleQ = 0; + if (q != 0 && + scale != 0) { + scaleQ = scale * q; + } + return new Complex(scale, -scaleQ); } /** @@ -427,8 +452,8 @@ public final class Complex implements Serializable { if (this == other) { return true; } - if (other instanceof Complex){ - Complex c = (Complex) other; + if (other instanceof Complex) { + final Complex c = (Complex) other; return equals(real, c.real) && equals(imaginary, c.imaginary); } @@ -581,10 +606,10 @@ public final class Complex implements Serializable { double b = imaginary; double c = factor.getReal(); double d = factor.getImaginary(); - final double ac = a*c; - final double bd = b*d; - final double ad = a*d; - final double bc = b*c; + final double ac = a * c; + final double bd = b * d; + final double ad = a * d; + final double bc = b * c; double x = ac - bd; double y = ad + bc; if (Double.isNaN(a) && Double.isNaN(b)) { @@ -628,8 +653,8 @@ public final class Complex implements Serializable { recalc = true; } if (recalc) { - x = Double.POSITIVE_INFINITY * (a*c - b*d); - y = Double.POSITIVE_INFINITY * (a*d + b*c); + x = Double.POSITIVE_INFINITY * (a * c - b * d); + y = Double.POSITIVE_INFINITY * (a * d + b * c); } } return new Complex(x, y); @@ -722,16 +747,14 @@ public final class Complex implements Serializable { return new Complex(0, Double.NEGATIVE_INFINITY); } else if (real == Double.NEGATIVE_INFINITY && imaginary == Double.POSITIVE_INFINITY) { - return new Complex(Math.PI * 0.75, Double.NEGATIVE_INFINITY); + return new Complex(PI_3_OVER_4, Double.NEGATIVE_INFINITY); } else if (real == Double.POSITIVE_INFINITY && imaginary == Double.POSITIVE_INFINITY) { return new Complex(PI_OVER_4, Double.NEGATIVE_INFINITY); - } else if (real == Double.POSITIVE_INFINITY && - Double.isNaN(imaginary)) { - return new Complex(Double.NaN , Double.POSITIVE_INFINITY); - } else if (real == Double.NEGATIVE_INFINITY && + } else if (Double.isInfinite(real) && Double.isNaN(imaginary)) { - return new Complex(Double.NaN, Double.NEGATIVE_INFINITY); + // Swap real and imaginary + return new Complex(Double.NaN, real); } else if (Double.isNaN(real) && imaginary == Double.POSITIVE_INFINITY) { return new Complex(Double.NaN, Double.NEGATIVE_INFINITY); @@ -748,8 +771,13 @@ public final class Complex implements Serializable { * @return the inverse sine of this complex number */ public Complex asin() { - return sqrt1z().add(multiply(I)).log().multiply(I.negate()); + // Define in terms of asinh + // asin(z) = -i asinh(iz) + return multiplyByI().asinh().multiplyByNegI(); + + //return sqrt1z().add(multiply(I)).log().multiply(I.negate()); } + /** * Compute the * <a href="http://mathworld.wolfram.com/InverseTangent.html"> @@ -761,7 +789,30 @@ public final class Complex implements Serializable { * @return the inverse tangent of this complex number */ public Complex atan() { - return add(I).divide(I.subtract(this)).log().multiply(I.multiply(0.5)); + // Define in terms of atanh + // atan(z) = -i atanh(iz) + return multiplyByI().atanh().multiplyByNegI(); + + // This is not exact to 1 ulp + //return add(I).divide(I.subtract(this)).log().multiply(I.multiply(0.5)); + } + + /** + * Multiply the Complex by I. + * + * @return the result (iz) + */ + private Complex multiplyByI() { + return new Complex(-imaginary, real); + } + + /** + * Multiply the Complex by -I. + * + * @return the result (-iz) + */ + private Complex multiplyByNegI() { + return new Complex(imaginary, -real); } /** @@ -774,7 +825,7 @@ public final class Complex implements Serializable { * </p><p> * @return the inverse hyperbolic cosine of this complex number */ - public Complex asinh(){ + public Complex asinh() { if (neitherInfiniteNorZeroNorNaN(real) && imaginary == Double.POSITIVE_INFINITY) { return new Complex(Double.POSITIVE_INFINITY, PI_OVER_2); @@ -807,7 +858,7 @@ public final class Complex implements Serializable { * </p><p> * @return the inverse hyperbolic cosine of this complex number */ - public Complex atanh(){ + public Complex atanh() { if (real == 0 && Double.isNaN(imaginary)) { return new Complex(0, Double.NaN); @@ -832,17 +883,60 @@ public final class Complex implements Serializable { } return add(ONE).divide(ONE.subtract(this)).log().multiply(0.5); } - /** + + /** * Compute the * <a href="http://mathworld.wolfram.com/InverseHyperbolicCosine.html"> * inverse hyperbolic cosine</a> of this complex number. * Implements the formula: * <p> * {@code acosh(z) = log(z+sqrt(z^2-1))} - * </p><p> + * </p> + * * @return the inverse hyperbolic cosine of this complex number */ public Complex acosh() { + if (Double.isFinite(real)) { + if (real == 0 && + imaginary == 0) { + return new Complex(0, PI_OVER_2); + } + if (imaginary == Double.POSITIVE_INFINITY) { + return new Complex(Double.POSITIVE_INFINITY, PI_OVER_2); + } + if (Double.isNaN(imaginary)) { + return NAN; + } + } else if (Double.isInfinite(real)) { + if (real == Double.NEGATIVE_INFINITY && + positiveSignedFinite(imaginary)) { + return new Complex(Double.POSITIVE_INFINITY, Math.PI); + } + if (real == Double.POSITIVE_INFINITY && + positiveSignedFinite(imaginary)) { + return new Complex(Double.POSITIVE_INFINITY, 0); + } + if (real == Double.NEGATIVE_INFINITY && + imaginary == Double.POSITIVE_INFINITY) { + return new Complex(Double.POSITIVE_INFINITY, PI_3_OVER_4); + } + if (real == Double.POSITIVE_INFINITY && + imaginary == Double.POSITIVE_INFINITY) { + return new Complex(Double.POSITIVE_INFINITY, PI_OVER_4); + } + if (Double.isNaN(imaginary)) { + return new Complex(Double.POSITIVE_INFINITY, Double.NaN); + } + } else if (Double.isNaN(real)) { + if (imaginary == Double.POSITIVE_INFINITY) { + return new Complex(Double.POSITIVE_INFINITY, Double.NaN); + } + if (Double.isFinite(imaginary) || Double.isNaN(imaginary)) { + // optionally raises the ‘‘invalid’’ floating-point exception, for finite y. + // No condition for imaginary as negative infinity + return NAN; + } + } return square().subtract(ONE).sqrt().add(this).log(); } @@ -861,7 +955,7 @@ public final class Complex implements Serializable { * cosine</a> of this complex number. * Implements the formula: * <p> - * {@code cos(a + bi) = cos(a)cosh(b) - sin(a)sinh(b)i} + * {@code cos(a + bi) = cos(a)cosh(b) - sin(a)sinh(b)i} * </p><p> * where the (real) functions on the right-hand side are * {@link Math#sin}, {@link Math#cos}, @@ -871,8 +965,12 @@ public final class Complex implements Serializable { * @return the cosine of this complex number. */ public Complex cos() { - return new Complex(Math.cos(real) * Math.cosh(imaginary), - -Math.sin(real) * Math.sinh(imaginary)); + // Define in terms of cosh + // cos(z) = cosh(iz) + return multiplyByI().cosh(); + + //return new Complex(Math.cos(real) * Math.cosh(imaginary), + // -Math.sin(real) * Math.sinh(imaginary)); } /** @@ -903,10 +1001,7 @@ public final class Complex implements Serializable { imaginary == 0) { return new Complex(Double.POSITIVE_INFINITY, 0); } else if (real == Double.POSITIVE_INFINITY && - imaginary == Double.POSITIVE_INFINITY) { - return new Complex(Double.POSITIVE_INFINITY, Double.NaN); - } else if (real == Double.POSITIVE_INFINITY && - Double.isNaN(imaginary)) { + (imaginary == Double.POSITIVE_INFINITY || Double.isNaN(imaginary))) { return new Complex(Double.POSITIVE_INFINITY, Double.NaN); } else if (Double.isNaN(real) && imaginary == 0) { @@ -941,19 +1036,16 @@ public final class Complex implements Serializable { imaginary == Double.POSITIVE_INFINITY) { return Complex.ZERO; } else if (real == Double.POSITIVE_INFINITY && - imaginary == Double.POSITIVE_INFINITY) { + (imaginary == Double.POSITIVE_INFINITY || Double.isNaN(imaginary))) { return new Complex(Double.POSITIVE_INFINITY, Double.NaN); } else if (real == Double.NEGATIVE_INFINITY && Double.isNaN(imaginary)) { return Complex.ZERO; - } else if (real == Double.POSITIVE_INFINITY && - Double.isNaN(imaginary)) { - return new Complex(Double.POSITIVE_INFINITY, Double.NaN); } else if (Double.isNaN(real) && imaginary == 0) { return new Complex(Double.NaN, 0); } - double expReal = Math.exp(real); + final double expReal = Math.exp(real); return new Complex(expReal * Math.cos(imaginary), expReal * Math.sin(imaginary)); } @@ -979,11 +1071,10 @@ public final class Complex implements Serializable { if (real == Double.POSITIVE_INFINITY && imaginary == Double.POSITIVE_INFINITY) { return new Complex(Double.POSITIVE_INFINITY, PI_OVER_4); - } else if (real == Double.POSITIVE_INFINITY && - Double.isNaN(imaginary)) { - return new Complex(Double.POSITIVE_INFINITY, Double.NaN); - } else if (Double.isNaN(real) && - imaginary == Double.POSITIVE_INFINITY) { + } else if ((real == Double.POSITIVE_INFINITY && + Double.isNaN(imaginary)) || + ((Double.isNaN(real) && + imaginary == Double.POSITIVE_INFINITY))) { return new Complex(Double.POSITIVE_INFINITY, Double.NaN); } return new Complex(Math.log(abs()), @@ -998,6 +1089,16 @@ public final class Complex implements Serializable { * @return the base 10 logarithm of <code>this</code>. */ public Complex log10() { + // Same edge cases as log() + if (real == Double.POSITIVE_INFINITY && + imaginary == Double.POSITIVE_INFINITY) { + return new Complex(Double.POSITIVE_INFINITY, PI_OVER_4); + } else if ((real == Double.POSITIVE_INFINITY && + Double.isNaN(imaginary)) || + ((Double.isNaN(real) && + imaginary == Double.POSITIVE_INFINITY))) { + return new Complex(Double.POSITIVE_INFINITY, Double.NaN); + } return new Complex(Math.log(abs()) / Math.log(10), Math.atan2(imaginary, real)); } @@ -1019,14 +1120,14 @@ public final class Complex implements Serializable { public Complex pow(Complex x) { if (real == 0 && imaginary == 0) { + // This value is zero. Test the other. if (x.real > 0 && x.imaginary == 0) { // 0 raised to positive number is 0 return ZERO; - } else { - // 0 raised to anything else is NaN - return NAN; } + // 0 raised to anything else is NaN + return NAN; } return log().multiply(x).exp(); } @@ -1038,16 +1139,16 @@ public final class Complex implements Serializable { * @return <code>this<sup>x</sup></code>. * @see #pow(Complex) */ - public Complex pow(double x) { + public Complex pow(double x) { if (real == 0 && imaginary == 0) { + // This value is zero. Test the other. if (x > 0) { // 0 raised to positive number is 0 return ZERO; - } else { - // 0 raised to anything else is NaN - return NAN; } + // 0 raised to anything else is NaN + return NAN; } return log().multiply(x).exp(); } @@ -1070,8 +1171,12 @@ public final class Complex implements Serializable { * @return the sine of this complex number. */ public Complex sin() { - return new Complex(Math.sin(real) * Math.cosh(imaginary), - Math.cos(real) * Math.sinh(imaginary)); + // Define in terms of sinh + // sin(z) = -i sinh(iz) + return multiplyByI().sinh().multiplyByNegI(); + + //return new Complex(Math.sin(real) * Math.cosh(imaginary), + // Math.cos(real) * Math.sinh(imaginary)); } /** @@ -1155,10 +1260,9 @@ public final class Complex implements Serializable { final double t = Math.sqrt((Math.abs(real) + abs()) / 2); if (real >= 0) { return new Complex(t, imaginary / (2 * t)); - } else { - return new Complex(Math.abs(imaginary) / (2 * t), - Math.copySign(1d, imaginary) * t); } + return new Complex(Math.abs(imaginary) / (2 * t), + Math.copySign(1d, imaginary) * t); } /** @@ -1192,19 +1296,23 @@ public final class Complex implements Serializable { * @return the tangent of {@code this}. */ public Complex tan() { - if (imaginary > 20) { - return ONE; - } - if (imaginary < -20) { - return MINUS_I; - } - - final double real2 = 2 * real; - final double imaginary2 = 2 * imaginary; - final double d = Math.cos(real2) + Math.cosh(imaginary2); - - return new Complex(Math.sin(real2) / d, - Math.sinh(imaginary2) / d); + // Define in terms of tanh + // tan(z) = -i tanh(iz) + return multiplyByI().tanh().multiplyByNegI(); + + //if (imaginary > 20) { + // return ONE; + //} + //if (imaginary < -20) { + // return MINUS_I; + //} + // + //final double real2 = 2 * real; + //final double imaginary2 = 2 * imaginary; + //final double d = Math.cos(real2) + Math.cosh(imaginary2); + // + //return new Complex(Math.sin(real2) / d, + // Math.sinh(imaginary2) / d); } /** @@ -1224,6 +1332,11 @@ public final class Complex implements Serializable { * @return the hyperbolic tangent of {@code this}. */ public Complex tanh() { + // TODO - should these checks be made on real2 and imaginary2? + // Math.cos and Math.sin return NaN for infinity. + // Math.cosh returns positive infinity for infinity. + // Math.sinh returns the input infinity for infinity. + if (real == Double.POSITIVE_INFINITY && imaginary == Double.POSITIVE_INFINITY) { return ONE; @@ -1260,14 +1373,16 @@ public final class Complex implements Serializable { * @return the argument of {@code this}. */ public double getArgument() { + // Delegate return Math.atan2(imaginary, real); } /** - * Compute the argument of this complex number. - * C++11 syntax + * Compute the argument of this complex number + * (C++11 grammar). * * @return the argument of {@code this}. + * @see #getArgument() */ public double arg() { return getArgument(); @@ -1284,12 +1399,13 @@ public final class Complex implements Serializable { * for <i>{@code k=0, 1, ..., n-1}</i>, where {@code abs} and {@code phi} * are respectively the {@link #abs() modulus} and * {@link #getArgument() argument} of this complex number. - * <p> - * If one or both parts of this complex number is NaN, a list with all - * all elements set to {@code NaN + NaN i} is returned. + * + * <p>If one or both parts of this complex number is NaN, a list with all + * all elements set to {@code NaN + NaN i} is returned.</p> * * @param n Degree of root. * @return a List of all {@code n}-th roots of {@code this}. + * @throws IllegalArgumentException if {@code n} is zero. */ public List<Complex> nthRoot(int n) { if (n == 0) { @@ -1305,7 +1421,7 @@ public final class Complex implements Serializable { final double nthPhi = getArgument() / n; final double slice = 2 * Math.PI / n; double innerPart = nthPhi; - for (int k = 0; k < Math.abs(n) ; k++) { + for (int k = 0; k < Math.abs(n); k++) { // inner part final double realPart = nthRootOfAbs * Math.cos(innerPart); final double imaginaryPart = nthRootOfAbs * Math.sin(innerPart); @@ -1329,17 +1445,6 @@ public final class Complex implements Serializable { } /** - * Check that the argument is positive and throw a RuntimeException - * if it is not. - * @param arg {@code double} to check - */ - private static void checkNotNegative(double arg) { - if (arg <= 0) { - throw new IllegalArgumentException("Complex: Non-positive argument"); - } - } - - /** * Returns {@code true} if the values are equal according to semantics of * {@link Double#equals(Object)}. * @@ -1352,7 +1457,7 @@ public final class Complex implements Serializable { } /** - * Check that a value meets all the following conditions: + * Check that a value meets all the following conditions. * <ul> * <li>it is not {@code NaN},</li> * <li>it is not infinite,</li> @@ -1364,9 +1469,25 @@ public final class Complex implements Serializable { * {@code false} otherwise. */ private static boolean neitherInfiniteNorZeroNorNaN(double d) { - return !Double.isNaN(d) && - !Double.isInfinite(d) && - d != 0; + return Double.isFinite(d) && d != 0; + } + + /** + * Check that a value meets all the following conditions. + * <ul> + * <li>it is not {@code NaN},</li> + * <li>it is not infinite,</li> + * <li>it is positive signed,</li> + * </ul> + * + * <p>Note: This is true for negative zero.</p> + * + * @param d Value. + * @return {@code true} if {@code d} meets all the conditions and + * {@code false} otherwise. + */ + private static boolean positiveSignedFinite(double d) { + return Double.isFinite(d) && d >= 0; } /** See {@link #parse(String)}. */ diff --git a/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/CStandardTest.java b/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/CStandardTest.java index d1e5a9c..e336b0c 100644 --- a/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/CStandardTest.java +++ b/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/CStandardTest.java @@ -17,93 +17,171 @@ package org.apache.commons.numbers.complex; +import org.apache.commons.numbers.core.Precision; +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; +/** + * 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 { + // CHECKSTYLE: stop ConstantName 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 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 oneOne = Complex.ofCartesian(1, 1); - private static final Complex oneZero = Complex.ofCartesian(1, 0); - private static final Complex oneInf = Complex.ofCartesian(1, inf); - private static final Complex oneNaN = Complex.ofCartesian(1, nan); - private static final Complex zeroInf = Complex.ofCartesian(0, inf); - private static final Complex zeroNegInf = Complex.ofCartesian(0,negInf); - private static final Complex zeroNaN = Complex.ofCartesian(0, nan); - private static final Complex zeroPiTwo = Complex.ofCartesian(0.0, piOverTwo); - private static final Complex negZeroZero = Complex.ofCartesian(-0.0, 0); - private static final Complex negI = Complex.ofCartesian(0.0, -1.0); - private static final Complex infOne = Complex.ofCartesian(inf, 1); - private static final Complex infZero = Complex.ofCartesian(inf, 0); - private static final Complex infNaN = Complex.ofCartesian(inf, nan); - private static final Complex infInf = Complex.ofCartesian(inf, inf); - private static final Complex infPiTwo = Complex.ofCartesian(inf, piOverTwo); - private static final Complex infPiFour = Complex.ofCartesian(inf, piOverFour); - private static final Complex infPi = Complex.ofCartesian(inf, Math.PI); - private static final Complex negInfInf = Complex.ofCartesian(negInf, inf); - private static final Complex negInfZero = Complex.ofCartesian(negInf, 0); - private static final Complex negInfOne = Complex.ofCartesian(negInf, 1); - private static final Complex negInfNaN = Complex.ofCartesian(negInf, nan); - private static final Complex negInfPosInf = Complex.ofCartesian(negInf, inf); - private static final Complex negInfPi = Complex.ofCartesian(negInf, Math.PI); - private static final Complex nanInf = Complex.ofCartesian(nan, inf); - private static final Complex nanNegInf = Complex.ofCartesian(nan, negInf); - private static final Complex nanZero = Complex.ofCartesian(nan, 0); - private static final Complex nanOne = Complex.ofCartesian(nan, 1); - private static final Complex piTwoNaN = Complex.ofCartesian(piOverTwo, nan); - private static final Complex piNegInf = Complex.ofCartesian(Math.PI, negInf); - private static final Complex piTwoNegInf = Complex.ofCartesian(piOverTwo, negInf); - private static final Complex piTwoNegZero = Complex.ofCartesian(piOverTwo, -0.0); - private static final Complex threePiFourNegInf = Complex.ofCartesian(threePiOverFour,negInf); - private static final Complex piFourNegInf = Complex.ofCartesian(piOverFour, negInf); - private static final Complex NAN = Complex.ofCartesian(nan, nan); + private static final double threePiOverFour = 3.0 * Math.PI / 4.0; + private static final Complex oneOne = complex(1, 1); + private static final Complex oneZero = complex(1, 0); + private static final Complex oneInf = complex(1, inf); + private static final Complex oneNaN = complex(1, nan); + 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 negI = complex(0.0, -1.0); + private static final Complex infOne = complex(inf, 1); + 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 negInfOne = complex(negInf, 1); + private static final Complex negInfNaN = complex(negInf, nan); + private static final Complex negInfPosInf = complex(negInf, inf); + 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 nanOne = complex(nan, 1); + 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); + // CHECKSTYLE: resume ConstantName - public void assertComplex(Complex c1, Complex c2, double realTol, double imagTol) { - Assertions.assertEquals(c1.getReal(), c2.getReal(), realTol); - Assertions.assertEquals(c1.getImaginary(), c2.getImaginary(), imagTol); + /** + * Assert the two complex numbers have their real and imaginary components within + * the given tolerance. + * + * @param c1 the first complex + * @param c2 the second complex + * @param maxUlps {@code (maxUlps - 1)} is the number of floating point + * values between the real (resp. imaginary) parts of {@code x} and + * {@code y}. + */ + public void assertComplex(Complex c1, Complex c2, int maxUlps) { + if (!Precision.equals(c1.getReal(), c2.getReal(), maxUlps) || + !Precision.equals(c1.getImaginary(), c2.getImaginary(), maxUlps)) { + Assertions.fail(c1 + " != " + c2); + } } + /** + * Assert the two complex numbers have equivalent real and imaginary components as + * defined by the {@code ==} operator. + * + * @param c1 the first complex + * @param c2 the second complex + */ public void assertComplex(Complex c1, Complex c2) { - Assertions.assertEquals(c1.getReal(), c2.getReal(),0.0); - Assertions.assertEquals(c1.getImaginary(), c2.getImaginary(), 0.0); + // Use a delta of zero to allow comparison of -0.0 to 0.0 + Assertions.assertEquals(c1.getReal(), c2.getReal(), 0.0, "real"); + Assertions.assertEquals(c1.getImaginary(), c2.getImaginary(), 0.0, "imaginary"); } + /** + * 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); + } /** - * ISO C Standard G.6.3 + * ISO C Standard G.6 (3). */ @Test public void testSqrt1() { - Complex z1 = Complex.ofCartesian(-2.0, 0.0); - Complex z2 = Complex.ofCartesian(0.0, Math.sqrt(2)); - assertComplex(z1.sqrt(), z2); - z1 = Complex.ofCartesian(-2.0, -0.0); - z2 = Complex.ofCartesian(0.0, -Math.sqrt(2)); - assertComplex(z1.sqrt(), z2); + 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() { - Complex z1 = Complex.ofReal(3.0); - Complex z2 = Complex.ofCartesian(0.0, 3.0); - assertComplex(z1.asin(), negI.multiply(z2.asinh())); - assertComplex(z1.atan(), negI.multiply(z2.atanh()), Math.ulp(1.0), Math.ulp(1.0)); - assertComplex(z1.cos(), z2.cosh()); - assertComplex(z1.sin(), negI.multiply(z2.sinh())); - assertComplex(z1.tan(), negI.multiply(z2.tanh())); + 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 = Complex.I.multiply(z); + assertComplex(z.asin(), negI.multiply(iz.asinh())); + assertComplex(z.atan(), negI.multiply(iz.atanh())); + assertComplex(z.cos(), iz.cosh()); + assertComplex(z.sin(), negI.multiply(iz.sinh())); + assertComplex(z.tan(), negI.multiply(iz.tanh())); + } + } + + /** + * Create a number in the range {@code (-1,1)}. + * + * @param rng the random generator + * @return the number + */ + private static double next(UniformRandomProvider rng) { + return rng.nextDouble() * (rng.nextBoolean() ? -1 : 1); } /** - * ISO C Standard G.6.1.1 + * 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() { - assertComplex(oneOne.acos().conj(), oneOne.conj().acos(), Math.ulp(1.0), Math.ulp(1.0)); + assertComplex(oneOne.acos().conj(), oneOne.conj().acos(), 1); assertComplex(Complex.ZERO.acos(), piTwoNegZero); assertComplex(negZeroZero.acos(), piTwoNegZero); assertComplex(zeroNaN.acos(), piTwoNaN); @@ -121,7 +199,29 @@ public class CStandardTest { } /** - * ISO C Standard G.6.2.2 + * ISO C Standard G.6.2.1. + */ + @Test + public void testAcosh() { + assertComplex(oneOne.acosh().conj(), oneOne.conj().acosh(), 1); + assertComplex(Complex.ZERO.acosh(), zeroPiTwo); + assertComplex(negZeroZero.acosh(), zeroPiTwo); + assertComplex(oneInf.acosh(), infPiTwo); + assertComplex(zeroNaN.acosh(), NAN); + assertComplex(oneNaN.acosh(), NAN); + assertComplex(negInfOne.acosh(), infPi); + assertComplex(infOne.acosh(), infZero); + assertComplex(negInfPosInf.acosh(), infThreePiFour); + assertComplex(infInf.acosh(), infPiFour); + assertComplex(infNaN.acosh(), infNaN); + assertComplex(negInfNaN.acosh(), infNaN); + assertComplex(nanOne.acosh(), NAN); + assertComplex(nanInf.acosh(), infNaN); + assertComplex(NAN.acosh(), NAN); + } + + /** + * ISO C Standard G.6.2.2. */ @Test public void testAsinh() { @@ -140,7 +240,7 @@ public class CStandardTest { } /** - * ISO C Standard G.6.2.3 + * ISO C Standard G.6.2.3. */ @Test public void testAtanh() { @@ -148,7 +248,7 @@ public class CStandardTest { assertComplex(Complex.ZERO.atanh(), Complex.ZERO); assertComplex(zeroNaN.atanh(), zeroNaN); assertComplex(oneZero.atanh(), infZero); - assertComplex(oneInf.atanh(),zeroPiTwo); + assertComplex(oneInf.atanh(), zeroPiTwo); assertComplex(oneNaN.atanh(), NAN); assertComplex(infOne.atanh(), zeroPiTwo); assertComplex(infInf.atanh(), zeroPiTwo); @@ -159,7 +259,7 @@ public class CStandardTest { } /** - * ISO C Standard G.6.2.4 + * ISO C Standard G.6.2.4. */ @Test public void testCosh() { @@ -181,7 +281,7 @@ public class CStandardTest { } /** - * ISO C Standard G.6.2.5 + * ISO C Standard G.6.2.5. */ @Test public void testSinh() { @@ -201,7 +301,7 @@ public class CStandardTest { } /** - * ISO C Standard G.6.2.6 + * ISO C Standard G.6.2.6. */ @Test public void testTanh() { @@ -218,7 +318,7 @@ public class CStandardTest { } /** - * ISO C Standard G.6.3.1 + * ISO C Standard G.6.3.1. */ @Test public void testExp() { @@ -239,7 +339,7 @@ public class CStandardTest { } /** - * ISO C Standard G.6.3.2 + * ISO C Standard G.6.3.2. */ @Test public void testLog() { @@ -258,7 +358,27 @@ public class CStandardTest { } /** - * ISO C Standard G.6.4.2 + * Same edge cases as log() since the real component is divided by Math.log(10) whic + * has no effect on infinite or nan. + */ + @Test + public void testLog10() { + assertComplex(oneOne.log10().conj(), oneOne.conj().log10()); + assertComplex(negZeroZero.log10(), negInfPi); + assertComplex(Complex.ZERO.log10(), negInfZero); + assertComplex(oneInf.log10(), infPiTwo); + assertComplex(oneNaN.log10(), NAN); + assertComplex(negInfOne.log10(), infPi); + assertComplex(infOne.log10(), infZero); + assertComplex(infInf.log10(), infPiFour); + assertComplex(infNaN.log10(), infNaN); + assertComplex(nanOne.log10(), NAN); + assertComplex(nanInf.log10(), infNaN); + assertComplex(NAN.log10(), NAN); + } + + /** + * ISO C Standard G.6.4.2. */ @Test public void testSqrt2() { diff --git a/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/ComplexTest.java b/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/ComplexTest.java index e8128dd..8078da8 100644 --- a/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/ComplexTest.java +++ b/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/ComplexTest.java @@ -18,15 +18,19 @@ package org.apache.commons.numbers.complex; import java.util.List; + +import org.apache.commons.rng.UniformRandomProvider; +import org.apache.commons.rng.simple.RandomSource; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; - /** + * Tests for {@link Complex}. */ public class ComplexTest { + // CHECKSTYLE: stop ConstantName private static final double inf = Double.POSITIVE_INFINITY; private static final double neginf = Double.NEGATIVE_INFINITY; private static final double nan = Double.NaN; @@ -51,44 +55,137 @@ public class ComplexTest { private static final Complex nanZero = Complex.ofCartesian(nan, 0); private static final Complex NAN = Complex.ofCartesian(nan, nan); private static final Complex INF = Complex.ofCartesian(inf, inf); + // CHECKSTYLE: resume ConstantName + + /** + * Used to test the number category of a Complex. + */ + private enum NumberType { + NAN, + INFINITE, + FINITE + } @Test - public void testConstructor() { - Complex z = Complex.ofCartesian(3.0, 4.0); - Assertions.assertEquals(3.0, z.getReal(), 1.0e-5); - Assertions.assertEquals(4.0, z.getImaginary(), 1.0e-5); + public void testCartesianConstructor() { + final Complex z = Complex.ofCartesian(3.0, 4.0); + Assertions.assertEquals(3.0, z.getReal()); + Assertions.assertEquals(4.0, z.getImaginary()); } @Test - public void testConstructorNaN() { - Complex z = Complex.ofCartesian(3.0, Double.NaN); - Assertions.assertTrue(z.isNaN()); + public void testRealConstructor() { + final Complex z = Complex.ofReal(3.0); + Assertions.assertEquals(3.0, z.getReal()); + Assertions.assertEquals(0.0, z.getImaginary()); + } - z = Complex.ofCartesian(nan, 4.0); - Assertions.assertTrue(z.isNaN()); + @Test + public void testPolarConstructor() { + final double r = 98765; + final double theta = 0.12345; + final Complex z = Complex.ofPolar(r, theta); + final Complex y = Complex.ofCis(theta); + Assertions.assertEquals(r * y.getReal(), z.getReal()); + Assertions.assertEquals(r * y.getImaginary(), z.getImaginary()); - z = Complex.ofCartesian(3.0, 4.0); - Assertions.assertFalse(z.isNaN()); + Assertions.assertThrows(IllegalArgumentException.class, () -> Complex.ofPolar(-1, 0), + "negative modulus should not be allowed"); + } + + @Test + public void testCisConstructor() { + final double x = 0.12345; + final Complex z = Complex.ofCis(x); + Assertions.assertEquals(Math.cos(x), z.getReal()); + Assertions.assertEquals(Math.sin(x), z.getImaginary()); + } + + @Test + public void testNumberType() { + assertNumberType(0, 0, NumberType.FINITE); + assertNumberType(1, 0, NumberType.FINITE); + assertNumberType(0, 1, NumberType.FINITE); + + assertNumberType(inf, 0, NumberType.INFINITE); + assertNumberType(-inf, 0, NumberType.INFINITE); + assertNumberType(0, inf, NumberType.INFINITE); + assertNumberType(0, -inf, NumberType.INFINITE); + // A complex or imaginary value with at least one infinite part is regarded as an infinity + // (even if its other part is a NaN). + assertNumberType(inf, nan, NumberType.INFINITE); + assertNumberType(-inf, nan, NumberType.INFINITE); + assertNumberType(nan, inf, NumberType.INFINITE); + assertNumberType(nan, -inf, NumberType.INFINITE); + + assertNumberType(nan, 0, NumberType.NAN); + assertNumberType(0, nan, NumberType.NAN); + assertNumberType(nan, nan, NumberType.NAN); + } + + /** + * Assert the number type of the Complex created from the real and imaginary + * components. + * + * @param real the real component + * @param imaginary the imaginary component + * @param type the type + */ + private static void assertNumberType(double real, double imaginary, NumberType type) { + final Complex z = Complex.ofCartesian(real, imaginary); + final boolean isNaN = z.isNaN(); + final boolean isInfinite = z.isInfinite(); + final boolean isFinite = z.isFinite(); + // A number can be only one + int count = isNaN ? 1 : 0; + count += isInfinite ? 1 : 0; + count += isFinite ? 1 : 0; + Assertions.assertEquals(1, count, + () -> String.format("Complex can be only one type: isNaN=%s, isInfinite=%s, isFinite=%s: %s", + isNaN, isInfinite, isFinite, z)); + switch (type) { + case FINITE: + Assertions.assertTrue(isFinite, () -> "not finite: " + z); + break; + case INFINITE: + Assertions.assertTrue(isInfinite, () -> "not infinite: " + z); + break; + case NAN: + Assertions.assertTrue(isNaN, () -> "not nan: " + z); + break; + default: + Assertions.fail("Unknown number type"); + } + } + + + @Test + public void testProj() { + final Complex z = Complex.ofCartesian(3.0, 4.0); + Assertions.assertSame(z, z.proj()); + TestUtils.assertSame(infZero, Complex.ofCartesian(inf, 4.0).proj()); + TestUtils.assertSame(infZero, Complex.ofCartesian(3.0, inf).proj()); } @Test public void testAbs() { - Complex z = Complex.ofCartesian(3.0, 4.0); + final Complex z = Complex.ofCartesian(3.0, 4.0); Assertions.assertEquals(5.0, z.abs(), 1.0e-5); } @Test public void testAbsNaN() { Assertions.assertTrue(Double.isNaN(NAN.abs())); - Complex z = Complex.ofCartesian(inf, nan); - Assertions.assertTrue(Double.isNaN(z.abs())); + // The result is infinite if either argument is infinite + Assertions.assertEquals(inf, Complex.ofCartesian(inf, nan).abs()); + Assertions.assertEquals(inf, Complex.ofCartesian(nan, inf).abs()); } @Test public void testAdd() { - Complex x = Complex.ofCartesian(3.0, 4.0); - Complex y = Complex.ofCartesian(5.0, 6.0); - Complex z = x.add(y); + final Complex x = Complex.ofCartesian(3.0, 4.0); + final Complex y = Complex.ofCartesian(5.0, 6.0); + final Complex z = x.add(y); Assertions.assertEquals(8.0, z.getReal(), 1.0e-5); Assertions.assertEquals(10.0, z.getImaginary(), 1.0e-5); } @@ -96,8 +193,8 @@ public class ComplexTest { @Test public void testAddInf() { Complex x = Complex.ofCartesian(1, 1); - Complex z = Complex.ofCartesian(inf, 0); - Complex w = x.add(z); + final Complex z = Complex.ofCartesian(inf, 0); + final Complex w = x.add(z); Assertions.assertEquals(1, w.getImaginary(), 0); Assertions.assertEquals(inf, w.getReal(), 0); @@ -108,26 +205,26 @@ public class ComplexTest { @Test public void testScalarAdd() { - Complex x = Complex.ofCartesian(3.0, 4.0); - double yDouble = 2.0; - Complex yComplex = Complex.ofReal(yDouble); + final Complex x = Complex.ofCartesian(3.0, 4.0); + final double yDouble = 2.0; + final Complex yComplex = Complex.ofReal(yDouble); Assertions.assertEquals(x.add(yComplex), x.add(yDouble)); } @Test public void testScalarAddNaN() { - Complex x = Complex.ofCartesian(3.0, 4.0); - double yDouble = Double.NaN; - Complex yComplex = Complex.ofReal(yDouble); + final Complex x = Complex.ofCartesian(3.0, 4.0); + final double yDouble = Double.NaN; + final Complex yComplex = Complex.ofReal(yDouble); Assertions.assertEquals(x.add(yComplex), x.add(yDouble)); } @Test public void testScalarAddInf() { Complex x = Complex.ofCartesian(1, 1); - double yDouble = Double.POSITIVE_INFINITY; + final double yDouble = Double.POSITIVE_INFINITY; - Complex yComplex = Complex.ofReal(yDouble); + final Complex yComplex = Complex.ofReal(yDouble); Assertions.assertEquals(x.add(yComplex), x.add(yDouble)); x = Complex.ofCartesian(neginf, 0); @@ -136,15 +233,15 @@ public class ComplexTest { @Test public void testConjugate() { - Complex x = Complex.ofCartesian(3.0, 4.0); - Complex z = x.conjugate(); + final Complex x = Complex.ofCartesian(3.0, 4.0); + final Complex z = x.conjugate(); Assertions.assertEquals(3.0, z.getReal(), 1.0e-5); Assertions.assertEquals(-4.0, z.getImaginary(), 1.0e-5); } @Test public void testConjugateNaN() { - Complex z = NAN.conjugate(); + final Complex z = NAN.conjugate(); Assertions.assertTrue(z.isNaN()); } @@ -158,100 +255,100 @@ public class ComplexTest { @Test public void testDivide() { - Complex x = Complex.ofCartesian(3.0, 4.0); - Complex y = Complex.ofCartesian(5.0, 6.0); - Complex z = x.divide(y); + final Complex x = Complex.ofCartesian(3.0, 4.0); + final Complex y = Complex.ofCartesian(5.0, 6.0); + final Complex z = x.divide(y); Assertions.assertEquals(39.0 / 61.0, z.getReal(), 1.0e-5); Assertions.assertEquals(2.0 / 61.0, z.getImaginary(), 1.0e-5); } @Test public void testDivideReal() { - Complex x = Complex.ofCartesian(2d, 3d); - Complex y = Complex.ofCartesian(2d, 0d); + final Complex x = Complex.ofCartesian(2d, 3d); + final Complex y = Complex.ofCartesian(2d, 0d); Assertions.assertEquals(Complex.ofCartesian(1d, 1.5), x.divide(y)); } @Test public void testDivideImaginary() { - Complex x = Complex.ofCartesian(2d, 3d); - Complex y = Complex.ofCartesian(0d, 2d); + final Complex x = Complex.ofCartesian(2d, 3d); + final Complex y = Complex.ofCartesian(0d, 2d); Assertions.assertEquals(Complex.ofCartesian(1.5d, -1d), x.divide(y)); } @Test public void testDivideZero() { - Complex x = Complex.ofCartesian(3.0, 4.0); - Complex z = x.divide(Complex.ZERO); + final Complex x = Complex.ofCartesian(3.0, 4.0); + final Complex z = x.divide(Complex.ZERO); Assertions.assertEquals(INF, z); } @Test public void testDivideZeroZero() { - Complex x = Complex.ofCartesian(0.0, 0.0); - Complex z = x.divide(Complex.ZERO); + final Complex x = Complex.ofCartesian(0.0, 0.0); + final Complex z = x.divide(Complex.ZERO); Assertions.assertEquals(NAN, z); } @Test public void testDivideNaN() { - Complex x = Complex.ofCartesian(3.0, 4.0); - Complex z = x.divide(NAN); + final Complex x = Complex.ofCartesian(3.0, 4.0); + final Complex z = x.divide(NAN); Assertions.assertTrue(z.isNaN()); } @Test public void testDivideNanInf() { - Complex z = oneInf.divide(Complex.ONE); - Assertions.assertTrue(Double.isNaN(z.getReal())); - Assertions.assertEquals(inf, z.getImaginary(), 0); + Complex z = oneInf.divide(Complex.ONE); + Assertions.assertTrue(Double.isNaN(z.getReal())); + Assertions.assertEquals(inf, z.getImaginary(), 0); - z = negInfNegInf.divide(oneNan); - Assertions.assertTrue(Double.isNaN(z.getReal())); - Assertions.assertTrue(Double.isNaN(z.getImaginary())); + z = negInfNegInf.divide(oneNan); + Assertions.assertTrue(Double.isNaN(z.getReal())); + Assertions.assertTrue(Double.isNaN(z.getImaginary())); - z = negInfInf.divide(Complex.ONE); - Assertions.assertTrue(Double.isInfinite(z.getReal())); - Assertions.assertTrue(Double.isInfinite(z.getImaginary())); + z = negInfInf.divide(Complex.ONE); + Assertions.assertTrue(Double.isInfinite(z.getReal())); + Assertions.assertTrue(Double.isInfinite(z.getImaginary())); } @Test public void testScalarDivide() { - Complex x = Complex.ofCartesian(3.0, 4.0); - double yDouble = 2.0; - Complex yComplex = Complex.ofReal(yDouble); + final Complex x = Complex.ofCartesian(3.0, 4.0); + final double yDouble = 2.0; + final Complex yComplex = Complex.ofReal(yDouble); Assertions.assertEquals(x.divide(yComplex), x.divide(yDouble)); } @Test public void testScalarDivideNaN() { - Complex x = Complex.ofCartesian(3.0, 4.0); - double yDouble = Double.NaN; - Complex yComplex = Complex.ofReal(yDouble); + final Complex x = Complex.ofCartesian(3.0, 4.0); + final double yDouble = Double.NaN; + final Complex yComplex = Complex.ofReal(yDouble); Assertions.assertEquals(x.divide(yComplex), x.divide(yDouble)); } @Test public void testScalarDivideZero() { - Complex x = Complex.ofCartesian(1,1); + final Complex x = Complex.ofCartesian(1, 1); TestUtils.assertEquals(x.divide(Complex.ZERO), x.divide(0), 0); } @Test public void testReciprocal() { - Complex z = Complex.ofCartesian(5.0, 6.0); - Complex act = z.reciprocal(); - double expRe = 5.0 / 61.0; - double expIm = -6.0 / 61.0; + final Complex z = Complex.ofCartesian(5.0, 6.0); + final Complex act = z.reciprocal(); + final double expRe = 5.0 / 61.0; + final double expIm = -6.0 / 61.0; Assertions.assertEquals(expRe, act.getReal(), Math.ulp(expRe)); Assertions.assertEquals(expIm, act.getImaginary(), Math.ulp(expIm)); } @Test public void testReciprocalReciprocal() { - Complex z = Complex.ofCartesian(5.0, 6.0); - Complex zRR = z.reciprocal().reciprocal(); + final Complex z = Complex.ofCartesian(5.0, 6.0); + final Complex zRR = z.reciprocal().reciprocal(); final double tol = 1e-14; Assertions.assertEquals(zRR.getReal(), z.getReal(), tol); Assertions.assertEquals(zRR.getImaginary(), z.getImaginary(), tol); @@ -259,13 +356,13 @@ public class ComplexTest { @Test public void testReciprocalReal() { - Complex z = Complex.ofCartesian(-2.0, 0.0); + final Complex z = Complex.ofCartesian(-2.0, 0.0); Assertions.assertTrue(Complex.equals(Complex.ofCartesian(-0.5, 0.0), z.reciprocal())); } @Test public void testReciprocalImaginary() { - Complex z = Complex.ofCartesian(0.0, -2.0); + final Complex z = Complex.ofCartesian(0.0, -2.0); Assertions.assertEquals(Complex.ofCartesian(0.0, 0.5), z.reciprocal()); } @@ -276,9 +373,9 @@ public class ComplexTest { @Test public void testMultiply() { - Complex x = Complex.ofCartesian(3.0, 4.0); - Complex y = Complex.ofCartesian(5.0, 6.0); - Complex z = x.multiply(y); + final Complex x = Complex.ofCartesian(3.0, 4.0); + final Complex y = Complex.ofCartesian(5.0, 6.0); + final Complex z = x.multiply(y); Assertions.assertEquals(-9.0, z.getReal(), 1.0e-5); Assertions.assertEquals(38.0, z.getImaginary(), 1.0e-5); } @@ -291,26 +388,26 @@ public class ComplexTest { @Test public void testScalarMultiply() { - Complex x = Complex.ofCartesian(3.0, 4.0); - double yDouble = 2.0; - Complex yComplex = Complex.ofReal(yDouble); + final Complex x = Complex.ofCartesian(3.0, 4.0); + final double yDouble = 2.0; + final Complex yComplex = Complex.ofReal(yDouble); Assertions.assertEquals(x.multiply(yComplex), x.multiply(yDouble)); - int zInt = -5; - Complex zComplex = Complex.ofReal(zInt); + final int zInt = -5; + final Complex zComplex = Complex.ofReal(zInt); Assertions.assertEquals(x.multiply(zComplex), x.multiply(zInt)); } @Test public void testScalarMultiplyNaN() { - Complex x = Complex.ofCartesian(3.0, 4.0); - double yDouble = Double.NaN; - Complex yComplex = Complex.ofReal(yDouble); + final Complex x = Complex.ofCartesian(3.0, 4.0); + final double yDouble = Double.NaN; + final Complex yComplex = Complex.ofReal(yDouble); Assertions.assertEquals(x.multiply(yComplex), x.multiply(yDouble)); } @Test public void testScalarMultiplyInf() { - Complex x = Complex.ofCartesian(1, 1); + final Complex x = Complex.ofCartesian(1, 1); double yDouble = Double.POSITIVE_INFINITY; Complex yComplex = Complex.ofReal(yDouble); Assertions.assertEquals(x.multiply(yComplex), x.multiply(yDouble)); @@ -322,23 +419,23 @@ public class ComplexTest { @Test public void testNegate() { - Complex x = Complex.ofCartesian(3.0, 4.0); - Complex z = x.negate(); + final Complex x = Complex.ofCartesian(3.0, 4.0); + final Complex z = x.negate(); Assertions.assertEquals(-3.0, z.getReal(), 1.0e-5); Assertions.assertEquals(-4.0, z.getImaginary(), 1.0e-5); } @Test public void testNegateNaN() { - Complex z = NAN.negate(); + final Complex z = NAN.negate(); Assertions.assertTrue(z.isNaN()); } @Test public void testSubtract() { - Complex x = Complex.ofCartesian(3.0, 4.0); - Complex y = Complex.ofCartesian(5.0, 6.0); - Complex z = x.subtract(y); + final Complex x = Complex.ofCartesian(3.0, 4.0); + final Complex y = Complex.ofCartesian(5.0, 6.0); + final Complex z = x.subtract(y); Assertions.assertEquals(-2.0, z.getReal(), 1.0e-5); Assertions.assertEquals(-2.0, z.getImaginary(), 1.0e-5); } @@ -346,8 +443,8 @@ public class ComplexTest { @Test public void testSubtractInf() { Complex x = Complex.ofCartesian(1, 1); - Complex z = Complex.ofCartesian(neginf, 0); - Complex w = x.subtract(z); + final Complex z = Complex.ofCartesian(neginf, 0); + final Complex w = x.subtract(z); Assertions.assertEquals(1, w.getImaginary(), 0); Assertions.assertEquals(inf, w.getReal(), 0); @@ -357,25 +454,25 @@ public class ComplexTest { @Test public void testScalarSubtract() { - Complex x = Complex.ofCartesian(3.0, 4.0); - double yDouble = 2.0; - Complex yComplex = Complex.ofReal(yDouble); + final Complex x = Complex.ofCartesian(3.0, 4.0); + final double yDouble = 2.0; + final Complex yComplex = Complex.ofReal(yDouble); Assertions.assertEquals(x.subtract(yComplex), x.subtract(yDouble)); } @Test public void testScalarSubtractNaN() { - Complex x = Complex.ofCartesian(3.0, 4.0); - double yDouble = Double.NaN; - Complex yComplex = Complex.ofReal(yDouble); + final Complex x = Complex.ofCartesian(3.0, 4.0); + final double yDouble = Double.NaN; + final Complex yComplex = Complex.ofReal(yDouble); Assertions.assertEquals(x.subtract(yComplex), x.subtract(yDouble)); } @Test public void testScalarSubtractInf() { Complex x = Complex.ofCartesian(1, 1); - double yDouble = Double.POSITIVE_INFINITY; - Complex yComplex = Complex.ofReal(yDouble); + final double yDouble = Double.POSITIVE_INFINITY; + final Complex yComplex = Complex.ofReal(yDouble); Assertions.assertEquals(x.subtract(yComplex), x.subtract(yDouble)); x = Complex.ofCartesian(neginf, 0); @@ -385,33 +482,33 @@ public class ComplexTest { @Test public void testEqualsNull() { - Complex x = Complex.ofCartesian(3.0, 4.0); + final Complex x = Complex.ofCartesian(3.0, 4.0); Assertions.assertNotEquals(null, x); } @Test public void testFloatingPointEqualsPrecondition1() { Assertions.assertThrows(NullPointerException.class, - () -> Complex.equals(Complex.ofCartesian(3.0, 4.0), null, 3) + () -> Complex.equals(Complex.ofCartesian(3.0, 4.0), null, 3) ); - } + @Test public void testFloatingPointEqualsPrecondition2() { Assertions.assertThrows(NullPointerException.class, - () -> Complex.equals(null, Complex.ofCartesian(3.0, 4.0), 3) + () -> Complex.equals(null, Complex.ofCartesian(3.0, 4.0), 3) ); } @Test public void testEqualsClass() { - Complex x = Complex.ofCartesian(3.0, 4.0); + final Complex x = Complex.ofCartesian(3.0, 4.0); Assertions.assertFalse(x.equals(this)); } @Test public void testEqualsSame() { - Complex x = Complex.ofCartesian(3.0, 4.0); + final Complex x = Complex.ofCartesian(3.0, 4.0); Assertions.assertEquals(x, x); } @@ -494,22 +591,22 @@ public class ComplexTest { @Test public void testEqualsTrue() { - Complex x = Complex.ofCartesian(3.0, 4.0); - Complex y = Complex.ofCartesian(3.0, 4.0); + final Complex x = Complex.ofCartesian(3.0, 4.0); + final Complex y = Complex.ofCartesian(3.0, 4.0); Assertions.assertEquals(x, y); } @Test public void testEqualsRealDifference() { - Complex x = Complex.ofCartesian(0.0, 0.0); - Complex y = Complex.ofCartesian(0.0 + Double.MIN_VALUE, 0.0); + final Complex x = Complex.ofCartesian(0.0, 0.0); + final Complex y = Complex.ofCartesian(0.0 + Double.MIN_VALUE, 0.0); Assertions.assertNotEquals(x, y); } @Test public void testEqualsImaginaryDifference() { - Complex x = Complex.ofCartesian(0.0, 0.0); - Complex y = Complex.ofCartesian(0.0, 0.0 + Double.MIN_VALUE); + final Complex x = Complex.ofCartesian(0.0, 0.0); + final Complex y = Complex.ofCartesian(0.0, 0.0 + Double.MIN_VALUE); Assertions.assertNotEquals(x, y); } @@ -520,8 +617,8 @@ public class ComplexTest { Assertions.assertNotEquals(x.hashCode(), y.hashCode()); y = Complex.ofCartesian(0.0 + Double.MIN_VALUE, 0.0); Assertions.assertNotEquals(x.hashCode(), y.hashCode()); - Complex realNan = Complex.ofCartesian(Double.NaN, 0.0); - Complex imaginaryNan = Complex.ofCartesian(0.0, Double.NaN); + final Complex realNan = Complex.ofCartesian(Double.NaN, 0.0); + final Complex imaginaryNan = Complex.ofCartesian(0.0, Double.NaN); Assertions.assertEquals(realNan.hashCode(), imaginaryNan.hashCode()); Assertions.assertEquals(imaginaryNan.hashCode(), NAN.hashCode()); @@ -542,74 +639,86 @@ public class ComplexTest { } @Test - @Disabled - public void testJava() {// TODO more debug + @Disabled("Used to output the java environment") + public void testJava() { + // CHECKSTYLE: stop Regexp System.out.println(">>testJava()"); // MathTest#testExpSpecialCases() checks the following: // Assert.assertEquals("exp of -infinity should be 0.0", 0.0, Math.exp(Double.NEGATIVE_INFINITY), Precision.EPSILON); // Let's check how well Math works: - System.out.println("Math.exp="+Math.exp(Double.NEGATIVE_INFINITY)); - String props[] = { - "java.version", // Java Runtime Environment version - "java.vendor", // Java Runtime Environment vendor - "java.vm.specification.version", // Java Virtual Machine specification version - "java.vm.specification.vendor", // Java Virtual Machine specification vendor - "java.vm.specification.name", // Java Virtual Machine specification name - "java.vm.version", // Java Virtual Machine implementation version - "java.vm.vendor", // Java Virtual Machine implementation vendor - "java.vm.name", // Java Virtual Machine implementation name - "java.specification.version", // Java Runtime Environment specification version - "java.specification.vendor", // Java Runtime Environment specification vendor - "java.specification.name", // Java Runtime Environment specification name - "java.class.version", // Java class format version number + System.out.println("Math.exp=" + Math.exp(Double.NEGATIVE_INFINITY)); + final String[] props = { + "java.version", // Java Runtime Environment version + "java.vendor", // Java Runtime Environment vendor + "java.vm.specification.version", // Java Virtual Machine specification version + "java.vm.specification.vendor", // Java Virtual Machine specification vendor + "java.vm.specification.name", // Java Virtual Machine specification name + "java.vm.version", // Java Virtual Machine implementation version + "java.vm.vendor", // Java Virtual Machine implementation vendor + "java.vm.name", // Java Virtual Machine implementation name + "java.specification.version", // Java Runtime Environment specification version + "java.specification.vendor", // Java Runtime Environment specification vendor + "java.specification.name", // Java Runtime Environment specification name + "java.class.version", // Java class format version number }; - for(String t : props) { + for (final String t : props) { System.out.println(t + "=" + System.getProperty(t)); } System.out.println("<<testJava()"); + // CHECKSTYLE: resume Regexp } - @Test - public void testScalarPow() { - Complex x = Complex.ofCartesian(3, 4); - double yDouble = 5.0; - Complex yComplex = Complex.ofReal(yDouble); + public void testPow() { + final Complex x = Complex.ofCartesian(3, 4); + final double yDouble = 5.0; + final Complex yComplex = Complex.ofReal(yDouble); Assertions.assertEquals(x.pow(yComplex), x.pow(yDouble)); } @Test - public void testScalarPowNanBase() { - Complex x = NAN; - double yDouble = 5.0; - Complex yComplex = Complex.ofReal(yDouble); - Assertions.assertEquals(x.pow(yComplex), x.pow(yDouble)); + public void testPowComplexZeroBase() { + final double x = Double.MIN_VALUE; + assertPowComplexZeroBase(0, 0, NAN); + assertPowComplexZeroBase(0, x, NAN); + assertPowComplexZeroBase(x, x, NAN); + assertPowComplexZeroBase(x, 0, Complex.ZERO); + } + + private static void assertPowComplexZeroBase(double re, double im, Complex expected) { + final Complex z = Complex.ofCartesian(re, im); + final Complex c = Complex.ZERO.pow(z); + Assertions.assertEquals(expected, c); } @Test - public void testScalarPowZeroBaseZeroExponent() { - Complex x = Complex.ZERO; - double yDouble = 0; - Assertions.assertEquals(NAN, x.pow(yDouble)); - Complex yComplex = Complex.ofReal(yDouble); - Assertions.assertEquals(NAN, x.pow(yComplex)); + public void testPowScalarZeroBase() { + final double x = Double.MIN_VALUE; + assertPowScalarZeroBase(0, NAN); + assertPowScalarZeroBase(x, Complex.ZERO); } + + private static void assertPowScalarZeroBase(double exp, Complex expected) { + final Complex c = Complex.ZERO.pow(exp); + Assertions.assertEquals(expected, c); + } + @Test - public void testScalarPowZeroBasePositiveExponent() { - Complex x = Complex.ZERO; - double yDouble = Double.MIN_VALUE; - Assertions.assertEquals(Complex.ZERO, x.pow(yDouble)); - Complex yComplex = Complex.ofReal(yDouble); - Assertions.assertEquals(Complex.ZERO, x.pow(yComplex)); + public void testPowNanBase() { + final Complex x = NAN; + final double yDouble = 5.0; + final Complex yComplex = Complex.ofReal(yDouble); + Assertions.assertEquals(x.pow(yComplex), x.pow(yDouble)); } @Test - public void testScalarPowNanExponent() { - Complex x = Complex.ofCartesian(3, 4); - double yDouble = Double.NaN; - Complex yComplex = Complex.ofReal(yDouble); + public void testPowNanExponent() { + final Complex x = Complex.ofCartesian(3, 4); + final double yDouble = Double.NaN; + final Complex yComplex = Complex.ofReal(yDouble); Assertions.assertEquals(x.pow(yComplex), x.pow(yDouble)); } + @Test public void testSqrtPolar() { final double tol = 1e-12; @@ -619,13 +728,20 @@ public class ComplexTest { double theta = 0; for (int j = 0; j < 11; j++) { theta += pi / 12; - Complex z = Complex.ofPolar(r, theta); - Complex sqrtz = Complex.ofPolar(Math.sqrt(r), theta / 2); + final Complex z = Complex.ofPolar(r, theta); + final Complex sqrtz = Complex.ofPolar(Math.sqrt(r), theta / 2); TestUtils.assertEquals(sqrtz, z.sqrt(), tol); } } } + @Test + public void testZerothRootThrows() { + final Complex c = Complex.ofCartesian(1, 1); + Assertions.assertThrows(IllegalArgumentException.class, () -> c.nthRoot(0), + "zeroth root should not be allowed"); + } + /** * Test: computing <b>third roots</b> of z. * <pre> @@ -640,9 +756,9 @@ public class ComplexTest { @Test public void testNthRootNormalThirdRoot() { // The complex number we want to compute all third-roots for. - Complex z = Complex.ofCartesian(-2,2); + final Complex z = Complex.ofCartesian(-2, 2); // The List holding all third roots - Complex[] thirdRootsOfZ = z.nthRoot(3).toArray(new Complex[0]); + final Complex[] thirdRootsOfZ = z.nthRoot(3).toArray(new Complex[0]); // Returned Collection must not be empty! Assertions.assertEquals(3, thirdRootsOfZ.length); // test z_0 @@ -656,7 +772,6 @@ public class ComplexTest { Assertions.assertEquals(-1.3660254037844384, thirdRootsOfZ[2].getImaginary(), 1.0e-5); } - /** * Test: computing <b>fourth roots</b> of z. * <pre> @@ -672,9 +787,9 @@ public class ComplexTest { @Test public void testNthRootNormalFourthRoot() { // The complex number we want to compute all third-roots for. - Complex z = Complex.ofCartesian(5,-2); + final Complex z = Complex.ofCartesian(5, -2); // The List holding all fourth roots - Complex[] fourthRootsOfZ = z.nthRoot(4).toArray(new Complex[0]); + final Complex[] fourthRootsOfZ = z.nthRoot(4).toArray(new Complex[0]); // Returned Collection must not be empty! Assertions.assertEquals(4, fourthRootsOfZ.length); // test z_0 @@ -706,9 +821,9 @@ public class ComplexTest { public void testNthRootCornercaseThirdRootImaginaryPartEmpty() { // The number 8 has three third roots. One we all already know is the number 2. // But there are two more complex roots. - Complex z = Complex.ofCartesian(8,0); + final Complex z = Complex.ofCartesian(8, 0); // The List holding all third roots - Complex[] thirdRootsOfZ = z.nthRoot(3).toArray(new Complex[0]); + final Complex[] thirdRootsOfZ = z.nthRoot(3).toArray(new Complex[0]); // Returned Collection must not be empty! Assertions.assertEquals(3, thirdRootsOfZ.length); // test z_0 @@ -737,9 +852,9 @@ public class ComplexTest { @Test public void testNthRootCornercaseThirdRootRealPartZero() { // complex number with only imaginary part - Complex z = Complex.ofCartesian(0,2); + final Complex z = Complex.ofCartesian(0, 2); // The List holding all third roots - Complex[] thirdRootsOfZ = z.nthRoot(3).toArray(new Complex[0]); + final Complex[] thirdRootsOfZ = z.nthRoot(3).toArray(new Complex[0]); // Returned Collection must not be empty! Assertions.assertEquals(3, thirdRootsOfZ.length); // test z_0 @@ -771,13 +886,13 @@ public class ComplexTest { @Test public void testNthRootNegativeArg() { // The complex number we want to compute all third-roots for. - Complex z = Complex.ofCartesian(1, 0); + final Complex z = Complex.ofCartesian(1, 0); // The List holding all fourth roots Complex[] fourthRootsOfZ = z.nthRoot(4).toArray(new Complex[0]); // test z_0 Assertions.assertEquals(1, fourthRootsOfZ[0].getReal(), 1.0e-5); Assertions.assertEquals(0, fourthRootsOfZ[0].getImaginary(), 1.0e-5); -// test z_1 + // test z_1 Assertions.assertEquals(0, fourthRootsOfZ[1].getReal(), 1.0e-5); Assertions.assertEquals(1, fourthRootsOfZ[1].getImaginary(), 1.0e-5); // test z_2 @@ -791,7 +906,7 @@ public class ComplexTest { // test z_0 Assertions.assertEquals(1, fourthRootsOfZ[0].getReal(), 1.0e-5); Assertions.assertEquals(0, fourthRootsOfZ[0].getImaginary(), 1.0e-5); -// test z_1 + // test z_1 Assertions.assertEquals(0, fourthRootsOfZ[1].getReal(), 1.0e-5); Assertions.assertEquals(-1, fourthRootsOfZ[1].getImaginary(), 1.0e-5); // test z_2 @@ -808,9 +923,9 @@ public class ComplexTest { final Complex z = Complex.ofReal(Double.NaN); final List<Complex> r = z.nthRoot(n); Assertions.assertEquals(n, r.size()); - for (Complex c : r) { - Assertions.assertTrue(Double.isNaN(c.real())); - Assertions.assertTrue(Double.isNaN(c.imag())); + for (final Complex c : r) { + Assertions.assertTrue(Double.isNaN(c.getReal())); + Assertions.assertTrue(Double.isNaN(c.getImaginary())); } } @Test @@ -827,29 +942,28 @@ public class ComplexTest { @Test public void testGetArgument() { Complex z = Complex.ofCartesian(1, 0); - Assertions.assertEquals(0.0, z.getArgument(), 1.0e-12); + assertGetArgument(0.0, z, 1.0e-12); z = Complex.ofCartesian(1, 1); - Assertions.assertEquals(Math.PI/4, z.getArgument(), 1.0e-12); + assertGetArgument(Math.PI / 4, z, 1.0e-12); z = Complex.ofCartesian(0, 1); - Assertions.assertEquals(Math.PI/2, z.getArgument(), 1.0e-12); + assertGetArgument(Math.PI / 2, z, 1.0e-12); z = Complex.ofCartesian(-1, 1); - Assertions.assertEquals(3 * Math.PI/4, z.getArgument(), 1.0e-12); + assertGetArgument(3 * Math.PI / 4, z, 1.0e-12); z = Complex.ofCartesian(-1, 0); - Assertions.assertEquals(Math.PI, z.getArgument(), 1.0e-12); + assertGetArgument(Math.PI, z, 1.0e-12); z = Complex.ofCartesian(-1, -1); - Assertions.assertEquals(-3 * Math.PI/4, z.getArgument(), 1.0e-12); + assertGetArgument(-3 * Math.PI / 4, z, 1.0e-12); z = Complex.ofCartesian(0, -1); - Assertions.assertEquals(-Math.PI/2, z.getArgument(), 1.0e-12); + assertGetArgument(-Math.PI / 2, z, 1.0e-12); z = Complex.ofCartesian(1, -1); - Assertions.assertEquals(-Math.PI/4, z.getArgument(), 1.0e-12); - + assertGetArgument(-Math.PI / 4, z, 1.0e-12); } /** @@ -857,14 +971,14 @@ public class ComplexTest { */ @Test public void testGetArgumentInf() { - Assertions.assertEquals(Math.PI/4, infInf.getArgument(), 1.0e-12); - Assertions.assertEquals(Math.PI/2, oneInf.getArgument(), 1.0e-12); - Assertions.assertEquals(0.0, infOne.getArgument(), 1.0e-12); - Assertions.assertEquals(Math.PI/2, zeroInf.getArgument(), 1.0e-12); - Assertions.assertEquals(0.0, infZero.getArgument(), 1.0e-12); - Assertions.assertEquals(Math.PI, negInfOne.getArgument(), 1.0e-12); - Assertions.assertEquals(-3.0*Math.PI/4, negInfNegInf.getArgument(), 1.0e-12); - Assertions.assertEquals(-Math.PI/2, oneNegInf.getArgument(), 1.0e-12); + assertGetArgument(Math.PI / 4, infInf, 1.0e-12); + assertGetArgument(Math.PI / 2, oneInf, 1.0e-12); + assertGetArgument(0.0, infOne, 1.0e-12); + assertGetArgument(Math.PI / 2, zeroInf, 1.0e-12); + assertGetArgument(0.0, infZero, 1.0e-12); + assertGetArgument(Math.PI, negInfOne, 1.0e-12); + assertGetArgument(-3.0 * Math.PI / 4, negInfNegInf, 1.0e-12); + assertGetArgument(-Math.PI / 2, oneNegInf, 1.0e-12); } /** @@ -872,9 +986,15 @@ public class ComplexTest { */ @Test public void testGetArgumentNaN() { - Assertions.assertTrue(Double.isNaN(nanZero.getArgument())); - Assertions.assertTrue(Double.isNaN(zeroNan.getArgument())); - Assertions.assertTrue(Double.isNaN(NAN.getArgument())); + assertGetArgument(Double.NaN, nanZero, 0); + assertGetArgument(Double.NaN, zeroNan, 0); + assertGetArgument(Double.NaN, NAN, 0); + } + + private static void assertGetArgument(double expected, Complex complex, double delta) { + final double actual = complex.getArgument(); + Assertions.assertEquals(expected, actual, delta); + Assertions.assertEquals(actual, complex.arg(), delta); } @Test @@ -896,35 +1016,37 @@ public class ComplexTest { final String re = "1.234"; final String im = "5.678"; Assertions.assertThrows(IllegalArgumentException.class, - () -> Complex.parse( re + "," + im + ")") + () -> Complex.parse(re + "," + im + ")") ); - } + @Test public void testParseWrongEnd() { final String re = "1.234"; final String im = "5.678"; Assertions.assertThrows(IllegalArgumentException.class, - () -> Complex.parse("(" + re + "," + im) + () -> Complex.parse("(" + re + "," + im) ); - } + @Test public void testParseMissingSeparator() { final String re = "1.234"; final String im = "5.678"; Assertions.assertThrows(IllegalArgumentException.class, - () -> Complex.parse("(" + re + " " + im + ")") + () -> Complex.parse("(" + re + " " + im + ")") ); } + @Test public void testParseInvalidRe() { final String re = "I.234"; final String im = "5.678"; Assertions.assertThrows(IllegalArgumentException.class, - () -> Complex.parse("(" + re + "," + im + ")") + () -> Complex.parse("(" + re + "," + im + ")") ); } + @Test public void testParseInvalidIm() { final String re = "1.234"; @@ -941,4 +1063,37 @@ public class ComplexTest { final String str = "( " + re + " , " + im + " )"; Assertions.assertEquals(Complex.ofCartesian(re, im), Complex.parse(str)); } + + @Test + public void testCGrammar() { + final UniformRandomProvider rng = RandomSource.create(RandomSource.SPLIT_MIX_64); + for (int i = 0; i < 10; i++) { + final Complex z = Complex.ofCartesian(rng.nextDouble(), rng.nextDouble()); + Assertions.assertEquals(z.getReal(), z.real(), "real"); + Assertions.assertEquals(z.getImaginary(), z.imag(), "imag"); + Assertions.assertEquals(z.conjugate(), z.conj(), "conj"); + Assertions.assertEquals(z.getArgument(), z.arg(), "arg"); + } + } + + @Test + public void testLog10() { + final double ln10 = Math.log(10.0); + final UniformRandomProvider rng = RandomSource.create(RandomSource.SPLIT_MIX_64); + for (int i = 0; i < 10; i++) { + final Complex z = Complex.ofCartesian(rng.nextDouble(), rng.nextDouble()); + final Complex lnz = z.log(); + final Complex log10z = z.log10(); + Assertions.assertEquals(lnz.getReal() / ln10, log10z.getReal(), "real"); + Assertions.assertEquals(lnz.getImaginary(), log10z.getImaginary(), "imag"); + } + } + + @Test + @Disabled("Required if not implemented in terms of tanh") + public void testTan() { + // Check the conditions on the imaginary component that create special results. + TestUtils.assertEquals(Complex.ONE, Complex.ofCartesian(0, 25).tan(), 0); + TestUtils.assertEquals(Complex.ofCartesian(0, -1), Complex.ofCartesian(0, -25).tan(), 0); + } } diff --git a/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/TestUtils.java b/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/TestUtils.java index 3fae6c5..17d7ac8 100644 --- a/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/TestUtils.java +++ b/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/TestUtils.java @@ -23,7 +23,6 @@ import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; -import org.apache.commons.numbers.complex.Complex; import org.apache.commons.numbers.core.Precision; import org.junit.jupiter.api.Assertions; @@ -32,7 +31,7 @@ import org.junit.jupiter.api.Assertions; * Test utilities. * TODO: Cleanup (remove unused and obsolete methods). */ -public class TestUtils { +public final class TestUtils { /** * Collection of static methods used in math unit tests. */ @@ -41,8 +40,12 @@ public class TestUtils { } /** - * Verifies that real and imaginary parts of the two complex arguments - * are exactly the same. Also ensures that NaN / infinite components match. + * Verifies that real and imaginary parts of the two complex arguments are exactly the + * same as defined by {@link Double#compare(double, double)}. Also ensures that NaN / + * infinite components match. + * + * @param expected the expected value + * @param actual the actual value */ public static void assertSame(Complex expected, Complex actual) { Assertions.assertEquals(expected.getReal(), actual.getReal()); @@ -50,8 +53,12 @@ public class TestUtils { } /** - * Verifies that real and imaginary parts of the two complex arguments - * differ by at most delta. Also ensures that NaN / infinite components match. + * Verifies that real and imaginary parts of the two complex arguments differ by at + * most delta. Also ensures that NaN / infinite components match. + * + * @param expected the expected value + * @param actual the actual value + * @param delta the delta */ public static void assertEquals(Complex expected, Complex actual, double delta) { Assertions.assertEquals(expected.getReal(), actual.getReal(), delta); @@ -68,17 +75,17 @@ public class TestUtils { public static Object serializeAndRecover(Object o) { try { // serialize the Object - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - ObjectOutputStream so = new ObjectOutputStream(bos); + final ByteArrayOutputStream bos = new ByteArrayOutputStream(); + final ObjectOutputStream so = new ObjectOutputStream(bos); so.writeObject(o); // deserialize the Object - ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); - ObjectInputStream si = new ObjectInputStream(bis); + final ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); + final ObjectInputStream si = new ObjectInputStream(bis); return si.readObject(); - } catch (IOException ioe) { + } catch (final IOException ioe) { return null; - } catch (ClassNotFoundException cnfe) { + } catch (final ClassNotFoundException cnfe) { return null; } } @@ -90,7 +97,7 @@ public class TestUtils { * @param object the object to serialize and recover */ public static void checkSerializedEquality(Object object) { - Object object2 = serializeAndRecover(object); + final Object object2 = serializeAndRecover(object); Assertions.assertEquals(object, object2, "Equals check"); Assertions.assertEquals(object.hashCode(), object2.hashCode(), "HashCode check"); } @@ -130,7 +137,7 @@ public class TestUtils { } else if (expected == 0.0) { Assertions.assertEquals(actual, expected, relativeError, msg); } else { - double absError = Math.abs(expected) * relativeError; + final double absError = Math.abs(expected) * relativeError; Assertions.assertEquals(expected, actual, absError, msg); } } @@ -145,7 +152,7 @@ public class TestUtils { */ public static void assertContains(String msg, Complex[] values, Complex z, double epsilon) { - for (Complex value : values) { + for (final Complex value : values) { if (Precision.equals(value.getReal(), z.getReal(), epsilon) && Precision.equals(value.getImaginary(), z.getImaginary(), epsilon)) { return; @@ -176,7 +183,7 @@ public class TestUtils { */ public static void assertContains(String msg, double[] values, double x, double epsilon) { - for (double value : values) { + for (final double value : values) { if (Precision.equals(value, x, epsilon)) { return; } @@ -193,12 +200,12 @@ public class TestUtils { */ public static void assertContains(double[] values, double x, double epsilon) { - assertContains(null, values, x, epsilon); + assertContains(null, values, x, epsilon); } /** verifies that two arrays are close (sup norm) */ public static void assertEquals(String msg, Complex[] expected, Complex[] observed, double tolerance) { - StringBuilder out = new StringBuilder(msg); + final StringBuilder out = new StringBuilder(msg); if (expected.length != observed.length) { out.append("\n Arrays not same length. \n"); out.append("expected has length "); @@ -208,7 +215,7 @@ public class TestUtils { Assertions.fail(out.toString()); } boolean failure = false; - for (int i=0; i < expected.length; i++) { + for (int i = 0; i < expected.length; i++) { if (!Precision.equalsIncludingNaN(expected[i].getReal(), observed[i].getReal(), tolerance)) { failure = true; out.append("\n Real elements at index "); @@ -265,8 +272,8 @@ public class TestUtils { } } if (positiveMassCount < densityValues.length) { - int[] newPoints = new int[positiveMassCount]; - double[] newValues = new double[positiveMassCount]; + final int[] newPoints = new int[positiveMassCount]; + final double[] newValues = new double[positiveMassCount]; int j = 0; for (int i = 0; i < densityValues.length; i++) { if (densityValues[i] > 0) { @@ -275,8 +282,8 @@ public class TestUtils { j++; } } - System.arraycopy(newPoints,0,densityPoints,0,positiveMassCount); - System.arraycopy(newValues,0,densityValues,0,positiveMassCount); + System.arraycopy(newPoints, 0, densityPoints, 0, positiveMassCount); + System.arraycopy(newValues, 0, densityValues, 0, positiveMassCount); } return positiveMassCount; }
