%mathpiper,def="Taylor3"



/* Taylor3, implementation of Taylor series expansion by doing calculation on series directly.
 */

Defun("Taylor3'MultiplyCoefs",{coefs1,coefs2,degree})
[
  Local(result,i,j,jset,ilimit,jlimit);
  Bind(result, ArrayCreate(AddN(degree,1),0));
  Bind(i,1);
  Bind(ilimit,AddN(degree,2));
  While (Not IsEqual(i,ilimit))
  [
//Echo(coefs1,coefs2);
    Bind(j,1);
    Bind(jlimit,AddN(degree,SubtractN(3,i)));
    While (Not IsEqual(j,jlimit))
    [
      Bind(jset,AddN(i,SubtractN(j,1)));
//Echo("index = ",i+j-1);
      ArraySet(result,jset,ArrayGet(result,jset) + ArrayGet(coefs1,i)*ArrayGet(coefs2,j));
      Bind(j,AddN(j,1));
    ];
    Bind(i,AddN(i,1));
  ];
  result;
];


Bodied("Taylor3'TaylorCoefs",0);
10 # (Taylor3'TaylorCoefs(_var,_degree)(_var)) <--
[
  Local(result);
  Bind(result,ArrayCreate(degree+1,0));
  ArraySet(result,2, 1);
  result;
//Echo("degree = ",degree);
//  BaseVector(2,degree+1);
];
20 # (Taylor3'TaylorCoefs(_var,_degree)(_atom))_(IsFreeOf(var,atom))
   <--
   [
    Local(result);
    Bind(result,ArrayCreate(degree+1,0));
    ArraySet(result,1, atom);
    result;
//     atom*BaseVector(1,degree+1);
   ];
30 # (Taylor3'TaylorCoefs(_var,_degree)(_X + _Y))
   <--
   [
     Local(result,add,i);
     Bind(result,Taylor3'TaylorCoefs(var,degree)(X));
     Bind(add, Taylor3'TaylorCoefs(var,degree)(Y));
     For(i:=1,i<=degree+1,i++)
     [
       ArraySet(result,i,ArrayGet(result,i)+ArrayGet(add,i));
     ];
     result;
   ];

30 # (Taylor3'TaylorCoefs(_var,_degree)(_X - _Y))
   <--
   [
     Local(result,add,i);
     Bind(result,Taylor3'TaylorCoefs(var,degree)(X));
     Bind(add, Taylor3'TaylorCoefs(var,degree)(Y));
     For(i:=1,i<=degree+1,i++)
     [
       ArraySet(result,i,ArrayGet(result,i)-ArrayGet(add,i));
     ];
     result;
   ];

30 # (Taylor3'TaylorCoefs(_var,_degree)(   - _Y))
   <--
   [
     Local(result,add,i);
     Bind(result,Taylor3'TaylorCoefs(var,degree)(Y));
     For(i:=1,i<=degree+1,i++)
     [
       ArraySet(result,i,-ArrayGet(result,i));
     ];
     result;
   ];

30 # (Taylor3'TaylorCoefs(_var,_degree)(_X * _Y))
   <-- Taylor3'MultiplyCoefs(
         Taylor3'TaylorCoefs(var,degree)(X),
         Taylor3'TaylorCoefs(var,degree)(Y),
         degree);

30 # (Taylor3'TaylorCoefs(_var,_degree)((_X) ^ N_IsPositiveInteger))
  <--
[
  Local(result,factor);
  factor:=Taylor3'TaylorCoefs(var,degree)(X);
  result:=ArrayCreate(degree+1,0);
  result[1] := 1;
  //TODO@@@ optimize
  While(N>0)
  [
    result:=Taylor3'MultiplyCoefs(result,factor,degree);
    N--;
  ];
  result;
];

60 # Taylor3'UniFunction("Exp")     <-- True;
60 # Taylor3'CompCoeff("Exp", _n) <-- 1/n!;

80 # Taylor3'UniFunction("Ln")     <-- False; // False because this rule is only applicable for Ln(x+1)
80 # Taylor3'CompCoeff("Ln", 0) <-- 0;
81 # Taylor3'CompCoeff("Ln", _n) <-- (-1)^(n+1)/n;

90 # Taylor3'UniFunction("Sin")           <-- True;
90 # Taylor3'CompCoeff("Sin", n_IsOdd)  <-- (-1)^((n-1)/2) / n!;
90 # Taylor3'CompCoeff("Sin", n_IsEven) <-- 0;

100 # Taylor3'UniFunction("Cos")           <-- True;
100 # Taylor3'CompCoeff("Cos", n_IsOdd)  <-- 0;
100 # Taylor3'CompCoeff("Cos", n_IsEven) <-- (-1)^(n/2) / n!;


210 # Taylor3'UniFunction(_any)_
      (
        [
          Local(result);
          result:= Deriv(var)ListToFunction({ToAtom(any),var});
          Type(result) != "Deriv";
        ]
      )           <--
      [
        True;
      ];
210 # Taylor3'CompCoeff(_any, n_IsInteger)
    <--
    [
      Limit(var,0)(Deriv(var,n)(ListToFunction({ToAtom(any),var}))/n!);
    ];



60000 # Taylor3'UniFunction(_any) <-- False;


Taylor3'FuncCoefs(_fname,_degree) <--
[
  Local(sins,i);
  Bind(sins, ArrayCreate(degree+1,0));
  For (i:=0,i<=degree,Bind(i,i+1))
  [
    ArraySet(sins,i+1, Taylor3'CompCoeff(fname,i));
  ];
  sins;
];


100 # (Taylor3'TaylorCoefs(_var,_degree)(Ln(_f)))_(Simplify(f-1) = var) <-- Taylor3'FuncCoefs("Ln",degree);


110 # (Taylor3'TaylorCoefs(_var,_degree)(f_IsFunction))_(ArgumentsCount(f) = 1 And (Taylor3'UniFunction(Type(f)))) <--
[
  Local(sins,i,j,result,xx,expr,sinfact);
  expr := f[1];
  sins:=Taylor3'FuncCoefs(Type(f),degree);
//Echo("sins = ",sins);
  expr:=Taylor3'TaylorCoefs(var,degree)expr;
  result:=ArrayCreate(degree+1,0);
  ArraySet(result,1, ArrayGet(sins,1));
  xx:=expr;
//Echo("8...",sins,expr);
  For (i:=2,i<=degree+1,i++)
  [
    Bind(sinfact,sins[i]);
//Echo("8.1..",i," ",j);
    For (j:=1,j<=degree+1,j++)
    [
      ArraySet(result,j,ArrayGet(result,j) + (ArrayGet(xx,j) * sinfact));
    ];
//Echo("8.2..");
    Bind(xx,Taylor3'MultiplyCoefs(xx,expr,degree));
//Echo("8.3..");
  ];
  result;
];


(Taylor3(_var,_degree)(_expr)) <-- Add((Taylor3'TaylorCoefs(var,degree)(expr))[1 .. degree+1]*var^(0 .. degree));
10 # (Taylor3(_x,  0, _n) _y) <--              Taylor3(x,n)              y;
20 # (Taylor3(_x, _a, _n) _y) <-- Subst(x,x-a) Taylor3(x,n) Subst(x,x+a) y;




%/mathpiper