diff --git a/MyExpression.Console/Program.cs b/MyExpression.Console/Program.cs index cddaf61..dd93d7a 100644 --- a/MyExpression.Console/Program.cs +++ b/MyExpression.Console/Program.cs @@ -12,13 +12,16 @@ public class Program { private static void Main(string[] args) { - System.Console.Write("Equation: "); - var s = System.Console.ReadLine(); - var p = Polynomial.Parse(s); - var e = new PolynomialEquation(p, 1e-8); - e.Solve(); - //System.Console.WriteLine(String.Join(" ", e.AllRoots)); - System.Console.WriteLine(" Roots: " + String.Join(" ", e.Roots)); + //System.Console.Write("Equation: "); + //var s = System.Console.ReadLine(); + //System.Console.Write(" Epsilon: "); + //var eps = System.Console.ReadLine(); + //var p = Polynomial.Parse(s); + //var e = new PolynomialEquation(p, Double.Parse(eps)); + //e.Solve(); + ////System.Console.WriteLine(String.Join(" ", e.AllRoots)); + //System.Console.WriteLine(" Roots: " + String.Join(" ", e.Roots)); + //System.Console.ReadKey(); //var s = System.Console.ReadLine(); //var evaluator = new CodeDomEval(s); @@ -48,6 +51,12 @@ private static void Main(string[] args) //e.Solve(); ////System.Console.WriteLine(String.Join(" ", e.AllRoots)); //System.Console.WriteLine(" Roots: " + String.Join(" ", e.Roots)); + + //var f = new CodeDomEval("pow(x, 3) - 2 * pow(x, 2) - x + 2"); + //var f = new CodeDomEval("sin(x)"); + var e = new FunctionEquation(Math.Sin, new Interval(-5, 5), eps: 1e-15); + e.Solve(); + var r = e.AllRoots; } } } diff --git a/MyExpression.Core.Tests/CodeDomEvalTests.cs b/MyExpression.Core.Tests/CodeDomEvalTests.cs index 9ff8087..b51c64f 100644 --- a/MyExpression.Core.Tests/CodeDomEvalTests.cs +++ b/MyExpression.Core.Tests/CodeDomEvalTests.cs @@ -1,10 +1,13 @@ -using NUnit.Framework; +// Copyright (c) 2018 Vladislav Prekel + using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using NUnit.Framework; + namespace MyExpression.Core.Tests { [TestFixture] @@ -13,15 +16,12 @@ public class CodeDomEvalTests [Test] public void RandomSinCos() { - Func evalsin = new CodeDomEval("Math.Sin(x)").Calculate; - double sin(double x) => Math.Sin(x); - Func evalcos = new CodeDomEval("Math.Cos(x)").Calculate; + Func evalcos = new CodeDomEval("cos(x)").Calculate; double cos(double x) => Math.Cos(x); var r = new MyRandom(); - for (var i = 0; i < 100; i++) + for (var i = 0; i < 10; i++) { var x = r.Next(100) * r.NextDouble(); - Assert.AreEqual(sin(x), evalsin(x)); Assert.AreEqual(cos(x), evalcos(x)); } } @@ -29,14 +29,26 @@ public void RandomSinCos() [Test] public void Random() { - Func f1 = new CodeDomEval("Math.Sin(x)*1/x*4383+2143/1414+141-1.2*23*x*Math.Abs(x*Math.Sin(x))").Calculate; + Func f1 = new CodeDomEval("sin(x)*1/x*4383+2143/1414+141-1.2*23*x*Math.Abs(x*Math.Sin(x))").Calculate; double f(double x) => Math.Sin(x) * 1 / x * 4383 + 2143 / 1414 + 141 - 1.2 * 23 * x * Math.Abs(x * Math.Sin(x)); var r = new MyRandom(); - for (var i = 0; i < 1000; i++) + for (var i = 0; i < 100; i++) { var x = r.Next(100) * r.NextDouble(); Assert.AreEqual(f(x), f1(x), 1e-8); } } + + [Test] + public void ReadmeTest() + { + var s = "sin(x)"; + var c = new CodeDomEval(s); + Func f = c.Calculate; + Assert.AreEqual(0, f(0), 1e-7); + Assert.AreEqual(1, f(Math.PI / 2), 1e-7); + Assert.AreEqual(0, f(Math.PI), 1e-7); + Assert.AreEqual(Math.Sqrt(2) / 2, f(Math.PI / 4), 1e-7); + } } } diff --git a/MyExpression.Core.Tests/FunctionEquationTests.cs b/MyExpression.Core.Tests/FunctionEquationTests.cs new file mode 100644 index 0000000..eb372c2 --- /dev/null +++ b/MyExpression.Core.Tests/FunctionEquationTests.cs @@ -0,0 +1,23 @@ +// Copyright (c) 2018 Vladislav Prekel + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using NUnit.Framework; + +namespace MyExpression.Core.Tests +{ + [TestFixture] + public class FunctionEquationTests + { + [Test] + public void CustomTest() + { + var e1 = new FunctionEquation((x) => Math.Sin(x) * Math.Sin(x) * Math.Cos(x) / Math.Tan(x), new Interval(-5, 5), 1e-3, 1e-5); + e1.Solve(); + } + } +} diff --git a/MyExpression.Core.Tests/LinearEquationTests.cs b/MyExpression.Core.Tests/LinearEquationTests.cs index ac42911..c3b2bc4 100644 --- a/MyExpression.Core.Tests/LinearEquationTests.cs +++ b/MyExpression.Core.Tests/LinearEquationTests.cs @@ -43,5 +43,30 @@ public void ParseTest_Random() Assert.AreEqual(a, le.A); Assert.AreEqual(b, le.B); } + + [Test] + public void ToStringTest() + { + var l = new LinearEquation(1, 1); + Assert.AreEqual("x+1", l.ToString()); + l = new LinearEquation(0, 1); + Assert.AreEqual("1", l.ToString()); + l = new LinearEquation(1, 0); + Assert.AreEqual("x", l.ToString()); + l = new LinearEquation(-1, 0); + Assert.AreEqual("-x", l.ToString()); + l = new LinearEquation(-1, -1); + Assert.AreEqual("-x-1", l.ToString()); + l = new LinearEquation(2, 2); + Assert.AreEqual("2x+2", l.ToString()); + l = new LinearEquation(0, 2); + Assert.AreEqual("2", l.ToString()); + l = new LinearEquation(2, 0); + Assert.AreEqual("2x", l.ToString()); + l = new LinearEquation(-2, 0); + Assert.AreEqual("-2x", l.ToString()); + l = new LinearEquation(-2, -2); + Assert.AreEqual("-2x-2", l.ToString()); + } } } \ No newline at end of file diff --git a/MyExpression.Core.Tests/MyExpression.Core.Tests.csproj b/MyExpression.Core.Tests/MyExpression.Core.Tests.csproj index ed4200c..689c430 100644 --- a/MyExpression.Core.Tests/MyExpression.Core.Tests.csproj +++ b/MyExpression.Core.Tests/MyExpression.Core.Tests.csproj @@ -48,6 +48,7 @@ + diff --git a/MyExpression.Core.Tests/PolynomialEquationTests.cs b/MyExpression.Core.Tests/PolynomialEquationTests.cs index aea6d44..faa0de0 100644 --- a/MyExpression.Core.Tests/PolynomialEquationTests.cs +++ b/MyExpression.Core.Tests/PolynomialEquationTests.cs @@ -53,7 +53,7 @@ public void Cubic_Vieta_Dgt0_Random() } var p = new Polynomial(a, b, c, d); - var pe = new PolynomialEquation(p, 1e-9); + var pe = new PolynomialEquation(p, 1e-11); pe.Solve(); Assert.AreEqual(3, pe.AllRoots.Count); @@ -95,7 +95,7 @@ public void Cubic_Manual() for (var i = 0; i < e.Count; i++) { - var pe = new PolynomialEquation(new Polynomial(e[i]), 1e-9); + var pe = new PolynomialEquation(new Polynomial(e[i]), 1e-10); pe.Solve(); for (var j = 0; j < pe.Roots.Count; j++) { @@ -125,7 +125,7 @@ public void Cubic_Int_Deq0_Random() new Monomial(c, 1), new Monomial(d, 0), }; - var pe = new PolynomialEquation(p, 1e-10); + var pe = new PolynomialEquation(p, 1e-11); pe.Solve(); Assert.AreEqual(3, pe.AllRoots.Count); @@ -158,7 +158,7 @@ public void Cubic_Int_Dlt0_Random() new Monomial(c, 1), new Monomial(d, 0), }; - var pe = new PolynomialEquation(p, 1e-9); + var pe = new PolynomialEquation(p, 1e-10); pe.Solve(); Assert.AreEqual(1, pe.AllRoots.Count); @@ -188,7 +188,7 @@ public void Cubic_Vieta_Dlt0_Random() new Monomial(c, 1), new Monomial(d, 0), }; - var pe = new PolynomialEquation(p, 1e-8); + var pe = new PolynomialEquation(p, 1e-9); pe.Solve(); Assert.AreEqual(1, pe.AllRoots.Count); @@ -197,40 +197,6 @@ public void Cubic_Vieta_Dlt0_Random() Assert.AreEqual(0, p.Calculate(pe.Roots[0]), 1e-6); } - //[Test] - //public void Cubic_Deq0_Random() - //{ - // var r = new MyRandom(); - // double a, b, c, d; - // while (true) - // { - // //a = 2 * r.NextDouble() * r.NextSign(); - // a = 5 * r.NextDouble() * r.NextSign(); - // b = 5 * r.NextDouble() * r.NextSign(); - // c = 5 * r.NextDouble() * r.NextSign(); - // d = 5 * r.NextDouble() * r.NextSign(); - // if (Math.Abs(CubicDiscriminant(a, b, c, d)) < 1e-7 && Math.Abs(a) >= 1e-2) break; - // } - - // var p = new Polynomial - // { - // new Monomial(a, 3), - // new Monomial(b, 2), - // new Monomial(c, 1), - // new Monomial(d, 0), - // }; - // var pe = new PolynomialEquation(p, 1e-9); - // pe.Solve(); - - // Assert.AreEqual(3, pe.AllRoots.Count); - // Assert.IsTrue(new Interval(1, 2).IsInInterval(pe.Roots.Count), pe.Roots.Count.ToString()); - - // foreach (var i in pe.AllRoots) - // { - // Assert.AreEqual(0, p.Calculate(i), 1e-5); - // } - //} - [Test] public void Cubic_Vieta_RootsToCoef_Random() { @@ -252,7 +218,7 @@ public void Cubic_Vieta_RootsToCoef_Random() c = CubicVietaC(a, x); d = CubicVietaD(a, x); Assert.Greater(CubicDiscriminant(a, b, c, d), 0); - pe = new PolynomialEquation(new Polynomial(a, b, c, d), 1e-7); + pe = new PolynomialEquation(new Polynomial(a, b, c, d), 1e-9); pe.Solve(); Assert.AreEqual(3, pe.AllRoots.Count); Assert.AreEqual(3, pe.Roots.Count); @@ -280,7 +246,7 @@ public void Cubic_Vieta_RootsToCoef_Random() c = CubicVietaC(a, z); d = CubicVietaD(a, z); Assert.AreEqual(0, CubicDiscriminant(a, b, c, d)); - pe = new PolynomialEquation(new Polynomial(a, b, c, d), 1e-6); + pe = new PolynomialEquation(new Polynomial(a, b, c, d), 1e-8); pe.Solve(); Assert.AreEqual(3, pe.AllRoots.Count); Assert.IsTrue(new Interval(1, 2).IsInInterval(pe.Roots.Count), pe.Roots.Count.ToString()); @@ -289,5 +255,21 @@ public void Cubic_Vieta_RootsToCoef_Random() Assert.AreEqual(z[2], pe.AllRoots[2], 1e-5); } } + + [Test] + public void ReadmeTest() + { + var s = "x^3-2x^2-x+2"; + var eps = 1e-8; + var p = Polynomial.Parse(s); + var e = new PolynomialEquation(p, eps); + e.Solve(); + Assert.AreEqual(-1, e.Roots[0], 1e-7); + Assert.AreEqual(0, p.Calculate(e.Roots[0]), 1e-7); + Assert.AreEqual(1, e.Roots[1], 1e-7); + Assert.AreEqual(0, p.Calculate(e.Roots[1]), 1e-7); + Assert.AreEqual(2, e.Roots[2], 1e-7); + Assert.AreEqual(0, p.Calculate(e.Roots[2]), 1e-7); + } } } diff --git a/MyExpression.Core/CodeDomEval.cs b/MyExpression.Core/CodeDomEval.cs index a66aafe..8760b69 100644 --- a/MyExpression.Core/CodeDomEval.cs +++ b/MyExpression.Core/CodeDomEval.cs @@ -35,6 +35,41 @@ public double Evaluate(double x) } } }"; + private string ReformExpression(string e) + { + var sb = new StringBuilder(e); + sb.Replace(" ", ""); + sb.Replace("abs", "Math.Abs"); + sb.Replace("acos", "Math.Acos"); + sb.Replace("asin", "Math.Asin"); + sb.Replace("atan", "Math.Atan"); + sb.Replace("atan2", "Math.Atan2"); + sb.Replace("bigmul", "Math.BigMul"); + sb.Replace("ceiling", "Math.Ceiling"); + sb.Replace("cos", "Math.Cos"); + sb.Replace("cosh", "Math.Cosh"); + sb.Replace("divrem", "Math.DivRem"); + sb.Replace("e", "Math.E"); + sb.Replace("exp", "Math.Exp"); + sb.Replace("floor", "Math.Floor"); + sb.Replace("ieeeremainder", "Math.IEEERemainder"); + sb.Replace("log", "Math.Log"); + sb.Replace("log10", "Math.Log10"); + sb.Replace("max", "Math.Max"); + sb.Replace("min", "Math.Min"); + sb.Replace("pi", "Math.PI"); + sb.Replace("pow", "Math.Pow"); + sb.Replace("round", "Math.Round"); + sb.Replace("sign", "Math.Sign"); + sb.Replace("sin", "Math.Sin"); + sb.Replace("sinh", "Math.Sinh"); + sb.Replace("sqrt", "Math.Sqrt"); + sb.Replace("tan", "Math.Tan"); + sb.Replace("tanh", "Math.Tanh"); + sb.Replace("truncate", "Math.Truncate"); + return sb.ToString(); + } + /// /// Конструктор /// @@ -49,7 +84,8 @@ public CodeDomEval(string expression) // Компиляция сборки с вычисляющим классом var compilerParams = CreateCompilerParameters(); //var src = String.Format(SourceFormat, expression); - var src = SourceFormat.Replace("[||]", expression); + var rfex = ReformExpression(expression); + var src = SourceFormat.Replace("[||]", rfex); CompilerResults = provider.CompileAssemblyFromSource(compilerParams, src); if (CompilerResults.Errors.Count == 0) @@ -62,9 +98,9 @@ public CodeDomEval(string expression) // Сбор ошибок компиляции foreach (CompilerError error in CompilerResults.Errors) { - sb.Append(error.ErrorText + "\n"); + sb.Append(error + "\n"); } - throw new Exception("Ошибка сборки\n" + sb); + throw new ApplicationException("Ошибка сборки\n" + sb); } if (!IsSuccessfulBuild) throw new Exception("Ошибка сборки"); diff --git a/MyExpression.Core/FunctionEquation.cs b/MyExpression.Core/FunctionEquation.cs new file mode 100644 index 0000000..b1b2777 --- /dev/null +++ b/MyExpression.Core/FunctionEquation.cs @@ -0,0 +1,70 @@ +// Copyright (c) 2018 Vladislav Prekel + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MyExpression.Core +{ + public class FunctionEquation : IEquation + { + public Func Function { get; private set; } + + public Interval Interval { get; private set; } + public double Step { get; private set; } + public double Epsilon { get; private set; } + + public FunctionEquation(Func function, Interval interval, double step = 1e-3, double eps = 1e-8) + { + Function = function; + Interval = interval; + Step = step; + Epsilon = eps; + } + + public FunctionEquation(IFunctionX functionx, Interval interval, double step = 1e-3, double eps = 1e-8) + : this(functionx.Calculate, interval, step, eps) { } + + public IList AllRoots { get; private set; } + + public IList Roots => AllRoots; + + public void Solve() + { + AllRoots = new List(); + var x0 = Interval.Left; + var y0 = Function(x0); + var y = 0d; + var fl = false; + var x1 = Double.NaN; + for (var x = Interval.Left + Step; x <= Interval.Right; x += Step) + { + y = Function(x); + + if (!fl && Math.Abs(y) < Epsilon) + { + fl = true; + x1 = x; + } + if (fl && Math.Abs(y) >= Epsilon) + { + fl = false; + AllRoots.Add(x1); + } + + var p = y0 * y; + if (!Double.IsInfinity(p) && !Double.IsNaN(p) && p < 0) + { + var bs = new RecursiveBinarySearch(Function, new Interval(x0, x), Epsilon); + x1 = bs.Solve(); + if (!fl) AllRoots.Add(x1); + } + + y0 = y; + x0 = x; + } + } + } +} diff --git a/MyExpression.Core/IDerivativable.cs b/MyExpression.Core/IDerivativable.cs new file mode 100644 index 0000000..1d535d9 --- /dev/null +++ b/MyExpression.Core/IDerivativable.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MyExpression.Core +{ + public interface IDerivativable + { + IFunctionX Derivative { get; } + //double Calculate(double x); + } +} diff --git a/MyExpression.Core/Equation.cs b/MyExpression.Core/IEquation.cs similarity index 85% rename from MyExpression.Core/Equation.cs rename to MyExpression.Core/IEquation.cs index 1379f94..3b205d9 100644 --- a/MyExpression.Core/Equation.cs +++ b/MyExpression.Core/IEquation.cs @@ -8,9 +8,10 @@ namespace MyExpression.Core { - interface IEquation + public interface IEquation { IList AllRoots { get; } IList Roots { get; } + void Solve(); } } diff --git a/MyExpression.Core/LinearEquation.cs b/MyExpression.Core/LinearEquation.cs index faa6cd3..a4bae3a 100644 --- a/MyExpression.Core/LinearEquation.cs +++ b/MyExpression.Core/LinearEquation.cs @@ -49,7 +49,20 @@ public static LinearEquation Parse(string s) public override string ToString() { - return ToPolynomial() + " = 0"; + var k = A.ToString(); + var m = B.ToString(); + var x = "x"; + if (A == 0) return m; + if (A == 1) k = ""; + if (A == -1) k = "-"; + if (B > 0) m = "+" + m; + if (B == 0) m = ""; + return $"{k}{x}{m}"; + } + + public void Solve() + { + } } } diff --git a/MyExpression.Core/Monomial.cs b/MyExpression.Core/Monomial.cs index f71a242..c210770 100644 --- a/MyExpression.Core/Monomial.cs +++ b/MyExpression.Core/Monomial.cs @@ -7,7 +7,7 @@ namespace MyExpression.Core { - public class Monomial : IFunctionX, IComparable + public class Monomial : IFunctionX, IDerivativable, IComparable { public double Coefficient { get; set; } public double Degree { get; set; } @@ -77,7 +77,7 @@ public Monomial(double coef = 1, double degree = 1) Degree = degree; } - public Monomial Derivative + public IFunctionX Derivative { get { diff --git a/MyExpression.Core/MyExpression.Core.csproj b/MyExpression.Core/MyExpression.Core.csproj index ce40839..20d6c6c 100644 --- a/MyExpression.Core/MyExpression.Core.csproj +++ b/MyExpression.Core/MyExpression.Core.csproj @@ -42,11 +42,15 @@ - + + + + + @@ -57,6 +61,7 @@ + + + +