/*
    complex.js  -  Don Cross, February 2005.
*/

function zcomplex (real, imag)
{
    var z = new Object();
    z.x = real;
    z.y = imag;
    return z;
}

function zscale (scalar, a)
{
    return zcomplex (scalar * a.x, scalar * a.y);
}

function zshrink (a, scalar)
{
    return zcomplex (a.x / scalar, a.y / scalar);
}

function zneg (a)
{
    return zcomplex (-a.x, -a.y);
}


function zadd (a, b)
{
    return zcomplex (a.x + b.x, a.y + b.y);
}


function zsub (a, b)
{
    return zcomplex (a.x - b.x, a.y - b.y);
}


function zdiv (a, b)
{
    /*
        a.x + i(a.y)     a.x + i(a.y)   b.x - i(b.y)      (a.x b.x + a.y b.y) + (a.y b.x - a.x b.y)
        ------------  =  ------------ * ------------  =   -----------------------------------------
        b.x + i(b.y)     b.x + i(b.y)   b.x - i(b.y)                  (b.x)^2 + (b.y)^2
    */

    var denom = (b.x * b.x) + (b.y * b.y);
    return zcomplex (((a.x * b.x) + (a.y * b.y)) / denom, ((a.y * b.x) - (a.x * b.y)) / denom);
}


function zmul (a, b)
{
    return zcomplex ((a.x * b.x) - (a.y * b.y), (a.y * b.x) + (a.x * b.y));
}


function zsquare (a)
{
    return zmul (a, a);
}


function zcube (a)
{
    return zmul (a, zmul(a,a));
}


function zmag (a)
{
    return Math.sqrt ((a.x * a.x) + (a.y * a.y));
}


function atan2 (y, x)
{
    var a = 0;
    if ( x == 0 ) {
        if ( y >= 0 ) {
            a = Math.PI / 2;
        } else {
            a = -Math.PI / 2;
        }
    } else {
        a = Math.atan(y/x);
        if ( x < 0 ) {
            a += Math.PI;
        }
    }
    return a;
}


function zang (a)
{
    return atan2 (a.y, a.x);
}


function zsqrt (a)
{
    var z = new Object();

    if (a.y == 0) {     // use simpler method for real numbers, to avoid division by zero
        if (a.x < 0) {
            z.x = 0;
            z.y = Math.sqrt (-a.x);
        } else {
            z.x = Math.sqrt (a.x);
            z.y = 0;
        }
    } else {
        var r = a.x + zmag(a);      // since the imaginary part is not zero, we know r can't be zero.
        if (r < 0) {
            r = -r;
            z.x = a.y / Math.sqrt(2*r);     // division is safe because r != 0
            z.y = Math.sqrt (r/2);
        } else {
            z.x = Math.sqrt (r/2);
            z.y = a.y / Math.sqrt (2*r);    // division is safe because r != 0
        }
    }

    return z;
}


function zcbrt (a, n)
{
    /*
        This function returns one of the 3 complex cube roots of the complex number 'a'.
        The value of n=0..2 selects which root is returned.
    */

    var rho   = Math.pow (zmag(a), 1.0/3.0);
    var theta = ((2*Math.PI*n) + zang(a)) / 3.0;
    return zcomplex (rho * Math.cos(theta), rho * Math.sin(theta));
}


function IsZero (z)
{
    return (z.x == 0) && (z.y == 0);
}


/*
    $Log: complex.js,v $
    Revision 1.4  2005/02/12 20:25:22  dcross
    1. Made coding style consistent:  use semicolons to terminate statements in atan2().
    2. Removed redundant copy of IsZero() from solver.html.  (Might break code on some browsers.)

    Revision 1.3  2005/02/12 17:53:39  dcross
    1. Reworked complex.js to use zcomplex() pseudo-constructor, thus reducing code size.
    2. Fixed division-by-zero problem in zsqrt.  Doh!
    3. Added zshrink, which is more convenient for dividing a complex number by a real number.
    4. The equation solver now removes redundant solutions.  See new function PostProcess().

    Revision 1.2  2005/02/11 21:44:34  dcross
    Added zcomplex() function to create a complex constant.

    Revision 1.1  2005/02/11 21:19:25  dcross
    Factored out complex stuff into its own module

*/
