/**
         * Generates the variable function ptg for the formula.
         * <p>
         * For IF Formulas, Additional PTGs are Added To the Tokens
         * @param name
         * @param numArgs
         * @return Ptg a null Is returned if we're in an IF formula, it needs extreme manipulation and Is handled in this function
         */
        private ParseNode GetFunction(String name, NamePtg namePtg, ParseNode[] args)
        {

            FunctionMetadata fm = FunctionMetadataRegistry.GetFunctionByName(name.ToUpper());
            int numArgs = args.Length;
            if (fm == null)
            {
                if (namePtg == null)
                {
                    throw new InvalidOperationException("NamePtg must be supplied for external functions");
                }
                // must be external function
                ParseNode[] allArgs = new ParseNode[numArgs + 1];
                allArgs[0] = new ParseNode(namePtg);
                Array.Copy(args, 0, allArgs, 1, numArgs);
                return new ParseNode(new FuncVarPtg(name, (byte)(numArgs + 1)), allArgs);
            }

            if (namePtg != null)
            {
                throw new InvalidOperationException("NamePtg no applicable To internal functions");
            }
            bool IsVarArgs = !fm.HasFixedArgsLength;
            int funcIx = fm.Index;
            ValidateNumArgs(args.Length, fm);

            AbstractFunctionPtg retval;
            if (IsVarArgs)
            {
                retval = new FuncVarPtg(name, (byte)numArgs);
            }
            else
            {
                retval = new FuncPtg(funcIx);
            }
            return new ParseNode(retval, args);
        }
        // visibility raised for testing
        /* package */
        public ValueEval EvaluateFormula(OperationEvaluationContext ec, Ptg[] ptgs)
        {

            Stack<ValueEval> stack = new Stack<ValueEval>();
            for (int i = 0, iSize = ptgs.Length; i < iSize; i++)
            {

                // since we don't know how To handle these yet :(
                Ptg ptg = ptgs[i];
                if (ptg is AttrPtg)
                {
                    AttrPtg attrPtg = (AttrPtg)ptg;
                    if (attrPtg.IsSum)
                    {
                        // Excel prefers To encode 'SUM()' as a tAttr Token, but this evaluator
                        // expects the equivalent function Token
                        byte nArgs = 1;  // tAttrSum always Has 1 parameter
                        ptg = new FuncVarPtg("SUM", nArgs);
                    }
                    				if (attrPtg.IsOptimizedChoose) {
					ValueEval arg0 = stack.Pop();
					int[] jumpTable = attrPtg.JumpTable;
					int dist;
					int nChoices = jumpTable.Length;
					try {
						int switchIndex = Choose.EvaluateFirstArg(arg0, ec.RowIndex, ec.ColumnIndex);
						if (switchIndex<1 || switchIndex > nChoices) {
							stack.Push(ErrorEval.VALUE_INVALID);
							dist = attrPtg.ChooseFuncOffset + 4; // +4 for tFuncFar(CHOOSE)
						} else {
							dist = jumpTable[switchIndex-1];
						}
					} catch (EvaluationException e) {
						stack.Push(e.GetErrorEval());
						dist = attrPtg.ChooseFuncOffset + 4; // +4 for tFuncFar(CHOOSE)
					}
					// Encoded dist for tAttrChoose includes size of jump table, but
					// countTokensToBeSkipped() does not (it counts whole tokens).
					dist -= nChoices*2+2; // subtract jump table size
					i+= CountTokensToBeSkipped(ptgs, i, dist);
					continue;
				}
				if (attrPtg.IsOptimizedIf) {
					ValueEval arg0 = stack.Pop();
					bool evaluatedPredicate;
					try {
						evaluatedPredicate = If.EvaluateFirstArg(arg0, ec.RowIndex, ec.ColumnIndex);
					} catch (EvaluationException e) {
						stack.Push(e.GetErrorEval());
						int dist = attrPtg.Data;
						i+= CountTokensToBeSkipped(ptgs, i, dist);
						attrPtg = (AttrPtg) ptgs[i];
						dist = attrPtg.Data+1;
						i+= CountTokensToBeSkipped(ptgs, i, dist);
						continue;
					}
					if (evaluatedPredicate) {
						// nothing to skip - true param folows
					} else {
						int dist = attrPtg.Data;
						i+= CountTokensToBeSkipped(ptgs, i, dist);
						Ptg nextPtg = ptgs[i+1];
						if (ptgs[i] is AttrPtg && nextPtg is FuncVarPtg) {
							// this is an if statement without a false param (as opposed to MissingArgPtg as the false param)
							i++;
							stack.Push(BoolEval.FALSE);
						}
					}
					continue;
				}
				if (attrPtg.IsSkip) {
					int dist = attrPtg.Data+1;
					i+= CountTokensToBeSkipped(ptgs, i, dist);
					if (stack.Peek() == MissingArgEval.instance) {
						stack.Pop();
						stack.Push(BlankEval.instance);
					}
					continue;
				}
                }
                if (ptg is ControlPtg)
                {
                    // skip Parentheses, Attr, etc
                    continue;
                }
                if (ptg is MemFuncPtg)
                {
                    // can ignore, rest of Tokens for this expression are in OK RPN order
                    continue;
                }
                if (ptg is MemErrPtg) { continue; }

                ValueEval opResult;
                if (ptg is OperationPtg)
                {
                    OperationPtg optg = (OperationPtg)ptg;

                    if (optg is UnionPtg) { continue; }

                    int numops = optg.NumberOfOperands;
                    ValueEval[] ops = new ValueEval[numops];

                    // storing the ops in reverse order since they are popping
                    for (int j = numops - 1; j >= 0; j--)
                    {
                        ValueEval p = (ValueEval)stack.Pop();
                        ops[j] = p;
                    }
                    //				logDebug("Invoke " + operation + " (nAgs=" + numops + ")");
                    opResult = OperationEvaluatorFactory.Evaluate(optg, ops, ec);
                }
                else
                {
                    opResult = GetEvalForPtg(ptg, ec);
                }
                if (opResult == null)
                {
                    throw new Exception("Evaluation result must not be null");
                }
                //			logDebug("push " + opResult);
                stack.Push(opResult);
            }

            ValueEval value = ((ValueEval)stack.Pop());
            if (stack.Count != 0)
            {
                throw new InvalidOperationException("evaluation stack not empty");
            }
            value = DereferenceValue(value, ec.RowIndex, ec.ColumnIndex);
            if (value == BlankEval.instance)
            {
                // Note Excel behaviour here. A blank value is converted To zero.
                return NumberEval.ZERO;
                // Formulas _never_ evaluate To blank.  If a formula appears To have evaluated To
                // blank, the actual value is empty string. This can be verified with ISBLANK().
            }
            return value;
        }
        // visibility raised for testing
        /* package */
        public ValueEval EvaluateFormula(int sheetIndex, int srcRowNum, int srcColNum, Ptg[] ptgs, EvaluationTracker tracker)
        {

            Stack stack = new Stack();
            for (int i = 0, iSize = ptgs.Length; i < iSize; i++)
            {

                // since we don't know how To handle these yet :(
                Ptg ptg = ptgs[i];
                if (ptg is AttrPtg)
                {
                    AttrPtg attrPtg = (AttrPtg)ptg;
                    if (attrPtg.IsSum)
                    {
                        // Excel prefers To encode 'SUM()' as a tAttr Token, but this evaluator
                        // expects the equivalent function Token
                        byte nArgs = 1;  // tAttrSum always Has 1 parameter
                        ptg = new FuncVarPtg("SUM", nArgs);
                    }
                }
                if (ptg is ControlPtg)
                {
                    // skip Parentheses, Attr, etc
                    continue;
                }
                if (ptg is MemFuncPtg)
                {
                    // can ignore, rest of Tokens for this expression are in OK RPN order
                    continue;
                }
                if (ptg is MemErrPtg) { continue; }

                Eval opResult;
                if (ptg is OperationPtg)
                {
                    OperationPtg optg = (OperationPtg)ptg;

                    if (optg is UnionPtg) { continue; }

                    OperationEval operation = OperationEvaluatorFactory.Create(optg);

                    int numops = operation.NumberOfOperands;
                    Eval[] ops = new Eval[numops];

                    // storing the ops in reverse order since they are popping
                    for (int j = numops - 1; j >= 0; j--)
                    {
                        Eval p = (Eval)stack.Pop();
                        ops[j] = p;
                    }
                    //				logDebug("Invoke " + operation + " (nAgs=" + numops + ")");
                    opResult = InvokeOperation(operation, ops, _workbook, sheetIndex, srcRowNum, srcColNum);
                    if (opResult == MissingArgEval.instance)
                    {
                        opResult = BlankEval.INSTANCE;
                    }
                }
                else
                {
                    opResult = GetEvalForPtg(ptg, sheetIndex, tracker);
                }
                if (opResult == null)
                {
                    throw new Exception("Evaluation result must not be null");
                }
                //			logDebug("push " + opResult);
                stack.Push(opResult);
            }

            ValueEval value = ((ValueEval)stack.Pop());
            if (stack.Count != 0)
            {
                throw new InvalidOperationException("evaluation stack not empty");
            }
            value = DereferenceValue(value, srcRowNum, srcColNum);
            if (value == BlankEval.INSTANCE)
            {
                // Note Excel behaviour here. A blank value Is converted To zero.
                return NumberEval.ZERO;
                // Formulas _never_ evaluate To blank.  If a formula appears To have evaluated To
                // blank, the actual value Is empty string. This can be verified with ISBLANK().
            }
            return value;
        }