| /******************************************************************************* | 
 |  * You may amend and distribute as you like, but don't remove this header! | 
 |  * | 
 |  * EPPlus provides server-side generation of Excel 2007/2010 spreadsheets. | 
 |  * See http://www.codeplex.com/EPPlus for details. | 
 |  * | 
 |  * Copyright (C) 2011  Jan Källman | 
 |  * | 
 |  * This library is free software; you can redistribute it and/or | 
 |  * modify it under the terms of the GNU Lesser General Public | 
 |  * License as published by the Free Software Foundation; either | 
 |  * version 2.1 of the License, or (at your option) any later version. | 
 |  | 
 |  * This library is distributed in the hope that it will be useful, | 
 |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.   | 
 |  * See the GNU Lesser General Public License for more details. | 
 |  * | 
 |  * The GNU Lesser General Public License can be viewed at http://www.opensource.org/licenses/lgpl-license.php | 
 |  * If you unfamiliar with this license or have questions about it, here is an http://www.gnu.org/licenses/gpl-faq.html | 
 |  * | 
 |  * All code and executables are provided "as is" with no warranty either express or implied.  | 
 |  * The author accepts no liability for any damage or loss of business that this product may cause. | 
 |  * | 
 |  * Code change notes: | 
 |  *  | 
 |  * Author							Change						Date | 
 |  * ****************************************************************************** | 
 |  * Mats Alm   		                Added       		        2013-03-01 (Prior file history on https://github.com/swmal/ExcelFormulaParser) | 
 |  *******************************************************************************/ | 
 | using System; | 
 | using System.Collections.Generic; | 
 | using System.Linq; | 
 | using System.Text; | 
 | using OfficeOpenXml.FormulaParsing.ExpressionGraph; | 
 | using OfficeOpenXml.FormulaParsing.Exceptions; | 
 | using OfficeOpenXml.Utils; | 
 |  | 
 | namespace OfficeOpenXml.FormulaParsing.Excel.Operators | 
 | { | 
 |     public class Operator : IOperator | 
 |     { | 
 |         private const int PrecedencePercent = 2; | 
 |         private const int PrecedenceExp = 4; | 
 |         private const int PrecedenceMultiplyDevide = 6; | 
 |         private const int PrecedenceIntegerDivision = 8; | 
 |         private const int PrecedenceModulus = 10; | 
 |         private const int PrecedenceAddSubtract = 12; | 
 |         private const int PrecedenceConcat = 15; | 
 |         private const int PrecedenceComparison = 25; | 
 |  | 
 |         private Operator() { } | 
 |  | 
 |         private Operator(Operators @operator, int precedence, Func<CompileResult, CompileResult, CompileResult> implementation) | 
 |         { | 
 |             _implementation = implementation; | 
 |             _precedence = precedence; | 
 |             _operator = @operator; | 
 |         } | 
 |  | 
 |         private readonly Func<CompileResult, CompileResult, CompileResult> _implementation; | 
 |         private readonly int _precedence; | 
 |         private readonly Operators _operator; | 
 |  | 
 |         int IOperator.Precedence | 
 |         { | 
 |             get { return _precedence; } | 
 |         } | 
 |  | 
 |         Operators IOperator.Operator | 
 |         { | 
 |             get { return _operator; } | 
 |         } | 
 |  | 
 |         public CompileResult Apply(CompileResult left, CompileResult right) | 
 |         { | 
 |             if (left.Result is ExcelErrorValue) | 
 |             { | 
 |                 return new CompileResult(left.Result, DataType.ExcelError); | 
 |                 //throw(new ExcelErrorValueException((ExcelErrorValue)left.Result)); | 
 |             } | 
 |             else if (right.Result is ExcelErrorValue) | 
 |             { | 
 |                 return new CompileResult(right.Result, DataType.ExcelError); | 
 |                 //throw(new ExcelErrorValueException((ExcelErrorValue)right.Result)); | 
 |             } | 
 |             return _implementation(left, right); | 
 |         } | 
 |  | 
 |         public override string ToString() | 
 |         { | 
 |             return "Operator: " + _operator; | 
 |         } | 
 |  | 
 |         private static IOperator _plus; | 
 |         public static IOperator Plus | 
 |         { | 
 |             get | 
 |             { | 
 |                 return _plus ?? (_plus = new Operator(Operators.Plus, PrecedenceAddSubtract, (l, r) => | 
 |                 { | 
 |                     l = l == null || l.Result == null ? new CompileResult(0, DataType.Integer) : l; | 
 |                     r = r == null || r.Result == null ? new CompileResult(0, DataType.Integer) : r; | 
 |                     ExcelErrorValue errorVal; | 
 |                     if (EitherIsError(l, r, out errorVal)) | 
 |                     { | 
 |                         return new CompileResult(errorVal); | 
 |                     } | 
 |                     if (l.DataType == DataType.Integer && r.DataType == DataType.Integer) | 
 |                     { | 
 |                         return new CompileResult(l.ResultNumeric + r.ResultNumeric, DataType.Integer); | 
 |                     } | 
 |                     else if ((l.IsNumeric || l.IsNumericString || l.Result is ExcelDataProvider.IRangeInfo) && | 
 |                              (r.IsNumeric || r.IsNumericString || r.Result is ExcelDataProvider.IRangeInfo)) | 
 |                     { | 
 |                         return new CompileResult(l.ResultNumeric + r.ResultNumeric, DataType.Decimal); | 
 |                     } | 
 |                     return new CompileResult(eErrorType.Value); | 
 |                 })); | 
 |             } | 
 |         } | 
 |  | 
 |         private static IOperator _minus; | 
 |         public static IOperator Minus | 
 |         { | 
 |             get | 
 |             { | 
 |                 return _minus ?? (_minus = new Operator(Operators.Minus, PrecedenceAddSubtract, (l, r) => | 
 |                 { | 
 |                     l = l == null || l.Result == null ? new CompileResult(0, DataType.Integer) : l; | 
 |                     r = r == null || r.Result == null ? new CompileResult(0, DataType.Integer) : r; | 
 |                     if (l.DataType == DataType.Integer && r.DataType == DataType.Integer) | 
 |                     { | 
 |                         return new CompileResult(l.ResultNumeric - r.ResultNumeric, DataType.Integer); | 
 |                     } | 
 |                     else if ((l.IsNumeric || l.IsNumericString || l.Result is ExcelDataProvider.IRangeInfo) && | 
 |                              (r.IsNumeric || r.IsNumericString || r.Result is ExcelDataProvider.IRangeInfo)) | 
 |                     { | 
 |                         return new CompileResult(l.ResultNumeric - r.ResultNumeric, DataType.Decimal); | 
 |                     } | 
 |  | 
 |                     return new CompileResult(eErrorType.Value); | 
 |                 })); | 
 |             } | 
 |         } | 
 |  | 
 |         private static IOperator _multiply; | 
 |         public static IOperator Multiply | 
 |         { | 
 |             get | 
 |             { | 
 |                 return _multiply ?? (_multiply = new Operator(Operators.Multiply, PrecedenceMultiplyDevide, (l, r) => | 
 |                 { | 
 |                     l = l ?? new CompileResult(0, DataType.Integer); | 
 |                     r = r ?? new CompileResult(0, DataType.Integer); | 
 |                     if (l.DataType == DataType.Integer && r.DataType == DataType.Integer) | 
 |                     { | 
 |                         return new CompileResult(l.ResultNumeric*r.ResultNumeric, DataType.Integer); | 
 |                     } | 
 |                     else if ((l.IsNumeric || l.IsNumericString || l.Result is ExcelDataProvider.IRangeInfo) && | 
 |                              (r.IsNumeric || r.IsNumericString || r.Result is ExcelDataProvider.IRangeInfo)) | 
 |                     { | 
 |                         return new CompileResult(l.ResultNumeric*r.ResultNumeric, DataType.Decimal); | 
 |                     } | 
 |                     return new CompileResult(eErrorType.Value); | 
 |                 })); | 
 |             } | 
 |         } | 
 |  | 
 |         private static IOperator _divide; | 
 |         public static IOperator Divide | 
 |         { | 
 |             get | 
 |             { | 
 |                 return _divide ?? (_divide = new Operator(Operators.Divide, PrecedenceMultiplyDevide, (l, r) => | 
 |                 { | 
 |                     if (!(l.IsNumeric || l.IsNumericString || l.Result is ExcelDataProvider.IRangeInfo) || | 
 |                         !(r.IsNumeric || r.IsNumericString || r.Result is ExcelDataProvider.IRangeInfo)) | 
 |                     { | 
 |                         return new CompileResult(eErrorType.Value); | 
 |                     } | 
 |                     var left = l.ResultNumeric; | 
 |                     var right = r.ResultNumeric; | 
 |                     if (Math.Abs(right - 0d) < double.Epsilon) | 
 |                     { | 
 |                         return new CompileResult(eErrorType.Div0); | 
 |                     } | 
 |                     else if ((l.IsNumeric || l.IsNumericString || l.Result is ExcelDataProvider.IRangeInfo) && | 
 |                              (r.IsNumeric || r.IsNumericString || r.Result is ExcelDataProvider.IRangeInfo)) | 
 |                     { | 
 |                         return new CompileResult(left/right, DataType.Decimal); | 
 |                     } | 
 |                     return new CompileResult(eErrorType.Value); | 
 |                 })); | 
 |             } | 
 |         } | 
 |  | 
 |         public static IOperator Exp | 
 |         { | 
 |             get | 
 |             { | 
 |                 return new Operator(Operators.Exponentiation, PrecedenceExp, (l, r) => | 
 |                     { | 
 |                         if (l == null && r == null) | 
 |                         { | 
 |                             return new CompileResult(eErrorType.Value); | 
 |                         } | 
 |                         l = l ?? new CompileResult(0, DataType.Integer); | 
 |                         r = r ?? new CompileResult(0, DataType.Integer); | 
 |                         if ((l.IsNumeric || l.Result is ExcelDataProvider.IRangeInfo) && (r.IsNumeric || r.Result is ExcelDataProvider.IRangeInfo)) | 
 |                         { | 
 |                             return new CompileResult(Math.Pow(l.ResultNumeric, r.ResultNumeric), DataType.Decimal); | 
 |                         } | 
 |                         return new CompileResult(0d, DataType.Decimal); | 
 |                     }); | 
 |             } | 
 |         } | 
 |  | 
 |         public static IOperator Concat | 
 |         { | 
 |             get | 
 |             { | 
 |                 return new Operator(Operators.Concat, PrecedenceConcat, (l, r) => | 
 |                     { | 
 |                         l = l ?? new CompileResult(string.Empty, DataType.String); | 
 |                         r = r ?? new CompileResult(string.Empty, DataType.String); | 
 |                         var lStr = l.Result != null ? l.ResultValue.ToString() : string.Empty; | 
 |                         var rStr = r.Result != null ? r.ResultValue.ToString() : string.Empty; | 
 |                         return new CompileResult(string.Concat(lStr, rStr), DataType.String); | 
 |                     }); | 
 |             } | 
 |         } | 
 |  | 
 |         private static IOperator _greaterThan; | 
 |         public static IOperator GreaterThan | 
 |         { | 
 |             get | 
 |             { | 
 |                 //return new Operator(Operators.GreaterThan, PrecedenceComparison, (l, r) => new CompileResult(Compare(l, r) > 0, DataType.Boolean)); | 
 |                 return _greaterThan ?? | 
 |                        (_greaterThan = | 
 |                            new Operator(Operators.LessThanOrEqual, PrecedenceComparison, | 
 |                                (l, r) => Compare(l, r, (compRes) => compRes > 0))); | 
 |             } | 
 |         } | 
 |  | 
 |         private static IOperator _eq; | 
 |         public static IOperator Eq | 
 |         { | 
 |             get | 
 |             { | 
 |                 //return new Operator(Operators.Equals, PrecedenceComparison, (l, r) => new CompileResult(Compare(l, r) == 0, DataType.Boolean)); | 
 |                 return _eq ?? | 
 |                        (_eq = | 
 |                            new Operator(Operators.LessThanOrEqual, PrecedenceComparison, | 
 |                                (l, r) => Compare(l, r, (compRes) => compRes == 0))); | 
 |             } | 
 |         } | 
 |  | 
 |         private static IOperator _notEqualsTo; | 
 |         public static IOperator NotEqualsTo | 
 |         { | 
 |             get | 
 |             { | 
 |                 //return new Operator(Operators.NotEqualTo, PrecedenceComparison, (l, r) => new CompileResult(Compare(l, r) != 0, DataType.Boolean)); | 
 |                 return _notEqualsTo ?? | 
 |                        (_notEqualsTo = | 
 |                            new Operator(Operators.LessThanOrEqual, PrecedenceComparison, | 
 |                                (l, r) => Compare(l, r, (compRes) => compRes != 0))); | 
 |             } | 
 |         } | 
 |  | 
 |         private static IOperator _greaterThanOrEqual; | 
 |         public static IOperator GreaterThanOrEqual | 
 |         { | 
 |             get | 
 |             { | 
 |                 //return new Operator(Operators.GreaterThanOrEqual, PrecedenceComparison, (l, r) => new CompileResult(Compare(l, r) >= 0, DataType.Boolean)); | 
 |                 return _greaterThanOrEqual ?? | 
 |                        (_greaterThanOrEqual = | 
 |                            new Operator(Operators.LessThanOrEqual, PrecedenceComparison, | 
 |                                (l, r) => Compare(l, r, (compRes) => compRes >= 0))); | 
 |             } | 
 |         } | 
 |  | 
 |         private static IOperator _lessThan; | 
 |         public static IOperator LessThan | 
 |         { | 
 |             get | 
 |             { | 
 |                 //return new Operator(Operators.LessThan, PrecedenceComparison, (l, r) => new CompileResult(Compare(l, r) < 0, DataType.Boolean)); | 
 |                 return _lessThan ?? | 
 |                        (_lessThan = | 
 |                            new Operator(Operators.LessThanOrEqual, PrecedenceComparison, | 
 |                                (l, r) => Compare(l, r, (compRes) => compRes < 0))); | 
 |             } | 
 |         } | 
 |  | 
 |         public static IOperator LessThanOrEqual | 
 |         { | 
 |             get | 
 |             { | 
 |                 //return new Operator(Operators.LessThanOrEqual, PrecedenceComparison, (l, r) => new CompileResult(Compare(l, r) <= 0, DataType.Boolean)); | 
 |                 return new Operator(Operators.LessThanOrEqual, PrecedenceComparison, (l, r) => Compare(l, r, (compRes) => compRes <= 0)); | 
 |             } | 
 |         } | 
 |  | 
 |         private static IOperator _percent; | 
 |         public static IOperator Percent | 
 |         { | 
 |             get | 
 |             { | 
 |                 if (_percent == null) | 
 |                 { | 
 |                     _percent = new Operator(Operators.Percent, PrecedencePercent, (l, r) => | 
 |                         { | 
 |                             l = l ?? new CompileResult(0, DataType.Integer); | 
 |                             r = r ?? new CompileResult(0, DataType.Integer); | 
 |                             if (l.DataType == DataType.Integer && r.DataType == DataType.Integer) | 
 |                             { | 
 |                                 return new CompileResult(l.ResultNumeric * r.ResultNumeric, DataType.Integer); | 
 |                             } | 
 |                             else if ((l.IsNumeric || l.Result is ExcelDataProvider.IRangeInfo) && (r.IsNumeric || r.Result is ExcelDataProvider.IRangeInfo)) | 
 |                             { | 
 |                                 return new CompileResult(l.ResultNumeric * r.ResultNumeric, DataType.Decimal); | 
 |                             } | 
 |                             return new CompileResult(eErrorType.Value); | 
 |                         }); | 
 |                 } | 
 |                 return _percent; | 
 |             } | 
 |         } | 
 |  | 
 |         private static object GetObjFromOther(CompileResult obj, CompileResult other) | 
 |         { | 
 |             if (obj.Result == null) | 
 |             { | 
 |                 if (other.DataType == DataType.String) return string.Empty; | 
 |                 else return 0d; | 
 |             } | 
 |             return obj.ResultValue; | 
 |         } | 
 |  | 
 |         private static CompileResult Compare(CompileResult l, CompileResult r, Func<int, bool> comparison ) | 
 |         { | 
 |             ExcelErrorValue errorVal; | 
 |             if (EitherIsError(l, r, out errorVal)) | 
 |             { | 
 |                 return new CompileResult(errorVal); | 
 |             } | 
 |             object left, right; | 
 |             left = GetObjFromOther(l, r); | 
 |             right = GetObjFromOther(r, l); | 
 |             if (ConvertUtil.IsNumeric(left) && ConvertUtil.IsNumeric(right)) | 
 |             { | 
 |                 var lnum = ConvertUtil.GetValueDouble(left); | 
 |                 var rnum = ConvertUtil.GetValueDouble(right); | 
 |                 if (Math.Abs(lnum - rnum) < double.Epsilon) | 
 |                 { | 
 |                     return new CompileResult(comparison(0), DataType.Boolean); | 
 |                 } | 
 |                 var comparisonResult = lnum.CompareTo(rnum); | 
 |                 return new CompileResult(comparison(comparisonResult), DataType.Boolean); | 
 |             } | 
 |             else | 
 |             { | 
 |                 var comparisonResult = CompareString(left, right); | 
 |                 return new CompileResult(comparison(comparisonResult), DataType.Boolean); | 
 |             } | 
 |         } | 
 |  | 
 |         private static int CompareString(object l, object r) | 
 |         { | 
 |             var sl = (l ?? "").ToString(); | 
 |             var sr = (r ?? "").ToString(); | 
 |             return System.String.Compare(sl, sr, System.StringComparison.Ordinal); | 
 |         } | 
 |  | 
 |         private static bool  EitherIsError(CompileResult l, CompileResult r, out ExcelErrorValue errorVal) | 
 |         { | 
 |             if (l.DataType == DataType.ExcelError) | 
 |             { | 
 |                 errorVal = (ExcelErrorValue) l.Result; | 
 |                 return true; | 
 |             } | 
 |             if (r.DataType == DataType.ExcelError) | 
 |             { | 
 |                 errorVal = (ExcelErrorValue) r.Result; | 
 |                 return true; | 
 |             } | 
 |             errorVal = null; | 
 |             return false; | 
 |         } | 
 |     } | 
 | } |