
import math.*;
import numbers.floats.*;

/*
 *	Complex natural logarithm
 *
 *
 *
 * SYNOPSIS:
 *
 * double complex clog();
 * double complex z, w;
 *
 * w = clog (z);
 *
 *
 *
 * DESCRIPTION:
 *
 * Returns complex logarithm to the base e (2.718...) of
 * the complex argument x.
 *
 * If z = x + iy, r = sqrt( x**2 + y**2 ),
 * then
 *       w = log(r) + i arctan(y/x).
 *
 * The arctangent ranges from -PI to +PI.
 *
 *
 * ACCURACY:
 *
 *                      Relative error:
 * arithmetic   domain     # trials      peak         rms
 *    DEC       -10,+10      7000       8.5e-17     1.9e-17
 *    IEEE      -10,+10     30000       5.0e-15     1.1e-16
 *
 * Larger relative error can be observed for z near 1 +i0.
 * In IEEE arithmetic the peak absolute error is 5.2e-16, rms
 * absolute error 1.0e-16.
 */


[T | Complex?(T)]
overload log(z:T){
    var rr = abs(z);
    var p = log(rr);
    rr = atan2(imagValue(z), real(z));
    return Complex(p, rr);
}

/*							cexp()
 *
 *	Complex exponential function
 *
 *
 *
 * SYNOPSIS:
 *
 * double complex cexp ();
 * double complex z, w;
 *
 * w = cexp (z);
 *
 *
 *
 * DESCRIPTION:
 *
 * Returns the exponential of the complex argument z
 * into the complex result w.
 *
 * If
 *     z = x + iy,
 *     r = exp(x),
 *
 * then
 *
 *     w = r cos y + i r sin y.
 *
 *
 * ACCURACY:
 *
 *                      Relative error:
 * arithmetic   domain     # trials      peak         rms
 *    DEC       -10,+10      8700       3.7e-17     1.1e-17
 *    IEEE      -10,+10     30000       3.0e-16     8.7e-17
 *
 */

[T | Complex?(T)]
overload exp(z:T){
  var r = exp(real(z));
  return Complex(r * cos(imagValue(z)) , r * sin(imagValue(z))) ;
}

/*							csin()
 *
 *	Complex circular sine
 *
 *
 *
 * SYNOPSIS:
 *
 * double complex csin();
 * double complex z, w;
 *
 * w = csin (z);
 *
 *
 *
 * DESCRIPTION:
 *
 * If
 *     z = x + iy,
 *
 * then
 *
 *     w = sin x  cosh y  +  i cos x sinh y.
 *
 * csin(z) = -i csinh(iz).
 *
 * ACCURACY:
 *
 *                      Relative error:
 * arithmetic   domain     # trials      peak         rms
 *    DEC       -10,+10      8400       5.3e-17     1.3e-17
 *    IEEE      -10,+10     30000       3.8e-16     1.0e-16
 * Also tested by csin(casin(z)) = z.
 *
 */

[T | Complex?(T)]
overload sin(z:T){
    var ch,sh = chsh(imagValue(z));
    return Complex(sin(real(z)) * ch , cos (real(z)) * sh);
}

/* calculate cosh and sinh */
[T | Float?(T)]
private chsh(x:T){
    if (abs(x) <= 0.5){
        return cosh(x),sinh(x);
    } else {
        var e = exp(x);
        var ei = 0.5/e;
        e = 0.5 * e;
        return  e + ei, e - ei;
    }
}

/*							ccos()
 *
 *	Complex circular cosine
 *
 *
 *
 * SYNOPSIS:
 *
 * double complex ccos();
 * double complex z, w;
 *
 * w = ccos (z);
 *
 *
 *
 * DESCRIPTION:
 *
 * If
 *     z = x + iy,
 *
 * then
 *
 *     w = cos x  cosh y  -  i sin x sinh y.
 *
 *
 *
 * ACCURACY:
 *
 *                      Relative error:
 * arithmetic   domain     # trials      peak         rms
 *    DEC       -10,+10      8400       4.5e-17     1.3e-17
 *    IEEE      -10,+10     30000       3.8e-16     1.0e-16
 */

[T | Complex?(T)]
overload cos(z:T){
    var ch,sh = chsh(imagValue(z));
    return Complex(cos(real(z)) * ch , - sin(real(z)) * sh);
}


/*							ctan()
 *
 *	Complex circular tangent
 *
 *
 *
 * SYNOPSIS:
 *
 * double complex ctan();
 * double complex z, w;
 *
 * w = ctan (z);
 *
 *
 *
 * DESCRIPTION:
 *
 * If
 *     z = x + iy,
 *
 * then
 *
 *           sin 2x  +  i sinh 2y
 *     w  =  --------------------.
 *            cos 2x  +  cosh 2y
 *
 * On the real axis the denominator is zero at odd multiples
 * of PI/2.  The denominator is evaluated by its Taylor
 * series near these points.
 *
 * ctan(z) = -i ctanh(iz).
 *
 * ACCURACY:
 *
 *                      Relative error:
 * arithmetic   domain     # trials      peak         rms
 *    DEC       -10,+10      5200       7.1e-17     1.6e-17
 *    IEEE      -10,+10     30000       7.2e-16     1.2e-16
 * Also tested by ctan * ccot = 1 and catan(ctan(z))  =  z.
 */

[T | Complex?(T)]
overload tan(z:T){
    alias C = ComplexBaseType(T);
    alias MAXNUM = GreatestPositiveFinite(C);
    var d = cos (2.0 * real (z)) + cosh (2.0 * imagValue(z));
    if (abs(d) < 0.25)
        d = tans(z);
    if (d == 0.0 )
//         mtherr ("ctan", OVERFLOW);
        return Complex(MAXNUM,MAXNUM);
    return Complex(sin(2.0 * real(z)) / d, sinh (2.0 * imagValue(z)) / d);
}

/*							ccot()
 *
 *	Complex circular cotangent
 *
 *
 *
 * SYNOPSIS:
 *
 * double complex ccot();
 * double complex z, w;
 *
 * w = ccot (z);
 *
 *
 *
 * DESCRIPTION:
 *
 * If
 *     z = x + iy,
 *
 * then
 *
 *           sin 2x  -  i sinh 2y
 *     w  =  --------------------.
 *            cosh 2y  -  cos 2x
 *
 * On the real axis, the denominator has zeros at even
 * multiples of PI/2.  Near these points it is evaluated
 * by a Taylor series.
 *
 *
 * ACCURACY:
 *
 *                      Relative error:
 * arithmetic   domain     # trials      peak         rms
 *    DEC       -10,+10      3000       6.5e-17     1.6e-17
 *    IEEE      -10,+10     30000       9.2e-16     1.2e-16
 * Also tested by ctan * ccot = 1 + i0.
 */

[T | Complex?(T)]
overload cot(z:T) {
    alias C = ComplexBaseType(T);
    alias MAXNUM = GreatestPositiveFinite(C);
    var d = cosh(2.0 * imagValue(z)) - cos(2.0 * real(z));
    if (abs(d) < 0.25)
        d = tans(z);
    if (d == 0.0){
//       mtherr ("ccot", OVERFLOW);
        return Complex(MAXNUM,MAXNUM);
    }
    return Complex(sin (2.0 * real(z)) / d, - (sinh (2.0 * imagValue(z)) / d) );
}


/* Program to subtract nearest integer multiple of PI */
/* extended precision value of PI: */
alias DP1 = 3.14159265160560607910E0;
alias DP2 = 1.98418714791870343106E-9;
alias DP3 = 1.14423774522196636802E-17;

[T | Float?(T)]
private inline redupi(x:T){
    var t = x/M_PI;
    if(t >= T() )
        t += T(0.5);
    else
        t -= T(0.5);
    var i = Long(t);	/* the multiple */
    t = T(i);
    return ((x - t * T(DP1)) - t * T(DP2)) - t * T(DP3);
}


/*  Taylor series expansion for cosh(2y) - cos(2x)	*/
[C | Complex?(C)]
private tans(z:C){
    alias T = ComplexBaseType(C);
    alias ZERO = T(0);
    alias ONE = T(1);
    var x = abs (2.0 * real(z));
    var y = abs (2.0 * imagValue(z));
    x = redupi(x);
    x = x * x;
    y = y * y;
    var x2,y2,f,rn,d = ONE,ONE,ONE,ZERO,ZERO;
loop1:
        inc(rn);
        f *= rn;
        inc(rn);
        f *= rn;
        x2 *= x;
        y2 *= y;
        var t = y2 + x2;
        t /= f;
        d += t;
        inc(rn);
        f *= rn;
        inc(rn);
        f *= rn;
        x2 *= x;
        y2 *= y;
        t = y2 - x2;
        t /= f;
        d += t;
    if(abs(t/d) > epsilon(T)) goto loop1;
    return d;
}

/*							casin()
 *
 *	Complex circular arc sine
 *
 *
 *
 * SYNOPSIS:
 *
 * double complex casin();
 * double complex z, w;
 *
 * w = casin (z);
 *
 *
 *
 * DESCRIPTION:
 *
 * Inverse complex sine:
 *
 *                               2
 * w = -i clog( iz + csqrt( 1 - z ) ).
 *
 * casin(z) = -i casinh(iz)
 *
 * ACCURACY:
 *
 *                      Relative error:
 * arithmetic   domain     # trials      peak         rms
 *    DEC       -10,+10     10100       2.1e-15     3.4e-16
 *    IEEE      -10,+10     30000       2.2e-14     2.7e-15
 * Larger relative error can be observed for z near zero.
 * Also tested by csin(casin(z)) = z.
 */

[T | Complex?(T)]
overload asin(z:T){
    alias x = real(z);
    alias y = imag(z);
    if (imagValue(z) == 0.0)
        if (abs(x) > 1.0)
            return T(M_PI_2, 0.);
        else
            return T(asin(x),0.);

    var zz = Complex((x - imagValue(z)) * (x + imagValue(z)) , 2.0 * x * y);
    zz = 1.0 - real(zz) - imag(zz);
    zz = (z * 1.j) + sqrt(zz);
    return  log(zz) * -1.j;
}

/*							cacos()
 *
 *	Complex circular arc cosine
 *
 *
 *
 * SYNOPSIS:
 *
 * double complex cacos();
 * double complex z, w;
 *
 * w = cacos (z);
 *
 *
 *
 * DESCRIPTION:
 *
 *
 * w = arccos z  =  PI/2 - arcsin z.
 *
 *
 *
 *
 * ACCURACY:
 *
 *                      Relative error:
 * arithmetic   domain     # trials      peak         rms
 *    DEC       -10,+10      5200      1.6e-15      2.8e-16
 *    IEEE      -10,+10     30000      1.8e-14      2.2e-15
 */

[T | Complex?(T)]
overload acos(z:T) {
    var w = asin(z);
    return (M_PI_2  -  real(w)) - imag(w);
}

/*							catan()
 *
 *	Complex circular arc tangent
 *
 *
 *
 * SYNOPSIS:
 *
 * double complex catan();
 * double complex z, w;
 *
 * w = catan (z);
 *
 *
 *
 * DESCRIPTION:
 *
 * If
 *     z = x + iy,
 *
 * then
 *          1       (    2x     )
 * Re w  =  - arctan(-----------)  +  k PI
 *          2       (     2    2)
 *                  (1 - x  - y )
 *
 *               ( 2         2)
 *          1    (x  +  (y+1) )
 * Im w  =  - log(------------)
 *          4    ( 2         2)
 *               (x  +  (y-1) )
 *
 * Where k is an arbitrary integer.
 *
 * catan(z) = -i catanh(iz).
 *
 * ACCURACY:
 *
 *                      Relative error:
 * arithmetic   domain     # trials      peak         rms
 *    DEC       -10,+10      5900       1.3e-16     7.8e-18
 *    IEEE      -10,+10     30000       2.3e-15     8.5e-17
 * The check catan( ctan(z) )  =  z, with |x| and |y| < PI/2,
 * had peak relative error 1.5e-16, rms relative error
 * 2.9e-17.  See also clog().
 */

[T | Complex?(T)]
overload atan(z:T){
    alias C = ComplexBaseType(T);
    alias MAXNUM = GreatestPositiveFinite(C);
    alias x = real (z);
    alias y = imagValue(z);

    if (x == 0.0 and y > 1.0)
        return Complex(MAXNUM,MAXNUM);

    var x2 = x * x;
    var a = 1.0 - x2 - y * y;
    if (a == 0.0)
        return Complex(MAXNUM,MAXNUM);

    var t = 0.5 * atan2(2.0 * x, a);
    var w = redupi(t); //XXX Fixme - problem with returned real part

    t = y - 1.0;
    a = x2 + (t * t);
    if (a == 0.0)
        return Complex(MAXNUM,MAXNUM);

    t = y + 1.0;
    a = (x2 + t * t)/a;
    return Complex(w , 0.25 * log(a));
}


/*							csinh
 *
 *	Complex hyperbolic sine
 *
 *
 *
 * SYNOPSIS:
 *
 * double complex csinh();
 * double complex z, w;
 *
 * w = csinh (z);
 *
 * DESCRIPTION:
 *
 * csinh z = (cexp(z) - cexp(-z))/2
 *         = sinh x * cos y  +  i cosh x * sin y .
 *
 * ACCURACY:
 *
 *                      Relative error:
 * arithmetic   domain     # trials      peak         rms
 *    IEEE      -10,+10     30000       3.1e-16     8.2e-17
 *
 */
[T | Complex?(T)]
overload sinh(z:T)
    = Complex(sinh(real(z)) * cos(imagValue(z)) , cosh(real(z)) * sin(imagValue(z)));


/*							casinh
 *
 *	Complex inverse hyperbolic sine
 *
 *
 *
 * SYNOPSIS:
 *
 * double complex casinh();
 * double complex z, w;
 *
 * w = casinh (z);
 *
 *
 *
 * DESCRIPTION:
 *
 * casinh z = -i casin iz .
 *
 * ACCURACY:
 *
 *                      Relative error:
 * arithmetic   domain     # trials      peak         rms
 *    IEEE      -10,+10     30000       1.8e-14     2.6e-15
 *
 */
[T | Complex?(T)]
overload asinh(z:T) = -1.j *  asin (z * 1.j);


/*							ccosh
 *
 *	Complex hyperbolic cosine
 *
 *
 *
 * SYNOPSIS:
 *
 * double complex ccosh();
 * double complex z, w;
 *
 * w = ccosh (z);
 *
 *
 *
 * DESCRIPTION:
 *
 * ccosh(z) = cosh x  cos y + i sinh x sin y .
 *
 * ACCURACY:
 *
 *                      Relative error:
 * arithmetic   domain     # trials      peak         rms
 *    IEEE      -10,+10     30000       2.9e-16     8.1e-17
 *
 */
[T | Complex?(T)]
overload cosh(z:T)
    = Complex(cosh(real(z)) * cos(imagValue(z)) , sinh(real(z)) * sin(imagValue(z)));


/*							cacosh
 *
 *	Complex inverse hyperbolic cosine
 *
 *
 *
 * SYNOPSIS:
 *
 * double complex cacosh();
 * double complex z, w;
 *
 * w = cacosh (z);
 *
 *
 *
 * DESCRIPTION:
 *
 * acosh z = i acos z .
 *
 * ACCURACY:
 *
 *                      Relative error:
 * arithmetic   domain     # trials      peak         rms
 *    IEEE      -10,+10     30000       1.6e-14     2.1e-15
 *
 */
[T | Complex?(T)]
overload acosh(z:T) = 1.j * acos(z);


/*							ctanh
 *
 *	Complex hyperbolic tangent
 *
 *
 *
 * SYNOPSIS:
 *
 * double complex ctanh();
 * double complex z, w;
 *
 * w = ctanh (z);
 *
 *
 *
 * DESCRIPTION:
 *
 * tanh z = (sinh 2x  +  i sin 2y) / (cosh 2x + cos 2y) .
 *
 * ACCURACY:
 *prun
 *                      Relative error:
 * arithmetic   domain     # trials      peak         rms
 *    IEEE      -10,+10     30000       1.7e-14     2.4e-16
 *
 */
[T | Complex?(T)]
overload tanh(z:T){
    var d = 1./(cosh(2.0 * real(z)) + cos(2.0 * imagValue(z)));
    return Complex(sinh(2.0 * real(z)) * d  , sin (2.0 * imagValue(z)) * d) ;
}


/*							catanh
 *
 *	Complex inverse hyperbolic tangent
 *
 *
 *
 * SYNOPSIS:
 *
 * double complex catanh();
 * double complex z, w;
 *
 * w = catanh (z);
 *
 *
 *
 * DESCRIPTION:
 *
 * Inverse tanh, equal to  -i catan (iz);
 *
 * ACCURACY:
 *
 *                      Relative error:
 * arithmetic   domain     # trials      peak         rms
 *    IEEE      -10,+10     30000       2.3e-16     6.2e-17
 *
 */
[T | Complex?(T)]
overload atanh(z:T) = -1.j * atan(z * 1.j);

/*							cpow
 *
 *	Complex power function
 *
 *
 *
 * SYNOPSIS:
 *
 * double complex cpow();
 * double complex a, z, w;
 *
 * w = cpow (a, z);
 *
 *
 *
 * DESCRIPTION:
 *
 * Raises complex A to the complex Zth power.
 * Definition is per AMS55 # 4.2.8,
 * analytically equivalent to cpow(a,z) = cexp(z clog(a)).
 *
 * ACCURACY:
 *
 *                      Relative error:
 * arithmetic   domain     # trials      peak         rms
 *    IEEE      -10,+10     30000       9.4e-15     1.5e-15
 *
 */
[T,U | Complex?(T,U)]
overload pow(a:T, z:U){
    var absa = abs(a);
    if (absa == 0.0)
        return T();
    var r = pow(absa, real(z));
    var theta = real(z) * arg(a);
    if (imag(z) != 0.){
        r = r * exp (-imagValue(z) * arg(a));
        theta = theta + imagValue(z) * log(absa);
    }
    return Complex(r * cos(theta) , r * sin(theta));
}


[T | Complex?(T)]
overload log10(x:T) = log(x)/log(10.0);
