diff --git a/AngouriMath/Convenience/MathS.cs b/AngouriMath/Convenience/MathS.cs
index 0bf0371a4..cf76bf829 100644
--- a/AngouriMath/Convenience/MathS.cs
+++ b/AngouriMath/Convenience/MathS.cs
@@ -18,6 +18,7 @@
using AngouriMath.Functions.Algebra.NumericalSolving;
using AngouriMath.Functions.Boolean;
using AngouriMath.Core.Exceptions;
+using System.Diagnostics.CodeAnalysis;
namespace AngouriMath.Core
{
@@ -862,6 +863,9 @@ public static class Boolean
public static class Utils
{
+ ///
+ /// Performs the expansion operation over the given variable
+ ///
public static Entity SmartExpandOver(Entity expr, Variable x)
{
var linChildren = Sumf.LinearChildren(expr);
@@ -878,6 +882,50 @@ public static Entity SmartExpandOver(Entity expr, Variable x)
return TreeAnalyzer.MultiHangBinary(nodes, (a, b) => a + b);
}
+
+ ///
+ /// Extracts a polynomial with integer powers
+ ///
+ /// From which to extract the polynomial
+ /// Over which variable to extract the polynomial
+ ///
+ /// Where to put the dictionary, whose keys
+ /// are powers, and values - coefficients
+ ///
+ /// Whether the input expression is a valid polynomial
+ public static bool TryGetPolynomial(Entity expr, Variable variable,
+ [NotNullWhen(true)] out Dictionary? dst)
+ => TreeAnalyzer.TryGetPolynomial(expr, variable, out dst);
+
+ ///
+ /// Extracts the linear coefficient and the bias over a variable
+ /// a x + b
+ ///
+ /// From which to extract the linear function
+ /// Over which to extract
+ /// The linear coefficient
+ /// The bias
+ /// Whether the extract was successful
+ public static bool TryGetPolyLinear(Entity expr, Variable variable,
+ [NotNullWhen(true)] out Entity? a,
+ [NotNullWhen(true)] out Entity? b)
+ => TreeAnalyzer.TryGetPolyLinear(expr, variable, out a, out b);
+
+ ///
+ /// Extracts the quadratic coefficient, linear coefficient and the bias over a variable
+ /// a x ^ 2 + b x + c
+ ///
+ /// From which to extract the quadratic function
+ /// Over which to extract
+ /// The quadratic coefficient
+ /// The linear coefficient
+ /// The bias
+ /// Whether the extract was successful
+ public static bool TryGetPolyQuadratic(Entity expr, Variable variable,
+ [NotNullWhen(true)] out Entity? a,
+ [NotNullWhen(true)] out Entity? b,
+ [NotNullWhen(true)] out Entity? c)
+ => TreeAnalyzer.TryGetPolyQuadratic(expr, variable, out a, out b, out c);
}
}
}
diff --git a/AngouriMath/Functions/Continuous/Algebra/Integration.cs b/AngouriMath/Functions/Continuous/Algebra/Integration.cs
index 187f94e75..426ce8815 100644
--- a/AngouriMath/Functions/Continuous/Algebra/Integration.cs
+++ b/AngouriMath/Functions/Continuous/Algebra/Integration.cs
@@ -47,7 +47,7 @@ public Complex DefiniteIntegral(Variable x, (EDecimal Re, EDecimal Im) from, (ED
}
namespace AngouriMath.Functions.Algebra
{
- internal static class Integration
+ public static partial class Integration
{
/// Numerical definite integration, see more in
internal static Complex Integrate(Entity func, Entity.Variable x, (EDecimal Re, EDecimal Im) from, (EDecimal Re, EDecimal Im) to, int stepCount)
diff --git a/AngouriMath/Functions/Continuous/Algebra/Integration/IndefiniteIntegralSolver.cs b/AngouriMath/Functions/Continuous/Algebra/Integration/IndefiniteIntegralSolver.cs
new file mode 100644
index 000000000..e391e885a
--- /dev/null
+++ b/AngouriMath/Functions/Continuous/Algebra/Integration/IndefiniteIntegralSolver.cs
@@ -0,0 +1,97 @@
+using System.Linq;
+
+namespace AngouriMath.Functions.Algebra
+{
+ static class IndefiniteIntegralSolver
+ {
+ internal static Entity? SolveBySplittingSum(Entity expr, Entity.Variable x)
+ {
+ var splitted = TreeAnalyzer.GatherLinearChildrenOverSumAndExpand(expr, e => e.ContainsNode(x));
+ if (splitted is null || splitted.Count < 2) return null; // nothing to do, let other solvers do the work
+ splitted[0] = Integration.ComputeIndefiniteIntegral(splitted[0], x); // base case for aggregate
+ var result = splitted.Aggregate((e1, e2) => e1 + Integration.ComputeIndefiniteIntegral(e2, x));
+ return result;
+ }
+
+ internal static Entity? SolveAsPolynomialTerm(Entity expr, Entity.Variable x) => expr switch
+ {
+ Entity.Mulf(var m1, var m2) =>
+ !m1.ContainsNode(x) ?
+ m1 * Integration.ComputeIndefiniteIntegral(m2, x) :
+ !m2.ContainsNode(x) ?
+ m2 * Integration.ComputeIndefiniteIntegral(m1, x) :
+ null,
+
+ Entity.Divf(var div, var over) =>
+ !div.ContainsNode(x) ?
+ over is Entity.Powf(var @base, var power) ?
+ div * Integration.ComputeIndefiniteIntegral(MathS.Pow(@base, -power), x) :
+ div * Integration.ComputeIndefiniteIntegral(MathS.Pow(over, -1), x) :
+ !over.ContainsNode(x) ?
+ Integration.ComputeIndefiniteIntegral(div, x) / over :
+ null,
+
+ Entity.Powf(var @base, var power) =>
+ !power.ContainsNode(x) && @base == x ?
+ power == -1 ?
+ MathS.Ln(@base) : // TODO: here should be ln(abs(x)) but for now I left it as is
+ MathS.Pow(x, power + 1) / (power + 1) :
+ null,
+
+ Entity.Variable(var v) =>
+ v == x ? MathS.Pow(x, 2) / 2 : v * x,
+
+ _ => null
+ };
+
+ internal static Entity? SolveIntegratingByParts(Entity expr, Entity.Variable x)
+ {
+ static Entity? IntegrateByParts(Entity v, Entity u, Entity.Variable x, int currentRecursion = 0)
+ {
+ if (v == 0) return 0;
+ if (currentRecursion == MathS.Settings.MaxExpansionTermCount) return null;
+
+ var integral = Integration.ComputeIndefiniteIntegral(u, x);
+ var differential = v.Derive(x).InnerSimplify();
+ var result = IntegrateByParts(differential, integral, x, currentRecursion + 1);
+ return (result is null) ? null : v * integral - result;
+ }
+
+ if (expr is Entity.Mulf(var f, var g))
+ {
+ if (MathS.TryPolynomial(f, x, out var fPoly))
+ {
+ return IntegrateByParts(fPoly, g, x);
+ }
+ if (MathS.TryPolynomial(g, x, out var gPoly))
+ {
+ return IntegrateByParts(gPoly, f, x);
+ }
+ else return null;
+ }
+ else return null;
+ }
+
+ internal static Entity? SolveLogarithmic(Entity expr, Entity.Variable x) => expr switch
+ {
+ Entity.Logf(var @base, var arg) =>
+ @base.ContainsNode(x) ?
+ Integration.ComputeIndefiniteIntegral(MathS.Ln(arg) / MathS.Ln(@base), x) :
+ arg is Entity.Powf(var y, var pow) ? // log(b, y^p) = ln(y^p) / ln(b) = ln(p) / ln(b) * ln(y)
+ Integration.ComputeIndefiniteIntegral(pow / MathS.Ln(@base) * MathS.Ln(y), x) :
+ null,
+
+ _ => null
+ };
+
+ internal static Entity? SolveExponential(Entity expr, Entity.Variable x) => expr switch
+ {
+ Entity.Powf(var @base, var pow) =>
+ @base.ContainsNode(x) ?
+ Integration.ComputeIndefiniteIntegral(MathS.Pow(MathS.e, MathS.Ln(@base) * pow), x) :
+ null,
+
+ _ => null
+ };
+ }
+}
diff --git a/AngouriMath/Functions/Continuous/Algebra/Integration/IntegralPatterns.cs b/AngouriMath/Functions/Continuous/Algebra/Integration/IntegralPatterns.cs
new file mode 100644
index 000000000..0be45e765
--- /dev/null
+++ b/AngouriMath/Functions/Continuous/Algebra/Integration/IntegralPatterns.cs
@@ -0,0 +1,40 @@
+namespace AngouriMath.Functions.Algebra
+{
+ internal static class IntegralPatterns
+ {
+ internal static Entity? TryStandardIntegrals(Entity expr, Entity.Variable x) => expr switch
+ {
+ Entity.Sinf(var arg) =>
+ TreeAnalyzer.TryGetPolyLinear(arg, x, out var a, out _) ?
+ -MathS.Cos(arg) / a :
+ null,
+
+ Entity.Cosf(var arg) =>
+ TreeAnalyzer.TryGetPolyLinear(arg, x, out var a, out _) ?
+ MathS.Sin(arg) / a :
+ null,
+
+ Entity.Tanf(var arg) =>
+ TreeAnalyzer.TryGetPolyLinear(arg, x, out var a, out _) ?
+ -MathS.Ln(MathS.Cos(arg)) / a :
+ null,
+
+ Entity.Cotanf(var arg) =>
+ TreeAnalyzer.TryGetPolyLinear(arg, x, out var a, out _) ?
+ MathS.Ln(MathS.Sin(arg)) / a :
+ null,
+
+ Entity.Logf(var @base, var arg) =>
+ !@base.ContainsNode(x) && TreeAnalyzer.TryGetPolyLinear(arg, x, out var a, out var b) ?
+ ((b / a + x) * MathS.Ln(arg) - x) / MathS.Ln(@base) :
+ null,
+
+ Entity.Powf(var @base, var power) =>
+ !@base.ContainsNode(x) && TreeAnalyzer.TryGetPolyLinear(power, x, out var a, out _) ?
+ MathS.Pow(@base, power) / (a * MathS.Ln(@base)) :
+ null,
+
+ _ => null
+ };
+ }
+}
diff --git a/AngouriMath/Functions/Continuous/Algebra/Integration/Integration.cs b/AngouriMath/Functions/Continuous/Algebra/Integration/Integration.cs
new file mode 100644
index 000000000..41d1d87f2
--- /dev/null
+++ b/AngouriMath/Functions/Continuous/Algebra/Integration/Integration.cs
@@ -0,0 +1,29 @@
+namespace AngouriMath.Functions.Algebra
+{
+ public static partial class Integration
+ {
+ public static Entity ComputeIndefiniteIntegral(Entity expr, Entity.Variable x)
+ {
+ if (!expr.ContainsNode(x)) return expr * x; // base case, handle here
+
+ Entity? answer = null;
+
+ answer = IntegralPatterns.TryStandardIntegrals(expr, x);
+ if (answer is { }) return answer;
+
+ answer = IndefiniteIntegralSolver.SolveBySplittingSum(expr, x);
+ if (answer is { }) return answer;
+
+ answer = IndefiniteIntegralSolver.SolveAsPolynomialTerm(expr, x);
+ if (answer is { }) return answer;
+
+ answer = IndefiniteIntegralSolver.SolveIntegratingByParts(expr, x);
+ if (answer is { }) return answer;
+
+ answer = IndefiniteIntegralSolver.SolveLogarithmic(expr, x);
+ if (answer is { }) return answer;
+
+ return new Entity.Integralf(expr, x, 1); // return as integral if nothing can be done with expression
+ }
+ }
+}
diff --git a/AngouriMath/Functions/Continuous/Solvers/EquationSolver/AnalyticalEquationSolver.cs b/AngouriMath/Functions/Continuous/Solvers/EquationSolver/AnalyticalEquationSolver.cs
index 3ab4d6d68..064a7497c 100644
--- a/AngouriMath/Functions/Continuous/Solvers/EquationSolver/AnalyticalEquationSolver.cs
+++ b/AngouriMath/Functions/Continuous/Solvers/EquationSolver/AnalyticalEquationSolver.cs
@@ -147,12 +147,22 @@ static Entity TryDowncast(Entity equation, Variable x, Entity root)
// // //
}
- // if no replacement worked, try trigonometry solver
+ // if no replacement worked, try trigonometric solver
if (TrigonometricSolver.TrySolveLinear(expr, x, out var trig) && trig is FiniteSet elsTrig)
return (Set)elsTrig.Select(ent => TryDowncast(expr, x, ent)).ToSet().InnerSimplified;
// // //
- // if no trigonometric rules helped, common denominator might help
+ // if no trigonometric rules helped, try exponential-multiplicative solver
+ if (ExponentialSolver.SolveMultiplicative(expr, x) is { } expMul && expMul is FiniteSet elsExpMul)
+ return (Set)elsExpMul.Select(ent => TryDowncast(expr, x, ent)).ToSet().InnerSimplified;
+ // // //
+
+ // if no exponential-multiplicative rules helped, try exponential-linear solver
+ if (ExponentialSolver.SolveLinear(expr, x) is { } expLin && expLin is FiniteSet elsExpLin)
+ return (Set)elsExpLin.Select(ent => TryDowncast(expr, x, ent)).ToSet().InnerSimplified;
+ // // //
+
+ // if no exponential-linear rules helped, common denominator might help
if (CommonDenominatorSolver.TrySolveGCD(expr, x, out var commonDenom) && commonDenom is FiniteSet elsCd)
return (Set)elsCd.Select(ent => TryDowncast(expr, x, ent)).ToSet().InnerSimplified;
// // //
diff --git a/AngouriMath/Functions/Continuous/Solvers/EquationSolver/ExponentialSolver.cs b/AngouriMath/Functions/Continuous/Solvers/EquationSolver/ExponentialSolver.cs
new file mode 100644
index 000000000..0de23bb7a
--- /dev/null
+++ b/AngouriMath/Functions/Continuous/Solvers/EquationSolver/ExponentialSolver.cs
@@ -0,0 +1,92 @@
+using AngouriMath.Extensions;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Xml;
+using static AngouriMath.Entity.Set;
+
+namespace AngouriMath.Functions.Algebra.AnalyticalSolving
+{
+ class ExponentialSolver
+ {
+ internal static Entity.Set? SolveLinear(Entity expr, Entity.Variable x)
+ {
+ var replacement = Entity.Variable.CreateTemp(expr.Vars);
+
+ Func preparator = e => e switch
+ {
+ Entity.Powf(var @base, var arg) when
+ TreeAnalyzer.TryGetPolyLinear(arg, x, out var a, out var b) =>
+ MathS.Pow(MathS.e, b) * MathS.Pow(MathS.Pow(MathS.e, x), MathS.Ln(@base) * a),
+
+ _ => e,
+ };
+
+ Func replacer = e => e switch
+ {
+ Entity.Powf(var @base, var arg)
+ when @base == MathS.e && arg == x
+ => replacement,
+
+ _ => e,
+ };
+
+ expr = expr.Replace(preparator);
+ expr = expr.Replace(replacer);
+
+ if (expr.ContainsNode(x)) return null; // cannot be solved, not a pure exponential
+
+ expr = expr.InnerSimplify();
+ if (AnalyticalEquationSolver.Solve(expr, replacement) is FiniteSet els && els.Any())
+ return (Entity.Set)els.Select(sol => MathS.Pow(MathS.e, x).Invert(sol, x).ToSet()).Unite().InnerSimplified;
+ else
+ return null;
+ }
+
+ internal static Entity.Set? SolveMultiplicative(Entity expr, Entity.Variable x)
+ {
+ Entity? substitution = null;
+ Entity ApplyPowerTransform(Entity @base, Entity arg)
+ {
+ var mults = Entity.Mulf.LinearChildren(arg);
+ if (mults.Count() == 0) return MathS.Pow(@base, arg);
+
+ Entity innerPower = 1;
+ Entity outerPower = 1;
+ foreach(var mult in mults)
+ {
+ if (mult.InnerSimplify() is Entity.Number)
+ outerPower = outerPower * mult;
+ else
+ innerPower = innerPower * mult;
+ }
+ substitution = MathS.Pow(@base, innerPower);
+ return MathS.Pow(substitution, outerPower.InnerSimplified);
+ }
+
+ Func powerTransform = e => e switch
+ {
+ Entity.Powf(var @base, var arg)
+ when @base == x && !arg.ContainsNode(x) =>
+ ApplyPowerTransform(@base, arg),
+
+ _ => e,
+ };
+
+ expr = expr.Replace(powerTransform);
+ if (substitution is null) return null;
+
+ var replacement = Entity.Variable.CreateTemp(expr.Vars);
+ expr = expr.Substitute(substitution, replacement);
+
+ if (expr.ContainsNode(x)) return null; // cannot be solved, not a multiplicative exponenial equation
+
+ expr = expr.InnerSimplify();
+ if (AnalyticalEquationSolver.Solve(expr, replacement) is FiniteSet els && els.Any())
+ return (Entity.Set)els.Select(sol => substitution.Invert(sol, x).ToSet()).Unite().InnerSimplified;
+ else
+ return null;
+ }
+ }
+}
diff --git a/AngouriMath/Functions/Continuous/Solvers/InequalitySolver/AnalyticalInequalitySolver.cs b/AngouriMath/Functions/Continuous/Solvers/InequalitySolver/AnalyticalInequalitySolver.cs
index 601ef9d2c..f5aee2412 100644
--- a/AngouriMath/Functions/Continuous/Solvers/InequalitySolver/AnalyticalInequalitySolver.cs
+++ b/AngouriMath/Functions/Continuous/Solvers/InequalitySolver/AnalyticalInequalitySolver.cs
@@ -20,10 +20,7 @@ internal static class AnalyticalInequalitySolver
///
internal static Set Solve(Entity expr, Variable x)
{
- if (expr is Minusf(var v, var c) && v == x && !c.ContainsNode(x))
- return new Interval(c, false, Number.Real.PositiveInfinity, false);
- if (expr is Minusf(var c1, var v1) && v1 == x && !c1.ContainsNode(x))
- return new Interval(Number.Real.NegativeInfinity, false, c1, false);
+ // TODO: poly parser
throw FutureReleaseException.Raised("Inequalities are not implemented yet", "1.2.1");
}
}
diff --git a/AngouriMath/Functions/Evaluation/Evaluation.Continuous.Classes.cs b/AngouriMath/Functions/Evaluation/Evaluation.Continuous.Classes.cs
index 076571f62..629cb6823 100644
--- a/AngouriMath/Functions/Evaluation/Evaluation.Continuous.Classes.cs
+++ b/AngouriMath/Functions/Evaluation/Evaluation.Continuous.Classes.cs
@@ -13,6 +13,7 @@
using AngouriMath.Core;
using AngouriMath.Core.Exceptions;
using static AngouriMath.Entity.Set;
+using AngouriMath.Functions.Algebra;
namespace AngouriMath
{
@@ -373,21 +374,27 @@ Var.InnerSimplified is Variable var
}
public partial record Integralf
{
+ private Entity SequentialIntegrating(Entity expr, Variable var, int iterations)
+ {
+ if (iterations < 0)
+ return this;
+ var changed = expr;
+ for (int i = 0; i < iterations; i++)
+ changed = Integration.ComputeIndefiniteIntegral(changed.InnerSimplified, var);
+ return changed;
+ }
+
protected override Entity InnerEval() => (Expression.Evaled, Var.Evaled, Iterations) switch
{
(Tensor expr, var var, var iters) => expr.Elementwise(n => new Integralf(n, var, iters).Evaled),
(var expr, _, 0) => expr,
// TODO: consider Derivative for negative cases
- (var expr, Variable var, var asInt) =>
- throw FutureReleaseException.Raised("Integration is not implemented yet", "1.2.3"),
+ (var expr, Variable var, int asInt) => SequentialIntegrating(expr, var, asInt),
+
_ => this
};
internal override Entity InnerSimplify() =>
- Var.InnerSimplified is Variable
- ? Iterations == 0
- ? Expression.InnerSimplified
- : throw FutureReleaseException.Raised("Integration is not implemented yet", "1.2.3")
- : this;
+ Var.InnerSimplified is Variable var ? SequentialIntegrating(Expression.InnerSimplified, var, Iterations) : this;
}
public partial record Limitf
{
diff --git a/AngouriMath/Functions/Evaluation/Patterns.cs b/AngouriMath/Functions/Evaluation/Patterns.cs
index 438b612e5..01cd1a02b 100644
--- a/AngouriMath/Functions/Evaluation/Patterns.cs
+++ b/AngouriMath/Functions/Evaluation/Patterns.cs
@@ -177,12 +177,12 @@ Entity FactorizeFactorialMultiplications(Entity x, Entity x2, Number factConst,
internal static Func TrigonometricToExponentialRules(Variable from, Variable to) => tree =>
{
// sin(ax + b) = (t^a * e^(i*b) - t^(-a) * e^(-i*b)) / (2i)
- Entity SinResult(Variable x, Number a, Entity b) =>
+ Entity SinResult(Variable x, Entity a, Entity b) =>
x == from
? MathS.Pow(to, a) * (MathS.Pow(MathS.e, b * MathS.i) / (2 * MathS.i)) - MathS.Pow(to, -a) * MathS.Pow(MathS.e, -b * MathS.i) / (2 * MathS.i)
: tree;
// cos(ax + b) = (t^a * e^(i*b) + t^(-a) * e^(-i*b)) / 2
- Entity CosResult(Variable x, Number a, Entity b) =>
+ Entity CosResult(Variable x, Entity a, Entity b) =>
x == from
? MathS.Pow(to, a) * (MathS.Pow(MathS.e, b * MathS.i) / 2) + MathS.Pow(to, -a) * MathS.Pow(MathS.e, -b * MathS.i) / 2
: tree;
@@ -191,36 +191,12 @@ Entity CosResult(Variable x, Number a, Entity b) =>
// e.g. tan(ax + b) = -i + (2i)/(1 + e^(2i*b) t^(2a))
return tree switch
{
- Sinf(Variable x) => SinResult(x, 1, 0),
- Sinf(Mulf(Variable x, Number a)) => SinResult(x, a, 0),
- Sinf(Mulf(Number a, Variable x)) => SinResult(x, a, 0),
- Sinf(Sumf(Variable x, var b)) => SinResult(x, 1, b),
- Sinf(Sumf(var b, Variable x)) => SinResult(x, 1, b),
- Sinf(Sumf(Mulf(Variable x, Number a), var b)) => SinResult(x, a, b),
- Sinf(Sumf(Mulf(Number a, Variable x), var b)) => SinResult(x, a, b),
- Sinf(Sumf(var b, Mulf(Variable x, Number a))) => SinResult(x, a, b),
- Sinf(Sumf(var b, Mulf(Number a, Variable x))) => SinResult(x, a, b),
- Sinf(Minusf(Variable x, var b)) => SinResult(x, 1, -b),
- Sinf(Minusf(var b, Variable x)) => SinResult(x, -1, b),
- Sinf(Minusf(Mulf(Variable x, Number a), var b)) => SinResult(x, a, -b),
- Sinf(Minusf(Mulf(Number a, Variable x), var b)) => SinResult(x, a, -b),
- Sinf(Minusf(var b, Mulf(Variable x, Number a))) => SinResult(x, -a, b),
- Sinf(Minusf(var b, Mulf(Number a, Variable x))) => SinResult(x, -a, b),
- Cosf(Variable x) => CosResult(x, 1, 0),
- Cosf(Mulf(Variable x, Number a)) => CosResult(x, a, 0),
- Cosf(Mulf(Number a, Variable x)) => CosResult(x, a, 0),
- Cosf(Sumf(Variable x, var b)) => CosResult(x, 1, b),
- Cosf(Sumf(var b, Variable x)) => CosResult(x, 1, b),
- Cosf(Sumf(Mulf(Variable x, Number a), var b)) => CosResult(x, a, b),
- Cosf(Sumf(Mulf(Number a, Variable x), var b)) => CosResult(x, a, b),
- Cosf(Sumf(var b, Mulf(Variable x, Number a))) => CosResult(x, a, b),
- Cosf(Sumf(var b, Mulf(Number a, Variable x))) => CosResult(x, a, b),
- Cosf(Minusf(Variable x, var b)) => CosResult(x, 1, -b),
- Cosf(Minusf(var b, Variable x)) => CosResult(x, -1, b),
- Cosf(Minusf(Mulf(Variable x, Number a), var b)) => CosResult(x, a, -b),
- Cosf(Minusf(Mulf(Number a, Variable x), var b)) => CosResult(x, a, -b),
- Cosf(Minusf(var b, Mulf(Variable x, Number a))) => CosResult(x, -a, b),
- Cosf(Minusf(var b, Mulf(Number a, Variable x))) => CosResult(x, -a, b),
+ Sinf(var arg) => TreeAnalyzer.TryGetPolyLinear(arg, from, out var a, out var b) ?
+ SinResult(from, a.InnerSimplify(), b.InnerSimplify()) : tree,
+
+ Cosf(var arg) => TreeAnalyzer.TryGetPolyLinear(arg, from, out var a, out var b) ?
+ CosResult(from, a.InnerSimplify(), b.InnerSimplify()) : tree,
+
_ => tree
};
};
@@ -577,19 +553,15 @@ private static bool IsZero(Entity entity)
// Suggestions to refactor this?
private static bool OppositeSigns(ComparisonSign left, ComparisonSign right)
- {
- if (left is Lessf)
- return right is Greaterf or GreaterOrEqualf or Equalsf;
- if (left is LessOrEqualf)
- return right is Greaterf;
- if (left is Greaterf)
- return right is Lessf or LessOrEqualf or Equalsf;
- if (left is GreaterOrEqualf)
- return right is Lessf;
- if (left is Equalsf)
- return right is Lessf or Greaterf;
- return false;
- }
+ => left switch
+ {
+ Lessf => right is Greaterf or GreaterOrEqualf or Equalsf,
+ LessOrEqualf => right is Greaterf,
+ Greaterf => right is Lessf or LessOrEqualf or Equalsf,
+ GreaterOrEqualf => right is Lessf,
+ Equalsf => right is Lessf or Greaterf,
+ _ => false
+ };
internal static Entity InequalityEqualityRules(Entity x) => x switch
{
diff --git a/AngouriMath/Functions/Evaluation/TreeAnalyzer/LongDivision.cs b/AngouriMath/Functions/Evaluation/TreeAnalyzer/LongDivision.cs
index 068716321..9a173dbaf 100644
--- a/AngouriMath/Functions/Evaluation/TreeAnalyzer/LongDivision.cs
+++ b/AngouriMath/Functions/Evaluation/TreeAnalyzer/LongDivision.cs
@@ -17,11 +17,11 @@ namespace AngouriMath.Functions
using static Entity.Number;
internal static partial class TreeAnalyzer
{
- class PolynomialInformation
+ internal sealed class PolynomialInformation
{
- readonly Dictionary> monoInfo = new();
- readonly Dictionary replacements = new();
- readonly Dictionary revertReplacements = new();
+ private readonly Dictionary> monoInfo = new();
+ private readonly Dictionary replacements = new();
+ private readonly Dictionary revertReplacements = new();
public IReadOnlyDictionary> MonoInfo => monoInfo;
public IReadOnlyDictionary Replacements => replacements;
public IReadOnlyDictionary RevertReplacements => revertReplacements;
@@ -36,32 +36,34 @@ public void AddMonoInfo(Variable variable, Dictionary? powers)
if (powers is { }) monoInfo.Add(variable, powers);
}
}
+
+ internal static PolynomialInformation GatherAllPossiblePolynomials(Entity expr, bool replaceVars)
+ {
+ // Init
+ var res = new PolynomialInformation();
+
+ if (replaceVars)
+ {
+ // Replace all variables we can
+ foreach (var varMentioned in expr.Vars)
+ res.AddReplacement(expr.Vars, GetMinimumSubtree(expr, varMentioned));
+ expr = expr.Substitute(res.Replacements);
+ }
+
+ // Gather info about each var as if this var was the only argument of the polynomial P(x)
+ var children = Sumf.LinearChildren(expr);
+ foreach (var varMentioned in expr.Vars)
+ res.AddMonoInfo(varMentioned, Algebra.AnalyticalSolving.PolynomialSolver.GatherMonomialInformation
+ (children, varMentioned));
+ return res;
+ }
+
///
/// Divides one polynomial over another one:
///
///
internal static (Entity Divided, Entity Remainder)? PolynomialLongDivision(Entity p, Entity q)
{
- static PolynomialInformation GatherAllPossiblePolynomials(Entity expr, bool replaceVars)
- {
- // Init
- var res = new PolynomialInformation();
-
- if (replaceVars)
- {
- // Replace all variables we can
- foreach (var varMentioned in expr.Vars)
- res.AddReplacement(expr.Vars, GetMinimumSubtree(expr, varMentioned));
- expr = expr.Substitute(res.Replacements);
- }
-
- // Gather info about each var as if this var was the only argument of the polynomial P(x)
- var children = Sumf.LinearChildren(expr);
- foreach (var varMentioned in expr.Vars)
- res.AddMonoInfo(varMentioned, Algebra.AnalyticalSolving.PolynomialSolver.GatherMonomialInformation
- (children, varMentioned));
- return res;
- }
if (!p.Vars.Any() || !q.Vars.Any())
return null; // There are no variables to find polynomial as
diff --git a/AngouriMath/Functions/Evaluation/TreeAnalyzer/PolyParser.cs b/AngouriMath/Functions/Evaluation/TreeAnalyzer/PolyParser.cs
new file mode 100644
index 000000000..367e41523
--- /dev/null
+++ b/AngouriMath/Functions/Evaluation/TreeAnalyzer/PolyParser.cs
@@ -0,0 +1,97 @@
+
+/* Copyright (c) 2019-2020 Angourisoft
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software
+ * is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using AngouriMath.Core;
+using AngouriMath.Core.Exceptions;
+using PeterO.Numbers;
+
+namespace AngouriMath.Functions
+{
+ using static Entity;
+ using static Entity.Number;
+ internal static partial class TreeAnalyzer
+ {
+ ///
+ /// Finds all monomials with respect to the power of
+ ///
+ internal static bool TryGetPolynomial(Entity expr, Variable variable,
+ [NotNullWhen(true)] out Dictionary? dst)
+ {
+ var children = Sumf.LinearChildren(expr.Expand());
+ dst = Algebra.AnalyticalSolving.PolynomialSolver.GatherMonomialInformation
+ (children, variable);
+ return dst is not null;
+ }
+
+ /// a x + b
+ internal static bool TryGetPolyLinear(Entity expr, Variable variable,
+ [NotNullWhen(true)] out Entity? a,
+ [NotNullWhen(true)] out Entity? b)
+ {
+ a = b = null;
+ if (TryGetPolynomial(expr, variable, out var monoInfo)
+ && AssertIsValidNormalPoly(monoInfo, 1, out var res))
+ {
+ a = res[1];
+ b = res[0];
+ return true;
+ }
+ return false;
+ }
+
+ /// a x ^ 2 + b x + c
+ internal static bool TryGetPolyQuadratic(Entity expr, Variable variable,
+ [NotNullWhen(true)] out Entity? a,
+ [NotNullWhen(true)] out Entity? b,
+ [NotNullWhen(true)] out Entity? c)
+ {
+ a = b = c = null;
+ if (TryGetPolynomial(expr, variable, out var monoInfo)
+ && AssertIsValidNormalPoly(monoInfo, 2, out var res))
+ {
+ a = res[2];
+ b = res[1];
+ c = res[0];
+ return true;
+ }
+ return false;
+ }
+
+ private static bool AssertIsValidNormalPoly(Dictionary polyInfo,
+ int maxPower, out Entity[] monoByPower)
+ {
+ monoByPower = new Entity[maxPower + 1];
+ foreach (var pair in polyInfo)
+ {
+ if (!pair.Key.CanFitInInt32())
+ return false;
+ var asInt = pair.Key.ToInt32Unchecked();
+ if (asInt < 0)
+ return false;
+ if (asInt > maxPower)
+ return false;
+ monoByPower[asInt] = pair.Value;
+ }
+ for (int i = 0; i < monoByPower.Length; i++)
+ if (monoByPower[i] is null)
+ monoByPower[i] = 0;
+ return true;
+ }
+ }
+}
diff --git a/Samples/Samples/Program.cs b/Samples/Samples/Program.cs
index bd7ba1c00..9663e489f 100644
--- a/Samples/Samples/Program.cs
+++ b/Samples/Samples/Program.cs
@@ -1,5 +1,11 @@
-using AngouriMath.Extensions;
+using System;
+using AngouriMath;
+using static AngouriMath.Entity;
+using static AngouriMath.Entity.Boolean;
using static AngouriMath.MathS;
+using AngouriMath.Extensions;
using static System.Console;
-WriteLine("a = b = c = d = e".ToEntity());
\ No newline at end of file
+WriteLine("a = b = c = d = e".ToEntity());
+Console.WriteLine("4^x + 2^x - (2^x)^3 - a".SolveEquation("x"));
+
diff --git a/Tests/UnitTests/Core/PolyParser.cs b/Tests/UnitTests/Core/PolyParser.cs
new file mode 100644
index 000000000..d57e052fd
--- /dev/null
+++ b/Tests/UnitTests/Core/PolyParser.cs
@@ -0,0 +1,97 @@
+using AngouriMath;
+using static AngouriMath.Entity.Number;
+using Xunit;
+using AngouriMath.Functions;
+
+namespace UnitTests.Core
+{
+ public class PolyParser
+ {
+ // Workaround to avoid ToString which results in SO in RC1
+ private void AssertEntityEqual(Entity a, Entity b)
+ {
+ b = b.InnerSimplified;
+ if (a != b)
+ Assert.False(true, $"{a.Stringize()} is not {b.Stringize()}");
+ }
+
+ private void Fail()
+ {
+ Assert.True(false, "Not parsed for some reason");
+ }
+
+ [Fact]
+ public void TestLinear1()
+ {
+ if (MathS.Utils.TryGetPolyLinear("a x + b", "x", out var a, out var b))
+ {
+ AssertEntityEqual("a", a);
+ AssertEntityEqual("b", b);
+ }
+ else
+ Fail();
+ }
+
+ [Fact]
+ public void TestLinear2()
+ {
+ if (MathS.Utils.TryGetPolyLinear("a x - b", "x", out var a, out var b))
+ {
+ AssertEntityEqual("a", a);
+ AssertEntityEqual("-b", b);
+ }
+ else
+ Fail();
+ }
+
+ [Fact]
+ public void TestLinear3()
+ {
+ if (MathS.Utils.TryGetPolyLinear("a x - b + 3x + x", "x", out var a, out var b))
+ {
+ AssertEntityEqual("a + 3 + 1", a);
+ AssertEntityEqual("-b", b);
+ }
+ else
+ Fail();
+ }
+
+ [Fact]
+ public void TestLinear4()
+ {
+ Assert.False(MathS.Utils.TryGetPolyLinear("a x - b + 3x + x2", "x", out _, out _));
+ }
+
+ [Fact]
+ public void TestLinear5()
+ {
+ Assert.False(MathS.Utils.TryGetPolyLinear("a x - b + 3x + x^(-999)", "x", out _, out _));
+ }
+
+ [Fact]
+ public void TestQuadratic1()
+ {
+ if (MathS.Utils.TryGetPolyQuadratic("a x2 + b x + c", "x", out var a, out var b, out var c))
+ {
+ AssertEntityEqual("a", a);
+ AssertEntityEqual("b", b);
+ AssertEntityEqual("c", c);
+ }
+ else
+ Fail();
+ }
+
+ [Fact]
+ public void TestQuadratic2()
+ {
+ if (MathS.Utils.TryGetPolyQuadratic("a x2 + b x2 + c", "x", out var a, out var b, out var c))
+ {
+ AssertEntityEqual("a + b", a);
+ AssertEntityEqual("0", b);
+ AssertEntityEqual("c", c);
+ }
+ else
+ Fail();
+ }
+ }
+}