diff --git a/MathExpressionsNet.Tests/CodeDomTests.cs b/MathExpressionsNet.Tests/CodeDomTests.cs index 53d97c1..86c6fd8 100644 --- a/MathExpressionsNet.Tests/CodeDomTests.cs +++ b/MathExpressionsNet.Tests/CodeDomTests.cs @@ -13,10 +13,13 @@ public class CodeDomTests [Test] public void StringsToExpressionsTest() { + var f0 = CodeDom.ParseExpression>("2+5*4").Compile(); + Assert.That(f0(), Is.EqualTo(22)); + var f1 = ((Expression>)CodeDom.GetExpressionFrom("x=>0")).Compile(); VerifyFunctions(f1, functions.LeftBoundCond); - f1 = ((Expression>)CodeDom.GetExpressionFrom("t=>t+3*t*t")).Compile(); + f1 = CodeDom.ParseExpression>("t+3*t*t", "t").Compile(); VerifyFunctions(f1, functions.RightBoundCond); var f2 = ((Expression>)CodeDom.GetExpressionFrom("(x,t)=>x*x*t+3*x*t*t")).Compile(); @@ -27,9 +30,6 @@ public void StringsToExpressionsTest() var f3 = ((Expression>)CodeDom.GetExpressionFrom("(x,t,u)=>x*x*t+u*u")).Compile(); VerifyFunctions(f3, functions.K); - - f3 = ((Expression>)CodeDom.GetExpressionFrom("(x,t,u)=>x*x+6*x*t-2*u*Math.Pow(2*x*t+3*t*t,2)-2*t*(x*x*t+u*u)")).Compile(); - VerifyFunctions(f3, functions.g); } private void VerifyFunctions(Func generatedFunc, Func expectedFunc) @@ -53,6 +53,22 @@ private void VerifyFunctions(Func generatedFunc, Assert.That(generatedFunc(a, b, c), Is.EqualTo(expectedFunc(a, b, c))); } + [Test] + public void MakeNewFunction() + { + var exprU = CodeDom.ParseExpression>("x * x * t + 3 * x * t * t", "x", "t"); + var exprK = CodeDom.ParseExpression>("x * x * t + u * u", "x", "t", "u"); + string du_dt = exprU.Derive("t").Simplify().Body.ToString(); + string dK_du = exprK.Derive("u").Simplify().Body.ToString(); + var dudx = exprU.Derive("x").Simplify(); + string du_dx = dudx.Body.ToString(); + string d2u_dx2 = dudx.Derive("x").Simplify().Body.ToString(); + string g = $"{du_dt}-{dK_du}*Math.Pow({du_dx},2)-{d2u_dx2}*{exprK.Simplify().Body}".Replace(" ", ""); + Assert.That(g, Is.EqualTo("((x*x)+(6*(x*t)))-(2*u)*Math.Pow(((2*(x*t))+(3*(t*t))),2)-(2*t)*(((x*x)*t)+(u*u))")); + var gFunc = CodeDom.ParseExpression>(g, "x", "t", "u").Compile(); + VerifyFunctions(gFunc, functions.g); + } + [Test] public void ThrowingException() { diff --git a/MathExpressionsNet.Tests/Functions.cs b/MathExpressionsNet.Tests/Functions.cs index 00b5cc3..f03578e 100644 --- a/MathExpressionsNet.Tests/Functions.cs +++ b/MathExpressionsNet.Tests/Functions.cs @@ -5,14 +5,10 @@ namespace MathExpressionsNet.Tests public class Functions { public Func K => (x, t, u) => x * x * t + u * u; - public Func dK_du => (x, t, u) => 2 * u; public Func g => (x, t, u) => x * x + 6 * x * t - 2 * u * Math.Pow(2 * x * t + 3 * t * t, 2) - 2 * t * (x * x * t + u * u); public Func LeftBoundCond => t => 0; public Func RightBoundCond => t => t + 3 * t * t; - public Func u => (x, t) => x * x * t + 3 * x * t * t; public Func du_dx => (x, t) => 2 * x * t + 3 * t * t; - public Func d2u_dx2 => (x, t) => 2 * t; - public Func du_dt => (x, t) => x * x + 6 * x * t; } } diff --git a/MathExpressionsNet.Tests/Properties/AssemblyInfo.cs b/MathExpressionsNet.Tests/Properties/AssemblyInfo.cs index c51a7bf..a3cccb2 100644 --- a/MathExpressionsNet.Tests/Properties/AssemblyInfo.cs +++ b/MathExpressionsNet.Tests/Properties/AssemblyInfo.cs @@ -31,5 +31,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyVersion("1.0.1.0")] +[assembly: AssemblyFileVersion("1.0.1.0")] diff --git a/MathExpressionsNet/CodeDom.cs b/MathExpressionsNet/CodeDom.cs index c0eac45..b070430 100644 --- a/MathExpressionsNet/CodeDom.cs +++ b/MathExpressionsNet/CodeDom.cs @@ -11,9 +11,10 @@ namespace MathExpressionsNet { public class CodeDom { - const string TYPE_NAME = "TemporaryNamespace.Temporary"; - const string METHOD_NAME = "Get"; - const string CODE_HEADER = @" + private const string TypeName = "TemporaryNamespace.Temporary"; + private const string MethodName = "Get"; + + private const string CodeHeader = @" namespace TemporaryNamespace { using System; @@ -25,10 +26,12 @@ public Expression Get() { return New( "; - const string CODE_FOOTER = @" + + private const string CodeFooter = @" ); } + static Expression New(Expression> e) { return e; } static Expression New(Expression> e) { return e; } static Expression New(Expression> e) { return e; } static Expression New(Expression> e) { return e; } @@ -37,29 +40,28 @@ public Expression Get() } "; - static CompilerResults Compile(string source) + private static CompilerResults Compile(string source) { CodeDomProvider provider = new CSharpCodeProvider( new Dictionary { { "CompilerVersion", "v3.5" } }); - CompilerParameters cp = new CompilerParameters(); - cp.GenerateInMemory = true; + var cp = new CompilerParameters { GenerateInMemory = true }; cp.ReferencedAssemblies.Add("System.Core.dll"); CompilerResults cr = provider.CompileAssemblyFromSource( cp, - CODE_HEADER + source + CODE_FOOTER + CodeHeader + source + CodeFooter ); return cr; } - static Expression Execute(CompilerResults cr) + private static Expression Execute(CompilerResults cr) { Assembly asm = cr.CompiledAssembly; - Type myClass = asm.GetType(TYPE_NAME); + Type myClass = asm.GetType(TypeName); Object o = Activator.CreateInstance(myClass); - MethodInfo mi = myClass.GetMethod(METHOD_NAME); + MethodInfo mi = myClass.GetMethod(MethodName); return (Expression)mi.Invoke(o, null); } @@ -69,9 +71,9 @@ public static Expression GetExpressionFrom(string source) if (cr.Errors.HasErrors) { - StringBuilder sb = new StringBuilder(); + var sb = new StringBuilder(); sb.Append("Compilation failed: "); - Regex reg = new Regex(@":\serror\s(?.*)$"); + var reg = new Regex(@":\serror\s(?.*)$"); foreach (var error in cr.Errors) { @@ -87,6 +89,12 @@ public static Expression GetExpressionFrom(string source) return Execute(cr); } + + public static Expression ParseExpression(string func, params string[] variables) + { + var expr = (Expression)GetExpressionFrom($"({string.Join(",", variables)}) => {func}"); + return expr.Simplify(); + } } public class ExpressionCodeDomException : Exception diff --git a/MathExpressionsNet/DifferentialOperator.cs b/MathExpressionsNet/DifferentialOperator.cs index bee5fd7..fbb5524 100644 --- a/MathExpressionsNet/DifferentialOperator.cs +++ b/MathExpressionsNet/DifferentialOperator.cs @@ -5,7 +5,7 @@ namespace MathExpressionsNet { public class DifferentialOperator { - Expression characteristic; + private Expression characteristic; #region init @@ -16,7 +16,7 @@ public class DifferentialOperator /// parameter name public DifferentialOperator(string paramName) { - this.characteristic = Expression.Parameter(typeof(double), paramName); + characteristic = Expression.Parameter(typeof(double), paramName); } /// @@ -25,7 +25,7 @@ public DifferentialOperator(string paramName) /// Laplacian = (∂/∂x)^2 + (∂/∂y)^2 /// /// - DifferentialOperator(Expression characteristic) + private DifferentialOperator(Expression characteristic) { this.characteristic = characteristic; } @@ -55,6 +55,7 @@ public DifferentialOperator( : this((Expression)e) { } #endregion + #region apply operator /// @@ -65,7 +66,7 @@ public DifferentialOperator( /// derivative public Expression Apply(Expression e) { - return Apply(this.characteristic, e); + return Apply(characteristic, e); } /// @@ -128,6 +129,7 @@ static public Expression Apply(Expression characteristic, Expression e) } #endregion + #region operator /// diff --git a/MathExpressionsNet/ExpressionExtensions.cs b/MathExpressionsNet/ExpressionExtensions.cs index c84db21..b8e1517 100644 --- a/MathExpressionsNet/ExpressionExtensions.cs +++ b/MathExpressionsNet/ExpressionExtensions.cs @@ -14,30 +14,46 @@ public static class ExpressionExtensions /// express a term as style such like { constant, body }. /// 2 * x * 3 * y -> { 6, x * y }. /// - class Term + private class Term { public double Constant { get; set; } public Expression Body { get; set; } - public Term(double c) { this.Constant = c; this.Body = null; } - public Term(Expression b) { this.Constant = 1.0; this.Body = b; } - public Term(double c, Expression b) { this.Constant = c; this.Body = b; } + + public Term(double c) + { + Constant = c; + Body = null; + } + + public Term(Expression b) + { + Constant = 1.0; + Body = b; + } + + public Term(double c, Expression b) + { + Constant = c; + Body = b; + } public Expression ToExpression() { - if (this.Constant == 0) + if (Constant == 0) return Expression.Constant(0.0); - if (this.Body == null) - return Expression.Constant(this.Constant); - if (this.Constant == 1) - return this.Body; + if (Body == null) + return Expression.Constant(Constant); + if (Constant == 1) + return Body; return Expression.Multiply( - Expression.Constant(this.Constant), - this.Body); + Expression.Constant(Constant), + Body); } } #endregion + #region Derive /// @@ -118,6 +134,7 @@ private static Expression Derive(this Expression e, string paramName) Expression d = op.Derive(paramName); return OptimizedNegate(d); } + case ExpressionType.Add: { Expression dleft = @@ -261,16 +278,12 @@ private static Expression Derive(this MethodCallExpression me, string paramName) /// method name /// arguments of the method /// expression - static Expression MathCall(string methodName, - IEnumerable arguments) + private static Expression MathCall(string methodName, IEnumerable arguments) { - return Expression.Call(null, - typeof(System.Math).GetMethod(methodName), - arguments); + return Expression.Call(null, typeof(Math).GetMethod(methodName), arguments); } - static Expression MathCall(string methodName, - params Expression[] arguments) + private static Expression MathCall(string methodName, params Expression[] arguments) { return MathCall(methodName, arguments); } @@ -278,6 +291,7 @@ static Expression MathCall(string methodName, #endregion #endregion + #region optimized arithmetic /// @@ -289,8 +303,7 @@ internal static Expression OptimizedNegate(Expression e) { if (e.IsConstant()) { - return Expression.Constant( - -(double)((ConstantExpression)e).Value); + return Expression.Constant(-(double)((ConstantExpression)e).Value); } if (e.NodeType == ExpressionType.Negate) @@ -309,7 +322,7 @@ internal static Expression OptimizedNegate(Expression e) /// operand 1 /// operand 2 /// result - static Expression OptimizedAdd(Expression e1, Expression e2) + private static Expression OptimizedAdd(Expression e1, Expression e2) { // 0 + x -> x if (e1.IsConstant()) @@ -365,7 +378,7 @@ static Expression OptimizedAdd(Expression e1, Expression e2) /// operand 1 /// operand 2 /// result - static Expression OptimizedSub(Expression e1, Expression e2) + private static Expression OptimizedSub(Expression e1, Expression e2) { // 0 - x -> -x if (e1.IsConstant()) @@ -419,7 +432,7 @@ static Expression OptimizedSub(Expression e1, Expression e2) /// operand 1 /// operand 2 /// result - static Expression OptimizedMul(Expression e1, Expression e2) + private static Expression OptimizedMul(Expression e1, Expression e2) { Expression mul = Expression.Multiply(e1, e2); @@ -433,7 +446,7 @@ static Expression OptimizedMul(Expression e1, Expression e2) /// operand 1 /// operand 2 /// result - static Expression OptimizedDiv(Expression e1, Expression e2) + private static Expression OptimizedDiv(Expression e1, Expression e2) { Expression div = Expression.Divide(e1, e2); @@ -529,7 +542,7 @@ public static Expression Simplify(this Expression e) /// /// expression to be reduced /// reduced result - static Expression Simplify(this Expression e) + private static Expression Simplify(this Expression e) { switch (e.NodeType) { @@ -568,7 +581,7 @@ static Expression Simplify(this Expression e) /// /// terget /// result - static Expression Cancel(this Expression e) + private static Expression Cancel(this Expression e) { List terms = new List(); @@ -583,7 +596,7 @@ static Expression Cancel(this Expression e) /// expression to be deconstructed /// negate sign of e if minus == true /// list into which deconstructed terms are stored - static void DeconstructSum(Expression e, bool minus, List terms) + private static void DeconstructSum(Expression e, bool minus, List terms) { if (e.NodeType == ExpressionType.Negate) { @@ -625,7 +638,7 @@ static void DeconstructSum(Expression e, bool minus, List terms) /// /// list in which expressions are stored /// sum of terms - static Expression ConstructSum(List terms) + private static Expression ConstructSum(List terms) { Expression sum = Expression.Constant(0.0); foreach (var term in terms) @@ -643,7 +656,7 @@ static Expression ConstructSum(List terms) /// for example, 2 * x * 3 * x * 4 -> 24 * x * x. /// /// Expression to be optimized - static Term FoldConstants(Expression e) + private static Term FoldConstants(Expression e) { List n = new List(); List d = new List(); @@ -656,7 +669,7 @@ static Term FoldConstants(Expression e) /// /// terget /// result - static Expression Reduce(this Expression e) + private static Expression Reduce(this Expression e) { return FoldConstants(e).ToExpression(); } @@ -666,8 +679,8 @@ static Expression Reduce(this Expression e) /// x / a * y * z / b / c -> num = {x, y, z}, denom = {a, b, c}. /// /// expression to be deconstructed - /// list into which deconstructed expressions are stored - static void DeconstructProduct(Expression e, List num, List denom) + /// list into which deconstructed expressions are stored + private static void DeconstructProduct(Expression e, List num, List denom) { if (e.NodeType == ExpressionType.Multiply) { @@ -724,7 +737,7 @@ static void DeconstructProduct(Expression e, List num, List x * y * z. /// /// list in which expressions are stored - static Term ConstructProduct(IEnumerable list) + private static Term ConstructProduct(IEnumerable list) { double c = 1; Expression prod = null; @@ -748,7 +761,7 @@ static Term ConstructProduct(IEnumerable list) /// /// list in which expressions of numerator are stored /// list in which expressions of denominator are stored - static Term ConstructProduct(List num, List denom) + private static Term ConstructProduct(List num, List denom) { double c = 1; @@ -796,7 +809,7 @@ static Term ConstructProduct(List num, List denom) /// operand 1 /// operand 2 /// result - static Term Div(Term t1, Term t2) + private static Term Div(Term t1, Term t2) { double c1 = t1.Constant; double c2 = t2.Constant; @@ -950,7 +963,7 @@ public static bool IsZero(this Expression e) /// /// operand /// true if e is a constant - static bool IsConstant(this Expression e) + private static bool IsConstant(this Expression e) { return e.NodeType == ExpressionType.Constant && e.Type.Name == "Double"; @@ -959,7 +972,7 @@ static bool IsConstant(this Expression e) #endregion #region identical for parameters/arguments - static bool IsIdenticalTo( + private static bool IsIdenticalTo( this ICollection args1, ICollection args2) { @@ -977,7 +990,7 @@ static bool IsIdenticalTo( return true; } - static bool IsIdenticalTo( + private static bool IsIdenticalTo( this ICollection args1, ICollection args2) { diff --git a/MathExpressionsNet/Properties/AssemblyInfo.cs b/MathExpressionsNet/Properties/AssemblyInfo.cs index 2ac4758..10e1a6e 100644 --- a/MathExpressionsNet/Properties/AssemblyInfo.cs +++ b/MathExpressionsNet/Properties/AssemblyInfo.cs @@ -31,5 +31,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyVersion("1.0.1.0")] +[assembly: AssemblyFileVersion("1.0.1.0")]