Calculate the value of a cell formula

@access	public
@param	PHPExcel_Cell	$pCell		Cell to calculate
@param	Boolean			$resetLog	Flag indicating whether the debug log should be reset or not
@return	mixed
@throws	PHPExcel_Calculation_Exception
171204
public function calculateCellValue(PHPExcel_Cell $pCell = NULL, $resetLog = TRUE) {
		if ($pCell === NULL) {
			return NULL;
		}

		$returnArrayAsType = self::$returnArrayAsType;
		if ($resetLog) {
			//	Initialise the logging settings if requested
			$this->formulaError = null;
			$this->_debugLog->clearLog();
			$this->_cyclicReferenceStack->clear();
			$this->_cyclicFormulaCount = 1;

			self::$returnArrayAsType = self::RETURN_ARRAY_AS_ARRAY;
		}

		//	Execute the calculation for the cell formula
		try {
			$result = self::_unwrapResult($this->_calculateFormulaValue($pCell->getValue(), $pCell->getCoordinate(), $pCell));
		} catch (PHPExcel_Exception $e) {
			throw new PHPExcel_Calculation_Exception($e->getMessage());
		}

		if ((is_array($result)) && (self::$returnArrayAsType != self::RETURN_ARRAY_AS_ARRAY)) {
			self::$returnArrayAsType = $returnArrayAsType;
			$testResult = PHPExcel_Calculation_Functions::flattenArray($result);
			if (self::$returnArrayAsType == self::RETURN_ARRAY_AS_ERROR) {
				return PHPExcel_Calculation_Functions::VALUE();
			}
			//	If there's only a single cell in the array, then we allow it
			if (count($testResult) != 1) {
				//	If keys are numeric, then it's a matrix result rather than a cell range result, so we permit it
				$r = array_keys($result);
				$r = array_shift($r);
				if (!is_numeric($r)) { return PHPExcel_Calculation_Functions::VALUE(); }
				if (is_array($result[$r])) {
					$c = array_keys($result[$r]);
					$c = array_shift($c);
					if (!is_numeric($c)) {
						return PHPExcel_Calculation_Functions::VALUE();
					}
				}
			}
			$result = array_shift($testResult);
		}
		self::$returnArrayAsType = $returnArrayAsType;


		if ($result === NULL) {
			return 0;
		} elseif((is_float($result)) && ((is_nan($result)) || (is_infinite($result)))) {
			return PHPExcel_Calculation_Functions::NaN();
		}
		return $result;
	}
1226726
public function isDataSet($pCoord)
    {
        if ($pCoord === $this->currentObjectID) {
            return true;
        }

        //    Check if the requested entry exists in the cache
        $this->selectQuery->bindValue('id', $pCoord, SQLITE3_TEXT);
        $cellResult = $this->selectQuery->execute();
        if ($cellResult === false) {
            throw new PHPExcel_Exception($this->DBHandle->lastErrorMsg());
        }
        $cellData = $cellResult->fetchArray(SQLITE3_ASSOC);

        return ($cellData === false) ? false : true;
    }
309202
public function setDataValidation($pCellCoordinate = 'A1', PHPExcel_Cell_DataValidation $pDataValidation = null)
    {
        if ($pDataValidation === null) {
            unset($this->_dataValidationCollection[$pCellCoordinate]);
        } else {
            $this->_dataValidationCollection[$pCellCoordinate] = $pDataValidation;
        }
        return $this;
    }
213645
function _cellToPackedRowcol($cell)
	{
		$cell = strtoupper($cell);
		list($row, $col, $row_rel, $col_rel) = $this->_cellToRowcol($cell);
		if ($col >= 256) {
			throw new PHPExcel_Writer_Exception("Column in: $cell greater than 255");
		}
		if ($row >= 65536) {
			throw new PHPExcel_Writer_Exception("Row in: $cell greater than 65536 ");
		}

		// Set the high bits to indicate if row or col are relative.
		$col |= $col_rel << 14;
		$col |= $row_rel << 15;
		$col = pack('v', $col);

		$row = pack('v', $row);

		return array($row, $col);
	}
239832
protected function calcRelativeValues()
    {
        foreach ($this->stackLog as $key => $value) {
            foreach ($this->metrics as $metric) {
                foreach (array('own', 'sub', 'total') as $column) {
                    if (!isset($this->stackLog[$key][$metric . '_' . $column])) {
                        continue;
                    }
                    $this->stackLog[$key][$metric . '_rel_' . $column] = $this->stackLog[$key][$metric . '_' . $column] / $this->stackLog['timetracker_0'][$metric . '_total'];
                }
                $this->stackLog[$key][$metric . '_rel_offset'] = $this->stackLog[$key][$metric . '_start_relative'] / $this->stackLog['timetracker_0'][$metric . '_total'];
            }
        }
    }
339348
public function setValueExplicit($pValue = NULL, $pDataType = PHPExcel_Cell_DataType::TYPE_STRING)
	{
		// set the value according to data type
		switch ($pDataType) {
			case PHPExcel_Cell_DataType::TYPE_STRING2:
				$pDataType = PHPExcel_Cell_DataType::TYPE_STRING;
			case PHPExcel_Cell_DataType::TYPE_STRING:
			case PHPExcel_Cell_DataType::TYPE_NULL:
			case PHPExcel_Cell_DataType::TYPE_INLINE:
				$this->_value = PHPExcel_Cell_DataType::checkString($pValue);
				break;
			case PHPExcel_Cell_DataType::TYPE_NUMERIC:
				$this->_value = (float)$pValue;
				break;
			case PHPExcel_Cell_DataType::TYPE_FORMULA:
				$this->_value = (string)$pValue;
				break;
			case PHPExcel_Cell_DataType::TYPE_BOOL:
				$this->_value = (bool)$pValue;
				break;
			case PHPExcel_Cell_DataType::TYPE_ERROR:
				$this->_value = PHPExcel_Cell_DataType::checkErrorCode($pValue);
				break;
			default:
				throw new PHPExcel_Exception('Invalid datatype: ' . $pDataType);
				break;
		}

		// set the datatype
		$this->_dataType = $pDataType;

		return $this->notifyCacheController();
	}
778293
public function bindValue(PHPExcel_Cell $cell, $value = null)
    {
        // sanitize UTF-8 strings
        if (is_string($value)) {
            $value = PHPExcel_Shared_String::SanitizeUTF8($value);
        }

        // Find out data type
        $dataType = parent::dataTypeForValue($value);

        // Style logic - strings
        if ($dataType === PHPExcel_Cell_DataType::TYPE_STRING && !$value instanceof PHPExcel_RichText) {
            //    Test for booleans using locale-setting
            if ($value == PHPExcel_Calculation::getTRUE()) {
                $cell->setValueExplicit( TRUE, PHPExcel_Cell_DataType::TYPE_BOOL);
                return true;
            } elseif($value == PHPExcel_Calculation::getFALSE()) {
                $cell->setValueExplicit( FALSE, PHPExcel_Cell_DataType::TYPE_BOOL);
                return true;
            }

            // Check for number in scientific format
            if (preg_match('/^'.PHPExcel_Calculation::CALCULATION_REGEXP_NUMBER.'$/', $value)) {
                $cell->setValueExplicit( (float) $value, PHPExcel_Cell_DataType::TYPE_NUMERIC);
                return true;
            }

            // Check for fraction
            if (preg_match('/^([+-]?)\s*([0-9]+)\s?\/\s*([0-9]+)$/', $value, $matches)) {
                // Convert value to number
                $value = $matches[2] / $matches[3];
                if ($matches[1] == '-') $value = 0 - $value;
                $cell->setValueExplicit( (float) $value, PHPExcel_Cell_DataType::TYPE_NUMERIC);
                // Set style
                $cell->getWorksheet()->getStyle( $cell->getCoordinate() )
                    ->getNumberFormat()->setFormatCode( '??/??' );
                return true;
            } elseif (preg_match('/^([+-]?)([0-9]*) +([0-9]*)\s?\/\s*([0-9]*)$/', $value, $matches)) {
                // Convert value to number
                $value = $matches[2] + ($matches[3] / $matches[4]);
                if ($matches[1] == '-') $value = 0 - $value;
                $cell->setValueExplicit( (float) $value, PHPExcel_Cell_DataType::TYPE_NUMERIC);
                // Set style
                $cell->getWorksheet()->getStyle( $cell->getCoordinate() )
                    ->getNumberFormat()->setFormatCode( '# ??/??' );
                return true;
            }

            // Check for percentage
            if (preg_match('/^\-?[0-9]*\.?[0-9]*\s?\%$/', $value)) {
                // Convert value to number
                $value = (float) str_replace('%', '', $value) / 100;
                $cell->setValueExplicit( $value, PHPExcel_Cell_DataType::TYPE_NUMERIC);
                // Set style
                $cell->getWorksheet()->getStyle( $cell->getCoordinate() )
                    ->getNumberFormat()->setFormatCode( PHPExcel_Style_NumberFormat::FORMAT_PERCENTAGE_00 );
                return true;
            }

            // Check for currency
            $currencyCode = PHPExcel_Shared_String::getCurrencyCode();
            $decimalSeparator = PHPExcel_Shared_String::getDecimalSeparator();
            $thousandsSeparator = PHPExcel_Shared_String::getThousandsSeparator();
            if (preg_match('/^'.preg_quote($currencyCode).' *(\d{1,3}('.preg_quote($thousandsSeparator).'\d{3})*|(\d+))('.preg_quote($decimalSeparator).'\d{2})?$/', $value)) {
                // Convert value to number
                $value = (float) trim(str_replace(array($currencyCode, $thousandsSeparator, $decimalSeparator), array('', '', '.'), $value));
                $cell->setValueExplicit( $value, PHPExcel_Cell_DataType::TYPE_NUMERIC);
                // Set style
                $cell->getWorksheet()->getStyle( $cell->getCoordinate() )
                    ->getNumberFormat()->setFormatCode(
                        str_replace('$', $currencyCode, PHPExcel_Style_NumberFormat::FORMAT_CURRENCY_USD_SIMPLE )
                    );
                return true;
            } elseif (preg_match('/^\$ *(\d{1,3}(\,\d{3})*|(\d+))(\.\d{2})?$/', $value)) {
                // Convert value to number
                $value = (float) trim(str_replace(array('$',','), '', $value));
                $cell->setValueExplicit( $value, PHPExcel_Cell_DataType::TYPE_NUMERIC);
                // Set style
                $cell->getWorksheet()->getStyle( $cell->getCoordinate() )
                    ->getNumberFormat()->setFormatCode( PHPExcel_Style_NumberFormat::FORMAT_CURRENCY_USD_SIMPLE );
                return true;
            }

            // Check for time without seconds e.g. '9:45', '09:45'
            if (preg_match('/^(\d|[0-1]\d|2[0-3]):[0-5]\d$/', $value)) {
                // Convert value to number
                list($h, $m) = explode(':', $value);
                $days = $h / 24 + $m / 1440;
                $cell->setValueExplicit($days, PHPExcel_Cell_DataType::TYPE_NUMERIC);
                // Set style
                $cell->getWorksheet()->getStyle( $cell->getCoordinate() )
                    ->getNumberFormat()->setFormatCode( PHPExcel_Style_NumberFormat::FORMAT_DATE_TIME3 );
                return true;
            }

            // Check for time with seconds '9:45:59', '09:45:59'
            if (preg_match('/^(\d|[0-1]\d|2[0-3]):[0-5]\d:[0-5]\d$/', $value)) {
                // Convert value to number
                list($h, $m, $s) = explode(':', $value);
                $days = $h / 24 + $m / 1440 + $s / 86400;
                // Convert value to number
                $cell->setValueExplicit($days, PHPExcel_Cell_DataType::TYPE_NUMERIC);
                // Set style
                $cell->getWorksheet()->getStyle( $cell->getCoordinate() )
                    ->getNumberFormat()->setFormatCode( PHPExcel_Style_NumberFormat::FORMAT_DATE_TIME4 );
                return true;
            }

            // Check for datetime, e.g. '2008-12-31', '2008-12-31 15:59', '2008-12-31 15:59:10'
            if (($d = PHPExcel_Shared_Date::stringToExcel($value)) !== false) {
                // Convert value to number
                $cell->setValueExplicit($d, PHPExcel_Cell_DataType::TYPE_NUMERIC);
                // Determine style. Either there is a time part or not. Look for ':'
                if (strpos($value, ':') !== false) {
                    $formatCode = 'yyyy-mm-dd h:mm';
                } else {
                    $formatCode = 'yyyy-mm-dd';
                }
                $cell->getWorksheet()->getStyle( $cell->getCoordinate() )
                    ->getNumberFormat()->setFormatCode($formatCode);
                return true;
            }

            // Check for newline character "\n"
            if (strpos($value, "\n") !== FALSE) {
                $value = PHPExcel_Shared_String::SanitizeUTF8($value);
                $cell->setValueExplicit($value, PHPExcel_Cell_DataType::TYPE_STRING);
                // Set style
                $cell->getWorksheet()->getStyle( $cell->getCoordinate() )
                    ->getAlignment()->setWrapText(TRUE);
                return true;
            }
        }

        // Not bound yet? Use parent...
        return parent::bindValue($cell, $value);
    }
788287
public function setValue($pValue = NULL)
	{
		if (!self::getValueBinder()->bindValue($this, $pValue)) {
			throw new PHPExcel_Exception("Value could not be bound to cell.");
		}
		return $this;
	}
153986
public function prev()
    {
        if ($this->position <= $this->startColumn) {
            throw new PHPExcel_Exception(
                "Column is already at the beginning of range (" .
                PHPExcel_Cell::stringFromColumnIndex($this->endColumn) . " - " .
                PHPExcel_Cell::stringFromColumnIndex($this->endColumn) . ")"
            );
        }

        do {
            --$this->position;
        } while (($this->onlyExistingCells) &&
            (!$this->subject->cellExistsByColumnAndRow($this->position, $this->rowIndex)) &&
            ($this->position >= $this->startColumn));
    }
50898
public function getValue($nullValue = null, $calculateFormulas = false, $formatData = true)
    {
        $value = $nullValue;
        if ($this->cell->getValue() !== null) {
            if ($this->cell->getValue() instanceof RichText) {
                $value = $this->cell->getValue()->getPlainText();
            } elseif ($calculateFormulas) {
                $value = $this->cell->getCalculatedValue();
            } else {
                $value = $this->cell->getValue();
            }

            if ($formatData) {
                $style = $this->cell->getWorksheet()->getParent()->getCellXfByIndex($this->cell->getXfIndex());
                $value = NumberFormat::toFormattedString(
                    $value,
                    ($style && $style->getNumberFormat()) ? $style->getNumberFormat()->getFormatCode() : NumberFormat::FORMAT_GENERAL
                );
            }
        }

        return $value;
    }
804499
public function getCell($pCoordinate = 'A1')
    {
        // Check cell collection
        if ($this->_cellCollection->isDataSet($pCoordinate)) {
            return $this->_cellCollection->getCacheData($pCoordinate);
        }

        // Worksheet reference?
        if (strpos($pCoordinate, '!') !== false) {
            $worksheetReference = PHPExcel_Worksheet::extractSheetTitle($pCoordinate, true);
			return $this->_parent->getSheetByName($worksheetReference[0])->getCell($worksheetReference[1]);
        }

        // Named range?
        if ((!preg_match('/^'.PHPExcel_Calculation::CALCULATION_REGEXP_CELLREF.'$/i', $pCoordinate, $matches)) &&
            (preg_match('/^'.PHPExcel_Calculation::CALCULATION_REGEXP_NAMEDRANGE.'$/i', $pCoordinate, $matches))) {
            $namedRange = PHPExcel_NamedRange::resolveRange($pCoordinate, $this);
            if ($namedRange !== NULL) {
                $pCoordinate = $namedRange->getRange();
                return $namedRange->getWorksheet()->getCell($pCoordinate);
            }
        }

        // Uppercase coordinate
        $pCoordinate = strtoupper($pCoordinate);

        if (strpos($pCoordinate,':') !== false || strpos($pCoordinate,',') !== false) {
            throw new PHPExcel_Exception('Cell coordinate can not be a range of cells.');
        } elseif (strpos($pCoordinate,'$') !== false) {
            throw new PHPExcel_Exception('Cell coordinate must not be absolute.');
        } else {
            // Create new cell object

            // Coordinates
            $aCoordinates = PHPExcel_Cell::coordinateFromString($pCoordinate);

			$cell = $this->_cellCollection->addCacheData($pCoordinate,new PHPExcel_Cell(NULL, PHPExcel_Cell_DataType::TYPE_NULL, $this));
            $this->_cellCollectionIsSorted = false;

            if (PHPExcel_Cell::columnIndexFromString($this->_cachedHighestColumn) < PHPExcel_Cell::columnIndexFromString($aCoordinates[0]))
                $this->_cachedHighestColumn = $aCoordinates[0];

            $this->_cachedHighestRow = max($this->_cachedHighestRow,$aCoordinates[1]);

            // Cell needs appropriate xfIndex
            $rowDimensions    = $this->getRowDimensions();
            $columnDimensions = $this->getColumnDimensions();

            if ( isset($rowDimensions[$aCoordinates[1]]) && $rowDimensions[$aCoordinates[1]]->getXfIndex() !== null ) {
                // then there is a row dimension with explicit style, assign it to the cell
                $cell->setXfIndex($rowDimensions[$aCoordinates[1]]->getXfIndex());
            } else if ( isset($columnDimensions[$aCoordinates[0]]) ) {
                // then there is a column dimension, assign it to the cell
                $cell->setXfIndex($columnDimensions[$aCoordinates[0]]->getXfIndex());
            } else {
                // set to default index
                $cell->setXfIndex(0);
            }

            return $cell;
        }
    }
404873
static String retriveCellValue(Cell cell) {
        String cellValue = "";
        if (cell == null) {
            return cellValue;
        }
        try {
            switch (cell.getCellType()) {
                case Cell.CELL_TYPE_NUMERIC:
                    if (CellDateFormat.isDateCell(cell)) {
                        Date date = cell.getDateCellValue();
                        DateFormat dateFormat = CellDateFormat
                                .getFormat(cell.getCellStyle().getDataFormat()).getDateFormat();
                        cellValue = dateFormat.format(date);
                    } else {
                        cellValue = roundValue(cell.getNumericCellValue());
                    }
                    break;
                case Cell.CELL_TYPE_FORMULA:
                    cellValue = roundValue(cell.getSheet().getWorkbook().getCreationHelper()
                            .createFormulaEvaluator().evaluate(cell).formatAsString());
                    break;
                default:
                    cellValue = cell.getStringCellValue();
            }
        } catch (Exception e) {
            LOG.warn(MessageManager.getMessage("cell.fail"), cell.getRowIndex(),
                    cell.getColumnIndex(), e.getMessage());
        }

        return cellValue;
    }
1264119
public function isDataSet($pCoord)
    {
        //    Check if the requested entry is the current object, or exists in the cache
        if (parent::isDataSet($pCoord)) {
            if ($this->currentObjectID == $pCoord) {
                return true;
            }
            //    Check if the requested entry still exists in apc
            $success = apc_fetch($this->cachePrefix.$pCoord.'.cache');
            if ($success === false) {
                //    Entry no longer exists in APC, so clear it from the cache array
                parent::deleteCacheData($pCoord);
                throw new PHPExcel_Exception('Cell entry '.$pCoord.' no longer exists in APC cache');
            }
            return true;
        }
        return false;
    }
927580
public static function isDateTime(PHPExcel_Cell $pCell)
    {
        return self::isDateTimeFormat(
            $pCell->getWorksheet()->getStyle(
                $pCell->getCoordinate()
            )->getNumberFormat()
        );
    }
305866
public function addCacheData($pCoord, PHPExcel_Cell $cell) {
		$this->_cellCache[$pCoord] = $cell;

		//	Set current entry to the new/updated entry
		$this->_currentObjectID = $pCoord;

		return $cell;
	}
757561
public function value($column)
    {
        $result = (array) $this->first([$column]);

        return count($result) > 0 ? reset($result) : null;
    }
462799
private function _processTokenStack($tokens, $cellID = NULL, PHPExcel_Cell $pCell = NULL) {
		if ($tokens == FALSE) return FALSE;

		//	If we're using cell caching, then $pCell may well be flushed back to the cache (which detaches the parent cell collection),
		//		so we store the parent cell collection so that we can re-attach it when necessary
		$pCellWorksheet = ($pCell !== NULL) ? $pCell->getWorksheet() : NULL;
		$pCellParent = ($pCell !== NULL) ? $pCell->getParent() : null;
		$stack = new PHPExcel_Calculation_Token_Stack;

		//	Loop through each token in turn
		foreach ($tokens as $tokenData) {
//			print_r($tokenData);
//			echo '<br />';
			$token = $tokenData['value'];
//			echo '<b>Token is '.$token.'</b><br />';
			// if the token is a binary operator, pop the top two values off the stack, do the operation, and push the result back on the stack
			if (isset(self::$_binaryOperators[$token])) {
//				echo 'Token is a binary operator<br />';
				//	We must have two operands, error if we don't
				if (($operand2Data = $stack->pop()) === NULL) return $this->_raiseFormulaError('Internal error - Operand value missing from stack');
				if (($operand1Data = $stack->pop()) === NULL) return $this->_raiseFormulaError('Internal error - Operand value missing from stack');

				$operand1 = self::_dataTestReference($operand1Data);
				$operand2 = self::_dataTestReference($operand2Data);

				//	Log what we're doing
				if ($token == ':') {
					$this->_debugLog->writeDebugLog('Evaluating Range ', $this->_showValue($operand1Data['reference']), ' ', $token, ' ', $this->_showValue($operand2Data['reference']));
				} else {
					$this->_debugLog->writeDebugLog('Evaluating ', $this->_showValue($operand1), ' ', $token, ' ', $this->_showValue($operand2));
				}

				//	Process the operation in the appropriate manner
				switch ($token) {
					//	Comparison (Boolean) Operators
					case '>'	:			//	Greater than
					case '<'	:			//	Less than
					case '>='	:			//	Greater than or Equal to
					case '<='	:			//	Less than or Equal to
					case '='	:			//	Equality
					case '<>'	:			//	Inequality
						$this->_executeBinaryComparisonOperation($cellID,$operand1,$operand2,$token,$stack);
						break;
					//	Binary Operators
					case ':'	:			//	Range
						$sheet1 = $sheet2 = '';
						if (strpos($operand1Data['reference'],'!') !== FALSE) {
							list($sheet1,$operand1Data['reference']) = explode('!',$operand1Data['reference']);
						} else {
							$sheet1 = ($pCellParent !== NULL) ? $pCellWorksheet->getTitle() : '';
						}
						if (strpos($operand2Data['reference'],'!') !== FALSE) {
							list($sheet2,$operand2Data['reference']) = explode('!',$operand2Data['reference']);
						} else {
							$sheet2 = $sheet1;
						}
						if ($sheet1 == $sheet2) {
							if ($operand1Data['reference'] === NULL) {
								if ((trim($operand1Data['value']) != '') && (is_numeric($operand1Data['value']))) {
									$operand1Data['reference'] = $pCell->getColumn().$operand1Data['value'];
								} elseif (trim($operand1Data['reference']) == '') {
									$operand1Data['reference'] = $pCell->getCoordinate();
								} else {
									$operand1Data['reference'] = $operand1Data['value'].$pCell->getRow();
								}
							}
							if ($operand2Data['reference'] === NULL) {
								if ((trim($operand2Data['value']) != '') && (is_numeric($operand2Data['value']))) {
									$operand2Data['reference'] = $pCell->getColumn().$operand2Data['value'];
								} elseif (trim($operand2Data['reference']) == '') {
									$operand2Data['reference'] = $pCell->getCoordinate();
								} else {
									$operand2Data['reference'] = $operand2Data['value'].$pCell->getRow();
								}
							}

							$oData = array_merge(explode(':',$operand1Data['reference']),explode(':',$operand2Data['reference']));
							$oCol = $oRow = array();
							foreach($oData as $oDatum) {
								$oCR = PHPExcel_Cell::coordinateFromString($oDatum);
								$oCol[] = PHPExcel_Cell::columnIndexFromString($oCR[0]) - 1;
								$oRow[] = $oCR[1];
							}
							$cellRef = PHPExcel_Cell::stringFromColumnIndex(min($oCol)).min($oRow).':'.PHPExcel_Cell::stringFromColumnIndex(max($oCol)).max($oRow);
							if ($pCellParent !== NULL) {
								$cellValue = $this->extractCellRange($cellRef, $this->_workbook->getSheetByName($sheet1), FALSE);
							} else {
								return $this->_raiseFormulaError('Unable to access Cell Reference');
							}
							$stack->push('Cell Reference',$cellValue,$cellRef);
						} else {
							$stack->push('Error',PHPExcel_Calculation_Functions::REF(),NULL);
						}

						break;
					case '+'	:			//	Addition
						$this->_executeNumericBinaryOperation($cellID,$operand1,$operand2,$token,'plusEquals',$stack);
						break;
					case '-'	:			//	Subtraction
						$this->_executeNumericBinaryOperation($cellID,$operand1,$operand2,$token,'minusEquals',$stack);
						break;
					case '*'	:			//	Multiplication
						$this->_executeNumericBinaryOperation($cellID,$operand1,$operand2,$token,'arrayTimesEquals',$stack);
						break;
					case '/'	:			//	Division
						$this->_executeNumericBinaryOperation($cellID,$operand1,$operand2,$token,'arrayRightDivide',$stack);
						break;
					case '^'	:			//	Exponential
						$this->_executeNumericBinaryOperation($cellID,$operand1,$operand2,$token,'power',$stack);
						break;
					case '&'	:			//	Concatenation
						//	If either of the operands is a matrix, we need to treat them both as matrices
						//		(converting the other operand to a matrix if need be); then perform the required
						//		matrix operation
						if (is_bool($operand1)) {
							$operand1 = ($operand1) ? self::$_localeBoolean['TRUE'] : self::$_localeBoolean['FALSE'];
						}
						if (is_bool($operand2)) {
							$operand2 = ($operand2) ? self::$_localeBoolean['TRUE'] : self::$_localeBoolean['FALSE'];
						}
						if ((is_array($operand1)) || (is_array($operand2))) {
							//	Ensure that both operands are arrays/matrices
							self::_checkMatrixOperands($operand1,$operand2,2);
							try {
								//	Convert operand 1 from a PHP array to a matrix
								$matrix = new PHPExcel_Shared_JAMA_Matrix($operand1);
								//	Perform the required operation against the operand 1 matrix, passing in operand 2
								$matrixResult = $matrix->concat($operand2);
								$result = $matrixResult->getArray();
							} catch (PHPExcel_Exception $ex) {
								$this->_debugLog->writeDebugLog('JAMA Matrix Exception: ', $ex->getMessage());
								$result = '#VALUE!';
							}
						} else {
							$result = '"'.str_replace('""','"',self::_unwrapResult($operand1,'"').self::_unwrapResult($operand2,'"')).'"';
						}
						$this->_debugLog->writeDebugLog('Evaluation Result is ', $this->_showTypeDetails($result));
						$stack->push('Value',$result);
						break;
					case '|'	:			//	Intersect
						$rowIntersect = array_intersect_key($operand1,$operand2);
						$cellIntersect = $oCol = $oRow = array();
						foreach(array_keys($rowIntersect) as $row) {
							$oRow[] = $row;
							foreach($rowIntersect[$row] as $col => $data) {
								$oCol[] = PHPExcel_Cell::columnIndexFromString($col) - 1;
								$cellIntersect[$row] = array_intersect_key($operand1[$row],$operand2[$row]);
							}
						}
						$cellRef = PHPExcel_Cell::stringFromColumnIndex(min($oCol)).min($oRow).':'.PHPExcel_Cell::stringFromColumnIndex(max($oCol)).max($oRow);
						$this->_debugLog->writeDebugLog('Evaluation Result is ', $this->_showTypeDetails($cellIntersect));
						$stack->push('Value',$cellIntersect,$cellRef);
						break;
				}

			// if the token is a unary operator, pop one value off the stack, do the operation, and push it back on
			} elseif (($token === '~') || ($token === '%')) {
//				echo 'Token is a unary operator<br />';
				if (($arg = $stack->pop()) === NULL) return $this->_raiseFormulaError('Internal error - Operand value missing from stack');
				$arg = $arg['value'];
				if ($token === '~') {
//					echo 'Token is a negation operator<br />';
					$this->_debugLog->writeDebugLog('Evaluating Negation of ', $this->_showValue($arg));
					$multiplier = -1;
				} else {
//					echo 'Token is a percentile operator<br />';
					$this->_debugLog->writeDebugLog('Evaluating Percentile of ', $this->_showValue($arg));
					$multiplier = 0.01;
				}
				if (is_array($arg)) {
					self::_checkMatrixOperands($arg,$multiplier,2);
					try {
						$matrix1 = new PHPExcel_Shared_JAMA_Matrix($arg);
						$matrixResult = $matrix1->arrayTimesEquals($multiplier);
						$result = $matrixResult->getArray();
					} catch (PHPExcel_Exception $ex) {
						$this->_debugLog->writeDebugLog('JAMA Matrix Exception: ', $ex->getMessage());
						$result = '#VALUE!';
					}
					$this->_debugLog->writeDebugLog('Evaluation Result is ', $this->_showTypeDetails($result));
					$stack->push('Value',$result);
				} else {
					$this->_executeNumericBinaryOperation($cellID,$multiplier,$arg,'*','arrayTimesEquals',$stack);
				}

			} elseif (preg_match('/^'.self::CALCULATION_REGEXP_CELLREF.'$/i', $token, $matches)) {
				$cellRef = NULL;
//				echo 'Element '.$token.' is a Cell reference<br />';
				if (isset($matches[8])) {
//					echo 'Reference is a Range of cells<br />';
					if ($pCell === NULL) {
//						We can't access the range, so return a REF error
						$cellValue = PHPExcel_Calculation_Functions::REF();
					} else {
						$cellRef = $matches[6].$matches[7].':'.$matches[9].$matches[10];
						if ($matches[2] > '') {
							$matches[2] = trim($matches[2],"\"'");
							if ((strpos($matches[2],'[') !== FALSE) || (strpos($matches[2],']') !== FALSE)) {
								//	It's a Reference to an external workbook (not currently supported)
								return $this->_raiseFormulaError('Unable to access External Workbook');
							}
							$matches[2] = trim($matches[2],"\"'");
//							echo '$cellRef='.$cellRef.' in worksheet '.$matches[2].'<br />';
							$this->_debugLog->writeDebugLog('Evaluating Cell Range ', $cellRef, ' in worksheet ', $matches[2]);
							if ($pCellParent !== NULL) {
								$cellValue = $this->extractCellRange($cellRef, $this->_workbook->getSheetByName($matches[2]), FALSE);
							} else {
								return $this->_raiseFormulaError('Unable to access Cell Reference');
							}
							$this->_debugLog->writeDebugLog('Evaluation Result for cells ', $cellRef, ' in worksheet ', $matches[2], ' is ', $this->_showTypeDetails($cellValue));
//							$cellRef = $matches[2].'!'.$cellRef;
						} else {
//							echo '$cellRef='.$cellRef.' in current worksheet<br />';
							$this->_debugLog->writeDebugLog('Evaluating Cell Range ', $cellRef, ' in current worksheet');
							if ($pCellParent !== NULL) {
								$cellValue = $this->extractCellRange($cellRef, $pCellWorksheet, FALSE);
							} else {
								return $this->_raiseFormulaError('Unable to access Cell Reference');
							}
							$this->_debugLog->writeDebugLog('Evaluation Result for cells ', $cellRef, ' is ', $this->_showTypeDetails($cellValue));
						}
					}
				} else {
//					echo 'Reference is a single Cell<br />';
					if ($pCell === NULL) {
//						We can't access the cell, so return a REF error
						$cellValue = PHPExcel_Calculation_Functions::REF();
					} else {
						$cellRef = $matches[6].$matches[7];
						if ($matches[2] > '') {
							$matches[2] = trim($matches[2],"\"'");
							if ((strpos($matches[2],'[') !== FALSE) || (strpos($matches[2],']') !== FALSE)) {
								//	It's a Reference to an external workbook (not currently supported)
								return $this->_raiseFormulaError('Unable to access External Workbook');
							}
//							echo '$cellRef='.$cellRef.' in worksheet '.$matches[2].'<br />';
							$this->_debugLog->writeDebugLog('Evaluating Cell ', $cellRef, ' in worksheet ', $matches[2]);
							if ($pCellParent !== NULL) {
								$cellSheet = $this->_workbook->getSheetByName($matches[2]);
								if ($cellSheet && $cellSheet->cellExists($cellRef)) {
									$cellValue = $this->extractCellRange($cellRef, $this->_workbook->getSheetByName($matches[2]), FALSE);
									$pCell->attach($pCellParent);
								} else {
									$cellValue = NULL;
								}
							} else {
								return $this->_raiseFormulaError('Unable to access Cell Reference');
							}
							$this->_debugLog->writeDebugLog('Evaluation Result for cell ', $cellRef, ' in worksheet ', $matches[2], ' is ', $this->_showTypeDetails($cellValue));
//							$cellRef = $matches[2].'!'.$cellRef;
						} else {
//							echo '$cellRef='.$cellRef.' in current worksheet<br />';
							$this->_debugLog->writeDebugLog('Evaluating Cell ', $cellRef, ' in current worksheet');
							if ($pCellParent->isDataSet($cellRef)) {
								$cellValue = $this->extractCellRange($cellRef, $pCellWorksheet, FALSE);
								$pCell->attach($pCellParent);
							} else {
								$cellValue = NULL;
							}
							$this->_debugLog->writeDebugLog('Evaluation Result for cell ', $cellRef, ' is ', $this->_showTypeDetails($cellValue));
						}
					}
				}
				$stack->push('Value',$cellValue,$cellRef);

			// if the token is a function, pop arguments off the stack, hand them to the function, and push the result back on
			} elseif (preg_match('/^'.self::CALCULATION_REGEXP_FUNCTION.'$/i', $token, $matches)) {
//				echo 'Token is a function<br />';
				$functionName = $matches[1];
				$argCount = $stack->pop();
				$argCount = $argCount['value'];
				if ($functionName != 'MKMATRIX') {
					$this->_debugLog->writeDebugLog('Evaluating Function ', self::_localeFunc($functionName), '() with ', (($argCount == 0) ? 'no' : $argCount), ' argument', (($argCount == 1) ? '' : 's'));
				}
				if ((isset(self::$_PHPExcelFunctions[$functionName])) || (isset(self::$_controlFunctions[$functionName]))) {	// function
					if (isset(self::$_PHPExcelFunctions[$functionName])) {
						$functionCall = self::$_PHPExcelFunctions[$functionName]['functionCall'];
						$passByReference = isset(self::$_PHPExcelFunctions[$functionName]['passByReference']);
						$passCellReference = isset(self::$_PHPExcelFunctions[$functionName]['passCellReference']);
					} elseif (isset(self::$_controlFunctions[$functionName])) {
						$functionCall = self::$_controlFunctions[$functionName]['functionCall'];
						$passByReference = isset(self::$_controlFunctions[$functionName]['passByReference']);
						$passCellReference = isset(self::$_controlFunctions[$functionName]['passCellReference']);
					}
					// get the arguments for this function
//					echo 'Function '.$functionName.' expects '.$argCount.' arguments<br />';
					$args = $argArrayVals = array();
					for ($i = 0; $i < $argCount; ++$i) {
						$arg = $stack->pop();
						$a = $argCount - $i - 1;
						if (($passByReference) &&
							(isset(self::$_PHPExcelFunctions[$functionName]['passByReference'][$a])) &&
							(self::$_PHPExcelFunctions[$functionName]['passByReference'][$a])) {
							if ($arg['reference'] === NULL) {
								$args[] = $cellID;
								if ($functionName != 'MKMATRIX') { $argArrayVals[] = $this->_showValue($cellID); }
							} else {
								$args[] = $arg['reference'];
								if ($functionName != 'MKMATRIX') { $argArrayVals[] = $this->_showValue($arg['reference']); }
							}
						} else {
							$args[] = self::_unwrapResult($arg['value']);
							if ($functionName != 'MKMATRIX') { $argArrayVals[] = $this->_showValue($arg['value']); }
						}
					}
					//	Reverse the order of the arguments
					krsort($args);
					if (($passByReference) && ($argCount == 0)) {
						$args[] = $cellID;
						$argArrayVals[] = $this->_showValue($cellID);
					}
//					echo 'Arguments are: ';
//					print_r($args);
//					echo '<br />';
					if ($functionName != 'MKMATRIX') {
						if ($this->_debugLog->getWriteDebugLog()) {
							krsort($argArrayVals);
							$this->_debugLog->writeDebugLog('Evaluating ', self::_localeFunc($functionName), '( ', implode(self::$_localeArgumentSeparator.' ',PHPExcel_Calculation_Functions::flattenArray($argArrayVals)), ' )');
						}
					}
					//	Process each argument in turn, building the return value as an array
//					if (($argCount == 1) && (is_array($args[1])) && ($functionName != 'MKMATRIX')) {
//						$operand1 = $args[1];
//						$this->_debugLog->writeDebugLog('Argument is a matrix: ', $this->_showValue($operand1));
//						$result = array();
//						$row = 0;
//						foreach($operand1 as $args) {
//							if (is_array($args)) {
//								foreach($args as $arg) {
//									$this->_debugLog->writeDebugLog('Evaluating ', self::_localeFunc($functionName), '( ', $this->_showValue($arg), ' )');
//									$r = call_user_func_array($functionCall,$arg);
//									$this->_debugLog->writeDebugLog('Evaluation Result for ', self::_localeFunc($functionName), '() function call is ', $this->_showTypeDetails($r));
//									$result[$row][] = $r;
//								}
//								++$row;
//							} else {
//								$this->_debugLog->writeDebugLog('Evaluating ', self::_localeFunc($functionName), '( ', $this->_showValue($args), ' )');
//								$r = call_user_func_array($functionCall,$args);
//								$this->_debugLog->writeDebugLog('Evaluation Result for ', self::_localeFunc($functionName), '() function call is ', $this->_showTypeDetails($r));
//								$result[] = $r;
//							}
//						}
//					} else {
					//	Process the argument with the appropriate function call
						if ($passCellReference) {
							$args[] = $pCell;
						}
						if (strpos($functionCall,'::') !== FALSE) {
							$result = call_user_func_array(explode('::',$functionCall),$args);
						} else {
							foreach($args as &$arg) {
								$arg = PHPExcel_Calculation_Functions::flattenSingleValue($arg);
							}
							unset($arg);
							$result = call_user_func_array($functionCall,$args);
						}
//					}
					if ($functionName != 'MKMATRIX') {
						$this->_debugLog->writeDebugLog('Evaluation Result for ', self::_localeFunc($functionName), '() function call is ', $this->_showTypeDetails($result));
					}
					$stack->push('Value',self::_wrapResult($result));
				}

			} else {
				// if the token is a number, boolean, string or an Excel error, push it onto the stack
				if (isset(self::$_ExcelConstants[strtoupper($token)])) {
					$excelConstant = strtoupper($token);
//					echo 'Token is a PHPExcel constant: '.$excelConstant.'<br />';
					$stack->push('Constant Value',self::$_ExcelConstants[$excelConstant]);
					$this->_debugLog->writeDebugLog('Evaluating Constant ', $excelConstant, ' as ', $this->_showTypeDetails(self::$_ExcelConstants[$excelConstant]));
				} elseif ((is_numeric($token)) || ($token === NULL) || (is_bool($token)) || ($token == '') || ($token{0} == '"') || ($token{0} == '#')) {
//					echo 'Token is a number, boolean, string, null or an Excel error<br />';
					$stack->push('Value',$token);
				// if the token is a named range, push the named range name onto the stack
				} elseif (preg_match('/^'.self::CALCULATION_REGEXP_NAMEDRANGE.'$/i', $token, $matches)) {
//					echo 'Token is a named range<br />';
					$namedRange = $matches[6];
//					echo 'Named Range is '.$namedRange.'<br />';
					$this->_debugLog->writeDebugLog('Evaluating Named Range ', $namedRange);
					$cellValue = $this->extractNamedRange($namedRange, ((NULL !== $pCell) ? $pCellWorksheet : NULL), FALSE);
					$pCell->attach($pCellParent);
					$this->_debugLog->writeDebugLog('Evaluation Result for named range ', $namedRange, ' is ', $this->_showTypeDetails($cellValue));
					$stack->push('Named Range',$cellValue,$namedRange);
				} else {
					return $this->_raiseFormulaError("undefined variable '$token'");
				}
			}
		}
		// when we're out of tokens, the stack should have a single element, the final result
		if ($stack->count() != 1) return $this->_raiseFormulaError("internal error");
		$output = $stack->pop();
		$output = $output['value'];

//		if ((is_array($output)) && (self::$returnArrayAsType != self::RETURN_ARRAY_AS_ARRAY)) {
//			return array_shift(PHPExcel_Calculation_Functions::flattenArray($output));
//		}
		return $output;
	}