From 4564adbf19ebc6da19befcc7dbef310a00b4de13 Mon Sep 17 00:00:00 2001
From: Phil Steitz
Date: Tue, 6 Jan 2009 03:46:29 +0000
Subject: [PATCH] Changed return type of nthRoot to List Renamed getPhi to
getArgument Changed and documented behavior of nthRoot wrt NaN, infinite
components Improved nth root computation Added some test cases
git-svn-id: https://svn.apache.org/repos/asf/commons/proper/math/trunk@731822 13f79535-47bb-0310-9956-ffa450edef68
---
.../apache/commons/math/complex/Complex.java | 64 +++++++----
.../commons/math/complex/ComplexTest.java | 100 ++++++++++++++----
2 files changed, 124 insertions(+), 40 deletions(-)
diff --git a/src/java/org/apache/commons/math/complex/Complex.java b/src/java/org/apache/commons/math/complex/Complex.java
index df2ee1f3f..fa4dd8a8b 100644
--- a/src/java/org/apache/commons/math/complex/Complex.java
+++ b/src/java/org/apache/commons/math/complex/Complex.java
@@ -19,7 +19,7 @@ package org.apache.commons.math.complex;
import java.io.Serializable;
import java.util.ArrayList;
-import java.util.Collection;
+import java.util.List;
import org.apache.commons.math.MathRuntimeException;
import org.apache.commons.math.util.MathUtils;
@@ -867,50 +867,76 @@ public class Complex implements Serializable {
/**
- * Compute the angle phi of this complex number.
- * @return the angle phi of this complex number
+ * Compute the argument of this complex number.
+ *
+ * The argument is the angle phi between the positive real axis and the point
+ * representing this number in the complex plane. The value returned is between -PI (not inclusive)
+ * and PI (inclusive), with negative values returned for numbers with negative imaginary parts.
+ *
+ * If either real or imaginary part (or both) is NaN, NaN is returned. Infinite parts are handled
+ * as java.Math.atan2 handles them, essentially treating finite parts as zero in the presence of
+ * an infinite coordinate and returning a multiple of pi/4 depending on the signs of the infinite
+ * parts. See the javadoc for java.Math.atan2 for full details.
+ *
+ * @return the argument of this complex number
*/
- public double getPhi() {
+ public double getArgument() {
return Math.atan2(getImaginary(), getReal());
}
/**
- * Compute the n-th root of this complex number.
- *
- * For a given n it implements the formula:
- * z_k = pow( abs , 1.0/n ) * (cos(phi + k * 2π) + i * (sin(phi + k * 2π)
- * with k=0, 1, ..., n-1
and pow(abs, 1.0 / n)
is the nth root of the absolute-value.
- *
+ *
Computes the n-th roots of this complex number.
+ *
+ * The nth roots are defined by the formula:
+ * zk = abs 1/n (cos(phi + 2πk/n) + i (sin(phi + 2πk/n))
+ * for k=0, 1, ..., n-1
, where abs
and phi
are
+ * respectively the {@link #abs() modulus} and {@link #getArgument() argument} of this complex number.
+ *
+ * If one or both parts of this complex number is NaN, a list with just one element,
+ * {@link #NaN} is returned.
+ * if neither part is NaN, but at least one part is infinite, the result is a one-element
+ * list containing {@link #INF}.
*
* @param n degree of root
- * @return Collection all nth roots of this complex number as a Collection
- * @throws IllegalArgumentException if parameter n is negative
+ * @return List all nth roots of this complex number
+ * @throws IllegalArgumentException if parameter n is less than or equal to 0
* @since 2.0
*/
- public Collection nthRoot(int n) throws IllegalArgumentException {
+ public List nthRoot(int n) throws IllegalArgumentException {
if (n <= 0) {
throw MathRuntimeException.createIllegalArgumentException("cannot compute nth root for null or negative n: {0}",
new Object[] { n });
}
+
+ List result = new ArrayList();
+
+ if (isNaN()) {
+ result.add(Complex.NaN);
+ return result;
+ }
+
+ if (isInfinite()) {
+ result.add(Complex.INF);
+ return result;
+ }
- Collection result = new ArrayList();
-
- // nth root of abs
+ // nth root of abs -- faster / more accurate to use a solver here?
final double nthRootOfAbs = Math.pow(abs(), 1.0 / n);
// Compute nth roots of complex number with k = 0, 1, ... n-1
- final double phi = getPhi();
+ final double nthPhi = getArgument()/n;
+ final double slice = 2 * Math.PI / n;
+ double innerPart = nthPhi;
for (int k = 0; k < n ; k++) {
// inner part
- final double innerPart = (phi + k * 2 * Math.PI) / n;
final double realPart = nthRootOfAbs * Math.cos(innerPart);
final double imaginaryPart = nthRootOfAbs * Math.sin(innerPart);
result.add(createComplex(realPart, imaginaryPart));
+ innerPart += slice;
}
return result;
-
}
/**
diff --git a/src/test/org/apache/commons/math/complex/ComplexTest.java b/src/test/org/apache/commons/math/complex/ComplexTest.java
index 48d130c89..0aa8138e2 100644
--- a/src/test/org/apache/commons/math/complex/ComplexTest.java
+++ b/src/test/org/apache/commons/math/complex/ComplexTest.java
@@ -19,6 +19,8 @@ package org.apache.commons.math.complex;
import org.apache.commons.math.TestUtils;
+import java.util.List;
+
import junit.framework.TestCase;
/**
@@ -801,7 +803,7 @@ public class ComplexTest extends TestCase {
*
*
*/
- public void testNthRoot_cornercase_thirdRoot_realPartEmpty() {
+ public void testNthRoot_cornercase_thirdRoot_realPartZero() {
// complex number with only imaginary part
Complex z = new Complex(0,2);
// The List holding all third roots
@@ -823,26 +825,82 @@ public class ComplexTest extends TestCase {
* Test cornercases with NaN and Infinity.
*/
public void testNthRoot_cornercase_NAN_Inf() {
- // third root of z = 1 + NaN * i
- for (Complex c : oneNaN.nthRoot(3)) {
- // both parts should be nan
- assertEquals(nan, c.getReal());
- assertEquals(nan, c.getImaginary());
- }
- // third root of z = inf + NaN * i
- for (Complex c : infNaN.nthRoot(3)) {
- // both parts should be nan
- assertEquals(nan, c.getReal());
- assertEquals(nan, c.getImaginary());
- }
- // third root of z = neginf + 1 * i
- Complex[] zInfOne = negInfOne.nthRoot(2).toArray(new Complex[0]);
- // first root
- assertEquals(inf, zInfOne[0].getReal());
- assertEquals(inf, zInfOne[0].getImaginary());
- // second root
- assertEquals(neginf, zInfOne[1].getReal());
- assertEquals(neginf, zInfOne[1].getImaginary());
+ // NaN + finite -> NaN
+ List roots = oneNaN.nthRoot(3);
+ assertEquals(1,roots.size());
+ assertEquals(Complex.NaN, roots.get(0));
+
+ roots = nanZero.nthRoot(3);
+ assertEquals(1,roots.size());
+ assertEquals(Complex.NaN, roots.get(0));
+
+ // NaN + infinite -> NaN
+ roots = nanInf.nthRoot(3);
+ assertEquals(1,roots.size());
+ assertEquals(Complex.NaN, roots.get(0));
+
+ // finite + infinite -> Inf
+ roots = oneInf.nthRoot(3);
+ assertEquals(1,roots.size());
+ assertEquals(Complex.INF, roots.get(0));
+
+ // infinite + infinite -> Inf
+ roots = negInfInf.nthRoot(3);
+ assertEquals(1,roots.size());
+ assertEquals(Complex.INF, roots.get(0));
+ }
+
+ /**
+ * Test standard values
+ */
+ public void testGetArgument() {
+ Complex z = new Complex(1, 0);
+ assertEquals(0.0, z.getArgument(), 1.0e-12);
+
+ z = new Complex(1, 1);
+ assertEquals(Math.PI/4, z.getArgument(), 1.0e-12);
+
+ z = new Complex(0, 1);
+ assertEquals(Math.PI/2, z.getArgument(), 1.0e-12);
+
+ z = new Complex(-1, 1);
+ assertEquals(3 * Math.PI/4, z.getArgument(), 1.0e-12);
+
+ z = new Complex(-1, 0);
+ assertEquals(Math.PI, z.getArgument(), 1.0e-12);
+
+ z = new Complex(-1, -1);
+ assertEquals(-3 * Math.PI/4, z.getArgument(), 1.0e-12);
+
+ z = new Complex(0, -1);
+ assertEquals(-Math.PI/2, z.getArgument(), 1.0e-12);
+
+ z = new Complex(1, -1);
+ assertEquals(-Math.PI/4, z.getArgument(), 1.0e-12);
+
+ }
+
+ /**
+ * Verify atan2-style handling of infinite parts
+ */
+ public void testGetArgumentInf() {
+ assertEquals(Math.PI/4, infInf.getArgument(), 1.0e-12);
+ assertEquals(Math.PI/2, oneInf.getArgument(), 1.0e-12);
+ assertEquals(0.0, infOne.getArgument(), 1.0e-12);
+ assertEquals(Math.PI/2, zeroInf.getArgument(), 1.0e-12);
+ assertEquals(0.0, infZero.getArgument(), 1.0e-12);
+ assertEquals(Math.PI, negInfOne.getArgument(), 1.0e-12);
+ assertEquals(-3.0*Math.PI/4, negInfNegInf.getArgument(), 1.0e-12);
+ assertEquals(-Math.PI/2, oneNegInf.getArgument(), 1.0e-12);
+ }
+
+ /**
+ * Verify that either part NaN results in NaN
+ */
+ public void testGetArgumentNaN() {
+ assertEquals(nan, nanZero.getArgument());
+ assertEquals(nan, zeroNaN.getArgument());
+ assertEquals(nan, Complex.NaN.getArgument());
}
}