123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366 |
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Linq;
- using System.Text;
- using System.Text.RegularExpressions;
- namespace FMSAdmin.Helpers.Formula {
- public class MathCalculator {
- public enum Parameter {
- A = 0, B, C, D, E, F, G, H, I,
- J, K, L, M, N, O, P, Q, R,
- S, T, U, V, W, X, Y, Z
- }
- public enum OperatorType {
- Plus,
- Minus,
- Multiplication,
- Division,
- OpenBrace
- }
- public delegate decimal CalculateFunctionDelegate(decimal inputValue);
- public Dictionary<Parameter, MathParameterValue> Parameters = new Dictionary<Parameter, MathParameterValue>();
- public Dictionary<String, CalculateFunctionDelegate> Functions = new Dictionary<String, CalculateFunctionDelegate>();
- private List<String> OperationOrder = new List<string>();
- private static Regex doubleRegex = new Regex(@"^-?\d+(?:\.\d+)?", RegexOptions.Compiled);
- private static Regex functionRegex = new Regex(@"^\$(.+?)\((.)\)\$", RegexOptions.Compiled);
- private String Fomula { get; set; }
- Stack<FormulaItem> stack = new Stack<FormulaItem>();
- List<FormulaItem> CompliedFormula = new List<FormulaItem>();
- public void TracePostFormula() {
- foreach (var item in this.CompliedFormula) {
- }
- }
- public void SetParameter(MathParameterValue[] parameters) {
- foreach (var p in parameters) {
- this.Parameters.Add(p.Variable, p);
- }
- MapParameters();
- }
- public void SetFunction(string functionName, CalculateFunctionDelegate callback) {
- if (this.Functions.ContainsKey(functionName)) {
- this.Functions[functionName] = callback;
- return;
- }
- this.Functions.Add(functionName, callback);
- MapFunctions();
- }
- public List<FunctionItem> GetFunctions() {
- var list = new List<FunctionItem>();
- foreach (var item in this.CompliedFormula) {
- switch (item.Type) {
- case FormulaItem.ItemType.Function:
- list.Add((FunctionItem)item);
- break;
- }
- }
- return list;
- }
- private void MapFunctions() {
- foreach (var item in this.CompliedFormula) {
- switch (item.Type) {
- case FormulaItem.ItemType.Function:
- var f = item as FunctionItem;
- f.Function.FunctionDelegate = this.Functions[f.Function.FunctionName];
- break;
- }
- }
- }
- private void MapParameters() {
- foreach (var item in this.CompliedFormula) {
- switch (item.Type) {
- case FormulaItem.ItemType.OperandField:
- var o = item as OperandFieldItem;
- o.ParameterValue = this.Parameters[o.ParameterValue.Variable];
- break;
- case FormulaItem.ItemType.Function:
- var f = item as FunctionItem;
- switch (f.Value.Type) {
- case FormulaItem.ItemType.OperandField:
- var of = (OperandFieldItem)f.Value;
- of.ParameterValue = this.Parameters[of.ParameterValue.Variable];
- break;
- }
- break;
- }
- }
- }
- public decimal? Calculate() {
- Stack<FormulaItem> stack = new Stack<FormulaItem>();
- foreach (var item in this.CompliedFormula) {
- switch (item.Type) {
- case FormulaItem.ItemType.Constant:
- case FormulaItem.ItemType.OperandField:
- case FormulaItem.ItemType.Function:
- stack.Push(item);
- break;
- case FormulaItem.ItemType.Operator:
- var ci = this.CalculateOperator(stack.Pop(), stack.Pop(), ((OperatorItem)item).Operator);
- if (ci == null) {
- return null;
- }
- stack.Push(ci);
- break;
- }
- }
- return stack.Pop().GetValue();
- }
- private ConstantItem CalculateOperator(FormulaItem item1, FormulaItem item2, OperatorType op) {
- decimal result = 0;
- switch (op) {
- case OperatorType.Plus:
- result = item2.GetValue() + item1.GetValue();
- break;
- case OperatorType.Minus:
- result = item2.GetValue() - item1.GetValue();
- break;
- case OperatorType.Multiplication:
- result = item2.GetValue() * item1.GetValue();
- break;
- case OperatorType.Division:
- if (item1.GetValue() == 0)
- return null;
- result = item2.GetValue() / item1.GetValue();
- break;
- }
- return new ConstantItem { Value = result };
- }
- public bool Compile(string formula) {
- this.CompliedFormula.Clear();
- formula = formula.Replace(" ", "");
- Stack<OperatorItem> stack = new Stack<OperatorItem>();
- char[] braces = new char[] { '(', ')' };
- string operators = "+-*/";
- var array = formula.ToCharArray();
- int runcount = 0;
- for (var i = 0; i < array.Length;) {
- if (++runcount > 1000) break;
- var item = array[i];
- switch (item) {
- case '(':
- stack.Push(new OperatorItem { Operator = OperatorType.OpenBrace });
- i++;
- break;
- case ')':
- while (stack.Any()) {
- var stackItem = stack.Pop();
- if (stackItem.Operator == OperatorType.OpenBrace) {
- break;
- }
- CompliedFormula.Add(stackItem);
- }
- i++;
- break;
- case '$':
- Match funcMatch = functionRegex.Match(formula.Substring(i));
- if (funcMatch.Success == false || funcMatch.Groups.Count != 3) {
- CompliedFormula.Clear();
- return false;
- }
- var f = new FunctionItem {
- Function = new MathFunction { FunctionName = funcMatch.Groups[1].Value }
- };
- string value = funcMatch.Groups[2].Value;
- if ('A' <= value[0] && value[0] <= 'Z') {
- if (value.Length > 1) {
- return false;
- }
- f.Value = new OperandFieldItem { ParameterValue = new MathParameterValue { Variable = (Parameter)(value[0] - 'A') } };
- } else {
- decimal outputValue;
- if (decimal.TryParse(value, out outputValue) == false) {
- return false;
- }
- f.Value = new ConstantItem { Value = outputValue };
- }
- CompliedFormula.Add(f);
- i += funcMatch.Length;
- break;
- default:
- int operatorIndex = operators.IndexOf(item);
- bool isProcessed = false;
- if (operatorIndex == (int)OperatorType.Minus) {
- if (i == 0 || array[i - 1] == '(' || operators.IndexOf(array[i - 1]) >= 0) {
- isProcessed = true;
- }
- }
- if (isProcessed == false) {
- if (operatorIndex >= 0) {
- //if( operatorIndex < (int)OperatorType.Multiplication )
- {
- while (stack.Any()) {
- var stackItem = stack.Peek();
- if (stackItem.Operator == OperatorType.OpenBrace) break;
- if ((int)stackItem.Operator < operatorIndex) break;
- stack.Pop();
- CompliedFormula.Add(stackItem);
- }
- }
- stack.Push(new OperatorItem { Operator = (OperatorType)operatorIndex });
- i++;
- break;
- } else if ('A' <= item && item <= 'Z') {
- //Trace.WriteLine( item );
- CompliedFormula.Add(new OperandFieldItem { ParameterValue = new MathParameterValue { Variable = (Parameter)(item - 'A') } });
- i++;
- break;
- }
- }
- Match match = doubleRegex.Match(formula.Substring(i));
- if (match.Success == false) {
- throw new Exception("Error: Check Syntax : " + item);
- }
- decimal parseValue = decimal.Parse(match.Value);
- //Trace.WriteLine( value );
- CompliedFormula.Add(new ConstantItem { Value = parseValue });
- i += match.Value.Length;
- break;
- }
- }
- while (stack.Any()) {
- var operatorItem = stack.Pop();
- //Trace.WriteLine( operatorItem.Operator );
- CompliedFormula.Add(operatorItem);
- }
- return true;
- }
- public bool ExtractDouble(string inputValue, out decimal output) {
- return decimal.TryParse(inputValue, out output);
- }
- public abstract class FormulaItem {
- public enum ItemType {
- Operator,
- Constant,
- OperandField,
- Function
- }
- public ItemType Type { get; set; }
- public abstract decimal GetValue();
- }
- public class OperatorItem : FormulaItem {
- public OperatorType Operator { get; set; }
- public OperatorItem() {
- this.Type = FormulaItem.ItemType.Operator;
- }
- public override string ToString() {
- switch (this.Operator) {
- case OperatorType.Plus:
- return "+";
- case OperatorType.Minus:
- return "-";
- case OperatorType.Multiplication:
- return "*";
- case OperatorType.Division:
- return "/";
- }
- return "Unknown";
- }
- public override decimal GetValue() {
- throw new Exception("OperatorItem.GetValue() Called!");
- //return 0;
- }
- }
- public class ConstantItem : FormulaItem {
- public decimal Value { get; set; }
- public ConstantItem() {
- this.Type = FormulaItem.ItemType.Constant;
- }
- public override string ToString() {
- return this.Value.ToString();
- }
- public override decimal GetValue() {
- return this.Value;
- }
- }
- public class OperandFieldItem : FormulaItem {
- public MathParameterValue ParameterValue { get; set; }
- public OperandFieldItem() {
- this.Type = FormulaItem.ItemType.OperandField;
- }
- public override string ToString() {
- return this.ParameterValue.Variable.ToString();
- }
- public override decimal GetValue() {
- return this.ParameterValue.Value;
- }
- }
- public class FunctionItem : FormulaItem {
- public MathFunction Function { get; set; }
- public FormulaItem Value { get; set; }
- public FunctionItem() {
- this.Type = FormulaItem.ItemType.Function;
- }
- public override string ToString() {
- return string.Format("Function({0})", this.Value.GetValue());
- }
- public override decimal GetValue() {
- return this.Function.FunctionDelegate(this.Value.GetValue());
- }
- }
- }
- public class MathFunction {
- public String FunctionName { get; set; }
- public MathCalculator.CalculateFunctionDelegate FunctionDelegate { get; set; }
- }
- public class MathParameterValue {
- public MathCalculator.Parameter Variable { get; set; }
- public decimal Value { get; set; }
- }
- }
|