/// <summary>
        /// Takes the calcDef and extracts the first expression using the operator of the creator. This is called repeated until there are no more expressions of the operator.
        /// </summary>
        /// <param name="calcToolDef"></param>
        /// <param name="calcDef_upperCase"></param>
        /// <returns></returns>
        public MathOperationDefinition CreateMathOperation(CalculationToolDefinition calcToolDef, ref string calcDef, ref string calcDef_upperCase)
        {
            List <DataValueDefinition> valueObjects = new List <DataValueDefinition>(); // NOTE: this List will be owned by the MathOperationDefinition that we create
            List <string> operatorsUsed             = new List <string>();              // NOTE: this List will be owned by the MathOperationDefinition that we create

            int index;
            int indexOfFirstOperator    = -1;
            int indexAfterFirstOperator = -1;

            // find first matching operator
            for (index = 0; index < calcDef_upperCase.Length && indexOfFirstOperator < 0; index++)
            {
                for (int opNdx = 0; opNdx < numOperators; opNdx++)
                {
                    if (calcDef_upperCase[index] == supportedOperators[opNdx][0] ||
                        (supportedOperators[opNdx][0] == ' ' && Char.IsWhiteSpace(calcDef_upperCase[index])))
                    {
                        bool didNotFindConflict = true;
                        for (int y = 1; y < supportedOperators[opNdx].Length; y++)
                        {
                            if (index + y >= calcDef_upperCase.Length || // if we run out of calcDef, we have a conflict (non-match)
                                calcDef_upperCase[index + y] != supportedOperators[opNdx][y])
                            {
                                if (index + y < calcDef_upperCase.Length &&
                                    supportedOperators[opNdx][y] == ' ' && Char.IsWhiteSpace(calcDef_upperCase[index + y]))
                                {
                                    // special case where they don't have to match exactly
                                }
                                else
                                {
                                    didNotFindConflict = false;
                                    break;
                                }
                            }
                        }
                        if (didNotFindConflict)
                        {
                            indexOfFirstOperator    = index;
                            indexAfterFirstOperator = index + supportedOperators[opNdx].Length;
                            operatorsUsed.Add(supportedOperators[opNdx]);
                            break;
                        }
                    }
                }
            }

            if (indexOfFirstOperator < 0)
            {
                return(null); //operator not found...we must have extracted all instances in previous calls to this method
            }

            // grab value before operator
            index = indexOfFirstOperator - 1;
            while (index >= 0 && Char.IsWhiteSpace(calcDef_upperCase[index]))
            {
                index--;
            }
            int  indexOfValueBeforeFirstOperator = -1;
            int  indexAfterValueBeforeFirstOperatorIdentifier = index + 1;
            bool foundNonDigits = false;
            bool foundSomething = false;

            while (index >= 0)
            {
                if (Char.IsLetter(calcDef_upperCase[index]))
                {
                    foundSomething = true;
                    foundNonDigits = true;
                    index--;
                }
                else if (Char.IsDigit(calcDef_upperCase[index]))
                {
                    foundSomething = true;
                    index--;
                }
                else if (calcDef_upperCase[index] == '_')
                {
                    foundSomething = true;
                    foundNonDigits = true;
                    index--;
                }
                else if (calcDef_upperCase[index] == '#' && foundSomething && !foundNonDigits && (
                             (index == 0) || (index > 0 && Char.IsWhiteSpace(calcDef_upperCase[index - 1]))
                             )
                         )
                {
                    index--;
                }
                else
                {
                    break;
                }
            }
            if (!foundSomething)
            {
                throw new ArgumentException("Error parsing calculation. code=43987598");
            }
            indexOfValueBeforeFirstOperator = index + 1;
            string theValueIdentifier = calcDef.Substring(indexOfValueBeforeFirstOperator, indexAfterValueBeforeFirstOperatorIdentifier - indexOfValueBeforeFirstOperator);

            if (foundNonDigits)
            {
                if (!Char.IsLetter(theValueIdentifier[0]))
                {
                    throw new ArgumentException("Error parsing calculation: value identifier doesn't start with a letter: '" + theValueIdentifier + "'");
                }
            }

            if (!foundNonDigits && theValueIdentifier[0] == '#')
            {
                valueObjects.Add(calcToolDef.GetValueForAlias(theValueIdentifier));
            }
            else
            {
                valueObjects.Add(calcToolDef.TestSequence().DataValueRegistry.GetObject(theValueIdentifier));
            }

            // grab value after operator
            index = indexAfterFirstOperator;
            valueObjects.Add(GrabNextValue(calcToolDef, ref calcDef, ref index));

            // while next token is a matching operator, grab next value and check next token
            string theOperator;

            do
            {
                theOperator = GrabOperator(ref calcDef_upperCase, ref index);
                if (theOperator.Length > 0)
                {
                    operatorsUsed.Add(theOperator);
                    valueObjects.Add(GrabNextValue(calcToolDef, ref calcDef, ref index));
                }
            } while (theOperator != String.Empty);

            // remove substring for parsing and replace it with alias
            int    lengthOfSubString = index < calcDef_upperCase.Length ? index - indexOfValueBeforeFirstOperator : calcDef_upperCase.Length - indexOfValueBeforeFirstOperator;
            string subExpression     = calcDef.Substring(indexOfValueBeforeFirstOperator, lengthOfSubString).Trim();

            string calcDefBeforeAlias       = "";
            string calcDefBeforeAlias_upper = "";

            if (indexOfValueBeforeFirstOperator > 0)
            {
                calcDefBeforeAlias       = calcDef.Substring(0, indexOfValueBeforeFirstOperator).Trim();
                calcDefBeforeAlias_upper = calcDef_upperCase.Substring(0, indexOfValueBeforeFirstOperator).Trim();
                if (calcDefBeforeAlias.Length > 0)
                {
                    calcDefBeforeAlias       += ' ';
                    calcDefBeforeAlias_upper += ' ';
                }
            }
            string calcDefAfterAlias       = "";
            string calcDefAfterAlias_upper = "";

            if (index < calcDef_upperCase.Length)
            {
                calcDefAfterAlias       = calcDef.Substring(index).Trim();
                calcDefAfterAlias_upper = calcDef_upperCase.Substring(index).Trim();
                if (calcDefAfterAlias.Length > 0)
                {
                    calcDefAfterAlias       = ' ' + calcDefAfterAlias;
                    calcDefAfterAlias_upper = ' ' + calcDefAfterAlias_upper;
                }
            }
            if (calcDefBeforeAlias.Length > 0 || calcDefAfterAlias.Length > 0)
            {
                string           alias     = "#" + (calcToolDef.aliasCount++);
                CalculationAlias aliasGuts = new CalculationAlias();
                aliasGuts.expressionToLookupValue = subExpression;
                aliasGuts.expressionForExpansion  = "(" + subExpression + ")";
                calcToolDef.aliasMap.Add(alias, aliasGuts);
                calcDef           = calcDefBeforeAlias + alias + calcDefAfterAlias;
                calcDef_upperCase = calcDefBeforeAlias_upper + alias + calcDefAfterAlias_upper;
                if (calcDef.Length != calcDef_upperCase.Length)
                {
                    throw new ArgumentException("Error parsing calculation. code=293084");
                }
            }
            else
            {
                // nothing left to parse
                calcDef           = "";
                calcDef_upperCase = "";
            }

            // return calculation object
            string expandedCalcDef = calcToolDef.ExpandCalcDef(subExpression);

            return(CreateMathOperation(calcToolDef.TestSequence(), expandedCalcDef, valueObjects, operatorsUsed));
        }
        protected DataValueDefinition GrabNextValue(CalculationToolDefinition calcToolDef, ref string calcDef, ref int index)
        {
            while (index < calcDef.Length && Char.IsWhiteSpace(calcDef[index]))
            {
                index++;
            }
            int  indexOfValue    = index;
            int  indexAfterValue = -1;
            bool foundNonDigits  = false;
            bool foundSomething  = false;
            bool foundDecimal    = false;

            while (index < calcDef.Length)
            {
                if (Char.IsLetter(calcDef[index]))
                {
                    foundSomething = true;
                    foundNonDigits = true;
                    index++;
                }
                else if (Char.IsDigit(calcDef[index]))
                {
                    foundSomething = true;
                    index++;
                }
                else if (!foundNonDigits && !foundDecimal && calcDef[index] == '.')
                {
                    foundSomething = true;
                    foundDecimal   = true;
                    index++;
                }
                else if (calcDef[index] == '_')
                {
                    foundSomething = true;
                    foundNonDigits = true;
                    index++;
                }
                else if (calcDef[index] == '#' && !foundSomething && index + 1 < calcDef.Length && Char.IsDigit(calcDef[index + 1]))
                {
                    // start of an alias
                    foundSomething = true;
                    index++;
                }
                else
                {
                    break;
                }
            }
            if (!foundSomething)
            {
                throw new ArgumentException("Error parsing calculation. code=433447598");
            }
            indexAfterValue = index;
            string theValueIdentifier = calcDef.Substring(indexOfValue, indexAfterValue - indexOfValue);

            if (foundNonDigits)
            {
                if (!Char.IsLetter(calcDef[indexOfValue]))
                {
                    throw new ArgumentException("Error parsing calculation: value identifier doesn't start with a letter: '" + theValueIdentifier + "'");
                }
            }
            if (!foundNonDigits && theValueIdentifier[0] == '#')
            {
                return(calcToolDef.GetValueForAlias(theValueIdentifier));
            }
            else
            {
                return(calcToolDef.TestSequence().DataValueRegistry.GetObject(theValueIdentifier));
            }
        }