function Complex(re, im) {
    this.re = re;
    this.im = im;
};

// a + b
Complex.add = function (a, b) {
    return new Complex(a.re + b.re, a.im + b.im);
};

// a - b
Complex.sub = function (a, b) {
    return new Complex(a.re - b.re, a.im - b.im);
};

// a * b
Complex.mult = function(a, b) {
    return new Complex(a.re * b.re - a.im * b.im, a.re * b.im + a.im * b.re);
};

// cartesian -> polar
Complex.car2pol = function(c) {
	return new Complex(Complex.abs(c), Math.atan2(c.im,c.re));
};

// polar -> cartesian
Complex.pol2car = function(c) {
	return new Complex(c.re*Math.cos(c.im), c.re*Math.sin(c.im));
};

// a / b
Complex.div = function(a, b) {
	a = Complex.car2pol(a);
	b = Complex.car2pol(b);
	var c = new Complex(a.re/b.re, a.im-b.im);
    return Complex.pol2car(c);
};

// sin(x+iy) = sin(x)*cosh(y) + i*cos(x)*sinh(y)
Complex.sin = function(c) {
	return new Complex(Math.sin(c.re)*cosh(c.im), Math.cos(c.re)*sinh(c.im));
};

// cos(x+iy) = cos(x)*cosh(y) - i*sin(x)*sinh(y)
Complex.cos = function(c){
	return new Complex(Math.cos(c.re)*cosh(c.re), -1*Math.sin(c.re)*sinh(c.im));
};

// complex conjugate
Complex.conj = function(c){
	return new Complex(c.re, -1*c.im);
};

// abs
Complex.abs = function(c) {
    return Math.sqrt(c.re*c.re + c.im*c.im);
};

// abs^2
Complex.abs2 = function(c) {
    return c.re*c.re + c.im*c.im;
};

// sqrt
Complex.sqrt = function(c) {
	c = Complex.car2pol(c);	
	c.re = Math.sqrt(c.re);
	c.im = c.im/2;
	return Complex.pol2car(c);
};

// a^b
Complex.pow = function(a,b) {
	var c = Complex.car2pol(a);
	c.re = Math.pow(c.re, b);
	c.im = c.im * b;
	return Complex.pol2car(c);
};

// negative
Complex.prototype.neg = function() {
    return new Complex(-c.re, -c.im);
};

// to string
Complex.prototype.toString = function() {
    return "{" + c.re + "," + c.im + "}";
};

// misc complex constants
Complex.zero = new Complex(0,0);
Complex.one = new Complex(1,0);
Complex.i = new Complex(0,1);

