
/* See the documentation on the assignment of the precedence of the rules.
 */

/* Some very basic functions that are used always any way... */

/* Implementation of Nth that allows extending. */
RuleBase("Nth",{alist,aindex});
Rule("Nth",2,0,
    MathAnd(Equals(IsFunction(alist),True),
            Equals(IsInteger(aindex),True)))
     MathNth(alist,aindex);

Rule("Nth",2,1,Equals(IsList(aindex),True))
[
  Map({{ii},alist[[ii]]},{aindex});
];

Rule("Nth",2,2,
   MathAnd(
           Equals(IsGeneric(alist),True),
           Equals(GenericTypeName(alist),"Array"),
           Equals(IsInteger(aindex),True)
          )
    )
[
  ArrayGet(alist,aindex);
];

Rule("Nth",2,3,Equals(IsString(aindex),True))
[
  Nth(Assoc(aindex,alist),2);
];


Function("NrArgs",{aLeft}) Length(Listify(aLeft))-1;

Function("IsNonObject",{x}) Type(x) != "Object";

Function("IsVector",{aLeft})  IsList(aLeft);
Function("IsMatrix",{aLeft})
[
  If(IsList(aLeft),
    [
      Local(result);
      result:=True;

      /*TODO also check nr elements! */
      ForEach(item,aLeft)
        If(Not(IsList(item)),result:=False);
      result;
    ],
    False
    );
];


Function("IsRational",{aLeft}) Type(aLeft) = "/";

Function("IsRationalNumeric",{aLeft})
    Type(aLeft) = "/" And
    IsNumber(aLeft[[1]]) And
    IsNumber(aLeft[[2]]);

IsRationalOrNumber(x) <-- (IsNumber(x) Or IsRationalNumeric(x));

IsNegativeNumber(x):= IsNumber(x) And x < 0;
IsPositiveNumber(x):= IsNumber(x) And x > 0;

IsZero(x)    :=  (x+0.5)-0.5 = 0;
IsNotZero(x) :=  (x+0.5)-0.5 != 0;

IsNonZeroInteger(x) := (IsInteger(x) And x != 0);

1 # Numer(_x / _y)      <-- x;
2 # Numer(x_IsNumber)   <-- x;
1 # Denom(_x / _y)      <-- y;
2 # Denom(x_IsNumber)   <-- 1;




Set(Numeric,False);
Function("N",{aNumberBody})
[
  Local(prevNumeric,numericresult);
  prevNumeric:=Numeric;
  Numeric:=True;
  numericresult:=Apply("Eval",{aNumberBody});
  Numeric:=prevNumeric;
  numericresult;
];
HoldArg("N",aNumberBody);
UnFence("N",1);

Function("++",{aVar})
[
   MacroSet(aVar,MathAdd(Eval(aVar),1));
];
UnFence("++",1);
HoldArg("++",aVar);

Function("--",{aVar})
[
   MacroSet(aVar,MathSubtract(Eval(aVar),1));
];
UnFence("--",1);
HoldArg("--",aVar);



Function("TableForm",{list})
[
  Local(i);
  ForEach(i,list)
  [
    Write(i);
    NewLine();
  ];
  True;
];



/* Standard arithmetic */

/* TODO!
*/
RuleBase("+",{aLeft,aRight});  
RuleBase("-",{aLeft});         
RuleBase("-",{aLeft,aRight});  
RuleBase("*",{aLeft,aRight});  
RuleBase("/",{aLeft,aRight});  
RuleBase("^",{aLeft,aRight});  
RuleBase("+",{aLeft});         


/* Addition */

100 # + _x  <-- x;

50 # x_IsNumber + y_IsNumber <-- MathAdd(x,y);
100 # 0 + _x    <-- x;
100 # _x + 0    <-- x;
100 # _x + _x   <-- 2*x;
101 # _x + - _y <-- x-y;
101 # - _x + _y <-- y-x;
150 # _n1 / _d + _n2 / _d <-- (n1+n2)/d;

210 # x_IsNumber + (y_IsNumber / z_IsNumber) <--(x*z+y)/z;
210 # (y_IsNumber / z_IsNumber) + x_IsNumber <--(x*z+y)/z;
210 # (x_IsNumber / v_IsNumber) + (y_IsNumber / z_IsNumber) <--(x*z+y*v)/(v*z);


220 # + x_IsList          <-- MapSingle("+",x);
220 # x_IsList + y_IsList <-- Map("+",{x,y});


/* Subtraction arity 1 */

50 # -0 <-- 0;
55 # - (x_IsNumber) <-- MathSubtract(x);
110 # - (- _x)      <-- x;
110 # - (_x - _y) <-- y-x;
111 # - (x_IsNumber / _y) <-- (-x)/y;
200 # - (x_IsList) <-- MapSingle("-",x);

/* Subtraction arity 2 */
50  # x_IsNumber - y_IsNumber <-- MathSubtract(x,y);
100 # 0 - _x <-- -x;
100 # _x - 0 <-- x;
100 # _x - _x <-- 0;

110 # _x - (- _y) <-- x + y;
111 # (_x + _y)- _x <-- y;
111 # (_x + _y)- _y <-- x;
112 # _x - (_x + _y) <-- - y;
112 # _y - (_x + _y) <-- - x;

200 # x_IsList - y_IsList <-- Map("-",{x,y});

210 # x_IsNumber - (y_IsNumber / z_IsNumber) <--(x*z-y)/z;
210 # (y_IsNumber / z_IsNumber) - x_IsNumber <--(y-x*z)/z;
210 # (x_IsNumber / v_IsNumber) - (y_IsNumber / z_IsNumber) <--(x*z-y*v)/(v*z);


/* Multiplication */

50  # x_IsNumber * y_IsNumber <-- MathMultiply(x,y);
100 #  1  * _x  <-- x;
100 # _x  *  1  <-- x;
100 # (_f  * _x)_(f= -1)  <-- -x;
100 # (_x  * _f)_(f= -1)  <-- -x;

100 # x_IsNumber * (y_IsNumber * _z) <-- (x*y)*z;
100 # x_IsNumber * (_y * z_IsNumber) <-- (x*z)*y;

100 # (_x * _y) * _y <-- x * y^2;
100 # (_x * _y) * _x <-- y * x^2;
100 # _y * (_x * _y) <-- x * y^2;
100 # _x * (_x * _y) <-- y * x^2;

100 # (_x * _y)* _x ^ n_IsInteger <-- y * x^(n+1);
100 # (_y * _x)* _x ^ n_IsInteger <-- y * x^(n+1);

105 # x_IsNumber * -(_y) <-- (-x)*y;
105 # (-(_x)) * (y_IsNumber) <-- (-y)*x;

106 # _x * -(_y) <-- -(x*y);
106 # -(_x) * _y <-- -(x*y);


200 # x_IsMatrix * y_IsMatrix <-- 
[
   Local(i,j,k,row,result);
   result:=ZeroMatrix(Length(x),Length(y[[1]]));
   For(i:=1,i<=Length(x),i++)
   For(j:=1,j<=Length(y),j++)
   For(k:=1,k<=Length(y[[1]]),k++)
   [
     row:=result[[i]];
     row[[k]]:= row[[k]]+x[[i]][[j]]*y[[j]][[k]];
   ];
   result;
];

210 # x_IsMatrix * y_IsVector <-- 
[
   Local(i,result);
   result:={};
   For(i:=1,i<=Length(x),i++)
   [ DestructiveInsert(result,i,x[[i]] . y); ];
   result;
];


240 # (x_IsVector * y_IsNonObject)_Not(IsList(y)) <-- y*x;
241 # (x_IsNonObject * y_IsVector)_Not(IsList(x)) <--
[
   Local(i,result);
   result:={};
   For(i:=1,i<=Length(y),i++)
   [ DestructiveInsert(result,i,x * y[[i]]); ];
   result;
];

/* Note: this rule MUST be past all the transformations on
 * matrices, since they are lists also.
 */
230 # x_IsList * y_IsList <-- Map("*",{x,y});
242 # (x_IsInteger / y_IsInteger) * (v_IsInteger / w_IsInteger) <-- (x*v)/(y*w);
243 #  x_IsInteger * (y_IsInteger / z_IsInteger) <--  (x*y)/z;
243 #  (y_IsInteger / z_IsInteger) * x_IsInteger <--  (x*y)/z;

400 # _x * _x <-- x^2;
401 # 0 * _x <-- 0;
401 # _x * 0 <-- 0;


/* Division */

50 # _x / 0 <-- Infinity;
51 # (x_IsNumber / y_IsNumber)_(Numeric = True Or
                  Not(IsInteger(x) And IsInteger(y))) <-- MathDivide(x,y);


51 # x_IsNumber / y_IsNegativeNumber <-- (-x)/(-y);

51 # (x_IsNonZeroInteger / y_IsNonZeroInteger)_(MathGcd(x,y) > 1) <--
     [
       Local(gcd);
       Set(gcd,MathGcd(x,y));
       MathDivide(x,gcd)/MathDivide(y,gcd);
     ];
    

100 # _x / _x <-- 1;
100 # _x /  1 <-- x;
200 # (_x / _y)/ _z <-- x/(y*z);
201 # x_IsVector / y_IsNonObject <--
[
   Local(i,result);
   result:={};
   For(i:=1,i<=Length(x),i++)
   [ DestructiveInsert(result,i,x[[i]] / y); ];
   result;
];

230 # _x / (_y / _z) <-- (x*z)/y;
240 # x_IsList / y_IsList <-- Map("/",{x,y});
400 # 0 / _x <-- 0;

/* Faster version of raising power to 0.5 */
50 # (x_IsNumber ^ _y)_((y-0.5)=0) <-- MathSqrt(x);
60 # _x ^ 1 <-- x;
/* Regular raising to the power. */
61 # x_IsNumber ^ y_IsNumber <-- MathPower(x,y);
70  # (_x ^ m_IsNumber) ^ n_IsNumber <-- x^(n*m);
200 # x_IsList ^ n_IsList <-- Map("^",{x,n});
200 # x_IsList ^ n_IsPositiveInteger <-- Map({{xx},xx^n},{x});
400 # _x ^ 0 <-- 1;



Function("+-",{x,y})  x + -y;
Function("/-",{x,y})  x / -y;
Function("*-",{x,y})  x * -y;
Function("^-",{x,y})  x ^ -y;
Function(":=-",{xleft,yminright}) Apply(":=",{xleft,-yminright}); 
HoldArg(":=-",xleft);
HoldArg(":=-",yminright);


