package vmm.core; /** * A XahLeeComplex number, with a real and an imaginary part. (Possibley to be replaced with * a class that has better support for XahLeeComplex arithmetic and functions of a XahLeeComplex variable.) */ public class XahLeeComplex { public double re, im; /** * Create a XahLeeComplex number initially equal to zero */ public XahLeeComplex() { } /** * Create a XahLeeComplex number initially equal to the real number x. */ public XahLeeComplex(double x) { re = x; } /** * Create a XahLeeComplex number initially equal to x + iy */ public XahLeeComplex(double x, double y) { re = x; im = y; } /** * Create a new XahLeeComplex number that is initially equal to a given XahLeeComplex number. * @param c The XahLeeComplex number to be copied. If null, it is treated as zero. */ public XahLeeComplex(XahLeeComplex c) { copy(c); } public static final XahLeeComplex ZERO_C = new XahLeeComplex(0,0); public static final XahLeeComplex ONE_C = new XahLeeComplex(1,0); public static final XahLeeComplex I_C = new XahLeeComplex(0,1); /** * Returns true if obj is equal to this XahLeeComplex number. If obj is null or is not * of type XahLeeComplex, the return value is false. */ public boolean equals(Object obj) { try { XahLeeComplex c = (XahLeeComplex)obj; return c.re == re && c.im == im; } catch (Exception e) { return false; } } /** * Computes the conjugate of a XahLeeComplex number. */ public XahLeeComplex conj() { return new XahLeeComplex( re, -im ); } /** * Returns the XahLeeComplex number (r*cos(theta)) + i*(r*sin(theta)). */ public static XahLeeComplex polar(double r, double theta) { return new XahLeeComplex(r*Math.cos(theta),r*Math.sin(theta)); } /** * Sets this XahLeeComplex number equal to a copy of a given number. * @param c The number to be copied; if null, the number is treated as zero. */ public void copy(XahLeeComplex c) { if (c == null) re = im = 0; else { re = c.re; im = c.im; } } /** * Returns this + c; c must be non-null. */ public XahLeeComplex plus(XahLeeComplex c) { return new XahLeeComplex(re + c.re, im + c.im); } /** * Returns this - c; c must be non-null. */ public XahLeeComplex minus(XahLeeComplex c) { return new XahLeeComplex(re - c.re, im - c.im); } /** * Returns this * c; c must be non-null. */ public XahLeeComplex times(XahLeeComplex c) { return new XahLeeComplex(re*c.re - im*c.im, re*c.im + im*c.re); } /** * Returns this / c; c must be non-null. */ public XahLeeComplex dividedBy(XahLeeComplex c) { double denom = c.re*c.re + c.im*c.im; if (denom == 0) return new XahLeeComplex(Double.NaN,Double.NaN); else return new XahLeeComplex( (re*c.re+im*c.im)/denom, (im*c.re-re*c.im)/denom); } public XahLeeComplex times(double x) { return new XahLeeComplex(re*x, im*x); } public XahLeeComplex plus(double x) { return new XahLeeComplex(re+x, im); } public XahLeeComplex minus(double x) { return new XahLeeComplex(re-x, im); } public XahLeeComplex dividedBy(double x) { return new XahLeeComplex(re/x, im/x); } /** * Returns the absolute value squared of this. * @return real part squared plus imaginary part squared */ public double abs2() { return (re*re + im*im); } /** * Returns the absolute value, "r" in polar coordinates, of this. * @return the square root of (real part squared plus imaginary part squared) */ public double r() { return Math.sqrt(re*re + im*im); } /** * Returns arg(this), the angular polar coordinate of this XahLeeComplex number, in the range -pi to pi. * The return value is simply Math.atan2(imaginary part, real part). */ public double theta() { return Math.atan2(im,re); } /** * Computes the XahLeeComplex exponential function, e^z, where z is this XahLeeComplex number. */ public XahLeeComplex exponential() { double length = Math.exp(re); return new XahLeeComplex( length*Math.cos(im), length*Math.sin(im) ); } /** * Computes the XahLeeComplex reciprocal function, 1/z, where z is this XahLeeComplex number. */ public XahLeeComplex inverse() { double length = re*re+im*im; return new XahLeeComplex( re/length, -im/length ); } public XahLeeComplex log() { double modulus = Math.sqrt(re*re + im*im); double arg = Math.atan2(im,re); return new XahLeeComplex(Math.log(modulus), arg); } /** * Computes that XahLeeComplex logarithm of this XahLeeComplex number * that is nearest to previous. * A test code is in fractals.TestAnalyticContinuation. */ public XahLeeComplex logNearer(XahLeeComplex previous) { XahLeeComplex c = new XahLeeComplex(this.log()); double h = (c.im - previous.im)/(2*Math.PI); double d = (2*Math.PI)*Math.floor(h+0.5); c.im = c.im - d; return c; } public double sinh(double x) { return (Math.exp(x) - Math.exp(-x))/2; } public double cosh(double x) { return (Math.exp(x) + Math.exp(-x))/2; } public XahLeeComplex sine() { double x, y; XahLeeComplex z = new XahLeeComplex(0.0,0.0); x = re; y = im; z.re = Math.sin(x) * cosh(y); z.im = Math.cos(x) * sinh(y); return z; } public XahLeeComplex power(double x) { double modulus = Math.sqrt(re*re + im*im); double arg = Math.atan2(im,re); double log_re = Math.log(modulus); double log_im = arg; double x_log_re = x * log_re; double x_log_im = x * log_im; double modulus_ans = Math.exp(x_log_re); return new XahLeeComplex(modulus_ans*Math.cos(x_log_im), modulus_ans*Math.sin(x_log_im)); } /** * Returns a XahLeeComplex k-th root of this XahLeeComplex number. The root that is returned is * the one with the smallest positive arg. * (If k is 0, the return value is 1. If k is negative, the value is 1/integerRoot(-k).) */ public XahLeeComplex integerRoot(int k) { double a,b; boolean neg = false; if (k < 0) { k = -k; neg = true; } if (k == 0) { a = 1; b = 0; } else if (k == 1) { a = re; b = im; } else { double length = r(); double angle = theta(); if (angle < 0) angle += Math.PI*2; length = Math.pow(length,1.0/k); angle = angle / k; a = length*Math.cos(angle); b = length*Math.sin(angle); } if (neg) { double denom = a*a + b*b; a = a/denom; b = -b/denom; } return new XahLeeComplex(a,b); } /** * Computes that square root of this XahLeeComplex number * that is nearer to previous than to minus previous. * A test code is in fractals.TestAnalyticContinuation. */ public XahLeeComplex squareRootNearer(XahLeeComplex previous) { XahLeeComplex c; c = this.integerRoot(2); if (c.re*previous.re + c.im*previous.im < 0){ c.re=-c.re; c.im=-c.im; } return new XahLeeComplex(c.re, c.im); } public double[] stereographicProjection() { double rsquare,rsquarePlusOne; double [] projPoint = new double[3]; rsquare = re * re + im * im; rsquarePlusOne = rsquare + 1; projPoint[0] = (2 * re)/rsquarePlusOne; projPoint[1] = (2 * im)/rsquarePlusOne; projPoint[2] = (rsquare - 1)/rsquarePlusOne; return projPoint; } }