Files
wg_cpso/CaeGlobals/UnitSystem/MyNCalc.cs

310 lines
12 KiB
C#
Raw Permalink Normal View History

2026-03-25 18:20:24 +08:00
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnitsNet;
using UnitsNet.Units;
using NCalc;
using System.Text.RegularExpressions;
using System.Reflection;
namespace CaeGlobals
{
public static class MyNCalc
{
// Variables
public static OrderedDictionary<string, object> ExistingParameters = null;
// Methods
static public double ConvertFromString(string valueString, Func<string, double> ConvertToCurrentUnits)
{
double valueDouble;
valueString = valueString.Trim();
//
if (valueString.Length == 0 || valueString == "=") return 0; // empty string -> 0
if (!double.TryParse(valueString, out valueDouble))
{
if (valueString.StartsWith("="))
{
valueString = valueString.Substring(1, valueString.Length - 1);
valueString = PreprocessExpression(valueString);
//List<string> parameters = GetParameters(valueString);
Expression e = GetExpression(valueString);
if (!e.HasErrors())
{
object result = e.Evaluate();
if (result is bool bl) valueDouble = bl ? 1 : 0;
else if (result is byte byt) valueDouble = byt;
else if (result is decimal dec) valueDouble = (double)dec;
else if (result is int i) valueDouble = i;
else if (result is float f) valueDouble = f;
else if (result is double d) valueDouble = d;
else
{
double.TryParse(result.ToString(), out valueDouble);
}
}
else
{
throw new CaeException("Equation error:" + Environment.NewLine + e.Error);
}
}
else valueDouble = ConvertToCurrentUnits(valueString);
}
//
return valueDouble;
}
static public double SolveEquation(string valueString)
{
double valueDouble;
valueString = valueString.Trim();
//
if (valueString.Length == 0 || valueString == "=") return 0; // empty string -> 0
if (!double.TryParse(valueString, out valueDouble))
{
if (valueString.StartsWith("="))
{
valueString = valueString.Substring(1, valueString.Length - 1);
valueString = PreprocessExpression(valueString);
//List<string> parameters = GetParameters(valueString);
Expression e = GetExpression(valueString);
if (!e.HasErrors())
{
object result = e.Evaluate();
if (result is bool bl) valueDouble = bl ? 1 : 0;
else if (result is byte byt) valueDouble = byt;
else if (result is decimal dec) valueDouble = (double)dec;
else if (result is int i) valueDouble = i;
else if (result is float f) valueDouble = f;
else if (result is double d) valueDouble = d;
else
{
double.TryParse(result.ToString(), out valueDouble);
}
}
else
{
throw new CaeException("Equation error:" + Environment.NewLine + e.Error);
}
}
else throw new CaeException("Equation error:" + Environment.NewLine + "Equation must start with = sign.");
}
//
return valueDouble;
}
static public double[] SolveArrayEquation(string equation)
{
double[] values;
string scalarEquation = equation;
equation = equation.Trim();
//
if (equation.Length == 0 || equation == "=")
throw new CaeException("Equation error:" + Environment.NewLine + "Equation cannot be evaluated.");
//
if (equation.StartsWith("="))
{
equation = equation.Substring(1, equation.Length - 1);
equation = PreprocessExpression(equation);
//
Expression e = GetArrayExpression(equation);
if (!e.HasErrors())
{
object result = e.Evaluate();
if (result is List<object> list)
{
// Array equation
if (list.Count > 0)
{
int count = 0;
values = new double[list.Count];
foreach (var obj in list)
{
if (obj is bool bl) values[count++] = bl ? 1 : 0;
else if (obj is byte byt) values[count++] = byt;
else if (obj is decimal dec) values[count++] = (double)dec;
else if (obj is int i) values[count++] = i;
else if (obj is float f) values[count++] = f;
else if (obj is double d) values[count++] = d;
else count++;
}
}
// Scalar equation
else
{
double value = ConvertFromString(scalarEquation, null);
return new double[] { value };
}
}
else
{
throw new CaeException("Equation error:" + Environment.NewLine + "Equation return type unrecognized.");
}
}
else
{
throw new CaeException("Equation error:" + Environment.NewLine + e.Error);
}
}
else throw new CaeException("Equation error:" + Environment.NewLine + "Equation must start with = sign.");
//
return values;
}
static public bool HasErrors(string equation, out HashSet<string> parameterNames)
{
double valueDouble;
parameterNames = null;
//
equation = equation.Trim();
//
if (equation.Length == 0 || equation == "=") return false;
if (!double.TryParse(equation, out valueDouble))
{
if (equation.StartsWith("="))
{
equation = equation.Substring(1, equation.Length - 1);
equation = PreprocessExpression(equation);
Expression e = GetExpression(equation);
parameterNames = GetParameters(equation);
//
if (!e.HasErrors())
{
object result = e.Evaluate();
}
return e.HasErrors();
}
else return false;
}
//
return false;
}
static public Expression GetExpression(string expression)
{
Expression e = new Expression(expression, EvaluateOptions.IgnoreCase);
// Add constants
e.Parameters.Add("pi", Math.PI);
e.Parameters.Add("Pi", Math.PI);
//
if (ExistingParameters != null)
{
foreach (var entry in ExistingParameters) e.Parameters.Add(entry.Key, entry.Value);
}
return e;
}
//
static public Expression GetArrayExpression(string expression)
{
Expression e = new Expression(expression, EvaluateOptions.IgnoreCase | EvaluateOptions.IterateParameters);
// Add constants
e.Parameters.Add("pi", Math.PI);
e.Parameters.Add("Pi", Math.PI);
//
if (ExistingParameters != null)
{
foreach (var entry in ExistingParameters) e.Parameters.Add(entry.Key, entry.Value);
}
return e;
}
static public HashSet<string> GetParameters(string expression)
{
HashSet<string> parameters = new HashSet<string>();
Expression e = new Expression(expression);
//
e.EvaluateFunction += delegate (string name, FunctionArgs args) {
args.EvaluateParameters();
args.Result = 0;
};
e.EvaluateParameter += delegate (string name, ParameterArgs args) {
parameters.Add(name);
args.Result = 0;
};
try
{
e.Evaluate();
}
catch
{
}
return parameters;
}
//
static public string PreprocessExpression(string expr)
{
// Sort parameter names by length (longest first)
var sortedNames = ExistingParameters.Keys
.OrderByDescending(name => name.Length)
.ToList();
// Step 1: Replace each parameter with a unique placeholder
int count = 0;
string placeholder;
string bracketedParam;
Dictionary<string, string> placeholders = new Dictionary<string, string>();
//
foreach (var param in sortedNames)
{
// Create a unique placeholder
placeholder = $"@{count++}@";
bracketedParam = $"[{param}]";
// Replace all instances of the parameter with the placeholder
if (expr.Contains(bracketedParam))
{
expr = expr.Replace(bracketedParam, placeholder);
// Store the mapping between placeholder and actual parameter
placeholders[placeholder] = param;
}
else if (expr.Contains(param))
{
expr = expr.Replace(param, placeholder);
// Store the mapping between placeholder and actual parameter
placeholders[placeholder] = param;
}
}
// Step 2: Replace the placeholders with the bracketed parameter names
string bracketed;
foreach (var entry in placeholders)
{
bracketed = $"[{entry.Value}]";
expr = expr.Replace(entry.Key, bracketed);
}
//
return expr;
}
static public string[] GetReservedParameterNames()
{
return new string[] { "Pi", "pi", "x", "y", "z" };
}
//
static public string[] GetFunctionConstants()
{
return new string[] { "Pi" };
}
static public string[] GetFunctionSnippets()
{
return new string[] { "Abs(^)",
"Acos(^)",
"Asin(^)",
"Atan(^)",
"Ceiling(^)",
"Cos(^)",
"Exp(^)",
"Floor(^)",
"IEEERemainder(^, )",
"Ln(^)",
"Log(^, )",
"Log10(^)",
"Max(^, )",
"Min(^, )",
"Pow(^, )",
"Round(^, )",
"Sign(^)",
"Sin(^)",
"Sqrt(^)",
"Tan(^)",
"Truncate(^)",
"If(^, , )" };
}
}
}