public class Complex extends Vector2D {
    
    public Complex() { super(); }
    
    public Complex(double re, double im) { super(re, im); }

    public Complex(Complex template) { super((Vector2D) template); }

    public Complex(double x) { super(x, 0.0); }

    public double real() { return x; }

    public double imaginary() { return y; }

    public double re() { return x; }
    
    public double im() { return y; }

    public void real(double value) { x = value; }
    
    public void imaginary(double value) { y = value; }

    public void re(double value) { x = value; }
    
    public void im(double value) { y = value; }

    public double abs() { return length(); }

    public double arg() { return angle(); }

    public void add(double a) { x += a; }

    public void subtract(double a) { x -= a; }

    public void times(double a) { scale(a); }

    public void times(Complex c) { math('*', c); }

    public Complex product(Complex a, Complex b) { return math(a, '*', b); }

    public void over(double a) { scale(1.0 / a); }

    public void over(Complex c) { math('/', c); }

    public Complex fraction(Complex a, Complex b) { return math(a, '/', b); }
    
    public void conjugate() { y *= -1; }

    public static Complex conjugate(Complex arg) {
	Complex c = new Complex(arg);
	c.conjugate();
	return c;
    }


    public void inverse() { 
	double norm = norm();
	conjugate();
	scale(1.0 / norm());
    }

    public static Complex inverse(Complex arg) {
	Complex c = new Complex(arg);
	c.inverse();
	return c;
    }


    public void pow(double b) {
	double r = length();
	double phi = angle();

	r = Math.pow(r, b);
	phi *= b;

	x = r * Math.cos(phi);
	y = r * Math.sin(phi);
    }
    
    public static Complex pow(Complex arg, double exp) {
	Complex c = new Complex(arg);
	c.pow(exp);
	return c;
    }


    public void sqrt() { pow(0.5); }

    public static Complex sqrt(Complex arg) {
	Complex c = new Complex(arg);
	c.sqrt();
	return c;
    }	

    public void exp() {
	double r = Math.exp(x);
	x = r * Math.cos(y);
	y = r * Math.sin(y);
    }	

    public static Complex exp(Complex arg) {
	Complex c = new Complex(arg);
	c.exp();
	return c;
    }


    public void log() {
	double r = length();
	x = Math.log(r);
	y = angle();
    }

    public static Complex log(Complex arg) {
	Complex c = new Complex(arg);
	c.log();
	return c;
    }


    public void cos() {
	Complex e = math(i, '*', this); 
	e.exp();
	e.add(inverse(e));
	e.math('/', 2.0);
    }

    public static Complex cos(Complex arg) {
	Complex c = new Complex(arg);
	c.cos();
	return c;
    }

    public void sin() {
	Complex e = math(i, '*', this);
	e.exp();
	e.subtract(inverse(e));
	e.math('/', new Complex(0.0, 2.0));
    }

    public static Complex sin(Complex arg) {
	Complex c = new Complex(arg);
	c.sin();
	return c;
    }

    public void tan() {
	Complex c = cos(this);
	sin();
	math('/', c);
    }

    public static Complex tan(Complex arg) {
	Complex c = new Complex(arg);
	c.tan();
	return c;
    }


    public void cosh() {
	Complex e = exp(this);
	e.add(inverse(e));
	e.math('/', 2.0);
    }

    public static Complex cosh(Complex arg) {
	Complex c = new Complex(arg);
	c.cos();
	return c;
    }

    public void sinh() {
	Complex e = exp(this);
	e.subtract(inverse(e));
	e.math('/', new Complex(0.0, 2.0));
    }

    public static Complex sinh(Complex arg) {
	Complex c = new Complex(arg);
	c.sin();
	return c;
    }

    public void tanh() {
	Complex c = cos(this);
	sin();
	math('/', c);
    }

    public static Complex tanh(Complex arg) {
	Complex c = new Complex(arg);
	c.tan();
	return c;
    }
    

    public void math(char op, Complex b) throws IllegalArgumentException {
	double ax;

	switch(op) {
	case '*': 
	    ax = x;
	    x = ax * b.x - y * b.y;
	    y = y * b.x + ax * b.y;
	    break;
	case '/': 
	    ax = x;
	    x = ax * b.x - y * b.y;
	    y = y * b.x - ax * b.y;
	    scale(1.0 / b.norm());
	    break;
	default: 
	    super.math(op, b);
	}
    }


    public static Complex math(Complex a, char op, Complex b) throws IllegalArgumentException {
	Complex result = new Complex();

	switch(op) {
	case '*': 
	    result.x = a.x * b.x - a.y * b.y; 
	    result.y = a.y * b.x + a.x * b.y; 
	    break;
	case '/': 
	    result.x = a.x * b.x - a.y * b.y; 
	    result.y = a.y * b.x - a.x * b.y; 
	    result.scale(1.0 / b.norm());
	    break;
	default: 
	    Vector2D v = Vector2D.math(a, op, b);
	    result.x = v.x; result.y = v.y;
	}

	return result;
    }

    
    public void math(char op, double b) throws IllegalArgumentException {
	switch(op) {
	case '+': x += b; break;
	case '-': x -= b; break;
	case '^': pow(b); break;	    
	default: super.math(op, b);
	}
    }

    public static Complex math(Complex a, char op, double b) throws IllegalArgumentException {
	Complex result = new Complex(a);

	switch(op) {
	case '+': result.x = a.x + b; result.y = a.y; break;
	case '-': result.x = a.x - b; result.y = a.y; break;
	case '^': result.pow(b); break;	    
	default: 
	    Vector2D v = Vector2D.math(a, op, b);
	    result.x = v.x; result.y = v.y;	    
	}

	return result;
    }

    public static Complex math(double a, char op, Complex b) throws IllegalArgumentException {
	if(op == '*' || op == '+') return math(b, op, a);
	return math(new Complex(a), op, b);
    }

    public String toString() { return x + (y < 0 ? "" : "+") + y + "i"; }

    final static Complex i = new Complex(0.0, 1.0);
}
