

10 # Min(l1_IsList,l2_IsList) <-- Map("Min",{l1,l2});
10 # Max(l1_IsList,l2_IsList) <-- Map("Max",{l1,l2});

20 # Min(_l1,_l2) <-- If(l1<l2,l1,l2);
20 # Max(_l1,_l2) <-- If(l1>l2,l1,l2);

Min(list_IsList) <--
[
  Local(result);
  result:= list[1];
  ForEach(item,Tail(list)) result:=Min(result,item);
  result;
];
Max(list_IsList) <--
[
  Local(result);
  result:= list[1];
  ForEach(item,Tail(list)) result:=Max(result,item);
  result;
];

/* Factorials */

10 # 0! <-- 1;
10 # (Infinity)! <-- Infinity;
20 # ((n_IsPositiveInteger)!) <-- [
	Check(n <= 65535, "Factorial: Error: the argument " : ( ToString() Write(n) ) : " is too large, you may want to avoid exact calculation");
	MathFac(n);
];
30 # (n_IsList)! <-- MapSingle("!",n);

/* formulae for half-integer factorials:

(+(2*z+1)/2)! = Sqrt(Pi)*(2*z+1)! / (2^(2*z+1)*z!) for z >= 0
(-(2*z+1)/2)! = Sqrt(Pi)*(-1)^z*z!*2^(2*z) / (2*z)! for z >= 0

Staggered factorials are more efficient:
	(2*n-1)!! := 1*3*...*(2*n-1) = (2*n)! / (2^n*n!)
	(2*n)!! := 2*4*...*(2*n) = 2^n*n!

*/
/* // old version - not using staggered factorials
HalfIntegerFactorial(n_IsOdd) _ (n>0) <--
	Sqrt(Pi) * ( n! / ( 2^n*((n-1)/2)! ) );
HalfIntegerFactorial(n_IsOdd) _ (n<0)  <--
	Sqrt(Pi) * ( (-1)^((-n-1)/2)*2^(-n-1)*((-n-1)/2)! / (-n-1)! );
*/
// new version using staggered factorials
HalfIntegerFactorial(n_IsOdd) _ (n>0) <--
	Sqrt(Pi) * ( n!! / 2^((n+1)/2) );
HalfIntegerFactorial(n_IsOdd) _ (n<0)  <--
	Sqrt(Pi) * ( (-1)^((-n-1)/2)*2^((-n-1)/2) / (-n-2)!! );

/* Want to also compute (2.5)! */
40 # (n_IsRationalOrNumber)! _(Denom(Rationalize(n))=2) <-- HalfIntegerFactorial(Numer(Rationalize(n)));

/// partial factorial
n1_IsRationalOrNumber *** n2_IsRationalOrNumber <--
[
	Check(n2-n1 <= 65535, "Partial factorial: Error: the range " : ( ToString() Write(n2-n1) ) : " is too large, you may want to avoid exact calculation");
	If(n2-n1<0,
		1,
		Factorial'partial(n1, n2)
	);
];

/// recursive routine to evaluate "partial factorial" a*(a+1)*...*b
// Floor() will perform conversion to integer and a, b are numbers
2# Factorial'partial(_a, _b) _ (b-a>=4) <-- Factorial'partial(a, a+Floor((b-a)/2)) * Factorial'partial(a+Floor((b-a)/2)+1, b);
3# Factorial'partial(_a, _b) _ (b-a>=3) <-- a*(a+1)*(a+2)*(a+3);
4# Factorial'partial(_a, _b) _ (b-a>=2) <-- a*(a+1)*(a+2);
5# Factorial'partial(_a, _b) _ (b-a>=1) <-- a*(a+1);
6# Factorial'partial(_a, _b) _ (b-a>=0) <-- a;


/* Binomials -- now using partial factorial for speed */
// Bin(n,m) = Bin(n, n-m)
10 # Bin(n_IsPositiveInteger,m_IsNonNegativeInteger)_(2*m <= n) <-- ((n-m+1) *** n) / m!;
15 # Bin(n_IsPositiveInteger,m_IsNonNegativeInteger)_(2*m > n And m <= n) <-- Bin(n, n-m);
20 # Bin(n_IsInteger,m_IsInteger) <-- 0;

/// even/odd staggered factorial: product of even or odd integers up to n
1# (n_IsPositiveInteger)!! _ (n<=3) <-- n;
2# (n_IsPositiveInteger)!! <--
[
	Check(n<=65535, "Staggered factorial: Error: the argument " : ( ToString() Write(n) ) : " is too large, you may want to avoid exact calculation");
	Factorial'staggered(2+Mod(n, 2), n);
];

// the purpose of this mess "Div(a+b,2)+1+Mod(Div(a+b,2)+1-a, 2)" is to obtain the smallest integer which is >= Div(a+b,2)+1 and is also odd or even when a is odd or even; we need to add at most 1 to (Div(a+b,2)+1)
2# Factorial'staggered(_a, _b) _ (b-a>=6) <-- Factorial'staggered(a, Div(a+b,2)) * Factorial'staggered(Div(a+b,2)+1+Mod(Div(a+b,2)+1-a, 2), b);
3# Factorial'staggered(_a, _b) _ (b-a>=4) <-- a*(a+2)*(a+4);
4# Factorial'staggered(_a, _b) _ (b-a>=2) <-- a*(a+2);
5# Factorial'staggered(_a, _b) <-- a;

/// staggered factorial for lists is threaded
30 # (n_IsList)!! <-- MapSingle("!!",n);



/* Sums */

RuleBase("Sum",{sumvar,sumfrom,sumto,sumbody}); 

10 # Sum(_sumvar,sumfrom_IsNumber,sumto_IsNumber,_sumbody)_(sumto<sumfrom) <--
     ApplyPure("Sum",{sumvar,sumto,sumfrom,sumbody});
20 # Sum(_sumvar,sumfrom_IsNumber,sumto_IsNumber,_sumbody) <--
[
   Local(sumi,sumsum);
   sumsum:=0;
   For(sumi:=sumfrom,sumi<=sumto,sumi++)
       [
        MacroLocal(sumvar);
        MacroSet(sumvar,sumi);
        sumsum:=sumsum+Eval(sumbody);
       ];
   sumsum;
];
UnFence("Sum",4);
HoldArg("Sum",sumvar);
HoldArg("Sum",sumbody);

Function("Sum",{sumlist})
[
   Local(sumi,sumsum);
   sumsum:=0;
   ForEach(sumi,sumlist) sumsum:=sumsum+sumi;
   sumsum;
];

Average(list):=Sum(list)/Length(list);

Function("Factorize",{sumvar,sumfrom,sumto,sumbody})
[
   Local(sumi,sumsum);
   sumsum:=1;
   For(sumi:=sumfrom,sumi<=sumto And sumsum!=0,sumi++)
       [
        MacroLocal(sumvar);
        MacroSet(sumvar,sumi);
        sumsum:=sumsum*Eval(sumbody);
       ];
   sumsum;
];
UnFence("Factorize",4);
HoldArg("Factorize",sumvar);
HoldArg("Factorize",sumbody);

Factorize(sumlist_IsList) <--
[
   Local(sumi,sumsum);
   sumsum:=1;
   ForEach(sumi,sumlist)
   [
     sumsum:=sumsum*sumi;
   ];
   sumsum;
];


Function("Taylor",{taylorvariable,taylorat,taylororder,taylorfunction})
[
  Local(n,result,dif,polf);
  [
    MacroLocal(taylorvariable);
    [
      MacroLocal(taylorvariable);
      MacroSet(taylorvariable, taylorat);
      result:=Eval(taylorfunction);
    ];
    If(result=Undefined,
    [
      result:=Apply("Limit",{taylorvariable,taylorat,taylorfunction});
    ]);
/*
    MacroSet(taylorvariable,taylorat);
    result:=Eval(taylorfunction);
*/
  ];
  dif:=taylorfunction;
  polf:=(taylorvariable-taylorat);
  For(n:=1,result != Undefined And n<=taylororder,n++)
  [
    dif:= Deriv(taylorvariable) dif;
    Local(term);
    MacroLocal(taylorvariable);
    [
      MacroLocal(taylorvariable);
      MacroSet(taylorvariable, taylorat);
      term:=Eval(dif);
    ];
    If(term=Undefined,
    [
      term:=Apply("Limit",{taylorvariable,taylorat,dif});
    ]);
      
    result:=result+(term/(n!))*(polf^n);
/*    result:=result+Apply("Limit",{taylorvariable,taylorat,(dif/(n!))})*(polf^n); */
/*
    MacroSet(taylorvariable,taylorat);
    result:=result+(Eval(dif)/(n!))*(polf^n);
*/    
  ];
  result;
];

/*
RuleBase("Fibonacci",{n});
Rule("Fibonacci",1,0, IsInteger(n) And IsZero(n-1)) 1;
Rule("Fibonacci",1,0, IsInteger(n) And IsZero(n-2)) 1;
Rule("Fibonacci",1,1, IsInteger(n) And n > 2)
[
  Local(i,a1,a2,result);
  i:=3;
  a1:=1;
  a2:=1;
  result:=a1+a2;
  While(i<n)
  [
    a1:=a2;
    a2:=result;
    result:=a1+a2;
    i++;
  ];
  result;
];
*/

10 # Lucas(0) <-- {2,1};
11 # Lucas(n_IsPositiveInteger) <--
[
   Local(result, residue, z, t);
   {z,t}:=Lucas(ShiftRight(n,1));
   residue:=Mod(n,4);
   If(residue=0, [
     t:=z*t-1;
     z:=z*z-2;
   ], If(residue=1, [
     z:=z*t-1;
     t:=2+t*t;
   ], If(residue=2, [
     t:=1+z*t;
     z:=2+z*z;
   ], If(residue=3, [
     z:=1+z*t;
     t:=t*t-2;
   ]))));
   {z,t};
];

10 # Fibonacci(n_IsPositiveInteger) <--
[
   Local(z,t);
   {z,t}:=Lucas(n-1);
   ((z << 1)+t)/5;
];

// Closed form, but insanely slow due to RadSimp
// Maybe use N() instead and call it FibonacciApprox ?
/*
10 # Fibonacci(n_IsPositiveInteger) <-- 
[	Local(a,b);
	a:= ((1+Sqrt(5))/2)^n;
	b:= ((1-Sqrt(5))/2)^n;

	RadSimp( (a-b)/Sqrt(5) );
];
*/
