private static Entity TryDowncast(Entity equation, VariableEntity x, Entity root) { if (!MathS.CanBeEvaluated(root)) { return(root); } var preciseValue = root.Eval(); MathS.Settings.PrecisionErrorZeroRange.Set(1e-7m); MathS.Settings.FloatToRationalIterCount.Set(20); var downcasted = Number.Functional.Downcast(preciseValue) as ComplexNumber; MathS.Settings.FloatToRationalIterCount.Unset(); MathS.Settings.PrecisionErrorZeroRange.Unset(); var errorExpr = equation.Substitute(x, downcasted); if (!MathS.CanBeEvaluated(errorExpr)) { return(root); } var error = errorExpr.Eval(); bool ComplexRational(ComplexNumber a) => a.Real.IsRational() && a.Imaginary.IsRational(); var innerSimplified = root.InnerSimplify(); return(Number.IsZero(error) && ComplexRational(downcasted) ? downcasted : innerSimplified); }
/// <summary> /// Returns true if a is inside a rect with corners from and to, /// OR a is an unevaluable expression /// </summary> /// <param name="a"></param> /// <param name="from"></param> /// <param name="to"></param> /// <returns></returns> private static bool EntityInBounds(Entity a, ComplexNumber from, ComplexNumber to) { if (!MathS.CanBeEvaluated(a)) { return(true); } var r = a.Eval(); return(r.Real >= from.Real && r.Imaginary >= from.Imaginary && r.Real <= to.Real && r.Imaginary <= to.Imaginary); }
internal static void ParseMonomial <T>(Entity aVar, Entity expr, out Entity freeMono, ref TreeAnalyzer.IPrimitive <T> power) { if (expr.FindSubtree(aVar) == null) { freeMono = expr; return; } freeMono = 1; // a * b bool allowFloat = typeof(T) == typeof(decimal); foreach (var mp in TreeAnalyzer.LinearChildren(expr, "mulf", "divf", Const.FuncIfMul)) { if (mp.FindSubtree(aVar) == null) { freeMono *= mp; } else if (mp.entType == Entity.EntType.OPERATOR && mp.Name == "powf") { var pow_num = MathS.CanBeEvaluated(mp.Children[1]) ? mp.Children[1].Eval() : mp.Children[1]; // x ^ a is bad if (pow_num.entType != Entity.EntType.NUMBER) { freeMono = null; return; } // x ^ 0.3 is bad if (!allowFloat && !pow_num.Eval().IsInteger()) { freeMono = null; return; } if (mp == aVar) { if (allowFloat) { (power as TreeAnalyzer.PrimitiveDouble).Add(pow_num.GetValue().Real); } else { (power as TreeAnalyzer.PrimitiveInt).Add(pow_num.GetValue().Real.AsInt()); } } else { if (!MathS.CanBeEvaluated(mp.Children[1])) { freeMono = null; return; } Entity tmpFree; // TODO object pow; if (typeof(T) == typeof(decimal)) { pow = new TreeAnalyzer.PrimitiveDouble(); } else { pow = new TreeAnalyzer.PrimitiveInt(); } TreeAnalyzer.IPrimitive <T> q = pow as TreeAnalyzer.IPrimitive <T>; ParseMonomial <T>(aVar, mp.Children[0], out tmpFree, ref q); if (tmpFree == null) { freeMono = null; return; } else { // Can we eval it right here? mp.Children[1] = mp.Children[1].Eval(); freeMono *= MathS.Pow(tmpFree, mp.Children[1]); power.AddMp(q.GetValue(), mp.Children[1].GetValue()); } } } else if (mp == aVar) { if (allowFloat) { (power as TreeAnalyzer.PrimitiveDouble).Add(1); } else { (power as TreeAnalyzer.PrimitiveInt).Add(1); } } else { // a ^ x, (a + x) etc. are bad if (mp.FindSubtree(aVar) != null) { freeMono = null; return; } freeMono *= mp; } } // TODO: do we need to simplify freeMono? }
/// <summary> /// Finds all alternative forms of an expression /// </summary> /// <param name="src"></param> /// <param name="level"></param> /// <returns></returns> internal static Set Alternate(Entity src, int level) { if (src.entType == Entity.EntType.NUMBER || src.entType == Entity.EntType.VARIABLE) { return(new Set(src.Copy())); } var stage1 = src.InnerSimplify(); if (stage1.entType == Entity.EntType.NUMBER) { return(new Set(stage1)); } var history = new SortedDictionary <int, Entity>(); void TryInnerSimplify(ref Entity expr) { TreeAnalyzer.Sort(ref expr, TreeAnalyzer.SortLevel.HIGH_LEVEL); expr = expr.InnerSimplify(); } void __IterAddHistory(Entity expr) { Entity refexpr = expr.DeepCopy(); TryInnerSimplify(ref refexpr); var n = refexpr.Complexity() > expr.Complexity() ? expr : refexpr; history[n.Complexity()] = n; } void AddHistory(Entity expr) { __IterAddHistory(expr); Entity _res = expr; TreeAnalyzer.InvertNegativePowers(ref _res); __IterAddHistory(_res); } AddHistory(stage1); var res = stage1.DeepCopy(); for (int i = 0; i < Math.Abs(level); i++) { if (i == 0 || i > 2) { TreeAnalyzer.Sort(ref res, TreeAnalyzer.SortLevel.HIGH_LEVEL); } else if (i == 1) { TreeAnalyzer.Sort(ref res, TreeAnalyzer.SortLevel.MIDDLE_LEVEL); } else if (i == 2) { TreeAnalyzer.Sort(ref res, TreeAnalyzer.SortLevel.LOW_LEVEL); } res = res.InnerSimplify(); if (TreeAnalyzer.Optimization.ContainsPower(res)) { TreeAnalyzer.ReplaceInPlace(Patterns.PowerRules, ref res); AddHistory(res); } { TreeAnalyzer.InvertNegativePowers(ref res); TreeAnalyzer.InvertNegativeMultipliers(ref res); TreeAnalyzer.Sort(ref res, TreeAnalyzer.SortLevel.HIGH_LEVEL); AddHistory(res); TreeAnalyzer.ReplaceInPlace(Patterns.CommonRules, ref res); AddHistory(res); TreeAnalyzer.InvertNegativePowers(ref res); } { TreeAnalyzer.InvertNegativePowers(ref res); TreeAnalyzer.ReplaceInPlace(Patterns.DivisionPreparingRules, ref res); res = res.InnerSimplify(); TreeAnalyzer.FindDivisors(ref res, (num, denom) => !MathS.CanBeEvaluated(num) && !MathS.CanBeEvaluated(denom)); } res = res.InnerSimplify(); if (TreeAnalyzer.Optimization.ContainsTrigonometric(res)) { var res1 = res.DeepCopy(); TreeAnalyzer.ReplaceInPlace(Patterns.TrigonometricRules, ref res); AddHistory(res); TreeAnalyzer.ReplaceInPlace(Patterns.ExpandTrigonometricRules, ref res1); AddHistory(res1); res = res.Complexity() > res1.Complexity() ? res1 : res; } if (TreeAnalyzer.Optimization.ContainsPower(res)) { TreeAnalyzer.ReplaceInPlace(Patterns.PowerRules, ref res); AddHistory(res); } AddHistory(res); res = history[history.Keys.Min()].DeepCopy(); } if (level > 0) // if level < 0 we don't check whether expanded version is better { var expanded = res.Expand().Simplify(-level); AddHistory(expanded); var collapsed = res.Collapse().Simplify(-level); AddHistory(collapsed); } return(new Set(history.Values.Select(p => (Piece)p).ToArray())); }
/// <summary> /// Finds all alternative forms of an expression /// </summary> /// <param name="src"></param> /// <param name="level"></param> /// <returns></returns> internal static Set Alternate(Entity src, int level) { if (src.entType == Entity.EntType.NUMBER || src.entType == Entity.EntType.VARIABLE) { return(new Set(src.Copy())); } var stage1 = src.InnerSimplify(); if (stage1.entType == Entity.EntType.NUMBER) { return(new Set(stage1)); } var history = new SortedDictionary <int, List <Entity> >(); void TryInnerSimplify(ref Entity expr) { TreeAnalyzer.Sort(ref expr, TreeAnalyzer.SortLevel.HIGH_LEVEL); expr = expr.InnerSimplify(); } // List of criterians of expr's complexity int CountExpressionComplexity(Entity expr) => MathS.Settings.ComplexityCriteria.Value(expr); void __IterAddHistory(Entity expr) { Entity refexpr = expr.DeepCopy(); TryInnerSimplify(ref refexpr); var compl1 = CountExpressionComplexity(refexpr); var compl2 = CountExpressionComplexity(expr); var n = compl1 > compl2 ? expr : refexpr; var ncompl = Math.Min(compl2, compl1); if (!history.ContainsKey(ncompl)) { history[ncompl] = new List <Entity>(); } history[ncompl].Add(n); } void AddHistory(Entity expr) { __IterAddHistory(expr); Entity _res = expr; TreeAnalyzer.InvertNegativePowers(ref _res); __IterAddHistory(_res); } AddHistory(stage1); var res = stage1.DeepCopy(); for (int i = 0; i < Math.Abs(level); i++) { if (i == 0 || i > 2) { TreeAnalyzer.Sort(ref res, TreeAnalyzer.SortLevel.HIGH_LEVEL); } else if (i == 1) { TreeAnalyzer.Sort(ref res, TreeAnalyzer.SortLevel.MIDDLE_LEVEL); } else if (i == 2) { TreeAnalyzer.Sort(ref res, TreeAnalyzer.SortLevel.LOW_LEVEL); } res = res.InnerSimplify(); if (TreeAnalyzer.Optimization.ContainsPower(res)) { TreeAnalyzer.ReplaceInPlace(Patterns.PowerRules, ref res); AddHistory(res); } { TreeAnalyzer.InvertNegativePowers(ref res); TreeAnalyzer.InvertNegativeMultipliers(ref res); TreeAnalyzer.Sort(ref res, TreeAnalyzer.SortLevel.HIGH_LEVEL); AddHistory(res); #if DEBUG var pre = res.Substitute("x", 3).InnerEval(); #endif res = res.InnerSimplify(); #if DEBUG var post = res.Substitute("x", 3).InnerEval(); if (post != pre && MathS.CanBeEvaluated(res)) { throw new SysException("Wrong inner simplification"); } #endif TreeAnalyzer.ReplaceInPlace(Patterns.CommonRules, ref res); AddHistory(res); TreeAnalyzer.InvertNegativePowers(ref res); } { TreeAnalyzer.InvertNegativePowers(ref res); TreeAnalyzer.ReplaceInPlace(Patterns.DivisionPreparingRules, ref res); res = res.InnerSimplify(); TreeAnalyzer.FindDivisors(ref res, (num, denom) => !MathS.CanBeEvaluated(num) && !MathS.CanBeEvaluated(denom)); } res = res.InnerSimplify(); if (TreeAnalyzer.Optimization.ContainsTrigonometric(res)) { var res1 = res.DeepCopy(); TreeAnalyzer.ReplaceInPlace(Patterns.TrigonometricRules, ref res); AddHistory(res); TreeAnalyzer.ReplaceInPlace(Patterns.ExpandTrigonometricRules, ref res1); AddHistory(res1); res = res.Complexity() > res1.Complexity() ? res1 : res; } if (TreeAnalyzer.Optimization.ContainsPower(res)) { TreeAnalyzer.ReplaceInPlace(Patterns.PowerRules, ref res); AddHistory(res); } AddHistory(res); res = history[history.Keys.Min()][0].DeepCopy(); } if (level > 0) // if level < 0 we don't check whether expanded version is better { var expanded = res.Expand().Simplify(-level); AddHistory(expanded); var collapsed = res.Collapse().Simplify(-level); AddHistory(collapsed); } var result = new Set(); foreach (var pair in history) { foreach (var el in pair.Value) { result.Add(el); } } return(result); }
/// <summary> /// So that any numberical operations could be performed /// </summary> /// <returns></returns> internal bool IsNumeric() => (MathS.CanBeEvaluated(LowerBound().Item1) && MathS.CanBeEvaluated(UpperBound().Item1));
/// <summary> /// All constants, no matter multiplied or divided, are numerator's coefficients: /// 2 * x / 3 => num: 2 / 3 /// /// All entities that contain x and have a real negative power are denominator's multipliers /// 2 / (x + 3) => den: [x + 3] /// 2 / ((x^(-1) + 3) * (x2 + 1)) => den: [x^(-1) + 3, x2 + 1] /// /// All entities that have complex power are considered as product of an entity with a real power /// and an entity whole power's real part is 0 /// x ^ (-1 + 2i) => num: x ^ (2i), den: [x] /// x ^ (3 - 2i) => num: x ^ (3 - 2i), den: [] /// /// </summary> /// <param name="term"></param> /// <returns></returns> internal static (Entity numerator, List <(Entity den, RealNumber pow)> denominatorMultipliers) FindFractions(Entity term, VariableEntity x) { // TODO: consider cases where we should NOT gather all powers in row Entity GetPower(Entity expr) { if (expr.entType != Entity.EntType.OPERATOR || expr.Name != "powf") { return(1); } else { return(expr.Children[1] * GetPower(expr.Children[0])); } } Entity GetBase(Entity expr) { if (expr.entType != Entity.EntType.OPERATOR || expr.Name != "powf") { return(expr); } else { return(GetBase(expr.Children[0])); } } (Entity numerator, List <(Entity den, RealNumber pow)> denominatorMultipliers)oneInfo; oneInfo.numerator = 1; // Once InnerSimplify is called, we get rid of 1 * oneInfo.denominatorMultipliers = new List <(Entity den, RealNumber pow)>(); var multipliers = TreeAnalyzer.LinearChildren(term, "mulf", "divf", Const.FuncIfMul); var res = new FractionInfoList(); foreach (var multiplyer in multipliers) { if (multiplyer.FindSubtree(x) == null) { oneInfo.numerator *= multiplyer; continue; } var power = GetPower(multiplyer); if (!MathS.CanBeEvaluated(power)) { oneInfo.numerator *= multiplyer; continue; } var preciseValue = power.Eval(); if (preciseValue.IsImaginary()) { oneInfo.numerator *= multiplyer; continue; } var realPart = preciseValue as RealNumber; if (realPart > 0) { oneInfo.numerator *= multiplyer; continue; } oneInfo.denominatorMultipliers.Add((GetBase(multiplyer), realPart)); } return(oneInfo); }
/// <summary> /// If an evaluable expression is equal to zero, true, otherwise, false /// For example, 1 - 1 is zero, but 1 + a is not /// </summary> /// <param name="e"></param> /// <returns></returns> internal static bool IsZero(Entity e) => MathS.CanBeEvaluated(e) && e.Eval() == 0;