/*
 *    CCL (Complex C Library) - part of Mathlib
 */

#include "mathlib.constants"
#include "mathlib.errors"

typedef struct complex {
    double real;
    double imag;
} COMPLEX;

/*************************************************************************
 *    Cadd(z1,z2) = z1 + z2
 */
COMPLEX *Cadd(z1,z2)
COMPLEX *z1,*z2;
{
    static COMPLEX ztmp;

    ztmp.real = z1->real + z2->real;
    ztmp.imag = z1->imag + z2->imag;
    return(&ztmp);
}

/*************************************************************************
 *    Csub(z1,z2) = z1 - z2
 */
COMPLEX *Csub(z1,z2)
COMPLEX *z1,*z2;
{
    static COMPLEX ztmp;

    ztmp.real = z1->real - z2->real;
    ztmp.imag = z1->imag - z2->imag;
    return(&ztmp);
}

/*************************************************************************
 *    Cmult(z1,z2) = z1 * z2
 */
COMPLEX *Cmult(z1,z2)
COMPLEX *z1,*z2;
{
    static COMPLEX ztmp;

    ztmp.real = z1->real * z2->real - z1->imag * z2->imag;
    ztmp.imag = z1->real * z2->imag + z2->real * z1->imag;
    return(&ztmp);
}

/*************************************************************************
 *    Cmultd(z,d) = z * d
 */
COMPLEX *Cmultd(z,d)
COMPLEX *z;
double d;
{
    static COMPLEX ztmp;

    ztmp.real = z->real * d;
    ztmp.imag = z->imag * d;
    return(&ztmp);
}

/*************************************************************************
 *    Cscale(z,d1,d2) = real * d1 + j imag * d2
 *    where z = real + j imag
 */
COMPLEX *Cscale(z,d1,d2)
COMPLEX *z;
double d1,d2;
{
    static COMPLEX ztmp;

    ztmp.real = z->real * d1;
    ztmp.imag = z->imag * d2;
    return(&ztmp);
}

/*************************************************************************
 *    Cdiv(z1,z2) = z1 / z2
 */
COMPLEX *Cdiv(z1,z2)
COMPLEX *z1,*z2;
{
    static COMPLEX ztmp;

    double den,r;
    double absr,absi;
    double Dabs();

    absr = Dabs(z2->real);
    absi = Dabs(z2->imag);

    if (z1->real == 0.0 && z1->imag == 0.0) {
        ztmp.real = 0.0;
        ztmp.imag = 0.0;
    } else if (z2->real == 0.0 && z2->imag == 0.0) {
        ccl_error = ZERO_DIVIDE;
        ztmp.real = 0.0;
        ztmp.imag = 0.0;
    } else if (absr >= absi) {
        r = z2->imag / z2->real;
        den = z2->real + r * z2->imag;
        ztmp.real = (z1->real + z1->imag * r) / den;
        ztmp.imag = (z1->imag - z1->real * r) / den;
    } else {
        r = z2->real / z2->imag;
        den = z2->imag + r * z2->real;
        ztmp.real = (z1->real * r + z1->imag) / den;
        ztmp.imag = (z1->imag * r - z1->real) / den;
    }
    return(&ztmp);
}

/*************************************************************************
 *    Cdivd(z1,d) = z / d
 */
COMPLEX *Cdivd(z,d)
COMPLEX *z;
double d;
{
    static COMPLEX ztmp;

    if (d == 0.0) {
        ccl_error = ZERO_DIVIDE;
        ztmp.real = 0.0;
        ztmp.imag = 0.0;
    } else if (z->real == 0.0 && z->imag == 0.0) {
        ztmp.real = 0.0;
        ztmp.imag = 0.0;
    } else {
        ztmp.real = z->real / d;
        ztmp.imag = z->imag / d;
    }
    return(&ztmp);
}

/*************************************************************************
 *    Crcp(z) = 1 / z
 */
COMPLEX *Crcp(z)
COMPLEX *z;
{
    static COMPLEX ztmp;

    double denom;

    if (z->real == 0.0 && z->imag == 0.0) {
        ccl_error = ZERO_DIVIDE;
        ztmp.real = 0.0;
        ztmp.imag = 0.0;
    } else if (z->imag == 0.0) {
        ztmp.real = 1.0 / z->real;
        ztmp.imag = 0.0;
    } else if (z->real == 0.0) {
        ztmp.real = 0.0;
        ztmp.imag = -1.0 / z->imag;
    } else {
        denom = z->real * z->real + z->imag * z->imag;
        ztmp.real =   z->real / denom;
        ztmp.imag = - z->imag / denom;
    }
    return(&ztmp);
}

/*************************************************************************
 *    Cpow(z1,z2) = z1 ^ z2
 */
COMPLEX *Cpow(z1,z2)
COMPLEX *z1,*z2;
{
    static COMPLEX ztmp;
    double Datan(),Dln(),Dsin(),Dcos(),Dexp(),Dsign();
    double p,r,v,w,sign;

    sign = Dsign(z1->imag);

    if (z1->real == 0.0) {
        if (z1->imag == 0.0) {
            ztmp.real = 0.0;
            ztmp.imag = 0.0;
            return(&ztmp);
        } else
            p = PID2 * sign;
    }
    else {
        p = Datan(z1->imag/z1->real);
        if (z1->real < 0.0) {
            if (z1->imag >= 0.0)
                p += PI;
            else
                p -= PI;
        }
    }
    r = 0.5 * Dln(z1->real * z1->real + z1->imag * z1->imag);
    v = z2->real * p + z2->imag * r;
    w = Dexp(z2->real * r - z2->imag * p);

    ztmp.real = w * Dcos(v);
    ztmp.imag = w * Dsin(v);
    return(&ztmp);
}

/*************************************************************************
 *    Cpowd(z,d) = z ^ d
 */
COMPLEX *Cpowd(z,d)
COMPLEX *z;
double d;
{
    static COMPLEX ztmp;
    double Cabs();
    double Datan(),Dln(),Dcos(),Dsin(),Dexp(),Dabs();

    double phi,r,absr,absi,sqrr,sqri;

    if (z->real > 0.0)
        phi = Datan(z->imag/z->real);
    else if (z->real < 0.0 && z->imag >= 0.0)
        phi = Datan(z->imag/z->real) + PI;
    else if (z->real < 0.0 && z->imag < 0.0)
        phi = Datan(z->imag/z->real) - PI;
    else if (z->real == 0.0 && z->imag == 0.0) {
        ztmp.real = 0.0;
        ztmp.imag = 0.0;
        return(&ztmp);
    } else if (z->real == 0.0 && z->imag > 0.0)
        phi = PID2;
    else if (z->real == 0.0 && z->imag < 0.0)
        phi = -PID2;

    absr = Dabs(z->real);
    absi = Dabs(z->imag);

    r = Cabs(z);
    r = Dexp(d * Dln(r));
    phi = d * phi;

    ztmp.real = r * Dcos(phi);
    ztmp.imag = r * Dsin(phi);
    return(&ztmp);
}

/*************************************************************************
 *    Csqrt(z) = u (+-) jv
 *    where u = sqrt(0.5*(root+x)) and v = sqrt(0.5*(root-x))
 *    and root is the magnitude of z
 *    the sign is the same as that of the imaginary part of z
 */
COMPLEX *Csqrt(z)
COMPLEX *z;
{
    static COMPLEX ztmp;
    double Cabs();
    double Dsqrt();
    double root;

    if (z->real == 0.0 && z->imag == 0.0) {
        ztmp.real = 0.0;
        ztmp.imag = 0.0;
    } else if (z->imag == 0.0) {
        ztmp.real = Dsqrt(z->real);
        ztmp.imag = 0.0;
    } else {
        root = Cabs(z);
        ztmp.real = Dsqrt(0.5 * (root + z->real));
        ztmp.imag = Dsqrt(0.5 * (root - z->real));
        if (z->imag < 0.0)
            ztmp.imag = - ztmp.imag;
    }
    return(&ztmp);
}

/*************************************************************************
 *    Cln(z) = ln( Cabs(z) ) + j Carg(z)
 *    where z = real + j imag
 */
COMPLEX *Cln(z)
COMPLEX *z;
{
    static COMPLEX ztmp;
    double Cabs(),Carg();
    double Dln();

    if (z->imag == 0.0 && z->real > 0.0) {
        ztmp.real = Dln(z->real);
        ztmp.imag = 0.0;
    } else if (z->real == 0.0) {
        if (z->imag > 0.0) {
            ztmp.real = Dln(z->imag);
            ztmp.imag = PID2;
        } else {
            ztmp.real = Dln(-(z->imag));
            ztmp.imag = - PID2;
        }
    } else {
        ztmp.real = Dln(Cabs(z));
        ztmp.imag = Carg(z);
    }
    return(&ztmp);
}

/*************************************************************************
 *    Clogd(z,d) = ln(z) / ln(d)
 */
COMPLEX *Clogd(z,d)
COMPLEX *z;
double d;
{
    static COMPLEX ztmp;
    COMPLEX *Cln();
    double Dln();
    double lnd;

    lnd = Dln(d);
    ztmp = *Cln(z);
    ztmp.real /= lnd;
    ztmp.imag /= lnd;
    return(&ztmp);
}

/*************************************************************************
 *    Cexp(z) = exp(real) cos(imag) + j( exp(real) sin(imag) )
 *    where z = real + j imag
 */
COMPLEX *Cexp(z)
COMPLEX *z;
{
    static COMPLEX ztmp;
    double Dsin(),Dcos(),Dexp();
    double r;

    if (z->real == 0.0 && z->imag == 0.0) {
        ztmp.real = 1.0;
        ztmp.imag = 0.0;
    } else if (z->imag == 0.0) {
        ztmp.real = Dexp(z->real);
        ztmp.imag = 0.0;
    } else if (z->real == 0.0) {
        ztmp.real = Dcos(z->imag);
        ztmp.imag = Dsin(z->imag);
    } else {
        r = Dexp(z->real);
        ztmp.real = r * Dcos(z->imag);
        ztmp.imag = r * Dsin(z->imag);
    }
    return(&ztmp);
}

/*************************************************************************
 *    Cconj(z) = real - j imag
 *    where z = real + j imag
 */
COMPLEX *Cconj(z)
COMPLEX *z;
{
    static COMPLEX ztmp;

    ztmp.real = z->real;
    ztmp.imag = - z->imag;
    return(&ztmp);
}

/*************************************************************************
 *    Cxj(z) = j z
 */
COMPLEX *Cxj(z)
COMPLEX *z;
{
    static COMPLEX ztmp;

    ztmp.real = - z->imag;
    ztmp.imag = z->real;
    return(&ztmp);
}

/*************************************************************************
 *    Crotate(z,d) = r exp[ j (phi + d) ]
 */
COMPLEX *Crotate(z,d)
COMPLEX *z;
double d;
{
    static COMPLEX ztmp;
    double Dsin(),Dcos();
    double sind,cosd;

    sind = Dsin(d);
    cosd = Dcos(d);
    ztmp.real = z->real * cosd - z->imag * sind;
    ztmp.imag = z->real * sind + z->imag * cosd;
    return(&ztmp);
}

/*************************************************************************
 *    Cmax(z1,z2) = max(z1,z2)
 */
COMPLEX *Cmax(z1,z2)
COMPLEX *z1,*z2;
{
    static COMPLEX ztmp;
    double abs1,abs2;

    abs1 = z1->real * z1->real + z1->imag * z1->imag;
    abs2 = z2->real * z2->real + z2->imag * z2->imag;
    if (abs1 <= abs2) {
        ztmp.real = z2->real;
        ztmp.imag = z2->imag;
    } else {
        ztmp.real = z1->real;
        ztmp.imag = z1->imag;
    }
    return(&ztmp);
}

/*************************************************************************
 *    Cmin(z1,z2) = min(z1,z2)
 */
COMPLEX *Cmin(z1,z2)
COMPLEX *z1,*z2;
{
    static COMPLEX ztmp;
    double abs1,abs2;

    abs1 = z1->real * z1->real + z1->imag * z1->imag;
    abs2 = z2->real * z2->real + z2->imag * z2->imag;
    if (abs1 >= abs2) {
        ztmp.real = z2->real;
        ztmp.imag = z2->imag;
    } else {
        ztmp.real = z1->real;
        ztmp.imag = z1->imag;
    }
    return(&ztmp);
}

/*************************************************************************
 *    Cswap(z1,z2), swaps z1 and z2
 */
int Cswap(z1,z2)
COMPLEX *z1,*z2;
{
    COMPLEX temp;

    temp.real = z2->real;
    temp.imag = z2->imag;
    z2->real = z1->real;
    z2->imag = z1->imag;
    z1->real = temp.real;
    z1->imag = temp.imag;
}

/*************************************************************************
 *    Creal(z) = real
 *    where z = real + j imag
 */
double Creal(z)
COMPLEX *z;
{
    return(z->real);
}

/*************************************************************************
 *    Cimag(z) = imag
 *    where z = real + j imag
 */
double Cimag(z)
COMPLEX *z;
{
    return(z->imag);
}

/*************************************************************************
 *    Cabs(z) = sqrt( real^2 + imag^2 )
 *    where z = real + j imag
 */
double Cabs(z)
COMPLEX *z;
{
    double Dsqrt(),Dabs();
    double absr,absi,sqrr,sqri;

    absr = Dabs(z->real);
    absi = Dabs(z->imag);

    if (absr == 0.0)
        return(absi);
    else if (absi == 0.0)
        return(absr);
    else if (absr > absi) {
        sqrr = absr * absr;
        sqri = absi * absi;
        return(absr * Dsqrt(1 + sqri/sqrr));
    } else {
        sqrr = absr * absr;
        sqri = absi * absi;
        return(absi * Dsqrt(1 + sqrr/sqri));
    }
}

/*************************************************************************
 *    Carg(z) Return the angle of z to the positive real axis
 */
double Carg(z)
COMPLEX *z;
{
    double Datan(),Dsign();
    double sign;

    sign = Dsign(z->imag);

    if (z->real > 0.0)
        return(Datan(z->imag / z->real));
    else if (z->real < 0.0) {
        if (z->imag != 0.0)
            return(Datan(z->imag / z->real) + sign * PI);
        else
            return(-PI);
    } else {
        if (z->imag != 0.0)
            return(sign * PID2);
        else
            return(0.0);
    }
}

/*************************************************************************
 *    Cmplx(d1,d2) = z
 *    where z = d1 + j d2
 */
COMPLEX *Cmplx(d1,d2)
double d1,d2;
{
    static COMPLEX ztmp;

    ztmp.real = d1;
    ztmp.imag = d2;
    return(&ztmp);
}

/*************************************************************************
 *    Cmake(d1,d2) = z
 *    where z + d1 ( cos(d2) + j sin(d2) )
 */
COMPLEX *Cmake(d1,d2)
double d1,d2;
{
    double Dsin(),Dcos();
    static COMPLEX ztmp;

    ztmp.real = d1 * Dcos(d2);
    ztmp.imag = d1 * Dsin(d2);
    return(&ztmp);
}

/*************************************************************************
 *    Csin(z) = sin(real) cosh(imag) + j cos(real) sinh(imag)
 *    where z = real + j imag
 */
COMPLEX *Csin(z)
COMPLEX *z;
{
    static COMPLEX ztmp;
    double Dsin(),Dcos(),Dsinh(),Dcosh();

    if (z->imag == 0.0) {
        ztmp.real = Dsin(z->real);
        ztmp.imag = 0.0;
    } else {
        ztmp.real = Dsin(z->real) * Dcosh(z->imag);
        ztmp.imag = Dcos(z->real) * Dsinh(z->imag);
    }
    return(&ztmp);
}

/*************************************************************************
 *    Ccos(z) = cos(real) cosh(imag) - j sin(real) sinh(imag)
 */
COMPLEX *Ccos(z)
COMPLEX *z;
{
    static COMPLEX ztmp;
    double Dsin(),Dcos(),Dsinh(),Dcosh();

    if (z->imag == 0.0) {
        ztmp.real = Dcos(z->real);
        ztmp.imag = 0.0;
    } else {
        ztmp.real = Dcos(z->real) * Dcosh(z->imag);
        ztmp.imag = - Dsin(z->real) * Dsinh(z->imag);
    }
    return(&ztmp);
}

/*************************************************************************
 *    Ctan(z) = ( sin(2*real) + jsinh(2*imag) )
 *              -------------------------------
 *              ( cos(2*real) + cosh(2*imag) )
 *    where z = real + j imag
 */
COMPLEX *Ctan(z)
COMPLEX *z;
{
    static COMPLEX ztmp;
    double Dtan(),Dcos(),Dsin(),Dsinh(),Dcosh();
    double denom,real2,imag2;

    if (z->imag == 0.0) {
        ztmp.real = Dtan(z->real);
        ztmp.imag = 0.0;
    } else {
        real2 = 2.0 * z->real;
        imag2 = 2.0 * z->imag;
        denom = Dcos(real2) + Dcosh(imag2);
        ztmp.real = Dsin(real2) / denom;
        ztmp.imag = Dsinh(imag2) / denom;
    }
    return(&ztmp);
}

/*************************************************************************
 *    Casin(z) = k*pi + (-1)^k asin(b)
 *                    + j (-1)^k ln(a + sqrt(a^2 - 1))
 *    where a = 0.5 sqrt((x+1)^2 + y^2) + 0.5 sqrt((x-1)^2 + y^2)
 *    and   b = 0.5 sqrt((x+1)^2 + y^2) - 0.5 sqrt((x-1)^2 + y^2)
 *    and z = x + jy, k an integer
 */
COMPLEX *Casin(z)
COMPLEX *z;
{
    static COMPLEX ztmp;
    double Dsqrt(),Dasin(),Dln();
    double a,b;
    double xm1,xp1,x2,y2;
    double part1,part2;

    if (z->imag == 0.0) {
        ztmp.real = Dasin(z->real);
        ztmp.imag = 0.0;
    } else {
        x2 = z->real * z->real;
        y2 = z->imag * z->imag;
        xp1 = x2 + 2.0 * z->real + 1.0;
        xm1 = x2 - 2.0 * z->real + 1.0;
        part1 = 0.5 * Dsqrt(xp1 + y2);
        part2 = 0.5 * Dsqrt(xm1 + y2);
        a = part1 + part2;
        b = part1 - part2;
        ztmp.real = Dasin(b);
        ztmp.imag = Dln( a + Dsqrt(a * a - 1.0) );
    }
    return(&ztmp);
}

/*************************************************************************
 *    Cacos(z) = 2*k*pi (+-) [ acos(b)
 *                        - j ln(a + sqrt(a^2 - 1))
 *    where a = 0.5 sqrt((x+1)^2 + y^2) + 0.5 sqrt((x-1)^2 + y^2)
 *    and   b = 0.5 sqrt((x+1)^2 + y^2) - 0.5 sqrt((x-1)^2 + y^2)
 *    and   z = x + jy, K an integer.
 */
COMPLEX *Cacos(z)
COMPLEX *z;
{
    static COMPLEX ztmp;
    double Dsqrt(),Dacos(),Dln(),Dsqrt();
    double a,b;
    double xm1,xp1,x2,y2;
    double part1,part2;

    if (z->imag == 0.0) {
        ztmp.real = Dacos(z->real);
        ztmp.imag = 0.0;
    } else {
        x2 = z->real * z->real;
        y2 = z->imag * z->imag;
        xp1 = x2 + 2.0 * z->real + 1.0;
        xm1 = x2 - 2.0 * z->real + 1.0;
        part1 = 0.5 * Dsqrt(xp1 + y2);
        part2 = 0.5 * Dsqrt(xm1 + y2);
        a = part1 + part2;
        b = part1 - part2;
        ztmp.real = Dacos(b);
        ztmp.imag = - Dln( a + Dsqrt(a*a - 1.0) );
    }
    return(&ztmp);
}

/*************************************************************************
 *    Catan(z) = k*pi + 0.5 * atan(2x/(1-x^2-y^2))
 *                    + j/4 * ln (( x^2+(y+1)^2) / ( x^2+(y-1)^2))
 *    where z = x + jy, K an integer.
 */
COMPLEX *Catan(z)
COMPLEX *z;
{
    static COMPLEX ztmp;
    double Datan(),Dln();
    double ym1,yp1,x2,y2,denom;

    if (z->imag == 0.0) {
        ztmp.real = Datan(z->real);
        ztmp.imag = 0.0;
    } else {
        x2 = z->real * z->real;
        y2 = z->imag * z->imag;
        denom = 1.0 - x2 - y2;
        yp1 = x2 + y2 + 2.0 * z->imag + 1.0;
        ym1 = x2 + y2 - 2.0 * z->imag + 1.0;
        ztmp.real = 0.5 * Datan( 2.0 * z->real / denom );
        ztmp.imag = 0.25 * Dln( yp1 / ym1 );
    }
    return(&ztmp);
}

/*************************************************************************
 *    Csinh(z) = 0.5 ( Cexp(z) - Cexp(-z) )
 */
COMPLEX *Csinh(z)
COMPLEX *z;
{
    static COMPLEX ztmp;
    COMPLEX *Cexp();
    double Dsinh();
    COMPLEX mz,zt1,zt2;

    if (z->imag == 0.0) {
        ztmp.real = Dsinh(z->real);
        ztmp.imag = 0.0;
    } else {
        mz.real = - z->real;
        mz.imag = - z->imag;
        zt1 = *Cexp(z);
        zt2 = *Cexp(&mz);
        ztmp.real = 0.5 * (zt1.real - zt2.real );
        ztmp.imag = 0.5 * (zt1.imag - zt2.imag );
    }
    return(&ztmp);
}

/*************************************************************************
 *    Ccosh(z) = 0.5 ( Cexp(z) + Cexp(-z) )
 */
COMPLEX *Ccosh(z)
COMPLEX *z;
{
    static COMPLEX ztmp;
    COMPLEX *Cexp();
    COMPLEX mz,zt1,zt2;
    double Dcosh();

    if (z->imag == 0.0) {
        ztmp.real = Dcosh(z->real);
        ztmp.imag = 0.0;
    } else {
        mz.real = - z->real;
        mz.imag = - z->imag;
        zt1 = *Cexp(z);
        zt2 = *Cexp(&mz);
        ztmp.real = 0.5 * ( zt1.real + zt2.real );
        ztmp.imag = 0.5 * ( zt1.imag + zt2.imag );
    }
    return(&ztmp);
}

/*************************************************************************
 *    Ctanh(z) = ( 1 - Cexp(-2z) ) / ( 1 + Cexp(-2z) )
 */
COMPLEX *Ctanh(z)
COMPLEX *z;
{
    static COMPLEX ztmp;
    COMPLEX zt1,zt2,num,denom;
    COMPLEX *Cdiv(),*Cexp();
    double Dtanh();

    if (z->imag == 0.0) {
        ztmp.real = Dtanh(z->real);
        ztmp.imag = 0.0;
    } else {
        zt1.real = -2.0 * z->real;
        zt1.imag = -2.0 * z->imag;
        zt2 = *Cexp(&zt1);
        num.real = 1.0 - zt2.real;
        num.imag = - zt2.imag;
        denom.real = 1.0 + zt2.real;
        denom.imag = zt2.imag;
        ztmp = *Cdiv( &num , &denom );
    }
    return(&ztmp);
}

/*************************************************************************
 *    Casinh(z) = Cln( z + Csqrt( z^2 + 1 ))
 */
COMPLEX *Casinh(z)
COMPLEX *z;
{
    static COMPLEX ztmp;
    COMPLEX zt1,zt2;
    COMPLEX *Cln(),*Csqrt();

    zt1.real = z->real * z->real - z->imag * z->imag + 1.0;
    zt1.imag = 2.0 * z->real * z->imag;
    zt2 = *Csqrt(&zt1);
    zt2.real += z->real;
    zt2.imag += z->imag;
    ztmp = *Cln(&zt2);
    return(&ztmp);
}

/*************************************************************************
 *    Cacosh(z) = Cln ( z + Csqrt(z^2 - 1) )
 */
COMPLEX *Cacosh(z)
COMPLEX *z;
{
    static COMPLEX ztmp;
    COMPLEX zt1,zt2;
    COMPLEX *Csqrt(),*Cln();

    zt1.real = z->real * z->real - z->imag * z->imag - 1.0;
    zt1.imag = 2.0 * z->real * z->imag;
    zt2 = *Csqrt(&zt1);
    zt2.real += z->real;
    zt2.imag += z->imag;
    ztmp = *Cln(&zt2);
    return(&ztmp);
}

/*************************************************************************
 *    Catanh(z) = 0.5 * Cln( (1+z) / (1-z) )
 */
COMPLEX *Catanh(z)
COMPLEX *z;
{
    static COMPLEX ztmp;
    COMPLEX zp1,zm1,zt1;
    COMPLEX *Cln(),*Cdiv();

    zp1.real = 1.0 + z->real;
    zp1.imag = z->imag;
    zm1.real = 1.0 - z->real;
    zm1.imag = - (z->imag);
    zt1 = *Cln( Cdiv( &zp1 , &zm1 ) );
    ztmp.real = zt1.real * 0.5;
    ztmp.real = zt1.imag * 0.5;
    return(&ztmp);
}

/*************************************************************************
 *    Cuniform() = z
 *    where the magnitude and phase of z are uniformly distributed
 *    between (0,sqrt 2) and (0,2 pi) respectively
 */
COMPLEX *Cuniform()
{
    static COMPLEX ztmp;
    double Dsin(),Dcos(),Duniform();
    double d1,d2;

    d1 = SQRT2 * Duniform();
    d2 = TWOPI * Duniform();
    ztmp.real = d1 * Dcos(d2);
    ztmp.imag = d1 * Dsin(d2);
    return(&ztmp);
}

/*************************************************************************
 *    Cnormal() = z
 *    where the real and imaginary parts of z are normally
 *    distributed with mean 0 and variance 1, ie: N(0,1)
 *
 *    Given two samples u1 and u2 from a uniform distribution
 *    on (0,1)
 *    then    n1 = sqrt(2 ln(u1)) cos(2 pi u2)
 *    and     n2 = sqrt(2 ln(u1)) sin(2 pi u2)
 *    are two independent normally distributed samples ie:  N(0,1)
 */
COMPLEX *Cnormal()
{
    static COMPLEX ztmp;
    double Dln(),Dsqrt(),Dcos(),Dsin(),Duniform();
    double d1,d2,rand1;

    while ( (rand1 = Duniform()) == 0.0) ;
    d1 = Dsqrt( 2.0 * Dln(rand1) );
    d2 = TWOPI * ran();
    ztmp.real = d1 * Dcos(d2);
    ztmp.imag = d1 * Dsin(d2);
    return(&ztmp);
}

/*************************************************************************
 *    Crayleigh() = z
 *    where the real and imaginary parts are uniformly
 *    distributed on (-1,1)
 */
COMPLEX *Crayleigh()
{
    static COMPLEX ztmp;
    double Duniform();

    ztmp.real = 2.0 * Duniform() - 1.0;
    ztmp.imag = 2.0 * Duniform() - 1.0;
    return(&ztmp);
}

/*************************************************************************
 *    Cpoly(z1,n,z) evaluates a polynomial at z1 of degree n
 *    whose coefficients are in the complex array z.
 *
 *    f(z) = a0 + a1 z + a2 z^2 + .... + an z^n
 *         = a0 + z ( a1 + z ( a2 + .... + z an) ) ... )
 *    The a's are complex coeficients in increasing order.
 *    This function evaluates f(z1)
 */
COMPLEX *Cpoly(z1,n,z)
COMPLEX *z1;
int n;
COMPLEX z[];
{
    static COMPLEX ztmp;
    COMPLEX zp;
    int i;

    ztmp.real = z1->real;
    ztmp.imag = z1->imag;
    for (i = n; i > 0; i--) {
        zp.real = ztmp.real * z[i].real - ztmp.imag * z[i].imag;
        zp.imag = ztmp.real * z[i].imag + ztmp.imag * z[i].real;
        ztmp.real += z[i-1].real;
        ztmp.imag += z[i-1].imag;
    }
    return(&ztmp);
}


