/// <summary>
        /// Parse the assigner to get the tree of operations and their operands (of type literal, reference, or operation)
        /// </summary>
        /// <param name="assignerToken"></param>
        /// <param name="errorMessage"></param>
        /// <returns></returns>
        private Boolean ParseAssigner(String assignerToken, ref String errorMessage)
        {
            Boolean     returnValue = default(Boolean);
            OperandBase operand     = default(OperandBase);

            try
            {
                if (assignerToken == String.Empty)
                {
                    throw new ArgumentException(String.Format("Incorrectly formatted formula; the assigner is not defined: '{0}'", assignerToken));
                }

                //init parsing variables dictionary
                if (ParsingVariables == null)
                {
                    ParsingVariables = new Dictionary <String, String>();
                }
                else
                {
                    ParsingVariables.Clear();
                }

                //pre-parse token for parenthesis, and replace expressions in parenthesis with variables
                if (!ParseParenthesis(ref assignerToken, ref errorMessage))
                {
                    throw new ApplicationException(errorMessage);
                }

                //recursively parse assignerToken and build operation tree
                if (!ParseToken(assignerToken, ref operand, ref errorMessage))
                {
                    throw new ApplicationException(errorMessage);
                }

                //clear parsing variables dictionary
                ParsingVariables.Clear();

                AssignerOperand = operand;

                returnValue = true;
            }
            catch (Exception ex)
            {
                errorMessage = ex.Message;
                Log.Write(ex, MethodBase.GetCurrentMethod(), EventLogEntryType.Error);
                //throw;
            }
            return(returnValue);
        }
        /// <summary>
        /// The operation's OperandBase implementation is invoked here.
        /// </summary>
        /// <param name="dictionary"></param>
        /// <param name="context"></param>
        /// <returns></returns>
        public OperandBase Run(Dictionary <String, OperandBase> dictionary, FormulaExecutionContext context)
        {
            OperandBase returnValue = default(OperandBase);

            try
            {
                returnValue = functorOperandBase(dictionary, context);
            }
            catch (Exception ex)
            {
                Log.Write(ex, MethodBase.GetCurrentMethod(), EventLogEntryType.Error);

                throw;
            }
            return(returnValue);
        }
        /// <summary>
        /// Run operation on child operands. Must pass through sheet cell and formula to child operands that may need to do cell lookups.
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public override Single Run(FormulaExecutionContext context)
        {
            Single      returnValue     = default(Single);
            OperandBase operationResult = default(OperandBase);

            try
            {
                //The operand (of type OperandOperation) that contains this operation will have its Evaluate() called; Evaluate() will call this operation's Run().
                operationResult = Operator.Run(this.Operands, context);

                returnValue = operationResult.Evaluate(context);
            }
            catch (Exception ex)
            {
                Log.Write(ex, MethodBase.GetCurrentMethod(), EventLogEntryType.Error);

                throw;
            }
            return(returnValue);
        }
        /// <summary>
        /// Parse token to identify whether it is a pre-parsing variable, a literal, or a cell-reference.
        /// </summary>
        /// <param name="token"></param>
        /// <param name="operand"></param>
        /// <param name="errorMessage"></param>
        /// <returns></returns>
        private Boolean ParseOperand(String token, ref OperandBase operand, ref String errorMessage)
        {
            Boolean             returnValue   = default(Boolean);
            Single              numericResult = default(Single);
            Sheet               parentSheet   = default(Sheet);
            List <CategoryItem> criteria      = default(List <CategoryItem>);
            String              variableValue = default(String);

            try
            {
                //trap string leterals
                if ((token.Contains("'")) || (token.Contains("\"")))
                {
                    throw new ApplicationException(String.Format("String literals not supported: '{0}'", token));
                }
                //trap parentheses
                if ((token.Contains("(")) || (token.Contains(")")))
                {
                    throw new ApplicationException(String.Format("Parentheses found after pre-processing: '{0}'", token));
                }

                token = token.Trim();

                if (token.StartsWith(Formula.ParsingVariableIndicator))
                {
                    //token is a parsing variable; look up value and process token as an expression

                    //look up parsing variable
                    if (!ParsingVariables.TryGetValue(token, out variableValue))
                    {
                        throw new ApplicationException(String.Format("Unable to look up variable value for variable name: '{0}'", token));
                    }

                    //parse expression and return operand
                    if (!ParseToken(variableValue, ref operand, ref errorMessage))
                    {
                        throw new ApplicationException(errorMessage);
                    }

                    returnValue = true;
                }
                else if (token.StartsWith(Formula.FunctionIndicator))
                {
                    //token is a function

                    //parse function and return operand
                    if (!ParseFunction(token, ref operand, ref errorMessage))
                    {
                        throw new ApplicationException(errorMessage);
                    }

                    returnValue = true;
                }
                else if (Single.TryParse(token, out numericResult))
                {
                    //token is a numeric literal
                    //return literal operand with numeric value
                    operand = new OperandLiteral(numericResult);

                    returnValue = true;
                }
                else
                {
                    //token should be reference-operand
                    parentSheet = SettingsController <Settings> .Settings.Sheets.Find(s => s.Equals(this.Parent));

                    //parentSheet = SettingsController<Settings>.Settings.Sheets.Find(s => s.Name == this.Parent);
                    if (parentSheet == null)
                    {
                        throw new ApplicationException(String.Format("Unable to find sheet '{0}' for formula '{1}'.", this.Parent, this.Value));
                    }

                    criteria = parentSheet.GetCategoryItemsForAddressName(token, true);
                    if ((criteria == null) || (criteria.Count < 1))
                    {
                        throw new ApplicationException(String.Format("Unable to find category-item criteria in sheet '{0}' for formula '{1}'.", this.Parent, this.Value));
                    }

                    //store criteria
                    operand = new OperandReference(criteria);

                    returnValue = true;
                }
            }
            catch (Exception ex)
            {
                errorMessage = ex.Message;
                Log.Write(ex, MethodBase.GetCurrentMethod(), EventLogEntryType.Error);
                //throw;
            }
            return(returnValue);
        }
        /// <summary>
        /// Parse the string token to get an operand of type Literal, Reference, or Operation.
        /// 1) Literal operands return  the value that they contain.
        /// 2) Reference operands use their own category item criteria
        ///  to narrow down the list of cells to which the formula applies. Then the specific cell's criteria
        ///  is used to identify categories not in the criteria, and use those to narrow down to a single source cell.
        /// 3) Operation operands perform their operation and trigger the evaulation of their child operands.
        /// </summary>
        /// <param name="token"></param>
        /// <param name="operand"></param>
        /// <param name="errorMessage"></param>
        /// <returns></returns>
        private Boolean ParseToken(String token, ref OperandBase operand, ref String errorMessage)
        {
            Boolean returnValue = default(Boolean);

            String[]      tokens          = default(String[]);
            String        leftToken       = default(String);
            String        rightToken      = default(String);
            Boolean       isFoundOperator = default(Boolean);
            OperatorBase  foundOperator   = default(OperatorBase);
            OperandBase   leftOperand     = default(OperandBase);
            OperandBase   rightOperand    = default(OperandBase);
            OperationBase operation       = default(OperationBase);

            try
            {
                //look for operators, if any, in reverse precedence order
                foreach (OperatorBase op in arithmeticOperators)
                {
                    if (token.Contains(op.Name))
                    {
                        isFoundOperator = true;
                        foundOperator   = op;

                        break;
                    }
                }

                //create an array of one or two sub-tokens
                if (isFoundOperator)
                {
                    //split into 2 parts only
                    tokens = token.Split(new Char[] { foundOperator.Name[0] }, 2);
                }
                else
                {
                    //create list with single item
                    String[] newList = { token };
                    tokens = newList;
                }

                //process sub-tokens
                if (tokens.Length == 0)
                {
                    throw new ArgumentException(String.Format("Incorrectly formatted formula; there does not appear to be an operand: '{0}'", token));
                }
                else if (tokens.Length == 1)
                {
                    //no operator; process single operand
                    leftToken = tokens[0].Trim();
                    if (!ParseOperand(leftToken, ref leftOperand, ref errorMessage))
                    {
                        throw new ApplicationException(errorMessage);
                    }
                    operand = leftOperand;

                    returnValue = true;
                }
                else if (tokens.Length == 2)
                {
                    //operator found; process dual operands with recursive call
                    leftToken = tokens[0].Trim();
                    if (!ParseToken(leftToken, ref leftOperand, ref errorMessage))
                    {
                        throw new ApplicationException(errorMessage);
                    }

                    rightToken = tokens[1].Trim();
                    if (!ParseToken(rightToken, ref rightOperand, ref errorMessage))
                    {
                        throw new ApplicationException(errorMessage);
                    }

                    //create operation from left/right operands and operator, and create OperandOperation
                    //create OperationBinary which configures 2 OperandBase dictionary entries called Left, Right.
                    operation = new OperationBinary();
                    operation.Operands["Left"]  = leftOperand;
                    operation.Operands["Right"] = rightOperand;
                    operation.Operator          = foundOperator;

                    operand = new OperandOperation(operation);

                    returnValue = true;
                }
                else if (tokens.Length > 2)
                {
                    throw new ArgumentException(String.Format("Unexpected formula format: '{0}'", token));
                }
            }
            catch (Exception ex)
            {
                errorMessage = ex.Message;
                Log.Write(ex, MethodBase.GetCurrentMethod(), EventLogEntryType.Error);
                //throw;
            }
            return(returnValue);
        }
        /// <summary>
        /// Parse the string token to get an operand of type Operation,
        /// whose operation is of type Function(?),
        /// and where the operator is the defined in the token.
        /// </summary>
        /// <param name="token"></param>
        /// <param name="operand"></param>
        /// <param name="errorMessage"></param>
        /// <returns></returns>
        private Boolean ParseFunction(String token, ref OperandBase operand, ref String errorMessage)
        {
            Boolean      returnValue        = default(Boolean);
            Boolean      isFoundOperator    = default(Boolean);
            OperatorBase foundOperator      = default(OperatorBase);
            Int32        countOfLeftBraces  = default(Int32);
            Int32        countOfRightBraces = default(Int32);
            Regex        regEx           = default(Regex);
            Match        match           = default(Match);
            String       parametersToken = default(String);

            String[] parameterTokens   = default(String[]);
            String   functionNameToken = default(String);
            Dictionary <String, OperandBase> parameterDictionary = default(Dictionary <String, OperandBase>);
            OperandBase parameterOperand = default(OperandBase);
            Int32       parameterIndex   = default(Int32);

            try
            {
                countOfLeftBraces = (from Char ch in token
                                     where ch == Formula.LeftBrace
                                     select ch).Count();
                countOfRightBraces = (from Char ch in token
                                      where ch == Formula.RightBrace
                                      select ch).Count();
                if ((countOfLeftBraces > 0) || (countOfRightBraces > 0))
                {
                    //braces found

                    if (countOfLeftBraces != countOfRightBraces)
                    {
                        //mismatched pair
                        throw new ApplicationException(String.Format("Mis-matched braces in token: '{0}'", token));
                    }

                    //get parameters

                    regEx = new Regex(Formula.RegExFindInnermostBracesAndContents);
                    match = regEx.Match(token);
                    if ((match.Value == null) || (match.Value == String.Empty))
                    {
                        //pair did not match pattern
                        throw new ApplicationException(String.Format("Braces in token not formatted correctly: '{0}'", token));
                    }

                    parametersToken = match.Value.Replace(Formula.LeftBrace.ToString(), String.Empty).Replace(Formula.RightBrace.ToString(), String.Empty);
                    if ((parametersToken == null) || (parametersToken == String.Empty))
                    {
                        //pair did not contain an expression
                        throw new ApplicationException(String.Format("Value in parenthesis in token not formatted correctly: '{0}'", token));
                    }

                    parameterTokens = parametersToken.Split(Formula.FunctionParameterDelimiter);
                    if (parameterTokens.Length == 0)
                    {
                        //pair did not contain any values
                        throw new ApplicationException(String.Format("Value in parenthesis in token not formatted correctly: '{0}'", token));
                    }

                    //get function name

                    regEx = new Regex(Formula.RegExFindFunctionNameDelimitersAndContents);
                    match = regEx.Match(token);
                    if ((match.Value == null) || (match.Value == String.Empty))
                    {
                        //pair did not match pattern
                        throw new ApplicationException(String.Format("Function name delimiters in token not formatted correctly: '{0}'", token));
                    }

                    functionNameToken = match.Value.Replace(Formula.FunctionIndicator.ToString(), String.Empty).Replace(Formula.LeftBrace.ToString(), String.Empty);
                    if ((functionNameToken == null) || (functionNameToken == String.Empty))
                    {
                        //pair did not contain an expression
                        throw new ApplicationException(String.Format("Function name in token not formatted correctly: '{0}'", token));
                    }

                    //look for operators, if any
                    foreach (OperatorBase op in functionOperators)
                    {
                        if (functionNameToken.ToUpper().Contains(op.Name))
                        {
                            isFoundOperator = true;
                            foundOperator   = op;

                            break;
                        }
                    }

                    //get function

                    if (isFoundOperator)
                    {
                        //build operation operand with function operation consisting of operator and each parameter token parsed into an operand.
                        parameterDictionary = new Dictionary <String, OperandBase>();
                        foreach (String parameterToken in parameterTokens)
                        {
                            if (!ParseToken(parameterToken, ref parameterOperand, ref errorMessage))
                            {
                                //error parsing operand token
                                throw new ApplicationException(String.Format("Unable to parse parameter '{0}' in function token '{1}'.", parameterToken, token));
                            }
                            parameterDictionary.Add(String.Format("Value{0}", parameterIndex++), parameterOperand);
                        }

                        //set operand to new OperandOperation of type OperationFunction using operator of type OperatorXXX (foundOperator)
                        operand = new OperandOperation(new OperationFunction(foundOperator, parameterDictionary));
                    }
                    else
                    {
                        throw new ApplicationException(String.Format("Unable to find function: '{0}'", token));
                    }
                }
                else
                {
                    throw new ApplicationException(String.Format("Unable to find braces for parameters: '{0}'", token));
                }

                returnValue = true;
            }
            catch (Exception ex)
            {
                Log.Write(ex, MethodBase.GetCurrentMethod(), EventLogEntryType.Error);
                //throw;
            }
            return(returnValue);
        }