/* -*-C-*- tpower.c */

#include "elefunt.h"

/*#     program to test power function (**)
#
#     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
#                 minexp - the largest in magnitude negative
#                          integer such that  float(ibeta)**minexp
#                          is a positive floating-point number
#                 maxexp - the largest positive integer exponent
#                          for a finite floating-point number
#                 xmin   - the smallest non-vanishing floating-point
#                          power of the radix
#                 xmax   - the largest finite floating-point
#                          number
#
#        ran(k) - a function subprogram returning random real
#                 numbers uniformly distributed over (0,1)
#
#
#     standard fortran subprograms required
#
#         abs, alog, amax1, exp, float, sqrt
#
#
#     latest revision - december 6, 1979
#
#     author - w. j. cody
#              argonne national laboratory
#
#**********************************************************************/

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

    dp_t
        eps,
        epsneg,
        xmax,
        xmin;

    volatile dp_t 
	a,
        ait,
        albeta,
        alxmax,
        b,
        beta,
        c,
        del,
        dely,
        onep5,
        r6,
        r7,
        scale,
        xl,
        xn,
        xsq,
        x1,
        y,
        y1,
        y2,
        z,
        zz;

    dp_t
	w,
        x;

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

    (void)ranset(initseed());
    machar(&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);
    alxmax = ALOG(xmax);
    onep5 = (TWO + ONE) / TWO;
    scale = ONE;
    j = (it + 1) / 2;
    for (i = 1; i <= j; i++)	/* do i = 1, j */
	scale = scale * beta;
    a = ONE / beta;
    b = ONE;
    c = -AMAX1(alxmax, -ALOG(xmin)) / ALOG(100e0);
    dely = -c - c;
    n = maxtest();
    xn = TO_FP_T(n);
    y = ZERO;
    y1 = ZERO;
    zz = ZERO;

    /* random argument accuracy tests */

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

	for (i = 1; i <= n; i++)
	{			/* DO I = 1, N */
	    x = del * RAN() + xl;
	    if (j == 1)
	    {
		zz = POW(x, ONE);	/* zz=x**ONE */
		z = x;
	    }
	    else
	    {
		w *= scale;
		w = STORE(&w);
		x += w;
		x = STORE(&x);
		x -= w;
		x = STORE(&x);
		xsq = x * x;
		if (j != 4)
		{
		    zz = POW(xsq, onep5);	/* zz = xsq ** onep5; */
		    z = x * xsq;
		}
		else
		{
		    y = dely * RAN() + c;
		    y2 = (y / TWO + y) - y;
		    y = y2 + y2;
		    z = POW(x, y);	/* z=x ** y; */
		    zz = POW(xsq, y2);	/* zz=xsq ** y2; */
		}
	    }
	    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;
		if (j == 4)
		    y1 = y;
	    }
	    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 X**1.0 VS X\n\n\n");
	    (void)printf("%7d RANDOM ARGUMENTS WERE TESTED FROM THE INTERVAL\n", n);
	    (void)printf("      (%15.4e,%15.4e)\n\n\n", a, b);
	    (void)printf(" X**1.0 WAS LARGER%6d TIMES,\n", k1);
	    (void)printf("            AGREED%6d TIMES, AND\n", k2);
	    (void)printf("       WAS SMALLER%6d TIMES.\n\n\n", k3);
	}
	else if (j != 4)
	{
	    (void)printf("1TEST OF XSQ**1.5 VS XSQ*X\n\n\n");
	    (void)printf("%7d RANDOM ARGUMENTS WERE TESTED FROM THE INTERVAL\n", n);
	    (void)printf("      (%15.4e,%15.4e)\n\n\n", a, b);
	    (void)printf(" X**1.5 WAS LARGER%6d TIMES,\n", k1);
	    (void)printf("            AGREED%6d TIMES, AND\n", k2);
	    (void)printf("       WAS SMALLER%6d TIMES.\n\n\n", k3);
	}
	else
	{
	    (void)printf("1TEST OF X**Y VS XSQ**(Y/2)\n\n\n");
	    w = c + dely;
	    (void)printf(" %6d RANDOM ARGUMENTS WERE TESTED FROM THE REGION\n", n);
	    (void)printf("      X IN (%15.4e,%15.4e), Y IN (%15.4e,%15.4e)\n\n\n",
	        a, b, c, w);
	    (void)printf(" X**Y  WAS LARGER%6d TIMES,\n", k1);
	    (void)printf("           AGREED%6d TIMES, AND\n", k2);
	    (void)printf("      WAS SMALLER%6d TIMES.\n\n\n", k3);
	}
	(void)printf(" THERE ARE %4d BASE %4d SIGNIFICANT DIGITS IN A FLOATING-POINT NUMBER\n\n\n",
	    it, ibeta);
	w = -999.0e0;
	if (r6 != ZERO)
	    w = ALOG(ABS(r6)) / albeta;
	if (j != 4)
	{
	    (void)printf(" THE MAXIMUM RELATIVE ERROR OF %15.4e = %4d ** %7.2f\n",
	        r6, ibeta, w);
	    (void)printf("    OCCURRED FOR X =%17.6e\n", x1);
	}
	if (j == 4)
	{
	    (void)printf(" THE MAXIMUM RELATIVE ERROR OF %15.4e = %4d ** %7.2f\n",
	        r6, ibeta, w);
	    (void)printf("    OCCURRED FOR X =%17.6e Y =%17.6e\n", x1, y1);
	}
	w = AMAX1(ait + w, ZERO);
	(void)printf(
	    " THE ESTIMATED LOSS OF BASE%4d SIGNIFICANT DIGITS IS%7.2f\n\n\n",
	    ibeta, w);
	w = -999.0e0;
	if (r7 != ZERO)
	    w = ALOG(ABS(r7)) / albeta;
	(void)printf(
	" THE ROOT MEAN SQUARE RELATIVE ERROR WAS %15.4e = %4d **%7.2f\n",
	    r7, ibeta, w);
	w = AMAX1(ait + w, ZERO);
	(void)printf(
	    " THE ESTIMATED LOSS OF BASE%4d SIGNIFICANT DIGITS IS%7.2f\n\n\n",
	    ibeta, w);
	if (j != 1)
	{
	    b = 10.0e0;
	    a = 0.01e0;
	    if (j != 3)
	    {
		a = ONE;
		b = EXP(alxmax / 3.0e0);
	    }
	}
    }

    /* special tests */

    (void)printf("1SPECIAL TESTS\n\n\n");
    (void)printf(" THE IDENTITY  X ** Y = (1/X) ** (-Y)  WILL BE TESTED.\n\n");
    (void)printf("        X              Y         (X**Y-(1/X)**(-Y)) / X**Y\n\n");
    b = 10.0e0;

    for (i = 1; i <= 5; i++)
    {				/* DO I = 1, 5 	 */
	x = RAN() * b + ONE;
	y = RAN() * b + ONE;
	z = POW(x, y);		/* z=x ** y */
	zz = POW((ONE / x), -y);/* zz=(ONE/x) ** (-y) */
	w = (z - zz) / z;
	(void)printf("%15.7e%15.7e      %15.7e\n\n", x, y, w);
    }

    /* test of error returns */

    (void)printf("1TEST OF ERROR RETURNS\n\n\n");
    x = beta;
    y = TO_FP_T(minexp);
    (void)printf(" (%14.7e) ** (%14.7e) WILL BE COMPUTED.\n", x, y);
    (void)printf(" THIS SHOULD NOT TRIGGER AN ERROR MESSAGE\n\n\n");
    fflush(stdout);
    errno = 0;
    z = POW(x, y);		/* Z = X ** Y */
    if (errno)
	perror("POW()");
    (void)printf(" THE VALUE RETURNED IS %15.4e\n\n\n\n", z);
    y = TO_FP_T(maxexp - 1);
    (void)printf(" (%14.7e) ** (%14.7e) WILL BE COMPUTED.\n", x, y);
    (void)printf(" THIS SHOULD NOT TRIGGER AN ERROR MESSAGE\n\n\n");
    fflush(stdout);
    errno = 0;
    z = POW(x, y);		/* z = X ** Y */
    if (errno)
	perror("POW()");
    (void)printf(" THE VALUE RETURNED IS %15.4e\n\n\n\n", z);
    x = ZERO;
    y = TWO;
    (void)printf(" (%14.7e) ** (%14.7e) WILL BE COMPUTED.\n", x, y);
    (void)printf(" THIS SHOULD NOT TRIGGER AN ERROR MESSAGE\n\n\n");
    fflush(stdout);
    errno = 0;
    z = POW(x, y);		/* z = X ** Y */
    if (errno)
	perror("POW()");
    (void)printf(" THE VALUE RETURNED IS %15.4e\n\n\n\n", z);
    x = -y;
    y = ZERO;
    (void)printf(" (%14.7e) ** (%14.7e) WILL BE COMPUTED.\n", x, y);
    (void)printf(" THIS SHOULD TRIGGER AN ERROR MESSAGE\n\n\n");
    fflush(stdout);
    errno = 0;
    z = POW(x, y);		/* z = X ** Y */
    if (errno)
	perror("POW()");
    (void)printf(" THE VALUE RETURNED IS %15.4e\n\n\n\n", z);
    y = TWO;
    (void)printf(" (%14.7e) ** (%14.7e) WILL BE COMPUTED.\n", x, y);
    (void)printf(" THIS SHOULD TRIGGER AN ERROR MESSAGE\n\n\n");
    fflush(stdout);
    errno = 0;
    z = POW(x, y);		/* z = X ** Y */
    if (errno)
	perror("POW()");
    (void)printf(" THE VALUE RETURNED IS %15.4e\n\n\n\n", z);
    x = ZERO;
    y = ZERO;
    (void)printf(" (%14.7e) ** (%14.7e) WILL BE COMPUTED.\n", x, y);
    (void)printf(" THIS SHOULD TRIGGER AN ERROR MESSAGE\n\n\n");
    fflush(stdout);
    errno = 0;
    z = POW(x, y);		/* z = X ** Y */
    if (errno)
	perror("POW()");
    (void)printf(" THE VALUE RETURNED IS %15.4e\n\n\n\n", z);
    (void)printf(" THIS CONCLUDES THE TESTS\n");
}
