/* +------------------------------------------------------------------------+
   |                     Mobile Robot Programming Toolkit (MRPT)            |
   |                          https://www.mrpt.org/                         |
   |                                                                        |
   | Copyright (c) 2005-2023, Individual contributors, see AUTHORS file     |
   | See: https://www.mrpt.org/Authors - All rights reserved.               |
   | Released under BSD License. See: https://www.mrpt.org/License          |
   +------------------------------------------------------------------------+ */

#include "math-precomp.h"  // Precompiled headers
//
#include <mrpt/math/fresnel.h>

#include <cfloat>
#include <cmath>

// Based on code (public domain) in http://www.mymathlib.com/functions/

static long double Power_Series_S(long double x);
static long double xFresnel_Auxiliary_Cosine_Integral(long double x);
static long double xFresnel_Auxiliary_Sine_Integral(long double x);
static long double Power_Series_C(long double x);
static long double xChebyshev_Tn_Series(
	long double x, const long double a[], int degree);

// extern long double xFresnel_Sine_Integral(long double x);
// extern long double xFresnel_Cosine_Integral(long double x);

// the integral from 0 to x of sqrt(2 / pi) sin(t ^ 2) dt.
long double lfresnel_sin_alt(long double x)
{
	long double f, g, x2, s;

	if (std::abs(x) < 0.5L) return Power_Series_S(x);

	f = xFresnel_Auxiliary_Cosine_Integral(fabsl(x));
	g = xFresnel_Auxiliary_Sine_Integral(fabsl(x));
	x2 = x * x;
	s = 0.5L - cosl(x2) * f - sinl(x2) * g;
	return (x < 0.0L) ? -s : s;
}

// the integral from 0 to x of sqrt(2 / pi) cos(t ^ 2) dt.
long double lfresnel_cos_alt(long double x)
{
	long double f, g, x2, c;

	if (fabsl(x) < 0.5L) return Power_Series_C(x);

	f = xFresnel_Auxiliary_Cosine_Integral(fabsl(x));
	g = xFresnel_Auxiliary_Sine_Integral(fabsl(x));
	x2 = x * x;
	c = 0.5L + sinl(x2) * f - cosl(x2) * g;
	return (x < 0.0L) ? -c : c;
}

long double mrpt::math::lfresnel_sin_integral(long double x) noexcept
{
	long double sqrt_2_o_pi = 7.978845608028653558798921198687637369517e-1L;
	return lfresnel_sin_alt(x / sqrt_2_o_pi);
}

long double mrpt::math::lfresnel_cos_integral(long double x) noexcept
{
	long double sqrt_2_o_pi = 7.978845608028653558798921198687637369517e-1L;
	return lfresnel_cos_alt(x / sqrt_2_o_pi);
}

double mrpt::math::fresnel_sin_integral(double x) noexcept
{
	return static_cast<double>(
		lfresnel_sin_integral(static_cast<long double>(x)));
}

double mrpt::math::fresnel_cos_integral(double x) noexcept
{
	return static_cast<double>(
		lfresnel_cos_integral(static_cast<long double>(x)));
}

//                         Internally Defined Routines                        //
double Fresnel_Sine_Integral(double x);
long double xFresnel_Sine_Integral(long double x);

////////////////////////////////////////////////////////////////////////////////
// double Fresnel_Sine_Integral( double x )                                   //
//                                                                            //
//  Description:                                                              //
//     The Fresnel sine integral, S(x), is the integral with integrand        //
//                          sqrt(2/pi) sin(t^2) dt                            //
//     where the integral extends from 0 to x.                                //
//                                                                            //
//  Arguments:                                                                //
//     double  x  The argument of the Fresnel sine integral S().              //
//                                                                            //
//  Return Value:                                                             //
//     The value of the Fresnel sine integral S evaluated at x.               //
//                                                                            //
//  Example:                                                                  //
//     double y, x;                                                           //
//                                                                            //
//     ( code to initialize x )                                               //
//                                                                            //
//     y = Fresnel_Sine_Integral( x );                                        //
////////////////////////////////////////////////////////////////////////////////
double Fresnel_Sine_Integral(double x)
{
	return (double)xFresnel_Sine_Integral((long double)x);
}

////////////////////////////////////////////////////////////////////////////////
// long double xFresnel_Sine_Integral( long double x )                        //
//                                                                            //
//  Description:                                                              //
//     The Fresnel sine integral, S(x), is the integral with integrand        //
//                          sqrt(2/pi) sin(t^2) dt                            //
//     where the integral extends from 0 to x.                                //
//                                                                            //
//  Arguments:                                                                //
//     long double  x  The argument of the Fresnel sine integral S().         //
//                                                                            //
//  Return Value:                                                             //
//     The value of the Fresnel sine integral S evaluated at x.               //
//                                                                            //
//  Example:                                                                  //
//     long double y, x;                                                      //
//                                                                            //
//     ( code to initialize x )                                               //
//                                                                            //
//     y = xFresnel_Sine_Integral( x );                                       //
////////////////////////////////////////////////////////////////////////////////

long double xFresnel_Sine_Integral(long double x)
{
	long double f;
	long double g;
	long double x2;
	long double s;

	if (fabsl(x) < 0.5L) return Power_Series_S(x);

	f = xFresnel_Auxiliary_Cosine_Integral(fabsl(x));
	g = xFresnel_Auxiliary_Sine_Integral(fabsl(x));
	x2 = x * x;
	s = 0.5L - cosl(x2) * f - sinl(x2) * g;
	return (x < 0.0L) ? -s : s;
}

////////////////////////////////////////////////////////////////////////////////
// static long double Power_Series_S( long double x )                         //
//                                                                            //
//  Description:                                                              //
//     The power series representation for the Fresnel sine integral, S(x),   //
//      is                                                                    //
//               x^3 sqrt(2/pi) Sum (-x^4)^j / [(4j+3) (2j+1)!]               //
//     where the sum extends over j = 0, ,,,.                                 //
//                                                                            //
//  Arguments:                                                                //
//     long double  x                                                         //
//                The argument of the Fresnel sine integral S().              //
//                                                                            //
//  Return Value:                                                             //
//     The value of the Fresnel sine integral S evaluated at x.               //
//                                                                            //
//  Example:                                                                  //
//     long double y, x;                                                      //
//                                                                            //
//     ( code to initialize x )                                               //
//                                                                            //
//     y = Power_Series_S( x );                                               //
////////////////////////////////////////////////////////////////////////////////

static long double Power_Series_S(long double x)
{
	long double x2 = x * x;
	long double x3 = x * x2;
	long double x4 = -x2 * x2;
	long double xn = 1.0L;
	long double Sn = 1.0L;
	long double Sm1 = 0.0L;
	long double term;
	long double factorial = 1.0L;
	long double sqrt_2_o_pi = 7.978845608028653558798921198687637369517e-1L;
	int y = 0;

	if (x == 0.0L) return 0.0L;
	Sn /= 3.0L;
	while (fabsl(Sn - Sm1) > LDBL_EPSILON * fabsl(Sm1))
	{
		Sm1 = Sn;
		y += 1;
		factorial *= (long double)(y + y);
		factorial *= (long double)(y + y + 1);
		xn *= x4;
		term = xn / factorial;
		term /= (long double)(y + y + y + y + 3);
		Sn += term;
	}
	return x3 * sqrt_2_o_pi * Sn;
}

//                         Internally Defined Routines                        //
double Fresnel_Auxiliary_Sine_Integral(double x);
long double xFresnel_Auxiliary_Sine_Integral(long double x);

//                         Internally Defined Constants                       //
static long double const sqrt_2pi = 2.506628274631000502415765284811045253006L;

////////////////////////////////////////////////////////////////////////////////
// double Fresnel_Auxiliary_Sine_Integral( double x )                         //
//                                                                            //
//  Description:                                                              //
//     The Fresnel auxiliary sine integral, g(x), is the integral from 0 to   //
//     infinity of the integrand                                              //
//                     sqrt(2/pi) exp(-2xt) sin(t^2) dt                       //
//     where x >= 0.                                                          //
//                                                                            //
//  Arguments:                                                                //
//     double  x  The argument of the Fresnel auxiliary sine integral g()     //
//                where x >= 0.                                               //
//                                                                            //
//  Return Value:                                                             //
//     The value of the Fresnel auxiliary sine integral g evaluated at        //
//     x >= 0.                                                                //
//                                                                            //
//  Example:                                                                  //
//     double y, x;                                                           //
//                                                                            //
//     ( code to initialize x )                                               //
//                                                                            //
//     y = Fresnel_Auxiliary_Sine_Integral( x );                              //
////////////////////////////////////////////////////////////////////////////////
double Fresnel_Auxiliary_Sine_Integral(double x)
{
	return (double)xFresnel_Auxiliary_Sine_Integral((long double)x);
}

////////////////////////////////////////////////////////////////////////////////
// long double xFresnel_Auxiliary_Sine_Integral( double x )                   //
//                                                                            //
//  Description:                                                              //
//     The Fresnel auxiliary sine integral, g(x), is the integral from 0 to   //
//     infinity of the integrand                                              //
//                     sqrt(2/pi) exp(-2xt) sin(t^2) dt                       //
//     where x >= 0.                                                          //
//                                                                            //
//  Arguments:                                                                //
//     long double  x  The argument of the Fresnel auxiliary sine integral    //
//                     g() where x >= 0.                                      //
//                                                                            //
//  Return Value:                                                             //
//     The value of the Fresnel auxiliary sine integral g evaluated at        //
//     x >= 0.                                                                //
//                                                                            //
//  Example:                                                                  //
//     double y, x;                                                           //
//                                                                            //
//     ( code to initialize x )                                               //
//                                                                            //
//     y = xFresnel_Auxiliary_Sine_Integral( x );                             //
////////////////////////////////////////////////////////////////////////////////

static long double sin_Chebyshev_Expansion_0_1(long double x);
static long double sin_Chebyshev_Expansion_1_3(long double x);
static long double sin_Chebyshev_Expansion_3_5(long double x);
static long double sin_Chebyshev_Expansion_5_7(long double x);
static long double sin_Asymptotic_Series(long double x);

long double xFresnel_Auxiliary_Sine_Integral(long double x)
{
	if (x == 0.0L) return 0.5L;
	if (x <= 1.0L) return sin_Chebyshev_Expansion_0_1(x);
	if (x <= 3.0L) return sin_Chebyshev_Expansion_1_3(x);
	if (x <= 5.0L) return sin_Chebyshev_Expansion_3_5(x);
	if (x <= 7.0L) return sin_Chebyshev_Expansion_5_7(x);
	return sin_Asymptotic_Series(x);
}

////////////////////////////////////////////////////////////////////////////////
// static long double Chebyshev_Expansion_0_1( long double x )                //
//                                                                            //
//  Description:                                                              //
//     Evaluate the Fresnel auxiliary sine integral, g(x), on the interval    //
//     0 < x <= 1 using the Chebyshev interpolation formula.                  //
//                                                                            //
//  Arguments:                                                                //
//     long double  x  The argument of the Fresnel auxiliary sine integral    //
//                     where 0 < x <= 1.                                      //
//                                                                            //
//  Return Value:                                                             //
//     The value of the Fresnel auxiliary sine integral g evaluated at        //
//     x where 0 < x <= 1.                                                    //
//                                                                            //
//  Example:                                                                  //
//     long double y, x;                                                      //
//                                                                            //
//     ( code to initialize x )                                               //
//                                                                            //
//     y = Chebyshev_Expansion_0_1(x);                                        //
////////////////////////////////////////////////////////////////////////////////

static long double sin_Chebyshev_Expansion_0_1(long double x)
{
	static long double const c[] = {
		+2.560134650043040830997e-1L,  -1.993005146464943284549e-1L,
		+4.025503636721387266117e-2L,  -4.459600454502960250729e-3L,
		+6.447097305145147224459e-5L,  +7.544218493763717599380e-5L,
		-1.580422720690700333493e-5L,  +1.755845848573471891519e-6L,
		-9.289769688468301734718e-8L,  -5.624033192624251079833e-9L,
		+1.854740406702369495830e-9L,  -2.174644768724492443378e-10L,
		+1.392899828133395918767e-11L, -6.989216003725983789869e-14L,
		-9.959396121060010838331e-14L, +1.312085140393647257714e-14L,
		-9.240470383522792593305e-16L, +2.472168944148817385152e-17L,
		+2.834615576069400293894e-18L, -4.650983461314449088349e-19L,
		+3.544083040732391556797e-20L};

	static const int degree = sizeof(c) / sizeof(long double) - 1;
	static const long double midpoint = 0.5L;
	static const long double scale = 0.5L;

	return xChebyshev_Tn_Series((x - midpoint) / scale, c, degree);
}

////////////////////////////////////////////////////////////////////////////////
// static long double Chebyshev_Expansion_1_3( long double x )                //
//                                                                            //
//  Description:                                                              //
//     Evaluate the Fresnel auxiliary sine integral, g(x), on the interval    //
//     1 < x <= 3 using the Chebyshev interpolation formula.                  //
//                                                                            //
//  Arguments:                                                                //
//     long double  x  The argument of the Fresnel auxiliary sine integral    //
//                     where 1 < x <= 3.                                      //
//                                                                            //
//  Return Value:                                                             //
//     The value of the Fresnel auxiliary sine integral g evaluated at        //
//     x where 1 < x <= 3.                                                    //
//                                                                            //
//  Example:                                                                  //
//     long double y, x;                                                      //
//                                                                            //
//     ( code to initialize x )                                               //
//                                                                            //
//     y = Chebyshev_Expansion_1_3(x);                                        //
////////////////////////////////////////////////////////////////////////////////

static long double sin_Chebyshev_Expansion_1_3(long double x)
{
	static long double const c[] = {
		+3.470341566046115476477e-2L,  -3.855580521778624043304e-2L,
		+1.420604309383996764083e-2L,  -4.037349972538938202143e-3L,
		+9.292478174580997778194e-4L,  -1.742730601244797978044e-4L,
		+2.563352976720387343201e-5L,  -2.498437524746606551732e-6L,
		-1.334367201897140224779e-8L,  +7.436854728157752667212e-8L,
		-2.059620371321272169176e-8L,  +3.753674773239250330547e-9L,
		-5.052913010605479996432e-10L, +4.580877371233042345794e-11L,
		-7.664740716178066564952e-13L, -7.200170736686941995387e-13L,
		+1.812701686438975518372e-13L, -2.799876487275995466163e-14L,
		+3.048940815174731772007e-15L, -1.936754063718089166725e-16L,
		-7.653673328908379651914e-18L, +4.534308864750374603371e-18L,
		-8.011054486030591219007e-19L, +9.374587915222218230337e-20L,
		-7.144943099280650363024e-21L, +1.105276695821552769144e-22L,
		+6.989334213887669628647e-23L};

	static const int degree = sizeof(c) / sizeof(long double) - 1;
	static const long double midpoint = 2.0L;

	return xChebyshev_Tn_Series((x - midpoint), c, degree);
}

////////////////////////////////////////////////////////////////////////////////
// static long double Chebyshev_Expansion_3_5( long double x )                //
//                                                                            //
//  Description:                                                              //
//     Evaluate the Fresnel auxiliary sine integral, g(x), on the interval    //
//     3 < x <= 5 using the Chebyshev interpolation formula.                  //
//                                                                            //
//  Arguments:                                                                //
//     long double  x  The argument of the Fresnel auxiliary sine integral    //
//                     where 3 < x <= 5.                                      //
//                                                                            //
//  Return Value:                                                             //
//     The value of the Fresnel auxiliary sine integral g evaluated at        //
//     x where 3 < x <= 5.                                                    //
//                                                                            //
//  Example:                                                                  //
//     long double y, x;                                                      //
//                                                                            //
//     ( code to initialize x )                                               //
//                                                                            //
//     y = Chebyshev_Expansion_3_5(x);                                        //
////////////////////////////////////////////////////////////////////////////////

static long double sin_Chebyshev_Expansion_3_5(long double x)
{
	static long double const c[] = {
		+3.684922395955255848372e-3L,  -2.624595437764014386717e-3L,
		+6.329162500611499391493e-4L,  -1.258275676151483358569e-4L,
		+2.207375763252044217165e-5L,  -3.521929664607266176132e-6L,
		+5.186211398012883705616e-7L,  -7.095056569102400546407e-8L,
		+9.030550018646936241849e-9L,  -1.066057806832232908641e-9L,
		+1.157128073917012957550e-10L, -1.133877461819345992066e-11L,
		+9.633572308791154852278e-13L, -6.336675771012312827721e-14L,
		+1.634407356931822107368e-15L, +3.944542177576016972249e-16L,
		-9.577486627424256130607e-17L, +1.428772744117447206807e-17L,
		-1.715342656474756703926e-18L, +1.753564314320837957805e-19L,
		-1.526125102356904908532e-20L, +1.070275366865736879194e-21L,
		-4.783978662888842165071e-23L};

	static const int degree = sizeof(c) / sizeof(long double) - 1;
	static const long double midpoint = 4.0L;

	return xChebyshev_Tn_Series((x - midpoint), c, degree);
}

////////////////////////////////////////////////////////////////////////////////
// static long double Chebyshev_Expansion_5_7( long double x )                //
//                                                                            //
//  Description:                                                              //
//     Evaluate the Fresnel auxiliary sine integral, g(x), on the interval    //
//     5 < x <= 7 using the Chebyshev interpolation formula.                  //
//                                                                            //
//  Arguments:                                                                //
//     long double  x  The argument of the Fresnel auxiliary sine integral    //
//                     where 5 < x <= 7.                                      //
//                                                                            //
//  Return Value:                                                             //
//     The value of the Fresnel auxiliary sine integral g evaluated at        //
//     x where 5 < x <= 7.                                                    //
//                                                                            //
//  Example:                                                                  //
//     long double y, x;                                                      //
//                                                                            //
//     ( code to initialize x )                                               //
//                                                                            //
//     y = Chebyshev_Expansion_5_7(x);                                        //
////////////////////////////////////////////////////////////////////////////////

static long double sin_Chebyshev_Expansion_5_7(long double x)
{
	static long double const c[] = {
		+1.000801217561417083840e-3L,  -4.915205279689293180607e-4L,
		+8.133163567827942356534e-5L,  -1.120758739236976144656e-5L,
		+1.384441872281356422699e-6L,  -1.586485067224130537823e-7L,
		+1.717840749804993618997e-8L,  -1.776373217323590289701e-9L,
		+1.765399783094380160549e-10L, -1.692470022450343343158e-11L,
		+1.568238301528778401489e-12L, -1.405356860742769958771e-13L,
		+1.217377701691787512346e-14L, -1.017697418261094517680e-15L,
		+8.186068056719295045596e-17L, -6.305153620995673221364e-18L,
		+4.614110100197028845266e-19L, -3.165914620159266813849e-20L,
		+1.986716456911232767045e-21L, -1.078418278174434671506e-22L,
		+4.255983404468350776788e-24L};

	static const int degree = sizeof(c) / sizeof(long double) - 1;
	static const long double midpoint = 6.0L;

	return xChebyshev_Tn_Series((x - midpoint), c, degree);
}

////////////////////////////////////////////////////////////////////////////////
// static long double Asymptotic_Series( long double x )                      //
//                                                                            //
//  Description:                                                              //
//     For a large argument x, the auxiliary Fresnel sine integral, g(x),     //
//     can be expressed as the asymptotic series                              //
//      g(x) ~ 1/(x^3 * sqrt(8pi))[1 - 15/4x^4 + 945/16x^8 + ... +            //
//                                                (4j+1)!!/(-4x^4)^j + ... ]  //
//                                                                            //
//  Arguments:                                                                //
//     long double  x  The argument of the Fresnel auxiliary sine integral    //
//                     where x > 7.                                           //
//                                                                            //
//  Return Value:                                                             //
//     The value of the Fresnel auxiliary sine integral g evaluated at        //
//     x where x > 7.                                                         //
//                                                                            //
//  Example:                                                                  //
//     long double y, x;                                                      //
//                                                                            //
//     ( code to initialize x )                                               //
//                                                                            //
//     y = Asymptotic_Series( x );                                            //
////////////////////////////////////////////////////////////////////////////////
#define NUM_ASYMPTOTIC_TERMS 35
static long double sin_Asymptotic_Series(long double x)
{
	long double x2 = x * x;
	long double x4 = -4.0L * x2 * x2;
	long double xn = 1.0L;
	long double factorial = 1.0L;
	long double g = 0.0L;
	long double term[NUM_ASYMPTOTIC_TERMS + 1];
	long double epsilon = LDBL_EPSILON / 4.0L;
	int j = 5;
	int i = 0;

	term[0] = 1.0L;
	term[NUM_ASYMPTOTIC_TERMS] = 0.0L;
	for (i = 1; i < NUM_ASYMPTOTIC_TERMS; i++)
	{
		factorial *= ((long double)j * (long double)(j - 2));
		xn *= x4;
		term[i] = factorial / xn;
		j += 4;
		if (fabsl(term[i]) >= fabsl(term[i - 1]))
		{
			i--;
			break;
		}
		if (fabsl(term[i]) <= epsilon) break;
	}
	for (; i >= 0; i--)
		g += term[i];

	g /= (x * sqrt_2pi);
	return g / (x2 + x2);
}

//                         Internally Defined Routines                        //
double Fresnel_Auxiliary_Cosine_Integral(double x);
long double xFresnel_Auxiliary_Cosine_Integral(long double x);

////////////////////////////////////////////////////////////////////////////////
// double Fresnel_Auxiliary_Cosine_Integral( double x )                       //
//                                                                            //
//  Description:                                                              //
//     The Fresnel auxiliary cosine integral, f(x), is the integral from 0 to //
//     infinity of the integrand                                              //
//                     sqrt(2/pi) exp(-2xt) cos(t^2) dt                       //
//     where x >= 0.                                                          //
//                                                                            //
//  Arguments:                                                                //
//     double  x  The argument of the Fresnel auxiliary cosine integral f()   //
//                where x >= 0.                                               //
//                                                                            //
//  Return Value:                                                             //
//     The value of the Fresnel auxiliary cosine integral f evaluated at      //
//     x >= 0.                                                                //
//                                                                            //
//  Example:                                                                  //
//     double y, x;                                                           //
//                                                                            //
//     ( code to initialize x )                                               //
//                                                                            //
//     y = Fresnel_Auxiliary_Cosine_Integral( x );                            //
////////////////////////////////////////////////////////////////////////////////
double Fresnel_Auxiliary_Cosine_Integral(double x)
{
	return (double)xFresnel_Auxiliary_Cosine_Integral((long double)x);
}

static long double cos_Chebyshev_Expansion_0_1(long double x);
static long double cos_Chebyshev_Expansion_1_3(long double x);
static long double cos_Chebyshev_Expansion_3_5(long double x);
static long double cos_Chebyshev_Expansion_5_7(long double x);
static long double cos_Asymptotic_Series(long double x);

////////////////////////////////////////////////////////////////////////////////
// long double xFresnel_Auxiliary_Cosine_Integral( double x )                 //
//                                                                            //
//  Description:                                                              //
//     The Fresnel auxiliary cosine integral, f(x), is the integral from 0 to //
//     infinity of the integrand                                              //
//                     sqrt(2/pi) exp(-2xt) cos(t^2) dt                       //
//     where x >= 0.                                                          //
//                                                                            //
//  Arguments:                                                                //
//     long double  x  The argument of the Fresnel auxiliary cosine integral  //
//                     f() where x >= 0.                                      //
//                                                                            //
//  Return Value:                                                             //
//     The value of the Fresnel auxiliary cosine integral f evaluated at      //
//     x >= 0.                                                                //
//                                                                            //
//  Example:                                                                  //
//     double y, x;                                                           //
//                                                                            //
//     ( code to initialize x )                                               //
//                                                                            //
//     y = xFresnel_Auxiliary_Cosine_Integral( x );                           //
////////////////////////////////////////////////////////////////////////////////
long double xFresnel_Auxiliary_Cosine_Integral(long double x)
{
	if (x == 0.0L) return 0.5L;
	if (x <= 1.0L) return cos_Chebyshev_Expansion_0_1(x);
	if (x <= 3.0L) return cos_Chebyshev_Expansion_1_3(x);
	if (x <= 5.0L) return cos_Chebyshev_Expansion_3_5(x);
	if (x <= 7.0L) return cos_Chebyshev_Expansion_5_7(x);
	return cos_Asymptotic_Series(x);
}

//                         Externally Defined Routines                        //
extern long double xFresnel_Auxiliary_Cosine_Integral(long double x);
extern long double xFresnel_Auxiliary_Sine_Integral(long double x);

//                         Internally Defined Routines                        //
double Fresnel_Cosine_Integral(double x);
long double xFresnel_Cosine_Integral(long double x);

////////////////////////////////////////////////////////////////////////////////
// double Fresnel_Cosine_Integral( double x )                                 //
//                                                                            //
//  Description:                                                              //
//     The Fresnel cosine integral, C(x), is the integral with integrand      //
//                          sqrt(2/pi) cos(t^2) dt                            //
//     where the integral extends from 0 to x.                                //
//                                                                            //
//  Arguments:                                                                //
//     double  x  The argument of the Fresnel cosine integral C().            //
//                                                                            //
//  Return Value:                                                             //
//     The value of the Fresnel cosine integral C evaluated at x.             //
//                                                                            //
//  Example:                                                                  //
//     double y, x;                                                           //
//                                                                            //
//     ( code to initialize x )                                               //
//                                                                            //
//     y = Fresnel_Cosine_Integral( x );                                      //
////////////////////////////////////////////////////////////////////////////////
double Fresnel_Cosine_Integral(double x)
{
	return (double)xFresnel_Cosine_Integral((long double)x);
}

////////////////////////////////////////////////////////////////////////////////
// long double xFresnel_Cosine_Integral( long double x )                      //
//                                                                            //
//  Description:                                                              //
//     The Fresnel cosine integral, C(x), is the integral with integrand      //
//                          sqrt(2/pi) cos(t^2) dt                            //
//     where the integral extends from 0 to x.                                //
//                                                                            //
//  Arguments:                                                                //
//     long double  x  The argument of the Fresnel cosine integral C().       //
//                                                                            //
//  Return Value:                                                             //
//     The value of the Fresnel cosine integral C evaluated at x.             //
//                                                                            //
//  Example:                                                                  //
//     long double y, x;                                                      //
//                                                                            //
//     ( code to initialize x )                                               //
//                                                                            //
//     y = xFresnel_Cosine_Integral( x );                                     //
////////////////////////////////////////////////////////////////////////////////

long double xFresnel_Cosine_Integral(long double x)
{
	long double f;
	long double g;
	long double x2;
	long double c;

	if (fabsl(x) < 0.5L) return Power_Series_C(x);

	f = xFresnel_Auxiliary_Cosine_Integral(fabsl(x));
	g = xFresnel_Auxiliary_Sine_Integral(fabsl(x));
	x2 = x * x;
	c = 0.5L + sinl(x2) * f - cosl(x2) * g;
	return (x < 0.0L) ? -c : c;
}

////////////////////////////////////////////////////////////////////////////////
// static long double Power_Series_C( long double x )                         //
//                                                                            //
//  Description:                                                              //
//     The power series representation for the Fresnel cosine integral, C(x), //
//      is                                                                    //
//                 x sqrt(2/pi) Sum (-x^4)^j / [(4j+1) (2j)!]                 //
//     where the sum extends over j = 0, ,,,.                                 //
//                                                                            //
//  Arguments:                                                                //
//     long double  x                                                         //
//                The argument of the Fresnel cosine integral C().            //
//                                                                            //
//  Return Value:                                                             //
//     The value of the Fresnel cosine integral C evaluated at x.             //
//                                                                            //
//  Example:                                                                  //
//     long double y, x;                                                      //
//                                                                            //
//     ( code to initialize x )                                               //
//                                                                            //
//     y = Power_Series_C( x );                                               //
////////////////////////////////////////////////////////////////////////////////

static long double Power_Series_C(long double x)
{
	long double x2 = x * x;
	//	long double x3 = x * x2;
	long double x4 = -x2 * x2;
	long double xn = 1.0L;
	long double Sn = 1.0L;
	long double Sm1 = 0.0L;
	long double term;
	long double factorial = 1.0L;
	long double sqrt_2_o_pi = 7.978845608028653558798921198687637369517e-1L;
	int y = 0;

	if (x == 0.0L) return 0.0L;
	while (fabsl(Sn - Sm1) > LDBL_EPSILON * fabsl(Sm1))
	{
		Sm1 = Sn;
		y += 1;
		factorial *= (long double)(y + y);
		factorial *= (long double)(y + y - 1);
		xn *= x4;
		term = xn / factorial;
		term /= (long double)(y + y + y + y + 1);
		Sn += term;
	}
	return x * sqrt_2_o_pi * Sn;
}

////////////////////////////////////////////////////////////////////////////////
// File: xchebyshev_Tn_series.c                                               //
// Routine(s):                                                                //
//    xChebyshev_Tn_Series                                                    //
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// long double xChebyshev_Tn_Series(long double x, long double a[],int degree)//
//                                                                            //
//  Description:                                                              //
//     This routine uses Clenshaw's recursion algorithm to evaluate a given   //
//     polynomial p(x) expressed as a linear combination of Chebyshev         //
//     polynomials of the first kind, Tn, at a point x,                       //
//       p(x) = a[0] + a[1]*T[1](x) + a[2]*T[2](x) + ... + a[deg]*T[deg](x).  //
//                                                                            //
//     Clenshaw's recursion formula applied to Chebyshev polynomials of the   //
//     first kind is:                                                         //
//     Set y[degree + 2] = 0, y[degree + 1] = 0, then for k = degree, ..., 1  //
//     set y[k] = 2 * x * y[k+1] - y[k+2] + a[k].  Finally                    //
//     set y[0] = x * y[1] - y[2] + a[0].  Then p(x) = y[0].                  //
//                                                                            //
//  Arguments:                                                                //
//     long double x                                                          //
//        The point at which to evaluate the polynomial.                      //
//     long double a[]                                                        //
//        The coefficients of the expansion in terms of Chebyshev polynomials,//
//        i.e. a[k] is the coefficient of T[k](x).  Note that in the calling  //
//        routine a must be defined double a[N] where N >= degree + 1.        //
//     int    degree                                                          //
//        The degree of the polynomial p(x).                                  //
//                                                                            //
//  Return Value:                                                             //
//     The value of the polynomial at x.                                      //
//     If degree is negative, then 0.0 is returned.                           //
//                                                                            //
//  Example:                                                                  //
//     long double x, a[N], p;                                                //
//     int    deg = N - 1;                                                    //
//                                                                            //
//     ( code to initialize x, and a[i] i = 0, ... , a[deg] )                 //
//                                                                            //
//     p = xChebyshev_Tn_Series(x, a, deg);                                   //
////////////////////////////////////////////////////////////////////////////////
static long double xChebyshev_Tn_Series(
	long double x, const long double a[], int degree)
{
	long double yp2 = 0.0L;
	long double yp1 = 0.0L;
	long double y = 0.0L;
	long double two_x = x + x;
	int k;

	// Check that degree >= 0.  If not, then return 0. //

	if (degree < 0) return 0.0L;

	// Apply Clenshaw's recursion save the last iteration. //

	for (k = degree; k >= 1; k--, yp2 = yp1, yp1 = y)
		y = two_x * yp1 - yp2 + a[k];

	// Now apply the last iteration and return the result. //

	return x * yp1 - yp2 + a[0];
}

////////////////////////////////////////////////////////////////////////////////
// static long double Chebyshev_Expansion_0_1( long double x )                //
//                                                                            //
//  Description:                                                              //
//     Evaluate the Fresnel auxiliary cosine integral, f(x), on the interval  //
//     0 < x <= 1 using the Chebyshev interpolation formula.                  //
//                                                                            //
//  Arguments:                                                                //
//     long double  x  The argument of the Fresnel auxiliary cosine integral  //
//                     where 0 < x <= 1.                                      //
//                                                                            //
//  Return Value:                                                             //
//     The value of the Fresnel auxiliary cosine integral f evaluated at      //
//     x where 0 < x <= 1.                                                    //
//                                                                            //
//  Example:                                                                  //
//     long double y, x;                                                      //
//                                                                            //
//     ( code to initialize x )                                               //
//                                                                            //
//     y = Chebyshev_Expansion_0_1(x);                                        //
////////////////////////////////////////////////////////////////////////////////

static long double cos_Chebyshev_Expansion_0_1(long double x)
{
	static long double const c[] = {
		+4.200987560240514577713e-1L,  -9.358785913634965235904e-2L,
		-7.642539415723373644927e-3L,  +4.958117751796130135544e-3L,
		-9.750236036106120253456e-4L,  +1.075201474958704192865e-4L,
		-4.415344769301324238886e-6L,  -7.861633919783064216022e-7L,
		+1.919240966215861471754e-7L,  -2.175775608982741065385e-8L,
		+1.296559541430849437217e-9L,  +2.207205095025162212169e-11L,
		-1.479219615873704298874e-11L, +1.821350127295808288614e-12L,
		-1.228919312990171362342e-13L, +2.227139250593818235212e-15L,
		+5.734729405928016301596e-16L, -8.284965573075354177016e-17L,
		+6.067422701530157308321e-18L, -1.994908519477689596319e-19L,
		-1.173365630675305693390e-20L};

	static const int degree = sizeof(c) / sizeof(long double) - 1;
	static const long double midpoint = 0.5L;
	static const long double scale = 0.5L;

	return xChebyshev_Tn_Series((x - midpoint) / scale, c, degree);
}

////////////////////////////////////////////////////////////////////////////////
// static long double Chebyshev_Expansion_1_3( long double x )                //
//                                                                            //
//  Description:                                                              //
//     Evaluate the Fresnel auxiliary cosine integral, f(x), on the interval  //
//     1 < x <= 3 using the Chebyshev interpolation formula.                  //
//                                                                            //
//  Arguments:                                                                //
//     long double  x  The argument of the Fresnel auxiliary cosine integral  //
//                     where 1 < x <= 3.                                      //
//                                                                            //
//  Return Value:                                                             //
//     The value of the Fresnel auxiliary cosine integral f evaluated at      //
//     x where 1 < x <= 3.                                                    //
//                                                                            //
//  Example:                                                                  //
//     long double y, x;                                                      //
//                                                                            //
//     ( code to initialize x )                                               //
//                                                                            //
//     y = Chebyshev_Expansion_1_3(x);                                        //
////////////////////////////////////////////////////////////////////////////////

static long double cos_Chebyshev_Expansion_1_3(long double x)
{
	static long double const c[] = {
		+2.098677278318224971989e-1L,  -9.314234883154103266195e-2L,
		+1.739905936938124979297e-2L,  -2.454274824644285136137e-3L,
		+1.589872606981337312438e-4L,  +4.203943842506079780413e-5L,
		-2.018022256093216535093e-5L,  +5.125709636776428285284e-6L,
		-9.601813551752718650057e-7L,  +1.373989484857155846826e-7L,
		-1.348105546577211255591e-8L,  +2.745868700337953872632e-10L,
		+2.401655517097260106976e-10L, -6.678059547527685587692e-11L,
		+1.140562171732840809159e-11L, -1.401526517205212219089e-12L,
		+1.105498827380224475667e-13L, +2.040731455126809208066e-16L,
		-1.946040679213045143184e-15L, +4.151821375667161733612e-16L,
		-5.642257647205149369594e-17L, +5.266176626521504829010e-18L,
		-2.299025577897146333791e-19L, -2.952226367506641078731e-20L,
		+8.760405943193778149078e-21L};

	static const int degree = sizeof(c) / sizeof(long double) - 1;
	static const long double midpoint = 2.0L;

	return xChebyshev_Tn_Series((x - midpoint), c, degree);
}

////////////////////////////////////////////////////////////////////////////////
// static long double Chebyshev_Expansion_3_5( long double x )                //
//                                                                            //
//  Description:                                                              //
//     Evaluate the Fresnel auxiliary cosine integral, g(x), on the interval  //
//     3 < x <= 5 using the Chebyshev interpolation formula.                  //
//                                                                            //
//  Arguments:                                                                //
//     long double  x  The argument of the Fresnel auxiliary cosine integral  //
//                     where 3 < x <= 5.                                      //
//                                                                            //
//  Return Value:                                                             //
//     The value of the Fresnel auxiliary cosine integral f evaluated at      //
//     x where 3 < x <= 5.                                                    //
//                                                                            //
//  Example:                                                                  //
//     long double y, x;                                                      //
//                                                                            //
//     ( code to initialize x )                                               //
//                                                                            //
//     y = Chebyshev_Expansion_3_5(x);                                        //
////////////////////////////////////////////////////////////////////////////////

static long double cos_Chebyshev_Expansion_3_5(long double x)
{
	static long double const c[] = {
		+1.025703371090289562388e-1L,  -2.569833023232301400495e-2L,
		+3.160592981728234288078e-3L,  -3.776110718882714758799e-4L,
		+4.325593433537248833341e-5L,  -4.668447489229591855730e-6L,
		+4.619254757356785108280e-7L,  -3.970436510433553795244e-8L,
		+2.535664754977344448598e-9L,  -2.108170964644819803367e-11L,
		-2.959172018518707683013e-11L, +6.727219944906606516055e-12L,
		-1.062829587519902899001e-12L, +1.402071724705287701110e-13L,
		-1.619154679722651005075e-14L, +1.651319588396970446858e-15L,
		-1.461704569438083772889e-16L, +1.053521559559583268504e-17L,
		-4.760946403462515858756e-19L, -1.803784084922403924313e-20L,
		+7.873130866418738207547e-21L};

	static const int degree = sizeof(c) / sizeof(long double) - 1;
	static const long double midpoint = 4.0L;

	return xChebyshev_Tn_Series((x - midpoint), c, degree);
}

////////////////////////////////////////////////////////////////////////////////
// static long double Chebyshev_Expansion_5_7( long double x )                //
//                                                                            //
//  Description:                                                              //
//     Evaluate the Fresnel auxiliary cosine integral, g(x), on the interval  //
//     5 < x <= 7 using the Chebyshev interpolation formula.                  //
//                                                                            //
//  Arguments:                                                                //
//     long double  x  The argument of the Fresnel auxiliary cosine integral  //
//                     where 5 < x <= 7.                                      //
//                                                                            //
//  Return Value:                                                             //
//     The value of the Fresnel auxiliary cosine integral f evaluated at      //
//     x where 5 < x <= 7.                                                    //
//                                                                            //
//  Example:                                                                  //
//     long double y, x;                                                      //
//                                                                            //
//     ( code to initialize x )                                               //
//                                                                            //
//     y = Chebyshev_Expansion_5_7(x);                                        //
////////////////////////////////////////////////////////////////////////////////

static long double cos_Chebyshev_Expansion_5_7(long double x)
{
	static long double const c[] = {
		+6.738667333400589274018e-2L,  -1.128146832637904868638e-2L,
		+9.408843234170404670278e-4L,  -7.800074103496165011747e-5L,
		+6.409101169623350885527e-6L,  -5.201350558247239981834e-7L,
		+4.151668914650221476906e-8L,  -3.242202015335530552721e-9L,
		+2.460339340900396789789e-10L, -1.796823324763304661865e-11L,
		+1.244108496436438952425e-12L, -7.950417122987063540635e-14L,
		+4.419142625999150971878e-15L, -1.759082736751040110146e-16L,
		-1.307443936270786700760e-18L, +1.362484141039320395814e-18L,
		-2.055236564763877250559e-19L, +2.329142055084791308691e-20L,
		-2.282438671525884861970e-21L};

	static const int degree = sizeof(c) / sizeof(long double) - 1;
	static const long double midpoint = 6.0L;

	return xChebyshev_Tn_Series((x - midpoint), c, degree);
}

////////////////////////////////////////////////////////////////////////////////
// static long double Asymptotic_Series( long double x )                      //
//                                                                            //
//  Description:                                                              //
//     For a large argument x, the auxiliary Fresnel cosine integral, f(x),   //
//     can be expressed as the asymptotic series                              //
//      f(x) ~ 1/(x*sqrt(2pi))[1 - 3/4x^4 + 105/16x^8 + ... +                 //
//                                                (4j-1)!!/(-4x^4)^j + ... ]  //
//                                                                            //
//  Arguments:                                                                //
//     long double  x  The argument of the Fresnel auxiliary cosine integral  //
//                     where x > 7.                                           //
//                                                                            //
//  Return Value:                                                             //
//     The value of the Fresnel auxiliary cosine integral f evaluated at      //
//     x where x > 7.                                                         //
//                                                                            //
//  Example:                                                                  //
//     long double y, x;                                                      //
//                                                                            //
//     ( code to initialize x )                                               //
//                                                                            //
//     y = Asymptotic_Series( x );                                            //
////////////////////////////////////////////////////////////////////////////////
#define NUM_ASYMPTOTIC_TERMS 35
static long double cos_Asymptotic_Series(long double x)
{
	long double x2 = x * x;
	long double x4 = -4.0L * x2 * x2;
	long double xn = 1.0L;
	long double factorial = 1.0L;
	long double f = 0.0L;
	long double term[NUM_ASYMPTOTIC_TERMS + 1];
	long double epsilon = LDBL_EPSILON / 4.0L;
	int j = 3;
	int i = 0;

	term[0] = 1.0L;
	term[NUM_ASYMPTOTIC_TERMS] = 0.0L;
	for (i = 1; i < NUM_ASYMPTOTIC_TERMS; i++)
	{
		factorial *= ((long double)j * (long double)(j - 2));
		xn *= x4;
		term[i] = factorial / xn;
		j += 4;
		if (fabsl(term[i]) >= fabsl(term[i - 1]))
		{
			i--;
			break;
		}
		if (fabsl(term[i]) <= epsilon) break;
	}

	for (; i >= 0; i--)
		f += term[i];

	return f / (x * sqrt_2pi);
}
