/* -*-C-*- texp.c */

#include "elefunt.h"


/***********************************************************************
#     program to test exp
#
#     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 four
#                 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
#                 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, aint, alog, AMAX1, exp, float, sqrt
#
#
#     latest revision - december 6, 1979
#
#     author - w. j. cody
#              argonne national laboratory
#
#
***********************************************************************/

void
texp(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,
        b,
        beta,
        d,
        del,
        r6,
        r7,
        v,
        w,
        x,
        xl,
        xn,
        x1,
        y,
        z,
        zz;

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

    (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);
    v = 0.0625e0;
    a = TWO;
    b = ALOG(a) * 0.5e0;
    a = -b + v;
    d = ALOG(0.9e0 * xmax);
    n = maxtest();
    xn = TO_FP_T(n);

    /* random argument accuracy tests */
    for (j = 1; j <= 3; ++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;

	    /* purify arguments */
	    y = x - v;
	    if (y < ZERO)
		x = y + v;
	    z = EXP(x);
	    zz = EXP(y);
	    if (j == 1)
		z = z - z * 6.058693718652421388e-2;
	    else
	    {
		if (ibeta != 10)
		    z = z * 0.0625e0 - z * 2.4453321046920570389e-3;
		else
		    z = z * 6.0e-2 + z * 5.466789530794296106e-5;
	    }
	    w = ONE;
	    if (zz != ZERO)
		w = (z - zz) / zz;
	    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);

	(void)printf("1TEST OF EXP(X-%7.4f) VS EXP(X)/EXP(%7.4f)\n\n\n", v, v);
	(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(" EXP(X-V) 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;
	(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);
	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 == 2)
	{
	    a = -TWO * a;
	    b = TEN * a;
	    if (b < d)
		b = d;
	}
	else
	{
	    v = 45.0e0 / 16.0e0;
	    a = -TEN * b;
	    b = 4.0e0 * xmin * IPOW(beta, it);
	    b = ALOG(b);
	}
    }

    /* special tests */
    (void)printf("1SPECIAL TESTS\n\n\n");
    (void)printf(" THE IDENTITY EXP(X)*EXP(-X) = 1.0  WILL BE TESTED.\n\n");
    (void)printf("       X        F(X)*F(-X) - 1\n\n");

    for (i = 1; i <= 5; ++i)
    {
	x = RAN() * beta;
	y = -x;
	z = EXP(x) * EXP(y) - ONE;
	(void)printf("%15.7e%15.7e\n\n", x, z);
    }

    (void)printf("\n\n TEST OF SPECIAL ARGUMENTS\n\n\n");
    x = ZERO;
    y = EXP(x) - ONE;
    (void)printf(" EXP(0.0) - 1.0E0 = %15.7e\n\n", y);
    x = AINT(ALOG(xmin));
    y = EXP(x);
    (void)printf(" EXP(%13.6e) =%13.6e\n\n", x, y);
    x = AINT(ALOG(xmax));
    y = EXP(x);
    (void)printf(" EXP(%13.6e) =%13.6e\n\n", x, y);
    x = x / TWO;
    v = x / TWO;
    y = EXP(x);
    z = EXP(v);
    z = z * z;
    (void)printf(" IF EXP(%13.6e) = %13.6e IS NOT ABOUT\n", x, y);
    (void)printf(" EXP(%13.6e)**2 =%13.6e THERE IS AN ARG RED ERROR\n", v, z);

    /* test of error returns */

    (void)printf("1TEST OF ERROR RETURNS\n\n\n");
    x = -ONE / SQRT(xmin);
    (void)printf(" EXP WILL BE CALLED WITH THE ARGUMENT%15.4e\n",x);
    (void)printf(" THIS SHOULD TRIGGER AN ERROR MESSAGE\n\n\n");
    fflush(stdout);
    errno = 0;
    y = EXP(x);
    if (errno)
	perror("EXP()");
    (void)printf(" EXP RETURNED THE VALUE%15.4e\n\n\n\n", y);
    x = -x;
    (void)printf(" EXP WILL BE CALLED WITH THE ARGUMENT%15.4e\n", x);
    (void)printf(" THIS SHOULD TRIGGER AN ERROR MESSAGE\n\n\n");
    fflush(stdout);
    errno = 0;
    y = EXP(x);
    if (errno)
	perror("EXP()");
    (void)printf(" EXP RETURNED THE VALUE%15.4e\n\n\n\n", y);
    (void)printf(" THIS CONCLUDES THE TESTS\n");
}
