You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by ah...@apache.org on 2019/11/08 18:51:46 UTC
[commons-numbers] 19/32: [NUMBERS-78] Increase test coverage.
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 <ah...@apache.org>
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;
}