/// special functions coded for Yacas by Serge Winitzki

DefLoad("specfunc.rep/gamma.ys");
DefLoad("specfunc.rep/zeta.ys");
DefLoad("specfunc.rep/bernou.ys");

/// coded by Jonathan Leto

DefLoad("specfunc.rep/bessel.ys");

/////////////////////////////////////////////////
/// Euler's Gamma function
/////////////////////////////////////////////////

/// User visible functions: Gamma(x), LnGamma(x)

5 # Gamma(Infinity)	<-- Infinity;

10 # Gamma(_n)_(IsInteger(n) And n<=0) <-- Infinity;
10 # LnGamma(_n)_(IsInteger(n) And n<=0) <-- Infinity;

20 # Gamma(n_IsRationalOrNumber)_(IsPositiveInteger(n) Or IsInteger(N(2*n))) <-- Eval((n-1)!);
20 # LnGamma(n_IsRationalOrNumber)_(IsPositiveInteger(n) Or IsInteger(N(2*n))) <-- Eval(Ln((n-1)!));

30 # Gamma(x_IsConstant)_(Numeric) <-- GammaNum(N(x));
30 # LnGamma(x_IsConstant)_(Numeric) <-- LnGammaNum(N(x));

/////////////////////////////////////////////////
/// Riemann's Zeta function
/////////////////////////////////////////////////

/// identities for exact values of Zeta

10 # Zeta(1) <-- Infinity;
10 # Zeta(0) <-- -1/2;	// let's save time
10 # Zeta(3) <-- Zeta3();	// special case
10 # Zeta(n_IsEven)_(n>0) <-- Pi^n*(2^(n-1)/n! *Abs(Bernoulli(n)));
10 # Zeta(n_IsInteger)_(n<0) <-- -Bernoulli(-n+1)/(-n+1);

/// compute numeric value
20 # Zeta(_s)_(Numeric And IsNumber(N(s))) <-- N(ZetaNum(N(s)));

/////////////////////////////////////////////////
/// Bernoulli numbers and polynomials
/////////////////////////////////////////////////

/// Bernoulli(n): interface to Bernoulli numbers
10 # Bernoulli(0) <-- 1;
10 # Bernoulli(1) <-- -1/2;
15 # Bernoulli(n_IsInteger)_(n<0) <-- Undefined;
30 # Bernoulli(n_IsOdd) <-- 0;

/// numerical computations of Bernulli numbers use two different methods, one good for small numbers and one good only for very large numbers (using Zeta function)
20 # Bernoulli(n_IsEven)_(n<=Bernoulli1Threshold) <-- BernoulliArray(n)[n+1];
20 # Bernoulli(n_IsEven)_(n>Bernoulli1Threshold) <-- Bernoulli1(n);

/// Bernoulli1Threshold could in principle be set by the user
If(Not IsBound(Bernoulli1Threshold), Bernoulli1Threshold := 20);

/// Bernoulli polynomials of degree n in variable x
Bernoulli(n_IsInteger, _x) <-- [
	Local(B, i, result);
	B := BernoulliArray(n);
	result := B[1];
	For(i:=n-1, i>=0, i--) [
		result := result * x + B[n-i+1]*Bin(n,i);
	];
	result;
];

/////////////////////////////////////////////////
/// Bessel and related functions
/////////////////////////////////////////////////

10 # BesselJ(0,0) 	<-- 1;
10 # BesselI(0,0)	<-- 1;
10 # BesselJ(_n,0)_(n>0) <-- 0;
10 # BesselI(_n,0)_(n>0) <-- 0;
10 # BesselJ(_n,0)_(n<0 And IsInteger(n)) <-- 0;
10 # BesselI(_n,0)_(n<0 And IsInteger(n)) <-- 0;
10 # BesselJ(_n,0)_(n<0 And Not IsInteger(n)) <-- Infinity;

// The following should be ComplexInfinity, if/when that is implemented
10 # BesselI(_n,0)_(n<0 And Not IsInteger(n)) <-- Infinity;

10 # BesselJ(0,Infinity)<-- 0;
20 # BesselJ(1/2,_x)	<-- Sqrt(2/(x*Pi))*Sin(x);
20 # BesselI(1/2,_x)	<-- Sqrt(2/(x*Pi))*Sinh(x);
20 # BesselJ(-1/2,_x)	<-- Sqrt(2/(x*Pi))*Cos(x);
20 # BesselJ(3/2,_x)	<-- Sqrt(2/(x*Pi))*(Sin(x)/x - Cos(x));

20 # BesselI(3/2,_x)    <-- Sqrt(2/(x*Pi))*(Cosh(x) - Sinh(x)/x);

20 # BesselJ(-3/2,_x)	<-- Sqrt(2/(x*Pi))*(Cos(x)/x + Sin(x));

20 # BesselJ(5/2,_x)	<-- Sqrt(2/(x*Pi))*((3/x^2 - 1)*Sin(x) - 3*Cos(x)/x );
20 # BesselI(5/2,_x)    <-- Sqrt(2/(x*Pi))*((3/x^2 + 1)*Sinh(x) - 3*Cosh(x)/x );

20 # BesselJ(-5/2,_x)      <-- Sqrt(2/(x*Pi))*( (3/x^2 -1)*Cos(x) + 3*Sin(x)/x );


// Forward recursion, works great, but really slow when n << x
30 # BesselJ(_n,_x)_(IsInteger(n) And Abs(x) > 2*Gamma(n)) <-- N((2*(n+1)/x)*BesselJ(n+1,x) - BesselJ(n+2,x));

30 # BesselJ(_n,_z)_(n<0 And IsInteger(n) ) <-- (-1)^n*BesselJ(-n,z);
30 # BesselI(_n,_z)_(n<0 And IsInteger(n) ) <-- BesselI(-n,z);


// When I put "And Numeric" on the next rule, I lose precision. Why ?
// Also, if I move the the "_IsComplex" to the end with "IsComplex(x)" 
// I lose precision.

//40 # BesselJ(_n,x_IsComplex)_(Abs(x)<= 2*Gamma(n) )  <-- N(BesselNsmall(n,x,0));
//40 # BesselI(_n,x_IsComplex)_(Abs(x)<= 2*Gamma(n) )  <-- N(BesselNsmall(n,x,1));
40 # BesselJ(_n,x_IsComplex)_(Abs(x)<= 2*Gamma(n) )  <-- 
[
ApproxInfSum((-1)^k*(x/2)^(2*k+c[1])/(k! * Gamma(k+c[1]+1) ),0,x,{n} );
];

40 # BesselI(_n,x_IsComplex)_(Abs(x)<= 2*Gamma(n) )  <--
[
ApproxInfSum((x/2)^(2*k+c[1])/(k! * Gamma(k+c[1]+1) ),0,x,{n} );
];


// This is buggy
40 # BesselY(_n,x_IsComplex)_(Abs(x)<= 2*Gamma(n) )  <-- N((Cos(n*Pi)*BesselJ(n,x) - BesselJ(-n,x))/Sin(Pi*n));

50 # BesselJ(0,x_IsComplex)_(Numeric) <-- N(BesselJN0(x));

//50 # BesselJ(_n_IsPositiveNumber,_z_IsComplex) <-- BesselJN(n,z);


// Ex:
// Bessel of order n:
// ApproxInfSum((-1)^k*(x/2)^(2*k+c[1])/(k! * Gamma(k+c[1]+1) ),1,x,{n} );

Function("ApproxInfSum",{expr,start,x})[
	ApproxInfSum(expr,start,x,{0});
];

/// FIXME this has a roundoff problem when Numeric=True
// Summation must be on k
Function("ApproxInfSum",{expr,start,x,c})
[
        Local(term,result,k);
        Local(prec,eps,tmp);
        prec:=GetPrecision();
        Precision(Ceil(1.2*prec)); // this is a guess
        eps:=5*10^(-prec);

        term:=1;
        k:=start;
        result:=0;
        While( Abs(term) >= eps )[
                term:=N(expr);
		//Echo({"term is ",term});
                k:=k+1;
		result:=result+term;
        ];
        Precision(prec);
        // This should not round, only truncate
        // some outputs will be off by one in the last digit
        RoundTo(result,prec);


];

/////////////////////////////////////////////////
/// Error and complementary error functions
/////////////////////////////////////////////////

10 # Erf(0)		<-- 0;
//10 # Erfc(0)		<-- 1;
10 # Erf(Infinity)	<-- 1;
10 # Erf(Undefined) <-- Undefined;
//10 # Erfc(Infinity)	<-- 0;
10 # Erf(x_IsNumber)_(x<0)	<-- Erf(-x);
40 # Erf(x_IsNumber)_(Abs(x) <= 1 )  <-- N(2/Sqrt(Pi)*ApproxInfSum((-1)^k*x^(2*k+1)/((2*k+1)*k!),0,x));

10 # Erfc(_x)		<-- 1 - Erf(x);

/////////////////////////////////////////////////
/// Fresnel integrals
/////////////////////////////////////////////////

10 # FresnelSin(0)		<-- 0;
10 # FresnelSin(Infinity)	<-- 1/2;
10 # FresnelSin(x_IsNumber)_(x<0)	<-- -FresnelSin(x);
10 # FresnelCos(0)              <-- 0;
10 # FresnelCos(Infinity)       <-- 1/2;
10 # FresnelCos(x_IsNumber)_(x<0)       <-- -FresnelCos(x);

40 # FresnelSin(x_IsNumber)_(Abs(x) <= 1) <-- N(Sqrt(2/Pi)*ApproxInfSum((-1)^(k+1)*x^(2*k+1)/(k! * (2*k+1)),1,x));
40 # FresnelCos(x_IsNumber)_(Abs(x) <= 1) <-- N(Sqrt(2/Pi)*ApproxInfSum((-1)^(k+1)*x^(4*k-3)/((4*k-3) * (2*k-2)! ),1,x));

