123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448 |
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Linq;
- using System.Text;
- using System.Text.RegularExpressions;
- namespace iBemsDataService.Util
- {
- 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()
- {
- Trace.Write( "Formula : " );
- foreach( var item in this.CompliedFormula )
- {
- Trace.Write( item );
- Trace.Write( " " );
- }
- Trace.WriteLine( "" );
- }
- public void SetParameter( MathParameterValue[] parameters )
- {
- foreach( var p in parameters )
- {
- this.Parameters.Add( p.Variable , p );
- }
- //var pv = new ParameterValue { Variable = p, Value = value };
- //this.Parameters.Add( p , pv );
- MapParameters();
- // return pv;
- }
- public void SetFunction( string functionName, CalculateFunctionDelegate callback)
- {
- if( this.Functions.ContainsKey( functionName ) )
- {
- this.Functions[functionName] = callback;
- return;
- }
- this.Functions.Add( functionName , callback );
- //var pv = new ParameterValue { Variable = p, Value = value };
- //this.Parameters.Add( p , pv );
- MapFunctions();
- // return pv;
- }
- 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 );
- // Trace.WriteLine( ci.GetValue() );
- break;
- }
- }
- return stack.Pop().GetValue();
- }
- private ConstantItem CalculateOperator( FormulaItem item1 , FormulaItem item2 , OperatorType op )
- {
- decimal result = 0;
- // Trace.WriteLine( item2.GetValue() + " " + op + " " + item1.GetValue() );
- 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();
- for( var i = 0 ; i < array.Length ; )
- {
- 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 );
- //Trace.WriteLine( stackItem.Operator );
- }
- i++;
- break;
- case '$':
- Match funcMatch = functionRegex.Match( formula.Substring( i ) );
- if( funcMatch.Success == false || funcMatch.Groups.Count != 3 )
- {
- CompliedFormula.Clear();
- return false;
- }
- Trace.WriteLine( funcMatch.Groups[0].Value );
- Trace.WriteLine( funcMatch.Groups[1].Value );
- Trace.WriteLine( funcMatch.Groups[2].Value );
- 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 )
- {
- Trace.WriteLine( "function parameter wrong!" );
- 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; }
- }
- }
-
|