/* Program to test range reduction of trigonometry functions
 *
 * -- Steve Moshier
 */

#include "mconf.h"
#ifdef ANSIPROT
extern double floor ( double );
extern double ldexp ( double, int );
extern double sin ( double );
#else
double floor(), ldexp(), sin();
#endif

#define TNCEPHES_PI 6.283185307179586476925

int 
main (void)
{
char s[40];
double a, n, t, x, y, z;
int lflg;

x = TNCEPHES_PI/4.0;
t = 1.0;

loop:

t = 2.0 * t;

/* Stop testing at a point beyond which the integer part of
 * x/2pi cannot be represented exactly by a double precision number.
 * The library trigonometry functions will probably give up long before
 * this point is reached.
 */
if( t > 1.0e16 )
	exit(0);

/* Adjust the following to choose a nontrivial x
 * where test function(x) has a slope of about 1 or more.
 */
x = TNCEPHES_PI * t  + 0.5;

z = x;
lflg = 0;

inlup:

/* floor() returns the largest integer less than its argument.
 * If you do not have this, or AINT(), then you may convert x/TNCEPHES_PI
 * to a long integer and then back to double; but in that case
 * x will be limited to the largest value that will fit into a
 * long integer.
 */
n = floor( z/TNCEPHES_PI );

/* Carefully subtract 2 pi n from x.
 * This is done by subtracting n * 2**k in such a way that there
 * is no arithmetic cancellation error at any step.  The k are the
 * bits in the number 2 pi.
 *
 * If you do not have ldexp(), then you may multiply or
 * divide n by an appropriate power of 2 after each step.
 * For example:
 *  a = z - 4*n;
 *  a -= 2*n;
 *  n /= 4;
 *  a -= n;   n/4
 *  n /= 8;
 *  a -= n;   n/32
 * etc.
 * This will only work if division by a power of 2 is exact.
 */

a = z - ldexp(n, 2);	/* 4n */
a -= ldexp( n, 1);	/* 2n */
a -= ldexp( n, -2 );	/* n/4 */
a -= ldexp( n, -5 );	/* n/32 */
a -= ldexp( n, -9 );	/* n/512 */
a += ldexp( n, -15 );	/* add n/32768 */
a -= ldexp( n, -17 );	/* n/131072 */
a -= ldexp( n, -18 );
a -= ldexp( n, -20 );
a -= ldexp( n, -22 );
a -= ldexp( n, -24 );
a -= ldexp( n, -28 );
a -= ldexp( n, -32 );
a -= ldexp( n, -37 );
a -= ldexp( n, -39 );
a -= ldexp( n, -40 );
a -= ldexp( n, -42 );
a -= ldexp( n, -46 );
a -= ldexp( n, -47 );

/* Subtract what is left of 2 pi n after all the above reductions.
 */
a -= 2.44929359829470635445e-16 * n;

/* If the test is extended too far, it is possible
 * to have chosen the wrong value of n.  The following
 * will fix that, but at some reduction in accuracy.
 */
if( (a > TNCEPHES_PI) || (a < -1e-11) )
	{
	z = a;
	lflg += 1;
	printf( "Warning! Reduction failed on first try.\n" );
	goto inlup;
	}
if( a < 0.0 )
	{
	printf( "Warning! Reduced value < 0\n" );
	a += TNCEPHES_PI;
	}

/* Compute the test function at x and at a = x mod 2 pi.
 */
y = sin(x);
z = sin(a);
printf( "sin(%.15e) error = %.3e\n", x, y-z );
goto loop;
}

