MathCalculator.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Text.RegularExpressions;
  7. namespace iBemsDataService.Util
  8. {
  9. public class MathCalculator
  10. {
  11. public enum Parameter
  12. {
  13. A = 0 , B , C , D , E , F , G , H , I ,
  14. J , K , L , M , N , O , P , Q , R ,
  15. S , T , U , V , W , X , Y , Z
  16. }
  17. public enum OperatorType
  18. {
  19. Plus,
  20. Minus,
  21. Multiplication,
  22. Division,
  23. OpenBrace
  24. }
  25. public delegate decimal CalculateFunctionDelegate( decimal inputValue );
  26. public Dictionary<Parameter, MathParameterValue> Parameters = new Dictionary<Parameter , MathParameterValue>();
  27. public Dictionary<String, CalculateFunctionDelegate> Functions = new Dictionary<String , CalculateFunctionDelegate>();
  28. private List<String> OperationOrder = new List<string>();
  29. private static Regex doubleRegex = new Regex( @"^-?\d+(?:\.\d+)?" , RegexOptions.Compiled );
  30. private static Regex functionRegex = new Regex( @"^\$(.+?)\((.)\)\$" , RegexOptions.Compiled );
  31. private String Fomula { get; set; }
  32. Stack<FormulaItem> stack = new Stack<FormulaItem>();
  33. List<FormulaItem> CompliedFormula = new List<FormulaItem>();
  34. public void TracePostFormula()
  35. {
  36. Trace.Write( "Formula : " );
  37. foreach( var item in this.CompliedFormula )
  38. {
  39. Trace.Write( item );
  40. Trace.Write( " " );
  41. }
  42. Trace.WriteLine( "" );
  43. }
  44. public void SetParameter( MathParameterValue[] parameters )
  45. {
  46. foreach( var p in parameters )
  47. {
  48. this.Parameters.Add( p.Variable , p );
  49. }
  50. //var pv = new ParameterValue { Variable = p, Value = value };
  51. //this.Parameters.Add( p , pv );
  52. MapParameters();
  53. // return pv;
  54. }
  55. public void SetFunction( string functionName, CalculateFunctionDelegate callback)
  56. {
  57. if( this.Functions.ContainsKey( functionName ) )
  58. {
  59. this.Functions[functionName] = callback;
  60. return;
  61. }
  62. this.Functions.Add( functionName , callback );
  63. //var pv = new ParameterValue { Variable = p, Value = value };
  64. //this.Parameters.Add( p , pv );
  65. MapFunctions();
  66. // return pv;
  67. }
  68. public List<FunctionItem> GetFunctions()
  69. {
  70. var list = new List<FunctionItem>();
  71. foreach( var item in this.CompliedFormula )
  72. {
  73. switch( item.Type )
  74. {
  75. case FormulaItem.ItemType.Function:
  76. list.Add( (FunctionItem)item );
  77. break;
  78. }
  79. }
  80. return list;
  81. }
  82. private void MapFunctions()
  83. {
  84. foreach( var item in this.CompliedFormula )
  85. {
  86. switch( item.Type )
  87. {
  88. case FormulaItem.ItemType.Function:
  89. var f = item as FunctionItem;
  90. f.Function.FunctionDelegate = this.Functions[f.Function.FunctionName];
  91. break;
  92. }
  93. }
  94. }
  95. private void MapParameters()
  96. {
  97. foreach( var item in this.CompliedFormula )
  98. {
  99. switch( item.Type )
  100. {
  101. case FormulaItem.ItemType.OperandField:
  102. var o = item as OperandFieldItem;
  103. o.ParameterValue = this.Parameters[o.ParameterValue.Variable];
  104. break;
  105. case FormulaItem.ItemType.Function:
  106. var f = item as FunctionItem;
  107. switch( f.Value.Type )
  108. {
  109. case FormulaItem.ItemType.OperandField:
  110. var of = (OperandFieldItem)f.Value;
  111. of.ParameterValue = this.Parameters[of.ParameterValue.Variable];
  112. break;
  113. }
  114. break;
  115. }
  116. }
  117. }
  118. public decimal? Calculate()
  119. {
  120. Stack<FormulaItem> stack = new Stack<FormulaItem>();
  121. foreach( var item in this.CompliedFormula )
  122. {
  123. switch( item.Type )
  124. {
  125. case FormulaItem.ItemType.Constant:
  126. case FormulaItem.ItemType.OperandField:
  127. case FormulaItem.ItemType.Function:
  128. stack.Push(item);
  129. break;
  130. case FormulaItem.ItemType.Operator:
  131. var ci = this.CalculateOperator( stack.Pop() , stack.Pop() , ((OperatorItem)item).Operator );
  132. if( ci == null )
  133. {
  134. return null;
  135. }
  136. stack.Push( ci );
  137. // Trace.WriteLine( ci.GetValue() );
  138. break;
  139. }
  140. }
  141. return stack.Pop().GetValue();
  142. }
  143. private ConstantItem CalculateOperator( FormulaItem item1 , FormulaItem item2 , OperatorType op )
  144. {
  145. decimal result = 0;
  146. // Trace.WriteLine( item2.GetValue() + " " + op + " " + item1.GetValue() );
  147. switch( op )
  148. {
  149. case OperatorType.Plus:
  150. result = item2.GetValue() + item1.GetValue();
  151. break;
  152. case OperatorType.Minus:
  153. result = item2.GetValue() - item1.GetValue();
  154. break;
  155. case OperatorType.Multiplication:
  156. result = item2.GetValue() * item1.GetValue();
  157. break;
  158. case OperatorType.Division:
  159. if( item1.GetValue() == 0 )
  160. return null;
  161. result = item2.GetValue() / item1.GetValue();
  162. break;
  163. }
  164. return new ConstantItem { Value = result };
  165. }
  166. public bool Compile( string formula )
  167. {
  168. this.CompliedFormula.Clear();
  169. formula = formula.Replace( " ", "" );
  170. Stack<OperatorItem> stack = new Stack<OperatorItem>();
  171. char[] braces = new char[] { '(' , ')' };
  172. string operators = "+-*/";
  173. var array = formula.ToCharArray();
  174. for( var i = 0 ; i < array.Length ; )
  175. {
  176. var item = array[i];
  177. switch( item )
  178. {
  179. case '(':
  180. stack.Push( new OperatorItem { Operator = OperatorType.OpenBrace } );
  181. i++;
  182. break;
  183. case ')':
  184. while( stack.Any() )
  185. {
  186. var stackItem = stack.Pop();
  187. if( stackItem.Operator == OperatorType.OpenBrace )
  188. {
  189. break;
  190. }
  191. CompliedFormula.Add( stackItem );
  192. //Trace.WriteLine( stackItem.Operator );
  193. }
  194. i++;
  195. break;
  196. case '$':
  197. Match funcMatch = functionRegex.Match( formula.Substring( i ) );
  198. if( funcMatch.Success == false || funcMatch.Groups.Count != 3 )
  199. {
  200. CompliedFormula.Clear();
  201. return false;
  202. }
  203. Trace.WriteLine( funcMatch.Groups[0].Value );
  204. Trace.WriteLine( funcMatch.Groups[1].Value );
  205. Trace.WriteLine( funcMatch.Groups[2].Value );
  206. var f = new FunctionItem {
  207. Function = new MathFunction { FunctionName = funcMatch.Groups[1].Value }
  208. };
  209. string value = funcMatch.Groups[2].Value;
  210. if( 'A' <= value[0] && value[0] <= 'Z' )
  211. {
  212. if( value.Length > 1 )
  213. {
  214. Trace.WriteLine( "function parameter wrong!" );
  215. return false;
  216. }
  217. f.Value = new OperandFieldItem { ParameterValue = new MathParameterValue { Variable = (Parameter)(value[0] - 'A') } };
  218. }
  219. else
  220. {
  221. decimal outputValue;
  222. if( decimal.TryParse( value, out outputValue ) == false )
  223. {
  224. return false;
  225. }
  226. f.Value = new ConstantItem { Value = outputValue };
  227. }
  228. CompliedFormula.Add( f );
  229. i += funcMatch.Length;
  230. break;
  231. default:
  232. int operatorIndex = operators.IndexOf(item);
  233. bool isProcessed = false;
  234. if( operatorIndex == (int)OperatorType.Minus )
  235. {
  236. if( i == 0 || array[i-1] == '(' || operators.IndexOf(array[i-1]) >= 0 )
  237. {
  238. isProcessed = true;
  239. }
  240. }
  241. if( isProcessed == false )
  242. {
  243. if( operatorIndex >= 0 )
  244. {
  245. //if( operatorIndex < (int)OperatorType.Multiplication )
  246. {
  247. while( stack.Any() )
  248. {
  249. var stackItem = stack.Peek();
  250. if( stackItem.Operator == OperatorType.OpenBrace ) break;
  251. if( (int)stackItem.Operator < operatorIndex ) break;
  252. stack.Pop();
  253. CompliedFormula.Add( stackItem );
  254. }
  255. }
  256. stack.Push( new OperatorItem { Operator = (OperatorType)operatorIndex } );
  257. i++;
  258. break;
  259. }
  260. else if( 'A' <= item && item <= 'Z' )
  261. {
  262. //Trace.WriteLine( item );
  263. CompliedFormula.Add( new OperandFieldItem { ParameterValue = new MathParameterValue { Variable = (Parameter)(item - 'A') } } );
  264. i++;
  265. break;
  266. }
  267. }
  268. Match match = doubleRegex.Match(formula.Substring( i ));
  269. if( match.Success == false )
  270. {
  271. throw new Exception( "Error: Check Syntax : " + item );
  272. }
  273. decimal parseValue = decimal.Parse( match.Value );
  274. //Trace.WriteLine( value );
  275. CompliedFormula.Add( new ConstantItem { Value = parseValue } );
  276. i += match.Value.Length;
  277. break;
  278. }
  279. }
  280. while( stack.Any() )
  281. {
  282. var operatorItem = stack.Pop();
  283. //Trace.WriteLine( operatorItem.Operator );
  284. CompliedFormula.Add( operatorItem );
  285. }
  286. return true;
  287. }
  288. public bool ExtractDouble( string inputValue, out decimal output )
  289. {
  290. return decimal.TryParse( inputValue, out output);
  291. }
  292. public abstract class FormulaItem
  293. {
  294. public enum ItemType
  295. {
  296. Operator,
  297. Constant,
  298. OperandField,
  299. Function
  300. }
  301. public ItemType Type { get; set; }
  302. public abstract decimal GetValue();
  303. }
  304. public class OperatorItem : FormulaItem
  305. {
  306. public OperatorType Operator { get; set; }
  307. public OperatorItem()
  308. {
  309. this.Type = FormulaItem.ItemType.Operator;
  310. }
  311. public override string ToString()
  312. {
  313. switch( this.Operator )
  314. {
  315. case OperatorType.Plus:
  316. return "+";
  317. case OperatorType.Minus:
  318. return "-";
  319. case OperatorType.Multiplication:
  320. return "*";
  321. case OperatorType.Division:
  322. return "/";
  323. }
  324. return "Unknown";
  325. }
  326. public override decimal GetValue()
  327. {
  328. throw new Exception( "OperatorItem.GetValue() Called!" );
  329. //return 0;
  330. }
  331. }
  332. public class ConstantItem : FormulaItem
  333. {
  334. public decimal Value { get; set; }
  335. public ConstantItem()
  336. {
  337. this.Type = FormulaItem.ItemType.Constant;
  338. }
  339. public override string ToString()
  340. {
  341. return this.Value.ToString();
  342. }
  343. public override decimal GetValue()
  344. {
  345. return this.Value;
  346. }
  347. }
  348. public class OperandFieldItem : FormulaItem
  349. {
  350. public MathParameterValue ParameterValue { get; set; }
  351. public OperandFieldItem()
  352. {
  353. this.Type = FormulaItem.ItemType.OperandField;
  354. }
  355. public override string ToString()
  356. {
  357. return this.ParameterValue.Variable.ToString();
  358. }
  359. public override decimal GetValue()
  360. {
  361. return this.ParameterValue.Value;
  362. }
  363. }
  364. public class FunctionItem : FormulaItem
  365. {
  366. public MathFunction Function { get; set; }
  367. public FormulaItem Value { get; set; }
  368. public FunctionItem()
  369. {
  370. this.Type = FormulaItem.ItemType.Function;
  371. }
  372. public override string ToString()
  373. {
  374. return string.Format( "Function({0})" , this.Value.GetValue() );
  375. }
  376. public override decimal GetValue()
  377. {
  378. return this.Function.FunctionDelegate( this.Value.GetValue() );
  379. }
  380. }
  381. }
  382. public class MathFunction
  383. {
  384. public String FunctionName { get; set; }
  385. public MathCalculator.CalculateFunctionDelegate FunctionDelegate { get; set; }
  386. }
  387. public class MathParameterValue
  388. {
  389. public MathCalculator.Parameter Variable { get; set; }
  390. public decimal Value { get; set; }
  391. }
  392. }