

/* TruncRadian truncates the radian r so it is between 0 and 2*Pi.
 * It calculates r mod 2*Pi using the required precision.
 */
TruncRadian(_r) <--
[
  Local(twopi);
  r:=N(Eval(r),2*GetPrecision());
  twopi:=N(2*Pi,2*GetPrecision());
  N(r-MathFloor(r/twopi)*twopi);
];
HoldArg("TruncRadian",r);



/* Standard analytic functions */
2 # Sin(x_IsNumber)_(Numeric) <--
[
  If(x<0 Or x>7, x:=TruncRadian(x));
  MathSin(x);
];
4 # Sin(ArcSin(_x))           <-- x;
5 # Sin(- _x)                 <-- -Sin(x);
6 # (Sin(x_IsConstant))_(IsNegativeNumber(N(x)))   <-- -Sin(N(-x));
// must prevent it from looping

110 # Sin(Complex(_r,_i)) <--
    (Exp(I*Complex(r,i)) - Exp(- I*Complex(r,i))) / (I*2) ;

1 # SinMap( _n )_(Not(IsRationalOrNumber(n))) <-- UnList({Atom("Sin"),n*Pi});
2 # SinMap( _n )_(n<0) <-- -SinMap(-n);
2 # SinMap( _n )_(n>2) <-- SinMap(Mod(n,2));
3 # SinMap( _n )_(n>1) <-- SinMap(n-2);
4 # SinMap( _n )_(n>1/2) <-- SinMap(1-n);

5 # SinMap( n_IsInteger ) <-- 0;
5 # SinMap( 1/6 ) <-- 1/2;
5 # SinMap( 1/4 ) <-- Sqrt(2)/2;
5 # SinMap( 1/3 ) <-- Sqrt(3)/2;
5 # SinMap( 1/2 ) <-- 1;
10 # SinMap(_n) <-- UnList({Atom("Sin"),n*Pi});

200 # Sin(_v)_(Not(Numeric) And CanBeUni(v,Pi) And Degree(v,Pi) < 2 And Coef(v,Pi,0) = 0) <--
[
  SinMap(Coef(v,Pi,1));
];  

2 # Cos(x_IsNumber)_(Numeric) <--
[
  If(x<0 Or x>7, x:=TruncRadian(x));
  MathCos(x);
];
4 # Cos(ArcCos(_x))           <-- x;
5 # Cos(- _x)                 <-- Cos(x);
6 # (Cos(x_IsConstant))_(IsNegativeNumber(N(x)))   <-- Cos(N(-x));
// must prevent it from looping

110 # Cos(Complex(_r,_i)) <--
    (Exp(I*Complex(r,i)) + Exp(- I*Complex(r,i))) / (2) ;

1 # CosMap( _n )_(Not(IsRationalOrNumber(n))) <-- UnList({Atom("Cos"),n*Pi});
2 # CosMap( _n )_(n<0) <-- CosMap(-n);
2 # CosMap( _n )_(n>2) <-- CosMap(Mod(n,2));
3 # CosMap( _n )_(n>1) <-- CosMap(2-n);
4 # CosMap( _n )_(n>1/2) <-- -CosMap(1-n);

5 # CosMap( 0 ) <-- 1;
5 # CosMap( 1/6 ) <-- Sqrt(3)/2;
5 # CosMap( 1/4 ) <-- Sqrt(2)/2;
5 # CosMap( 1/3 ) <-- 1/2;
5 # CosMap( 1/2 ) <-- 0;
10 # CosMap(_n) <-- UnList({Atom("Cos"),n*Pi});

200 # Cos(_v)_(Not(Numeric) And CanBeUni(v,Pi) And Degree(v,Pi) < 2 And Coef(v,Pi,0) = 0) <--
      CosMap(Coef(v,Pi,1));

400 # Cos(x_IsRationalOrNumber) <-- 
    [
     Local(ll);
     ll:= MathFloor(N(x/Pi));
     If(IsEven(ll),x:=(x - Pi*ll),x:=(-x + Pi*(ll+1)));
     UnList({Cos,x});
     ];

400 # Cos(x_IsRationalOrNumber) <-- 
    [
     Local(ll);
     ll:= MathFloor(N(Abs(x)/Pi));
     If(IsEven(ll),x:=(Abs(x) - Pi*ll),x:=(-Abs(x) + Pi*(ll+1)));
     UnList({Cos,x});
     ];



100 # Sin(_x)/Tan(_x) <-- Cos(x);
100 # Tan(_x)/Sin(_x) <-- (1/Cos(x));
100 # Tan(_x)*Cos(_x) <-- Sin(x);
100 # Cos(_x)*Tan(_x) <-- Sin(x);
100 # Sin(_x)/Cos(_x) <-- Tan(x);

2 # Exp(x_IsNumber)_(Numeric) <-- MyMathExp(x);
4 # Exp(Ln(_x))           <-- x;
110 # Exp(Complex(_r,_i)) <--  Exp(r)*(Cos(i) + I*Sin(i));
200 # Exp(0) <-- 1;
200 # Exp(-Infinity) <-- 0;
200 # Exp(Infinity) <-- Infinity;

// improve convergence of Exp(x) for large x
MathExpThreshold := If(Not IsBound(MathExpThreshold), 500);

// large positive x
10 # MyMathExp(_x) _ (x > MathExpThreshold) <-- [
	Local(i, y);
	i:=0;
	For(i:=0, x > MathExpThreshold, i++)
		x := N(x/2);
	For(y:= MathExp(x), i>0, i--)
		y := N(y*y);
	y;
	
];
// large negative x
20 # MyMathExp(_x) _ (x < N(-MathExpThreshold/2)) <-- N(1/MyMathExp(-x));
// other values of x
30 # MyMathExp(_x) <-- MathExp(x);

2 # Tan(x_IsNumber)_(Numeric) <--
[
  If(x<0 Or x>7, x:=TruncRadian(x));
  MathTan(x);
];

4 # Tan(ArcTan(_x))           <-- x;
110 # Tan(Complex(_r,_i))       <-- Sin(Complex(r,i))/Cos(Complex(r,i));

200 # Tan(0)                  <-- 0;
200 # Tan(n_IsInteger*Pi)     <-- 0;
200 # Tan(Pi*n_IsInteger)     <-- 0;
200 # Tan((n_IsInteger/2)*Pi) <-- Infinity;
200 # Tan((n_IsInteger/2)*Pi) <-- Infinity;
200 # Tan(Pi*(n_IsInteger/2)) <-- Infinity;
200 # Tan(Pi*(n_IsInteger/2)) <-- Infinity;

2 # Ln(0)                    <-- -Infinity;
2 # Ln(1)                    <-- 0;
2 # Ln(Infinity)                    <-- Infinity;
/* 2 # Ln(-Infinity)                    <-- 0; */
2 # Ln(x_IsNegativeNumber)_(Numeric) <-- Complex(MathLog(-x), Pi);
3 # Ln(x_IsNumber)_(Numeric) <-- MathLog(x);
4 # Ln(Exp(_x))              <-- x;


3 # Ln(Complex(_r,_i)) <-- Complex(Ln(Abs(Complex(r,i))), Arg(Complex(r,i)));
4 # Ln(x_IsNegativeNumber) <-- Complex(Ln(-x), Pi);

2 # ArcSin(x_IsNumber)_(Numeric) <-- MathArcSin(x);
4 # ArcSin(Sin(_x))           <-- x;
110 # ArcSin(Complex(_r,_i)) <--
    (- I) * Ln((I*Complex(r,i)) + ((1-(Complex(r,i)^2))^(1/2)));
200 # ArcSin(0) <-- 0;
200 # ArcSin(1) <-- Pi/2;
200 # ArcSin(_n)_(n = -1) <-- -Pi/2;

2 # ArcCos(x_IsNumber)_(Numeric) <-- Pi/2-MathArcSin(x);
4 # ArcCos(Cos(_x))           <-- x;
5 # (ArcTan(x_IsConstant))_(IsNegativeNumber(N(x)))   <-- -ArcTan(-x);
110 # ArcCos(Complex(_r,_i)) <--
    (- I)*Ln(Complex(r,i) + (Complex(r,i)^2 - 1)^(1/2));
   /* TODO check! */
200 # ArcCos(0) <-- Pi/2;
200 # ArcCos(1) <-- 0;
200 # ArcCos(_n)_(n = -1) <-- Pi;

2 # ArcTan(x_IsNumber)_(Numeric) <-- If(Abs(x)>1,
	Sign(x)*Pi/2-MyArcTan(1/x), /* to improve convergence */
	MyArcTan(x)
);
4 # ArcTan(Tan(_x))           <-- x;
110 # ArcTan(Complex(_r,_i)) <--
     (- I*0.5)*Ln(Complex(1,Complex(r,i))/ Complex(1, - Complex(r,i)));

200 # ArcTan(Sqrt(3)) <-- Pi/3;
200 # ArcTan(-Sqrt(3)) <-- -Pi/3;
200 # ArcTan(1) <-- Pi/4;
200 # ArcTan(0) <-- 0;
200 # ArcTan(_n)_(n = -1) <-- -Pi/4;
200 # ArcTan(Infinity) <-- Pi/2;
200 # ArcTan(-Infinity) <-- -Pi/2;

/* Threading of standard analytic functions */
Sin(xlist_IsList) <-- MapSingle("Sin",xlist);
Cos(xlist_IsList) <-- MapSingle("Cos",xlist);
Tan(xlist_IsList) <-- MapSingle("Tan",xlist);

ArcSin(xlist_IsList) <-- MapSingle("ArcSin",xlist);
ArcCos(xlist_IsList) <-- MapSingle("ArcCos",xlist);
ArcTan(xlist_IsList) <-- MapSingle("ArcTan",xlist);

Exp(xlist_IsList) <-- MapSingle("Exp",xlist);
Ln(xlist_IsList) <-- MapSingle("Ln",xlist);



10 # ContArcTan(_x,_n,_n) <-- (2*n-1);
20 # ContArcTan(_x,_n,_m) <--
[
  (2*n-1) + (n*x)^2/ContArcTan(x,n+1,m);
];

MyArcTan(x,n) :=
[
  x/ContArcTan(x,1,n);
];

MyArcTan(x) := MyArcTan(x,2*GetPrecision());

