/* Standard arithmetic */


/* 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;
100 # _x + n_IsConstant*(_x)   <-- (n+1)*x;
100 # n_IsConstant*(_x) + _x   <-- (n+1)*x;
100 # m_IsConstant*(_x) + n_IsConstant*(_x) <-- (m + n) * x;
100 # _x / m_IsConstant + _x <-- (m + 1) * x / m;
100 # _x + _x / m_IsConstant <-- (m + 1) * x / m;
100 # _x / m_IsConstant + _x / n_IsConstant <-- (m + n) * x / (m * n);
100 # (a_IsConstant * _x) / m_IsConstant + _x / n_IsConstant <-- (a * n + m) * x / (m * n);
100 # _x / m_IsConstant + (b_IsConstant * _x) / n_IsConstant <-- (n + b * m) * x / (m * n);
100 # (a_IsConstant * _x) / m_IsConstant + (b_IsConstant * _x) / n_IsConstant <-- (a * n + b * m) * x / (m * n);
101 # _x + - _y <-- x-y;
101 # _x + (- _y)/(_z) <-- x-(y/z);
101 # (- _y)/(_z) + _x  <-- x-(y/z);
101 # (- _x) + _y <-- y-x;
102 # _x + y_IsNegativeNumber <-- x-(-y);
102 # _x + y_IsNegativeNumber * _z <-- x-((-y)*z);
102 # _x + (y_IsNegativeNumber)/(_z) <-- x-((-y)/z);
102 # (y_IsNegativeNumber)/(_z) + _x  <-- x-((-y)/z);
102 # (x_IsNegativeNumber) + _y <-- y-(-x);
// fractions
150 # _n1 / _d + _n2 / _d <-- (n1+n2)/d;

200 # (x_IsNumber + _y)_Not(IsNumber(y)) <-- y+x;
200 # ((_y + x_IsNumber) + _z)_Not(IsNumber(y) Or IsNumber(z)) <-- (y+z)+x;
200 # ((x_IsNumber + _y) + z_IsNumber)_Not(IsNumber(y)) <-- y+(x+z);
200 # ((_x + y_IsNumber) + z_IsNumber)_Not(IsNumber(x)) <-- x+(y+z);
// fractions
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);	// this rule is never active

220 # (xlist_IsList + ylist_IsList)_(Length(xlist)=Length(ylist)) <-- Map("+",{xlist,ylist});
    
SumListSide(_x, y_IsList) <--
[
   Local(i,result);
   result:={};
   For(i:=1,i<=Length(y),i++)
   [ DestructiveInsert(result,i,x + y[i]); ];
   result;
];

240 # (x_IsList + _y)_Not(IsList(y)) <-- SumListSide(y,x);
241 # (_x + y_IsList)_Not(IsList(x)) <-- SumListSide(x,y);

250 # z_IsInfinity + Complex(_x,_y) <-- Complex(x+z,y);
250 # Complex(_x,_y) + z_IsInfinity <-- Complex(x+z,y);

251 # z_IsInfinity + _x <-- z;
251 # _x + z_IsInfinity <-- z;


250 # Undefined + _y <-- Undefined;
250 # _x + Undefined <-- Undefined;



/* Subtraction arity 1 */

//50 # -0 <-- 0;
51 # -Undefined <-- Undefined;
54 # - (- _x)      <-- x;
55 # (- (x_IsNumber)) <-- MathSubtract(0,x);
100 # _x - n_IsConstant*(_x)   <-- (1-n)*x;
100 # n_IsConstant*(_x) - _x   <-- (n-1)*x;

110 # - (_x - _y) <-- y-x;
111 # - (x_IsNumber / _y) <-- (-x)/y;
LocalSymbols(x)
[
  200 # - (x_IsList) <-- MapSingle("-",x);
];

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

110 # _x - (- _y) <-- x + y;
110 # _x - (y_IsNegativeNumber) <-- x + (-y);
111 # (_x + _y)- _x <-- y;
111 # (_x + _y)- _y <-- x;
112 # _x - (_x + _y) <-- - y;
112 # _y - (_x + _y) <-- - x;
113 # (- _x) - _y <-- -(x+y);
113 # (x_IsNegativeNumber) - _y <-- -((-x)+y);
113 # (x_IsNegativeNumber)/_y - _z <-- -((-x)/y+z);

120 # ((_x-y_IsNumber) + z_IsNumber)_Not(IsNumber(x)) <-- x+(z-y);
120 # ((_x-y_IsNumber) - z_IsNumber)_Not(IsNumber(x)) <-- x-(y+z);
130 # ((_x-y_IsNumber) + _z)_(Not IsNumber(x) And Not IsNumber(z)) <-- (x+z)-y;
130 # ((_x+y_IsNumber) - _z)_(Not IsNumber(x) And Not IsNumber(z)) <-- (x-z)+y;

140 # (x_IsNumber - _y) + z_IsNumber <-- x+z-y;

/* TODO move to this precedence everywhere? */
LocalSymbols(x,y,xarg,yarg)
[
  10 # ((x_IsList) - (y_IsList))_(Length(x)=Length(y)) <-- 
  [
    Map({{xarg,yarg},xarg-yarg},{x,y});
  ];
];

240 # (x_IsList - y_IsNonObject)_Not(IsList(y)) <-- -(y-x);

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

250 # z_IsInfinity - Complex(_x,_y) <-- Complex(-x+z,-y);
250 # Complex(_x,_y) - z_IsInfinity <-- Complex(x-z,y);

251 # z_IsInfinity - _x <-- z;
251 # _x - z_IsInfinity <-- -z;

250 # Undefined - _y <-- Undefined;
250 # _x - Undefined <-- Undefined;
// fractions
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;

95 # x_IsMatrix * y_IsMatrix <-- 
[
   Check(Length(x[1]) = Length(y), "matrix product: incompatible matrix sizes");
   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;
];


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


97 # (x_IsList * y_IsNonObject)_Not(IsList(y)) <-- y*x;
98 # (x_IsNonObject * y_IsList)_Not(IsList(x)) <--
[
   Local(i,result);
   result:={};
   For(i:=1,i<=Length(y),i++)
   [ DestructiveInsert(result,i,x * y[i]); ];
   result;
];


50  # _x * Undefined <-- Undefined;
50  # Undefined * _y <-- Undefined;


100  # 0 * Infinity <-- Undefined;
100  # Infinity * 0 <-- Undefined;

101 # 0    * (_x) <-- 0;
101 # (_x) *    0 <-- 0;

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

100 # ((x_IsNumber * _y) * _z)_(Not IsNumber(y) And Not IsNumber(z)) <-- x*(y*z);

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 / _z) <-- (x*y)/z;
// fractions
100 # (_y / _z) * _x <-- (x*y)/z;
100 # (_x * y_IsNumber)_Not(IsNumber(x)) <-- y*x;

100 # (_x) * (_x) ^ (n_IsConstant) <-- x^(n+1);
100 # (_x) ^ (n_IsConstant) * (_x) <-- x^(n+1);
100 # (_x * _y)* _x ^ n_IsConstant <-- y * x^(n+1);
100 # (_y * _x)* _x ^ n_IsConstant <-- 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);

107 # -( (-(_x))/(_y)) <-- x/y;
107 # -( (_x)/(-(_y))) <-- x/y;


250  # x_IsNumber * y_IsInfinity <-- Sign(x)*y;
250  # x_IsInfinity * y_IsNumber <-- Sign(y)*x;

260 # x_IsInfinity * y_IsInfinity <-- Sign(x) * Sign(y) * Infinity;

275 # (x_IsConstant * y_IsInfinity)_IsNumber(`N(@x)) <-- `N(@x) * y;
275 # (x_IsInfinity * y_IsConstant)_IsNumber(`N(@y)) <-- x * `N(@y);

/* Note: this rule MUST be past all the transformations on
 * matrices, since they are lists also.
 */
230 # (aLeft_IsList * aRight_IsList)_(Length(aLeft)=Length(aRight)) <-- 
	 Map("*",{aLeft,aRight});
// fractions
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;

// 605 # ((_x * y_IsAtom)*z_IsAtom)_(LessThan(String(z), String(y))) <-- (x * z) * y;
// 605 # ((_x * (y_IsAtom)^_p)*z_IsAtom)_(LessThan(String(z), String(y))) <-- (x * z) * y^p;
// 605 # ((_x * y_IsAtom)*(z_IsAtom)^_q)_(LessThan(String(z), String(y))) <-- (x * z^q) * y;
// 605 # ((_x * (y_IsAtom)^_p)*(z_IsAtom)^_q)_(LessThan(String(z), String(y))) <-- (x * z^q) * y^p;

// 610 # (x_IsAtom * y_IsAtom)_(Not IsNumber(x) And LessThan(String(y), String(x))) <-- (y * x);
// 610 # ((x_IsAtom)^_p * y_IsAtom)_(Not IsNumber(x) And LessThan(String(y), String(x))) <-- (y * x^p);
// 610 # (x_IsAtom * (y_IsAtom)^_q)_(Not IsNumber(x) And LessThan(String(y), String(x))) <-- (y^q * x);
// 610 # ((x_IsAtom)^_p * (y_IsAtom)^_q)_(Not IsNumber(x) And LessThan(String(y), String(x))) <-- (y^q * x^p);

// 620 # ((_x * y_IsFunction) * z_IsAtom)_(Not Equals(Type(y),"*") And Not Equals(Type(y),"^")) <-- (x * z) * y;
// 620 # ((_x * (y_IsFunction)^_p) * z_IsAtom)_(Not Equals(Type(y),"*") And Not Equals(Type(y),"^")) <-- (x * z) * y^p;
// 620 # (x_IsFunction * y_IsAtom)_(Not Equals(Type(x),"*") And Not Equals(Type(x),"^") And y != Infinity) <-- y * x;
// 620 # ((x_IsFunction)^_p * y_IsAtom)_(Not Equals(Type(x),"*") And Not Equals(Type(x),"^") And y != Infinity) <-- y * x^p;

/* Division */

50 # 0 / 0 <-- Undefined;

52 # x_IsPositiveNumber / 0 <-- Infinity;
52 # x_IsNegativeNumber / 0 <-- -Infinity;
55 # (_x / y_IsNumber)_(IsZero(y)) <-- Undefined;
55 # 0 / _x <-- 0;
60 # (x_IsNumber / y_IsNumber)_(InNumericMode() /* Sorry, Serge Or
                                Not(IsInteger(x) And IsInteger(y)) */ ) <--
    MathDivide(x,y);

// unnecessary rule (see #100 below). TODO: REMOVE
//55 # x_IsNumber / y_IsNegativeNumber <-- (-x)/(-y);

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

57 # ((x_IsNonZeroInteger * _expr) / y_IsNonZeroInteger)_(MathGcd(x,y) > 1) <--
     [
       Local(gcd);
       Set(x,x);
       Set(y,y);
       Set(gcd,MathGcd(x,y));
       (MathDiv(x,gcd)*expr)/MathDiv(y,gcd);
     ];

57 # ((x_IsNonZeroInteger) / (y_IsNonZeroInteger * _expr))_(MathGcd(x,y) > 1) <--
     [
       Local(gcd);
       Set(x,x);
       Set(y,y);
       Set(gcd,MathGcd(x,y));
       MathDiv(x,gcd)/(MathDiv(y,gcd)*expr);
     ];

57 # ((x_IsNonZeroInteger * _p) / (y_IsNonZeroInteger * _q))_(MathGcd(x,y) > 1) <--
     [
       Local(gcd);
       Set(x,x);
       Set(y,y);
       Set(gcd,MathGcd(x,y));
       (MathDiv(x,gcd)*p)/(MathDiv(y,gcd)*q);
     ];
    

90 # x_IsInfinity / y_IsInfinity <-- Undefined;
95  # x_IsInfinity / y_IsNumber <-- Sign(y)*x;
95  # x_IsInfinity / y_IsComplex <-- Infinity;

90 # Undefined / _y <-- Undefined;
90 # _y / Undefined <-- Undefined;


100 # _x / _x <-- 1;
100 # _x /  1 <-- x;
100 # (_x / y_IsNegativeNumber) <-- -x/(-y);
100 # (_x / - _y) <-- -x/y;
// fractions
200 # (_x / _y)/ _z <-- x/(y*z);
230 # _x / (_y / _z) <-- (x*z)/y;

240 # (xlist_IsList / ylist_IsList)_(Length(xlist)=Length(ylist)) <-- 
	 Map("/",{xlist,ylist});


250 # (x_IsList / _y)_(Not(IsList(y))) <--
[
   Local(i,result);
   result:={};
   For(i:=1,i<=Length(x),i++)
   [ DestructiveInsert(result,i,x[i] / y); ];
   result;
];

250 # (_x / y_IsList)_(Not(IsList(x))) <--
[
   Local(i,result);
   result:={};
   For(i:=1,i<=Length(y),i++)
   [ DestructiveInsert(result,i,x/y[i]); ];
   result;
];

250 # _x / Infinity <-- 0;
250 # _x / (-Infinity) <-- 0;


400 # 0 / _x <-- 0;

/* Faster version of raising power to 0.5 */
50 # _x ^ (1/2) <-- Sqrt(x);
50 # (x_IsPositiveNumber ^ (1/2))_IsInteger(MathSqrt(x)) <-- MathSqrt(x);
58 # 1 ^ n_IsInfinity <-- Undefined; 
59 # _x ^ 1 <-- x;
59 # 1 ^ _n <-- 1;
59 # x_IsZero ^ y_IsZero <-- Undefined;
60 # (x_IsZero ^ n_IsRationalOrNumber)_(n>0) <-- 0;
60 # (x_IsZero ^ n_IsRationalOrNumber)_(n<0) <-- Infinity;
// This is to fix:
// In> 0.0000^2
// Out> 0.0000^2;
// In> 0.0^2/2
// Out> 0.0^2/2;
//60 # (x_IsNumber ^ n_IsRationalOrNumber)_(x+1=1) <-- 0;

59 # _x ^ Undefined <-- Undefined;
59 # Undefined ^ _x <-- Undefined;

/* Regular raising to the power. */
61 # Infinity ^ (y_IsNegativeNumber) <-- 0;
61 # (-Infinity) ^ (y_IsNegativeNumber) <-- 0;
//61 # x_IsPositiveNumber ^ y_IsPositiveNumber <-- MathPower(x,y);
//61 # x_IsPositiveNumber ^ y_IsNegativeNumber <-- (1/MathPower(x,-y));
// integer powers are very fast
61 # x_IsPositiveNumber ^ y_IsPositiveInteger <-- MathIntPower(x,y);
61 # x_IsPositiveNumber ^ y_IsNegativeInteger <-- 1/MathIntPower(x,-y);
65 # (x_IsPositiveNumber ^ y_IsNumber)_InNumericMode() <-- Exp(y*Ln(x));

90 # (-_x)^m_IsEven <-- x^m;
91 # (x_IsConstant ^ (m_IsOdd / p_IsOdd))_(IsNegativeNumber(Re(N(Eval(x))))) <--
     -((-x)^(m/p));
92 # (x_IsNegativeNumber ^ y_IsNumber)_InNumericMode() <-- Exp(y*Ln(x));


70  # (_x ^ m_IsRationalOrNumber) ^ n_IsRationalOrNumber <-- x^(n*m);

80 # (x_IsNumber/y_IsNumber) ^ n_IsPositiveInteger <-- x^n/y^n;
80 # (x_IsNumber/y_IsNumber) ^ n_IsNegativeInteger <-- y^(-n)/x^(-n);
80 # x_IsNegativeNumber ^ n_IsEven <-- (-x)^n;
80 # x_IsNegativeNumber ^ n_IsOdd <-- -((-x)^n);


100  # ((_x)*(_x ^ _m)) <-- x^(m+1);
100  # ((_x ^ _m)*(_x)) <-- x^(m+1);
100  # ((_x ^ _n)*(_x ^ _m)) <-- x^(m+n);

100  # ((x_IsNumber)^(n_IsInteger/(_m)))_(n>1) <-- MathIntPower(x,n)^(1/m);

100 # Sqrt(_n)^(m_IsEven) <-- n^(m/2);


200 # x_IsMatrix ^ n_IsPositiveInteger <-- x*(x^(n-1));
204 # (xlist_IsList ^ nlist_IsList)_(Length(xlist)=Length(nlist)) <-- 
	 Map("^",{xlist,nlist});
205 # (xlist_IsList ^ n_IsConstant)_(Not(IsList(n))) <-- 
	 Map({{xx},xx^n},{xlist});
206 # (_x ^ n_IsList)_(Not(IsList(x))) <-- Map({{xx},x^xx},{n});
249 # x_IsInfinity ^ 0 <-- Undefined;
250 # Infinity ^ (_n) <-- Infinity;
250 # Infinity ^ (_x_IsComplex) <-- Infinity;
250 # ((-Infinity) ^ (n_IsNumber))_(IsEven(n)) <-- Infinity;
250 # ((-Infinity) ^ (n_IsNumber))_(IsOdd(n)) <-- -Infinity;

250 # (x_IsNumber ^ Infinity)_(x> -1 And x < 1) <-- 0;
250 # (x_IsNumber ^ Infinity)_(x> 1) <-- Infinity;

// these Magnitude(x)s should probably be changed to Abs(x)s

250 # (x_IsComplex ^ Infinity)_(Magnitude(x) > 1) <-- Infinity;
250 # (x_IsComplex ^ Infinity)_(Magnitude(x) < -1) <-- -Infinity;
250 # (x_IsComplex ^ Infinity)_(Magnitude(x) > -1 And Magnitude(x) < 1) <-- 0;

250 # (x_IsNumber ^ -Infinity)_(x> -1 And x < 1) <-- Infinity;
250 # (x_IsNumber ^ -Infinity)_(x< -1) <-- 0;
250 # (x_IsNumber ^ -Infinity)_(x> 1) <-- 0;

255 # (x_IsComplex ^ Infinity)_(Abs(x) = 1) <-- Undefined;
255 # (x_IsComplex ^ -Infinity)_(Abs(x) = 1) <-- Undefined;



400 # _x ^ 0 <-- 1;

500 # ((x_IsPositiveInteger)^(1/y_IsPositiveInteger))_(IsEven(y) And IsInteger(MathSqrt(x))) <-- MathSqrt(x)^(1/(y/2));
