/* * ------------------------------------------------------------------------- * $Id: Complex.java,v 1.3 2006/02/13 17:40:03 pfisterer Exp $ * ------------------------------------------------------------------------- * Copyright (c) 1997 - 1998 by Visual Numerics, Inc. All rights reserved. * * Permission to use, copy, modify, and distribute this software is freely * granted by Visual Numerics, Inc., provided that the copyright notice * above and the following warranty disclaimer are preserved in human * readable form. * * Because this software is licenses free of charge, it is provided * "AS IS", with NO WARRANTY. TO THE EXTENT PERMITTED BY LAW, VNI * DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED * TO ITS PERFORMANCE, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * VNI WILL NOT BE LIABLE FOR ANY DAMAGES WHATSOEVER ARISING OUT OF THE USE * OF OR INABILITY TO USE THIS SOFTWARE, INCLUDING BUT NOT LIMITED TO DIRECT, * INDIRECT, SPECIAL, CONSEQUENTIAL, PUNITIVE, AND EXEMPLARY DAMAGES, EVEN * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. * * ------------------------------------------------------------------------- */ package org.tritonus.lowlevel.dsp; /** * This class implements complex numbers. It provides the basic operations * (addition, subtraction, multiplication, division) as well as a set of * complex functions. * * The binary operations have the form, where op is <code>plus</code>, * <code>minus</code>, <code>times</code> or <code>over</code>. * <pre> * public static Complex op(Complex x, Complex y) // x op y * public static Complex op(Complex x, double y) // x op y * public static Complex op(double x, Complex y) // x op y * public Complex op(Complex y) // this op y * public Complex op(double y) // this op y * public Complex opReverse(double x) // x op this * </pre> * * The functions in this class follow the rules for complex arithmetic * as defined C9x Annex G:"IEC 559-compatible complex arithmetic." * The API is not the same, but handling of infinities, NaNs, and positive * and negative zeros is intended to follow the same rules. * * This class depends on the standard java.lang.Math class following * certain rules, as defined in the C9x Annex F, for the handling of * infinities, NaNs, and positive and negative zeros. Sun's specification * is that java.lang.Math should reproduce the results in the Sun's fdlibm * C library. This library appears to follow the Annex F specification. * At least on Windows, Sun's JDK 1.0 and 1.1 do NOT follow this specification. * Sun's JDK 1.2(RC2) does follow the Annex F specification. Thesefore, * this class will not give the expected results for edge cases with * JDK 1.0 and 1.1. */ public class Complex implements java.io.Serializable, Cloneable { /** * @serial Real part of the Complex. */ private double m_re; /** * @serial Imaginary part of the Complex. */ private double m_im; /** * Serialization ID */ static final long serialVersionUID = -633126172485117692L; /** * String used in converting Complex to String. * Default is "i", but sometimes "j" is desired. * Note that this is set for the class, not for * a particular instance of a Complex. */ public static String suffix = "i"; private final static long negZeroBits = Double.doubleToLongBits(1.0/Double.NEGATIVE_INFINITY); /** * Constructs a Complex equal to the argument. * @param z A Complex object * If z is null then a NullPointerException is thrown. */ public Complex(Complex z) { m_re = z.m_re; m_im = z.m_im; } /** * Constructs a Complex with real and imaginary parts given * by the input arguments. * @param re A double value equal to the real part of the Complex object. * @param im A double value equal to the imaginary part of the Complex object. */ public Complex(double re, double im) { this.m_re = re; this.m_im = im; } /** * Constructs a Complex with a zero imaginary part. * @param re A double value equal to the real part of the Complex object. */ public Complex(double re) { this.m_re = re; this.m_im = 0.0; } /** * Constructs a Complex equal to zero. */ public Complex() { m_re = 0.0; m_im = 0.0; } /** * Tests if this is a complex Not-a-Number (NaN) value. * @return True if either component of the Complex object is NaN; * false, otherwise. */ private boolean isNaN() { return (Double.isNaN(m_re) || Double.isNaN(m_im)); } /** * Compares with another Complex. * <p><em>Note: To be useful in hashtables this method * considers two NaN double values to be equal. This * is not according to IEEE specification.</em> * @param z A Complex object. * @return True if the real and imaginary parts of this object * are equal to their counterparts in the argument; false, otherwise. */ public boolean equals(Complex z) { if (isNaN() && z.isNaN()) { return true; } else { return (m_re == z.m_re && m_im == z.m_im); } } /** * Compares this object against the specified object. * <p><em>Note: To be useful in hashtables this method * considers two NaN double values to be equal. This * is not according to IEEE specification</em> * @param obj The object to compare with. * @return True if the objects are the same; false otherwise. */ public boolean equals(Object obj) { if (obj == null) { return false; } else if (obj instanceof Complex) { return equals((Complex)obj); } else { return false; } } /** * Returns a hashcode for this Complex. * @return A hash code value for this object. */ public int hashCode() { long re_bits = Double.doubleToLongBits(m_re); long im_bits = Double.doubleToLongBits(m_im); return (int)((re_bits^im_bits)^((re_bits^im_bits)>>32)); } /** * Returns the real part of a Complex object. * @return The real part of z. */ public double real() { return m_re; } /** * Returns the imaginary part of a Complex object. * @param z A Complex object. * @return The imaginary part of z. */ public double imag() { return m_im; } /** * Returns the real part of a Complex object. * @param z A Complex object. * @return The real part of z. */ public static double real(Complex z) { return z.m_re; } /** * Returns the imaginary part of a Complex object. * @param z A Complex object. * @return The imaginary part of z. */ public static double imag(Complex z) { return z.m_im; } /** * Returns the negative of a Complex object, -z. * @param z A Complex object. * @return A newly constructed Complex initialized to * the negative of the argument. */ public static Complex negative(Complex z) { return new Complex(-z.m_re, -z.m_im); } /** * Returns the complex conjugate of a Complex object. * @param z A Complex object. * @return A newly constructed Complex initialized to complex conjugate of z. */ public static Complex conjugate(Complex z) { return new Complex(z.m_re, -z.m_im); } /** * Returns the sum of two Complex objects, x+y. * @param x A Complex object. * @param y A Complex object. * @return A newly constructed Complex initialized to x+y. */ public static Complex plus(Complex x, Complex y) { return new Complex(x.m_re+y.m_re, x.m_im+y.m_im); } /** * Returns the sum of a Complex and a double, x+y. * @param x A Complex object. * @param y A double value. * @return A newly constructed Complex initialized to x+y. */ public static Complex plus(Complex x, double y) { return new Complex(x.m_re+y, x.m_im); } /** * Returns the sum of a double and a Complex, x+y. * @param x A double value. * @param y A Complex object. * @return A newly constructed Complex initialized to x+y. */ public static Complex plus(double x, Complex y) { return new Complex(x+y.m_re, y.m_im); } /** * Returns the sum of this Complex and another Complex, this+y. * @param y A Complex object. * @return A newly constructed Complex initialized to this+y. */ public Complex plus(Complex y) { return new Complex(m_re+y.m_re, m_im+y.m_im); } /** * Returns the sum of this Complex a double, this+y. * @param y A double value. * @return A newly constructed Complex initialized to this+y. */ public Complex plus(double y) { return new Complex(m_re+y, m_im); } /** * Returns the sum of this Complex and a double, x+this. * @param x A double value. * @return A newly constructed Complex initialized to x+this. */ public Complex plusReverse(double x) { return new Complex(m_re+x, m_im); } /** * Returns the difference of two Complex objects, x-y. * @param x A Complex object. * @param y A Complex object. * @return A newly constructed Complex initialized to x-y. */ public static Complex minus(Complex x, Complex y) { return new Complex(x.m_re-y.m_re, x.m_im-y.m_im); } /** * Returns the difference of a Complex object and a double, x-y. * @param x A Complex object. * @param y A double value. * @return A newly constructed Complex initialized to x-y. */ public static Complex minus(Complex x, double y) { return new Complex(x.m_re-y, x.m_im); } /** * Returns the difference of a double and a Complex object, x-y. * @param x A double value. * @param y A Complex object. * @return A newly constructed Complex initialized to x-y.. */ public static Complex minus(double x, Complex y) { return new Complex(x-y.m_re, -y.m_im); } /** * Returns the difference of this Complex object and * another Complex object, this-y. * @param y A Complex object. * @return A newly constructed Complex initialized to this-y. */ public Complex minus(Complex y) { return new Complex(m_re-y.m_re, m_im-y.m_im); } /** * Subtracts a double from this Complex and returns the difference, this-y. * @param y A double value. * @return A newly constructed Complex initialized to this-y. */ public Complex minus(double y) { return new Complex(m_re-y, m_im); } /** * Returns the difference of this Complex object and a double, this-y. * @param y A double value. * @return A newly constructed Complex initialized to x-this. */ public Complex minusReverse(double x) { return new Complex(x-m_re, -m_im); } /** * Returns the product of two Complex objects, x*y. * @param x A Complex object. * @param y A Complex object. * @return A newly constructed Complex initialized to x*y. */ public static Complex times(Complex x, Complex y) { Complex t = new Complex(x.m_re*y.m_re-x.m_im*y.m_im, x.m_re*y.m_im+x.m_im*y.m_re); if (Double.isNaN(t.m_re) && Double.isNaN(t.m_im)) timesNaN(x, y, t); return t; } /* * Returns sign(b)*|a|. */ private static double copysign(double a, double b) { double abs = Math.abs(a); return ((b < 0) ? -abs : abs); } /** * Recovers infinities when computed x*y = NaN+i*NaN. * This code is not part of times(), so that times * could be inlined by an optimizing compiler. * <p> * This algorithm is adapted from the C9x Annex G: * "IEC 559-compatible complex arithmetic." * @param x First Complex operand. * @param y Second Complex operand. * @param t The product x*y, computed without regard to NaN. * The real and/or the imaginary part of t is * expected to be NaN. * @return The corrected product of x*y. */ private static void timesNaN(Complex x, Complex y, Complex t) { boolean recalc = false; double a = x.m_re; double b = x.m_im; double c = y.m_re; double d = y.m_im; if (Double.isInfinite(a) || Double.isInfinite(b)) { // x is infinite a = copysign(Double.isInfinite(a)?1.0:0.0, a); b = copysign(Double.isInfinite(b)?1.0:0.0, b); if (Double.isNaN(c)) c = copysign(0.0, c); if (Double.isNaN(d)) d = copysign(0.0, d); recalc = true; } if (Double.isInfinite(c) || Double.isInfinite(d)) { // x is infinite a = copysign(Double.isInfinite(c)?1.0:0.0, c); b = copysign(Double.isInfinite(d)?1.0:0.0, d); if (Double.isNaN(a)) a = copysign(0.0, a); if (Double.isNaN(b)) b = copysign(0.0, b); recalc = true; } if (!recalc) { if (Double.isInfinite(a*c) || Double.isInfinite(b*d) || Double.isInfinite(a*d) || Double.isInfinite(b*c)) { // Change all NaNs to 0 if (Double.isNaN(a)) a = copysign(0.0, a); if (Double.isNaN(b)) b = copysign(0.0, b); if (Double.isNaN(c)) c = copysign(0.0, c); if (Double.isNaN(d)) d = copysign(0.0, d); recalc = true; } } if (recalc) { t.m_re = Double.POSITIVE_INFINITY * (a*c - b*d); t.m_im = Double.POSITIVE_INFINITY * (a*d + b*c); } } /** * Returns the product of a Complex object and a double, x*y. * @param x A Complex object. * @param y A double value. * @return A newly constructed Complex initialized to x*y. */ public static Complex times(Complex x, double y) { return new Complex(x.m_re*y, x.m_im*y); } /** * Returns the product of a double and a Complex object, x*y. * @param x A double value. * @param y A Complex object. * @return A newly constructed Complex initialized to x*y. */ public static Complex times(double x, Complex y) { return new Complex(x*y.m_re, x*y.m_im); } /** * Returns the product of this Complex object and another Complex object, this*y. * @param y A Complex object. * @return A newly constructed Complex initialized to this*y. */ public Complex times(Complex y) { return times(this,y); } /** * Returns the product of this Complex object and a double, this*y. * @param y A double value. * @return A newly constructed Complex initialized to this*y. */ public Complex times(double y) { return new Complex(m_re*y, m_im*y); } /** * Returns the product of a double and this Complex, x*this. * @param y A double value. * @return A newly constructed Complex initialized to x*this. */ public Complex timesReverse(double x) { return new Complex(x*m_re, x*m_im); } private static boolean isFinite(double x) { return !(Double.isInfinite(x) || Double.isNaN(x)); } /** * Returns Complex object divided by a Complex object, x/y. * @param x The numerator, a Complex object. * @param y The denominator, a Complex object. * @return A newly constructed Complex initialized to x/y. */ public static Complex over(Complex x, Complex y) { double a = x.m_re; double b = x.m_im; double c = y.m_re; double d = y.m_im; double scale = Math.max(Math.abs(c), Math.abs(d)); boolean isScaleFinite = isFinite(scale); if (isScaleFinite) { c /= scale; d /= scale; } double den = c*c + d*d; Complex z = new Complex((a*c+b*d)/den, (b*c-a*d)/den); if (isScaleFinite) { z.m_re /= scale; z.m_im /= scale; } // Recover infinities and zeros computed as NaN+iNaN. if (Double.isNaN(z.m_re) && Double.isNaN(z.m_im)) { if (den == 0.0 && (!Double.isNaN(a) || !Double.isNaN(b))) { double s = copysign(Double.POSITIVE_INFINITY, c); z.m_re = s * a; z.m_im = s * b; } else if ((Double.isInfinite(a) || Double.isInfinite(b)) && isFinite(c) && isFinite(d)) { a = copysign(Double.isInfinite(a)?1.0:0.0, a); b = copysign(Double.isInfinite(b)?1.0:0.0, b); z.m_re = Double.POSITIVE_INFINITY * (a*c + b*d); z.m_im = Double.POSITIVE_INFINITY * (b*c - a*d); } else if (Double.isInfinite(scale) && isFinite(a) && isFinite(b)) { c = copysign(Double.isInfinite(c)?1.0:0.0, c); d = copysign(Double.isInfinite(d)?1.0:0.0, d); z.m_re = 0.0 * (a*c + b*d); z.m_im = 0.0 * (b*c - a*d); } } return z; } /** * Returns Complex object divided by a double, x/y. * @param x The numerator, a Complex object. * @param y The denominator, a double. * @return A newly constructed Complex initialized to x/y. */ public static Complex over(Complex x, double y) { return new Complex(x.m_re/y, x.m_im/y); } /** * Returns a double divided by a Complex object, x/y. * @param x A double value. * @param y The denominator, a Complex object. * @return A newly constructed Complex initialized to x/y. */ public static Complex over(double x, Complex y) { return y.overReverse(x); } /** * Returns this Complex object divided by another Complex object, this/y. * @param y The denominator, a Complex object. * @return A newly constructed Complex initialized to x/y. */ public Complex over(Complex y) { return over(this, y); } /** * Returns this Complex object divided by double, this/y. * @param y The denominator, a double. * @return A newly constructed Complex initialized to x/y. */ public Complex over(double y) { return over(this, y); } /** * Returns a double dividied by this Complex object, x/this. * @param x The numerator, a double. * @return A newly constructed Complex initialized to x/this. */ public Complex overReverse(double x) { double den, t; Complex z; if (Math.abs(m_re) > Math.abs(m_im)) { t = m_im / m_re; den = m_re + m_im*t; z = new Complex(x/den, -x*t/den); } else { t = m_re / m_im; den = m_im + m_re*t; z = new Complex(x*t/den, -x/den); } return z; } /** * Returns the absolute value (modulus) of a Complex, |z|. * @param z A Complex object. * @return A double value equal to the absolute value of the argument. */ public static double abs(Complex z) { double x = Math.abs(z.m_re); double y = Math.abs(z.m_im); if (Double.isInfinite(x) || Double.isInfinite(y)) return Double.POSITIVE_INFINITY; if (x + y == 0.0) { return 0.0; } else if (x > y) { y /= x; return x*Math.sqrt(1.0+y*y); } else { x /= y; return y*Math.sqrt(x*x+1.0); } } /** * Returns the argument (phase) of a Complex, in radians, * with a branch cut along the negative real axis. * @param z A Complex object. * @return A double value equal to the argument (or phase) of a Complex. * It is in the interval [-pi,pi]. */ public static double argument(Complex z) { return Math.atan2(z.m_im, z.m_re); } /** * Returns the square root of a Complex, * with a branch cut along the negative real axis. * @param z A Complex object. * @return A newly constructed Complex initialized * to square root of z. Its real part is * non-negative. */ public static Complex sqrt(Complex z) { Complex result = new Complex(); if (Double.isInfinite(z.m_im)) { result.m_re = Double.POSITIVE_INFINITY; result.m_im = z.m_im; } else if (Double.isNaN(z.m_re)) { result.m_re = result.m_im = Double.NaN; } else if (Double.isNaN(z.m_im)) { if (Double.isInfinite(z.m_re)) { if (z.m_re > 0) { result.m_re = z.m_re; result.m_im = z.m_im; } else { result.m_re = z.m_im; result.m_im = Double.POSITIVE_INFINITY; } } else { result.m_re = result.m_im = Double.NaN; } } else { // Numerically correct version of formula 3.7.27 // in the NBS Hanbook, as suggested by Pete Stewart. double t = abs(z); if (Math.abs(z.m_re) <= Math.abs(z.m_im)) { // No cancellation in these formulas result.m_re = Math.sqrt(0.5*(t+z.m_re)); result.m_im = Math.sqrt(0.5*(t-z.m_re)); } else { // Stable computation of the above formulas if (z.m_re > 0) { result.m_re = t + z.m_re; result.m_im = Math.abs(z.m_im)*Math.sqrt(0.5/result.m_re); result.m_re = Math.sqrt(0.5*result.m_re); } else { result.m_im = t - z.m_re; result.m_re = Math.abs(z.m_im)*Math.sqrt(0.5/result.m_im); result.m_im = Math.sqrt(0.5*result.m_im); } } if (z.m_im < 0) result.m_im = -result.m_im; } return result; } /** * Returns the exponential of a Complex z, exp(z). * @param z A Complex object. * @return A newly constructed Complex initialized to exponential * of the argument. */ public static Complex exp(Complex z) { Complex result = new Complex(); double r = Math.exp(z.m_re); double cosa = Math.cos(z.m_im); double sina = Math.sin(z.m_im); if (Double.isInfinite(z.m_im) || Double.isNaN(z.m_im) || Math.abs(cosa)>1) { cosa = sina = Double.NaN; } if (Double.isInfinite(z.m_re) || Double.isInfinite(r)) { if (z.m_re < 0) { r = 0; if (Double.isInfinite(z.m_im) || Double.isNaN(z.m_im)) { cosa = sina = 0; } else { cosa /= Double.POSITIVE_INFINITY; sina /= Double.POSITIVE_INFINITY; } } else { r = z.m_re; if (Double.isNaN(z.m_im)) cosa = 1; } } if (z.m_im == 0.0) { result.m_re = r; result.m_im = z.m_im; } else { result.m_re = r*cosa; result.m_im = r*sina; } return result; } /** * Returns the logarithm of a Complex z, * with a branch cut along the negative real axis. * @param z A Complex object. * @return A newly constructed Complex initialized to logarithm * of the argument. Its imaginary part is in the * interval [-i*pi,i*pi]. */ public static Complex log(Complex z) { Complex result = new Complex(); if (Double.isNaN(z.m_re)) { result.m_re = result.m_im = z.m_re; if (Double.isInfinite(z.m_im)) result.m_re = Double.POSITIVE_INFINITY; } else if (Double.isNaN(z.m_im)) { result.m_re = result.m_im = z.m_im; if (Double.isInfinite(z.m_re)) result.m_re = Double.POSITIVE_INFINITY; } else { result.m_re = Math.log(abs(z)); result.m_im = argument(z); } return result; } /** * Returns the sine of a Complex. * @param z A Complex object. * @return A newly constructed Complex initialized to sine of the argument. */ public static Complex sin(Complex z) { // sin(z) = -i*sinh(i*z) Complex iz = new Complex(-z.m_im,z.m_re); Complex s = sinh(iz); double re = s.m_im; s.m_im = -s.m_re; s.m_re = re; return s; } /** * Returns the cosine of a Complex. * @param z A Complex object. * @return A newly constructed Complex initialized to cosine of the argument. */ public static Complex cos(Complex z) { // cos(z) = cosh(i*z) return cosh(new Complex(-z.m_im,z.m_re)); } /** * Returns the tangent of a Complex. * @param z A Complex object. * @return A newly constructed Complex initialized * to tangent of the argument. */ public static Complex tan(Complex z) { // tan = -i*tanh(i*z) Complex iz = new Complex(-z.m_im,z.m_re); Complex s = tanh(iz); double re = s.m_im; s.m_im = -s.m_re; s.m_re = re; return s; } /** * Returns the inverse sine (arc sine) of a Complex, * with branch cuts outside the interval [-1,1] along the * real axis. * @param z A Complex object. * @return A newly constructed Complex initialized to inverse * (arc) sine of the argument. The real part of the * result is in the interval [-pi/2,+pi/2]. */ public static Complex asin(Complex z) { Complex result = new Complex(); double r = abs(z); if (Double.isInfinite(r)) { boolean infiniteX = Double.isInfinite(z.m_re); boolean infiniteY = Double.isInfinite(z.m_im); if (infiniteX) { double pi2 = 0.5*Math.PI; result.m_re = (z.m_re>0 ? pi2 : -pi2); if (infiniteY) result.m_re /= 2; } else if (infiniteY) { result.m_re = z.m_re/Double.POSITIVE_INFINITY; } if (Double.isNaN(z.m_im)) { result.m_im = -z.m_re; result.m_re = z.m_im; } else { result.m_im = z.m_im*Double.POSITIVE_INFINITY; } return result; } else if (Double.isNaN(r)) { result.m_re = result.m_im = Double.NaN; if (z.m_re == 0) result.m_re = z.m_re; } else if (r < 2.58095e-08) { // sqrt(6.0*dmach(3)) = 2.58095e-08 result.m_re = z.m_re; result.m_im = z.m_im; } else if (z.m_re == 0) { result.m_re = 0; result.m_im = Sfun.asinh(z.m_im); } else if (r <= 0.1) { Complex z2 = times(z,z); //log(eps)/log(rmax) = 8 where rmax = 0.1 for (int i = 1; i <= 8; i++) { double twoi = 2*(8-i) + 1; result = times(times(result,z2),twoi/(twoi+1.0)); result.m_re += 1.0/twoi; } result = result.times(z); } else { // A&S 4.4.26 // asin(z) = -i*log(z+sqrt(1-z)*sqrt(1+z)) // or, since log(iz) = log(z) +i*pi/2, // asin(z) = pi/2 - i*log(z+sqrt(z+1)*sqrt(z-1)) Complex w = ((z.m_im < 0) ? negative(z) : z); Complex sqzp1 = sqrt(plus(w,1.0)); if (sqzp1.m_im < 0.0) sqzp1 = negative(sqzp1); Complex sqzm1 = sqrt(minus(w,1.0)); result = log(plus(w,times(sqzp1,sqzm1))); double rx = result.m_re; result.m_re = 0.5*Math.PI + result.m_im; result.m_im = -rx; } if (result.m_re > 0.5*Math.PI) { result.m_re = Math.PI - result.m_re; result.m_im = -result.m_im; } if (result.m_re < -0.5*Math.PI) { result.m_re = -Math.PI - result.m_re; result.m_im = -result.m_im; } if (z.m_im < 0) { result.m_re = -result.m_re; result.m_im = -result.m_im; } return result; } /** * Returns the inverse cosine (arc cosine) of a Complex, * with branch cuts outside the interval [-1,1] along the * real axis. * @param z A Complex object. * @return A newly constructed Complex initialized to * inverse (arc) cosine of the argument. * The real part of the result is in the interval [0,pi]. */ public static Complex acos(Complex z) { Complex result = new Complex(); double r = abs(z); if (Double.isInfinite(z.m_re) && Double.isNaN(z.m_im)) { result.m_re = Double.NaN; result.m_im = Double.NEGATIVE_INFINITY; } else if (Double.isInfinite(r)) { result.m_re = Math.atan2(Math.abs(z.m_im),z.m_re); result.m_im = z.m_im*Double.NEGATIVE_INFINITY; } else if (r == 0) { result.m_re = Math.PI/2; result.m_im = -z.m_im; } else { result = minus(Math.PI/2,asin(z)); } return result; } /** * Returns the inverse tangent (arc tangent) of a Complex, * with branch cuts outside the interval [-i,i] along the * imaginary axis. * @param z A Complex object. * @return A newly constructed Complex initialized to * inverse (arc) tangent of the argument. * Its real part is in the interval [-pi/2,pi/2]. */ public static Complex atan(Complex z) { Complex result = new Complex(); double r = abs(z); if (Double.isInfinite(r)) { double pi2 = 0.5*Math.PI; double im = (Double.isNaN(z.m_im) ? 0 : z.m_im); result.m_re = (z.m_re<0 ? -pi2 : pi2); result.m_im = (im<0 ? -1 : 1)/Double.POSITIVE_INFINITY; if (Double.isNaN(z.m_re)) result.m_re = z.m_re; } else if (Double.isNaN(r)) { result.m_re = result.m_im = Double.NaN; if (z.m_im == 0) result.m_im = z.m_im; } else if (r < 1.82501e-08) { // sqrt(3.0*dmach(3)) = 1.82501e-08 result.m_re = z.m_re; result.m_im = z.m_im; } else if (r < 0.1) { Complex z2 = times(z,z); // -0.4343*log(dmach(3))+1 = 17 for (int k = 0; k < 17; k++) { Complex temp = times(z2,result); int twoi = 2*(17-k) - 1; result.m_re = 1.0/twoi - temp.m_re; result.m_im = -temp.m_im; } result = result.times(z); } else if (r < 9.0072e+15) { // 1.0/dmach(3) = 9.0072e+15 double r2 = r*r; result.m_re = 0.5*Math.atan2(2*z.m_re,1.0-r2); result.m_im = 0.25*Math.log((r2+2*z.m_im+1)/(r2-2*z.m_im+1)); } else { result.m_re = ((z.m_re < 0.0) ? -0.5*Math.PI : 0.5*Math.PI); } return result; } /** * Returns the hyperbolic sine of a Complex. * @param z A Complex object. * @return A newly constructed Complex initialized to hyperbolic * sine of the argument. */ public static Complex sinh(Complex z) { double coshx = Sfun.cosh(z.m_re); double sinhx = Sfun.sinh(z.m_re); double cosy = Math.cos(z.m_im); double siny = Math.sin(z.m_im); boolean infiniteX = Double.isInfinite(coshx); boolean infiniteY = Double.isInfinite(z.m_im); Complex result; if (z.m_im == 0) { result = new Complex(Sfun.sinh(z.m_re)); } else { // A&S 4.5.49 result = new Complex(sinhx*cosy, coshx*siny); if (infiniteY) { result.m_im = Double.NaN; if (z.m_re == 0) result.m_re = 0; } if (infiniteX) { result.m_re = z.m_re*cosy; result.m_im = z.m_re*siny; if (z.m_im == 0) result.m_im = 0; if (infiniteY) result.m_re = z.m_im; } } return result; } /** * Returns the hyperbolic cosh of a Complex. * @param z A Complex object. * @return A newly constructed Complex initialized to * the hyperbolic cosine of the argument. */ public static Complex cosh(Complex z) { if (z.m_im == 0) { return new Complex(Sfun.cosh(z.m_re)); } double coshx = Sfun.cosh(z.m_re); double sinhx = Sfun.sinh(z.m_re); double cosy = Math.cos(z.m_im); double siny = Math.sin(z.m_im); boolean infiniteX = Double.isInfinite(coshx); boolean infiniteY = Double.isInfinite(z.m_im); // A&S 4.5.50 Complex result = new Complex(coshx*cosy, sinhx*siny); if (infiniteY) result.m_re = Double.NaN; if (z.m_re == 0) { result.m_im = 0; } else if (infiniteX) { result.m_re = z.m_re*cosy; result.m_im = z.m_re*siny; if (z.m_im == 0) result.m_im = 0; if (Double.isNaN(z.m_im)) { result.m_re = z.m_re; } else if (infiniteY) { result.m_re = z.m_im; } } return result; } /** * Returns the hyperbolic tanh of a Complex. * @param z A Complex object. * @return A newly constructed Complex initialized to * the hyperbolic tangent of the argument. */ public static Complex tanh(Complex z) { double sinh2x = Sfun.sinh(2*z.m_re); if (z.m_im == 0) { return new Complex(Sfun.tanh(z.m_re)); } else if (sinh2x == 0) { return new Complex(0,Math.tan(z.m_im)); } double cosh2x = Sfun.cosh(2*z.m_re); double cos2y = Math.cos(2*z.m_im); double sin2y = Math.sin(2*z.m_im); boolean infiniteX = Double.isInfinite(cosh2x); // Workaround for bug in JDK 1.2beta4 if (Double.isInfinite(z.m_im) || Double.isNaN(z.m_im)) { cos2y = sin2y = Double.NaN; } if (infiniteX) return new Complex(z.m_re > 0 ? 1 : -1); // A&S 4.5.51 double den = (cosh2x + cos2y); return new Complex(sinh2x/den, sin2y/den); } /** * Returns the Complex z raised to the x power, * with a branch cut for the first parameter (z) along the * negative real axis. * @param z A Complex object. * @param x A double value. * @return A newly constructed Complex initialized to z to the power x. */ public static Complex pow(Complex z, double x) { double absz = abs(z); Complex result = new Complex(); if (absz == 0.0) { result = z; } else { double a = argument(z); double e = Math.pow(absz, x); result.m_re = e*Math.cos(x*a); result.m_im = e*Math.sin(x*a); } return result; } /** * Returns the inverse hyperbolic sine (arc sinh) of a Complex, * with a branch cuts outside the interval [-i,i]. * @param z A Complex object. * @return A newly constructed Complex initialized to * inverse (arc) hyperbolic sine of the argument. * Its imaginary part is in the interval [-i*pi/2,i*pi/2]. */ public static Complex asinh(Complex z) { // asinh(z) = i*asin(-i*z) Complex miz = new Complex(z.m_im,-z.m_re); Complex result = asin(miz); double rx = result.m_im; result.m_im = result.m_re; result.m_re = -rx; return result; } /** * Returns the inverse hyperbolic cosine (arc cosh) of a Complex, * with a branch cut at values less than one along the real axis. * @param z A Complex object. * @return A newly constructed Complex initialized to * inverse (arc) hyperbolic cosine of the argument. * The real part of the result is non-negative and its * imaginary part is in the interval [-i*pi,i*pi]. */ public static Complex acosh(Complex z) { Complex result = acos(z); double rx = -result.m_im; result.m_im = result.m_re; result.m_re = rx; if (result.m_re < 0 || isNegZero(result.m_re)) { result.m_re = -result.m_re; result.m_im = -result.m_im; } return result; } /** * Returns true is x is a negative zero. */ private static boolean isNegZero(double x) { return (Double.doubleToLongBits(x) == negZeroBits); } /** * Returns the inverse hyperbolic tangent (arc tanh) of a Complex, * with a branch cuts outside the interval [-1,1] on the real axis. * @param z A Complex object. * @return A newly constructed Complex initialized to * inverse (arc) hyperbolic tangent of the argument. * The imaginary part of the result is in the interval * [-i*pi/2,i*pi/2]. */ public static Complex atanh(Complex z) { // atanh(z) = i*atan(-i*z) Complex miz = new Complex(z.m_im,-z.m_re); Complex result = atan(miz); double rx = result.m_im; result.m_im = result.m_re; result.m_re = -rx; return result; } /** * Returns the Complex x raised to the Complex y power. * @param x A Complex object. * @param y A Complex object. * @return A newly constructed Complex initialized * to x<SUP><FONT SIZE="1">y</FONT></SUP><FONT SIZE="3">. */ public static Complex pow(Complex x, Complex y) { return exp(times(y,log(x))); } /** * Returns a String representation for the specified Complex. * @return A String representation for this object. */ public String toString() { if (m_im == 0.0) return String.valueOf(m_re); if (m_re == 0.0) return String.valueOf(m_im) + suffix; String sign = (m_im < 0.0) ? "" : "+"; return (String.valueOf(m_re) + sign + String.valueOf(m_im) + suffix); } /** * Parses a string into a Complex. * @param s The string to be parsed. * @return A newly constructed Complex initialized to the value represented * by the string argument. * @exception NumberFormatException If the string does not contain a parsable Complex number. * @exception NullPointerException If the input argument is null. */ public static Complex valueOf(String s) throws NumberFormatException { String input = s.trim(); int iBeginNumber = 0; Complex z = new Complex(); int state = 0; int sign = 1; boolean haveRealPart = false; /* * state values * 0 Initial State * 1 After Initial Sign * 2 In integer part * 3 In fractional part * 4 In exponential part (after 'e' but fore sign or digits) * 5 In exponential digits */ for (int k = 0; k < input.length(); k++) { char ch = input.charAt(k); switch (ch) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (state == 0 || state == 1) { state = 2; } else if (state == 4) { state = 5; } break; case '-': case '+': sign = ((ch=='+') ? 1 : -1); if (state == 0) { state = 1; } else if (state == 4) { state = 5; } else { if (!haveRealPart) { // have the real part of the number z.m_re = Double.valueOf(input.substring(iBeginNumber,k)).doubleValue(); haveRealPart = true; // perpare to part the imaginary part iBeginNumber = k; state = 1; } else { throw new NumberFormatException(input); } } break; case '.': if (state == 0 || state == 1 || state == 2) state = 3; else throw new NumberFormatException(input); break; case 'i': case 'I': case 'j': case 'J': if (k+1 != input.length()) { throw new NumberFormatException(input); } else if (state == 0 || state == 1) { z.m_im = sign; return z; } else if (state == 2 || state == 3 || state == 5) { z.m_im = Double.valueOf(input.substring(iBeginNumber,k)).doubleValue(); return z; } else { throw new NumberFormatException(input); } case 'e': case 'E': case 'd': case 'D': if (state == 2 || state == 3) { state = 4; } else { throw new NumberFormatException(input); } break; default: throw new NumberFormatException(input); } } if (!haveRealPart) { z.m_re = Double.valueOf(input).doubleValue(); return z; } else { throw new NumberFormatException(input); } } }