package com.imsl.math; /* * ------------------------------------------------------------------------- * $Id: VNComplex.java,v 1.4 1999/05/05 14:51:01 brophy 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. * * ------------------------------------------------------------------------- */ /** * This class implements VNComplex numbers. It provides the basic operations * (addition, subtraction, multiplication, division) as well as a set of * VNComplex functions. * * The binary operations have the form, where op is plus, * minus, times or over. *
  *	public static VNComplex op(VNComplex x, VNComplex y)   // x op y
  *	public static VNComplex op(VNComplex x, double y)    // x op y
  *	public static VNComplex op(double x, VNComplex y)    // x op y
  *	public VNComplex op(VNComplex y)                     // this op y
  *	public VNComplex op(double y)                      // this op y
  *	public VNComplex opReverse(double x)               // x op this
  * 
* * The functions in this class follow the rules for VNComplex arithmetic * as defined C9x Annex G:"IEC 559-compatible VNComplex 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 VNComplex implements java.io.Serializable, Cloneable { /** * @serial Real part of the VNComplex. */ private double re; /** * @serial Imaginary part of the VNComplex. */ private double im; /** * Serialization ID */ static final long serialVersionUID = -633126172485117692L; /** * String used in converting VNComplex 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 VNComplex. */ public static String suffix = "i"; private final static long negZeroBits = Double.doubleToLongBits(1.0/Double.NEGATIVE_INFINITY); /** * Constructs a VNComplex equal to zero. */ public VNComplex() { re = 0.0; im = 0.0; } /** * Constructs a VNComplex with a zero imaginary part. * @param re A double value equal to the real part of the VNComplex object. */ public VNComplex(double re) { this.re = re; this.im = 0.0; } /** * Constructs a VNComplex with real and imaginary parts given * by the input arguments. * @param re A double value equal to the real part of the VNComplex object. * @param im A double value equal to the imaginary part of the VNComplex object. */ public VNComplex(double re, double im) { this.re = re; this.im = im; } /** * Constructs a VNComplex equal to the argument. * @param z A VNComplex object * If z is null then a NullPointerException is thrown. */ public VNComplex(VNComplex z) { re = z.re; im = z.im; } /** * Returns the absolute value (modulus) of a VNComplex, |z|. * @param z A VNComplex object. * @return A double value equal to the absolute value of the argument. */ public static double abs(VNComplex z) { double x = Math.abs(z.re); double y = Math.abs(z.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 inverse cosine (arc cosine) of a VNComplex, * with branch cuts outside the interval [-1,1] along the * real axis. * @param z A VNComplex object. * @return A newly constructed VNComplex initialized to * inverse (arc) cosine of the argument. * The real part of the result is in the interval [0,pi]. */ public static VNComplex acos(VNComplex z) { VNComplex result = new VNComplex(); double r = abs(z); if (Double.isInfinite(z.re) && Double.isNaN(z.im)) { result.re = Double.NaN; result.im = Double.NEGATIVE_INFINITY; } else if (Double.isInfinite(r)) { result.re = Math.atan2(Math.abs(z.im),z.re); result.im = z.im*Double.NEGATIVE_INFINITY; } else if (r == 0) { result.re = Math.PI/2; result.im = -z.im; } else { result = minus(Math.PI/2,asin(z)); } return result; } /** * Returns the inverse hyperbolic cosine (arc cosh) of a VNComplex, * with a branch cut at values less than one along the real axis. * @param z A VNComplex object. * @return A newly constructed VNComplex 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 VNComplex acosh(VNComplex z) { VNComplex result = acos(z); double rx = -result.im; result.im = result.re; result.re = rx; if (result.re < 0 || isNegZero(result.re)) { result.re = -result.re; result.im = -result.im; } return result; } /** * Returns the argument (phase) of a VNComplex, in radians, * with a branch cut along the negative real axis. * @param z A VNComplex object. * @return A double value equal to the argument (or phase) of a VNComplex. * It is in the interval [-pi,pi]. */ public static double argument(VNComplex z) { return Math.atan2(z.im, z.re); } /** * Returns the inverse sine (arc sine) of a VNComplex, * with branch cuts outside the interval [-1,1] along the * real axis. * @param z A VNComplex object. * @return A newly constructed VNComplex initialized to inverse * (arc) sine of the argument. The real part of the * result is in the interval [-pi/2,+pi/2]. */ public static VNComplex asin(VNComplex z) { VNComplex result = new VNComplex(); double r = abs(z); if (Double.isInfinite(r)) { boolean infiniteX = Double.isInfinite(z.re); boolean infiniteY = Double.isInfinite(z.im); if (infiniteX) { double pi2 = 0.5*Math.PI; result.re = (z.re>0 ? pi2 : -pi2); if (infiniteY) result.re /= 2; } else if (infiniteY) { result.re = z.re/Double.POSITIVE_INFINITY; } if (Double.isNaN(z.im)) { result.im = -z.re; result.re = z.im; } else { result.im = z.im*Double.POSITIVE_INFINITY; } return result; } else if (Double.isNaN(r)) { result.re = result.im = Double.NaN; if (z.re == 0) result.re = z.re; } else if (r < 2.58095e-08) { // sqrt(6.0*dmach(3)) = 2.58095e-08 result.re = z.re; result.im = z.im; } else if (z.re == 0) { result.re = 0; result.im = Sfun.asinh(z.im); } else if (r <= 0.1) { VNComplex 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.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)) VNComplex w = ((z.im < 0) ? negative(z) : z); VNComplex sqzp1 = sqrt(plus(w,1.0)); if (sqzp1.im < 0.0) sqzp1 = negative(sqzp1); VNComplex sqzm1 = sqrt(minus(w,1.0)); result = log(plus(w,times(sqzp1,sqzm1))); double rx = result.re; result.re = 0.5*Math.PI + result.im; result.im = -rx; } if (result.re > 0.5*Math.PI) { result.re = Math.PI - result.re; result.im = -result.im; } if (result.re < -0.5*Math.PI) { result.re = -Math.PI - result.re; result.im = -result.im; } if (z.im < 0) { result.re = -result.re; result.im = -result.im; } return result; } /** * Returns the inverse hyperbolic sine (arc sinh) of a VNComplex, * with a branch cuts outside the interval [-i,i]. * @param z A VNComplex object. * @return A newly constructed VNComplex initialized to * inverse (arc) hyperbolic sine of the argument. * Its imaginary part is in the interval [-i*pi/2,i*pi/2]. */ public static VNComplex asinh(VNComplex z) { // asinh(z) = i*asin(-i*z) VNComplex miz = new VNComplex(z.im,-z.re); VNComplex result = asin(miz); double rx = result.im; result.im = result.re; result.re = -rx; return result; } /** * Returns the inverse tangent (arc tangent) of a VNComplex, * with branch cuts outside the interval [-i,i] along the * imaginary axis. * @param z A VNComplex object. * @return A newly constructed VNComplex initialized to * inverse (arc) tangent of the argument. * Its real part is in the interval [-pi/2,pi/2]. */ public static VNComplex atan(VNComplex z) { VNComplex result = new VNComplex(); double r = abs(z); if (Double.isInfinite(r)) { double pi2 = 0.5*Math.PI; double im = (Double.isNaN(z.im) ? 0 : z.im); result.re = (z.re<0 ? -pi2 : pi2); result.im = (im<0 ? -1 : 1)/Double.POSITIVE_INFINITY; if (Double.isNaN(z.re)) result.re = z.re; } else if (Double.isNaN(r)) { result.re = result.im = Double.NaN; if (z.im == 0) result.im = z.im; } else if (r < 1.82501e-08) { // sqrt(3.0*dmach(3)) = 1.82501e-08 result.re = z.re; result.im = z.im; } else if (r < 0.1) { VNComplex z2 = times(z,z); // -0.4343*log(dmach(3))+1 = 17 for (int k = 0; k < 17; k++) { VNComplex temp = times(z2,result); int twoi = 2*(17-k) - 1; result.re = 1.0/twoi - temp.re; result.im = -temp.im; } result = result.times(z); } else if (r < 9.0072e+15) { // 1.0/dmach(3) = 9.0072e+15 double r2 = r*r; result.re = 0.5*Math.atan2(2*z.re,1.0-r2); result.im = 0.25*Math.log((r2+2*z.im+1)/(r2-2*z.im+1)); } else { result.re = ((z.re < 0.0) ? -0.5*Math.PI : 0.5*Math.PI); } return result; } /** * Returns the inverse hyperbolic tangent (arc tanh) of a VNComplex, * with a branch cuts outside the interval [-1,1] on the real axis. * @param z A VNComplex object. * @return A newly constructed VNComplex 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 VNComplex atanh(VNComplex z) { // atanh(z) = i*atan(-i*z) VNComplex miz = new VNComplex(z.im,-z.re); VNComplex result = atan(miz); double rx = result.im; result.im = result.re; result.re = -rx; return result; } /** * Returns the VNComplex conjugate of a VNComplex object. * @param z A VNComplex object. * @return A newly constructed VNComplex initialized to VNComplex conjugate of z. */ public static VNComplex conjugate(VNComplex z) { return new VNComplex(z.re, -z.im); } /* * Returns sign(b)*|a|. */ private static double copysign(double a, double b) { double abs = Math.abs(a); return ((b < 0) ? -abs : abs); } /** * Returns the cosine of a VNComplex. * @param z A VNComplex object. * @return A newly constructed VNComplex initialized to cosine of the argument. */ public static VNComplex cos(VNComplex z) { // cos(z) = cosh(i*z) return cosh(new VNComplex(-z.im,z.re)); } /** * Returns the hyperbolic cosh of a VNComplex. * @param z A VNComplex object. * @return A newly constructed VNComplex initialized to * the hyperbolic cosine of the argument. */ public static VNComplex cosh(VNComplex z) { if (z.im == 0) { return new VNComplex(Sfun.cosh(z.re)); } double coshx = Sfun.cosh(z.re); double sinhx = Sfun.sinh(z.re); double cosy = Math.cos(z.im); double siny = Math.sin(z.im); boolean infiniteX = Double.isInfinite(coshx); boolean infiniteY = Double.isInfinite(z.im); // A&S 4.5.50 VNComplex result = new VNComplex(coshx*cosy, sinhx*siny); if (infiniteY) result.re = Double.NaN; if (z.re == 0) { result.im = 0; } else if (infiniteX) { result.re = z.re*cosy; result.im = z.re*siny; if (z.im == 0) result.im = 0; if (Double.isNaN(z.im)) { result.re = z.re; } else if (infiniteY) { result.re = z.im; } } return result; } /** * Compares with another VNComplex. *

Note: To be useful in hashtables this method * considers two NaN double values to be equal. This * is not according to IEEE specification. * @param z A VNComplex 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(VNComplex z) { if (isNaN() && z.isNaN()) { return true; } else { return (re == z.re && im == z.im); } } /** * Compares this object against the specified object. *

Note: To be useful in hashtables this method * considers two NaN double values to be equal. This * is not according to IEEE specification * @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 VNComplex) { return equals((VNComplex)obj); } else { return false; } } /** * Returns the exponential of a VNComplex z, exp(z). * @param z A VNComplex object. * @return A newly constructed VNComplex initialized to exponential * of the argument. */ public static VNComplex exp(VNComplex z) { VNComplex result = new VNComplex(); double r = Math.exp(z.re); double cosa = Math.cos(z.im); double sina = Math.sin(z.im); if (Double.isInfinite(z.im) || Double.isNaN(z.im) || Math.abs(cosa)>1) { cosa = sina = Double.NaN; } if (Double.isInfinite(z.re) || Double.isInfinite(r)) { if (z.re < 0) { r = 0; if (Double.isInfinite(z.im) || Double.isNaN(z.im)) { cosa = sina = 0; } else { cosa /= Double.POSITIVE_INFINITY; sina /= Double.POSITIVE_INFINITY; } } else { r = z.re; if (Double.isNaN(z.im)) cosa = 1; } } if (z.im == 0.0) { result.re = r; result.im = z.im; } else { result.re = r*cosa; result.im = r*sina; } return result; } /** * Returns a hashcode for this VNComplex. * @return A hash code value for this object. */ public int hashCode() { long re_bits = Double.doubleToLongBits(re); long im_bits = Double.doubleToLongBits(im); return (int)((re_bits^im_bits)^((re_bits^im_bits)>>32)); } /** * Returns the imaginary part of a VNComplex object. * @param z A VNComplex object. * @return The imaginary part of z. */ public double imag() { return im; } /** * Returns the imaginary part of a VNComplex object. * @param z A VNComplex object. * @return The imaginary part of z. */ public static double imag(VNComplex z) { return z.im; } private static boolean isFinite(double x) { return !(Double.isInfinite(x) || Double.isNaN(x)); } /** * Tests if this is a VNComplex Not-a-Number (NaN) value. * @return True if either component of the VNComplex object is NaN; * false, otherwise. */ private boolean isNaN() { return (Double.isNaN(re) || Double.isNaN(im)); } /** * Returns true is x is a negative zero. */ private static boolean isNegZero(double x) { return (Double.doubleToLongBits(x) == negZeroBits); } /** * Returns the logarithm of a VNComplex z, * with a branch cut along the negative real axis. * @param z A VNComplex object. * @return A newly constructed VNComplex initialized to logarithm * of the argument. Its imaginary part is in the * interval [-i*pi,i*pi]. */ public static VNComplex log(VNComplex z) { VNComplex result = new VNComplex(); if (Double.isNaN(z.re)) { result.re = result.im = z.re; if (Double.isInfinite(z.im)) result.re = Double.POSITIVE_INFINITY; } else if (Double.isNaN(z.im)) { result.re = result.im = z.im; if (Double.isInfinite(z.re)) result.re = Double.POSITIVE_INFINITY; } else { result.re = Math.log(abs(z)); result.im = argument(z); } return result; } /** * Subtracts a double from this VNComplex and returns the difference, this-y. * @param y A double value. * @return A newly constructed VNComplex initialized to this-y. */ public VNComplex minus(double y) { return new VNComplex(re-y, im); } /** * Returns the difference of a double and a VNComplex object, x-y. * @param x A double value. * @param y A VNComplex object. * @return A newly constructed VNComplex initialized to x-y.. */ public static VNComplex minus(double x, VNComplex y) { return new VNComplex(x-y.re, -y.im); } /** * Returns the difference of this VNComplex object and * another VNComplex object, this-y. * @param y A VNComplex object. * @return A newly constructed VNComplex initialized to this-y. */ public VNComplex minus(VNComplex y) { return new VNComplex(re-y.re, im-y.im); } /** * Returns the difference of a VNComplex object and a double, x-y. * @param x A VNComplex object. * @param y A double value. * @return A newly constructed VNComplex initialized to x-y. */ public static VNComplex minus(VNComplex x, double y) { return new VNComplex(x.re-y, x.im); } /** * Returns the difference of two VNComplex objects, x-y. * @param x A VNComplex object. * @param y A VNComplex object. * @return A newly constructed VNComplex initialized to x-y. */ public static VNComplex minus(VNComplex x, VNComplex y) { return new VNComplex(x.re-y.re, x.im-y.im); } /** * Returns the difference of this VNComplex object and a double, this-y. * @param y A double value. * @return A newly constructed VNComplex initialized to x-this. */ public VNComplex minusReverse(double x) { return new VNComplex(x-re, -im); } /** * Returns the negative of a VNComplex object, -z. * @param z A VNComplex object. * @return A newly constructed VNComplex initialized to * the negative of the argument. */ public static VNComplex negative(VNComplex z) { return new VNComplex(-z.re, -z.im); } /** * Returns this VNComplex object divided by double, this/y. * @param y The denominator, a double. * @return A newly constructed VNComplex initialized to x/y. */ public VNComplex over(double y) { return over(this, y); } /** * Returns a double divided by a VNComplex object, x/y. * @param x A double value. * @param y The denominator, a VNComplex object. * @return A newly constructed VNComplex initialized to x/y. */ public static VNComplex over(double x, VNComplex y) { return y.overReverse(x); } /** * Returns this VNComplex object divided by another VNComplex object, this/y. * @param y The denominator, a VNComplex object. * @return A newly constructed VNComplex initialized to x/y. */ public VNComplex over(VNComplex y) { return over(this, y); } /** * Returns VNComplex object divided by a double, x/y. * @param x The numerator, a VNComplex object. * @param y The denominator, a double. * @return A newly constructed VNComplex initialized to x/y. */ public static VNComplex over(VNComplex x, double y) { return new VNComplex(x.re/y, x.im/y); } /** * Returns VNComplex object divided by a VNComplex object, x/y. * @param x The numerator, a VNComplex object. * @param y The denominator, a VNComplex object. * @return A newly constructed VNComplex initialized to x/y. */ public static VNComplex over(VNComplex x, VNComplex y) { double a = x.re; double b = x.im; double c = y.re; double d = y.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; VNComplex z = new VNComplex((a*c+b*d)/den, (b*c-a*d)/den); if (isScaleFinite) { z.re /= scale; z.im /= scale; } // Recover infinities and zeros computed as NaN+iNaN. if (Double.isNaN(z.re) && Double.isNaN(z.im)) { if (den == 0.0 && (!Double.isNaN(a) || !Double.isNaN(b))) { double s = copysign(Double.POSITIVE_INFINITY, c); z.re = s * a; z.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.re = Double.POSITIVE_INFINITY * (a*c + b*d); z.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.re = 0.0 * (a*c + b*d); z.im = 0.0 * (b*c - a*d); } } return z; } /** * Returns a double dividied by this VNComplex object, x/this. * @param x The numerator, a double. * @return A newly constructed VNComplex initialized to x/this. */ public VNComplex overReverse(double x) { double den, t; VNComplex z; if (Math.abs(re) > Math.abs(im)) { t = im / re; den = re + im*t; z = new VNComplex(x/den, -x*t/den); } else { t = re / im; den = im + re*t; z = new VNComplex(x*t/den, -x/den); } return z; } /** * Returns the sum of this VNComplex a double, this+y. * @param y A double value. * @return A newly constructed VNComplex initialized to this+y. */ public VNComplex plus(double y) { return new VNComplex(re+y, im); } /** * Returns the sum of a double and a VNComplex, x+y. * @param x A double value. * @param y A VNComplex object. * @return A newly constructed VNComplex initialized to x+y. */ public static VNComplex plus(double x, VNComplex y) { return new VNComplex(x+y.re, y.im); } /** * Returns the sum of this VNComplex and another VNComplex, this+y. * @param y A VNComplex object. * @return A newly constructed VNComplex initialized to this+y. */ public VNComplex plus(VNComplex y) { return new VNComplex(re+y.re, im+y.im); } /** * Returns the sum of a VNComplex and a double, x+y. * @param x A VNComplex object. * @param y A double value. * @return A newly constructed VNComplex initialized to x+y. */ public static VNComplex plus(VNComplex x, double y) { return new VNComplex(x.re+y, x.im); } /** * Returns the sum of two VNComplex objects, x+y. * @param x A VNComplex object. * @param y A VNComplex object. * @return A newly constructed VNComplex initialized to x+y. */ public static VNComplex plus(VNComplex x, VNComplex y) { return new VNComplex(x.re+y.re, x.im+y.im); } /** * Returns the sum of this VNComplex and a double, x+this. * @param x A double value. * @return A newly constructed VNComplex initialized to x+this. */ public VNComplex plusReverse(double x) { return new VNComplex(re+x, im); } /** * Returns the VNComplex z raised to the x power, * with a branch cut for the first parameter (z) along the * negative real axis. * @param z A VNComplex object. * @param x A double value. * @return A newly constructed VNComplex initialized to z to the power x. */ public static VNComplex pow(VNComplex z, double x) { double absz = abs(z); VNComplex result = new VNComplex(); if (absz == 0.0) { result = z; } else { double a = argument(z); double e = Math.pow(absz, x); result.re = e*Math.cos(x*a); result.im = e*Math.sin(x*a); } return result; } /** * Returns the VNComplex x raised to the VNComplex y power. * @param x A VNComplex object. * @param y A VNComplex object. * @return A newly constructed VNComplex initialized * to xy. */ public static VNComplex pow(VNComplex x, VNComplex y) { return exp(times(y,log(x))); } /** * Returns the real part of a VNComplex object. * @return The real part of z. */ public double real() { return re; } /** * Returns the real part of a VNComplex object. * @param z A VNComplex object. * @return The real part of z. */ public static double real(VNComplex z) { return z.re; } /** * Returns the sine of a VNComplex. * @param z A VNComplex object. * @return A newly constructed VNComplex initialized to sine of the argument. */ public static VNComplex sin(VNComplex z) { // sin(z) = -i*sinh(i*z) VNComplex iz = new VNComplex(-z.im,z.re); VNComplex s = sinh(iz); double re = s.im; s.im = -s.re; s.re = re; return s; } /** * Returns the hyperbolic sine of a VNComplex. * @param z A VNComplex object. * @return A newly constructed VNComplex initialized to hyperbolic * sine of the argument. */ public static VNComplex sinh(VNComplex z) { double coshx = Sfun.cosh(z.re); double sinhx = Sfun.sinh(z.re); double cosy = Math.cos(z.im); double siny = Math.sin(z.im); boolean infiniteX = Double.isInfinite(coshx); boolean infiniteY = Double.isInfinite(z.im); VNComplex result; if (z.im == 0) { result = new VNComplex(Sfun.sinh(z.re)); } else { // A&S 4.5.49 result = new VNComplex(sinhx*cosy, coshx*siny); if (infiniteY) { result.im = Double.NaN; if (z.re == 0) result.re = 0; } if (infiniteX) { result.re = z.re*cosy; result.im = z.re*siny; if (z.im == 0) result.im = 0; if (infiniteY) result.re = z.im; } } return result; } /** * Returns the square root of a VNComplex, * with a branch cut along the negative real axis. * @param z A VNComplex object. * @return A newly constructed VNComplex initialized * to square root of z. Its real part is * non-negative. */ public static VNComplex sqrt(VNComplex z) { VNComplex result = new VNComplex(); if (Double.isInfinite(z.im)) { result.re = Double.POSITIVE_INFINITY; result.im = z.im; } else if (Double.isNaN(z.re)) { result.re = result.im = Double.NaN; } else if (Double.isNaN(z.im)) { if (Double.isInfinite(z.re)) { if (z.re > 0) { result.re = z.re; result.im = z.im; } else { result.re = z.im; result.im = Double.POSITIVE_INFINITY; } } else { result.re = result.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.re) <= Math.abs(z.im)) { // No cancellation in these formulas result.re = Math.sqrt(0.5*(t+z.re)); result.im = Math.sqrt(0.5*(t-z.re)); } else { // Stable computation of the above formulas if (z.re > 0) { result.re = t + z.re; result.im = Math.abs(z.im)*Math.sqrt(0.5/result.re); result.re = Math.sqrt(0.5*result.re); } else { result.im = t - z.re; result.re = Math.abs(z.im)*Math.sqrt(0.5/result.im); result.im = Math.sqrt(0.5*result.im); } } if (z.im < 0) result.im = -result.im; } return result; } /** * Returns the tangent of a VNComplex. * @param z A VNComplex object. * @return A newly constructed VNComplex initialized * to tangent of the argument. */ public static VNComplex tan(VNComplex z) { // tan = -i*tanh(i*z) VNComplex iz = new VNComplex(-z.im,z.re); VNComplex s = tanh(iz); double re = s.im; s.im = -s.re; s.re = re; return s; } /** * Returns the hyperbolic tanh of a VNComplex. * @param z A VNComplex object. * @return A newly constructed VNComplex initialized to * the hyperbolic tangent of the argument. */ public static VNComplex tanh(VNComplex z) { double sinh2x = Sfun.sinh(2*z.re); if (z.im == 0) { return new VNComplex(Sfun.tanh(z.re)); } else if (sinh2x == 0) { return new VNComplex(0,Math.tan(z.im)); } double cosh2x = Sfun.cosh(2*z.re); double cos2y = Math.cos(2*z.im); double sin2y = Math.sin(2*z.im); boolean infiniteX = Double.isInfinite(cosh2x); // Workaround for bug in JDK 1.2beta4 if (Double.isInfinite(z.im) || Double.isNaN(z.im)) { cos2y = sin2y = Double.NaN; } if (infiniteX) return new VNComplex(z.re > 0 ? 1 : -1); // A&S 4.5.51 double den = (cosh2x + cos2y); return new VNComplex(sinh2x/den, sin2y/den); } /** * Returns the product of this VNComplex object and a double, this*y. * @param y A double value. * @return A newly constructed VNComplex initialized to this*y. */ public VNComplex times(double y) { return new VNComplex(re*y, im*y); } /** * Returns the product of a double and a VNComplex object, x*y. * @param x A double value. * @param y A VNComplex object. * @return A newly constructed VNComplex initialized to x*y. */ public static VNComplex times(double x, VNComplex y) { return new VNComplex(x*y.re, x*y.im); } /** * Returns the product of this VNComplex object and another VNComplex object, this*y. * @param y A VNComplex object. * @return A newly constructed VNComplex initialized to this*y. */ public VNComplex times(VNComplex y) { return times(this,y); } /** * Returns the product of a VNComplex object and a double, x*y. * @param x A VNComplex object. * @param y A double value. * @return A newly constructed VNComplex initialized to x*y. */ public static VNComplex times(VNComplex x, double y) { return new VNComplex(x.re*y, x.im*y); } /** * Returns the product of two VNComplex objects, x*y. * @param x A VNComplex object. * @param y A VNComplex object. * @return A newly constructed VNComplex initialized to x*y. */ public static VNComplex times(VNComplex x, VNComplex y) { VNComplex t = new VNComplex(x.re*y.re-x.im*y.im, x.re*y.im+x.im*y.re); if (Double.isNaN(t.re) && Double.isNaN(t.im)) timesNaN(x, y, t); return t; } /** * 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. *

* This algorithm is adapted from the C9x Annex G: * "IEC 559-compatible VNComplex arithmetic." * @param x First VNComplex operand. * @param y Second VNComplex 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(VNComplex x, VNComplex y, VNComplex t) { boolean recalc = false; double a = x.re; double b = x.im; double c = y.re; double d = y.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.re = Double.POSITIVE_INFINITY * (a*c - b*d); t.im = Double.POSITIVE_INFINITY * (a*d + b*c); } } /** * Returns the product of a double and this VNComplex, x*this. * @param y A double value. * @return A newly constructed VNComplex initialized to x*this. */ public VNComplex timesReverse(double x) { return new VNComplex(x*re, x*im); } /** * Returns a String representation for the specified VNComplex. * @return A String representation for this object. */ public String toString() { if (im == 0.0) return String.valueOf(re); if (re == 0.0) return String.valueOf(im) + suffix; String sign = (im < 0.0) ? "" : "+"; return (String.valueOf(re) + sign + String.valueOf(im) + suffix); } /** * Parses a string into a VNComplex. * @param s The string to be parsed. * @return A newly constructed VNComplex initialized to the value represented * by the string argument. * @exception NumberFormatException If the string does not contain a parsable VNComplex number. * @exception NullPointerException If the input argument is null. */ public static VNComplex valueOf(String s) throws NumberFormatException { String input = s.trim(); int iBeginNumber = 0; VNComplex z = new VNComplex(); 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.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.im = sign; return z; } else if (state == 2 || state == 3 || state == 5) { z.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.re = Double.valueOf(input).doubleValue(); return z; } else { throw new NumberFormatException(input); } } }