protected IType CheckExpr(PowerExpr expr) { IType a = CheckExpr(expr.Expr1); IType b = CheckExpr(expr.Expr2); if (!(a is NumericType) || !a.CompatibleWith(b)) { AddError(String.Format("Power not possible. Incompatible types: '{0}', '{1}'. Only numeric types are supported.", a.ToString(), b.ToString()), true, expr.SourcePosition); } return (a is RealType || b is RealType) ? (IType)RealType.Instance : (IType)IntType.Instance; }
public override Value Visit(PowerExpr visitee) => this.VisitBinary(visitee, (lhs, rhs) => lhs.Power(rhs));
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; }
public Expr Power(PowerExpr power) { Expr sum1 = Builder.Multiply(power.Right.Visit(this), FunctionFactory.Ln(power.Left)); Expr sum2 = Builder.Divide(Builder.Multiply(power.Right, power.Left.Visit(this)), power.Left); Expr sum = Builder.Add(sum1, sum2); return Builder.Multiply(power, sum); }
public Number Power(PowerExpr power) { return power.Left.Visit(this) ^ power.Right.Visit(this); }