package ti.lang; import java.util.StringTokenizer; // Complex number immutable class (rectangular) // Kaushik Datta and Dan Bonachea {kdatta,bonachea}@cs.berkeley.edu public immutable class Complex { public double re; public double im; private static final double NaN = Double.NaN; private static final Complex i = new Complex(0, 1); public inline Complex() { re = 0.0; im = 0.0; } public inline Complex(double re, double im) { if (Double.isNaN(re+im)) { this.re = NaN; this.im = 0.0; } else { this.re = re; this.im = im; } } public String toString() { String s = new String(""); if ((re == 0) || (im == 0)) { if (im == 0) { s = s + re; } else { s = s + im + "i"; } } else { if (im > 0) { s = s + re + " + " + im + "i"; } else { s = s + re + " - " + Math.abs(im) + "i"; } } return s; } /* String s must be in the form: a, bi, or a+bi, where a and b can be parsed into doubles */ public static Complex parseComplex(String s) throws NullPointerException, NumberFormatException { String token1, token2, token3, token4, token5; double firstNumber, real, imag; real = 0.0; imag = 0.0; // remove all spaces from the string StringTokenizer st1 = new StringTokenizer(s, " ", false); String s1 = ""; while (st1.hasMoreTokens()) { s1 = s1 + st1.nextToken(); } // now parse the string s1 StringTokenizer st2 = new StringTokenizer(s1, "+-i", true); if (st2.hasMoreTokens()) { token1 = st2.nextToken(); } else { throw new NumberFormatException(); } if (token1.equals("+")) { if (st2.hasMoreTokens()) { token2 = st2.nextToken(); } else { throw new NumberFormatException(); } firstNumber = Double.valueOf(token2).doubleValue(); } else if (token1.equals("-")) { if (st2.hasMoreTokens()) { token2 = st2.nextToken(); } else { throw new NumberFormatException(); } firstNumber = -Double.valueOf(token2).doubleValue(); } else { firstNumber = Double.valueOf(token1).doubleValue(); } if (st2.hasMoreTokens()) { token3 = st2.nextToken(); if (token3.equals("i")) { imag = firstNumber; } else { real = firstNumber; if (st2.hasMoreTokens()) { token4 = st2.nextToken(); } else { throw new NumberFormatException(); } if (token3.equals("+")) { imag = Double.valueOf(token4).doubleValue(); } else if (token3.equals("-")) { imag = -Double.valueOf(token4).doubleValue(); } if (st2.hasMoreTokens()) { token5 = st2.nextToken(); } else { throw new NumberFormatException(); } if (!token5.equals("i")) { throw new NumberFormatException(); } } if (st2.hasMoreTokens()) { throw new NumberFormatException(); } else { return new Complex(real, imag); } } else { return new Complex(firstNumber, 0.0); } } /* Operator overloading methods */ // negative public inline Complex op-() { return new Complex(-re, -im); } public inline Complex op+(double d1) { return new Complex(re + d1, im); } public inline Complex op+(Complex c1) { return new Complex(re + c1.re, im + c1.im); } public inline Complex op-(double d1) { return new Complex(re - d1, im); } public inline Complex op-(Complex c1) { return new Complex(re - c1.re, im - c1.im); } public inline Complex op*(double d1) { return new Complex(re * d1, im * d1); } public inline Complex op*(Complex c1) { return new Complex(re * c1.re - im * c1.im, im * c1.re + re * c1.im); } public inline Complex op/(double d1) { return new Complex(re / d1, im / d1); } public Complex op/(Complex c1) { double magnitudeC1Squared = c1.re * c1.re + c1.im * c1.im; return new Complex((re * c1.re + im * c1.im) / magnitudeC1Squared, (im * c1.re - re * c1.im) / magnitudeC1Squared); } public inline boolean op==(double d1) { return ((re == d1) && (im == 0.0)); } public inline boolean op==(Complex c1) { return ((re == c1.re) && (im == c1.im)); } public inline boolean op!=(double d1) { return ((re != d1) || (im != 0.0)); } public inline boolean op!=(Complex c1) { return ((re != c1.re) || (im != c1.im)); } // complex conjugate public inline Complex op~() { return new Complex(re, -im); } // complex number raised to double power public Complex op^(double d1) { if (abs() > 0.0) { return (log() * d1).exp(); } else { if (d1 != 0.0) { return this; } else { return new Complex(NaN, 0.0); } } } // complex number raised to complex power public Complex op^(Complex c1) { if (abs() > 0.0) { return (log() * c1).exp(); } else { if (c1.abs() > 0.0) { return this; } else { return new Complex(NaN, 0.0); } } } /* non-operator overloading methods */ public inline Complex neg() { return (-this); } public inline Complex add(double d1) { return (this + d1); } public inline Complex add(Complex c1) { return (this + c1); } public static inline Complex add(Complex c1, double d1) { return (c1 + d1); } /* this add is for (d1 + c1), which is undefined using operator overloading */ public static inline Complex add(double d1, Complex c1) { return (c1 + d1); } public inline Complex sub(double d1) { return (this - d1); } public inline Complex sub(Complex c1) { return (this - c1); } public static inline Complex sub(Complex c1, double d1) { return (c1 - d1); } /* this subtract is for (d1 - c1), which is undefined using operator overloading */ public static inline Complex sub(double d1, Complex c1) { return new Complex(d1 - c1.re, -c1.im); } public inline Complex mult(double d1) { return (this * d1); } public inline Complex mult(Complex c1) { return (this * c1); } public static inline Complex mult(Complex c1, double d1) { return (c1 * d1); } /* this multiply is for (d1 * c1), which is undefined using operator overloading */ public static inline Complex mult(double d1, Complex c1) { return (c1 * d1); } public inline Complex div(double d1) { return (this / d1); } public inline Complex div(Complex c1) { return (this / c1); } public static inline Complex div(Complex c1, double d1) { return (c1 / d1); } /* this divide is for (d1 / c1), which is undefined using operator overloading */ public static Complex div(double d1, Complex c1) { double magnitudeC1Squared = c1.re * c1.re + c1.im * c1.im; return new Complex((d1 * c1.re)/magnitudeC1Squared, -(d1 * c1.im)/magnitudeC1Squared); } public inline boolean eq(double d1) { return (this == d1); } public inline boolean eq(Complex c1) { return (this == c1); } public inline boolean neq(double d1) { return (this != d1); } public inline boolean neq(Complex c1) { return (this != c1); } // returns the complex conjugate public inline Complex conj() { return (~this); } public inline Complex pow(double d1) { return this^d1; } public inline Complex pow(Complex c1) { return this^c1; } public inline Complex exp() { double magnitude = Math.exp(re); return new Complex(magnitude * Math.cos(im), magnitude * Math.sin(im)); } public inline Complex log() { return new Complex(Math.log(abs()), arg()); } public inline Complex sqrt() { return (log()/2.0).exp(); } public Complex sin() { double sinRe = Math.sin(re); double cosRe = Math.cos(re); double expIm = Math.exp(im); double expMinusIm = 1.0/expIm; return new Complex(sinRe * (expMinusIm + expIm)/2.0, cosRe * (expIm - expMinusIm)/2.0); } public Complex cos() { double sinRe = Math.sin(re); double cosRe = Math.cos(re); double expIm = Math.exp(im); double expMinusIm = 1.0/expIm; return new Complex(cosRe * (expMinusIm + expIm)/2.0, sinRe * (expMinusIm - expIm)/2.0); } public Complex tan() { double sinRe = Math.sin(re); double cosRe = Math.cos(re); double expIm = Math.exp(im); double expMinusIm = 1.0/expIm; double sum = expIm + expMinusIm; double diff = expIm - expMinusIm; double x1 = sinRe * sum; double y1 = cosRe * diff; double x2 = cosRe * sum; double y2 = sinRe * diff; double magnitudeSquared = x2 * x2 + y2 * y2; return new Complex((x1 * x2 - y1 * y2)/magnitudeSquared, (x2 * y1 + x1 * y2)/magnitudeSquared); } public Complex asin() { return (-i * (i * this + (-this * this + 1.0).sqrt()).log()); } public Complex acos() { return (-i * (this + i * (-this * this + 1.0).sqrt()).log()); } public Complex atan() { return (i/2.0) * ((i + this)/(i - this)).log(); } public Complex sinh() { double sinIm = Math.sin(im); double cosIm = Math.cos(im); double expRe = Math.exp(re); double expMinusRe = 1.0/expRe; return new Complex(cosIm * (expRe - expMinusRe)/2.0, sinIm * (expRe + expMinusRe)/2.0); } public Complex cosh() { double sinIm = Math.sin(im); double cosIm = Math.cos(im); double expRe = Math.exp(re); double expMinusRe = 1.0/expRe; return new Complex(cosIm * (expRe + expMinusRe)/2.0, sinIm * (expRe - expMinusRe)/2.0); } public Complex tanh() { double sinIm = Math.sin(im); double cosIm = Math.cos(im); double expRe = Math.exp(re); double expMinusRe = 1.0/expRe; double sum = expRe + expMinusRe; double diff = expRe - expMinusRe; double x1 = cosIm * diff; double y1 = sinIm * sum; double x2 = cosIm * sum; double y2 = sinIm * diff; double magnitudeSquared = x2 * x2 + y2 * y2; return new Complex((x1 * x2 + y1 * y2)/magnitudeSquared, (x2 * y1 - x1 * y2)/magnitudeSquared); } public Complex asinh() { return (this + ((this * this) + 1.0).sqrt()).log(); } // this function has extra code to match its result with other common math libraries public Complex acosh() { Complex result = (this + ((this * this) - 1.0).sqrt()).log(); if (this.re >= 0.0) { return result; } else if ((this.re > -1.0) || (this.im != 0.0)) { return -result; } else { return new Complex(-result.re, result.im); } } public Complex atanh() { return ((this + 1.0)/(-this + 1.0)).log() / 2.0; } /* Methods for converting to polar */ // returns the absolute value (magnitude) public inline double abs() { return Math.sqrt(re * re + im * im); } // returns abs()^2. This is used to compare magnitudes without Math.sqrt() public inline double absSquared() { return (re * re + im * im); } /* returns the argument (phase angle) in radians. The result must be between -pi and pi */ public inline double arg() { return Math.atan2(im, re); } /* returns a ComplexPolar object from the magnitude and angle (in radians) of the complex number */ public inline ComplexPolar toPolar() { return new ComplexPolar(abs(), arg()); } }