[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/phpexcel/PHPExcel/ -> Calculation.php (source)

   1  <?php
   2  
   3  /** PHPExcel root directory */
   4  if (!defined('PHPEXCEL_ROOT')) {
   5      /**
   6       * @ignore
   7       */
   8      define('PHPEXCEL_ROOT', dirname(__FILE__) . '/../');
   9      require (PHPEXCEL_ROOT . 'PHPExcel/Autoloader.php');
  10  }
  11  
  12  if (!defined('CALCULATION_REGEXP_CELLREF')) {
  13      //    Test for support of \P (multibyte options) in PCRE
  14      if (defined('PREG_BAD_UTF8_ERROR')) {
  15          //    Cell reference (cell or range of cells, with or without a sheet reference)
  16          define('CALCULATION_REGEXP_CELLREF', '((([^\s,!&%^\/\*\+<>=-]*)|(\'[^\']*\')|(\"[^\"]*\"))!)?\$?([a-z]{1,3})\$?(\d{1,7})');
  17          //    Named Range of cells
  18          define('CALCULATION_REGEXP_NAMEDRANGE', '((([^\s,!&%^\/\*\+<>=-]*)|(\'[^\']*\')|(\"[^\"]*\"))!)?([_A-Z][_A-Z0-9\.]*)');
  19      } else {
  20          //    Cell reference (cell or range of cells, with or without a sheet reference)
  21          define('CALCULATION_REGEXP_CELLREF', '(((\w*)|(\'[^\']*\')|(\"[^\"]*\"))!)?\$?([a-z]{1,3})\$?(\d+)');
  22          //    Named Range of cells
  23          define('CALCULATION_REGEXP_NAMEDRANGE', '(((\w*)|(\'.*\')|(\".*\"))!)?([_A-Z][_A-Z0-9\.]*)');
  24      }
  25  }
  26  
  27  /**
  28   * PHPExcel_Calculation (Multiton)
  29   *
  30   * Copyright (c) 2006 - 2015 PHPExcel
  31   *
  32   * This library is free software; you can redistribute it and/or
  33   * modify it under the terms of the GNU Lesser General Public
  34   * License as published by the Free Software Foundation; either
  35   * version 2.1 of the License, or (at your option) any later version.
  36   *
  37   * This library is distributed in the hope that it will be useful,
  38   * but WITHOUT ANY WARRANTY; without even the implied warranty of
  39   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  40   * Lesser General Public License for more details.
  41   *
  42   * You should have received a copy of the GNU Lesser General Public
  43   * License along with this library; if not, write to the Free Software
  44   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  45   *
  46   * @category   PHPExcel
  47   * @package    PHPExcel_Calculation
  48   * @copyright  Copyright (c) 2006 - 2015 PHPExcel (http://www.codeplex.com/PHPExcel)
  49   * @license    http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt    LGPL
  50   * @version    ##VERSION##, ##DATE##
  51   */
  52  class PHPExcel_Calculation
  53  {
  54      /** Constants                */
  55      /** Regular Expressions        */
  56      //    Numeric operand
  57      const CALCULATION_REGEXP_NUMBER        = '[-+]?\d*\.?\d+(e[-+]?\d+)?';
  58      //    String operand
  59      const CALCULATION_REGEXP_STRING        = '"(?:[^"]|"")*"';
  60      //    Opening bracket
  61      const CALCULATION_REGEXP_OPENBRACE    = '\(';
  62      //    Function (allow for the old @ symbol that could be used to prefix a function, but we'll ignore it)
  63      const CALCULATION_REGEXP_FUNCTION    = '@?([A-Z][A-Z0-9\.]*)[\s]*\(';
  64      //    Cell reference (cell or range of cells, with or without a sheet reference)
  65      const CALCULATION_REGEXP_CELLREF    = CALCULATION_REGEXP_CELLREF;
  66      //    Named Range of cells
  67      const CALCULATION_REGEXP_NAMEDRANGE    = CALCULATION_REGEXP_NAMEDRANGE;
  68      //    Error
  69      const CALCULATION_REGEXP_ERROR        = '\#[A-Z][A-Z0_\/]*[!\?]?';
  70  
  71  
  72      /** constants */
  73      const RETURN_ARRAY_AS_ERROR = 'error';
  74      const RETURN_ARRAY_AS_VALUE = 'value';
  75      const RETURN_ARRAY_AS_ARRAY = 'array';
  76  
  77      private static $returnArrayAsType = self::RETURN_ARRAY_AS_VALUE;
  78  
  79  
  80      /**
  81       * Instance of this class
  82       *
  83       * @access    private
  84       * @var PHPExcel_Calculation
  85       */
  86      private static $instance;
  87  
  88  
  89      /**
  90       * Instance of the workbook this Calculation Engine is using
  91       *
  92       * @access    private
  93       * @var PHPExcel
  94       */
  95      private $workbook;
  96  
  97      /**
  98       * List of instances of the calculation engine that we've instantiated for individual workbooks
  99       *
 100       * @access    private
 101       * @var PHPExcel_Calculation[]
 102       */
 103      private static $workbookSets;
 104  
 105      /**
 106       * Calculation cache
 107       *
 108       * @access    private
 109       * @var array
 110       */
 111      private $calculationCache = array ();
 112  
 113  
 114      /**
 115       * Calculation cache enabled
 116       *
 117       * @access    private
 118       * @var boolean
 119       */
 120      private $calculationCacheEnabled = true;
 121  
 122  
 123      /**
 124       * List of operators that can be used within formulae
 125       * The true/false value indicates whether it is a binary operator or a unary operator
 126       *
 127       * @access    private
 128       * @var array
 129       */
 130      private static $operators = array(
 131          '+' => true,    '-' => true,    '*' => true,    '/' => true,
 132          '^' => true,    '&' => true,    '%' => false,    '~' => false,
 133          '>' => true,    '<' => true,    '=' => true,    '>=' => true,
 134          '<=' => true,    '<>' => true,    '|' => true,    ':' => true
 135      );
 136  
 137      /**
 138       * List of binary operators (those that expect two operands)
 139       *
 140       * @access    private
 141       * @var array
 142       */
 143      private static $binaryOperators = array(
 144          '+' => true,    '-' => true,    '*' => true,    '/' => true,
 145          '^' => true,    '&' => true,    '>' => true,    '<' => true,
 146          '=' => true,    '>=' => true,    '<=' => true,    '<>' => true,
 147          '|' => true,    ':' => true
 148      );
 149  
 150      /**
 151       * The debug log generated by the calculation engine
 152       *
 153       * @access    private
 154       * @var PHPExcel_CalcEngine_Logger
 155       *
 156       */
 157      private $debugLog;
 158  
 159      /**
 160       * Flag to determine how formula errors should be handled
 161       *        If true, then a user error will be triggered
 162       *        If false, then an exception will be thrown
 163       *
 164       * @access    public
 165       * @var boolean
 166       *
 167       */
 168      public $suppressFormulaErrors = false;
 169  
 170      /**
 171       * Error message for any error that was raised/thrown by the calculation engine
 172       *
 173       * @access    public
 174       * @var string
 175       *
 176       */
 177      public $formulaError = null;
 178  
 179      /**
 180       * An array of the nested cell references accessed by the calculation engine, used for the debug log
 181       *
 182       * @access    private
 183       * @var array of string
 184       *
 185       */
 186      private $cyclicReferenceStack;
 187  
 188      private $cellStack = array();
 189  
 190      /**
 191       * Current iteration counter for cyclic formulae
 192       * If the value is 0 (or less) then cyclic formulae will throw an exception,
 193       *    otherwise they will iterate to the limit defined here before returning a result
 194       *
 195       * @var integer
 196       *
 197       */
 198      private $cyclicFormulaCounter = 1;
 199  
 200      private $cyclicFormulaCell = '';
 201  
 202      /**
 203       * Number of iterations for cyclic formulae
 204       *
 205       * @var integer
 206       *
 207       */
 208      public $cyclicFormulaCount = 1;
 209  
 210      /**
 211       * Precision used for calculations
 212       *
 213       * @var integer
 214       *
 215       */
 216      private $savedPrecision    = 14;
 217  
 218  
 219      /**
 220       * The current locale setting
 221       *
 222       * @var string
 223       *
 224       */
 225      private static $localeLanguage = 'en_us';                    //    US English    (default locale)
 226  
 227      /**
 228       * List of available locale settings
 229       * Note that this is read for the locale subdirectory only when requested
 230       *
 231       * @var string[]
 232       *
 233       */
 234      private static $validLocaleLanguages = array(
 235          'en'        //    English        (default language)
 236      );
 237  
 238      /**
 239       * Locale-specific argument separator for function arguments
 240       *
 241       * @var string
 242       *
 243       */
 244      private static $localeArgumentSeparator = ',';
 245      private static $localeFunctions = array();
 246  
 247      /**
 248       * Locale-specific translations for Excel constants (True, False and Null)
 249       *
 250       * @var string[]
 251       *
 252       */
 253      public static $localeBoolean = array(
 254          'TRUE'  => 'TRUE',
 255          'FALSE' => 'FALSE',
 256          'NULL'  => 'NULL'
 257      );
 258  
 259      /**
 260       * Excel constant string translations to their PHP equivalents
 261       * Constant conversion from text name/value to actual (datatyped) value
 262       *
 263       * @var string[]
 264       *
 265       */
 266      private static $excelConstants = array(
 267          'TRUE'  => true,
 268          'FALSE' => false,
 269          'NULL'  => null
 270      );
 271  
 272       //    PHPExcel functions
 273      private static $PHPExcelFunctions = array(
 274          'ABS' => array(
 275              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
 276              'functionCall' => 'abs',
 277              'argumentCount' => '1'
 278          ),
 279          'ACCRINT' => array(
 280              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
 281              'functionCall' => 'PHPExcel_Calculation_Financial::ACCRINT',
 282              'argumentCount' => '4-7'
 283          ),
 284          'ACCRINTM' => array(
 285              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
 286              'functionCall' => 'PHPExcel_Calculation_Financial::ACCRINTM',
 287              'argumentCount' => '3-5'
 288          ),
 289          'ACOS' => array(
 290              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
 291              'functionCall' => 'acos',
 292              'argumentCount' => '1'
 293          ),
 294          'ACOSH' => array(
 295              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
 296              'functionCall' => 'acosh',
 297              'argumentCount' => '1'
 298          ),
 299          'ADDRESS' => array(
 300              'category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
 301              'functionCall' => 'PHPExcel_Calculation_LookupRef::CELL_ADDRESS',
 302              'argumentCount' => '2-5'
 303          ),
 304          'AMORDEGRC' => array(
 305              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
 306              'functionCall' => 'PHPExcel_Calculation_Financial::AMORDEGRC',
 307              'argumentCount' => '6,7'
 308          ),
 309          'AMORLINC' => array(
 310              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
 311              'functionCall' => 'PHPExcel_Calculation_Financial::AMORLINC',
 312              'argumentCount' => '6,7'
 313          ),
 314          'AND' => array(
 315              'category' => PHPExcel_Calculation_Function::CATEGORY_LOGICAL,
 316              'functionCall' => 'PHPExcel_Calculation_Logical::LOGICAL_AND',
 317              'argumentCount' => '1+'
 318          ),
 319          'AREAS' => array(
 320              'category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
 321              'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
 322              'argumentCount' => '1'
 323          ),
 324          'ASC' => array(
 325              'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
 326              'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
 327              'argumentCount' => '1'
 328          ),
 329          'ASIN' => array(
 330              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
 331              'functionCall' => 'asin',
 332              'argumentCount' => '1'
 333          ),
 334          'ASINH' => array(
 335              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
 336              'functionCall' => 'asinh',
 337              'argumentCount' => '1'
 338          ),
 339          'ATAN' => array(
 340              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
 341              'functionCall' => 'atan',
 342              'argumentCount' => '1'
 343          ),
 344          'ATAN2' => array(
 345              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
 346              'functionCall' => 'PHPExcel_Calculation_MathTrig::ATAN2',
 347              'argumentCount' => '2'
 348          ),
 349          'ATANH' => array(
 350              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
 351              'functionCall' => 'atanh',
 352              'argumentCount' => '1'
 353          ),
 354          'AVEDEV' => array(
 355              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
 356              'functionCall' => 'PHPExcel_Calculation_Statistical::AVEDEV',
 357              'argumentCount' => '1+'
 358          ),
 359          'AVERAGE' => array(
 360              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
 361              'functionCall' => 'PHPExcel_Calculation_Statistical::AVERAGE',
 362              'argumentCount' => '1+'
 363          ),
 364          'AVERAGEA' => array(
 365              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
 366              'functionCall' => 'PHPExcel_Calculation_Statistical::AVERAGEA',
 367              'argumentCount' => '1+'
 368          ),
 369          'AVERAGEIF' => array(
 370              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
 371              'functionCall' => 'PHPExcel_Calculation_Statistical::AVERAGEIF',
 372              'argumentCount' => '2,3'
 373          ),
 374          'AVERAGEIFS' => array(
 375              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
 376              'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
 377              'argumentCount' => '3+'
 378          ),
 379          'BAHTTEXT' => array(
 380              'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
 381              'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
 382              'argumentCount' => '1'
 383          ),
 384          'BESSELI' => array(
 385              'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
 386              'functionCall' => 'PHPExcel_Calculation_Engineering::BESSELI',
 387              'argumentCount' => '2'
 388          ),
 389          'BESSELJ' => array(
 390              'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
 391              'functionCall' => 'PHPExcel_Calculation_Engineering::BESSELJ',
 392              'argumentCount' => '2'
 393          ),
 394          'BESSELK' => array(
 395              'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
 396              'functionCall' => 'PHPExcel_Calculation_Engineering::BESSELK',
 397              'argumentCount' => '2'
 398          ),
 399          'BESSELY' => array(
 400              'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
 401              'functionCall' => 'PHPExcel_Calculation_Engineering::BESSELY',
 402              'argumentCount' => '2'
 403          ),
 404          'BETADIST' => array(
 405              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
 406              'functionCall' => 'PHPExcel_Calculation_Statistical::BETADIST',
 407              'argumentCount' => '3-5'
 408          ),
 409          'BETAINV' => array(
 410              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
 411              'functionCall' => 'PHPExcel_Calculation_Statistical::BETAINV',
 412              'argumentCount' => '3-5'
 413          ),
 414          'BIN2DEC' => array(
 415              'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
 416              'functionCall' => 'PHPExcel_Calculation_Engineering::BINTODEC',
 417              'argumentCount' => '1'
 418          ),
 419          'BIN2HEX' => array(
 420              'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
 421              'functionCall' => 'PHPExcel_Calculation_Engineering::BINTOHEX',
 422              'argumentCount' => '1,2'
 423          ),
 424          'BIN2OCT' => array(
 425              'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
 426              'functionCall' => 'PHPExcel_Calculation_Engineering::BINTOOCT',
 427              'argumentCount' => '1,2'
 428          ),
 429          'BINOMDIST' => array(
 430              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
 431              'functionCall' => 'PHPExcel_Calculation_Statistical::BINOMDIST',
 432              'argumentCount' => '4'
 433          ),
 434          'CEILING' => array(
 435              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
 436              'functionCall' => 'PHPExcel_Calculation_MathTrig::CEILING',
 437              'argumentCount' => '2'
 438          ),
 439          'CELL' => array(
 440              'category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
 441              'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
 442              'argumentCount' => '1,2'
 443          ),
 444          'CHAR' => array(
 445              'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
 446              'functionCall' => 'PHPExcel_Calculation_TextData::CHARACTER',
 447              'argumentCount' => '1'
 448          ),
 449          'CHIDIST' => array(
 450              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
 451              'functionCall' => 'PHPExcel_Calculation_Statistical::CHIDIST',
 452              'argumentCount' => '2'
 453          ),
 454          'CHIINV' => array(
 455              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
 456              'functionCall' => 'PHPExcel_Calculation_Statistical::CHIINV',
 457              'argumentCount' => '2'
 458          ),
 459          'CHITEST' => array(
 460              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
 461              'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
 462              'argumentCount' => '2'
 463          ),
 464          'CHOOSE' => array(
 465              'category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
 466              'functionCall' => 'PHPExcel_Calculation_LookupRef::CHOOSE',
 467              'argumentCount' => '2+'
 468          ),
 469          'CLEAN' => array(
 470              'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
 471              'functionCall' => 'PHPExcel_Calculation_TextData::TRIMNONPRINTABLE',
 472              'argumentCount' => '1'
 473          ),
 474          'CODE' => array(
 475              'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
 476              'functionCall' => 'PHPExcel_Calculation_TextData::ASCIICODE',
 477              'argumentCount' => '1'
 478          ),
 479          'COLUMN' => array(
 480              'category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
 481              'functionCall' => 'PHPExcel_Calculation_LookupRef::COLUMN',
 482              'argumentCount' => '-1',
 483              'passByReference' => array(true)
 484          ),
 485          'COLUMNS' => array(
 486              'category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
 487              'functionCall' => 'PHPExcel_Calculation_LookupRef::COLUMNS',
 488              'argumentCount' => '1'
 489          ),
 490          'COMBIN' => array(
 491              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
 492              'functionCall' => 'PHPExcel_Calculation_MathTrig::COMBIN',
 493              'argumentCount' => '2'
 494          ),
 495          'COMPLEX' => array(
 496              'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
 497              'functionCall' => 'PHPExcel_Calculation_Engineering::COMPLEX',
 498              'argumentCount' => '2,3'
 499          ),
 500          'CONCATENATE' => array(
 501              'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
 502              'functionCall' => 'PHPExcel_Calculation_TextData::CONCATENATE',
 503              'argumentCount' => '1+'
 504          ),
 505          'CONFIDENCE' => array(
 506              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
 507              'functionCall' => 'PHPExcel_Calculation_Statistical::CONFIDENCE',
 508              'argumentCount' => '3'
 509          ),
 510          'CONVERT' => array(
 511              'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
 512              'functionCall' => 'PHPExcel_Calculation_Engineering::CONVERTUOM',
 513              'argumentCount' => '3'
 514          ),
 515          'CORREL' => array(
 516              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
 517              'functionCall' => 'PHPExcel_Calculation_Statistical::CORREL',
 518              'argumentCount' => '2'
 519          ),
 520          'COS' => array(
 521              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
 522              'functionCall' => 'cos',
 523              'argumentCount' => '1'
 524          ),
 525          'COSH' => array(
 526              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
 527              'functionCall' => 'cosh',
 528              'argumentCount' => '1'
 529          ),
 530          'COUNT' => array(
 531              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
 532              'functionCall' => 'PHPExcel_Calculation_Statistical::COUNT',
 533              'argumentCount' => '1+'
 534          ),
 535          'COUNTA' => array(
 536              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
 537              'functionCall' => 'PHPExcel_Calculation_Statistical::COUNTA',
 538              'argumentCount' => '1+'
 539          ),
 540          'COUNTBLANK' => array(
 541              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
 542              'functionCall' => 'PHPExcel_Calculation_Statistical::COUNTBLANK',
 543              'argumentCount' => '1'
 544          ),
 545          'COUNTIF' => array(
 546              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
 547              'functionCall' => 'PHPExcel_Calculation_Statistical::COUNTIF',
 548              'argumentCount' => '2'
 549          ),
 550          'COUNTIFS' => array(
 551              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
 552              'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
 553              'argumentCount' => '2'
 554          ),
 555          'COUPDAYBS' => array(
 556              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
 557              'functionCall' => 'PHPExcel_Calculation_Financial::COUPDAYBS',
 558              'argumentCount' => '3,4'
 559          ),
 560          'COUPDAYS' => array(
 561              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
 562              'functionCall' => 'PHPExcel_Calculation_Financial::COUPDAYS',
 563              'argumentCount' => '3,4'
 564          ),
 565          'COUPDAYSNC' => array(
 566              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
 567              'functionCall' => 'PHPExcel_Calculation_Financial::COUPDAYSNC',
 568              'argumentCount' => '3,4'
 569          ),
 570          'COUPNCD' => array(
 571              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
 572              'functionCall' => 'PHPExcel_Calculation_Financial::COUPNCD',
 573              'argumentCount' => '3,4'
 574          ),
 575          'COUPNUM' => array(
 576              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
 577              'functionCall' => 'PHPExcel_Calculation_Financial::COUPNUM',
 578              'argumentCount' => '3,4'
 579          ),
 580          'COUPPCD' => array(
 581              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
 582              'functionCall' => 'PHPExcel_Calculation_Financial::COUPPCD',
 583              'argumentCount' => '3,4'
 584          ),
 585          'COVAR' => array(
 586              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
 587              'functionCall' => 'PHPExcel_Calculation_Statistical::COVAR',
 588              'argumentCount' => '2'
 589          ),
 590          'CRITBINOM' => array(
 591              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
 592              'functionCall' => 'PHPExcel_Calculation_Statistical::CRITBINOM',
 593              'argumentCount' => '3'
 594          ),
 595          'CUBEKPIMEMBER' => array(
 596              'category' => PHPExcel_Calculation_Function::CATEGORY_CUBE,
 597              'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
 598              'argumentCount' => '?'
 599          ),
 600          'CUBEMEMBER' => array(
 601              'category' => PHPExcel_Calculation_Function::CATEGORY_CUBE,
 602              'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
 603              'argumentCount' => '?'
 604          ),
 605          'CUBEMEMBERPROPERTY' => array(
 606              'category' => PHPExcel_Calculation_Function::CATEGORY_CUBE,
 607              'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
 608              'argumentCount' => '?'
 609          ),
 610          'CUBERANKEDMEMBER' => array(
 611              'category' => PHPExcel_Calculation_Function::CATEGORY_CUBE,
 612              'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
 613              'argumentCount' => '?'
 614          ),
 615          'CUBESET' => array(
 616              'category' => PHPExcel_Calculation_Function::CATEGORY_CUBE,
 617              'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
 618              'argumentCount' => '?'
 619          ),
 620          'CUBESETCOUNT' => array(
 621              'category' => PHPExcel_Calculation_Function::CATEGORY_CUBE,
 622              'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
 623              'argumentCount' => '?'
 624          ),
 625          'CUBEVALUE' => array(
 626              'category' => PHPExcel_Calculation_Function::CATEGORY_CUBE,
 627              'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
 628              'argumentCount' => '?'
 629          ),
 630          'CUMIPMT' => array(
 631              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
 632              'functionCall' => 'PHPExcel_Calculation_Financial::CUMIPMT',
 633              'argumentCount' => '6'
 634          ),
 635          'CUMPRINC' => array(
 636              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
 637              'functionCall' => 'PHPExcel_Calculation_Financial::CUMPRINC',
 638              'argumentCount' => '6'
 639          ),
 640          'DATE' => array(
 641              'category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
 642              'functionCall' => 'PHPExcel_Calculation_DateTime::DATE',
 643              'argumentCount' => '3'
 644          ),
 645          'DATEDIF' => array(
 646              'category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
 647              'functionCall' => 'PHPExcel_Calculation_DateTime::DATEDIF',
 648              'argumentCount' => '2,3'
 649          ),
 650          'DATEVALUE' => array(
 651              'category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
 652              'functionCall' => 'PHPExcel_Calculation_DateTime::DATEVALUE',
 653              'argumentCount' => '1'
 654          ),
 655          'DAVERAGE' => array(
 656              'category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE,
 657              'functionCall' => 'PHPExcel_Calculation_Database::DAVERAGE',
 658              'argumentCount' => '3'
 659          ),
 660          'DAY' => array(
 661              'category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
 662              'functionCall' => 'PHPExcel_Calculation_DateTime::DAYOFMONTH',
 663              'argumentCount' => '1'
 664          ),
 665          'DAYS360' => array(
 666              'category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
 667              'functionCall' => 'PHPExcel_Calculation_DateTime::DAYS360',
 668              'argumentCount' => '2,3'
 669          ),
 670          'DB' => array(
 671              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
 672              'functionCall' => 'PHPExcel_Calculation_Financial::DB',
 673              'argumentCount' => '4,5'
 674          ),
 675          'DCOUNT' => array(
 676              'category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE,
 677              'functionCall' => 'PHPExcel_Calculation_Database::DCOUNT',
 678              'argumentCount' => '3'
 679          ),
 680          'DCOUNTA' => array(
 681              'category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE,
 682              'functionCall' => 'PHPExcel_Calculation_Database::DCOUNTA',
 683              'argumentCount' => '3'
 684          ),
 685          'DDB' => array(
 686              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
 687              'functionCall' => 'PHPExcel_Calculation_Financial::DDB',
 688              'argumentCount' => '4,5'
 689          ),
 690          'DEC2BIN' => array(
 691              'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
 692              'functionCall' => 'PHPExcel_Calculation_Engineering::DECTOBIN',
 693              'argumentCount' => '1,2'
 694          ),
 695          'DEC2HEX' => array(
 696              'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
 697              'functionCall' => 'PHPExcel_Calculation_Engineering::DECTOHEX',
 698              'argumentCount' => '1,2'
 699          ),
 700          'DEC2OCT' => array(
 701              'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
 702              'functionCall' => 'PHPExcel_Calculation_Engineering::DECTOOCT',
 703              'argumentCount' => '1,2'
 704          ),
 705          'DEGREES' => array(
 706              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
 707              'functionCall' => 'rad2deg',
 708              'argumentCount' => '1'
 709          ),
 710          'DELTA' => array(
 711              'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
 712              'functionCall' => 'PHPExcel_Calculation_Engineering::DELTA',
 713              'argumentCount' => '1,2'
 714          ),
 715          'DEVSQ' => array(
 716              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
 717              'functionCall' => 'PHPExcel_Calculation_Statistical::DEVSQ',
 718              'argumentCount' => '1+'
 719          ),
 720          'DGET' => array(
 721              'category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE,
 722              'functionCall' => 'PHPExcel_Calculation_Database::DGET',
 723              'argumentCount' => '3'
 724          ),
 725          'DISC' => array(
 726              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
 727              'functionCall' => 'PHPExcel_Calculation_Financial::DISC',
 728              'argumentCount' => '4,5'
 729          ),
 730          'DMAX' => array(
 731              'category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE,
 732              'functionCall' => 'PHPExcel_Calculation_Database::DMAX',
 733              'argumentCount' => '3'
 734          ),
 735          'DMIN' => array(
 736              'category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE,
 737              'functionCall' => 'PHPExcel_Calculation_Database::DMIN',
 738              'argumentCount' => '3'
 739          ),
 740          'DOLLAR' => array(
 741              'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
 742              'functionCall' => 'PHPExcel_Calculation_TextData::DOLLAR',
 743              'argumentCount' => '1,2'
 744          ),
 745          'DOLLARDE' => array(
 746              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
 747              'functionCall' => 'PHPExcel_Calculation_Financial::DOLLARDE',
 748              'argumentCount' => '2'
 749          ),
 750          'DOLLARFR' => array(
 751              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
 752              'functionCall' => 'PHPExcel_Calculation_Financial::DOLLARFR',
 753              'argumentCount' => '2'
 754          ),
 755          'DPRODUCT' => array(
 756              'category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE,
 757              'functionCall' => 'PHPExcel_Calculation_Database::DPRODUCT',
 758              'argumentCount' => '3'
 759          ),
 760          'DSTDEV' => array(
 761              'category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE,
 762              'functionCall' => 'PHPExcel_Calculation_Database::DSTDEV',
 763              'argumentCount' => '3'
 764          ),
 765          'DSTDEVP' => array(
 766              'category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE,
 767              'functionCall' => 'PHPExcel_Calculation_Database::DSTDEVP',
 768              'argumentCount' => '3'
 769          ),
 770          'DSUM' => array(
 771              'category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE,
 772              'functionCall' => 'PHPExcel_Calculation_Database::DSUM',
 773              'argumentCount' => '3'
 774          ),
 775          'DURATION' => array(
 776              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
 777              'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
 778              'argumentCount' => '5,6'
 779          ),
 780          'DVAR' => array(
 781              'category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE,
 782              'functionCall' => 'PHPExcel_Calculation_Database::DVAR',
 783              'argumentCount' => '3'
 784          ),
 785          'DVARP' => array(
 786              'category' => PHPExcel_Calculation_Function::CATEGORY_DATABASE,
 787              'functionCall' => 'PHPExcel_Calculation_Database::DVARP',
 788              'argumentCount' => '3'
 789          ),
 790          'EDATE' => array(
 791              'category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
 792              'functionCall' => 'PHPExcel_Calculation_DateTime::EDATE',
 793              'argumentCount' => '2'
 794          ),
 795          'EFFECT' => array(
 796              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
 797              'functionCall' => 'PHPExcel_Calculation_Financial::EFFECT',
 798              'argumentCount' => '2'
 799          ),
 800          'EOMONTH' => array(
 801              'category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
 802              'functionCall' => 'PHPExcel_Calculation_DateTime::EOMONTH',
 803              'argumentCount' => '2'
 804          ),
 805          'ERF' => array(
 806              'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
 807              'functionCall' => 'PHPExcel_Calculation_Engineering::ERF',
 808              'argumentCount' => '1,2'
 809          ),
 810          'ERFC' => array(
 811              'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
 812              'functionCall' => 'PHPExcel_Calculation_Engineering::ERFC',
 813              'argumentCount' => '1'
 814          ),
 815          'ERROR.TYPE' => array(
 816              'category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
 817              'functionCall' => 'PHPExcel_Calculation_Functions::ERROR_TYPE',
 818              'argumentCount' => '1'
 819          ),
 820          'EVEN' => array(
 821              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
 822              'functionCall' => 'PHPExcel_Calculation_MathTrig::EVEN',
 823              'argumentCount' => '1'
 824          ),
 825          'EXACT' => array(
 826              'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
 827              'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
 828              'argumentCount' => '2'
 829          ),
 830          'EXP' => array(
 831              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
 832              'functionCall' => 'exp',
 833              'argumentCount' => '1'
 834          ),
 835          'EXPONDIST' => array(
 836              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
 837              'functionCall' => 'PHPExcel_Calculation_Statistical::EXPONDIST',
 838              'argumentCount' => '3'
 839          ),
 840          'FACT' => array(
 841              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
 842              'functionCall' => 'PHPExcel_Calculation_MathTrig::FACT',
 843              'argumentCount' => '1'
 844          ),
 845          'FACTDOUBLE' => array(
 846              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
 847              'functionCall' => 'PHPExcel_Calculation_MathTrig::FACTDOUBLE',
 848              'argumentCount' => '1'
 849          ),
 850          'FALSE' => array(
 851              'category' => PHPExcel_Calculation_Function::CATEGORY_LOGICAL,
 852              'functionCall' => 'PHPExcel_Calculation_Logical::FALSE',
 853              'argumentCount' => '0'
 854          ),
 855          'FDIST' => array(
 856              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
 857              'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
 858              'argumentCount' => '3'
 859          ),
 860          'FIND' => array(
 861              'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
 862              'functionCall' => 'PHPExcel_Calculation_TextData::SEARCHSENSITIVE',
 863              'argumentCount' => '2,3'
 864          ),
 865          'FINDB' => array(
 866              'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
 867              'functionCall' => 'PHPExcel_Calculation_TextData::SEARCHSENSITIVE',
 868              'argumentCount' => '2,3'
 869          ),
 870          'FINV' => array(
 871              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
 872              'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
 873              'argumentCount' => '3'
 874          ),
 875          'FISHER' => array(
 876              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
 877              'functionCall' => 'PHPExcel_Calculation_Statistical::FISHER',
 878              'argumentCount' => '1'
 879          ),
 880          'FISHERINV' => array(
 881              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
 882              'functionCall' => 'PHPExcel_Calculation_Statistical::FISHERINV',
 883              'argumentCount' => '1'
 884          ),
 885          'FIXED' => array(
 886              'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
 887              'functionCall' => 'PHPExcel_Calculation_TextData::FIXEDFORMAT',
 888              'argumentCount' => '1-3'
 889          ),
 890          'FLOOR' => array(
 891              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
 892              'functionCall' => 'PHPExcel_Calculation_MathTrig::FLOOR',
 893              'argumentCount' => '2'
 894          ),
 895          'FORECAST' => array(
 896              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
 897              'functionCall' => 'PHPExcel_Calculation_Statistical::FORECAST',
 898              'argumentCount' => '3'
 899          ),
 900          'FREQUENCY' => array(
 901              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
 902              'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
 903              'argumentCount' => '2'
 904          ),
 905          'FTEST' => array(
 906              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
 907              'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
 908              'argumentCount' => '2'
 909          ),
 910          'FV' => array(
 911              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
 912              'functionCall' => 'PHPExcel_Calculation_Financial::FV',
 913              'argumentCount' => '3-5'
 914          ),
 915          'FVSCHEDULE' => array(
 916              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
 917              'functionCall' => 'PHPExcel_Calculation_Financial::FVSCHEDULE',
 918              'argumentCount' => '2'
 919          ),
 920          'GAMMADIST' => array(
 921              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
 922              'functionCall' => 'PHPExcel_Calculation_Statistical::GAMMADIST',
 923              'argumentCount' => '4'
 924          ),
 925          'GAMMAINV' => array(
 926              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
 927              'functionCall' => 'PHPExcel_Calculation_Statistical::GAMMAINV',
 928              'argumentCount' => '3'
 929          ),
 930          'GAMMALN' => array(
 931              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
 932              'functionCall' => 'PHPExcel_Calculation_Statistical::GAMMALN',
 933              'argumentCount' => '1'
 934          ),
 935          'GCD' => array(
 936              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
 937              'functionCall' => 'PHPExcel_Calculation_MathTrig::GCD',
 938              'argumentCount' => '1+'
 939          ),
 940          'GEOMEAN' => array(
 941              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
 942              'functionCall' => 'PHPExcel_Calculation_Statistical::GEOMEAN',
 943              'argumentCount' => '1+'
 944          ),
 945          'GESTEP' => array(
 946              'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
 947              'functionCall' => 'PHPExcel_Calculation_Engineering::GESTEP',
 948              'argumentCount' => '1,2'
 949          ),
 950          'GETPIVOTDATA' => array(
 951              'category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
 952              'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
 953              'argumentCount' => '2+'
 954          ),
 955          'GROWTH' => array(
 956              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
 957              'functionCall' => 'PHPExcel_Calculation_Statistical::GROWTH',
 958              'argumentCount' => '1-4'
 959          ),
 960          'HARMEAN' => array(
 961              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
 962              'functionCall' => 'PHPExcel_Calculation_Statistical::HARMEAN',
 963              'argumentCount' => '1+'
 964          ),
 965          'HEX2BIN' => array(
 966              'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
 967              'functionCall' => 'PHPExcel_Calculation_Engineering::HEXTOBIN',
 968              'argumentCount' => '1,2'
 969          ),
 970          'HEX2DEC' => array(
 971              'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
 972              'functionCall' => 'PHPExcel_Calculation_Engineering::HEXTODEC',
 973              'argumentCount' => '1'
 974          ),
 975          'HEX2OCT' => array(
 976              'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
 977              'functionCall' => 'PHPExcel_Calculation_Engineering::HEXTOOCT',
 978              'argumentCount' => '1,2'
 979          ),
 980          'HLOOKUP' => array(
 981              'category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
 982              'functionCall' => 'PHPExcel_Calculation_LookupRef::HLOOKUP',
 983              'argumentCount' => '3,4'
 984          ),
 985          'HOUR' => array(
 986              'category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
 987              'functionCall' => 'PHPExcel_Calculation_DateTime::HOUROFDAY',
 988              'argumentCount' => '1'
 989          ),
 990          'HYPERLINK' => array(
 991              'category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
 992              'functionCall' => 'PHPExcel_Calculation_LookupRef::HYPERLINK',
 993              'argumentCount' => '1,2',
 994              'passCellReference' => true
 995          ),
 996          'HYPGEOMDIST' => array(
 997              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
 998              'functionCall' => 'PHPExcel_Calculation_Statistical::HYPGEOMDIST',
 999              'argumentCount' => '4'
1000          ),
1001          'IF' => array(
1002              'category' => PHPExcel_Calculation_Function::CATEGORY_LOGICAL,
1003              'functionCall' => 'PHPExcel_Calculation_Logical::STATEMENT_IF',
1004              'argumentCount' => '1-3'
1005          ),
1006          'IFERROR' => array(
1007              'category' => PHPExcel_Calculation_Function::CATEGORY_LOGICAL,
1008              'functionCall' => 'PHPExcel_Calculation_Logical::IFERROR',
1009              'argumentCount' => '2'
1010          ),
1011          'IMABS' => array(
1012              'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
1013              'functionCall' => 'PHPExcel_Calculation_Engineering::IMABS',
1014              'argumentCount' => '1'
1015          ),
1016          'IMAGINARY' => array(
1017              'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
1018              'functionCall' => 'PHPExcel_Calculation_Engineering::IMAGINARY',
1019              'argumentCount' => '1'
1020          ),
1021          'IMARGUMENT' => array(
1022              'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
1023              'functionCall' => 'PHPExcel_Calculation_Engineering::IMARGUMENT',
1024              'argumentCount' => '1'
1025          ),
1026          'IMCONJUGATE' => array(
1027              'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
1028              'functionCall' => 'PHPExcel_Calculation_Engineering::IMCONJUGATE',
1029              'argumentCount' => '1'
1030          ),
1031          'IMCOS' => array(
1032              'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
1033              'functionCall' => 'PHPExcel_Calculation_Engineering::IMCOS',
1034              'argumentCount' => '1'
1035          ),
1036          'IMDIV' => array(
1037              'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
1038              'functionCall' => 'PHPExcel_Calculation_Engineering::IMDIV',
1039              'argumentCount' => '2'
1040          ),
1041          'IMEXP' => array(
1042              'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
1043              'functionCall' => 'PHPExcel_Calculation_Engineering::IMEXP',
1044              'argumentCount' => '1'
1045          ),
1046          'IMLN' => array(
1047              'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
1048              'functionCall' => 'PHPExcel_Calculation_Engineering::IMLN',
1049              'argumentCount' => '1'
1050          ),
1051          'IMLOG10' => array(
1052              'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
1053              'functionCall' => 'PHPExcel_Calculation_Engineering::IMLOG10',
1054              'argumentCount' => '1'
1055          ),
1056          'IMLOG2' => array(
1057              'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
1058              'functionCall' => 'PHPExcel_Calculation_Engineering::IMLOG2',
1059              'argumentCount' => '1'
1060          ),
1061          'IMPOWER' => array(
1062              'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
1063              'functionCall' => 'PHPExcel_Calculation_Engineering::IMPOWER',
1064              'argumentCount' => '2'
1065          ),
1066          'IMPRODUCT' => array(
1067              'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
1068              'functionCall' => 'PHPExcel_Calculation_Engineering::IMPRODUCT',
1069              'argumentCount' => '1+'
1070          ),
1071          'IMREAL' => array(
1072              'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
1073              'functionCall' => 'PHPExcel_Calculation_Engineering::IMREAL',
1074              'argumentCount' => '1'
1075          ),
1076          'IMSIN' => array(
1077              'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
1078              'functionCall' => 'PHPExcel_Calculation_Engineering::IMSIN',
1079              'argumentCount' => '1'
1080          ),
1081          'IMSQRT' => array(
1082              'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
1083              'functionCall' => 'PHPExcel_Calculation_Engineering::IMSQRT',
1084              'argumentCount' => '1'
1085          ),
1086          'IMSUB' => array(
1087              'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
1088              'functionCall' => 'PHPExcel_Calculation_Engineering::IMSUB',
1089              'argumentCount' => '2'
1090          ),
1091          'IMSUM' => array(
1092              'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
1093              'functionCall' => 'PHPExcel_Calculation_Engineering::IMSUM',
1094              'argumentCount' => '1+'
1095          ),
1096          'INDEX' => array(
1097              'category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
1098              'functionCall' => 'PHPExcel_Calculation_LookupRef::INDEX',
1099              'argumentCount' => '1-4'
1100          ),
1101          'INDIRECT' => array(
1102              'category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
1103              'functionCall' => 'PHPExcel_Calculation_LookupRef::INDIRECT',
1104              'argumentCount' => '1,2',
1105              'passCellReference' => true
1106          ),
1107          'INFO' => array(
1108              'category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
1109              'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
1110              'argumentCount' => '1'
1111          ),
1112          'INT' => array(
1113              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
1114              'functionCall' => 'PHPExcel_Calculation_MathTrig::INT',
1115              'argumentCount' => '1'
1116          ),
1117          'INTERCEPT' => array(
1118              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
1119              'functionCall' => 'PHPExcel_Calculation_Statistical::INTERCEPT',
1120              'argumentCount' => '2'
1121          ),
1122          'INTRATE' => array(
1123              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
1124              'functionCall' => 'PHPExcel_Calculation_Financial::INTRATE',
1125              'argumentCount' => '4,5'
1126          ),
1127          'IPMT' => array(
1128              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
1129              'functionCall' => 'PHPExcel_Calculation_Financial::IPMT',
1130              'argumentCount' => '4-6'
1131          ),
1132          'IRR' => array(
1133              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
1134              'functionCall' => 'PHPExcel_Calculation_Financial::IRR',
1135              'argumentCount' => '1,2'
1136          ),
1137          'ISBLANK' => array(
1138              'category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
1139              'functionCall' => 'PHPExcel_Calculation_Functions::IS_BLANK',
1140              'argumentCount' => '1'
1141          ),
1142          'ISERR' => array(
1143              'category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
1144              'functionCall' => 'PHPExcel_Calculation_Functions::IS_ERR',
1145              'argumentCount' => '1'
1146          ),
1147          'ISERROR' => array(
1148              'category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
1149              'functionCall' => 'PHPExcel_Calculation_Functions::IS_ERROR',
1150              'argumentCount' => '1'
1151          ),
1152          'ISEVEN' => array(
1153              'category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
1154              'functionCall' => 'PHPExcel_Calculation_Functions::IS_EVEN',
1155              'argumentCount' => '1'
1156          ),
1157          'ISLOGICAL' => array(
1158              'category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
1159              'functionCall' => 'PHPExcel_Calculation_Functions::IS_LOGICAL',
1160              'argumentCount' => '1'
1161          ),
1162          'ISNA' => array(
1163              'category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
1164              'functionCall' => 'PHPExcel_Calculation_Functions::IS_NA',
1165              'argumentCount' => '1'
1166          ),
1167          'ISNONTEXT' => array(
1168              'category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
1169              'functionCall' => 'PHPExcel_Calculation_Functions::IS_NONTEXT',
1170              'argumentCount' => '1'
1171          ),
1172          'ISNUMBER' => array(
1173              'category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
1174              'functionCall' => 'PHPExcel_Calculation_Functions::IS_NUMBER',
1175              'argumentCount' => '1'
1176          ),
1177          'ISODD' => array(
1178              'category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
1179              'functionCall' => 'PHPExcel_Calculation_Functions::IS_ODD',
1180              'argumentCount' => '1'
1181          ),
1182          'ISPMT' => array(
1183              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
1184              'functionCall' => 'PHPExcel_Calculation_Financial::ISPMT',
1185              'argumentCount' => '4'
1186          ),
1187          'ISREF' => array(
1188              'category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
1189              'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
1190              'argumentCount' => '1'
1191          ),
1192          'ISTEXT' => array(
1193              'category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
1194              'functionCall' => 'PHPExcel_Calculation_Functions::IS_TEXT',
1195              'argumentCount' => '1'
1196          ),
1197          'JIS' => array(
1198              'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
1199              'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
1200              'argumentCount' => '1'
1201          ),
1202          'KURT' => array(
1203              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
1204              'functionCall' => 'PHPExcel_Calculation_Statistical::KURT',
1205              'argumentCount' => '1+'
1206          ),
1207          'LARGE' => array(
1208              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
1209              'functionCall' => 'PHPExcel_Calculation_Statistical::LARGE',
1210              'argumentCount' => '2'
1211          ),
1212          'LCM' => array(
1213              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
1214              'functionCall' => 'PHPExcel_Calculation_MathTrig::LCM',
1215              'argumentCount' => '1+'
1216          ),
1217          'LEFT' => array(
1218              'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
1219              'functionCall' => 'PHPExcel_Calculation_TextData::LEFT',
1220              'argumentCount' => '1,2'
1221          ),
1222          'LEFTB' => array(
1223              'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
1224              'functionCall' => 'PHPExcel_Calculation_TextData::LEFT',
1225              'argumentCount' => '1,2'
1226          ),
1227          'LEN' => array(
1228              'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
1229              'functionCall' => 'PHPExcel_Calculation_TextData::STRINGLENGTH',
1230              'argumentCount' => '1'
1231          ),
1232          'LENB' => array(
1233              'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
1234              'functionCall' => 'PHPExcel_Calculation_TextData::STRINGLENGTH',
1235              'argumentCount' => '1'
1236          ),
1237          'LINEST' => array(
1238              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
1239              'functionCall' => 'PHPExcel_Calculation_Statistical::LINEST',
1240              'argumentCount' => '1-4'
1241          ),
1242          'LN' => array(
1243              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
1244              'functionCall' => 'log',
1245              'argumentCount' => '1'
1246          ),
1247          'LOG' => array(
1248              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
1249              'functionCall' => 'PHPExcel_Calculation_MathTrig::LOG_BASE',
1250              'argumentCount' => '1,2'
1251          ),
1252          'LOG10' => array(
1253              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
1254              'functionCall' => 'log10',
1255              'argumentCount' => '1'
1256          ),
1257          'LOGEST' => array(
1258              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
1259              'functionCall' => 'PHPExcel_Calculation_Statistical::LOGEST',
1260              'argumentCount' => '1-4'
1261          ),
1262          'LOGINV' => array(
1263              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
1264              'functionCall' => 'PHPExcel_Calculation_Statistical::LOGINV',
1265              'argumentCount' => '3'
1266          ),
1267          'LOGNORMDIST' => array(
1268              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
1269              'functionCall' => 'PHPExcel_Calculation_Statistical::LOGNORMDIST',
1270              'argumentCount' => '3'
1271          ),
1272          'LOOKUP' => array(
1273              'category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
1274              'functionCall' => 'PHPExcel_Calculation_LookupRef::LOOKUP',
1275              'argumentCount' => '2,3'
1276          ),
1277          'LOWER' => array(
1278              'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
1279              'functionCall' => 'PHPExcel_Calculation_TextData::LOWERCASE',
1280              'argumentCount' => '1'
1281          ),
1282          'MATCH' => array(
1283              'category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
1284              'functionCall' => 'PHPExcel_Calculation_LookupRef::MATCH',
1285              'argumentCount' => '2,3'
1286          ),
1287          'MAX' => array(
1288              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
1289              'functionCall' => 'PHPExcel_Calculation_Statistical::MAX',
1290              'argumentCount' => '1+'
1291          ),
1292          'MAXA' => array(
1293              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
1294              'functionCall' => 'PHPExcel_Calculation_Statistical::MAXA',
1295              'argumentCount' => '1+'
1296          ),
1297          'MAXIF' => array(
1298              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
1299              'functionCall' => 'PHPExcel_Calculation_Statistical::MAXIF',
1300              'argumentCount' => '2+'
1301          ),
1302          'MDETERM' => array(
1303              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
1304              'functionCall' => 'PHPExcel_Calculation_MathTrig::MDETERM',
1305              'argumentCount' => '1'
1306          ),
1307          'MDURATION' => array(
1308              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
1309              'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
1310              'argumentCount' => '5,6'
1311          ),
1312          'MEDIAN' => array(
1313              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
1314              'functionCall' => 'PHPExcel_Calculation_Statistical::MEDIAN',
1315              'argumentCount' => '1+'
1316          ),
1317          'MEDIANIF' => array(
1318              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
1319              'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
1320              'argumentCount' => '2+'
1321          ),
1322          'MID' => array(
1323              'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
1324              'functionCall' => 'PHPExcel_Calculation_TextData::MID',
1325              'argumentCount' => '3'
1326          ),
1327          'MIDB' => array(
1328              'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
1329              'functionCall' => 'PHPExcel_Calculation_TextData::MID',
1330              'argumentCount' => '3'
1331          ),
1332          'MIN' => array(
1333              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
1334              'functionCall' => 'PHPExcel_Calculation_Statistical::MIN',
1335              'argumentCount' => '1+'
1336          ),
1337          'MINA' => array(
1338              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
1339              'functionCall' => 'PHPExcel_Calculation_Statistical::MINA',
1340              'argumentCount' => '1+'
1341          ),
1342          'MINIF' => array(
1343              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
1344              'functionCall' => 'PHPExcel_Calculation_Statistical::MINIF',
1345              'argumentCount' => '2+'
1346          ),
1347          'MINUTE' => array(
1348              'category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
1349              'functionCall' => 'PHPExcel_Calculation_DateTime::MINUTEOFHOUR',
1350              'argumentCount' => '1'
1351          ),
1352          'MINVERSE' => array(
1353              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
1354              'functionCall' => 'PHPExcel_Calculation_MathTrig::MINVERSE',
1355              'argumentCount' => '1'
1356          ),
1357          'MIRR' => array(
1358              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
1359              'functionCall' => 'PHPExcel_Calculation_Financial::MIRR',
1360              'argumentCount' => '3'
1361          ),
1362          'MMULT' => array(
1363              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
1364              'functionCall' => 'PHPExcel_Calculation_MathTrig::MMULT',
1365              'argumentCount' => '2'
1366          ),
1367          'MOD' => array(
1368              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
1369              'functionCall' => 'PHPExcel_Calculation_MathTrig::MOD',
1370              'argumentCount' => '2'
1371          ),
1372          'MODE' => array(
1373              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
1374              'functionCall' => 'PHPExcel_Calculation_Statistical::MODE',
1375              'argumentCount' => '1+'
1376          ),
1377          'MONTH' => array(
1378              'category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
1379              'functionCall' => 'PHPExcel_Calculation_DateTime::MONTHOFYEAR',
1380              'argumentCount' => '1'
1381          ),
1382          'MROUND' => array(
1383              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
1384              'functionCall' => 'PHPExcel_Calculation_MathTrig::MROUND',
1385              'argumentCount' => '2'
1386          ),
1387          'MULTINOMIAL' => array(
1388              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
1389              'functionCall' => 'PHPExcel_Calculation_MathTrig::MULTINOMIAL',
1390              'argumentCount' => '1+'
1391          ),
1392          'N' => array(
1393              'category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
1394              'functionCall' => 'PHPExcel_Calculation_Functions::N',
1395              'argumentCount' => '1'
1396          ),
1397          'NA' => array(
1398              'category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
1399              'functionCall' => 'PHPExcel_Calculation_Functions::NA',
1400              'argumentCount' => '0'
1401          ),
1402          'NEGBINOMDIST' => array(
1403              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
1404              'functionCall' => 'PHPExcel_Calculation_Statistical::NEGBINOMDIST',
1405              'argumentCount' => '3'
1406          ),
1407          'NETWORKDAYS' => array(
1408              'category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
1409              'functionCall' => 'PHPExcel_Calculation_DateTime::NETWORKDAYS',
1410              'argumentCount' => '2+'
1411          ),
1412          'NOMINAL' => array(
1413              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
1414              'functionCall' => 'PHPExcel_Calculation_Financial::NOMINAL',
1415              'argumentCount' => '2'
1416          ),
1417          'NORMDIST' => array(
1418              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
1419              'functionCall' => 'PHPExcel_Calculation_Statistical::NORMDIST',
1420              'argumentCount' => '4'
1421          ),
1422          'NORMINV' => array(
1423              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
1424              'functionCall' => 'PHPExcel_Calculation_Statistical::NORMINV',
1425              'argumentCount' => '3'
1426          ),
1427          'NORMSDIST' => array(
1428              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
1429              'functionCall' => 'PHPExcel_Calculation_Statistical::NORMSDIST',
1430              'argumentCount' => '1'
1431          ),
1432          'NORMSINV' => array(
1433              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
1434              'functionCall' => 'PHPExcel_Calculation_Statistical::NORMSINV',
1435              'argumentCount' => '1'
1436          ),
1437          'NOT' => array(
1438              'category' => PHPExcel_Calculation_Function::CATEGORY_LOGICAL,
1439              'functionCall' => 'PHPExcel_Calculation_Logical::NOT',
1440              'argumentCount' => '1'
1441          ),
1442          'NOW' => array(
1443              'category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
1444              'functionCall' => 'PHPExcel_Calculation_DateTime::DATETIMENOW',
1445              'argumentCount' => '0'
1446          ),
1447          'NPER' => array(
1448              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
1449              'functionCall' => 'PHPExcel_Calculation_Financial::NPER',
1450              'argumentCount' => '3-5'
1451          ),
1452          'NPV' => array(
1453              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
1454              'functionCall' => 'PHPExcel_Calculation_Financial::NPV',
1455              'argumentCount' => '2+'
1456          ),
1457          'OCT2BIN' => array(
1458              'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
1459              'functionCall' => 'PHPExcel_Calculation_Engineering::OCTTOBIN',
1460              'argumentCount' => '1,2'
1461          ),
1462          'OCT2DEC' => array(
1463              'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
1464              'functionCall' => 'PHPExcel_Calculation_Engineering::OCTTODEC',
1465              'argumentCount' => '1'
1466          ),
1467          'OCT2HEX' => array(
1468              'category' => PHPExcel_Calculation_Function::CATEGORY_ENGINEERING,
1469              'functionCall' => 'PHPExcel_Calculation_Engineering::OCTTOHEX',
1470              'argumentCount' => '1,2'
1471          ),
1472          'ODD' => array(
1473              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
1474              'functionCall' => 'PHPExcel_Calculation_MathTrig::ODD',
1475              'argumentCount' => '1'
1476          ),
1477          'ODDFPRICE' => array(
1478              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
1479              'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
1480              'argumentCount' => '8,9'
1481          ),
1482          'ODDFYIELD' => array(
1483              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
1484              'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
1485              'argumentCount' => '8,9'
1486          ),
1487          'ODDLPRICE' => array(
1488              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
1489              'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
1490              'argumentCount' => '7,8'
1491          ),
1492          'ODDLYIELD' => array(
1493              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
1494              'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
1495              'argumentCount' => '7,8'
1496          ),
1497          'OFFSET' => array(
1498              'category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
1499              'functionCall' => 'PHPExcel_Calculation_LookupRef::OFFSET',
1500              'argumentCount' => '3,5',
1501              'passCellReference' => true,
1502              'passByReference' => array(true)
1503          ),
1504          'OR' => array(
1505              'category' => PHPExcel_Calculation_Function::CATEGORY_LOGICAL,
1506              'functionCall' => 'PHPExcel_Calculation_Logical::LOGICAL_OR',
1507              'argumentCount' => '1+'
1508          ),
1509          'PEARSON' => array(
1510              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
1511              'functionCall' => 'PHPExcel_Calculation_Statistical::CORREL',
1512              'argumentCount' => '2'
1513          ),
1514          'PERCENTILE' => array(
1515              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
1516              'functionCall' => 'PHPExcel_Calculation_Statistical::PERCENTILE',
1517              'argumentCount' => '2'
1518          ),
1519          'PERCENTRANK' => array(
1520              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
1521              'functionCall' => 'PHPExcel_Calculation_Statistical::PERCENTRANK',
1522              'argumentCount' => '2,3'
1523          ),
1524          'PERMUT' => array(
1525              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
1526              'functionCall' => 'PHPExcel_Calculation_Statistical::PERMUT',
1527              'argumentCount' => '2'
1528          ),
1529          'PHONETIC' => array(
1530              'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
1531              'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
1532              'argumentCount' => '1'
1533          ),
1534          'PI' => array(
1535              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
1536              'functionCall' => 'pi',
1537              'argumentCount' => '0'
1538          ),
1539          'PMT' => array(
1540              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
1541              'functionCall' => 'PHPExcel_Calculation_Financial::PMT',
1542              'argumentCount' => '3-5'
1543          ),
1544          'POISSON' => array(
1545              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
1546              'functionCall' => 'PHPExcel_Calculation_Statistical::POISSON',
1547              'argumentCount' => '3'
1548          ),
1549          'POWER' => array(
1550              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
1551              'functionCall' => 'PHPExcel_Calculation_MathTrig::POWER',
1552              'argumentCount' => '2'
1553          ),
1554          'PPMT' => array(
1555              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
1556              'functionCall' => 'PHPExcel_Calculation_Financial::PPMT',
1557              'argumentCount' => '4-6'
1558          ),
1559          'PRICE' => array(
1560              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
1561              'functionCall' => 'PHPExcel_Calculation_Financial::PRICE',
1562              'argumentCount' => '6,7'
1563          ),
1564          'PRICEDISC' => array(
1565              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
1566              'functionCall' => 'PHPExcel_Calculation_Financial::PRICEDISC',
1567              'argumentCount' => '4,5'
1568          ),
1569          'PRICEMAT' => array(
1570              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
1571              'functionCall' => 'PHPExcel_Calculation_Financial::PRICEMAT',
1572              'argumentCount' => '5,6'
1573          ),
1574          'PROB' => array(
1575              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
1576              'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
1577              'argumentCount' => '3,4'
1578          ),
1579          'PRODUCT' => array(
1580              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
1581              'functionCall' => 'PHPExcel_Calculation_MathTrig::PRODUCT',
1582              'argumentCount' => '1+'
1583          ),
1584          'PROPER' => array(
1585              'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
1586              'functionCall' => 'PHPExcel_Calculation_TextData::PROPERCASE',
1587              'argumentCount' => '1'
1588          ),
1589          'PV' => array(
1590              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
1591              'functionCall' => 'PHPExcel_Calculation_Financial::PV',
1592              'argumentCount' => '3-5'
1593          ),
1594          'QUARTILE' => array(
1595              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
1596              'functionCall' => 'PHPExcel_Calculation_Statistical::QUARTILE',
1597              'argumentCount' => '2'
1598          ),
1599          'QUOTIENT' => array(
1600              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
1601              'functionCall' => 'PHPExcel_Calculation_MathTrig::QUOTIENT',
1602              'argumentCount' => '2'
1603          ),
1604          'RADIANS' => array(
1605              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
1606              'functionCall' => 'deg2rad',
1607              'argumentCount' => '1'
1608          ),
1609          'RAND' => array(
1610              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
1611              'functionCall' => 'PHPExcel_Calculation_MathTrig::RAND',
1612              'argumentCount' => '0'
1613          ),
1614          'RANDBETWEEN' => array(
1615              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
1616              'functionCall' => 'PHPExcel_Calculation_MathTrig::RAND',
1617              'argumentCount' => '2'
1618          ),
1619          'RANK' => array(
1620              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
1621              'functionCall' => 'PHPExcel_Calculation_Statistical::RANK',
1622              'argumentCount' => '2,3'
1623          ),
1624          'RATE' => array(
1625              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
1626              'functionCall' => 'PHPExcel_Calculation_Financial::RATE',
1627              'argumentCount' => '3-6'
1628          ),
1629          'RECEIVED' => array(
1630              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
1631              'functionCall' => 'PHPExcel_Calculation_Financial::RECEIVED',
1632              'argumentCount' => '4-5'
1633          ),
1634          'REPLACE' => array(
1635              'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
1636              'functionCall' => 'PHPExcel_Calculation_TextData::REPLACE',
1637              'argumentCount' => '4'
1638          ),
1639          'REPLACEB' => array(
1640              'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
1641              'functionCall' => 'PHPExcel_Calculation_TextData::REPLACE',
1642              'argumentCount' => '4'
1643          ),
1644          'REPT' => array(
1645              'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
1646              'functionCall' => 'str_repeat',
1647              'argumentCount' => '2'
1648          ),
1649          'RIGHT' => array(
1650              'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
1651              'functionCall' => 'PHPExcel_Calculation_TextData::RIGHT',
1652              'argumentCount' => '1,2'
1653          ),
1654          'RIGHTB' => array(
1655              'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
1656              'functionCall' => 'PHPExcel_Calculation_TextData::RIGHT',
1657              'argumentCount' => '1,2'
1658          ),
1659          'ROMAN' => array(
1660              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
1661              'functionCall' => 'PHPExcel_Calculation_MathTrig::ROMAN',
1662              'argumentCount' => '1,2'
1663          ),
1664          'ROUND' => array(
1665              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
1666              'functionCall' => 'round',
1667              'argumentCount' => '2'
1668          ),
1669          'ROUNDDOWN' => array(
1670              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
1671              'functionCall' => 'PHPExcel_Calculation_MathTrig::ROUNDDOWN',
1672              'argumentCount' => '2'
1673          ),
1674          'ROUNDUP' => array(
1675              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
1676              'functionCall' => 'PHPExcel_Calculation_MathTrig::ROUNDUP',
1677              'argumentCount' => '2'
1678          ),
1679          'ROW' => array(
1680              'category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
1681              'functionCall' => 'PHPExcel_Calculation_LookupRef::ROW',
1682              'argumentCount' => '-1',
1683              'passByReference' => array(true)
1684          ),
1685          'ROWS' => array(
1686              'category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
1687              'functionCall' => 'PHPExcel_Calculation_LookupRef::ROWS',
1688              'argumentCount' => '1'
1689          ),
1690          'RSQ' => array(
1691              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
1692              'functionCall' => 'PHPExcel_Calculation_Statistical::RSQ',
1693              'argumentCount' => '2'
1694          ),
1695          'RTD' => array(
1696              'category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
1697              'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
1698              'argumentCount' => '1+'
1699          ),
1700          'SEARCH' => array(
1701              'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
1702              'functionCall' => 'PHPExcel_Calculation_TextData::SEARCHINSENSITIVE',
1703              'argumentCount' => '2,3'
1704          ),
1705          'SEARCHB' => array(
1706              'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
1707              'functionCall' => 'PHPExcel_Calculation_TextData::SEARCHINSENSITIVE',
1708              'argumentCount' => '2,3'
1709          ),
1710          'SECOND' => array(
1711              'category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
1712              'functionCall' => 'PHPExcel_Calculation_DateTime::SECONDOFMINUTE',
1713              'argumentCount' => '1'
1714          ),
1715          'SERIESSUM' => array(
1716              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
1717              'functionCall' => 'PHPExcel_Calculation_MathTrig::SERIESSUM',
1718              'argumentCount' => '4'
1719          ),
1720          'SIGN' => array(
1721              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
1722              'functionCall' => 'PHPExcel_Calculation_MathTrig::SIGN',
1723              'argumentCount' => '1'
1724          ),
1725          'SIN' => array(
1726              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
1727              'functionCall' => 'sin',
1728              'argumentCount' => '1'
1729          ),
1730          'SINH' => array(
1731              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
1732              'functionCall' => 'sinh',
1733              'argumentCount' => '1'
1734          ),
1735          'SKEW' => array(
1736              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
1737              'functionCall' => 'PHPExcel_Calculation_Statistical::SKEW',
1738              'argumentCount' => '1+'
1739          ),
1740          'SLN' => array(
1741              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
1742              'functionCall' => 'PHPExcel_Calculation_Financial::SLN',
1743              'argumentCount' => '3'
1744          ),
1745          'SLOPE' => array(
1746              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
1747              'functionCall' => 'PHPExcel_Calculation_Statistical::SLOPE',
1748              'argumentCount' => '2'
1749          ),
1750          'SMALL' => array(
1751              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
1752              'functionCall' => 'PHPExcel_Calculation_Statistical::SMALL',
1753              'argumentCount' => '2'
1754          ),
1755          'SQRT' => array(
1756              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
1757              'functionCall' => 'sqrt',
1758              'argumentCount' => '1'
1759          ),
1760          'SQRTPI' => array(
1761              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
1762              'functionCall' => 'PHPExcel_Calculation_MathTrig::SQRTPI',
1763              'argumentCount' => '1'
1764          ),
1765          'STANDARDIZE' => array(
1766              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
1767              'functionCall' => 'PHPExcel_Calculation_Statistical::STANDARDIZE',
1768              'argumentCount' => '3'
1769          ),
1770          'STDEV' => array(
1771              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
1772              'functionCall' => 'PHPExcel_Calculation_Statistical::STDEV',
1773              'argumentCount' => '1+'
1774          ),
1775          'STDEVA' => array(
1776              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
1777              'functionCall' => 'PHPExcel_Calculation_Statistical::STDEVA',
1778              'argumentCount' => '1+'
1779          ),
1780          'STDEVP' => array(
1781              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
1782              'functionCall' => 'PHPExcel_Calculation_Statistical::STDEVP',
1783              'argumentCount' => '1+'
1784          ),
1785          'STDEVPA' => array(
1786              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
1787              'functionCall' => 'PHPExcel_Calculation_Statistical::STDEVPA',
1788              'argumentCount' => '1+'
1789          ),
1790          'STEYX' => array(
1791              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
1792              'functionCall' => 'PHPExcel_Calculation_Statistical::STEYX',
1793              'argumentCount' => '2'
1794          ),
1795          'SUBSTITUTE' => array(
1796              'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
1797              'functionCall' => 'PHPExcel_Calculation_TextData::SUBSTITUTE',
1798              'argumentCount' => '3,4'
1799          ),
1800          'SUBTOTAL' => array(
1801              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
1802              'functionCall' => 'PHPExcel_Calculation_MathTrig::SUBTOTAL',
1803              'argumentCount' => '2+'
1804          ),
1805          'SUM' => array(
1806              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
1807              'functionCall' => 'PHPExcel_Calculation_MathTrig::SUM',
1808              'argumentCount' => '1+'
1809          ),
1810          'SUMIF' => array(
1811              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
1812              'functionCall' => 'PHPExcel_Calculation_MathTrig::SUMIF',
1813              'argumentCount' => '2,3'
1814          ),
1815          'SUMIFS' => array(
1816              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
1817              'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
1818              'argumentCount' => '?'
1819          ),
1820          'SUMPRODUCT' => array(
1821              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
1822              'functionCall' => 'PHPExcel_Calculation_MathTrig::SUMPRODUCT',
1823              'argumentCount' => '1+'
1824          ),
1825          'SUMSQ' => array(
1826              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
1827              'functionCall' => 'PHPExcel_Calculation_MathTrig::SUMSQ',
1828              'argumentCount' => '1+'
1829          ),
1830          'SUMX2MY2' => array(
1831              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
1832              'functionCall' => 'PHPExcel_Calculation_MathTrig::SUMX2MY2',
1833              'argumentCount' => '2'
1834          ),
1835          'SUMX2PY2' => array(
1836              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
1837              'functionCall' => 'PHPExcel_Calculation_MathTrig::SUMX2PY2',
1838              'argumentCount' => '2'
1839          ),
1840          'SUMXMY2' => array(
1841              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
1842              'functionCall' => 'PHPExcel_Calculation_MathTrig::SUMXMY2',
1843              'argumentCount' => '2'
1844          ),
1845          'SYD' => array(
1846              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
1847              'functionCall' => 'PHPExcel_Calculation_Financial::SYD',
1848              'argumentCount' => '4'
1849          ),
1850          'T' => array(
1851              'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
1852              'functionCall' => 'PHPExcel_Calculation_TextData::RETURNSTRING',
1853              'argumentCount' => '1'
1854          ),
1855          'TAN' => array(
1856              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
1857              'functionCall' => 'tan',
1858              'argumentCount' => '1'
1859          ),
1860          'TANH' => array(
1861              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
1862              'functionCall' => 'tanh',
1863              'argumentCount' => '1'
1864          ),
1865          'TBILLEQ' => array(
1866              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
1867              'functionCall' => 'PHPExcel_Calculation_Financial::TBILLEQ',
1868              'argumentCount' => '3'
1869          ),
1870          'TBILLPRICE' => array(
1871              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
1872              'functionCall' => 'PHPExcel_Calculation_Financial::TBILLPRICE',
1873              'argumentCount' => '3'
1874          ),
1875          'TBILLYIELD' => array(
1876              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
1877              'functionCall' => 'PHPExcel_Calculation_Financial::TBILLYIELD',
1878              'argumentCount' => '3'
1879          ),
1880          'TDIST' => array(
1881              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
1882              'functionCall' => 'PHPExcel_Calculation_Statistical::TDIST',
1883              'argumentCount' => '3'
1884          ),
1885          'TEXT' => array(
1886              'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
1887              'functionCall' => 'PHPExcel_Calculation_TextData::TEXTFORMAT',
1888              'argumentCount' => '2'
1889          ),
1890          'TIME' => array(
1891              'category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
1892              'functionCall' => 'PHPExcel_Calculation_DateTime::TIME',
1893              'argumentCount' => '3'
1894          ),
1895          'TIMEVALUE' => array(
1896              'category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
1897              'functionCall' => 'PHPExcel_Calculation_DateTime::TIMEVALUE',
1898              'argumentCount' => '1'
1899          ),
1900          'TINV' => array(
1901              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
1902              'functionCall' => 'PHPExcel_Calculation_Statistical::TINV',
1903              'argumentCount' => '2'
1904          ),
1905          'TODAY' => array(
1906              'category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
1907              'functionCall' => 'PHPExcel_Calculation_DateTime::DATENOW',
1908              'argumentCount' => '0'
1909          ),
1910          'TRANSPOSE' => array(
1911              'category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
1912              'functionCall' => 'PHPExcel_Calculation_LookupRef::TRANSPOSE',
1913              'argumentCount' => '1'
1914          ),
1915          'TREND' => array(
1916              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
1917              'functionCall' => 'PHPExcel_Calculation_Statistical::TREND',
1918              'argumentCount' => '1-4'
1919          ),
1920          'TRIM' => array(
1921              'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
1922              'functionCall' => 'PHPExcel_Calculation_TextData::TRIMSPACES',
1923              'argumentCount' => '1'
1924          ),
1925          'TRIMMEAN' => array(
1926              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
1927              'functionCall' => 'PHPExcel_Calculation_Statistical::TRIMMEAN',
1928              'argumentCount' => '2'
1929          ),
1930          'TRUE' => array(
1931              'category' => PHPExcel_Calculation_Function::CATEGORY_LOGICAL,
1932              'functionCall' => 'PHPExcel_Calculation_Logical::TRUE',
1933              'argumentCount' => '0'
1934          ),
1935          'TRUNC' => array(
1936              'category' => PHPExcel_Calculation_Function::CATEGORY_MATH_AND_TRIG,
1937              'functionCall' => 'PHPExcel_Calculation_MathTrig::TRUNC',
1938              'argumentCount' => '1,2'
1939          ),
1940          'TTEST' => array(
1941              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
1942              'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
1943              'argumentCount' => '4'
1944          ),
1945          'TYPE' => array(
1946              'category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
1947              'functionCall' => 'PHPExcel_Calculation_Functions::TYPE',
1948              'argumentCount' => '1'
1949          ),
1950          'UPPER' => array(
1951              'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
1952              'functionCall' => 'PHPExcel_Calculation_TextData::UPPERCASE',
1953              'argumentCount' => '1'
1954          ),
1955          'USDOLLAR' => array(
1956              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
1957              'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
1958              'argumentCount' => '2'
1959          ),
1960          'VALUE' => array(
1961              'category' => PHPExcel_Calculation_Function::CATEGORY_TEXT_AND_DATA,
1962              'functionCall' => 'PHPExcel_Calculation_TextData::VALUE',
1963              'argumentCount' => '1'
1964          ),
1965          'VAR' => array(
1966              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
1967              'functionCall' => 'PHPExcel_Calculation_Statistical::VARFunc',
1968              'argumentCount' => '1+'
1969          ),
1970          'VARA' => array(
1971              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
1972              'functionCall' => 'PHPExcel_Calculation_Statistical::VARA',
1973              'argumentCount' => '1+'
1974          ),
1975          'VARP' => array(
1976              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
1977              'functionCall' => 'PHPExcel_Calculation_Statistical::VARP',
1978              'argumentCount' => '1+'
1979          ),
1980          'VARPA' => array(
1981              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
1982              'functionCall' => 'PHPExcel_Calculation_Statistical::VARPA',
1983              'argumentCount' => '1+'
1984          ),
1985          'VDB' => array(
1986              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
1987              'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
1988              'argumentCount' => '5-7'
1989          ),
1990          'VERSION' => array(
1991              'category' => PHPExcel_Calculation_Function::CATEGORY_INFORMATION,
1992              'functionCall' => 'PHPExcel_Calculation_Functions::VERSION',
1993              'argumentCount' => '0'
1994          ),
1995          'VLOOKUP' => array(
1996              'category' => PHPExcel_Calculation_Function::CATEGORY_LOOKUP_AND_REFERENCE,
1997              'functionCall' => 'PHPExcel_Calculation_LookupRef::VLOOKUP',
1998              'argumentCount' => '3,4'
1999          ),
2000          'WEEKDAY' => array(
2001              'category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
2002              'functionCall' => 'PHPExcel_Calculation_DateTime::DAYOFWEEK',
2003              'argumentCount' => '1,2'
2004          ),
2005          'WEEKNUM' => array(
2006              'category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
2007              'functionCall' => 'PHPExcel_Calculation_DateTime::WEEKOFYEAR',
2008              'argumentCount' => '1,2'
2009          ),
2010          'WEIBULL' => array(
2011              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
2012              'functionCall' => 'PHPExcel_Calculation_Statistical::WEIBULL',
2013              'argumentCount' => '4'
2014          ),
2015          'WORKDAY' => array(
2016              'category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
2017              'functionCall' => 'PHPExcel_Calculation_DateTime::WORKDAY',
2018              'argumentCount' => '2+'
2019          ),
2020          'XIRR' => array(
2021              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
2022              'functionCall' => 'PHPExcel_Calculation_Financial::XIRR',
2023              'argumentCount' => '2,3'
2024          ),
2025          'XNPV' => array(
2026              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
2027              'functionCall' => 'PHPExcel_Calculation_Financial::XNPV',
2028              'argumentCount' => '3'
2029          ),
2030          'YEAR' => array(
2031              'category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
2032              'functionCall' => 'PHPExcel_Calculation_DateTime::YEAR',
2033              'argumentCount' => '1'
2034          ),
2035          'YEARFRAC' => array(
2036              'category' => PHPExcel_Calculation_Function::CATEGORY_DATE_AND_TIME,
2037              'functionCall' => 'PHPExcel_Calculation_DateTime::YEARFRAC',
2038              'argumentCount' => '2,3'
2039          ),
2040          'YIELD' => array(
2041              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
2042              'functionCall' => 'PHPExcel_Calculation_Functions::DUMMY',
2043              'argumentCount' => '6,7'
2044          ),
2045          'YIELDDISC' => array(
2046              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
2047              'functionCall' => 'PHPExcel_Calculation_Financial::YIELDDISC',
2048              'argumentCount' => '4,5'
2049          ),
2050          'YIELDMAT' => array(
2051              'category' => PHPExcel_Calculation_Function::CATEGORY_FINANCIAL,
2052              'functionCall' => 'PHPExcel_Calculation_Financial::YIELDMAT',
2053              'argumentCount' => '5,6'
2054          ),
2055          'ZTEST' => array(
2056              'category' => PHPExcel_Calculation_Function::CATEGORY_STATISTICAL,
2057              'functionCall' => 'PHPExcel_Calculation_Statistical::ZTEST',
2058              'argumentCount' => '2-3'
2059          )
2060      );
2061  
2062      //    Internal functions used for special control purposes
2063      private static $controlFunctions = array(
2064          'MKMATRIX' => array(
2065              'argumentCount' => '*',
2066              'functionCall' => 'self::mkMatrix'
2067          )
2068      );
2069  
2070  
2071      private function __construct(PHPExcel $workbook = null)
2072      {
2073          $setPrecision = (PHP_INT_SIZE == 4) ? 14 : 16;
2074          $this->savedPrecision = ini_get('precision');
2075          if ($this->savedPrecision < $setPrecision) {
2076              ini_set('precision', $setPrecision);
2077          }
2078          $this->delta = 1 * pow(10, -$setPrecision);
2079  
2080          if ($workbook !== null) {
2081              self::$workbookSets[$workbook->getID()] = $this;
2082          }
2083  
2084          $this->workbook = $workbook;
2085          $this->cyclicReferenceStack = new PHPExcel_CalcEngine_CyclicReferenceStack();
2086          $this->_debugLog = new PHPExcel_CalcEngine_Logger($this->cyclicReferenceStack);
2087      }
2088  
2089  
2090      public function __destruct()
2091      {
2092          if ($this->savedPrecision != ini_get('precision')) {
2093              ini_set('precision', $this->savedPrecision);
2094          }
2095      }
2096  
2097      private static function loadLocales()
2098      {
2099          $localeFileDirectory = PHPEXCEL_ROOT.'PHPExcel/locale/';
2100          foreach (glob($localeFileDirectory.'/*', GLOB_ONLYDIR) as $filename) {
2101              $filename = substr($filename, strlen($localeFileDirectory)+1);
2102              if ($filename != 'en') {
2103                  self::$validLocaleLanguages[] = $filename;
2104              }
2105          }
2106      }
2107  
2108      /**
2109       * Get an instance of this class
2110       *
2111       * @access    public
2112       * @param   PHPExcel $workbook  Injected workbook for working with a PHPExcel object,
2113       *                                    or NULL to create a standalone claculation engine
2114       * @return PHPExcel_Calculation
2115       */
2116      public static function getInstance(PHPExcel $workbook = null)
2117      {
2118          if ($workbook !== null) {
2119              if (isset(self::$workbookSets[$workbook->getID()])) {
2120                  return self::$workbookSets[$workbook->getID()];
2121              }
2122              return new PHPExcel_Calculation($workbook);
2123          }
2124  
2125          if (!isset(self::$instance) || (self::$instance === null)) {
2126              self::$instance = new PHPExcel_Calculation();
2127          }
2128  
2129          return self::$instance;
2130      }
2131  
2132      /**
2133       * Unset an instance of this class
2134       *
2135       * @access    public
2136       * @param   PHPExcel $workbook  Injected workbook identifying the instance to unset
2137       */
2138      public static function unsetInstance(PHPExcel $workbook = null)
2139      {
2140          if ($workbook !== null) {
2141              if (isset(self::$workbookSets[$workbook->getID()])) {
2142                  unset(self::$workbookSets[$workbook->getID()]);
2143              }
2144          }
2145      }
2146  
2147      /**
2148       * Flush the calculation cache for any existing instance of this class
2149       *        but only if a PHPExcel_Calculation instance exists
2150       *
2151       * @access    public
2152       * @return null
2153       */
2154      public function flushInstance()
2155      {
2156          $this->clearCalculationCache();
2157      }
2158  
2159  
2160      /**
2161       * Get the debuglog for this claculation engine instance
2162       *
2163       * @access    public
2164       * @return PHPExcel_CalcEngine_Logger
2165       */
2166      public function getDebugLog()
2167      {
2168          return $this->_debugLog;
2169      }
2170  
2171      /**
2172       * __clone implementation. Cloning should not be allowed in a Singleton!
2173       *
2174       * @access    public
2175       * @throws    PHPExcel_Calculation_Exception
2176       */
2177      final public function __clone()
2178      {
2179          throw new PHPExcel_Calculation_Exception('Cloning the calculation engine is not allowed!');
2180      }
2181  
2182  
2183      /**
2184       * Return the locale-specific translation of TRUE
2185       *
2186       * @access    public
2187       * @return     string        locale-specific translation of TRUE
2188       */
2189      public static function getTRUE()
2190      {
2191          return self::$localeBoolean['TRUE'];
2192      }
2193  
2194      /**
2195       * Return the locale-specific translation of FALSE
2196       *
2197       * @access    public
2198       * @return     string        locale-specific translation of FALSE
2199       */
2200      public static function getFALSE()
2201      {
2202          return self::$localeBoolean['FALSE'];
2203      }
2204  
2205      /**
2206       * Set the Array Return Type (Array or Value of first element in the array)
2207       *
2208       * @access    public
2209       * @param     string    $returnType            Array return type
2210       * @return     boolean                    Success or failure
2211       */
2212      public static function setArrayReturnType($returnType)
2213      {
2214          if (($returnType == self::RETURN_ARRAY_AS_VALUE) ||
2215              ($returnType == self::RETURN_ARRAY_AS_ERROR) ||
2216              ($returnType == self::RETURN_ARRAY_AS_ARRAY)) {
2217              self::$returnArrayAsType = $returnType;
2218              return true;
2219          }
2220          return false;
2221      }
2222  
2223  
2224      /**
2225       * Return the Array Return Type (Array or Value of first element in the array)
2226       *
2227       * @access    public
2228       * @return     string        $returnType            Array return type
2229       */
2230      public static function getArrayReturnType()
2231      {
2232          return self::$returnArrayAsType;
2233      }
2234  
2235  
2236      /**
2237       * Is calculation caching enabled?
2238       *
2239       * @access    public
2240       * @return boolean
2241       */
2242      public function getCalculationCacheEnabled()
2243      {
2244          return $this->calculationCacheEnabled;
2245      }
2246  
2247      /**
2248       * Enable/disable calculation cache
2249       *
2250       * @access    public
2251       * @param boolean $pValue
2252       */
2253      public function setCalculationCacheEnabled($pValue = true)
2254      {
2255          $this->calculationCacheEnabled = $pValue;
2256          $this->clearCalculationCache();
2257      }
2258  
2259  
2260      /**
2261       * Enable calculation cache
2262       */
2263      public function enableCalculationCache()
2264      {
2265          $this->setCalculationCacheEnabled(true);
2266      }
2267  
2268  
2269      /**
2270       * Disable calculation cache
2271       */
2272      public function disableCalculationCache()
2273      {
2274          $this->setCalculationCacheEnabled(false);
2275      }
2276  
2277  
2278      /**
2279       * Clear calculation cache
2280       */
2281      public function clearCalculationCache()
2282      {
2283          $this->calculationCache = array();
2284      }
2285  
2286      /**
2287       * Clear calculation cache for a specified worksheet
2288       *
2289       * @param string $worksheetName
2290       */
2291      public function clearCalculationCacheForWorksheet($worksheetName)
2292      {
2293          if (isset($this->calculationCache[$worksheetName])) {
2294              unset($this->calculationCache[$worksheetName]);
2295          }
2296      }
2297  
2298      /**
2299       * Rename calculation cache for a specified worksheet
2300       *
2301       * @param string $fromWorksheetName
2302       * @param string $toWorksheetName
2303       */
2304      public function renameCalculationCacheForWorksheet($fromWorksheetName, $toWorksheetName)
2305      {
2306          if (isset($this->calculationCache[$fromWorksheetName])) {
2307              $this->calculationCache[$toWorksheetName] = &$this->calculationCache[$fromWorksheetName];
2308              unset($this->calculationCache[$fromWorksheetName]);
2309          }
2310      }
2311  
2312  
2313      /**
2314       * Get the currently defined locale code
2315       *
2316       * @return string
2317       */
2318      public function getLocale()
2319      {
2320          return self::$localeLanguage;
2321      }
2322  
2323  
2324      /**
2325       * Set the locale code
2326       *
2327       * @param string $locale  The locale to use for formula translation
2328       * @return boolean
2329       */
2330      public function setLocale($locale = 'en_us')
2331      {
2332          //    Identify our locale and language
2333          $language = $locale = strtolower($locale);
2334          if (strpos($locale, '_') !== false) {
2335              list($language) = explode('_', $locale);
2336          }
2337  
2338          if (count(self::$validLocaleLanguages) == 1) {
2339              self::loadLocales();
2340          }
2341          //    Test whether we have any language data for this language (any locale)
2342          if (in_array($language, self::$validLocaleLanguages)) {
2343              //    initialise language/locale settings
2344              self::$localeFunctions = array();
2345              self::$localeArgumentSeparator = ',';
2346              self::$localeBoolean = array('TRUE' => 'TRUE', 'FALSE' => 'FALSE', 'NULL' => 'NULL');
2347              //    Default is English, if user isn't requesting english, then read the necessary data from the locale files
2348              if ($locale != 'en_us') {
2349                  //    Search for a file with a list of function names for locale
2350                  $functionNamesFile = PHPEXCEL_ROOT . 'PHPExcel'.DIRECTORY_SEPARATOR.'locale'.DIRECTORY_SEPARATOR.str_replace('_', DIRECTORY_SEPARATOR, $locale).DIRECTORY_SEPARATOR.'functions';
2351                  if (!file_exists($functionNamesFile)) {
2352                      //    If there isn't a locale specific function file, look for a language specific function file
2353                      $functionNamesFile = PHPEXCEL_ROOT . 'PHPExcel'.DIRECTORY_SEPARATOR.'locale'.DIRECTORY_SEPARATOR.$language.DIRECTORY_SEPARATOR.'functions';
2354                      if (!file_exists($functionNamesFile)) {
2355                          return false;
2356                      }
2357                  }
2358                  //    Retrieve the list of locale or language specific function names
2359                  $localeFunctions = file($functionNamesFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
2360                  foreach ($localeFunctions as $localeFunction) {
2361                      list($localeFunction) = explode('##', $localeFunction);    //    Strip out comments
2362                      if (strpos($localeFunction, '=') !== false) {
2363                          list($fName, $lfName) = explode('=', $localeFunction);
2364                          $fName = trim($fName);
2365                          $lfName = trim($lfName);
2366                          if ((isset(self::$PHPExcelFunctions[$fName])) && ($lfName != '') && ($fName != $lfName)) {
2367                              self::$localeFunctions[$fName] = $lfName;
2368                          }
2369                      }
2370                  }
2371                  //    Default the TRUE and FALSE constants to the locale names of the TRUE() and FALSE() functions
2372                  if (isset(self::$localeFunctions['TRUE'])) {
2373                      self::$localeBoolean['TRUE'] = self::$localeFunctions['TRUE'];
2374                  }
2375                  if (isset(self::$localeFunctions['FALSE'])) {
2376                      self::$localeBoolean['FALSE'] = self::$localeFunctions['FALSE'];
2377                  }
2378  
2379                  $configFile = PHPEXCEL_ROOT . 'PHPExcel'.DIRECTORY_SEPARATOR.'locale'.DIRECTORY_SEPARATOR.str_replace('_', DIRECTORY_SEPARATOR, $locale).DIRECTORY_SEPARATOR.'config';
2380                  if (!file_exists($configFile)) {
2381                      $configFile = PHPEXCEL_ROOT . 'PHPExcel'.DIRECTORY_SEPARATOR.'locale'.DIRECTORY_SEPARATOR.$language.DIRECTORY_SEPARATOR.'config';
2382                  }
2383                  if (file_exists($configFile)) {
2384                      $localeSettings = file($configFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
2385                      foreach ($localeSettings as $localeSetting) {
2386                          list($localeSetting) = explode('##', $localeSetting);    //    Strip out comments
2387                          if (strpos($localeSetting, '=') !== false) {
2388                              list($settingName, $settingValue) = explode('=', $localeSetting);
2389                              $settingName = strtoupper(trim($settingName));
2390                              switch ($settingName) {
2391                                  case 'ARGUMENTSEPARATOR':
2392                                      self::$localeArgumentSeparator = trim($settingValue);
2393                                      break;
2394                              }
2395                          }
2396                      }
2397                  }
2398              }
2399  
2400              self::$functionReplaceFromExcel = self::$functionReplaceToExcel =
2401              self::$functionReplaceFromLocale = self::$functionReplaceToLocale = null;
2402              self::$localeLanguage = $locale;
2403              return true;
2404          }
2405          return false;
2406      }
2407  
2408  
2409  
2410      public static function translateSeparator($fromSeparator, $toSeparator, $formula, &$inBraces)
2411      {
2412          $strlen = mb_strlen($formula);
2413          for ($i = 0; $i < $strlen; ++$i) {
2414              $chr = mb_substr($formula, $i, 1);
2415              switch ($chr) {
2416                  case '{':
2417                      $inBraces = true;
2418                      break;
2419                  case '}':
2420                      $inBraces = false;
2421                      break;
2422                  case $fromSeparator:
2423                      if (!$inBraces) {
2424                          $formula = mb_substr($formula, 0, $i).$toSeparator.mb_substr($formula, $i+1);
2425                      }
2426              }
2427          }
2428          return $formula;
2429      }
2430  
2431      private static function translateFormula($from, $to, $formula, $fromSeparator, $toSeparator)
2432      {
2433          //    Convert any Excel function names to the required language
2434          if (self::$localeLanguage !== 'en_us') {
2435              $inBraces = false;
2436              //    If there is the possibility of braces within a quoted string, then we don't treat those as matrix indicators
2437              if (strpos($formula, '"') !== false) {
2438                  //    So instead we skip replacing in any quoted strings by only replacing in every other array element after we've exploded
2439                  //        the formula
2440                  $temp = explode('"', $formula);
2441                  $i = false;
2442                  foreach ($temp as &$value) {
2443                      //    Only count/replace in alternating array entries
2444                      if ($i = !$i) {
2445                          $value = preg_replace($from, $to, $value);
2446                          $value = self::translateSeparator($fromSeparator, $toSeparator, $value, $inBraces);
2447                      }
2448                  }
2449                  unset($value);
2450                  //    Then rebuild the formula string
2451                  $formula = implode('"', $temp);
2452              } else {
2453                  //    If there's no quoted strings, then we do a simple count/replace
2454                  $formula = preg_replace($from, $to, $formula);
2455                  $formula = self::translateSeparator($fromSeparator, $toSeparator, $formula, $inBraces);
2456              }
2457          }
2458  
2459          return $formula;
2460      }
2461  
2462      private static $functionReplaceFromExcel = null;
2463      private static $functionReplaceToLocale  = null;
2464  
2465      public function _translateFormulaToLocale($formula)
2466      {
2467          if (self::$functionReplaceFromExcel === null) {
2468              self::$functionReplaceFromExcel = array();
2469              foreach (array_keys(self::$localeFunctions) as $excelFunctionName) {
2470                  self::$functionReplaceFromExcel[] = '/(@?[^\w\.])'.preg_quote($excelFunctionName).'([\s]*\()/Ui';
2471              }
2472              foreach (array_keys(self::$localeBoolean) as $excelBoolean) {
2473                  self::$functionReplaceFromExcel[] = '/(@?[^\w\.])'.preg_quote($excelBoolean).'([^\w\.])/Ui';
2474              }
2475  
2476          }
2477  
2478          if (self::$functionReplaceToLocale === null) {
2479              self::$functionReplaceToLocale = array();
2480              foreach (array_values(self::$localeFunctions) as $localeFunctionName) {
2481                  self::$functionReplaceToLocale[] = '$1'.trim($localeFunctionName).'$2';
2482              }
2483              foreach (array_values(self::$localeBoolean) as $localeBoolean) {
2484                  self::$functionReplaceToLocale[] = '$1'.trim($localeBoolean).'$2';
2485              }
2486          }
2487  
2488          return self::translateFormula(self::$functionReplaceFromExcel, self::$functionReplaceToLocale, $formula, ',', self::$localeArgumentSeparator);
2489      }
2490  
2491  
2492      private static $functionReplaceFromLocale = null;
2493      private static $functionReplaceToExcel    = null;
2494  
2495      public function _translateFormulaToEnglish($formula)
2496      {
2497          if (self::$functionReplaceFromLocale === null) {
2498              self::$functionReplaceFromLocale = array();
2499              foreach (array_values(self::$localeFunctions) as $localeFunctionName) {
2500                  self::$functionReplaceFromLocale[] = '/(@?[^\w\.])'.preg_quote($localeFunctionName).'([\s]*\()/Ui';
2501              }
2502              foreach (array_values(self::$localeBoolean) as $excelBoolean) {
2503                  self::$functionReplaceFromLocale[] = '/(@?[^\w\.])'.preg_quote($excelBoolean).'([^\w\.])/Ui';
2504              }
2505          }
2506  
2507          if (self::$functionReplaceToExcel === null) {
2508              self::$functionReplaceToExcel = array();
2509              foreach (array_keys(self::$localeFunctions) as $excelFunctionName) {
2510                  self::$functionReplaceToExcel[] = '$1'.trim($excelFunctionName).'$2';
2511              }
2512              foreach (array_keys(self::$localeBoolean) as $excelBoolean) {
2513                  self::$functionReplaceToExcel[] = '$1'.trim($excelBoolean).'$2';
2514              }
2515          }
2516  
2517          return self::translateFormula(self::$functionReplaceFromLocale, self::$functionReplaceToExcel, $formula, self::$localeArgumentSeparator, ',');
2518      }
2519  
2520  
2521      public static function localeFunc($function)
2522      {
2523          if (self::$localeLanguage !== 'en_us') {
2524              $functionName = trim($function, '(');
2525              if (isset(self::$localeFunctions[$functionName])) {
2526                  $brace = ($functionName != $function);
2527                  $function = self::$localeFunctions[$functionName];
2528                  if ($brace) {
2529                      $function .= '(';
2530                  }
2531              }
2532          }
2533          return $function;
2534      }
2535  
2536  
2537  
2538  
2539      /**
2540       * Wrap string values in quotes
2541       *
2542       * @param mixed $value
2543       * @return mixed
2544       */
2545      public static function wrapResult($value)
2546      {
2547          if (is_string($value)) {
2548              //    Error values cannot be "wrapped"
2549              if (preg_match('/^'.self::CALCULATION_REGEXP_ERROR.'$/i', $value, $match)) {
2550                  //    Return Excel errors "as is"
2551                  return $value;
2552              }
2553              //    Return strings wrapped in quotes
2554              return '"'.$value.'"';
2555          //    Convert numeric errors to NaN error
2556          } elseif ((is_float($value)) && ((is_nan($value)) || (is_infinite($value)))) {
2557              return PHPExcel_Calculation_Functions::NaN();
2558          }
2559  
2560          return $value;
2561      }
2562  
2563  
2564      /**
2565       * Remove quotes used as a wrapper to identify string values
2566       *
2567       * @param mixed $value
2568       * @return mixed
2569       */
2570      public static function unwrapResult($value)
2571      {
2572          if (is_string($value)) {
2573              if ((isset($value{0})) && ($value{0} == '"') && (substr($value, -1) == '"')) {
2574                  return substr($value, 1, -1);
2575              }
2576          //    Convert numeric errors to NaN error
2577          } elseif ((is_float($value)) && ((is_nan($value)) || (is_infinite($value)))) {
2578              return PHPExcel_Calculation_Functions::NaN();
2579          }
2580          return $value;
2581      }
2582  
2583  
2584  
2585  
2586      /**
2587       * Calculate cell value (using formula from a cell ID)
2588       * Retained for backward compatibility
2589       *
2590       * @access    public
2591       * @param    PHPExcel_Cell    $pCell    Cell to calculate
2592       * @return    mixed
2593       * @throws    PHPExcel_Calculation_Exception
2594       */
2595      public function calculate(PHPExcel_Cell $pCell = null)
2596      {
2597          try {
2598              return $this->calculateCellValue($pCell);
2599          } catch (PHPExcel_Exception $e) {
2600              throw new PHPExcel_Calculation_Exception($e->getMessage());
2601          }
2602      }
2603  
2604  
2605      /**
2606       * Calculate the value of a cell formula
2607       *
2608       * @access    public
2609       * @param    PHPExcel_Cell    $pCell        Cell to calculate
2610       * @param    Boolean            $resetLog    Flag indicating whether the debug log should be reset or not
2611       * @return    mixed
2612       * @throws    PHPExcel_Calculation_Exception
2613       */
2614      public function calculateCellValue(PHPExcel_Cell $pCell = null, $resetLog = true)
2615      {
2616          if ($pCell === null) {
2617              return null;
2618          }
2619  
2620          $returnArrayAsType = self::$returnArrayAsType;
2621          if ($resetLog) {
2622              //    Initialise the logging settings if requested
2623              $this->formulaError = null;
2624              $this->_debugLog->clearLog();
2625              $this->cyclicReferenceStack->clear();
2626              $this->cyclicFormulaCounter = 1;
2627  
2628              self::$returnArrayAsType = self::RETURN_ARRAY_AS_ARRAY;
2629          }
2630  
2631          //    Execute the calculation for the cell formula
2632          $this->cellStack[] = array(
2633              'sheet' => $pCell->getWorksheet()->getTitle(),
2634              'cell' => $pCell->getCoordinate(),
2635          );
2636          try {
2637              $result = self::unwrapResult($this->_calculateFormulaValue($pCell->getValue(), $pCell->getCoordinate(), $pCell));
2638              $cellAddress = array_pop($this->cellStack);
2639              $this->workbook->getSheetByName($cellAddress['sheet'])->getCell($cellAddress['cell']);
2640          } catch (PHPExcel_Exception $e) {
2641              $cellAddress = array_pop($this->cellStack);
2642              $this->workbook->getSheetByName($cellAddress['sheet'])->getCell($cellAddress['cell']);
2643              throw new PHPExcel_Calculation_Exception($e->getMessage());
2644          }
2645  
2646          if ((is_array($result)) && (self::$returnArrayAsType != self::RETURN_ARRAY_AS_ARRAY)) {
2647              self::$returnArrayAsType = $returnArrayAsType;
2648              $testResult = PHPExcel_Calculation_Functions::flattenArray($result);
2649              if (self::$returnArrayAsType == self::RETURN_ARRAY_AS_ERROR) {
2650                  return PHPExcel_Calculation_Functions::VALUE();
2651              }
2652              //    If there's only a single cell in the array, then we allow it
2653              if (count($testResult) != 1) {
2654                  //    If keys are numeric, then it's a matrix result rather than a cell range result, so we permit it
2655                  $r = array_keys($result);
2656                  $r = array_shift($r);
2657                  if (!is_numeric($r)) {
2658                      return PHPExcel_Calculation_Functions::VALUE();
2659                  }
2660                  if (is_array($result[$r])) {
2661                      $c = array_keys($result[$r]);
2662                      $c = array_shift($c);
2663                      if (!is_numeric($c)) {
2664                          return PHPExcel_Calculation_Functions::VALUE();
2665                      }
2666                  }
2667              }
2668              $result = array_shift($testResult);
2669          }
2670          self::$returnArrayAsType = $returnArrayAsType;
2671  
2672  
2673          if ($result === null) {
2674              return 0;
2675          } elseif ((is_float($result)) && ((is_nan($result)) || (is_infinite($result)))) {
2676              return PHPExcel_Calculation_Functions::NaN();
2677          }
2678          return $result;
2679      }
2680  
2681  
2682      /**
2683       * Validate and parse a formula string
2684       *
2685       * @param    string        $formula        Formula to parse
2686       * @return    array
2687       * @throws    PHPExcel_Calculation_Exception
2688       */
2689      public function parseFormula($formula)
2690      {
2691          //    Basic validation that this is indeed a formula
2692          //    We return an empty array if not
2693          $formula = trim($formula);
2694          if ((!isset($formula{0})) || ($formula{0} != '=')) {
2695              return array();
2696          }
2697          $formula = ltrim(substr($formula, 1));
2698          if (!isset($formula{0})) {
2699              return array();
2700          }
2701  
2702          //    Parse the formula and return the token stack
2703          return $this->_parseFormula($formula);
2704      }
2705  
2706  
2707      /**
2708       * Calculate the value of a formula
2709       *
2710       * @param    string            $formula    Formula to parse
2711       * @param    string            $cellID        Address of the cell to calculate
2712       * @param    PHPExcel_Cell    $pCell        Cell to calculate
2713       * @return    mixed
2714       * @throws    PHPExcel_Calculation_Exception
2715       */
2716      public function calculateFormula($formula, $cellID = null, PHPExcel_Cell $pCell = null)
2717      {
2718          //    Initialise the logging settings
2719          $this->formulaError = null;
2720          $this->_debugLog->clearLog();
2721          $this->cyclicReferenceStack->clear();
2722  
2723          if ($this->workbook !== null && $cellID === null && $pCell === null) {
2724              $cellID = 'A1';
2725              $pCell = $this->workbook->getActiveSheet()->getCell($cellID);
2726          } else {
2727              //    Disable calculation cacheing because it only applies to cell calculations, not straight formulae
2728              //    But don't actually flush any cache
2729              $resetCache = $this->getCalculationCacheEnabled();
2730              $this->calculationCacheEnabled = false;
2731          }
2732  
2733          //    Execute the calculation
2734          try {
2735              $result = self::unwrapResult($this->_calculateFormulaValue($formula, $cellID, $pCell));
2736          } catch (PHPExcel_Exception $e) {
2737              throw new PHPExcel_Calculation_Exception($e->getMessage());
2738          }
2739  
2740          if ($this->workbook === null) {
2741              //    Reset calculation cacheing to its previous state
2742              $this->calculationCacheEnabled = $resetCache;
2743          }
2744  
2745          return $result;
2746      }
2747  
2748  
2749      public function getValueFromCache($cellReference, &$cellValue)
2750      {
2751          // Is calculation cacheing enabled?
2752          // Is the value present in calculation cache?
2753          $this->_debugLog->writeDebugLog('Testing cache value for cell ', $cellReference);
2754          if (($this->calculationCacheEnabled) && (isset($this->calculationCache[$cellReference]))) {
2755              $this->_debugLog->writeDebugLog('Retrieving value for cell ', $cellReference, ' from cache');
2756              // Return the cached result
2757              $cellValue = $this->calculationCache[$cellReference];
2758              return true;
2759          }
2760          return false;
2761      }
2762  
2763      public function saveValueToCache($cellReference, $cellValue)
2764      {
2765          if ($this->calculationCacheEnabled) {
2766              $this->calculationCache[$cellReference] = $cellValue;
2767          }
2768      }
2769  
2770      /**
2771       * Parse a cell formula and calculate its value
2772       *
2773       * @param    string            $formula    The formula to parse and calculate
2774       * @param    string            $cellID        The ID (e.g. A3) of the cell that we are calculating
2775       * @param    PHPExcel_Cell    $pCell        Cell to calculate
2776       * @return    mixed
2777       * @throws    PHPExcel_Calculation_Exception
2778       */
2779      public function _calculateFormulaValue($formula, $cellID = null, PHPExcel_Cell $pCell = null)
2780      {
2781          $cellValue = null;
2782  
2783          //    Basic validation that this is indeed a formula
2784          //    We simply return the cell value if not
2785          $formula = trim($formula);
2786          if ($formula{0} != '=') {
2787              return self::wrapResult($formula);
2788          }
2789          $formula = ltrim(substr($formula, 1));
2790          if (!isset($formula{0})) {
2791              return self::wrapResult($formula);
2792          }
2793  
2794          $pCellParent = ($pCell !== null) ? $pCell->getWorksheet() : null;
2795          $wsTitle = ($pCellParent !== null) ? $pCellParent->getTitle() : "\x00Wrk";
2796          $wsCellReference = $wsTitle . '!' . $cellID;
2797  
2798          if (($cellID !== null) && ($this->getValueFromCache($wsCellReference, $cellValue))) {
2799              return $cellValue;
2800          }
2801  
2802          if (($wsTitle{0} !== "\x00") && ($this->cyclicReferenceStack->onStack($wsCellReference))) {
2803              if ($this->cyclicFormulaCount <= 0) {
2804                  $this->cyclicFormulaCell = '';
2805                  return $this->raiseFormulaError('Cyclic Reference in Formula');
2806              } elseif ($this->cyclicFormulaCell === $wsCellReference) {
2807                  ++$this->cyclicFormulaCounter;
2808                  if ($this->cyclicFormulaCounter >= $this->cyclicFormulaCount) {
2809                      $this->cyclicFormulaCell = '';
2810                      return $cellValue;
2811                  }
2812              } elseif ($this->cyclicFormulaCell == '') {
2813                  if ($this->cyclicFormulaCounter >= $this->cyclicFormulaCount) {
2814                      return $cellValue;
2815                  }
2816                  $this->cyclicFormulaCell = $wsCellReference;
2817              }
2818          }
2819  
2820          //    Parse the formula onto the token stack and calculate the value
2821          $this->cyclicReferenceStack->push($wsCellReference);
2822          $cellValue = $this->processTokenStack($this->_parseFormula($formula, $pCell), $cellID, $pCell);
2823          $this->cyclicReferenceStack->pop();
2824  
2825          // Save to calculation cache
2826          if ($cellID !== null) {
2827              $this->saveValueToCache($wsCellReference, $cellValue);
2828          }
2829  
2830          //    Return the calculated value
2831          return $cellValue;
2832      }
2833  
2834  
2835      /**
2836       * Ensure that paired matrix operands are both matrices and of the same size
2837       *
2838       * @param    mixed        &$operand1    First matrix operand
2839       * @param    mixed        &$operand2    Second matrix operand
2840       * @param    integer        $resize        Flag indicating whether the matrices should be resized to match
2841       *                                        and (if so), whether the smaller dimension should grow or the
2842       *                                        larger should shrink.
2843       *                                            0 = no resize
2844       *                                            1 = shrink to fit
2845       *                                            2 = extend to fit
2846       */
2847      private static function checkMatrixOperands(&$operand1, &$operand2, $resize = 1)
2848      {
2849          //    Examine each of the two operands, and turn them into an array if they aren't one already
2850          //    Note that this function should only be called if one or both of the operand is already an array
2851          if (!is_array($operand1)) {
2852              list($matrixRows, $matrixColumns) = self::getMatrixDimensions($operand2);
2853              $operand1 = array_fill(0, $matrixRows, array_fill(0, $matrixColumns, $operand1));
2854              $resize = 0;
2855          } elseif (!is_array($operand2)) {
2856              list($matrixRows, $matrixColumns) = self::getMatrixDimensions($operand1);
2857              $operand2 = array_fill(0, $matrixRows, array_fill(0, $matrixColumns, $operand2));
2858              $resize = 0;
2859          }
2860  
2861          list($matrix1Rows, $matrix1Columns) = self::getMatrixDimensions($operand1);
2862          list($matrix2Rows, $matrix2Columns) = self::getMatrixDimensions($operand2);
2863          if (($matrix1Rows == $matrix2Columns) && ($matrix2Rows == $matrix1Columns)) {
2864              $resize = 1;
2865          }
2866  
2867          if ($resize == 2) {
2868              //    Given two matrices of (potentially) unequal size, convert the smaller in each dimension to match the larger
2869              self::resizeMatricesExtend($operand1, $operand2, $matrix1Rows, $matrix1Columns, $matrix2Rows, $matrix2Columns);
2870          } elseif ($resize == 1) {
2871              //    Given two matrices of (potentially) unequal size, convert the larger in each dimension to match the smaller
2872              self::resizeMatricesShrink($operand1, $operand2, $matrix1Rows, $matrix1Columns, $matrix2Rows, $matrix2Columns);
2873          }
2874          return array( $matrix1Rows, $matrix1Columns, $matrix2Rows, $matrix2Columns);
2875      }
2876  
2877  
2878      /**
2879       * Read the dimensions of a matrix, and re-index it with straight numeric keys starting from row 0, column 0
2880       *
2881       * @param    mixed        &$matrix        matrix operand
2882       * @return    array        An array comprising the number of rows, and number of columns
2883       */
2884      private static function getMatrixDimensions(&$matrix)
2885      {
2886          $matrixRows = count($matrix);
2887          $matrixColumns = 0;
2888          foreach ($matrix as $rowKey => $rowValue) {
2889              $matrixColumns = max(count($rowValue), $matrixColumns);
2890              if (!is_array($rowValue)) {
2891                  $matrix[$rowKey] = array($rowValue);
2892              } else {
2893                  $matrix[$rowKey] = array_values($rowValue);
2894              }
2895          }
2896          $matrix = array_values($matrix);
2897          return array($matrixRows, $matrixColumns);
2898      }
2899  
2900  
2901      /**
2902       * Ensure that paired matrix operands are both matrices of the same size
2903       *
2904       * @param    mixed        &$matrix1        First matrix operand
2905       * @param    mixed        &$matrix2        Second matrix operand
2906       * @param    integer        $matrix1Rows    Row size of first matrix operand
2907       * @param    integer        $matrix1Columns    Column size of first matrix operand
2908       * @param    integer        $matrix2Rows    Row size of second matrix operand
2909       * @param    integer        $matrix2Columns    Column size of second matrix operand
2910       */
2911      private static function resizeMatricesShrink(&$matrix1, &$matrix2, $matrix1Rows, $matrix1Columns, $matrix2Rows, $matrix2Columns)
2912      {
2913          if (($matrix2Columns < $matrix1Columns) || ($matrix2Rows < $matrix1Rows)) {
2914              if ($matrix2Rows < $matrix1Rows) {
2915                  for ($i = $matrix2Rows; $i < $matrix1Rows; ++$i) {
2916                      unset($matrix1[$i]);
2917                  }
2918              }
2919              if ($matrix2Columns < $matrix1Columns) {
2920                  for ($i = 0; $i < $matrix1Rows; ++$i) {
2921                      for ($j = $matrix2Columns; $j < $matrix1Columns; ++$j) {
2922                          unset($matrix1[$i][$j]);
2923                      }
2924                  }
2925              }
2926          }
2927  
2928          if (($matrix1Columns < $matrix2Columns) || ($matrix1Rows < $matrix2Rows)) {
2929              if ($matrix1Rows < $matrix2Rows) {
2930                  for ($i = $matrix1Rows; $i < $matrix2Rows; ++$i) {
2931                      unset($matrix2[$i]);
2932                  }
2933              }
2934              if ($matrix1Columns < $matrix2Columns) {
2935                  for ($i = 0; $i < $matrix2Rows; ++$i) {
2936                      for ($j = $matrix1Columns; $j < $matrix2Columns; ++$j) {
2937                          unset($matrix2[$i][$j]);
2938                      }
2939                  }
2940              }
2941          }
2942      }
2943  
2944  
2945      /**
2946       * Ensure that paired matrix operands are both matrices of the same size
2947       *
2948       * @param    mixed        &$matrix1    First matrix operand
2949       * @param    mixed        &$matrix2    Second matrix operand
2950       * @param    integer        $matrix1Rows    Row size of first matrix operand
2951       * @param    integer        $matrix1Columns    Column size of first matrix operand
2952       * @param    integer        $matrix2Rows    Row size of second matrix operand
2953       * @param    integer        $matrix2Columns    Column size of second matrix operand
2954       */
2955      private static function resizeMatricesExtend(&$matrix1, &$matrix2, $matrix1Rows, $matrix1Columns, $matrix2Rows, $matrix2Columns)
2956      {
2957          if (($matrix2Columns < $matrix1Columns) || ($matrix2Rows < $matrix1Rows)) {
2958              if ($matrix2Columns < $matrix1Columns) {
2959                  for ($i = 0; $i < $matrix2Rows; ++$i) {
2960                      $x = $matrix2[$i][$matrix2Columns-1];
2961                      for ($j = $matrix2Columns; $j < $matrix1Columns; ++$j) {
2962                          $matrix2[$i][$j] = $x;
2963                      }
2964                  }
2965              }
2966              if ($matrix2Rows < $matrix1Rows) {
2967                  $x = $matrix2[$matrix2Rows-1];
2968                  for ($i = 0; $i < $matrix1Rows; ++$i) {
2969                      $matrix2[$i] = $x;
2970                  }
2971              }
2972          }
2973  
2974          if (($matrix1Columns < $matrix2Columns) || ($matrix1Rows < $matrix2Rows)) {
2975              if ($matrix1Columns < $matrix2Columns) {
2976                  for ($i = 0; $i < $matrix1Rows; ++$i) {
2977                      $x = $matrix1[$i][$matrix1Columns-1];
2978                      for ($j = $matrix1Columns; $j < $matrix2Columns; ++$j) {
2979                          $matrix1[$i][$j] = $x;
2980                      }
2981                  }
2982              }
2983              if ($matrix1Rows < $matrix2Rows) {
2984                  $x = $matrix1[$matrix1Rows-1];
2985                  for ($i = 0; $i < $matrix2Rows; ++$i) {
2986                      $matrix1[$i] = $x;
2987                  }
2988              }
2989          }
2990      }
2991  
2992  
2993      /**
2994       * Format details of an operand for display in the log (based on operand type)
2995       *
2996       * @param    mixed        $value    First matrix operand
2997       * @return    mixed
2998       */
2999      private function showValue($value)
3000      {
3001          if ($this->_debugLog->getWriteDebugLog()) {
3002              $testArray = PHPExcel_Calculation_Functions::flattenArray($value);
3003              if (count($testArray) == 1) {
3004                  $value = array_pop($testArray);
3005              }
3006  
3007              if (is_array($value)) {
3008                  $returnMatrix = array();
3009                  $pad = $rpad = ', ';
3010                  foreach ($value as $row) {
3011                      if (is_array($row)) {
3012                          $returnMatrix[] = implode($pad, array_map(array($this, 'showValue'), $row));
3013                          $rpad = '; ';
3014                      } else {
3015                          $returnMatrix[] = $this->showValue($row);
3016                      }
3017                  }
3018                  return '{ '.implode($rpad, $returnMatrix).' }';
3019              } elseif (is_string($value) && (trim($value, '"') == $value)) {
3020                  return '"'.$value.'"';
3021              } elseif (is_bool($value)) {
3022                  return ($value) ? self::$localeBoolean['TRUE'] : self::$localeBoolean['FALSE'];
3023              }
3024          }
3025          return PHPExcel_Calculation_Functions::flattenSingleValue($value);
3026      }
3027  
3028  
3029      /**
3030       * Format type and details of an operand for display in the log (based on operand type)
3031       *
3032       * @param    mixed        $value    First matrix operand
3033       * @return    mixed
3034       */
3035      private function showTypeDetails($value)
3036      {
3037          if ($this->_debugLog->getWriteDebugLog()) {
3038              $testArray = PHPExcel_Calculation_Functions::flattenArray($value);
3039              if (count($testArray) == 1) {
3040                  $value = array_pop($testArray);
3041              }
3042  
3043              if ($value === null) {
3044                  return 'a NULL value';
3045              } elseif (is_float($value)) {
3046                  $typeString = 'a floating point number';
3047              } elseif (is_int($value)) {
3048                  $typeString = 'an integer number';
3049              } elseif (is_bool($value)) {
3050                  $typeString = 'a boolean';
3051              } elseif (is_array($value)) {
3052                  $typeString = 'a matrix';
3053              } else {
3054                  if ($value == '') {
3055                      return 'an empty string';
3056                  } elseif ($value{0} == '#') {
3057                      return 'a '.$value.' error';
3058                  } else {
3059                      $typeString = 'a string';
3060                  }
3061              }
3062              return $typeString.' with a value of '.$this->showValue($value);
3063          }
3064      }
3065  
3066  
3067      private function convertMatrixReferences($formula)
3068      {
3069          static $matrixReplaceFrom = array('{', ';', '}');
3070          static $matrixReplaceTo = array('MKMATRIX(MKMATRIX(', '),MKMATRIX(', '))');
3071  
3072          //    Convert any Excel matrix references to the MKMATRIX() function
3073          if (strpos($formula, '{') !== false) {
3074              //    If there is the possibility of braces within a quoted string, then we don't treat those as matrix indicators
3075              if (strpos($formula, '"') !== false) {
3076                  //    So instead we skip replacing in any quoted strings by only replacing in every other array element after we've exploded
3077                  //        the formula
3078                  $temp = explode('"', $formula);
3079                  //    Open and Closed counts used for trapping mismatched braces in the formula
3080                  $openCount = $closeCount = 0;
3081                  $i = false;
3082                  foreach ($temp as &$value) {
3083                      //    Only count/replace in alternating array entries
3084                      if ($i = !$i) {
3085                          $openCount += substr_count($value, '{');
3086                          $closeCount += substr_count($value, '}');
3087                          $value = str_replace($matrixReplaceFrom, $matrixReplaceTo, $value);
3088                      }
3089                  }
3090                  unset($value);
3091                  //    Then rebuild the formula string
3092                  $formula = implode('"', $temp);
3093              } else {
3094                  //    If there's no quoted strings, then we do a simple count/replace
3095                  $openCount = substr_count($formula, '{');
3096                  $closeCount = substr_count($formula, '}');
3097                  $formula = str_replace($matrixReplaceFrom, $matrixReplaceTo, $formula);
3098              }
3099              //    Trap for mismatched braces and trigger an appropriate error
3100              if ($openCount < $closeCount) {
3101                  if ($openCount > 0) {
3102                      return $this->raiseFormulaError("Formula Error: Mismatched matrix braces '}'");
3103                  } else {
3104                      return $this->raiseFormulaError("Formula Error: Unexpected '}' encountered");
3105                  }
3106              } elseif ($openCount > $closeCount) {
3107                  if ($closeCount > 0) {
3108                      return $this->raiseFormulaError("Formula Error: Mismatched matrix braces '{'");
3109                  } else {
3110                      return $this->raiseFormulaError("Formula Error: Unexpected '{' encountered");
3111                  }
3112              }
3113          }
3114  
3115          return $formula;
3116      }
3117  
3118  
3119      private static function mkMatrix()
3120      {
3121          return func_get_args();
3122      }
3123  
3124  
3125      //    Binary Operators
3126      //    These operators always work on two values
3127      //    Array key is the operator, the value indicates whether this is a left or right associative operator
3128      private static $operatorAssociativity    = array(
3129          '^' => 0,                                                            //    Exponentiation
3130          '*' => 0, '/' => 0,                                                 //    Multiplication and Division
3131          '+' => 0, '-' => 0,                                                    //    Addition and Subtraction
3132          '&' => 0,                                                            //    Concatenation
3133          '|' => 0, ':' => 0,                                                    //    Intersect and Range
3134          '>' => 0, '<' => 0, '=' => 0, '>=' => 0, '<=' => 0, '<>' => 0        //    Comparison
3135      );
3136  
3137      //    Comparison (Boolean) Operators
3138      //    These operators work on two values, but always return a boolean result
3139      private static $comparisonOperators    = array('>' => true, '<' => true, '=' => true, '>=' => true, '<=' => true, '<>' => true);
3140  
3141      //    Operator Precedence
3142      //    This list includes all valid operators, whether binary (including boolean) or unary (such as %)
3143      //    Array key is the operator, the value is its precedence
3144      private static $operatorPrecedence    = array(
3145          ':' => 8,                                                                //    Range
3146          '|' => 7,                                                                //    Intersect
3147          '~' => 6,                                                                //    Negation
3148          '%' => 5,                                                                //    Percentage
3149          '^' => 4,                                                                //    Exponentiation
3150          '*' => 3, '/' => 3,                                                     //    Multiplication and Division
3151          '+' => 2, '-' => 2,                                                        //    Addition and Subtraction
3152          '&' => 1,                                                                //    Concatenation
3153          '>' => 0, '<' => 0, '=' => 0, '>=' => 0, '<=' => 0, '<>' => 0            //    Comparison
3154      );
3155  
3156      // Convert infix to postfix notation
3157      private function _parseFormula($formula, PHPExcel_Cell $pCell = null)
3158      {
3159          if (($formula = $this->convertMatrixReferences(trim($formula))) === false) {
3160              return false;
3161          }
3162  
3163          //    If we're using cell caching, then $pCell may well be flushed back to the cache (which detaches the parent worksheet),
3164          //        so we store the parent worksheet so that we can re-attach it when necessary
3165          $pCellParent = ($pCell !== null) ? $pCell->getWorksheet() : null;
3166  
3167          $regexpMatchString = '/^('.self::CALCULATION_REGEXP_FUNCTION.
3168                                 '|'.self::CALCULATION_REGEXP_CELLREF.
3169                                 '|'.self::CALCULATION_REGEXP_NUMBER.
3170                                 '|'.self::CALCULATION_REGEXP_STRING.
3171                                 '|'.self::CALCULATION_REGEXP_OPENBRACE.
3172                                 '|'.self::CALCULATION_REGEXP_NAMEDRANGE.
3173                                 '|'.self::CALCULATION_REGEXP_ERROR.
3174                               ')/si';
3175  
3176          //    Start with initialisation
3177          $index = 0;
3178          $stack = new PHPExcel_Calculation_Token_Stack;
3179          $output = array();
3180          $expectingOperator = false;                    //    We use this test in syntax-checking the expression to determine when a
3181                                                      //        - is a negation or + is a positive operator rather than an operation
3182          $expectingOperand = false;                    //    We use this test in syntax-checking the expression to determine whether an operand
3183                                                      //        should be null in a function call
3184          //    The guts of the lexical parser
3185          //    Loop through the formula extracting each operator and operand in turn
3186          while (true) {
3187  //echo 'Assessing Expression '.substr($formula, $index), PHP_EOL;
3188              $opCharacter = $formula{$index};    //    Get the first character of the value at the current index position
3189  //echo 'Initial character of expression block is '.$opCharacter, PHP_EOL;
3190              if ((isset(self::$comparisonOperators[$opCharacter])) && (strlen($formula) > $index) && (isset(self::$comparisonOperators[$formula{$index+1}]))) {
3191                  $opCharacter .= $formula{++$index};
3192  //echo 'Initial character of expression block is comparison operator '.$opCharacter.PHP_EOL;
3193              }
3194  
3195              //    Find out if we're currently at the beginning of a number, variable, cell reference, function, parenthesis or operand
3196              $isOperandOrFunction = preg_match($regexpMatchString, substr($formula, $index), $match);
3197  //echo '$isOperandOrFunction is '.(($isOperandOrFunction) ? 'True' : 'False').PHP_EOL;
3198  //var_dump($match);
3199  
3200              if ($opCharacter == '-' && !$expectingOperator) {                //    Is it a negation instead of a minus?
3201  //echo 'Element is a Negation operator', PHP_EOL;
3202                  $stack->push('Unary Operator', '~');                            //    Put a negation on the stack
3203                  ++$index;                                                    //        and drop the negation symbol
3204              } elseif ($opCharacter == '%' && $expectingOperator) {
3205  //echo 'Element is a Percentage operator', PHP_EOL;
3206                  $stack->push('Unary Operator', '%');                            //    Put a percentage on the stack
3207                  ++$index;
3208              } elseif ($opCharacter == '+' && !$expectingOperator) {            //    Positive (unary plus rather than binary operator plus) can be discarded?
3209  //echo 'Element is a Positive number, not Plus operator', PHP_EOL;
3210                  ++$index;                                                    //    Drop the redundant plus symbol
3211              } elseif ((($opCharacter == '~') || ($opCharacter == '|')) && (!$isOperandOrFunction)) {    //    We have to explicitly deny a tilde or pipe, because they are legal
3212                  return $this->raiseFormulaError("Formula Error: Illegal character '~'");                //        on the stack but not in the input expression
3213  
3214              } elseif ((isset(self::$operators[$opCharacter]) or $isOperandOrFunction) && $expectingOperator) {    //    Are we putting an operator on the stack?
3215  //echo 'Element with value '.$opCharacter.' is an Operator', PHP_EOL;
3216                  while ($stack->count() > 0 &&
3217                      ($o2 = $stack->last()) &&
3218                      isset(self::$operators[$o2['value']]) &&
3219                      @(self::$operatorAssociativity[$opCharacter] ? self::$operatorPrecedence[$opCharacter] < self::$operatorPrecedence[$o2['value']] : self::$operatorPrecedence[$opCharacter] <= self::$operatorPrecedence[$o2['value']])) {
3220                      $output[] = $stack->pop();                                //    Swap operands and higher precedence operators from the stack to the output
3221                  }
3222                  $stack->push('Binary Operator', $opCharacter);    //    Finally put our current operator onto the stack
3223                  ++$index;
3224                  $expectingOperator = false;
3225  
3226              } elseif ($opCharacter == ')' && $expectingOperator) {            //    Are we expecting to close a parenthesis?
3227  //echo 'Element is a Closing bracket', PHP_EOL;
3228                  $expectingOperand = false;
3229                  while (($o2 = $stack->pop()) && $o2['value'] != '(') {        //    Pop off the stack back to the last (
3230                      if ($o2 === null) {
3231                          return $this->raiseFormulaError('Formula Error: Unexpected closing brace ")"');
3232                      } else {
3233                          $output[] = $o2;
3234                      }
3235                  }
3236                  $d = $stack->last(2);
3237                  if (preg_match('/^'.self::CALCULATION_REGEXP_FUNCTION.'$/i', $d['value'], $matches)) {    //    Did this parenthesis just close a function?
3238                      $functionName = $matches[1];                                        //    Get the function name
3239  //echo 'Closed Function is '.$functionName, PHP_EOL;
3240                      $d = $stack->pop();
3241                      $argumentCount = $d['value'];        //    See how many arguments there were (argument count is the next value stored on the stack)
3242  //if ($argumentCount == 0) {
3243  //    echo 'With no arguments', PHP_EOL;
3244  //} elseif ($argumentCount == 1) {
3245  //    echo 'With 1 argument', PHP_EOL;
3246  //} else {
3247  //    echo 'With '.$argumentCount.' arguments', PHP_EOL;
3248  //}
3249                      $output[] = $d;                        //    Dump the argument count on the output
3250                      $output[] = $stack->pop();            //    Pop the function and push onto the output
3251                      if (isset(self::$controlFunctions[$functionName])) {
3252  //echo 'Built-in function '.$functionName, PHP_EOL;
3253                          $expectedArgumentCount = self::$controlFunctions[$functionName]['argumentCount'];
3254                          $functionCall = self::$controlFunctions[$functionName]['functionCall'];
3255                      } elseif (isset(self::$PHPExcelFunctions[$functionName])) {
3256  //echo 'PHPExcel function '.$functionName, PHP_EOL;
3257                          $expectedArgumentCount = self::$PHPExcelFunctions[$functionName]['argumentCount'];
3258                          $functionCall = self::$PHPExcelFunctions[$functionName]['functionCall'];
3259                      } else {    // did we somehow push a non-function on the stack? this should never happen
3260                          return $this->raiseFormulaError("Formula Error: Internal error, non-function on stack");
3261                      }
3262                      //    Check the argument count
3263                      $argumentCountError = false;
3264                      if (is_numeric($expectedArgumentCount)) {
3265                          if ($expectedArgumentCount < 0) {
3266  //echo '$expectedArgumentCount is between 0 and '.abs($expectedArgumentCount), PHP_EOL;
3267                              if ($argumentCount > abs($expectedArgumentCount)) {
3268                                  $argumentCountError = true;
3269                                  $expectedArgumentCountString = 'no more than '.abs($expectedArgumentCount);
3270                              }
3271                          } else {
3272  //echo '$expectedArgumentCount is numeric '.$expectedArgumentCount, PHP_EOL;
3273                              if ($argumentCount != $expectedArgumentCount) {
3274                                  $argumentCountError = true;
3275                                  $expectedArgumentCountString = $expectedArgumentCount;
3276                              }
3277                          }
3278                      } elseif ($expectedArgumentCount != '*') {
3279                          $isOperandOrFunction = preg_match('/(\d*)([-+,])(\d*)/', $expectedArgumentCount, $argMatch);
3280  //print_r($argMatch);
3281  //echo PHP_EOL;
3282                          switch ($argMatch[2]) {
3283                              case '+':
3284                                  if ($argumentCount < $argMatch[1]) {
3285                                      $argumentCountError = true;
3286                                      $expectedArgumentCountString = $argMatch[1].' or more ';
3287                                  }
3288                                  break;
3289                              case '-':
3290                                  if (($argumentCount < $argMatch[1]) || ($argumentCount > $argMatch[3])) {
3291                                      $argumentCountError = true;
3292                                      $expectedArgumentCountString = 'between '.$argMatch[1].' and '.$argMatch[3];
3293                                  }
3294                                  break;
3295                              case ',':
3296                                  if (($argumentCount != $argMatch[1]) && ($argumentCount != $argMatch[3])) {
3297                                      $argumentCountError = true;
3298                                      $expectedArgumentCountString = 'either '.$argMatch[1].' or '.$argMatch[3];
3299                                  }
3300                                  break;
3301                          }
3302                      }
3303                      if ($argumentCountError) {
3304                          return $this->raiseFormulaError("Formula Error: Wrong number of arguments for $functionName() function: $argumentCount given, ".$expectedArgumentCountString." expected");
3305                      }
3306                  }
3307                  ++$index;
3308  
3309              } elseif ($opCharacter == ',') {            //    Is this the separator for function arguments?
3310  //echo 'Element is a Function argument separator', PHP_EOL;
3311                  while (($o2 = $stack->pop()) && $o2['value'] != '(') {        //    Pop off the stack back to the last (
3312                      if ($o2 === null) {
3313                          return $this->raiseFormulaError("Formula Error: Unexpected ,");
3314                      } else {
3315                          $output[] = $o2;    // pop the argument expression stuff and push onto the output
3316                      }
3317                  }
3318                  //    If we've a comma when we're expecting an operand, then what we actually have is a null operand;
3319                  //        so push a null onto the stack
3320                  if (($expectingOperand) || (!$expectingOperator)) {
3321                      $output[] = array('type' => 'NULL Value', 'value' => self::$excelConstants['NULL'], 'reference' => null);
3322                  }
3323                  // make sure there was a function
3324                  $d = $stack->last(2);
3325                  if (!preg_match('/^'.self::CALCULATION_REGEXP_FUNCTION.'$/i', $d['value'], $matches)) {
3326                      return $this->raiseFormulaError("Formula Error: Unexpected ,");
3327                  }
3328                  $d = $stack->pop();
3329                  $stack->push($d['type'], ++$d['value'], $d['reference']);    // increment the argument count
3330                  $stack->push('Brace', '(');    // put the ( back on, we'll need to pop back to it again
3331                  $expectingOperator = false;
3332                  $expectingOperand = true;
3333                  ++$index;
3334  
3335              } elseif ($opCharacter == '(' && !$expectingOperator) {
3336  //                echo 'Element is an Opening Bracket<br />';
3337                  $stack->push('Brace', '(');
3338                  ++$index;
3339  
3340              } elseif ($isOperandOrFunction && !$expectingOperator) {    // do we now have a function/variable/number?
3341                  $expectingOperator = true;
3342                  $expectingOperand = false;
3343                  $val = $match[1];
3344                  $length = strlen($val);
3345  //                echo 'Element with value '.$val.' is an Operand, Variable, Constant, String, Number, Cell Reference or Function<br />';
3346  
3347                  if (preg_match('/^'.self::CALCULATION_REGEXP_FUNCTION.'$/i', $val, $matches)) {
3348                      $val = preg_replace('/\s/u', '', $val);
3349  //                    echo 'Element '.$val.' is a Function<br />';
3350                      if (isset(self::$PHPExcelFunctions[strtoupper($matches[1])]) || isset(self::$controlFunctions[strtoupper($matches[1])])) {    // it's a function
3351                          $stack->push('Function', strtoupper($val));
3352                          $ax = preg_match('/^\s*(\s*\))/ui', substr($formula, $index+$length), $amatch);
3353                          if ($ax) {
3354                              $stack->push('Operand Count for Function '.strtoupper($val).')', 0);
3355                              $expectingOperator = true;
3356                          } else {
3357                              $stack->push('Operand Count for Function '.strtoupper($val).')', 1);
3358                              $expectingOperator = false;
3359                          }
3360                          $stack->push('Brace', '(');
3361                      } else {    // it's a var w/ implicit multiplication
3362                          $output[] = array('type' => 'Value', 'value' => $matches[1], 'reference' => null);
3363                      }
3364                  } elseif (preg_match('/^'.self::CALCULATION_REGEXP_CELLREF.'$/i', $val, $matches)) {
3365  //                    echo 'Element '.$val.' is a Cell reference<br />';
3366                      //    Watch for this case-change when modifying to allow cell references in different worksheets...
3367                      //    Should only be applied to the actual cell column, not the worksheet name
3368  
3369                      //    If the last entry on the stack was a : operator, then we have a cell range reference
3370                      $testPrevOp = $stack->last(1);
3371                      if ($testPrevOp['value'] == ':') {
3372                          //    If we have a worksheet reference, then we're playing with a 3D reference
3373                          if ($matches[2] == '') {
3374                              //    Otherwise, we 'inherit' the worksheet reference from the start cell reference
3375                              //    The start of the cell range reference should be the last entry in $output
3376                              $startCellRef = $output[count($output)-1]['value'];
3377                              preg_match('/^'.self::CALCULATION_REGEXP_CELLREF.'$/i', $startCellRef, $startMatches);
3378                              if ($startMatches[2] > '') {
3379                                  $val = $startMatches[2].'!'.$val;
3380                              }
3381                          } else {
3382                              return $this->raiseFormulaError("3D Range references are not yet supported");
3383                          }
3384                      }
3385  
3386                      $output[] = array('type' => 'Cell Reference', 'value' => $val, 'reference' => $val);
3387  //                    $expectingOperator = FALSE;
3388                  } else {    // it's a variable, constant, string, number or boolean
3389  //                    echo 'Element is a Variable, Constant, String, Number or Boolean<br />';
3390                      //    If the last entry on the stack was a : operator, then we may have a row or column range reference
3391                      $testPrevOp = $stack->last(1);
3392                      if ($testPrevOp['value'] == ':') {
3393                          $startRowColRef = $output[count($output)-1]['value'];
3394                          $rangeWS1 = '';
3395                          if (strpos('!', $startRowColRef) !== false) {
3396                              list($rangeWS1, $startRowColRef) = explode('!', $startRowColRef);
3397                          }
3398                          if ($rangeWS1 != '') {
3399                              $rangeWS1 .= '!';
3400                          }
3401                          $rangeWS2 = $rangeWS1;
3402                          if (strpos('!', $val) !== false) {
3403                              list($rangeWS2, $val) = explode('!', $val);
3404                          }
3405                          if ($rangeWS2 != '') {
3406                              $rangeWS2 .= '!';
3407                          }
3408                          if ((is_integer($startRowColRef)) && (ctype_digit($val)) &&
3409                              ($startRowColRef <= 1048576) && ($val <= 1048576)) {
3410                              //    Row range
3411                              $endRowColRef = ($pCellParent !== null) ? $pCellParent->getHighestColumn() : 'XFD';    //    Max 16,384 columns for Excel2007
3412                              $output[count($output)-1]['value'] = $rangeWS1.'A'.$startRowColRef;
3413                              $val = $rangeWS2.$endRowColRef.$val;
3414                          } elseif ((ctype_alpha($startRowColRef)) && (ctype_alpha($val)) &&
3415                              (strlen($startRowColRef) <= 3) && (strlen($val) <= 3)) {
3416                              //    Column range
3417                              $endRowColRef = ($pCellParent !== null) ? $pCellParent->getHighestRow() : 1048576;        //    Max 1,048,576 rows for Excel2007
3418                              $output[count($output)-1]['value'] = $rangeWS1.strtoupper($startRowColRef).'1';
3419                              $val = $rangeWS2.$val.$endRowColRef;
3420                          }
3421                      }
3422  
3423                      $localeConstant = false;
3424                      if ($opCharacter == '"') {
3425  //                        echo 'Element is a String<br />';
3426                          //    UnEscape any quotes within the string
3427                          $val = self::wrapResult(str_replace('""', '"', self::unwrapResult($val)));
3428                      } elseif (is_numeric($val)) {
3429  //                        echo 'Element is a Number<br />';
3430                          if ((strpos($val, '.') !== false) || (stripos($val, 'e') !== false) || ($val > PHP_INT_MAX) || ($val < -PHP_INT_MAX)) {
3431  //                            echo 'Casting '.$val.' to float<br />';
3432                              $val = (float) $val;
3433                          } else {
3434  //                            echo 'Casting '.$val.' to integer<br />';
3435                              $val = (integer) $val;
3436                          }
3437                      } elseif (isset(self::$excelConstants[trim(strtoupper($val))])) {
3438                          $excelConstant = trim(strtoupper($val));
3439  //                        echo 'Element '.$excelConstant.' is an Excel Constant<br />';
3440                          $val = self::$excelConstants[$excelConstant];
3441                      } elseif (($localeConstant = array_search(trim(strtoupper($val)), self::$localeBoolean)) !== false) {
3442  //                        echo 'Element '.$localeConstant.' is an Excel Constant<br />';
3443                          $val = self::$excelConstants[$localeConstant];
3444                      }
3445                      $details = array('type' => 'Value', 'value' => $val, 'reference' => null);
3446                      if ($localeConstant) {
3447                          $details['localeValue'] = $localeConstant;
3448                      }
3449                      $output[] = $details;
3450                  }
3451                  $index += $length;
3452  
3453              } elseif ($opCharacter == '$') {    // absolute row or column range
3454                  ++$index;
3455              } elseif ($opCharacter == ')') {    // miscellaneous error checking
3456                  if ($expectingOperand) {
3457                      $output[] = array('type' => 'NULL Value', 'value' => self::$excelConstants['NULL'], 'reference' => null);
3458                      $expectingOperand = false;
3459                      $expectingOperator = true;
3460                  } else {
3461                      return $this->raiseFormulaError("Formula Error: Unexpected ')'");
3462                  }
3463              } elseif (isset(self::$operators[$opCharacter]) && !$expectingOperator) {
3464                  return $this->raiseFormulaError("Formula Error: Unexpected operator '$opCharacter'");
3465              } else {    // I don't even want to know what you did to get here
3466                  return $this->raiseFormulaError("Formula Error: An unexpected error occured");
3467              }
3468              //    Test for end of formula string
3469              if ($index == strlen($formula)) {
3470                  //    Did we end with an operator?.
3471                  //    Only valid for the % unary operator
3472                  if ((isset(self::$operators[$opCharacter])) && ($opCharacter != '%')) {
3473                      return $this->raiseFormulaError("Formula Error: Operator '$opCharacter' has no operands");
3474                  } else {
3475                      break;
3476                  }
3477              }
3478              //    Ignore white space
3479              while (($formula{$index} == "\n") || ($formula{$index} == "\r")) {
3480                  ++$index;
3481              }
3482              if ($formula{$index} == ' ') {
3483                  while ($formula{$index} == ' ') {
3484                      ++$index;
3485                  }
3486                  //    If we're expecting an operator, but only have a space between the previous and next operands (and both are
3487                  //        Cell References) then we have an INTERSECTION operator
3488  //                echo 'Possible Intersect Operator<br />';
3489                  if (($expectingOperator) && (preg_match('/^'.self::CALCULATION_REGEXP_CELLREF.'.*/Ui', substr($formula, $index), $match)) &&
3490                      ($output[count($output)-1]['type'] == 'Cell Reference')) {
3491  //                    echo 'Element is an Intersect Operator<br />';
3492                      while ($stack->count() > 0 &&
3493                          ($o2 = $stack->last()) &&
3494                          isset(self::$operators[$o2['value']]) &&
3495                          @(self::$operatorAssociativity[$opCharacter] ? self::$operatorPrecedence[$opCharacter] < self::$operatorPrecedence[$o2['value']] : self::$operatorPrecedence[$opCharacter] <= self::$operatorPrecedence[$o2['value']])) {
3496                          $output[] = $stack->pop();                                //    Swap operands and higher precedence operators from the stack to the output
3497                      }
3498                      $stack->push('Binary Operator', '|');    //    Put an Intersect Operator on the stack
3499                      $expectingOperator = false;
3500                  }
3501              }
3502          }
3503  
3504          while (($op = $stack->pop()) !== null) {    // pop everything off the stack and push onto output
3505              if ((is_array($op) && $op['value'] == '(') || ($op === '(')) {
3506                  return $this->raiseFormulaError("Formula Error: Expecting ')'");    // if there are any opening braces on the stack, then braces were unbalanced
3507              }
3508              $output[] = $op;
3509          }
3510          return $output;
3511      }
3512  
3513  
3514      private static function dataTestReference(&$operandData)
3515      {
3516          $operand = $operandData['value'];
3517          if (($operandData['reference'] === null) && (is_array($operand))) {
3518              $rKeys = array_keys($operand);
3519              $rowKey = array_shift($rKeys);
3520              $cKeys = array_keys(array_keys($operand[$rowKey]));
3521              $colKey = array_shift($cKeys);
3522              if (ctype_upper($colKey)) {
3523                  $operandData['reference'] = $colKey.$rowKey;
3524              }
3525          }
3526          return $operand;
3527      }
3528  
3529      // evaluate postfix notation
3530      private function processTokenStack($tokens, $cellID = null, PHPExcel_Cell $pCell = null)
3531      {
3532          if ($tokens == false) {
3533              return false;
3534          }
3535  
3536          //    If we're using cell caching, then $pCell may well be flushed back to the cache (which detaches the parent cell collection),
3537          //        so we store the parent cell collection so that we can re-attach it when necessary
3538          $pCellWorksheet = ($pCell !== null) ? $pCell->getWorksheet() : null;
3539          $pCellParent = ($pCell !== null) ? $pCell->getParent() : null;
3540          $stack = new PHPExcel_Calculation_Token_Stack;
3541  
3542          //    Loop through each token in turn
3543          foreach ($tokens as $tokenData) {
3544  //            print_r($tokenData);
3545  //            echo '<br />';
3546              $token = $tokenData['value'];
3547  //            echo '<b>Token is '.$token.'</b><br />';
3548              // 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
3549              if (isset(self::$binaryOperators[$token])) {
3550  //                echo 'Token is a binary operator<br />';
3551                  //    We must have two operands, error if we don't
3552                  if (($operand2Data = $stack->pop()) === null) {
3553                      return $this->raiseFormulaError('Internal error - Operand value missing from stack');
3554                  }
3555                  if (($operand1Data = $stack->pop()) === null) {
3556                      return $this->raiseFormulaError('Internal error - Operand value missing from stack');
3557                  }
3558  
3559                  $operand1 = self::dataTestReference($operand1Data);
3560                  $operand2 = self::dataTestReference($operand2Data);
3561  
3562                  //    Log what we're doing
3563                  if ($token == ':') {
3564                      $this->_debugLog->writeDebugLog('Evaluating Range ', $this->showValue($operand1Data['reference']), ' ', $token, ' ', $this->showValue($operand2Data['reference']));
3565                  } else {
3566                      $this->_debugLog->writeDebugLog('Evaluating ', $this->showValue($operand1), ' ', $token, ' ', $this->showValue($operand2));
3567                  }
3568  
3569                  //    Process the operation in the appropriate manner
3570                  switch ($token) {
3571                      //    Comparison (Boolean) Operators
3572                      case '>':            //    Greater than
3573                      case '<':            //    Less than
3574                      case '>=':            //    Greater than or Equal to
3575                      case '<=':            //    Less than or Equal to
3576                      case '=':            //    Equality
3577                      case '<>':            //    Inequality
3578                          $this->executeBinaryComparisonOperation($cellID, $operand1, $operand2, $token, $stack);
3579                          break;
3580                      //    Binary Operators
3581                      case ':':            //    Range
3582                          $sheet1 = $sheet2 = '';
3583                          if (strpos($operand1Data['reference'], '!') !== false) {
3584                              list($sheet1, $operand1Data['reference']) = explode('!', $operand1Data['reference']);
3585                          } else {
3586                              $sheet1 = ($pCellParent !== null) ? $pCellWorksheet->getTitle() : '';
3587                          }
3588                          if (strpos($operand2Data['reference'], '!') !== false) {
3589                              list($sheet2, $operand2Data['reference']) = explode('!', $operand2Data['reference']);
3590                          } else {
3591                              $sheet2 = $sheet1;
3592                          }
3593                          if ($sheet1 == $sheet2) {
3594                              if ($operand1Data['reference'] === null) {
3595                                  if ((trim($operand1Data['value']) != '') && (is_numeric($operand1Data['value']))) {
3596                                      $operand1Data['reference'] = $pCell->getColumn().$operand1Data['value'];
3597                                  } elseif (trim($operand1Data['reference']) == '') {
3598                                      $operand1Data['reference'] = $pCell->getCoordinate();
3599                                  } else {
3600                                      $operand1Data['reference'] = $operand1Data['value'].$pCell->getRow();
3601                                  }
3602                              }
3603                              if ($operand2Data['reference'] === null) {
3604                                  if ((trim($operand2Data['value']) != '') && (is_numeric($operand2Data['value']))) {
3605                                      $operand2Data['reference'] = $pCell->getColumn().$operand2Data['value'];
3606                                  } elseif (trim($operand2Data['reference']) == '') {
3607                                      $operand2Data['reference'] = $pCell->getCoordinate();
3608                                  } else {
3609                                      $operand2Data['reference'] = $operand2Data['value'].$pCell->getRow();
3610                                  }
3611                              }
3612  
3613                              $oData = array_merge(explode(':', $operand1Data['reference']), explode(':', $operand2Data['reference']));
3614                              $oCol = $oRow = array();
3615                              foreach ($oData as $oDatum) {
3616                                  $oCR = PHPExcel_Cell::coordinateFromString($oDatum);
3617                                  $oCol[] = PHPExcel_Cell::columnIndexFromString($oCR[0]) - 1;
3618                                  $oRow[] = $oCR[1];
3619                              }
3620                              $cellRef = PHPExcel_Cell::stringFromColumnIndex(min($oCol)).min($oRow).':'.PHPExcel_Cell::stringFromColumnIndex(max($oCol)).max($oRow);
3621                              if ($pCellParent !== null) {
3622                                  $cellValue = $this->extractCellRange($cellRef, $this->workbook->getSheetByName($sheet1), false);
3623                              } else {
3624                                  return $this->raiseFormulaError('Unable to access Cell Reference');
3625                              }
3626                              $stack->push('Cell Reference', $cellValue, $cellRef);
3627                          } else {
3628                              $stack->push('Error', PHPExcel_Calculation_Functions::REF(), null);
3629                          }
3630                          break;
3631                      case '+':            //    Addition
3632                          $this->executeNumericBinaryOperation($cellID, $operand1, $operand2, $token, 'plusEquals', $stack);
3633                          break;
3634                      case '-':            //    Subtraction
3635                          $this->executeNumericBinaryOperation($cellID, $operand1, $operand2, $token, 'minusEquals', $stack);
3636                          break;
3637                      case '*':            //    Multiplication
3638                          $this->executeNumericBinaryOperation($cellID, $operand1, $operand2, $token, 'arrayTimesEquals', $stack);
3639                          break;
3640                      case '/':            //    Division
3641                          $this->executeNumericBinaryOperation($cellID, $operand1, $operand2, $token, 'arrayRightDivide', $stack);
3642                          break;
3643                      case '^':            //    Exponential
3644                          $this->executeNumericBinaryOperation($cellID, $operand1, $operand2, $token, 'power', $stack);
3645                          break;
3646                      case '&':            //    Concatenation
3647                          //    If either of the operands is a matrix, we need to treat them both as matrices
3648                          //        (converting the other operand to a matrix if need be); then perform the required
3649                          //        matrix operation
3650                          if (is_bool($operand1)) {
3651                              $operand1 = ($operand1) ? self::$localeBoolean['TRUE'] : self::$localeBoolean['FALSE'];
3652                          }
3653                          if (is_bool($operand2)) {
3654                              $operand2 = ($operand2) ? self::$localeBoolean['TRUE'] : self::$localeBoolean['FALSE'];
3655                          }
3656                          if ((is_array($operand1)) || (is_array($operand2))) {
3657                              //    Ensure that both operands are arrays/matrices
3658                              self::checkMatrixOperands($operand1, $operand2, 2);
3659                              try {
3660                                  //    Convert operand 1 from a PHP array to a matrix
3661                                  $matrix = new PHPExcel_Shared_JAMA_Matrix($operand1);
3662                                  //    Perform the required operation against the operand 1 matrix, passing in operand 2
3663                                  $matrixResult = $matrix->concat($operand2);
3664                                  $result = $matrixResult->getArray();
3665                              } catch (PHPExcel_Exception $ex) {
3666                                  $this->_debugLog->writeDebugLog('JAMA Matrix Exception: ', $ex->getMessage());
3667                                  $result = '#VALUE!';
3668                              }
3669                          } else {
3670                              $result = '"'.str_replace('""', '"', self::unwrapResult($operand1, '"').self::unwrapResult($operand2, '"')).'"';
3671                          }
3672                          $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($result));
3673                          $stack->push('Value', $result);
3674                          break;
3675                      case '|':            //    Intersect
3676                          $rowIntersect = array_intersect_key($operand1, $operand2);
3677                          $cellIntersect = $oCol = $oRow = array();
3678                          foreach (array_keys($rowIntersect) as $row) {
3679                              $oRow[] = $row;
3680                              foreach ($rowIntersect[$row] as $col => $data) {
3681                                  $oCol[] = PHPExcel_Cell::columnIndexFromString($col) - 1;
3682                                  $cellIntersect[$row] = array_intersect_key($operand1[$row], $operand2[$row]);
3683                              }
3684                          }
3685                          $cellRef = PHPExcel_Cell::stringFromColumnIndex(min($oCol)).min($oRow).':'.PHPExcel_Cell::stringFromColumnIndex(max($oCol)).max($oRow);
3686                          $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($cellIntersect));
3687                          $stack->push('Value', $cellIntersect, $cellRef);
3688                          break;
3689                  }
3690  
3691              // if the token is a unary operator, pop one value off the stack, do the operation, and push it back on
3692              } elseif (($token === '~') || ($token === '%')) {
3693  //                echo 'Token is a unary operator<br />';
3694                  if (($arg = $stack->pop()) === null) {
3695                      return $this->raiseFormulaError('Internal error - Operand value missing from stack');
3696                  }
3697                  $arg = $arg['value'];
3698                  if ($token === '~') {
3699  //                    echo 'Token is a negation operator<br />';
3700                      $this->_debugLog->writeDebugLog('Evaluating Negation of ', $this->showValue($arg));
3701                      $multiplier = -1;
3702                  } else {
3703  //                    echo 'Token is a percentile operator<br />';
3704                      $this->_debugLog->writeDebugLog('Evaluating Percentile of ', $this->showValue($arg));
3705                      $multiplier = 0.01;
3706                  }
3707                  if (is_array($arg)) {
3708                      self::checkMatrixOperands($arg, $multiplier, 2);
3709                      try {
3710                          $matrix1 = new PHPExcel_Shared_JAMA_Matrix($arg);
3711                          $matrixResult = $matrix1->arrayTimesEquals($multiplier);
3712                          $result = $matrixResult->getArray();
3713                      } catch (PHPExcel_Exception $ex) {
3714                          $this->_debugLog->writeDebugLog('JAMA Matrix Exception: ', $ex->getMessage());
3715                          $result = '#VALUE!';
3716                      }
3717                      $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($result));
3718                      $stack->push('Value', $result);
3719                  } else {
3720                      $this->executeNumericBinaryOperation($cellID, $multiplier, $arg, '*', 'arrayTimesEquals', $stack);
3721                  }
3722  
3723              } elseif (preg_match('/^'.self::CALCULATION_REGEXP_CELLREF.'$/i', $token, $matches)) {
3724                  $cellRef = null;
3725  //                echo 'Element '.$token.' is a Cell reference<br />';
3726                  if (isset($matches[8])) {
3727  //                    echo 'Reference is a Range of cells<br />';
3728                      if ($pCell === null) {
3729  //                        We can't access the range, so return a REF error
3730                          $cellValue = PHPExcel_Calculation_Functions::REF();
3731                      } else {
3732                          $cellRef = $matches[6].$matches[7].':'.$matches[9].$matches[10];
3733                          if ($matches[2] > '') {
3734                              $matches[2] = trim($matches[2], "\"'");
3735                              if ((strpos($matches[2], '[') !== false) || (strpos($matches[2], ']') !== false)) {
3736                                  //    It's a Reference to an external workbook (not currently supported)
3737                                  return $this->raiseFormulaError('Unable to access External Workbook');
3738                              }
3739                              $matches[2] = trim($matches[2], "\"'");
3740  //                            echo '$cellRef='.$cellRef.' in worksheet '.$matches[2].'<br />';
3741                              $this->_debugLog->writeDebugLog('Evaluating Cell Range ', $cellRef, ' in worksheet ', $matches[2]);
3742                              if ($pCellParent !== null) {
3743                                  $cellValue = $this->extractCellRange($cellRef, $this->workbook->getSheetByName($matches[2]), false);
3744                              } else {
3745                                  return $this->raiseFormulaError('Unable to access Cell Reference');
3746                              }
3747                              $this->_debugLog->writeDebugLog('Evaluation Result for cells ', $cellRef, ' in worksheet ', $matches[2], ' is ', $this->showTypeDetails($cellValue));
3748  //                            $cellRef = $matches[2].'!'.$cellRef;
3749                          } else {
3750  //                            echo '$cellRef='.$cellRef.' in current worksheet<br />';
3751                              $this->_debugLog->writeDebugLog('Evaluating Cell Range ', $cellRef, ' in current worksheet');
3752                              if ($pCellParent !== null) {
3753                                  $cellValue = $this->extractCellRange($cellRef, $pCellWorksheet, false);
3754                              } else {
3755                                  return $this->raiseFormulaError('Unable to access Cell Reference');
3756                              }
3757                              $this->_debugLog->writeDebugLog('Evaluation Result for cells ', $cellRef, ' is ', $this->showTypeDetails($cellValue));
3758                          }
3759                      }
3760                  } else {
3761  //                    echo 'Reference is a single Cell<br />';
3762                      if ($pCell === null) {
3763  //                        We can't access the cell, so return a REF error
3764                          $cellValue = PHPExcel_Calculation_Functions::REF();
3765                      } else {
3766                          $cellRef = $matches[6].$matches[7];
3767                          if ($matches[2] > '') {
3768                              $matches[2] = trim($matches[2], "\"'");
3769                              if ((strpos($matches[2], '[') !== false) || (strpos($matches[2], ']') !== false)) {
3770                                  //    It's a Reference to an external workbook (not currently supported)
3771                                  return $this->raiseFormulaError('Unable to access External Workbook');
3772                              }
3773  //                            echo '$cellRef='.$cellRef.' in worksheet '.$matches[2].'<br />';
3774                              $this->_debugLog->writeDebugLog('Evaluating Cell ', $cellRef, ' in worksheet ', $matches[2]);
3775                              if ($pCellParent !== null) {
3776                                  $cellSheet = $this->workbook->getSheetByName($matches[2]);
3777                                  if ($cellSheet && $cellSheet->cellExists($cellRef)) {
3778                                      $cellValue = $this->extractCellRange($cellRef, $this->workbook->getSheetByName($matches[2]), false);
3779                                      $pCell->attach($pCellParent);
3780                                  } else {
3781                                      $cellValue = null;
3782                                  }
3783                              } else {
3784                                  return $this->raiseFormulaError('Unable to access Cell Reference');
3785                              }
3786                              $this->_debugLog->writeDebugLog('Evaluation Result for cell ', $cellRef, ' in worksheet ', $matches[2], ' is ', $this->showTypeDetails($cellValue));
3787  //                            $cellRef = $matches[2].'!'.$cellRef;
3788                          } else {
3789  //                            echo '$cellRef='.$cellRef.' in current worksheet<br />';
3790                              $this->_debugLog->writeDebugLog('Evaluating Cell ', $cellRef, ' in current worksheet');
3791                              if ($pCellParent->isDataSet($cellRef)) {
3792                                  $cellValue = $this->extractCellRange($cellRef, $pCellWorksheet, false);
3793                                  $pCell->attach($pCellParent);
3794                              } else {
3795                                  $cellValue = null;
3796                              }
3797                              $this->_debugLog->writeDebugLog('Evaluation Result for cell ', $cellRef, ' is ', $this->showTypeDetails($cellValue));
3798                          }
3799                      }
3800                  }
3801                  $stack->push('Value', $cellValue, $cellRef);
3802  
3803              // if the token is a function, pop arguments off the stack, hand them to the function, and push the result back on
3804              } elseif (preg_match('/^'.self::CALCULATION_REGEXP_FUNCTION.'$/i', $token, $matches)) {
3805  //                echo 'Token is a function<br />';
3806                  $functionName = $matches[1];
3807                  $argCount = $stack->pop();
3808                  $argCount = $argCount['value'];
3809                  if ($functionName != 'MKMATRIX') {
3810                      $this->_debugLog->writeDebugLog('Evaluating Function ', self::localeFunc($functionName), '() with ', (($argCount == 0) ? 'no' : $argCount), ' argument', (($argCount == 1) ? '' : 's'));
3811                  }
3812                  if ((isset(self::$PHPExcelFunctions[$functionName])) || (isset(self::$controlFunctions[$functionName]))) {    // function
3813                      if (isset(self::$PHPExcelFunctions[$functionName])) {
3814                          $functionCall = self::$PHPExcelFunctions[$functionName]['functionCall'];
3815                          $passByReference = isset(self::$PHPExcelFunctions[$functionName]['passByReference']);
3816                          $passCellReference = isset(self::$PHPExcelFunctions[$functionName]['passCellReference']);
3817                      } elseif (isset(self::$controlFunctions[$functionName])) {
3818                          $functionCall = self::$controlFunctions[$functionName]['functionCall'];
3819                          $passByReference = isset(self::$controlFunctions[$functionName]['passByReference']);
3820                          $passCellReference = isset(self::$controlFunctions[$functionName]['passCellReference']);
3821                      }
3822                      // get the arguments for this function
3823  //                    echo 'Function '.$functionName.' expects '.$argCount.' arguments<br />';
3824                      $args = $argArrayVals = array();
3825                      for ($i = 0; $i < $argCount; ++$i) {
3826                          $arg = $stack->pop();
3827                          $a = $argCount - $i - 1;
3828                          if (($passByReference) &&
3829                              (isset(self::$PHPExcelFunctions[$functionName]['passByReference'][$a])) &&
3830                              (self::$PHPExcelFunctions[$functionName]['passByReference'][$a])) {
3831                              if ($arg['reference'] === null) {
3832                                  $args[] = $cellID;
3833                                  if ($functionName != 'MKMATRIX') {
3834                                      $argArrayVals[] = $this->showValue($cellID);
3835                                  }
3836                              } else {
3837                                  $args[] = $arg['reference'];
3838                                  if ($functionName != 'MKMATRIX') {
3839                                      $argArrayVals[] = $this->showValue($arg['reference']);
3840                                  }
3841                              }
3842                          } else {
3843                              $args[] = self::unwrapResult($arg['value']);
3844                              if ($functionName != 'MKMATRIX') {
3845                                  $argArrayVals[] = $this->showValue($arg['value']);
3846                              }
3847                          }
3848                      }
3849                      //    Reverse the order of the arguments
3850                      krsort($args);
3851                      if (($passByReference) && ($argCount == 0)) {
3852                          $args[] = $cellID;
3853                          $argArrayVals[] = $this->showValue($cellID);
3854                      }
3855  //                    echo 'Arguments are: ';
3856  //                    print_r($args);
3857  //                    echo '<br />';
3858                      if ($functionName != 'MKMATRIX') {
3859                          if ($this->_debugLog->getWriteDebugLog()) {
3860                              krsort($argArrayVals);
3861                              $this->_debugLog->writeDebugLog('Evaluating ', self::localeFunc($functionName), '( ', implode(self::$localeArgumentSeparator.' ', PHPExcel_Calculation_Functions::flattenArray($argArrayVals)), ' )');
3862                          }
3863                      }
3864                      //    Process each argument in turn, building the return value as an array
3865  //                    if (($argCount == 1) && (is_array($args[1])) && ($functionName != 'MKMATRIX')) {
3866  //                        $operand1 = $args[1];
3867  //                        $this->_debugLog->writeDebugLog('Argument is a matrix: ', $this->showValue($operand1));
3868  //                        $result = array();
3869  //                        $row = 0;
3870  //                        foreach($operand1 as $args) {
3871  //                            if (is_array($args)) {
3872  //                                foreach($args as $arg) {
3873  //                                    $this->_debugLog->writeDebugLog('Evaluating ', self::localeFunc($functionName), '( ', $this->showValue($arg), ' )');
3874  //                                    $r = call_user_func_array($functionCall, $arg);
3875  //                                    $this->_debugLog->writeDebugLog('Evaluation Result for ', self::localeFunc($functionName), '() function call is ', $this->showTypeDetails($r));
3876  //                                    $result[$row][] = $r;
3877  //                                }
3878  //                                ++$row;
3879  //                            } else {
3880  //                                $this->_debugLog->writeDebugLog('Evaluating ', self::localeFunc($functionName), '( ', $this->showValue($args), ' )');
3881  //                                $r = call_user_func_array($functionCall, $args);
3882  //                                $this->_debugLog->writeDebugLog('Evaluation Result for ', self::localeFunc($functionName), '() function call is ', $this->showTypeDetails($r));
3883  //                                $result[] = $r;
3884  //                            }
3885  //                        }
3886  //                    } else {
3887                      //    Process the argument with the appropriate function call
3888                      if ($passCellReference) {
3889                          $args[] = $pCell;
3890                      }
3891                      if (strpos($functionCall, '::') !== false) {
3892                          $result = call_user_func_array(explode('::', $functionCall), $args);
3893                      } else {
3894                          foreach ($args as &$arg) {
3895                              $arg = PHPExcel_Calculation_Functions::flattenSingleValue($arg);
3896                          }
3897                          unset($arg);
3898                          $result = call_user_func_array($functionCall, $args);
3899                      }
3900                      if ($functionName != 'MKMATRIX') {
3901                          $this->_debugLog->writeDebugLog('Evaluation Result for ', self::localeFunc($functionName), '() function call is ', $this->showTypeDetails($result));
3902                      }
3903                      $stack->push('Value', self::wrapResult($result));
3904                  }
3905  
3906              } else {
3907                  // if the token is a number, boolean, string or an Excel error, push it onto the stack
3908                  if (isset(self::$excelConstants[strtoupper($token)])) {
3909                      $excelConstant = strtoupper($token);
3910  //                    echo 'Token is a PHPExcel constant: '.$excelConstant.'<br />';
3911                      $stack->push('Constant Value', self::$excelConstants[$excelConstant]);
3912                      $this->_debugLog->writeDebugLog('Evaluating Constant ', $excelConstant, ' as ', $this->showTypeDetails(self::$excelConstants[$excelConstant]));
3913                  } elseif ((is_numeric($token)) || ($token === null) || (is_bool($token)) || ($token == '') || ($token{0} == '"') || ($token{0} == '#')) {
3914  //                    echo 'Token is a number, boolean, string, null or an Excel error<br />';
3915                      $stack->push('Value', $token);
3916                  // if the token is a named range, push the named range name onto the stack
3917                  } elseif (preg_match('/^'.self::CALCULATION_REGEXP_NAMEDRANGE.'$/i', $token, $matches)) {
3918  //                    echo 'Token is a named range<br />';
3919                      $namedRange = $matches[6];
3920  //                    echo 'Named Range is '.$namedRange.'<br />';
3921                      $this->_debugLog->writeDebugLog('Evaluating Named Range ', $namedRange);
3922                      $cellValue = $this->extractNamedRange($namedRange, ((null !== $pCell) ? $pCellWorksheet : null), false);
3923                      $pCell->attach($pCellParent);
3924                      $this->_debugLog->writeDebugLog('Evaluation Result for named range ', $namedRange, ' is ', $this->showTypeDetails($cellValue));
3925                      $stack->push('Named Range', $cellValue, $namedRange);
3926                  } else {
3927                      return $this->raiseFormulaError("undefined variable '$token'");
3928                  }
3929              }
3930          }
3931          // when we're out of tokens, the stack should have a single element, the final result
3932          if ($stack->count() != 1) {
3933              return $this->raiseFormulaError("internal error");
3934          }
3935          $output = $stack->pop();
3936          $output = $output['value'];
3937  
3938  //        if ((is_array($output)) && (self::$returnArrayAsType != self::RETURN_ARRAY_AS_ARRAY)) {
3939  //            return array_shift(PHPExcel_Calculation_Functions::flattenArray($output));
3940  //        }
3941          return $output;
3942      }
3943  
3944  
3945      private function validateBinaryOperand($cellID, &$operand, &$stack)
3946      {
3947          if (is_array($operand)) {
3948              if ((count($operand, COUNT_RECURSIVE) - count($operand)) == 1) {
3949                  do {
3950                      $operand = array_pop($operand);
3951                  } while (is_array($operand));
3952              }
3953          }
3954          //    Numbers, matrices and booleans can pass straight through, as they're already valid
3955          if (is_string($operand)) {
3956              //    We only need special validations for the operand if it is a string
3957              //    Start by stripping off the quotation marks we use to identify true excel string values internally
3958              if ($operand > '' && $operand{0} == '"') {
3959                  $operand = self::unwrapResult($operand);
3960              }
3961              //    If the string is a numeric value, we treat it as a numeric, so no further testing
3962              if (!is_numeric($operand)) {
3963                  //    If not a numeric, test to see if the value is an Excel error, and so can't be used in normal binary operations
3964                  if ($operand > '' && $operand{0} == '#') {
3965                      $stack->push('Value', $operand);
3966                      $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($operand));
3967                      return false;
3968                  } elseif (!PHPExcel_Shared_String::convertToNumberIfFraction($operand)) {
3969                      //    If not a numeric or a fraction, then it's a text string, and so can't be used in mathematical binary operations
3970                      $stack->push('Value', '#VALUE!');
3971                      $this->_debugLog->writeDebugLog('Evaluation Result is a ', $this->showTypeDetails('#VALUE!'));
3972                      return false;
3973                  }
3974              }
3975          }
3976  
3977          //    return a true if the value of the operand is one that we can use in normal binary operations
3978          return true;
3979      }
3980  
3981  
3982      private function executeBinaryComparisonOperation($cellID, $operand1, $operand2, $operation, &$stack, $recursingArrays = false)
3983      {
3984          //    If we're dealing with matrix operations, we want a matrix result
3985          if ((is_array($operand1)) || (is_array($operand2))) {
3986              $result = array();
3987              if ((is_array($operand1)) && (!is_array($operand2))) {
3988                  foreach ($operand1 as $x => $operandData) {
3989                      $this->_debugLog->writeDebugLog('Evaluating Comparison ', $this->showValue($operandData), ' ', $operation, ' ', $this->showValue($operand2));
3990                      $this->executeBinaryComparisonOperation($cellID, $operandData, $operand2, $operation, $stack);
3991                      $r = $stack->pop();
3992                      $result[$x] = $r['value'];
3993                  }
3994              } elseif ((!is_array($operand1)) && (is_array($operand2))) {
3995                  foreach ($operand2 as $x => $operandData) {
3996                      $this->_debugLog->writeDebugLog('Evaluating Comparison ', $this->showValue($operand1), ' ', $operation, ' ', $this->showValue($operandData));
3997                      $this->executeBinaryComparisonOperation($cellID, $operand1, $operandData, $operation, $stack);
3998                      $r = $stack->pop();
3999                      $result[$x] = $r['value'];
4000                  }
4001              } else {
4002                  if (!$recursingArrays) {
4003                      self::checkMatrixOperands($operand1, $operand2, 2);
4004                  }
4005                  foreach ($operand1 as $x => $operandData) {
4006                      $this->_debugLog->writeDebugLog('Evaluating Comparison ', $this->showValue($operandData), ' ', $operation, ' ', $this->showValue($operand2[$x]));
4007                      $this->executeBinaryComparisonOperation($cellID, $operandData, $operand2[$x], $operation, $stack, true);
4008                      $r = $stack->pop();
4009                      $result[$x] = $r['value'];
4010                  }
4011              }
4012              //    Log the result details
4013              $this->_debugLog->writeDebugLog('Comparison Evaluation Result is ', $this->showTypeDetails($result));
4014              //    And push the result onto the stack
4015              $stack->push('Array', $result);
4016              return true;
4017          }
4018  
4019          //    Simple validate the two operands if they are string values
4020          if (is_string($operand1) && $operand1 > '' && $operand1{0} == '"') {
4021              $operand1 = self::unwrapResult($operand1);
4022          }
4023          if (is_string($operand2) && $operand2 > '' && $operand2{0} == '"') {
4024              $operand2 = self::unwrapResult($operand2);
4025          }
4026  
4027          // Use case insensitive comparaison if not OpenOffice mode
4028          if (PHPExcel_Calculation_Functions::getCompatibilityMode() != PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE) {
4029              if (is_string($operand1)) {
4030                  $operand1 = strtoupper($operand1);
4031              }
4032              if (is_string($operand2)) {
4033                  $operand2 = strtoupper($operand2);
4034              }
4035          }
4036  
4037          $useLowercaseFirstComparison = is_string($operand1) && is_string($operand2) && PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE;
4038  
4039          //    execute the necessary operation
4040          switch ($operation) {
4041              //    Greater than
4042              case '>':
4043                  if ($useLowercaseFirstComparison) {
4044                      $result = $this->strcmpLowercaseFirst($operand1, $operand2) > 0;
4045                  } else {
4046                      $result = ($operand1 > $operand2);
4047                  }
4048                  break;
4049              //    Less than
4050              case '<':
4051                  if ($useLowercaseFirstComparison) {
4052                      $result = $this->strcmpLowercaseFirst($operand1, $operand2) < 0;
4053                  } else {
4054                      $result = ($operand1 < $operand2);
4055                  }
4056                  break;
4057              //    Equality
4058              case '=':
4059                  if (is_numeric($operand1) && is_numeric($operand2)) {
4060                      $result = (abs($operand1 - $operand2) < $this->delta);
4061                  } else {
4062                      $result = strcmp($operand1, $operand2) == 0;
4063                  }
4064                  break;
4065              //    Greater than or equal
4066              case '>=':
4067                  if (is_numeric($operand1) && is_numeric($operand2)) {
4068                      $result = ((abs($operand1 - $operand2) < $this->delta) || ($operand1 > $operand2));
4069                  } elseif ($useLowercaseFirstComparison) {
4070                      $result = $this->strcmpLowercaseFirst($operand1, $operand2) >= 0;
4071                  } else {
4072                      $result = strcmp($operand1, $operand2) >= 0;
4073                  }
4074                  break;
4075              //    Less than or equal
4076              case '<=':
4077                  if (is_numeric($operand1) && is_numeric($operand2)) {
4078                      $result = ((abs($operand1 - $operand2) < $this->delta) || ($operand1 < $operand2));
4079                  } elseif ($useLowercaseFirstComparison) {
4080                      $result = $this->strcmpLowercaseFirst($operand1, $operand2) <= 0;
4081                  } else {
4082                      $result = strcmp($operand1, $operand2) <= 0;
4083                  }
4084                  break;
4085              //    Inequality
4086              case '<>':
4087                  if (is_numeric($operand1) && is_numeric($operand2)) {
4088                      $result = (abs($operand1 - $operand2) > 1E-14);
4089                  } else {
4090                      $result = strcmp($operand1, $operand2) != 0;
4091                  }
4092                  break;
4093          }
4094  
4095          //    Log the result details
4096          $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($result));
4097          //    And push the result onto the stack
4098          $stack->push('Value', $result);
4099          return true;
4100      }
4101  
4102      /**
4103       * Compare two strings in the same way as strcmp() except that lowercase come before uppercase letters
4104       * @param    string    $str1    First string value for the comparison
4105       * @param    string    $str2    Second string value for the comparison
4106       * @return   integer
4107       */
4108      private function strcmpLowercaseFirst($str1, $str2)
4109      {
4110          $inversedStr1 = PHPExcel_Shared_String::StrCaseReverse($str1);
4111          $inversedStr2 = PHPExcel_Shared_String::StrCaseReverse($str2);
4112  
4113          return strcmp($inversedStr1, $inversedStr2);
4114      }
4115  
4116      private function executeNumericBinaryOperation($cellID, $operand1, $operand2, $operation, $matrixFunction, &$stack)
4117      {
4118          //    Validate the two operands
4119          if (!$this->validateBinaryOperand($cellID, $operand1, $stack)) {
4120              return false;
4121          }
4122          if (!$this->validateBinaryOperand($cellID, $operand2, $stack)) {
4123              return false;
4124          }
4125  
4126          //    If either of the operands is a matrix, we need to treat them both as matrices
4127          //        (converting the other operand to a matrix if need be); then perform the required
4128          //        matrix operation
4129          if ((is_array($operand1)) || (is_array($operand2))) {
4130              //    Ensure that both operands are arrays/matrices of the same size
4131              self::checkMatrixOperands($operand1, $operand2, 2);
4132  
4133              try {
4134                  //    Convert operand 1 from a PHP array to a matrix
4135                  $matrix = new PHPExcel_Shared_JAMA_Matrix($operand1);
4136                  //    Perform the required operation against the operand 1 matrix, passing in operand 2
4137                  $matrixResult = $matrix->$matrixFunction($operand2);
4138                  $result = $matrixResult->getArray();
4139              } catch (PHPExcel_Exception $ex) {
4140                  $this->_debugLog->writeDebugLog('JAMA Matrix Exception: ', $ex->getMessage());
4141                  $result = '#VALUE!';
4142              }
4143          } else {
4144              if ((PHPExcel_Calculation_Functions::getCompatibilityMode() != PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE) &&
4145                  ((is_string($operand1) && !is_numeric($operand1) && strlen($operand1)>0) ||
4146                   (is_string($operand2) && !is_numeric($operand2) && strlen($operand2)>0))) {
4147                  $result = PHPExcel_Calculation_Functions::VALUE();
4148              } else {
4149                  //    If we're dealing with non-matrix operations, execute the necessary operation
4150                  switch ($operation) {
4151                      //    Addition
4152                      case '+':
4153                          $result = $operand1 + $operand2;
4154                          break;
4155                      //    Subtraction
4156                      case '-':
4157                          $result = $operand1 - $operand2;
4158                          break;
4159                      //    Multiplication
4160                      case '*':
4161                          $result = $operand1 * $operand2;
4162                          break;
4163                      //    Division
4164                      case '/':
4165                          if ($operand2 == 0) {
4166                              //    Trap for Divide by Zero error
4167                              $stack->push('Value', '#DIV/0!');
4168                              $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails('#DIV/0!'));
4169                              return false;
4170                          } else {
4171                              $result = $operand1 / $operand2;
4172                          }
4173                          break;
4174                      //    Power
4175                      case '^':
4176                          $result = pow($operand1, $operand2);
4177                          break;
4178                  }
4179              }
4180          }
4181  
4182          //    Log the result details
4183          $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($result));
4184          //    And push the result onto the stack
4185          $stack->push('Value', $result);
4186          return true;
4187      }
4188  
4189  
4190      // trigger an error, but nicely, if need be
4191      protected function raiseFormulaError($errorMessage)
4192      {
4193          $this->formulaError = $errorMessage;
4194          $this->cyclicReferenceStack->clear();
4195          if (!$this->suppressFormulaErrors) {
4196              throw new PHPExcel_Calculation_Exception($errorMessage);
4197          }
4198          trigger_error($errorMessage, E_USER_ERROR);
4199      }
4200  
4201  
4202      /**
4203       * Extract range values
4204       *
4205       * @param    string                &$pRange    String based range representation
4206       * @param    PHPExcel_Worksheet    $pSheet        Worksheet
4207       * @param    boolean                $resetLog    Flag indicating whether calculation log should be reset or not
4208       * @return  mixed                Array of values in range if range contains more than one element. Otherwise, a single value is returned.
4209       * @throws    PHPExcel_Calculation_Exception
4210       */
4211      public function extractCellRange(&$pRange = 'A1', PHPExcel_Worksheet $pSheet = null, $resetLog = true)
4212      {
4213          // Return value
4214          $returnValue = array ();
4215  
4216  //        echo 'extractCellRange('.$pRange.')', PHP_EOL;
4217          if ($pSheet !== null) {
4218              $pSheetName = $pSheet->getTitle();
4219  //            echo 'Passed sheet name is '.$pSheetName.PHP_EOL;
4220  //            echo 'Range reference is '.$pRange.PHP_EOL;
4221              if (strpos($pRange, '!') !== false) {
4222  //                echo '$pRange reference includes sheet reference', PHP_EOL;
4223                  list($pSheetName, $pRange) = PHPExcel_Worksheet::extractSheetTitle($pRange, true);
4224  //                echo 'New sheet name is '.$pSheetName, PHP_EOL;
4225  //                echo 'Adjusted Range reference is '.$pRange, PHP_EOL;
4226                  $pSheet = $this->workbook->getSheetByName($pSheetName);
4227              }
4228  
4229              // Extract range
4230              $aReferences = PHPExcel_Cell::extractAllCellReferencesInRange($pRange);
4231              $pRange = $pSheetName.'!'.$pRange;
4232              if (!isset($aReferences[1])) {
4233                  //    Single cell in range
4234                  sscanf($aReferences[0], '%[A-Z]%d', $currentCol, $currentRow);
4235                  $cellValue = null;
4236                  if ($pSheet->cellExists($aReferences[0])) {
4237                      $returnValue[$currentRow][$currentCol] = $pSheet->getCell($aReferences[0])->getCalculatedValue($resetLog);
4238                  } else {
4239                      $returnValue[$currentRow][$currentCol] = null;
4240                  }
4241              } else {
4242                  // Extract cell data for all cells in the range
4243                  foreach ($aReferences as $reference) {
4244                      // Extract range
4245                      sscanf($reference, '%[A-Z]%d', $currentCol, $currentRow);
4246                      $cellValue = null;
4247                      if ($pSheet->cellExists($reference)) {
4248                          $returnValue[$currentRow][$currentCol] = $pSheet->getCell($reference)->getCalculatedValue($resetLog);
4249                      } else {
4250                          $returnValue[$currentRow][$currentCol] = null;
4251                      }
4252                  }
4253              }
4254          }
4255  
4256          return $returnValue;
4257      }
4258  
4259  
4260      /**
4261       * Extract range values
4262       *
4263       * @param    string                &$pRange    String based range representation
4264       * @param    PHPExcel_Worksheet    $pSheet        Worksheet
4265       * @return  mixed                Array of values in range if range contains more than one element. Otherwise, a single value is returned.
4266       * @param    boolean                $resetLog    Flag indicating whether calculation log should be reset or not
4267       * @throws    PHPExcel_Calculation_Exception
4268       */
4269      public function extractNamedRange(&$pRange = 'A1', PHPExcel_Worksheet $pSheet = null, $resetLog = true)
4270      {
4271          // Return value
4272          $returnValue = array ();
4273  
4274  //        echo 'extractNamedRange('.$pRange.')<br />';
4275          if ($pSheet !== null) {
4276              $pSheetName = $pSheet->getTitle();
4277  //            echo 'Current sheet name is '.$pSheetName.'<br />';
4278  //            echo 'Range reference is '.$pRange.'<br />';
4279              if (strpos($pRange, '!') !== false) {
4280  //                echo '$pRange reference includes sheet reference', PHP_EOL;
4281                  list($pSheetName, $pRange) = PHPExcel_Worksheet::extractSheetTitle($pRange, true);
4282  //                echo 'New sheet name is '.$pSheetName, PHP_EOL;
4283  //                echo 'Adjusted Range reference is '.$pRange, PHP_EOL;
4284                  $pSheet = $this->workbook->getSheetByName($pSheetName);
4285              }
4286  
4287              // Named range?
4288              $namedRange = PHPExcel_NamedRange::resolveRange($pRange, $pSheet);
4289              if ($namedRange !== null) {
4290                  $pSheet = $namedRange->getWorksheet();
4291  //                echo 'Named Range '.$pRange.' (';
4292                  $pRange = $namedRange->getRange();
4293                  $splitRange = PHPExcel_Cell::splitRange($pRange);
4294                  //    Convert row and column references
4295                  if (ctype_alpha($splitRange[0][0])) {
4296                      $pRange = $splitRange[0][0] . '1:' . $splitRange[0][1] . $namedRange->getWorksheet()->getHighestRow();
4297                  } elseif (ctype_digit($splitRange[0][0])) {
4298                      $pRange = 'A' . $splitRange[0][0] . ':' . $namedRange->getWorksheet()->getHighestColumn() . $splitRange[0][1];
4299                  }
4300  //                echo $pRange.') is in sheet '.$namedRange->getWorksheet()->getTitle().'<br />';
4301  
4302  //                if ($pSheet->getTitle() != $namedRange->getWorksheet()->getTitle()) {
4303  //                    if (!$namedRange->getLocalOnly()) {
4304  //                        $pSheet = $namedRange->getWorksheet();
4305  //                    } else {
4306  //                        return $returnValue;
4307  //                    }
4308  //                }
4309              } else {
4310                  return PHPExcel_Calculation_Functions::REF();
4311              }
4312  
4313              // Extract range
4314              $aReferences = PHPExcel_Cell::extractAllCellReferencesInRange($pRange);
4315  //            var_dump($aReferences);
4316              if (!isset($aReferences[1])) {
4317                  //    Single cell (or single column or row) in range
4318                  list($currentCol, $currentRow) = PHPExcel_Cell::coordinateFromString($aReferences[0]);
4319                  $cellValue = null;
4320                  if ($pSheet->cellExists($aReferences[0])) {
4321                      $returnValue[$currentRow][$currentCol] = $pSheet->getCell($aReferences[0])->getCalculatedValue($resetLog);
4322                  } else {
4323                      $returnValue[$currentRow][$currentCol] = null;
4324                  }
4325              } else {
4326                  // Extract cell data for all cells in the range
4327                  foreach ($aReferences as $reference) {
4328                      // Extract range
4329                      list($currentCol, $currentRow) = PHPExcel_Cell::coordinateFromString($reference);
4330  //                    echo 'NAMED RANGE: $currentCol='.$currentCol.' $currentRow='.$currentRow.'<br />';
4331                      $cellValue = null;
4332                      if ($pSheet->cellExists($reference)) {
4333                          $returnValue[$currentRow][$currentCol] = $pSheet->getCell($reference)->getCalculatedValue($resetLog);
4334                      } else {
4335                          $returnValue[$currentRow][$currentCol] = null;
4336                      }
4337                  }
4338              }
4339  //                print_r($returnValue);
4340  //            echo '<br />';
4341          }
4342  
4343          return $returnValue;
4344      }
4345  
4346  
4347      /**
4348       * Is a specific function implemented?
4349       *
4350       * @param    string    $pFunction    Function Name
4351       * @return    boolean
4352       */
4353      public function isImplemented($pFunction = '')
4354      {
4355          $pFunction = strtoupper($pFunction);
4356          if (isset(self::$PHPExcelFunctions[$pFunction])) {
4357              return (self::$PHPExcelFunctions[$pFunction]['functionCall'] != 'PHPExcel_Calculation_Functions::DUMMY');
4358          } else {
4359              return false;
4360          }
4361      }
4362  
4363  
4364      /**
4365       * Get a list of all implemented functions as an array of function objects
4366       *
4367       * @return    array of PHPExcel_Calculation_Function
4368       */
4369      public function listFunctions()
4370      {
4371          $returnValue = array();
4372  
4373          foreach (self::$PHPExcelFunctions as $functionName => $function) {
4374              if ($function['functionCall'] != 'PHPExcel_Calculation_Functions::DUMMY') {
4375                  $returnValue[$functionName] = new PHPExcel_Calculation_Function(
4376                      $function['category'],
4377                      $functionName,
4378                      $function['functionCall']
4379                  );
4380              }
4381          }
4382  
4383          return $returnValue;
4384      }
4385  
4386  
4387      /**
4388       * Get a list of all Excel function names
4389       *
4390       * @return    array
4391       */
4392      public function listAllFunctionNames()
4393      {
4394          return array_keys(self::$PHPExcelFunctions);
4395      }
4396  
4397      /**
4398       * Get a list of implemented Excel function names
4399       *
4400       * @return    array
4401       */
4402      public function listFunctionNames()
4403      {
4404          $returnValue = array();
4405          foreach (self::$PHPExcelFunctions as $functionName => $function) {
4406              if ($function['functionCall'] != 'PHPExcel_Calculation_Functions::DUMMY') {
4407                  $returnValue[] = $functionName;
4408              }
4409          }
4410  
4411          return $returnValue;
4412      }
4413  }


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1