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

   Uebungen zur Analysis I fuer Informatiker - WS 2003/04
   Blatt 10

   Freiwillige Zusatzaufgabe 45) - sin_approx.cpp
   Approximation an die Sinus-Funktion

   Leonhard Fellermayr, Mat. Nr. 22128130XXXX

   Auch im WWW: http://www.slacky.de/files/uni/analysis/sin_approx.cpp

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

#include <iostream>

using namespace std;

const long double EPSILON_IEEE64 = 10e-16;

long double faktl (long double x)
{
        if (x == 1) return 1;
        else return x * faktl (x-1);
}

long double approx (long double z)
{
	long double snz = 0;
	int i, n = 0;

	while (fabsl (powl (z, 2*n+1)) / fabsl (faktl (2*n+1)) > EPSILON_IEEE64 * fabsl (snz))
	{
		snz = 0;

		for (i = 0; i <= n; i++)
			snz += powl (-1, i) * (powl (z, 2*i+1) / faktl (2*i+1));

		++n;
	}

	return (snz);
}

long double reduce_and_approx (long double z)
{
	if (z > 2 * M_PI)				/* (1) */
		return (-reduce_and_approx (-z));

	else if (z < 0)					/* (2) */
		return (reduce_and_approx (z + 2 * M_PI));

	else if ((M_PI < z) && (z <= 2 * M_PI))		/* (3) */
		return (-approx (z - M_PI));

	else if ((M_PI_2 < z) && (z <= M_PI))		/* (4) */
		return (approx (M_PI - z));

	else if ((M_PI_4 < z) && (z <= M_PI_2))		/* (5) */
		return (sqrtl (1 - powl (approx (M_PI / 2 - z), 2)));

	else	return (approx (z));		
}

int main ()
{
	long double z = 0;

	printf ("|     z | Reduction         | No Reduction      | C++ Standard Func |\n");

	for (int i = 0; i <= 64; i++)
	{
		/* wir geben zusaetzlich noch die Werte fuer z = 20,30,40,50 an */
		if (i > 60)	z += 10;
		else		z = -5 + 0.25 * i;

		printf ("| % 6.02Lf | % .15Lf | % 19.15Lf | % .15Lf |\n",
			z, reduce_and_approx (z), approx (z), sin (z));
	}

	return 0;
}
