package habanero.bhp;

/**
 * Rice Habanero Project
 *
 * @author Yonghong Yan (yanyh@rice.edu). 
 */
public class complex64 {
    public static final complex64 ZERO = new complex64(0.0,0.0);
    public static final complex64 ONE = new complex64(1.0,0.0);
    public static final complex64 MINUS_ONE = new complex64(-1.0,0.0);
    public static final complex64 I = new complex64(0.0,1.0);
    public static final complex64 MINUS_I = new complex64(0.0,-1.0);
    public static final complex64 TWO = new complex64(2.0,0.0);
    
    public final double real;
    public final double imag;

    public separable complex64(double re, double im) {
        this.real = re;
        this.imag = im;
    }

    public separable complex64(double re) {
        this.real = re;
        this.imag = 0.0;
    }

    public separable complex64() {
        this.real = this.imag = 0.0;
    }

    public separable complex64(complex64 c) {
        this.real = c.real;
        this.imag = c.imag;
    }

    /**
     * add in the other complex64 and return myself
     * @param other
     * @return
     */
    public separable complex64 add(complex64 other) {
        return new complex64(this.real + other.real, this.imag + other.imag);
    }

    /**
     * add the complex64 with a double scalar and return myself
     * @param off
     * @return
     */
    public separable complex64 add(double off) {
        return new complex64(this.real + off, this.imag);
    }

    /**
     * multiply the complex64 by other and return myself
     * @param other
     * @return
     */
    public separable complex64 mult(complex64 other) {
        double a = this.real;
        double b = this.imag;
        double c = other.real;
        double d = other.imag;
        return new complex64(a*c - b*d, b*c + a*d);
    }

    /**
     * mulitiply the complex64 by the scalar and return myself
     * @param dd
     * @return
     */
    public separable complex64 mult(double dd) {
        return new complex64(this.real * dd,
                           this.imag * dd);
    }

    /**
     * substract right from the complex64 and return myself
     * @param right
     * @return
     */
    public separable complex64 sub(complex64 right) {
        return new complex64(this.real - right.real,
                           this.imag - right.imag);
    }

    /**
     * substract a double scalar from the complex64 and return myself
     * @param ss
     * @return
     */
    public separable complex64 sub(double ss) {
        return new complex64(this.real - ss, this.imag);
    }


    /**
     * divide the complex64 by the right one and return myself
     * @param right
     * @return
     */
    public separable complex64 div(complex64 right) {
        double a = this.real;
        double b = this.imag;
        double c = right.real;
        double d = right.imag;
        return new complex64((a*c+b*d)/(c*c+d*d),
                           (b*c-a*d)/(c*c+d*d));
    }

    /**
     * divide the complex64 by the double scalar and return myself
     * @param dd
     * @return
     */
    public separable complex64 div(double dd) {
        return new complex64(this.real / dd,
                           this.imag / dd);
    }

    public separable complex64 minus() {
        return new complex64(-this.real, -this.imag);
    }

    public double abs() { // NOTE: not separable because StrictMath.sqrt is not separable
        return StrictMath.sqrt(real*real + imag*imag);
    }

    public separable boolean equals(complex64 other) {
        return (this.real==other.real && this.imag==other.imag);
    }

    public separable String output() {
        return "{" + real + ", " + imag + "}";
    }

    public separable String toString() {
        return output();
    }

    /**
     * @return
     */
    public complex64 sqrt() { // NOTE: not separable because StrictMath.sqrt is not separable
        double a = real;
        double b = imag;
        if (b==0.0) return new complex64(StrictMath.sqrt(a), 0.0);

        double p = 1.0/StrictMath.sqrt(2.0) * StrictMath.sqrt(a + StrictMath.sqrt(a*a+b*b));
        double q = b/(2.0*p);
        return new complex64(p, q);
    }

    /**
     * @return
     */
    public complex64 exp() { // NOTE: not separable because StrictMath.exp, .cos and .sin are not separable
        double p = StrictMath.exp(this.real)*StrictMath.cos(this.imag);
        double q = StrictMath.exp(this.real)*StrictMath.sin(this.imag);
        return new complex64(p, q);
    }

    // why error here?
    public separable complex64 square() {
        return this.mult(this);
    }

    public static void main(String argv[]) {
        complex64 t = new complex64(3.1415926, 2.14184590);

        complex64 tt = t.square();
        complex64 ttt = t.sqrt();
        complex64 tttt = t.exp();

        System.out.println(t + "  " + tt + "  " + ttt + "  " + tttt + "  " + t.abs() + "  " + t.mult(tttt));
    }
}
