/* -*-C-*- tatan.c */

#include "elefunt.h"

/*
#     program to test atan, atan2
#
#     data required
#
#        none
#
#     subprograms required from this package
#
#        machar - an environmental inquiry program providing
#                 information on the floating-point arithmetic
#                 system.  note that the call to machar can
#                 be deleted provided the following six
#                 parameters are assigned the values indicated
#
#                 ibeta  - the radix of the floating-point system
#                 it     - the number of base-ibeta digits in the
#                          significand of a floating-point number
#                 irnd   - 0 if floating-point addition chops,
#                          1 if floating-point addition rounds
#                 minexp - the largest in magnitude negative
#                          integer such that float(ibeta)**minexp
#                          is a positive floating-point number
#                 xmin   - the smallest non-vanishing floating-point
#                          power of the radix
#                 xmax   - the largest finite floating-point no.
#
#        ran(k) - a function subprogram returning random real
#                 numbers uniformly distributed over (0,1)
#
#     standard fortran subprograms required
#
#         abs, alog, amax1, atan, atan2, float, sqrt
#
#
#     latest revision - december 6, 1979
#
#     author - w. j. cody
#              argonne national laboratory
#
#*/

void
tatan(VOID_ARG)
{
    int i,
        k,
        ibeta,
        iexp,
        irnd,
        it,
        j,
        k1,
        k2,
        k3,
        machep,
        maxexp,
        minexp,
        n,
        negep,
        ngrd;

    qp_t
        eps,
        epsneg,
        xmax,
        xmin;

    volatile qp_t 
	a,
        ait,
        albeta,
        b,
        beta,
        betap,
        del,
        em,
        expon,
        half,
        ob32,
        r6,
        r7,
        sum,
        w,
        x,
        xl,
        xn,
        xsq,
        x1,
        y,
        z,
        zz;

    /*******************************************************************/

    (void)qranset(initseed());
    macharl(&ibeta, &it, &irnd, &ngrd, &machep, &negep, &iexp, &minexp,
	   &maxexp, &eps, &epsneg, &xmin, &xmax);
    beta = TO_FP_T(ibeta);
    albeta = ALOG(beta);
    ait = TO_FP_T(it);
    half = 0.5e+00L;
    a = -0.0625e+00L;
    b = -a;
    ob32 = b * half;
    n = maxtest();
    xn = TO_FP_T(n);

    /* random argument accuracy tests */

    for (j = 1; j <= 4; ++j)
    {
	k1 = 0;
	k3 = 0;
	x1 = ZERO;
	r6 = ZERO;
	r7 = ZERO;
	del = (b - a) / xn;
	xl = a;

	for (i = 1; i <= n; ++i)
	{
	    x = del * RAN() + xl;
	    if (j == 2)
		x = ((1.0e+00L + x * a) - ONE) * 16.0e+00L;
	    z = ATAN(x);
	    if (j == 1)
	    {
		xsq = x * x;

		/* Use 7 terms for 18 decimal figures, 16 for 40 decimal figures */
		/* (Taylor series error = (1/16)**m/(2*m+1)) */
		em = TO_FP_T(2*(16 + 1) + 1);
		sum = xsq / em;
		for (k = 1; k <= 16; ++k)
		{
		    em = em - TWO;
		    sum = (ONE / em - sum) * xsq;
		}

		sum = -x * sum;
		zz = x + sum;
		sum = (x - zz) + sum;
		if (irnd == 0)
		    zz = zz + (sum + sum);
	    }
	    else
	    if (j != 2)
	    {
		z = z + z;
		y = x / ((half + x * half) * ((half - x) + half));
		zz = ATAN(y);
	    }
	    else
	    {
		y = x - 0.0625e+00L;
		y = y / (ONE + x * a);
		zz = (ATAN(y) - 8.11900040426515260208870144948863937261122025008053925e-05L) + ob32;
		zz = zz + ob32;
	    }
	    w = ONE;
	    if (z != ZERO)
		w = (z - zz) / z;
	    if (w > ZERO)
		k1 = k1 + 1;
	    if (w < ZERO)
		k3 = k3 + 1;
	    w = ABS(w);
	    if (w > r6)
	    {
		r6 = w;
		x1 = x;
	    }
	    r7 = r7 + w * w;
	    xl = a + TO_FP_T(i) * del;	/* OLD: xl = xl + del (bad for large n) */
	}

	k2 = n - k3 - k1;
	r7 = SQRT(r7 / xn);
	if (j == 1)
	    (void)printf("1TEST OF ATAN(X) VS TRUNCATED TAYLOR SERIES\n\n");
	else if (j == 2)
	    (void)printf(
	    "1TEST OF ATAN(X) VS ATAN(1/16) + ATAN((X-1/16)/(1+X/16))\n\n");
	else if (j > 2)
	    (void)printf("1TEST OF 2*ATAN(X) VS ATAN(2X/(1-X*X))\n\n");
	(void)printf("%7d RANDOM ARGUMENTS WERE TESTED FROM THE INTERVAL\n",n);
	(void)printf("      (%15.4Le,%15.4Le)\n\n\n", a, b);
	(void)printf(" ATAN(X) WAS LARGER%6d TIMES,\n", k1);
	(void)printf("             AGREED%6d TIMES, AND\n", k2);
	(void)printf("        WAS SMALLER%6d TIMES.\n\n", k3);
	(void)printf(" THERE ARE%4d BASE%4d SIGNIFICANT DIGITS IN A FLOATING-POINT NUMBER\n\n",
	    it, ibeta);
	w = -999.0e+00L;
	if (r6 != ZERO)
	    w = ALOG(ABS(r6)) / albeta;
	(void)printf(" THE MAXIMUM RELATIVE ERROR OF%15.4Le = %4d **%7.2Lf\n",
	    r6, ibeta, w);
	(void)printf("    OCCURRED FOR X =%17.6Le\n", x1);
	w = AMAX1(ait + w, ZERO);
	(void)printf(
	    " THE ESTIMATED LOSS OF BASE%4d SIGNIFICANT DIGITS IS%7.2Lf\n\n\n",
	    ibeta, w);
	w = -999.0e+00L;
	if (r7 != ZERO)
	    w = ALOG(ABS(r7)) / albeta;
	(void)printf(" THE ROOT MEAN SQUARE RELATIVE ERROR WAS%15.4Le = %4d **%7.2Lf\n",
	    r7, ibeta, w);
	w = AMAX1(ait + w, ZERO);
	(void)printf(
	    " THE ESTIMATED LOSS OF BASE%4d SIGNIFICANT DIGITS IS%7.2Lf\n\n\n",
	    ibeta, w);
	a = b;
	if (j == 1)
	    b = TWO - SQRT(3.0e+00L);
	else if (j == 2)
	    b = SQRT(TWO) - ONE;
	else if (j == 3)
	    b = ONE;
    }

    /* special tests */

    (void)printf("1SPECIAL TESTS\n\n");
    (void)printf(" THE IDENTITY   ATAN(-X) = -ATAN(X)   WILL BE TESTED.\n\n\n");
    (void)printf("        X         F(X) + F(-X)\n\n");
    a = 5.0e+00L;

    for (i = 1; i <= 5; ++i)
    {
	x = RAN() * a;
	z = ATAN(x) + ATAN(-x);
	(void)printf("%15.7Le%15.7Le\n\n", x, z);
    }

    (void)printf(" THE IDENTITY ATAN(X) = X , X SMALL, WILL BE TESTED.\n\n\n");
    (void)printf("        X         X - F(X)\n\n");
    betap = IPOW(beta, it);
    x = RAN() / betap;

    for (i = 1; i <= 5; ++i)
    {
	z = x - ATAN(x);
	(void)printf("%15.7Le%15.7Le\n\n", x, z);
	x = x / beta;
    }

    (void)printf(" THE IDENTITY ATAN(X/Y) = ATAN2(X,Y) WILL BE TESTED\n");
    (void)printf(" THE FIRST COLUMN OF RESULTS SHOULD BE 0, THE SECOND +-PI\n\n");
    (void)printf("        X             Y     F1(X/Y)-F2(X,Y)F1(X/Y)-F2(X/(-Y))\n");
    a = -TWO;
    b = 4.0e+00L;

    for (i = 1; i <= 5; ++i)
    {
	x = RAN() * b + a;
	y = RAN();
	w = -y;
	z = ATAN(x / y) - ATAN2(x, y);
	zz = ATAN(x / w) - ATAN2(x, w);
	(void)printf("%15.7Le%15.7Le%15.7Le%15.7Le\n\n", x, y, z, zz);
    }

    (void)printf(" TEST OF UNDERFLOW FOR VERY SMALL ARGUMENT.\n\n");
    expon = TO_FP_T(minexp) *0.75e+00L;
    x = POW(beta, expon);
    y = ATAN(x);
    (void)printf("       ATAN(%13.6Le) =%13.6Le\n", x, y);

    /* test of error returns */

    (void)printf("1TEST OF ERROR RETURNS\n\n\n");

    (void)printf(" ATAN WILL BE CALLED WITH THE ARGUMENT%15.7Le\n", xmax);
    (void)printf(" THIS SHOULD NOT TRIGGER AN ERROR MESSAGE\n\n\n");
    fflush(stdout);
    errno = 0;
    z = ATAN(xmax);
    if (errno)
	perror("ATAN()");
    (void)printf("       ATAN(%13.6Le) =%13.6Le\n", xmax, z);

    x = ONE;
    y = ZERO;
    (void)printf(" ATAN2 WILL BE CALLED WITH THE ARGUMENTS\n%15.7Le%15.7Le\n\n", x, y);
    (void)printf(" THIS SHOULD NOT TRIGGER AN ERROR MESSAGE\n\n\n");
    fflush(stdout);
    errno = 0;
    z = ATAN2(x, y);
    if (errno)
	perror("ATAN2()");
    (void)printf("       ATAN2(%13.6Le%13.6Le) =%13.6Le\n", x, y, z);

    (void)printf(" ATAN2 WILL BE CALLED WITH THE ARGUMENTS\n%15.7Le%15.7Le\n\n",
        xmin, xmax);
    (void)printf(" THIS SHOULD NOT TRIGGER AN ERROR MESSAGE\n\n\n");
    fflush(stdout);
    errno = 0;
    z = ATAN2(xmin, xmax);
    if (errno)
	perror("ATAN2()");
    (void)printf("       ATAN2(%13.6Le%13.6Le) =%13.6Le\n", xmin, xmax, z);

    (void)printf(" ATAN2 WILL BE CALLED WITH THE ARGUMENTS\n%15.7Le%15.7Le\n\n",
        xmax, xmin);
    (void)printf(" THIS SHOULD NOT TRIGGER AN ERROR MESSAGE\n\n\n");
    fflush(stdout);
    errno = 0;
    z = ATAN2(xmax, xmin);
    if (errno)
	perror("ATAN2()");
    (void)printf("       ATAN2(%13.6Le%13.6Le) =%13.6Le\n", xmax, xmin, z);

    x = ZERO;
    (void)printf(" ATAN2 WILL BE CALLED WITH THE ARGUMENTS\n%15.7Le%15.7Le\n\n", x, y);
    (void)printf(" THIS SHOULD TRIGGER AN ERROR MESSAGE\n\n\n");
    fflush(stdout);
    errno = 0;
    z = ATAN2(x, y);
    if (errno)
	perror("ATAN2()");
    (void)printf("       ATAN2(%13.6Le%13.6Le) =%13.6Le\n", x, y, z);

    (void)printf(" THIS CONCLUDES THE TESTS\n");
}
