Ejemplo n.º 1
0
 public Expr Num(Expr e)
 {
     if (e is IntegerNumber)
         return e;
     IntegerNumber num = 0;
     IntegerNumber den = 1;
     Expr accum = null;
     switch (head(e))
     {
         case WKSID.times:
             bool numIsNum = false;
             List<Expr> fargs1 = flattenMults(e as CompositeExpr);
             List<Expr> fargs = new List<Expr>();
             for (int i = 0; i < fargs1.Count; i++)
                 fargs.Add(Num(fargs1[i]));
             List<Expr> additions = new List<Expr>();
             List<Expr> others = new List<Expr>();
             foreach (Expr t in fargs)
             {
                 if (head(t) == WKSID.plus)
                     additions.Add(t);
                 else others.Add(t);
             }
             Expr additiveAccum = null;
             foreach (Expr t in fargs)
             { // compute fraction terms
                 Expr tval = Num(t);
                 if (tval == WellKnownSym.infinity) // mult by inf -> inf
                     return WellKnownSym.infinity;
                 DivideExpr div = new DivideExpr(tval);
                 PowerExpr pow = new PowerExpr(tval);
                 if (tval is IntegerNumber) { num.Num = (numIsNum ? num.Num : 1) * (tval as IntegerNumber).Num; numIsNum = true; }
                 else if (pow.OK && pow.PowerInt && pow.IPower < 0) den.Num *= pow.IPower;
                 else if (div.OK && div.DivisorInt) den.Num *= div.IDivisor;
                 else if (head(tval) == WKSID.times && (head(Ag(tval, 0)) == WKSID.plusminus || head(Ag(tval, 1)) == WKSID.plusminus))
                     if (additiveAccum == null)
                         additiveAccum = tval;
                     else additiveAccum = Mult(additiveAccum, tval);
                 else if (head(tval) == WKSID.times && Ag(tval, 0) is IntegerNumber)
                 {
                     DivideExpr dexp = new DivideExpr(Ag(tval, 1));
                     if (dexp.OK && dexp.DivisorInt)
                     {                   // keep integer fractions, leave others unsimplified
                         if (den.Num == dexp.IDivisor)
                         {
                             num.Num *= (Ag(tval, 0) as IntegerNumber).Num;
                             numIsNum = true;
                         }
                         else
                         {
                             num.Num = (Ag(tval, 0) as IntegerNumber).Num * num.Num;
                             den.Num *= dexp.IDivisor;
                             numIsNum = true;
                         }
                     }
                     else if (accum == null)
                         accum = tval;
                     else accum = Mult(accum, tval);
                 }
                 else if (accum == null)
                     accum = tval;
                 else accum = Mult(accum, tval);
             }
             if (den.Num == 0 && num.Num == 0) return double.NaN;
             if (den.Num == 0) return WellKnownSym.infinity; // divide by zero -> infinity
             if (num.Num == 0 && numIsNum) return 0; // terminate early if we're multiplying by 0
             Expr fraction = null;
             if (numIsNum && (den.Num != 1 || num.Num != 1))
             {
                 if ((double)num.Num / (double)den.Num == (int)num.Num / (int)den.Num) // convert frac to integer if possible
                     fraction = (int)(num.Num / den.Num);
                 else if ((double)den.Num / (double)num.Num == (int)(den.Num / num.Num)) // reduce numerator if possible
                     fraction = Divide((int)(den.Num / num.Num));
                 else fraction = Mult(num, Divide(den)); // fraction
             }
             else if (den.Num != 1)
                 fraction = Divide(den);
             if ((fraction == null || (fraction is IntegerNumber && (fraction as IntegerNumber).Num == 1)))
                 if (accum != null && additiveAccum != null)
                     return Mult(accum, additiveAccum);
                 else if (accum != null)
                     return accum;
                 else if (additiveAccum != null)
                     return additiveAccum;
             if (fraction != null && accum == null && additiveAccum == null)
                 return fraction;
             if (fraction == null && accum == null && additiveAccum == null)
                 return 1;
             if (accum == null)
                 return Plus(fraction, additiveAccum);
             else if (additiveAccum == null)
             {
                 if (head(accum) == WKSID.plus)
                 {
                     List<Expr> plusterms = new List<Expr>();
                     for (int i = 0; i < Args(accum).Length; i++)
                         plusterms.Add(Num(Mult(Args(accum)[i], fraction)));
                     return Plus(plusterms.ToArray());
                 }
                 return Mult(fraction, accum);
             }
             return Plus(Mult(fraction, accum), additiveAccum);
         case WKSID.factorial:
             {
                 Expr arg = Num(Ag(e, 0));
                 if (arg is IntegerNumber && ((IntegerNumber)arg).Num > 0) return Factorial(((IntegerNumber)arg).Num);
                 else return new CompositeExpr(WellKnownSym.factorial, arg);
             }
         case WKSID.divide:
             return Num(Power(Ag(e, 0), -1));
         case WKSID.minus:
             Expr term = Num(Ag(e, 0));
             if (term == WellKnownSym.infinity) return WellKnownSym.infinity;
             if (term is IntegerNumber) return (int)-(term as IntegerNumber).Num;
             if (term is DoubleNumber) return (double)-(term as DoubleNumber).Num;
             switch (head(term))
             {
                 case WKSID.times: // - (a 4 c) ->  a -4 c
                     List<Expr> rem = new List<Expr>();
                     bool flipped = false;
                     foreach (Expr m in Args(term))
                         if (!flipped && m is IntegerNumber)
                         {
                             flipped = true;
                             rem.Add((int)-(m as IntegerNumber).Num);
                         }
                         else rem.Add(m);
                     if (flipped)
                         return Mult(rem.ToArray());
                     break;
                 case WKSID.divide:
                     DivideExpr dexp = new DivideExpr(term);
                     if (dexp.OK && dexp.DivisorInt)
                         return Divide((int)-dexp.IDivisor);
                     break;
                 case WKSID.plus:
                     return Num(Mult(-1, term));
             }
             return Minus(term);
         case WKSID.mod:
             {
                 Expr a = Num(Ag(e, 0));
                 Expr b = Num(Ag(e, 1));
                 IntegerNumber ia = a as IntegerNumber;
                 IntegerNumber ib = b as IntegerNumber;
                 DoubleNumber da = a as DoubleNumber;
                 DoubleNumber db = b as DoubleNumber;
                 if (ia != null && ib != null) return ia.Num % ib.Num;
                 else if (ia != null && db != null) return Math.IEEERemainder(ia.Num.AsDouble(), db.Num);
                 else if (da != null && ib != null) return Math.IEEERemainder(da.Num, ib.Num.AsDouble());
                 else if (da != null && db != null) return Math.IEEERemainder(da.Num, db.Num);
                 else return new CompositeExpr(WellKnownSym.mod, a, b);
             }
         case WKSID.power:
             {
                 Expr bas = Num(Ag(e, 0));
                 Expr pow = Num(Ag(e, 1));
                 if (head(bas) == WKSID.times)
                 { // distribute (ab)^x -> a^x b^x
                     List<Expr> nargs = new List<Expr>();
                     foreach (Expr t in Args(bas))
                         nargs.Add(Power(t, pow));
                     return Num(Mult(nargs.ToArray()));
                 }
                 if (bas is IntegerNumber && pow is IntegerNumber)
                 { // compute a^b
                     BigInt basI = (bas as IntegerNumber).Num;
                     BigInt powI = (pow as IntegerNumber).Num;
                     Expr powNum = new NullExpr();
                     try
                     {
                         if (powI == -1)
                             powNum = bas;
                         else powNum = Math.Pow((int)(bas as IntegerNumber), Math.Abs((int)(pow as IntegerNumber)));
                     }
                     catch (Exception)
                     {
                         powNum = new IntegerNumber((int)FSBigInt.Pow(basI.Num, (int)FSBigInt.Abs(powI.Num)));
                     }
                     if ((powNum is DoubleNumber) && (int)(powNum as DoubleNumber).Num == (powNum as DoubleNumber).Num)
                         powNum = (int)((powNum as DoubleNumber).Num);
                     if (powI < 0)
                         return Divide(powNum);
                     return powNum;
                 }
                 if (pow is IntegerNumber)
                 {
                     BigInt powI = (pow as IntegerNumber).Num;
                     if (powI == 1) return bas;             // x^1 -> x
                     if (powI == 0) return 1;               // x^0 -> 1
                 }
                 DivideExpr dexp = new DivideExpr(pow);  // XXX- hack - x^(a/b) sometimes is an int need symbolic test
                 if (bas is IntegerNumber && dexp.OK && dexp.DivisorInt)
                 {
                     int neg = ((int)(dexp.IDivisor / 2) == ((int)dexp.IDivisor) / 2 && (bas as IntegerNumber).Num < 0) ? -1 : 1;
                     double pd = Math.Pow(neg * (int)(bas as IntegerNumber).Num, 1.0 / (int)dexp.IDivisor);
                     if (Math.Abs(pd - Math.Round(pd)) < 1e-15)
                     {
                         int p = (int)Math.Round(pd);
                         if (neg < 0)
                         {
                             int n = (int)dexp.IDivisor;
                             // (-256)^(1/8) -> -2i
                             // (-128)^(1/7)-> -2
                             // (-64)^(1/6) -> +-2i
                             // (-32)^(1/5) -> -2
                             // (-16)^(1/4) -> 2+2i
                             // (-8)^(1/3) -> -2
                             // (-4)^(1/2) -> 2i
                             if ((n + 1) / 2 == (n + 1) / 2.0) // odd fraction power is -p
                                 return -p;
                             if (n == 2)               // sqrt is p*imaginary
                                 return p == 1 ? (Expr)WellKnownSym.i : Mult(p, WellKnownSym.i);
                             Expr re = Num(Mult(p, Num(new CompositeExpr(WellKnownSym.cos, Mult(WellKnownSym.pi, Divide(n))))));
                             Expr im = Num(Mult(p, new CompositeExpr(WellKnownSym.sin, Mult(WellKnownSym.pi, Divide(n)))));
                             if (re is RealNumber && im is RealNumber)
                                 return new ComplexNumber(re as RealNumber, im as RealNumber);
                             return Mult(Num(Power(Minus(bas), Divide(n))), Power(-1, Divide(n)));
                         }
                         return (int)p;
                     }
                     int mult = 1;
                     BigInt basVal = (bas as IntegerNumber).Num;
                     while (dexp.IDivisor == 2 && (basVal >= 4 || basVal <= -4) && ((int)basVal / 4.0) == (double)(basVal / 4))
                     {
                         mult *= 2;
                         basVal = basVal / 4;
                     }
                     while (dexp.IDivisor == 2 && (basVal >= 9 || basVal <= -9) && ((int)basVal / 9.0) == (double)(basVal / 9))
                     {
                         mult *= 3;
                         basVal = basVal / 9;
                     }
                     while (dexp.IDivisor == 2 && (basVal >= 25 || basVal <= -25) && ((int)basVal / 25.0) == (double)(basVal / 25))
                     {
                         mult *= 5;
                         basVal = basVal / 25;
                     }
                     while (dexp.IDivisor == 2 && (basVal >= 49 || basVal <= -49) && ((int)basVal / 49.0) == (double)(basVal / 49))
                     {
                         mult *= 7;
                         basVal = basVal / 49;
                     }
                     if (mult != 1)
                         if (basVal != 1)
                             return Mult(mult, Power(new IntegerNumber(basVal), pow));
                         else return new IntegerNumber(basVal);
                 }
                 if (head(bas) == WKSID.divide) return Num(Divide(Power(Ag(bas, 0), pow))); // (1/x)^a -> x^(-a)bas
                 if (head(bas) == WKSID.power) return Num(Power(Ag(bas, 0), Mult(Ag(bas, 1), pow))); // (x^a)^b ->  x^(a*b)
                 RealNumber bnum = bas as RealNumber, pnum = pow as RealNumber;
                 if (bnum != null && pnum != null)
                 {
                     double b = (bnum is IntegerNumber) ? (double)((bnum as IntegerNumber).Num) : (bnum as DoubleNumber).Num;
                     double p = (pnum is IntegerNumber) ? (double)((pnum as IntegerNumber).Num) : (pnum as DoubleNumber).Num;
                     double r = Math.Pow(b, p);
                     if (Math.Abs(r - Math.Round(r)) < 1e-15)
                         return (int)Math.Round(r);
                     return r;
                 }
                 return Power(bas, pow);
             }
         case WKSID.root: // nth root (x) -> x ^(1/n)
             return Num(Power(Ag(e, 0), Divide(Ag(e, 1))));
         case WKSID.acos:
             {
                 Expr val = Num(Ag(e, 0));
                 return new CompositeExpr(WellKnownSym.acos, val);
             }
         case WKSID.magnitude:
             {
                 Expr val = Num(Ag(e, 0));
                 if (val is IntegerNumber)
                     return (val as IntegerNumber).Num.abs();
                 else if (val is DoubleNumber)
                     return Math.Abs((val as DoubleNumber).Num);
                 else return new CompositeExpr(WellKnownSym.magnitude, val);
             }
         case WKSID.asin:
             {
                 Expr val = Num(Ag(e, 0));
                 return new CompositeExpr(WellKnownSym.asin, val);
             }
         case WKSID.atan:
             {
                 Expr val = Num(Ag(e, 0));
                 return new CompositeExpr(WellKnownSym.atan, val);
             }
         case WKSID.asec:
             {
                 Expr val = Num(Ag(e, 0));
                 return new CompositeExpr(WellKnownSym.asec, val);
             }
         case WKSID.acsc:
             {
                 Expr val = Num(Ag(e, 0));
                 return new CompositeExpr(WellKnownSym.acsc, val);
             }
         case WKSID.acot:
             {
                 Expr val = Num(Ag(e, 0));
                 return new CompositeExpr(WellKnownSym.acot, val);
             }
         case WKSID.sec:
             {
                 Expr val = Num(Ag(e, 0));
                 return new CompositeExpr(WellKnownSym.sec, val);
             }
         case WKSID.csc:
             {
                 Expr val = Num(Ag(e, 0));
                 return new CompositeExpr(WellKnownSym.csc, val);
             }
         case WKSID.cot:
             {
                 Expr val = Num(Ag(e, 0));
                 return new CompositeExpr(WellKnownSym.cot, val);
             }
         case WKSID.cos:
             {
                 Expr val = Num(Ag(e, 0));
                 if (val is IntegerNumber && (val as IntegerNumber).Num == 0) //cos(0) -> 1
                     return 1;
                 return new CompositeExpr(WellKnownSym.cos, val);
             }
         case WKSID.index:
             {
                 ArrayExpr ae = null, ai = null;
                 if (!IsArrayIndex(e, ref ae, ref ai)) return e;
                 int[] inds = ConvertToInd(ai, delegate(Expr ee) { return ExprToInd(Numericize(ee)); });
                 if (inds == null) return e;
                 return Num(ae[inds]);
             }
         case WKSID.sin:
             {
                 Expr val = Num(Ag(e, 0));
                 if ((val is IntegerNumber && (val as IntegerNumber).Num == 0)) //sin(0) -> 0
                     return 0;
                 return new CompositeExpr(WellKnownSym.sin, val);
             }
         case WKSID.tan:
             {
                 Expr val = Num(Ag(e, 0));
                 if ((val is IntegerNumber && (val as IntegerNumber).Num == 0)) //tan(0) -> 0
                     return 0;
                 return new CompositeExpr(WellKnownSym.tan, val);
             }
         case WKSID.plus:
             num.Num = 0;
             foreach (Expr t in Args(e))
             {
                 Expr tval = Num(t);
                 if (tval is WellKnownSym && (tval as WellKnownSym).ID == WKSID.infinity) // add infinity -> infinity
                     return WellKnownSym.infinity;
                 if (tval is IntegerNumber)
                 {
                     num.Num += den.Num * (BigInt)tval; // accumulate numerator
                     continue;
                 }
                 if (head(tval) == WKSID.divide && Ag(tval, 0) is IntegerNumber)
                 { // accumulate divisor (& numerator)
                     BigInt oldden = den.Num;
                     BigInt newden = (BigInt)Ag(tval, 0);
                     num.Num *= newden;
                     den.Num *= newden;
                     num.Num += oldden;
                     continue;
                 }
                 if (head(tval) == WKSID.times && Ag(tval, 0) is IntegerNumber)
                 {  // accumulate fraction
                     DivideExpr dexp = new DivideExpr(Ag(tval, 1));
                     if (dexp.OK && dexp.DivisorInt)
                     {                   // keep integer fractions, leave others unsimplified
                         BigInt oldden = den.Num;
                         BigInt newnum = (BigInt)Ag(tval, 0);
                         BigInt newden = dexp.IDivisor;
                         if (oldden == newden)
                         {
                             num.Num += newnum;
                         }
                         else
                         {
                             num.Num *= newden;
                             den.Num *= newden;
                             num.Num += oldden * newnum;
                         }
                         continue;
                     }
                 }
                 if (accum == null)
                     accum = tval;
                 else
                     accum = Plus(accum, tval);
             }
             if (num.Num == 0)
             {
                 if (accum != null)
                     return accum;
                 return 0;
             }
             Expr frac = ((double)num.Num / (double)den.Num == (int)(num.Num / den.Num)) ? (Expr)(int)(num.Num / den.Num) :
                                                                                       (Expr)Mult(num, Divide(den));
             return accum == null ? frac : Plus(frac, accum);
         default:
             if (e is CompositeExpr)
             {
                 List<Expr> args = new List<Expr>();
                 foreach (Expr a in Args(e))
                     args.Add(Num(a));
                 return new CompositeExpr((e as CompositeExpr).Head.Clone(), args.ToArray());
             }
             break;
     }
     return e;
 }
Ejemplo n.º 2
0
        protected IType CheckExpr(DivideExpr expr)
        {
            IType a = CheckExpr(expr.Expr1);
            IType b = CheckExpr(expr.Expr2);

            if (!(a is NumericType) || !a.CompatibleWith(b))
            {
                AddError(String.Format("Division not possible. Incompatible types: '{0}', '{1}'. Only numeric types are supported.",
                    a.ToString(), b.ToString()), true, expr.SourcePosition);

                return NumericType.Instance;
            }

            return (a is RealType || b is RealType) ? (IType)RealType.Instance : (IType)IntType.Instance;
        }