Author: psteitz Date: Mon Jan 5 19:46:29 2009 New Revision: 731822 URL: http://svn.apache.org/viewvc?rev=731822&view=rev Log: 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
Modified: commons/proper/math/trunk/src/java/org/apache/commons/math/complex/Complex.java commons/proper/math/trunk/src/test/org/apache/commons/math/complex/ComplexTest.java Modified: commons/proper/math/trunk/src/java/org/apache/commons/math/complex/Complex.java URL: http://svn.apache.org/viewvc/commons/proper/math/trunk/src/java/org/apache/commons/math/complex/Complex.java?rev=731822&r1=731821&r2=731822&view=diff ============================================================================== --- commons/proper/math/trunk/src/java/org/apache/commons/math/complex/Complex.java (original) +++ commons/proper/math/trunk/src/java/org/apache/commons/math/complex/Complex.java Mon Jan 5 19:46:29 2009 @@ -19,7 +19,7 @@ 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 @@ /** - * Compute the angle phi of this complex number. - * @return the angle phi of this complex number + * <p>Compute the argument of this complex number. + * </p> + * <p>The argument is the angle phi between the positive real axis and the point + * representing this number in the complex plane. The value returned is between -PI (not inclusive) + * and PI (inclusive), with negative values returned for numbers with negative imaginary parts. + * </p> + * <p>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.</p> + * + * @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. - * <p> - * For a given n it implements the formula: <pre> - * <code> z_k = pow( abs , 1.0/n ) * (cos(phi + k * 2π) + i * (sin(phi + k * 2π)</code></pre></p> - * with <i><code>k=0, 1, ..., n-1</code></i> and <i><code>pow(abs, 1.0 / n)</code></i> is the nth root of the absolute-value. - * <p> + * <p>Computes the n-th roots of this complex number. + * </p> + * <p>The nth roots are defined by the formula: <pre> + * <code> z<sub>k</sub> = abs<sup> 1/n</sup> (cos(phi + 2πk/n) + i (sin(phi + 2πk/n))</code></pre> + * for <i><code>k=0, 1, ..., n-1</code></i>, where <code>abs</code> and <code>phi</code> are + * respectively the {...@link #abs() modulus} and {...@link #getArgument() argument} of this complex number. + * </p> + * <p>If one or both parts of this complex number is NaN, a list with just one element, + * {...@link #NaN} is returned.</p> + * <p>if neither part is NaN, but at least one part is infinite, the result is a one-element + * list containing {...@link #INF}.</p> * * @param n degree of root - * @return Collection<Complex> all nth roots of this complex number as a Collection - * @throws IllegalArgumentException if parameter n is negative + * @return List<Complex> all nth roots of this complex number + * @throws IllegalArgumentException if parameter n is less than or equal to 0 * @since 2.0 */ - public Collection<Complex> nthRoot(int n) throws IllegalArgumentException { + public List<Complex> 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<Complex> result = new ArrayList<Complex>(); + + if (isNaN()) { + result.add(Complex.NaN); + return result; + } + + if (isInfinite()) { + result.add(Complex.INF); + return result; + } - Collection<Complex> result = new ArrayList<Complex>(); - - // 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; - } /** Modified: commons/proper/math/trunk/src/test/org/apache/commons/math/complex/ComplexTest.java URL: http://svn.apache.org/viewvc/commons/proper/math/trunk/src/test/org/apache/commons/math/complex/ComplexTest.java?rev=731822&r1=731821&r2=731822&view=diff ============================================================================== --- commons/proper/math/trunk/src/test/org/apache/commons/math/complex/ComplexTest.java (original) +++ commons/proper/math/trunk/src/test/org/apache/commons/math/complex/ComplexTest.java Mon Jan 5 19:46:29 2009 @@ -19,6 +19,8 @@ import org.apache.commons.math.TestUtils; +import java.util.List; + import junit.framework.TestCase; /** @@ -801,7 +803,7 @@ * </code> * </pre> */ - 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 @@ * 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<Complex> 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()); } }