/*
		Integration

Integration can be performed by the function Integrate, which
has two calling conventions:

*	Integrate(variable)expression
*	Integrate(variable,from,to)expression

Integrate can have its own set of rules for specific integrals,
which might return a correct answer immediately. Alternatively,
it calls the function AntiDeriv, to see if the anti-derivative
can be determined for the integral requested. If this is the
case, the anti-derivative is used to compose the output.
The function {IntegrateFinal} performs this last step. The anti-derivative
should be passed to this function.

	    Trigonometric functions

The function {HasTrig} detects that a function contains trigonometric
functions, and calls {TrigSimpCombine} to simplify the expression
such that multiplications between such functions are replaced by
additions. This is generally an easier form to integrate.

The step performed by {TrigSimpCombine} is done at the very end,
to give the transformation rules that can deal with simple trigonometric
functions a first chance to simplify it.

@@@TODO important corrollary: Integrate(x)Sin(Sin(x)) never halts!
@@@TODO add test code and fix this problem
@@@TODO fix the broken @ in functional.rep
@@@TODO make into module by using LocalSymbols


 */

////////////////////////////////////////////////////
//
// Integration calls.
//
// Determine integral by finding the anti-derivative. Determine the
// anti-derivative and pass it on to IntegrateFinal for processing
//
////////////////////////////////////////////////////

100 # (Integrate(_var,_from,_to)_expr)
    <-- IntegrateFinal(var,from,to,`AntiDeriv(@expr,@var),expr);
100 # (Integrate(_var)_f) <-- IntegrateFinal(AntiDeriv(f,var));

// Return un-evaluated, because no anti-derivative was found
10 # IntegrateFinal(_var,_from,_to,AntiDeriv(_expr,_var),_expr)
    <-- `Hold(Integrate(@var,@from,@to)@expr);
// Anti-derivative found, use for computing the integral
20 # IntegrateFinal(_var,_from,_to,_antideriv,_expr)
    <--
[
  Local(result);
  MacroLocal(var);
  MacroSet(var,to);
  result:=Eval(antideriv);
  MacroSet(var,from);
  result:=result-Eval(antideriv);
  result;
];

////////////////////////////////////////////////////
//
// Compute only the anti-derivative, if there is one
//
////////////////////////////////////////////////////
10 # IntegrateFinal(AntiDeriv(_exp,_var)) <-- `(Hold(Integrate(@var)(@exp)));
20 # IntegrateFinal(_antideriv) <-- antideriv+UniqueConstant();

////////////////////////////////////////////////////
//
// Computation of the anti-derivative
//
////////////////////////////////////////////////////

////////////////////////////////////////////////////
//
// simple arithmetic expansions:
//   Integrate(x)(f+g) = Integrate(x)(f)+Integrate(x)(g)
// etcetera
//
////////////////////////////////////////////////////
2 # AntiDeriv(_expr,_var)_Not(Contains(VarList(expr),var)) <--
    expr*var;
3 # AntiDeriv(_exx * _exy,_var)_Not(Contains(VarList(exx),var))  <--
    exx*AntiDeriv(exy,var);
3 # AntiDeriv(_exx * _exy,_var)_Not(Contains(VarList(exy),var))  <--
    exy*AntiDeriv(exx,var);
4 # AntiDeriv(_exx + _exy,_var) <-- AntiDeriv(exx,var)+AntiDeriv(exy,var);
4 # AntiDeriv(_exx - _exy,_var) <-- AntiDeriv(exx,var)-AntiDeriv(exy,var);
4 # AntiDeriv(- (_exx) ,_var) <-- -AntiDeriv(exx,var);

//
// Anti-derivative of a univariate polynomial
//
10 # AntiDeriv(UniVariate(_var,_first,_coefs),_var) <--
[
  Local(result);
  result:=FlatCopy(coefs);
  For(i:=1,i<=Length(result),i++)
  [
    result[i]:= result[i]/(first+i);
  ];
  UniVariate(var,first+1,result);
];

//
// A term is a simple term from a polynomial.
//

CanBeTerm(_expr,_var) <-- (MakeTerm(expr,var) != False);
10 # MakeTerm(     _var ^ (n_IsFreeOf(var)), _var) <-- {var,n,1};
10 # MakeTerm(_a * (_var ^ (n_IsFreeOf(var))), _var)_IsFreeOf(a,var) <-- {var,n,a};
10 # MakeTerm((_a * _var) ^ (n_IsFreeOf(var)), _var)_IsFreeOf(a,var) <-- {var,n,a^n};
10 # MakeTerm(_var ^ (n_IsFreeOf(var)) * _a, _var)_IsFreeOf(a,var) <-- {var,n,a};
20 # MakeTerm(_expr,_var) <-- False;

11 # AntiDeriv(_exx / _exy,_var)_(CanBeTerm(exy,var)) <--
    [
      Local(term);
      term:=MakeTerm(exy,var);
      AntiDeriv(exx*term[1]^(-term[2]),var)/term[3];
    ];

11 # AntiDeriv(_exy,_var)_(CanBeTerm(exy,var)) <--
    [
      Local(term);
      term:=MakeTerm(exy,var);
      (term[1]^(term[2]+1))/(term[3]*(term[2]+1));
    ];

// If the above fails, try a univariate polynomial
12 # AntiDeriv(_expr,_var)_CanBeUni(expr,var) <--
    NormalForm(AntiDeriv(Apply("MakeUni",{expr,var}),var));




/* Standard analytic function integration */


14 # AntiDeriv(Sin(_exx),_var)_
    (CanBeUni(exx,var) And Length(MakeUni(exx,var)[3]) <= 2) <--
[
  Local(uni);
  uni:=MakeUni(exx,var);
  (1/Coef(uni,1))*(-Cos(exx));
];
14 # AntiDeriv(Cos(_exx),_var)_
    (CanBeUni(exx,var) And Length(MakeUni(exx,var)[3]) <= 2) <--
[
  Local(uni);
  uni:=MakeUni(exx,var);
  (1/Coef(uni,1))*Sin(exx);
];
14 # AntiDeriv(Exp(_exx),_var)_
    (CanBeUni(exx,var) And Length(MakeUni(exx,var)[3]) <= 2) <--
[
  Local(uni);
  uni:=MakeUni(exx,var);
  (1/Coef(uni,1))*Exp(exx);
];

14 # AntiDeriv(Ln(_exx),_var)_
    (CanBeUni(exx,var) And Length(MakeUni(exx,var)[3]) <= 2) <--
[
  var*Ln(exx)-var;
];


14 # AntiDeriv(Abs(_exx),_var)_
     (CanBeUni(exx,var) And Length(MakeUni(exx,var)[3]) <= 2) <--
[
  Local(uni);
  uni:=MakeUni(exx,var);
  (Abs(exx)*exx)/(2*Coef(uni,1));
];

14 # AntiDeriv(if(_cond)(_body),_var) <--
     UnList({Atom("if"),cond,AntiDeriv(body,var)});
14 # AntiDeriv((_left) else (_right),_var) <--
     UnList({Atom("else"), (AntiDeriv(left,var)), (AntiDeriv(right,var)) } );


////////////////////////////////////////////////////
//
// Taking the anti-derivative of an expression poly(x)*f(x)
// for which poly(x) is a univariate polynomial in x and
// there exists an anti-derivative for f(x)
//
////////////////////////////////////////////////////
15 # AntiDeriv(_exx * _exy,_var)_(CanBeUni(exx,var) And
                            Type(Apply("AntiDeriv",{exy,var})) != "AntiDeriv") <--
[
  Local(anti,cf);
  anti:= `AntiDeriv(@exy,var);
  cf:=anti*Deriv(var)exx;
//  Echo({exx*anti,exx*exy,cf});
  exx*anti - `AntiDeriv(@cf,@var);
];


15 # AntiDeriv(_exy * _exx,_var)_(CanBeUni(exx,var) And
                            Type(Apply("AntiDeriv",{exy,var})) != "AntiDeriv") <--
[
  Local(anti,cf);
  anti:= `AntiDeriv(@exy,var);
  cf:=anti*Deriv(var)exx;
//  Echo({exx*anti,exx*exy,cf});
  exx*anti - `AntiDeriv(@cf,@var);
];

16 # AntiDeriv(_exy / _exx,_var)_(IsFreeOf(exx,var)) <--
(1/exx)*Apply("AntiDeriv",{exy,var});

20 # AntiDeriv(_exy / _exx,_var)_
        (CanBeUni(exx,var) And CanBeUni(exy,var) And Degree(exx,var) = 1 And Degree(exy,var) = 0) <--
[
  (exy/Coef(exx,var,1))*Ln(exx)-Apply("AntiDeriv",{Ln(exx)*Deriv(var)exy,var});
];

21 # AntiDeriv(_exy / _exx,_var)_
        (CanBeUni(exx,var) And CanBeUni(exy,var) And Degree(exx,var) > 1) <--
[
  Local(ee);
  ee:=Apart(exy/exx,var);
  Apply("AntiDeriv",{ee,var});
];

50 #  AntiDeriv(exp_HasTrig,_var) <--
[
  Apply("AntiDeriv",{TrigSimpCombine(exp),var});
/*  Apply("AntiDeriv",{Apply("TrigSimpCombine",{exp}),var});*/
];


10 # HasTrig(Sin(_x)) <-- True;
10 # HasTrig(Cos(_x)) <-- True;
20 # HasTrig(_x + _y) <-- HasTrig(x) Or HasTrig(y);
20 # HasTrig(_x - _y) <-- HasTrig(x) Or HasTrig(y);
20 # HasTrig(   - _y) <-- HasTrig(x) Or HasTrig(y);
20 # HasTrig(_x * _y) <-- HasTrig(x) Or HasTrig(y);
20 # HasTrig(_x / _y) <-- HasTrig(x) Or HasTrig(y);
20 # HasTrig(_x ^ _y) <-- HasTrig(x);
40 # HasTrig(_x) <-- False;
