1<?php
2
3/** PHPExcel root directory */
4if (!defined('PHPEXCEL_ROOT')) {
5    /**
6     * @ignore
7     */
8    define('PHPEXCEL_ROOT', dirname(__FILE__) . '/../');
9    require(PHPEXCEL_ROOT . 'PHPExcel/Autoloader.php');
10}
11
12if (!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 */
52class 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     * Epsilon Precision used for comparisons in calculations
212     *
213     * @var float
214     *
215     */
216    private $delta    = 0.1e-12;
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_MathTrig::SUMIFS',
1818            'argumentCount' => '3+'
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    public function __construct(PHPExcel $workbook = null)
2072    {
2073        $this->delta = 1 * pow(10, 0 - ini_get('precision'));
2074
2075        $this->workbook = $workbook;
2076        $this->cyclicReferenceStack = new PHPExcel_CalcEngine_CyclicReferenceStack();
2077        $this->_debugLog = new PHPExcel_CalcEngine_Logger($this->cyclicReferenceStack);
2078    }
2079
2080
2081    private static function loadLocales()
2082    {
2083        $localeFileDirectory = PHPEXCEL_ROOT.'PHPExcel/locale/';
2084        foreach (glob($localeFileDirectory.'/*', GLOB_ONLYDIR) as $filename) {
2085            $filename = substr($filename, strlen($localeFileDirectory)+1);
2086            if ($filename != 'en') {
2087                self::$validLocaleLanguages[] = $filename;
2088            }
2089        }
2090    }
2091
2092    /**
2093     * Get an instance of this class
2094     *
2095     * @access    public
2096     * @param   PHPExcel $workbook  Injected workbook for working with a PHPExcel object,
2097     *                                    or NULL to create a standalone claculation engine
2098     * @return PHPExcel_Calculation
2099     */
2100    public static function getInstance(PHPExcel $workbook = null)
2101    {
2102        if ($workbook !== null) {
2103            $instance = $workbook->getCalculationEngine();
2104            if (isset($instance)) {
2105                return $instance;
2106            }
2107        }
2108
2109        if (!isset(self::$instance) || (self::$instance === null)) {
2110            self::$instance = new PHPExcel_Calculation();
2111        }
2112        return self::$instance;
2113    }
2114
2115    /**
2116     * Unset an instance of this class
2117     *
2118     * @access    public
2119     */
2120    public function __destruct()
2121    {
2122        $this->workbook = null;
2123    }
2124
2125    /**
2126     * Flush the calculation cache for any existing instance of this class
2127     *        but only if a PHPExcel_Calculation instance exists
2128     *
2129     * @access    public
2130     * @return null
2131     */
2132    public function flushInstance()
2133    {
2134        $this->clearCalculationCache();
2135    }
2136
2137
2138    /**
2139     * Get the debuglog for this claculation engine instance
2140     *
2141     * @access    public
2142     * @return PHPExcel_CalcEngine_Logger
2143     */
2144    public function getDebugLog()
2145    {
2146        return $this->_debugLog;
2147    }
2148
2149    /**
2150     * __clone implementation. Cloning should not be allowed in a Singleton!
2151     *
2152     * @access    public
2153     * @throws    PHPExcel_Calculation_Exception
2154     */
2155    final public function __clone()
2156    {
2157        throw new PHPExcel_Calculation_Exception('Cloning the calculation engine is not allowed!');
2158    }
2159
2160
2161    /**
2162     * Return the locale-specific translation of TRUE
2163     *
2164     * @access    public
2165     * @return     string        locale-specific translation of TRUE
2166     */
2167    public static function getTRUE()
2168    {
2169        return self::$localeBoolean['TRUE'];
2170    }
2171
2172    /**
2173     * Return the locale-specific translation of FALSE
2174     *
2175     * @access    public
2176     * @return     string        locale-specific translation of FALSE
2177     */
2178    public static function getFALSE()
2179    {
2180        return self::$localeBoolean['FALSE'];
2181    }
2182
2183    /**
2184     * Set the Array Return Type (Array or Value of first element in the array)
2185     *
2186     * @access    public
2187     * @param     string    $returnType            Array return type
2188     * @return     boolean                    Success or failure
2189     */
2190    public static function setArrayReturnType($returnType)
2191    {
2192        if (($returnType == self::RETURN_ARRAY_AS_VALUE) ||
2193            ($returnType == self::RETURN_ARRAY_AS_ERROR) ||
2194            ($returnType == self::RETURN_ARRAY_AS_ARRAY)) {
2195            self::$returnArrayAsType = $returnType;
2196            return true;
2197        }
2198        return false;
2199    }
2200
2201
2202    /**
2203     * Return the Array Return Type (Array or Value of first element in the array)
2204     *
2205     * @access    public
2206     * @return     string        $returnType            Array return type
2207     */
2208    public static function getArrayReturnType()
2209    {
2210        return self::$returnArrayAsType;
2211    }
2212
2213
2214    /**
2215     * Is calculation caching enabled?
2216     *
2217     * @access    public
2218     * @return boolean
2219     */
2220    public function getCalculationCacheEnabled()
2221    {
2222        return $this->calculationCacheEnabled;
2223    }
2224
2225    /**
2226     * Enable/disable calculation cache
2227     *
2228     * @access    public
2229     * @param boolean $pValue
2230     */
2231    public function setCalculationCacheEnabled($pValue = true)
2232    {
2233        $this->calculationCacheEnabled = $pValue;
2234        $this->clearCalculationCache();
2235    }
2236
2237
2238    /**
2239     * Enable calculation cache
2240     */
2241    public function enableCalculationCache()
2242    {
2243        $this->setCalculationCacheEnabled(true);
2244    }
2245
2246
2247    /**
2248     * Disable calculation cache
2249     */
2250    public function disableCalculationCache()
2251    {
2252        $this->setCalculationCacheEnabled(false);
2253    }
2254
2255
2256    /**
2257     * Clear calculation cache
2258     */
2259    public function clearCalculationCache()
2260    {
2261        $this->calculationCache = array();
2262    }
2263
2264    /**
2265     * Clear calculation cache for a specified worksheet
2266     *
2267     * @param string $worksheetName
2268     */
2269    public function clearCalculationCacheForWorksheet($worksheetName)
2270    {
2271        if (isset($this->calculationCache[$worksheetName])) {
2272            unset($this->calculationCache[$worksheetName]);
2273        }
2274    }
2275
2276    /**
2277     * Rename calculation cache for a specified worksheet
2278     *
2279     * @param string $fromWorksheetName
2280     * @param string $toWorksheetName
2281     */
2282    public function renameCalculationCacheForWorksheet($fromWorksheetName, $toWorksheetName)
2283    {
2284        if (isset($this->calculationCache[$fromWorksheetName])) {
2285            $this->calculationCache[$toWorksheetName] = &$this->calculationCache[$fromWorksheetName];
2286            unset($this->calculationCache[$fromWorksheetName]);
2287        }
2288    }
2289
2290
2291    /**
2292     * Get the currently defined locale code
2293     *
2294     * @return string
2295     */
2296    public function getLocale()
2297    {
2298        return self::$localeLanguage;
2299    }
2300
2301
2302    /**
2303     * Set the locale code
2304     *
2305     * @param string $locale  The locale to use for formula translation
2306     * @return boolean
2307     */
2308    public function setLocale($locale = 'en_us')
2309    {
2310        //    Identify our locale and language
2311        $language = $locale = strtolower($locale);
2312        if (strpos($locale, '_') !== false) {
2313            list($language) = explode('_', $locale);
2314        }
2315
2316        if (count(self::$validLocaleLanguages) == 1) {
2317            self::loadLocales();
2318        }
2319        //    Test whether we have any language data for this language (any locale)
2320        if (in_array($language, self::$validLocaleLanguages)) {
2321            //    initialise language/locale settings
2322            self::$localeFunctions = array();
2323            self::$localeArgumentSeparator = ',';
2324            self::$localeBoolean = array('TRUE' => 'TRUE', 'FALSE' => 'FALSE', 'NULL' => 'NULL');
2325            //    Default is English, if user isn't requesting english, then read the necessary data from the locale files
2326            if ($locale != 'en_us') {
2327                //    Search for a file with a list of function names for locale
2328                $functionNamesFile = PHPEXCEL_ROOT . 'PHPExcel'.DIRECTORY_SEPARATOR.'locale'.DIRECTORY_SEPARATOR.str_replace('_', DIRECTORY_SEPARATOR, $locale).DIRECTORY_SEPARATOR.'functions';
2329                if (!file_exists($functionNamesFile)) {
2330                    //    If there isn't a locale specific function file, look for a language specific function file
2331                    $functionNamesFile = PHPEXCEL_ROOT . 'PHPExcel'.DIRECTORY_SEPARATOR.'locale'.DIRECTORY_SEPARATOR.$language.DIRECTORY_SEPARATOR.'functions';
2332                    if (!file_exists($functionNamesFile)) {
2333                        return false;
2334                    }
2335                }
2336                //    Retrieve the list of locale or language specific function names
2337                $localeFunctions = file($functionNamesFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
2338                foreach ($localeFunctions as $localeFunction) {
2339                    list($localeFunction) = explode('##', $localeFunction);    //    Strip out comments
2340                    if (strpos($localeFunction, '=') !== false) {
2341                        list($fName, $lfName) = explode('=', $localeFunction);
2342                        $fName = trim($fName);
2343                        $lfName = trim($lfName);
2344                        if ((isset(self::$PHPExcelFunctions[$fName])) && ($lfName != '') && ($fName != $lfName)) {
2345                            self::$localeFunctions[$fName] = $lfName;
2346                        }
2347                    }
2348                }
2349                //    Default the TRUE and FALSE constants to the locale names of the TRUE() and FALSE() functions
2350                if (isset(self::$localeFunctions['TRUE'])) {
2351                    self::$localeBoolean['TRUE'] = self::$localeFunctions['TRUE'];
2352                }
2353                if (isset(self::$localeFunctions['FALSE'])) {
2354                    self::$localeBoolean['FALSE'] = self::$localeFunctions['FALSE'];
2355                }
2356
2357                $configFile = PHPEXCEL_ROOT . 'PHPExcel'.DIRECTORY_SEPARATOR.'locale'.DIRECTORY_SEPARATOR.str_replace('_', DIRECTORY_SEPARATOR, $locale).DIRECTORY_SEPARATOR.'config';
2358                if (!file_exists($configFile)) {
2359                    $configFile = PHPEXCEL_ROOT . 'PHPExcel'.DIRECTORY_SEPARATOR.'locale'.DIRECTORY_SEPARATOR.$language.DIRECTORY_SEPARATOR.'config';
2360                }
2361                if (file_exists($configFile)) {
2362                    $localeSettings = file($configFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
2363                    foreach ($localeSettings as $localeSetting) {
2364                        list($localeSetting) = explode('##', $localeSetting);    //    Strip out comments
2365                        if (strpos($localeSetting, '=') !== false) {
2366                            list($settingName, $settingValue) = explode('=', $localeSetting);
2367                            $settingName = strtoupper(trim($settingName));
2368                            switch ($settingName) {
2369                                case 'ARGUMENTSEPARATOR':
2370                                    self::$localeArgumentSeparator = trim($settingValue);
2371                                    break;
2372                            }
2373                        }
2374                    }
2375                }
2376            }
2377
2378            self::$functionReplaceFromExcel = self::$functionReplaceToExcel =
2379            self::$functionReplaceFromLocale = self::$functionReplaceToLocale = null;
2380            self::$localeLanguage = $locale;
2381            return true;
2382        }
2383        return false;
2384    }
2385
2386
2387
2388    public static function translateSeparator($fromSeparator, $toSeparator, $formula, &$inBraces)
2389    {
2390        $strlen = mb_strlen($formula);
2391        for ($i = 0; $i < $strlen; ++$i) {
2392            $chr = mb_substr($formula, $i, 1);
2393            switch ($chr) {
2394                case '{':
2395                    $inBraces = true;
2396                    break;
2397                case '}':
2398                    $inBraces = false;
2399                    break;
2400                case $fromSeparator:
2401                    if (!$inBraces) {
2402                        $formula = mb_substr($formula, 0, $i).$toSeparator.mb_substr($formula, $i+1);
2403                    }
2404            }
2405        }
2406        return $formula;
2407    }
2408
2409    private static function translateFormula($from, $to, $formula, $fromSeparator, $toSeparator)
2410    {
2411        //    Convert any Excel function names to the required language
2412        if (self::$localeLanguage !== 'en_us') {
2413            $inBraces = false;
2414            //    If there is the possibility of braces within a quoted string, then we don't treat those as matrix indicators
2415            if (strpos($formula, '"') !== false) {
2416                //    So instead we skip replacing in any quoted strings by only replacing in every other array element after we've exploded
2417                //        the formula
2418                $temp = explode('"', $formula);
2419                $i = false;
2420                foreach ($temp as &$value) {
2421                    //    Only count/replace in alternating array entries
2422                    if ($i = !$i) {
2423                        $value = preg_replace($from, $to, $value);
2424                        $value = self::translateSeparator($fromSeparator, $toSeparator, $value, $inBraces);
2425                    }
2426                }
2427                unset($value);
2428                //    Then rebuild the formula string
2429                $formula = implode('"', $temp);
2430            } else {
2431                //    If there's no quoted strings, then we do a simple count/replace
2432                $formula = preg_replace($from, $to, $formula);
2433                $formula = self::translateSeparator($fromSeparator, $toSeparator, $formula, $inBraces);
2434            }
2435        }
2436
2437        return $formula;
2438    }
2439
2440    private static $functionReplaceFromExcel = null;
2441    private static $functionReplaceToLocale  = null;
2442
2443    public function _translateFormulaToLocale($formula)
2444    {
2445        if (self::$functionReplaceFromExcel === null) {
2446            self::$functionReplaceFromExcel = array();
2447            foreach (array_keys(self::$localeFunctions) as $excelFunctionName) {
2448                self::$functionReplaceFromExcel[] = '/(@?[^\w\.])'.preg_quote($excelFunctionName).'([\s]*\()/Ui';
2449            }
2450            foreach (array_keys(self::$localeBoolean) as $excelBoolean) {
2451                self::$functionReplaceFromExcel[] = '/(@?[^\w\.])'.preg_quote($excelBoolean).'([^\w\.])/Ui';
2452            }
2453
2454        }
2455
2456        if (self::$functionReplaceToLocale === null) {
2457            self::$functionReplaceToLocale = array();
2458            foreach (array_values(self::$localeFunctions) as $localeFunctionName) {
2459                self::$functionReplaceToLocale[] = '$1'.trim($localeFunctionName).'$2';
2460            }
2461            foreach (array_values(self::$localeBoolean) as $localeBoolean) {
2462                self::$functionReplaceToLocale[] = '$1'.trim($localeBoolean).'$2';
2463            }
2464        }
2465
2466        return self::translateFormula(self::$functionReplaceFromExcel, self::$functionReplaceToLocale, $formula, ',', self::$localeArgumentSeparator);
2467    }
2468
2469
2470    private static $functionReplaceFromLocale = null;
2471    private static $functionReplaceToExcel    = null;
2472
2473    public function _translateFormulaToEnglish($formula)
2474    {
2475        if (self::$functionReplaceFromLocale === null) {
2476            self::$functionReplaceFromLocale = array();
2477            foreach (array_values(self::$localeFunctions) as $localeFunctionName) {
2478                self::$functionReplaceFromLocale[] = '/(@?[^\w\.])'.preg_quote($localeFunctionName).'([\s]*\()/Ui';
2479            }
2480            foreach (array_values(self::$localeBoolean) as $excelBoolean) {
2481                self::$functionReplaceFromLocale[] = '/(@?[^\w\.])'.preg_quote($excelBoolean).'([^\w\.])/Ui';
2482            }
2483        }
2484
2485        if (self::$functionReplaceToExcel === null) {
2486            self::$functionReplaceToExcel = array();
2487            foreach (array_keys(self::$localeFunctions) as $excelFunctionName) {
2488                self::$functionReplaceToExcel[] = '$1'.trim($excelFunctionName).'$2';
2489            }
2490            foreach (array_keys(self::$localeBoolean) as $excelBoolean) {
2491                self::$functionReplaceToExcel[] = '$1'.trim($excelBoolean).'$2';
2492            }
2493        }
2494
2495        return self::translateFormula(self::$functionReplaceFromLocale, self::$functionReplaceToExcel, $formula, self::$localeArgumentSeparator, ',');
2496    }
2497
2498
2499    public static function localeFunc($function)
2500    {
2501        if (self::$localeLanguage !== 'en_us') {
2502            $functionName = trim($function, '(');
2503            if (isset(self::$localeFunctions[$functionName])) {
2504                $brace = ($functionName != $function);
2505                $function = self::$localeFunctions[$functionName];
2506                if ($brace) {
2507                    $function .= '(';
2508                }
2509            }
2510        }
2511        return $function;
2512    }
2513
2514
2515
2516
2517    /**
2518     * Wrap string values in quotes
2519     *
2520     * @param mixed $value
2521     * @return mixed
2522     */
2523    public static function wrapResult($value)
2524    {
2525        if (is_string($value)) {
2526            //    Error values cannot be "wrapped"
2527            if (preg_match('/^'.self::CALCULATION_REGEXP_ERROR.'$/i', $value, $match)) {
2528                //    Return Excel errors "as is"
2529                return $value;
2530            }
2531            //    Return strings wrapped in quotes
2532            return '"'.$value.'"';
2533        //    Convert numeric errors to NaN error
2534        } elseif ((is_float($value)) && ((is_nan($value)) || (is_infinite($value)))) {
2535            return PHPExcel_Calculation_Functions::NaN();
2536        }
2537
2538        return $value;
2539    }
2540
2541
2542    /**
2543     * Remove quotes used as a wrapper to identify string values
2544     *
2545     * @param mixed $value
2546     * @return mixed
2547     */
2548    public static function unwrapResult($value)
2549    {
2550        if (is_string($value)) {
2551            if ((isset($value{0})) && ($value{0} == '"') && (substr($value, -1) == '"')) {
2552                return substr($value, 1, -1);
2553            }
2554        //    Convert numeric errors to NaN error
2555        } elseif ((is_float($value)) && ((is_nan($value)) || (is_infinite($value)))) {
2556            return PHPExcel_Calculation_Functions::NaN();
2557        }
2558        return $value;
2559    }
2560
2561
2562
2563
2564    /**
2565     * Calculate cell value (using formula from a cell ID)
2566     * Retained for backward compatibility
2567     *
2568     * @access    public
2569     * @param    PHPExcel_Cell    $pCell    Cell to calculate
2570     * @return    mixed
2571     * @throws    PHPExcel_Calculation_Exception
2572     */
2573    public function calculate(PHPExcel_Cell $pCell = null)
2574    {
2575        try {
2576            return $this->calculateCellValue($pCell);
2577        } catch (PHPExcel_Exception $e) {
2578            throw new PHPExcel_Calculation_Exception($e->getMessage());
2579        }
2580    }
2581
2582
2583    /**
2584     * Calculate the value of a cell formula
2585     *
2586     * @access    public
2587     * @param    PHPExcel_Cell    $pCell        Cell to calculate
2588     * @param    Boolean            $resetLog    Flag indicating whether the debug log should be reset or not
2589     * @return    mixed
2590     * @throws    PHPExcel_Calculation_Exception
2591     */
2592    public function calculateCellValue(PHPExcel_Cell $pCell = null, $resetLog = true)
2593    {
2594        if ($pCell === null) {
2595            return null;
2596        }
2597
2598        $returnArrayAsType = self::$returnArrayAsType;
2599        if ($resetLog) {
2600            //    Initialise the logging settings if requested
2601            $this->formulaError = null;
2602            $this->_debugLog->clearLog();
2603            $this->cyclicReferenceStack->clear();
2604            $this->cyclicFormulaCounter = 1;
2605
2606            self::$returnArrayAsType = self::RETURN_ARRAY_AS_ARRAY;
2607        }
2608
2609        //    Execute the calculation for the cell formula
2610        $this->cellStack[] = array(
2611            'sheet' => $pCell->getWorksheet()->getTitle(),
2612            'cell' => $pCell->getCoordinate(),
2613        );
2614        try {
2615            $result = self::unwrapResult($this->_calculateFormulaValue($pCell->getValue(), $pCell->getCoordinate(), $pCell));
2616            $cellAddress = array_pop($this->cellStack);
2617            $this->workbook->getSheetByName($cellAddress['sheet'])->getCell($cellAddress['cell']);
2618        } catch (PHPExcel_Exception $e) {
2619            $cellAddress = array_pop($this->cellStack);
2620            $this->workbook->getSheetByName($cellAddress['sheet'])->getCell($cellAddress['cell']);
2621            throw new PHPExcel_Calculation_Exception($e->getMessage());
2622        }
2623
2624        if ((is_array($result)) && (self::$returnArrayAsType != self::RETURN_ARRAY_AS_ARRAY)) {
2625            self::$returnArrayAsType = $returnArrayAsType;
2626            $testResult = PHPExcel_Calculation_Functions::flattenArray($result);
2627            if (self::$returnArrayAsType == self::RETURN_ARRAY_AS_ERROR) {
2628                return PHPExcel_Calculation_Functions::VALUE();
2629            }
2630            //    If there's only a single cell in the array, then we allow it
2631            if (count($testResult) != 1) {
2632                //    If keys are numeric, then it's a matrix result rather than a cell range result, so we permit it
2633                $r = array_keys($result);
2634                $r = array_shift($r);
2635                if (!is_numeric($r)) {
2636                    return PHPExcel_Calculation_Functions::VALUE();
2637                }
2638                if (is_array($result[$r])) {
2639                    $c = array_keys($result[$r]);
2640                    $c = array_shift($c);
2641                    if (!is_numeric($c)) {
2642                        return PHPExcel_Calculation_Functions::VALUE();
2643                    }
2644                }
2645            }
2646            $result = array_shift($testResult);
2647        }
2648        self::$returnArrayAsType = $returnArrayAsType;
2649
2650
2651        if ($result === null) {
2652            return 0;
2653        } elseif ((is_float($result)) && ((is_nan($result)) || (is_infinite($result)))) {
2654            return PHPExcel_Calculation_Functions::NaN();
2655        }
2656        return $result;
2657    }
2658
2659
2660    /**
2661     * Validate and parse a formula string
2662     *
2663     * @param    string        $formula        Formula to parse
2664     * @return    array
2665     * @throws    PHPExcel_Calculation_Exception
2666     */
2667    public function parseFormula($formula)
2668    {
2669        //    Basic validation that this is indeed a formula
2670        //    We return an empty array if not
2671        $formula = trim($formula);
2672        if ((!isset($formula{0})) || ($formula{0} != '=')) {
2673            return array();
2674        }
2675        $formula = ltrim(substr($formula, 1));
2676        if (!isset($formula{0})) {
2677            return array();
2678        }
2679
2680        //    Parse the formula and return the token stack
2681        return $this->_parseFormula($formula);
2682    }
2683
2684
2685    /**
2686     * Calculate the value of a formula
2687     *
2688     * @param    string            $formula    Formula to parse
2689     * @param    string            $cellID        Address of the cell to calculate
2690     * @param    PHPExcel_Cell    $pCell        Cell to calculate
2691     * @return    mixed
2692     * @throws    PHPExcel_Calculation_Exception
2693     */
2694    public function calculateFormula($formula, $cellID = null, PHPExcel_Cell $pCell = null)
2695    {
2696        //    Initialise the logging settings
2697        $this->formulaError = null;
2698        $this->_debugLog->clearLog();
2699        $this->cyclicReferenceStack->clear();
2700
2701        if ($this->workbook !== null && $cellID === null && $pCell === null) {
2702            $cellID = 'A1';
2703            $pCell = $this->workbook->getActiveSheet()->getCell($cellID);
2704        } else {
2705            //    Disable calculation cacheing because it only applies to cell calculations, not straight formulae
2706            //    But don't actually flush any cache
2707            $resetCache = $this->getCalculationCacheEnabled();
2708            $this->calculationCacheEnabled = false;
2709        }
2710
2711        //    Execute the calculation
2712        try {
2713            $result = self::unwrapResult($this->_calculateFormulaValue($formula, $cellID, $pCell));
2714        } catch (PHPExcel_Exception $e) {
2715            throw new PHPExcel_Calculation_Exception($e->getMessage());
2716        }
2717
2718        if ($this->workbook === null) {
2719            //    Reset calculation cacheing to its previous state
2720            $this->calculationCacheEnabled = $resetCache;
2721        }
2722
2723        return $result;
2724    }
2725
2726
2727    public function getValueFromCache($cellReference, &$cellValue)
2728    {
2729        // Is calculation cacheing enabled?
2730        // Is the value present in calculation cache?
2731        $this->_debugLog->writeDebugLog('Testing cache value for cell ', $cellReference);
2732        if (($this->calculationCacheEnabled) && (isset($this->calculationCache[$cellReference]))) {
2733            $this->_debugLog->writeDebugLog('Retrieving value for cell ', $cellReference, ' from cache');
2734            // Return the cached result
2735            $cellValue = $this->calculationCache[$cellReference];
2736            return true;
2737        }
2738        return false;
2739    }
2740
2741    public function saveValueToCache($cellReference, $cellValue)
2742    {
2743        if ($this->calculationCacheEnabled) {
2744            $this->calculationCache[$cellReference] = $cellValue;
2745        }
2746    }
2747
2748    /**
2749     * Parse a cell formula and calculate its value
2750     *
2751     * @param    string            $formula    The formula to parse and calculate
2752     * @param    string            $cellID        The ID (e.g. A3) of the cell that we are calculating
2753     * @param    PHPExcel_Cell    $pCell        Cell to calculate
2754     * @return    mixed
2755     * @throws    PHPExcel_Calculation_Exception
2756     */
2757    public function _calculateFormulaValue($formula, $cellID = null, PHPExcel_Cell $pCell = null)
2758    {
2759        $cellValue = null;
2760
2761        //    Basic validation that this is indeed a formula
2762        //    We simply return the cell value if not
2763        $formula = trim($formula);
2764        if ($formula{0} != '=') {
2765            return self::wrapResult($formula);
2766        }
2767        $formula = ltrim(substr($formula, 1));
2768        if (!isset($formula{0})) {
2769            return self::wrapResult($formula);
2770        }
2771
2772        $pCellParent = ($pCell !== null) ? $pCell->getWorksheet() : null;
2773        $wsTitle = ($pCellParent !== null) ? $pCellParent->getTitle() : "\x00Wrk";
2774        $wsCellReference = $wsTitle . '!' . $cellID;
2775
2776        if (($cellID !== null) && ($this->getValueFromCache($wsCellReference, $cellValue))) {
2777            return $cellValue;
2778        }
2779
2780        if (($wsTitle{0} !== "\x00") && ($this->cyclicReferenceStack->onStack($wsCellReference))) {
2781            if ($this->cyclicFormulaCount <= 0) {
2782                $this->cyclicFormulaCell = '';
2783                return $this->raiseFormulaError('Cyclic Reference in Formula');
2784            } elseif ($this->cyclicFormulaCell === $wsCellReference) {
2785                ++$this->cyclicFormulaCounter;
2786                if ($this->cyclicFormulaCounter >= $this->cyclicFormulaCount) {
2787                    $this->cyclicFormulaCell = '';
2788                    return $cellValue;
2789                }
2790            } elseif ($this->cyclicFormulaCell == '') {
2791                if ($this->cyclicFormulaCounter >= $this->cyclicFormulaCount) {
2792                    return $cellValue;
2793                }
2794                $this->cyclicFormulaCell = $wsCellReference;
2795            }
2796        }
2797
2798        //    Parse the formula onto the token stack and calculate the value
2799        $this->cyclicReferenceStack->push($wsCellReference);
2800        $cellValue = $this->processTokenStack($this->_parseFormula($formula, $pCell), $cellID, $pCell);
2801        $this->cyclicReferenceStack->pop();
2802
2803        // Save to calculation cache
2804        if ($cellID !== null) {
2805            $this->saveValueToCache($wsCellReference, $cellValue);
2806        }
2807
2808        //    Return the calculated value
2809        return $cellValue;
2810    }
2811
2812
2813    /**
2814     * Ensure that paired matrix operands are both matrices and of the same size
2815     *
2816     * @param    mixed        &$operand1    First matrix operand
2817     * @param    mixed        &$operand2    Second matrix operand
2818     * @param    integer        $resize        Flag indicating whether the matrices should be resized to match
2819     *                                        and (if so), whether the smaller dimension should grow or the
2820     *                                        larger should shrink.
2821     *                                            0 = no resize
2822     *                                            1 = shrink to fit
2823     *                                            2 = extend to fit
2824     */
2825    private static function checkMatrixOperands(&$operand1, &$operand2, $resize = 1)
2826    {
2827        //    Examine each of the two operands, and turn them into an array if they aren't one already
2828        //    Note that this function should only be called if one or both of the operand is already an array
2829        if (!is_array($operand1)) {
2830            list($matrixRows, $matrixColumns) = self::getMatrixDimensions($operand2);
2831            $operand1 = array_fill(0, $matrixRows, array_fill(0, $matrixColumns, $operand1));
2832            $resize = 0;
2833        } elseif (!is_array($operand2)) {
2834            list($matrixRows, $matrixColumns) = self::getMatrixDimensions($operand1);
2835            $operand2 = array_fill(0, $matrixRows, array_fill(0, $matrixColumns, $operand2));
2836            $resize = 0;
2837        }
2838
2839        list($matrix1Rows, $matrix1Columns) = self::getMatrixDimensions($operand1);
2840        list($matrix2Rows, $matrix2Columns) = self::getMatrixDimensions($operand2);
2841        if (($matrix1Rows == $matrix2Columns) && ($matrix2Rows == $matrix1Columns)) {
2842            $resize = 1;
2843        }
2844
2845        if ($resize == 2) {
2846            //    Given two matrices of (potentially) unequal size, convert the smaller in each dimension to match the larger
2847            self::resizeMatricesExtend($operand1, $operand2, $matrix1Rows, $matrix1Columns, $matrix2Rows, $matrix2Columns);
2848        } elseif ($resize == 1) {
2849            //    Given two matrices of (potentially) unequal size, convert the larger in each dimension to match the smaller
2850            self::resizeMatricesShrink($operand1, $operand2, $matrix1Rows, $matrix1Columns, $matrix2Rows, $matrix2Columns);
2851        }
2852        return array( $matrix1Rows, $matrix1Columns, $matrix2Rows, $matrix2Columns);
2853    }
2854
2855
2856    /**
2857     * Read the dimensions of a matrix, and re-index it with straight numeric keys starting from row 0, column 0
2858     *
2859     * @param    mixed        &$matrix        matrix operand
2860     * @return    array        An array comprising the number of rows, and number of columns
2861     */
2862    private static function getMatrixDimensions(&$matrix)
2863    {
2864        $matrixRows = count($matrix);
2865        $matrixColumns = 0;
2866        foreach ($matrix as $rowKey => $rowValue) {
2867            $matrixColumns = max(count($rowValue), $matrixColumns);
2868            if (!is_array($rowValue)) {
2869                $matrix[$rowKey] = array($rowValue);
2870            } else {
2871                $matrix[$rowKey] = array_values($rowValue);
2872            }
2873        }
2874        $matrix = array_values($matrix);
2875        return array($matrixRows, $matrixColumns);
2876    }
2877
2878
2879    /**
2880     * Ensure that paired matrix operands are both matrices of the same size
2881     *
2882     * @param    mixed        &$matrix1        First matrix operand
2883     * @param    mixed        &$matrix2        Second matrix operand
2884     * @param    integer        $matrix1Rows    Row size of first matrix operand
2885     * @param    integer        $matrix1Columns    Column size of first matrix operand
2886     * @param    integer        $matrix2Rows    Row size of second matrix operand
2887     * @param    integer        $matrix2Columns    Column size of second matrix operand
2888     */
2889    private static function resizeMatricesShrink(&$matrix1, &$matrix2, $matrix1Rows, $matrix1Columns, $matrix2Rows, $matrix2Columns)
2890    {
2891        if (($matrix2Columns < $matrix1Columns) || ($matrix2Rows < $matrix1Rows)) {
2892            if ($matrix2Rows < $matrix1Rows) {
2893                for ($i = $matrix2Rows; $i < $matrix1Rows; ++$i) {
2894                    unset($matrix1[$i]);
2895                }
2896            }
2897            if ($matrix2Columns < $matrix1Columns) {
2898                for ($i = 0; $i < $matrix1Rows; ++$i) {
2899                    for ($j = $matrix2Columns; $j < $matrix1Columns; ++$j) {
2900                        unset($matrix1[$i][$j]);
2901                    }
2902                }
2903            }
2904        }
2905
2906        if (($matrix1Columns < $matrix2Columns) || ($matrix1Rows < $matrix2Rows)) {
2907            if ($matrix1Rows < $matrix2Rows) {
2908                for ($i = $matrix1Rows; $i < $matrix2Rows; ++$i) {
2909                    unset($matrix2[$i]);
2910                }
2911            }
2912            if ($matrix1Columns < $matrix2Columns) {
2913                for ($i = 0; $i < $matrix2Rows; ++$i) {
2914                    for ($j = $matrix1Columns; $j < $matrix2Columns; ++$j) {
2915                        unset($matrix2[$i][$j]);
2916                    }
2917                }
2918            }
2919        }
2920    }
2921
2922
2923    /**
2924     * Ensure that paired matrix operands are both matrices of the same size
2925     *
2926     * @param    mixed        &$matrix1    First matrix operand
2927     * @param    mixed        &$matrix2    Second matrix operand
2928     * @param    integer        $matrix1Rows    Row size of first matrix operand
2929     * @param    integer        $matrix1Columns    Column size of first matrix operand
2930     * @param    integer        $matrix2Rows    Row size of second matrix operand
2931     * @param    integer        $matrix2Columns    Column size of second matrix operand
2932     */
2933    private static function resizeMatricesExtend(&$matrix1, &$matrix2, $matrix1Rows, $matrix1Columns, $matrix2Rows, $matrix2Columns)
2934    {
2935        if (($matrix2Columns < $matrix1Columns) || ($matrix2Rows < $matrix1Rows)) {
2936            if ($matrix2Columns < $matrix1Columns) {
2937                for ($i = 0; $i < $matrix2Rows; ++$i) {
2938                    $x = $matrix2[$i][$matrix2Columns-1];
2939                    for ($j = $matrix2Columns; $j < $matrix1Columns; ++$j) {
2940                        $matrix2[$i][$j] = $x;
2941                    }
2942                }
2943            }
2944            if ($matrix2Rows < $matrix1Rows) {
2945                $x = $matrix2[$matrix2Rows-1];
2946                for ($i = 0; $i < $matrix1Rows; ++$i) {
2947                    $matrix2[$i] = $x;
2948                }
2949            }
2950        }
2951
2952        if (($matrix1Columns < $matrix2Columns) || ($matrix1Rows < $matrix2Rows)) {
2953            if ($matrix1Columns < $matrix2Columns) {
2954                for ($i = 0; $i < $matrix1Rows; ++$i) {
2955                    $x = $matrix1[$i][$matrix1Columns-1];
2956                    for ($j = $matrix1Columns; $j < $matrix2Columns; ++$j) {
2957                        $matrix1[$i][$j] = $x;
2958                    }
2959                }
2960            }
2961            if ($matrix1Rows < $matrix2Rows) {
2962                $x = $matrix1[$matrix1Rows-1];
2963                for ($i = 0; $i < $matrix2Rows; ++$i) {
2964                    $matrix1[$i] = $x;
2965                }
2966            }
2967        }
2968    }
2969
2970
2971    /**
2972     * Format details of an operand for display in the log (based on operand type)
2973     *
2974     * @param    mixed        $value    First matrix operand
2975     * @return    mixed
2976     */
2977    private function showValue($value)
2978    {
2979        if ($this->_debugLog->getWriteDebugLog()) {
2980            $testArray = PHPExcel_Calculation_Functions::flattenArray($value);
2981            if (count($testArray) == 1) {
2982                $value = array_pop($testArray);
2983            }
2984
2985            if (is_array($value)) {
2986                $returnMatrix = array();
2987                $pad = $rpad = ', ';
2988                foreach ($value as $row) {
2989                    if (is_array($row)) {
2990                        $returnMatrix[] = implode($pad, array_map(array($this, 'showValue'), $row));
2991                        $rpad = '; ';
2992                    } else {
2993                        $returnMatrix[] = $this->showValue($row);
2994                    }
2995                }
2996                return '{ '.implode($rpad, $returnMatrix).' }';
2997            } elseif (is_string($value) && (trim($value, '"') == $value)) {
2998                return '"'.$value.'"';
2999            } elseif (is_bool($value)) {
3000                return ($value) ? self::$localeBoolean['TRUE'] : self::$localeBoolean['FALSE'];
3001            }
3002        }
3003        return PHPExcel_Calculation_Functions::flattenSingleValue($value);
3004    }
3005
3006
3007    /**
3008     * Format type and details of an operand for display in the log (based on operand type)
3009     *
3010     * @param    mixed        $value    First matrix operand
3011     * @return    mixed
3012     */
3013    private function showTypeDetails($value)
3014    {
3015        if ($this->_debugLog->getWriteDebugLog()) {
3016            $testArray = PHPExcel_Calculation_Functions::flattenArray($value);
3017            if (count($testArray) == 1) {
3018                $value = array_pop($testArray);
3019            }
3020
3021            if ($value === null) {
3022                return 'a NULL value';
3023            } elseif (is_float($value)) {
3024                $typeString = 'a floating point number';
3025            } elseif (is_int($value)) {
3026                $typeString = 'an integer number';
3027            } elseif (is_bool($value)) {
3028                $typeString = 'a boolean';
3029            } elseif (is_array($value)) {
3030                $typeString = 'a matrix';
3031            } else {
3032                if ($value == '') {
3033                    return 'an empty string';
3034                } elseif ($value{0} == '#') {
3035                    return 'a '.$value.' error';
3036                } else {
3037                    $typeString = 'a string';
3038                }
3039            }
3040            return $typeString.' with a value of '.$this->showValue($value);
3041        }
3042    }
3043
3044
3045    private function convertMatrixReferences($formula)
3046    {
3047        static $matrixReplaceFrom = array('{', ';', '}');
3048        static $matrixReplaceTo = array('MKMATRIX(MKMATRIX(', '),MKMATRIX(', '))');
3049
3050        //    Convert any Excel matrix references to the MKMATRIX() function
3051        if (strpos($formula, '{') !== false) {
3052            //    If there is the possibility of braces within a quoted string, then we don't treat those as matrix indicators
3053            if (strpos($formula, '"') !== false) {
3054                //    So instead we skip replacing in any quoted strings by only replacing in every other array element after we've exploded
3055                //        the formula
3056                $temp = explode('"', $formula);
3057                //    Open and Closed counts used for trapping mismatched braces in the formula
3058                $openCount = $closeCount = 0;
3059                $i = false;
3060                foreach ($temp as &$value) {
3061                    //    Only count/replace in alternating array entries
3062                    if ($i = !$i) {
3063                        $openCount += substr_count($value, '{');
3064                        $closeCount += substr_count($value, '}');
3065                        $value = str_replace($matrixReplaceFrom, $matrixReplaceTo, $value);
3066                    }
3067                }
3068                unset($value);
3069                //    Then rebuild the formula string
3070                $formula = implode('"', $temp);
3071            } else {
3072                //    If there's no quoted strings, then we do a simple count/replace
3073                $openCount = substr_count($formula, '{');
3074                $closeCount = substr_count($formula, '}');
3075                $formula = str_replace($matrixReplaceFrom, $matrixReplaceTo, $formula);
3076            }
3077            //    Trap for mismatched braces and trigger an appropriate error
3078            if ($openCount < $closeCount) {
3079                if ($openCount > 0) {
3080                    return $this->raiseFormulaError("Formula Error: Mismatched matrix braces '}'");
3081                } else {
3082                    return $this->raiseFormulaError("Formula Error: Unexpected '}' encountered");
3083                }
3084            } elseif ($openCount > $closeCount) {
3085                if ($closeCount > 0) {
3086                    return $this->raiseFormulaError("Formula Error: Mismatched matrix braces '{'");
3087                } else {
3088                    return $this->raiseFormulaError("Formula Error: Unexpected '{' encountered");
3089                }
3090            }
3091        }
3092
3093        return $formula;
3094    }
3095
3096
3097    private static function mkMatrix()
3098    {
3099        return func_get_args();
3100    }
3101
3102
3103    //    Binary Operators
3104    //    These operators always work on two values
3105    //    Array key is the operator, the value indicates whether this is a left or right associative operator
3106    private static $operatorAssociativity    = array(
3107        '^' => 0,                                                            //    Exponentiation
3108        '*' => 0, '/' => 0,                                                 //    Multiplication and Division
3109        '+' => 0, '-' => 0,                                                    //    Addition and Subtraction
3110        '&' => 0,                                                            //    Concatenation
3111        '|' => 0, ':' => 0,                                                    //    Intersect and Range
3112        '>' => 0, '<' => 0, '=' => 0, '>=' => 0, '<=' => 0, '<>' => 0        //    Comparison
3113    );
3114
3115    //    Comparison (Boolean) Operators
3116    //    These operators work on two values, but always return a boolean result
3117    private static $comparisonOperators    = array('>' => true, '<' => true, '=' => true, '>=' => true, '<=' => true, '<>' => true);
3118
3119    //    Operator Precedence
3120    //    This list includes all valid operators, whether binary (including boolean) or unary (such as %)
3121    //    Array key is the operator, the value is its precedence
3122    private static $operatorPrecedence    = array(
3123        ':' => 8,                                                                //    Range
3124        '|' => 7,                                                                //    Intersect
3125        '~' => 6,                                                                //    Negation
3126        '%' => 5,                                                                //    Percentage
3127        '^' => 4,                                                                //    Exponentiation
3128        '*' => 3, '/' => 3,                                                     //    Multiplication and Division
3129        '+' => 2, '-' => 2,                                                        //    Addition and Subtraction
3130        '&' => 1,                                                                //    Concatenation
3131        '>' => 0, '<' => 0, '=' => 0, '>=' => 0, '<=' => 0, '<>' => 0            //    Comparison
3132    );
3133
3134    // Convert infix to postfix notation
3135    private function _parseFormula($formula, PHPExcel_Cell $pCell = null)
3136    {
3137        if (($formula = $this->convertMatrixReferences(trim($formula))) === false) {
3138            return false;
3139        }
3140
3141        //    If we're using cell caching, then $pCell may well be flushed back to the cache (which detaches the parent worksheet),
3142        //        so we store the parent worksheet so that we can re-attach it when necessary
3143        $pCellParent = ($pCell !== null) ? $pCell->getWorksheet() : null;
3144
3145        $regexpMatchString = '/^('.self::CALCULATION_REGEXP_FUNCTION.
3146                               '|'.self::CALCULATION_REGEXP_CELLREF.
3147                               '|'.self::CALCULATION_REGEXP_NUMBER.
3148                               '|'.self::CALCULATION_REGEXP_STRING.
3149                               '|'.self::CALCULATION_REGEXP_OPENBRACE.
3150                               '|'.self::CALCULATION_REGEXP_NAMEDRANGE.
3151                               '|'.self::CALCULATION_REGEXP_ERROR.
3152                             ')/si';
3153
3154        //    Start with initialisation
3155        $index = 0;
3156        $stack = new PHPExcel_Calculation_Token_Stack;
3157        $output = array();
3158        $expectingOperator = false;                    //    We use this test in syntax-checking the expression to determine when a
3159                                                    //        - is a negation or + is a positive operator rather than an operation
3160        $expectingOperand = false;                    //    We use this test in syntax-checking the expression to determine whether an operand
3161                                                    //        should be null in a function call
3162        //    The guts of the lexical parser
3163        //    Loop through the formula extracting each operator and operand in turn
3164        while (true) {
3165//echo 'Assessing Expression '.substr($formula, $index), PHP_EOL;
3166            $opCharacter = $formula{$index};    //    Get the first character of the value at the current index position
3167//echo 'Initial character of expression block is '.$opCharacter, PHP_EOL;
3168            if ((isset(self::$comparisonOperators[$opCharacter])) && (strlen($formula) > $index) && (isset(self::$comparisonOperators[$formula{$index+1}]))) {
3169                $opCharacter .= $formula{++$index};
3170//echo 'Initial character of expression block is comparison operator '.$opCharacter.PHP_EOL;
3171            }
3172
3173            //    Find out if we're currently at the beginning of a number, variable, cell reference, function, parenthesis or operand
3174            $isOperandOrFunction = preg_match($regexpMatchString, substr($formula, $index), $match);
3175//echo '$isOperandOrFunction is '.(($isOperandOrFunction) ? 'True' : 'False').PHP_EOL;
3176//var_dump($match);
3177
3178            if ($opCharacter == '-' && !$expectingOperator) {                //    Is it a negation instead of a minus?
3179//echo 'Element is a Negation operator', PHP_EOL;
3180                $stack->push('Unary Operator', '~');                            //    Put a negation on the stack
3181                ++$index;                                                    //        and drop the negation symbol
3182            } elseif ($opCharacter == '%' && $expectingOperator) {
3183//echo 'Element is a Percentage operator', PHP_EOL;
3184                $stack->push('Unary Operator', '%');                            //    Put a percentage on the stack
3185                ++$index;
3186            } elseif ($opCharacter == '+' && !$expectingOperator) {            //    Positive (unary plus rather than binary operator plus) can be discarded?
3187//echo 'Element is a Positive number, not Plus operator', PHP_EOL;
3188                ++$index;                                                    //    Drop the redundant plus symbol
3189            } elseif ((($opCharacter == '~') || ($opCharacter == '|')) && (!$isOperandOrFunction)) {    //    We have to explicitly deny a tilde or pipe, because they are legal
3190                return $this->raiseFormulaError("Formula Error: Illegal character '~'");                //        on the stack but not in the input expression
3191
3192            } elseif ((isset(self::$operators[$opCharacter]) or $isOperandOrFunction) && $expectingOperator) {    //    Are we putting an operator on the stack?
3193//echo 'Element with value '.$opCharacter.' is an Operator', PHP_EOL;
3194                while ($stack->count() > 0 &&
3195                    ($o2 = $stack->last()) &&
3196                    isset(self::$operators[$o2['value']]) &&
3197                    @(self::$operatorAssociativity[$opCharacter] ? self::$operatorPrecedence[$opCharacter] < self::$operatorPrecedence[$o2['value']] : self::$operatorPrecedence[$opCharacter] <= self::$operatorPrecedence[$o2['value']])) {
3198                    $output[] = $stack->pop();                                //    Swap operands and higher precedence operators from the stack to the output
3199                }
3200                $stack->push('Binary Operator', $opCharacter);    //    Finally put our current operator onto the stack
3201                ++$index;
3202                $expectingOperator = false;
3203
3204            } elseif ($opCharacter == ')' && $expectingOperator) {            //    Are we expecting to close a parenthesis?
3205//echo 'Element is a Closing bracket', PHP_EOL;
3206                $expectingOperand = false;
3207                while (($o2 = $stack->pop()) && $o2['value'] != '(') {        //    Pop off the stack back to the last (
3208                    if ($o2 === null) {
3209                        return $this->raiseFormulaError('Formula Error: Unexpected closing brace ")"');
3210                    } else {
3211                        $output[] = $o2;
3212                    }
3213                }
3214                $d = $stack->last(2);
3215                if (preg_match('/^'.self::CALCULATION_REGEXP_FUNCTION.'$/i', $d['value'], $matches)) {    //    Did this parenthesis just close a function?
3216                    $functionName = $matches[1];                                        //    Get the function name
3217//echo 'Closed Function is '.$functionName, PHP_EOL;
3218                    $d = $stack->pop();
3219                    $argumentCount = $d['value'];        //    See how many arguments there were (argument count is the next value stored on the stack)
3220//if ($argumentCount == 0) {
3221//    echo 'With no arguments', PHP_EOL;
3222//} elseif ($argumentCount == 1) {
3223//    echo 'With 1 argument', PHP_EOL;
3224//} else {
3225//    echo 'With '.$argumentCount.' arguments', PHP_EOL;
3226//}
3227                    $output[] = $d;                        //    Dump the argument count on the output
3228                    $output[] = $stack->pop();            //    Pop the function and push onto the output
3229                    if (isset(self::$controlFunctions[$functionName])) {
3230//echo 'Built-in function '.$functionName, PHP_EOL;
3231                        $expectedArgumentCount = self::$controlFunctions[$functionName]['argumentCount'];
3232                        $functionCall = self::$controlFunctions[$functionName]['functionCall'];
3233                    } elseif (isset(self::$PHPExcelFunctions[$functionName])) {
3234//echo 'PHPExcel function '.$functionName, PHP_EOL;
3235                        $expectedArgumentCount = self::$PHPExcelFunctions[$functionName]['argumentCount'];
3236                        $functionCall = self::$PHPExcelFunctions[$functionName]['functionCall'];
3237                    } else {    // did we somehow push a non-function on the stack? this should never happen
3238                        return $this->raiseFormulaError("Formula Error: Internal error, non-function on stack");
3239                    }
3240                    //    Check the argument count
3241                    $argumentCountError = false;
3242                    if (is_numeric($expectedArgumentCount)) {
3243                        if ($expectedArgumentCount < 0) {
3244//echo '$expectedArgumentCount is between 0 and '.abs($expectedArgumentCount), PHP_EOL;
3245                            if ($argumentCount > abs($expectedArgumentCount)) {
3246                                $argumentCountError = true;
3247                                $expectedArgumentCountString = 'no more than '.abs($expectedArgumentCount);
3248                            }
3249                        } else {
3250//echo '$expectedArgumentCount is numeric '.$expectedArgumentCount, PHP_EOL;
3251                            if ($argumentCount != $expectedArgumentCount) {
3252                                $argumentCountError = true;
3253                                $expectedArgumentCountString = $expectedArgumentCount;
3254                            }
3255                        }
3256                    } elseif ($expectedArgumentCount != '*') {
3257                        $isOperandOrFunction = preg_match('/(\d*)([-+,])(\d*)/', $expectedArgumentCount, $argMatch);
3258//print_r($argMatch);
3259//echo PHP_EOL;
3260                        switch ($argMatch[2]) {
3261                            case '+':
3262                                if ($argumentCount < $argMatch[1]) {
3263                                    $argumentCountError = true;
3264                                    $expectedArgumentCountString = $argMatch[1].' or more ';
3265                                }
3266                                break;
3267                            case '-':
3268                                if (($argumentCount < $argMatch[1]) || ($argumentCount > $argMatch[3])) {
3269                                    $argumentCountError = true;
3270                                    $expectedArgumentCountString = 'between '.$argMatch[1].' and '.$argMatch[3];
3271                                }
3272                                break;
3273                            case ',':
3274                                if (($argumentCount != $argMatch[1]) && ($argumentCount != $argMatch[3])) {
3275                                    $argumentCountError = true;
3276                                    $expectedArgumentCountString = 'either '.$argMatch[1].' or '.$argMatch[3];
3277                                }
3278                                break;
3279                        }
3280                    }
3281                    if ($argumentCountError) {
3282                        return $this->raiseFormulaError("Formula Error: Wrong number of arguments for $functionName() function: $argumentCount given, ".$expectedArgumentCountString." expected");
3283                    }
3284                }
3285                ++$index;
3286
3287            } elseif ($opCharacter == ',') {            //    Is this the separator for function arguments?
3288//echo 'Element is a Function argument separator', PHP_EOL;
3289                while (($o2 = $stack->pop()) && $o2['value'] != '(') {        //    Pop off the stack back to the last (
3290                    if ($o2 === null) {
3291                        return $this->raiseFormulaError("Formula Error: Unexpected ,");
3292                    } else {
3293                        $output[] = $o2;    // pop the argument expression stuff and push onto the output
3294                    }
3295                }
3296                //    If we've a comma when we're expecting an operand, then what we actually have is a null operand;
3297                //        so push a null onto the stack
3298                if (($expectingOperand) || (!$expectingOperator)) {
3299                    $output[] = array('type' => 'NULL Value', 'value' => self::$excelConstants['NULL'], 'reference' => null);
3300                }
3301                // make sure there was a function
3302                $d = $stack->last(2);
3303                if (!preg_match('/^'.self::CALCULATION_REGEXP_FUNCTION.'$/i', $d['value'], $matches)) {
3304                    return $this->raiseFormulaError("Formula Error: Unexpected ,");
3305                }
3306                $d = $stack->pop();
3307                $stack->push($d['type'], ++$d['value'], $d['reference']);    // increment the argument count
3308                $stack->push('Brace', '(');    // put the ( back on, we'll need to pop back to it again
3309                $expectingOperator = false;
3310                $expectingOperand = true;
3311                ++$index;
3312
3313            } elseif ($opCharacter == '(' && !$expectingOperator) {
3314//                echo 'Element is an Opening Bracket<br />';
3315                $stack->push('Brace', '(');
3316                ++$index;
3317
3318            } elseif ($isOperandOrFunction && !$expectingOperator) {    // do we now have a function/variable/number?
3319                $expectingOperator = true;
3320                $expectingOperand = false;
3321                $val = $match[1];
3322                $length = strlen($val);
3323//                echo 'Element with value '.$val.' is an Operand, Variable, Constant, String, Number, Cell Reference or Function<br />';
3324
3325                if (preg_match('/^'.self::CALCULATION_REGEXP_FUNCTION.'$/i', $val, $matches)) {
3326                    $val = preg_replace('/\s/u', '', $val);
3327//                    echo 'Element '.$val.' is a Function<br />';
3328                    if (isset(self::$PHPExcelFunctions[strtoupper($matches[1])]) || isset(self::$controlFunctions[strtoupper($matches[1])])) {    // it's a function
3329                        $stack->push('Function', strtoupper($val));
3330                        $ax = preg_match('/^\s*(\s*\))/ui', substr($formula, $index+$length), $amatch);
3331                        if ($ax) {
3332                            $stack->push('Operand Count for Function '.strtoupper($val).')', 0);
3333                            $expectingOperator = true;
3334                        } else {
3335                            $stack->push('Operand Count for Function '.strtoupper($val).')', 1);
3336                            $expectingOperator = false;
3337                        }
3338                        $stack->push('Brace', '(');
3339                    } else {    // it's a var w/ implicit multiplication
3340                        $output[] = array('type' => 'Value', 'value' => $matches[1], 'reference' => null);
3341                    }
3342                } elseif (preg_match('/^'.self::CALCULATION_REGEXP_CELLREF.'$/i', $val, $matches)) {
3343//                    echo 'Element '.$val.' is a Cell reference<br />';
3344                    //    Watch for this case-change when modifying to allow cell references in different worksheets...
3345                    //    Should only be applied to the actual cell column, not the worksheet name
3346
3347                    //    If the last entry on the stack was a : operator, then we have a cell range reference
3348                    $testPrevOp = $stack->last(1);
3349                    if ($testPrevOp['value'] == ':') {
3350                        //    If we have a worksheet reference, then we're playing with a 3D reference
3351                        if ($matches[2] == '') {
3352                            //    Otherwise, we 'inherit' the worksheet reference from the start cell reference
3353                            //    The start of the cell range reference should be the last entry in $output
3354                            $startCellRef = $output[count($output)-1]['value'];
3355                            preg_match('/^'.self::CALCULATION_REGEXP_CELLREF.'$/i', $startCellRef, $startMatches);
3356                            if ($startMatches[2] > '') {
3357                                $val = $startMatches[2].'!'.$val;
3358                            }
3359                        } else {
3360                            return $this->raiseFormulaError("3D Range references are not yet supported");
3361                        }
3362                    }
3363
3364                    $output[] = array('type' => 'Cell Reference', 'value' => $val, 'reference' => $val);
3365//                    $expectingOperator = FALSE;
3366                } else {    // it's a variable, constant, string, number or boolean
3367//                    echo 'Element is a Variable, Constant, String, Number or Boolean<br />';
3368                    //    If the last entry on the stack was a : operator, then we may have a row or column range reference
3369                    $testPrevOp = $stack->last(1);
3370                    if ($testPrevOp['value'] == ':') {
3371                        $startRowColRef = $output[count($output)-1]['value'];
3372                        $rangeWS1 = '';
3373                        if (strpos('!', $startRowColRef) !== false) {
3374                            list($rangeWS1, $startRowColRef) = explode('!', $startRowColRef);
3375                        }
3376                        if ($rangeWS1 != '') {
3377                            $rangeWS1 .= '!';
3378                        }
3379                        $rangeWS2 = $rangeWS1;
3380                        if (strpos('!', $val) !== false) {
3381                            list($rangeWS2, $val) = explode('!', $val);
3382                        }
3383                        if ($rangeWS2 != '') {
3384                            $rangeWS2 .= '!';
3385                        }
3386                        if ((is_integer($startRowColRef)) && (ctype_digit($val)) &&
3387                            ($startRowColRef <= 1048576) && ($val <= 1048576)) {
3388                            //    Row range
3389                            $endRowColRef = ($pCellParent !== null) ? $pCellParent->getHighestColumn() : 'XFD';    //    Max 16,384 columns for Excel2007
3390                            $output[count($output)-1]['value'] = $rangeWS1.'A'.$startRowColRef;
3391                            $val = $rangeWS2.$endRowColRef.$val;
3392                        } elseif ((ctype_alpha($startRowColRef)) && (ctype_alpha($val)) &&
3393                            (strlen($startRowColRef) <= 3) && (strlen($val) <= 3)) {
3394                            //    Column range
3395                            $endRowColRef = ($pCellParent !== null) ? $pCellParent->getHighestRow() : 1048576;        //    Max 1,048,576 rows for Excel2007
3396                            $output[count($output)-1]['value'] = $rangeWS1.strtoupper($startRowColRef).'1';
3397                            $val = $rangeWS2.$val.$endRowColRef;
3398                        }
3399                    }
3400
3401                    $localeConstant = false;
3402                    if ($opCharacter == '"') {
3403//                        echo 'Element is a String<br />';
3404                        //    UnEscape any quotes within the string
3405                        $val = self::wrapResult(str_replace('""', '"', self::unwrapResult($val)));
3406                    } elseif (is_numeric($val)) {
3407//                        echo 'Element is a Number<br />';
3408                        if ((strpos($val, '.') !== false) || (stripos($val, 'e') !== false) || ($val > PHP_INT_MAX) || ($val < -PHP_INT_MAX)) {
3409//                            echo 'Casting '.$val.' to float<br />';
3410                            $val = (float) $val;
3411                        } else {
3412//                            echo 'Casting '.$val.' to integer<br />';
3413                            $val = (integer) $val;
3414                        }
3415                    } elseif (isset(self::$excelConstants[trim(strtoupper($val))])) {
3416                        $excelConstant = trim(strtoupper($val));
3417//                        echo 'Element '.$excelConstant.' is an Excel Constant<br />';
3418                        $val = self::$excelConstants[$excelConstant];
3419                    } elseif (($localeConstant = array_search(trim(strtoupper($val)), self::$localeBoolean)) !== false) {
3420//                        echo 'Element '.$localeConstant.' is an Excel Constant<br />';
3421                        $val = self::$excelConstants[$localeConstant];
3422                    }
3423                    $details = array('type' => 'Value', 'value' => $val, 'reference' => null);
3424                    if ($localeConstant) {
3425                        $details['localeValue'] = $localeConstant;
3426                    }
3427                    $output[] = $details;
3428                }
3429                $index += $length;
3430
3431            } elseif ($opCharacter == '$') {    // absolute row or column range
3432                ++$index;
3433            } elseif ($opCharacter == ')') {    // miscellaneous error checking
3434                if ($expectingOperand) {
3435                    $output[] = array('type' => 'NULL Value', 'value' => self::$excelConstants['NULL'], 'reference' => null);
3436                    $expectingOperand = false;
3437                    $expectingOperator = true;
3438                } else {
3439                    return $this->raiseFormulaError("Formula Error: Unexpected ')'");
3440                }
3441            } elseif (isset(self::$operators[$opCharacter]) && !$expectingOperator) {
3442                return $this->raiseFormulaError("Formula Error: Unexpected operator '$opCharacter'");
3443            } else {    // I don't even want to know what you did to get here
3444                return $this->raiseFormulaError("Formula Error: An unexpected error occured");
3445            }
3446            //    Test for end of formula string
3447            if ($index == strlen($formula)) {
3448                //    Did we end with an operator?.
3449                //    Only valid for the % unary operator
3450                if ((isset(self::$operators[$opCharacter])) && ($opCharacter != '%')) {
3451                    return $this->raiseFormulaError("Formula Error: Operator '$opCharacter' has no operands");
3452                } else {
3453                    break;
3454                }
3455            }
3456            //    Ignore white space
3457            while (($formula{$index} == "\n") || ($formula{$index} == "\r")) {
3458                ++$index;
3459            }
3460            if ($formula{$index} == ' ') {
3461                while ($formula{$index} == ' ') {
3462                    ++$index;
3463                }
3464                //    If we're expecting an operator, but only have a space between the previous and next operands (and both are
3465                //        Cell References) then we have an INTERSECTION operator
3466//                echo 'Possible Intersect Operator<br />';
3467                if (($expectingOperator) && (preg_match('/^'.self::CALCULATION_REGEXP_CELLREF.'.*/Ui', substr($formula, $index), $match)) &&
3468                    ($output[count($output)-1]['type'] == 'Cell Reference')) {
3469//                    echo 'Element is an Intersect Operator<br />';
3470                    while ($stack->count() > 0 &&
3471                        ($o2 = $stack->last()) &&
3472                        isset(self::$operators[$o2['value']]) &&
3473                        @(self::$operatorAssociativity[$opCharacter] ? self::$operatorPrecedence[$opCharacter] < self::$operatorPrecedence[$o2['value']] : self::$operatorPrecedence[$opCharacter] <= self::$operatorPrecedence[$o2['value']])) {
3474                        $output[] = $stack->pop();                                //    Swap operands and higher precedence operators from the stack to the output
3475                    }
3476                    $stack->push('Binary Operator', '|');    //    Put an Intersect Operator on the stack
3477                    $expectingOperator = false;
3478                }
3479            }
3480        }
3481
3482        while (($op = $stack->pop()) !== null) {    // pop everything off the stack and push onto output
3483            if ((is_array($op) && $op['value'] == '(') || ($op === '(')) {
3484                return $this->raiseFormulaError("Formula Error: Expecting ')'");    // if there are any opening braces on the stack, then braces were unbalanced
3485            }
3486            $output[] = $op;
3487        }
3488        return $output;
3489    }
3490
3491
3492    private static function dataTestReference(&$operandData)
3493    {
3494        $operand = $operandData['value'];
3495        if (($operandData['reference'] === null) && (is_array($operand))) {
3496            $rKeys = array_keys($operand);
3497            $rowKey = array_shift($rKeys);
3498            $cKeys = array_keys(array_keys($operand[$rowKey]));
3499            $colKey = array_shift($cKeys);
3500            if (ctype_upper($colKey)) {
3501                $operandData['reference'] = $colKey.$rowKey;
3502            }
3503        }
3504        return $operand;
3505    }
3506
3507    // evaluate postfix notation
3508    private function processTokenStack($tokens, $cellID = null, PHPExcel_Cell $pCell = null)
3509    {
3510        if ($tokens == false) {
3511            return false;
3512        }
3513
3514        //    If we're using cell caching, then $pCell may well be flushed back to the cache (which detaches the parent cell collection),
3515        //        so we store the parent cell collection so that we can re-attach it when necessary
3516        $pCellWorksheet = ($pCell !== null) ? $pCell->getWorksheet() : null;
3517        $pCellParent = ($pCell !== null) ? $pCell->getParent() : null;
3518        $stack = new PHPExcel_Calculation_Token_Stack;
3519
3520        //    Loop through each token in turn
3521        foreach ($tokens as $tokenData) {
3522//            print_r($tokenData);
3523//            echo '<br />';
3524            $token = $tokenData['value'];
3525//            echo '<b>Token is '.$token.'</b><br />';
3526            // 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
3527            if (isset(self::$binaryOperators[$token])) {
3528//                echo 'Token is a binary operator<br />';
3529                //    We must have two operands, error if we don't
3530                if (($operand2Data = $stack->pop()) === null) {
3531                    return $this->raiseFormulaError('Internal error - Operand value missing from stack');
3532                }
3533                if (($operand1Data = $stack->pop()) === null) {
3534                    return $this->raiseFormulaError('Internal error - Operand value missing from stack');
3535                }
3536
3537                $operand1 = self::dataTestReference($operand1Data);
3538                $operand2 = self::dataTestReference($operand2Data);
3539
3540                //    Log what we're doing
3541                if ($token == ':') {
3542                    $this->_debugLog->writeDebugLog('Evaluating Range ', $this->showValue($operand1Data['reference']), ' ', $token, ' ', $this->showValue($operand2Data['reference']));
3543                } else {
3544                    $this->_debugLog->writeDebugLog('Evaluating ', $this->showValue($operand1), ' ', $token, ' ', $this->showValue($operand2));
3545                }
3546
3547                //    Process the operation in the appropriate manner
3548                switch ($token) {
3549                    //    Comparison (Boolean) Operators
3550                    case '>':            //    Greater than
3551                    case '<':            //    Less than
3552                    case '>=':            //    Greater than or Equal to
3553                    case '<=':            //    Less than or Equal to
3554                    case '=':            //    Equality
3555                    case '<>':            //    Inequality
3556                        $this->executeBinaryComparisonOperation($cellID, $operand1, $operand2, $token, $stack);
3557                        break;
3558                    //    Binary Operators
3559                    case ':':            //    Range
3560                        $sheet1 = $sheet2 = '';
3561                        if (strpos($operand1Data['reference'], '!') !== false) {
3562                            list($sheet1, $operand1Data['reference']) = explode('!', $operand1Data['reference']);
3563                        } else {
3564                            $sheet1 = ($pCellParent !== null) ? $pCellWorksheet->getTitle() : '';
3565                        }
3566                        if (strpos($operand2Data['reference'], '!') !== false) {
3567                            list($sheet2, $operand2Data['reference']) = explode('!', $operand2Data['reference']);
3568                        } else {
3569                            $sheet2 = $sheet1;
3570                        }
3571                        if ($sheet1 == $sheet2) {
3572                            if ($operand1Data['reference'] === null) {
3573                                if ((trim($operand1Data['value']) != '') && (is_numeric($operand1Data['value']))) {
3574                                    $operand1Data['reference'] = $pCell->getColumn().$operand1Data['value'];
3575                                } elseif (trim($operand1Data['reference']) == '') {
3576                                    $operand1Data['reference'] = $pCell->getCoordinate();
3577                                } else {
3578                                    $operand1Data['reference'] = $operand1Data['value'].$pCell->getRow();
3579                                }
3580                            }
3581                            if ($operand2Data['reference'] === null) {
3582                                if ((trim($operand2Data['value']) != '') && (is_numeric($operand2Data['value']))) {
3583                                    $operand2Data['reference'] = $pCell->getColumn().$operand2Data['value'];
3584                                } elseif (trim($operand2Data['reference']) == '') {
3585                                    $operand2Data['reference'] = $pCell->getCoordinate();
3586                                } else {
3587                                    $operand2Data['reference'] = $operand2Data['value'].$pCell->getRow();
3588                                }
3589                            }
3590
3591                            $oData = array_merge(explode(':', $operand1Data['reference']), explode(':', $operand2Data['reference']));
3592                            $oCol = $oRow = array();
3593                            foreach ($oData as $oDatum) {
3594                                $oCR = PHPExcel_Cell::coordinateFromString($oDatum);
3595                                $oCol[] = PHPExcel_Cell::columnIndexFromString($oCR[0]) - 1;
3596                                $oRow[] = $oCR[1];
3597                            }
3598                            $cellRef = PHPExcel_Cell::stringFromColumnIndex(min($oCol)).min($oRow).':'.PHPExcel_Cell::stringFromColumnIndex(max($oCol)).max($oRow);
3599                            if ($pCellParent !== null) {
3600                                $cellValue = $this->extractCellRange($cellRef, $this->workbook->getSheetByName($sheet1), false);
3601                            } else {
3602                                return $this->raiseFormulaError('Unable to access Cell Reference');
3603                            }
3604                            $stack->push('Cell Reference', $cellValue, $cellRef);
3605                        } else {
3606                            $stack->push('Error', PHPExcel_Calculation_Functions::REF(), null);
3607                        }
3608                        break;
3609                    case '+':            //    Addition
3610                        $this->executeNumericBinaryOperation($cellID, $operand1, $operand2, $token, 'plusEquals', $stack);
3611                        break;
3612                    case '-':            //    Subtraction
3613                        $this->executeNumericBinaryOperation($cellID, $operand1, $operand2, $token, 'minusEquals', $stack);
3614                        break;
3615                    case '*':            //    Multiplication
3616                        $this->executeNumericBinaryOperation($cellID, $operand1, $operand2, $token, 'arrayTimesEquals', $stack);
3617                        break;
3618                    case '/':            //    Division
3619                        $this->executeNumericBinaryOperation($cellID, $operand1, $operand2, $token, 'arrayRightDivide', $stack);
3620                        break;
3621                    case '^':            //    Exponential
3622                        $this->executeNumericBinaryOperation($cellID, $operand1, $operand2, $token, 'power', $stack);
3623                        break;
3624                    case '&':            //    Concatenation
3625                        //    If either of the operands is a matrix, we need to treat them both as matrices
3626                        //        (converting the other operand to a matrix if need be); then perform the required
3627                        //        matrix operation
3628                        if (is_bool($operand1)) {
3629                            $operand1 = ($operand1) ? self::$localeBoolean['TRUE'] : self::$localeBoolean['FALSE'];
3630                        }
3631                        if (is_bool($operand2)) {
3632                            $operand2 = ($operand2) ? self::$localeBoolean['TRUE'] : self::$localeBoolean['FALSE'];
3633                        }
3634                        if ((is_array($operand1)) || (is_array($operand2))) {
3635                            //    Ensure that both operands are arrays/matrices
3636                            self::checkMatrixOperands($operand1, $operand2, 2);
3637                            try {
3638                                //    Convert operand 1 from a PHP array to a matrix
3639                                $matrix = new PHPExcel_Shared_JAMA_Matrix($operand1);
3640                                //    Perform the required operation against the operand 1 matrix, passing in operand 2
3641                                $matrixResult = $matrix->concat($operand2);
3642                                $result = $matrixResult->getArray();
3643                            } catch (PHPExcel_Exception $ex) {
3644                                $this->_debugLog->writeDebugLog('JAMA Matrix Exception: ', $ex->getMessage());
3645                                $result = '#VALUE!';
3646                            }
3647                        } else {
3648                            $result = '"'.str_replace('""', '"', self::unwrapResult($operand1, '"').self::unwrapResult($operand2, '"')).'"';
3649                        }
3650                        $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($result));
3651                        $stack->push('Value', $result);
3652                        break;
3653                    case '|':            //    Intersect
3654                        $rowIntersect = array_intersect_key($operand1, $operand2);
3655                        $cellIntersect = $oCol = $oRow = array();
3656                        foreach (array_keys($rowIntersect) as $row) {
3657                            $oRow[] = $row;
3658                            foreach ($rowIntersect[$row] as $col => $data) {
3659                                $oCol[] = PHPExcel_Cell::columnIndexFromString($col) - 1;
3660                                $cellIntersect[$row] = array_intersect_key($operand1[$row], $operand2[$row]);
3661                            }
3662                        }
3663                        $cellRef = PHPExcel_Cell::stringFromColumnIndex(min($oCol)).min($oRow).':'.PHPExcel_Cell::stringFromColumnIndex(max($oCol)).max($oRow);
3664                        $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($cellIntersect));
3665                        $stack->push('Value', $cellIntersect, $cellRef);
3666                        break;
3667                }
3668
3669            // if the token is a unary operator, pop one value off the stack, do the operation, and push it back on
3670            } elseif (($token === '~') || ($token === '%')) {
3671//                echo 'Token is a unary operator<br />';
3672                if (($arg = $stack->pop()) === null) {
3673                    return $this->raiseFormulaError('Internal error - Operand value missing from stack');
3674                }
3675                $arg = $arg['value'];
3676                if ($token === '~') {
3677//                    echo 'Token is a negation operator<br />';
3678                    $this->_debugLog->writeDebugLog('Evaluating Negation of ', $this->showValue($arg));
3679                    $multiplier = -1;
3680                } else {
3681//                    echo 'Token is a percentile operator<br />';
3682                    $this->_debugLog->writeDebugLog('Evaluating Percentile of ', $this->showValue($arg));
3683                    $multiplier = 0.01;
3684                }
3685                if (is_array($arg)) {
3686                    self::checkMatrixOperands($arg, $multiplier, 2);
3687                    try {
3688                        $matrix1 = new PHPExcel_Shared_JAMA_Matrix($arg);
3689                        $matrixResult = $matrix1->arrayTimesEquals($multiplier);
3690                        $result = $matrixResult->getArray();
3691                    } catch (PHPExcel_Exception $ex) {
3692                        $this->_debugLog->writeDebugLog('JAMA Matrix Exception: ', $ex->getMessage());
3693                        $result = '#VALUE!';
3694                    }
3695                    $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($result));
3696                    $stack->push('Value', $result);
3697                } else {
3698                    $this->executeNumericBinaryOperation($cellID, $multiplier, $arg, '*', 'arrayTimesEquals', $stack);
3699                }
3700
3701            } elseif (preg_match('/^'.self::CALCULATION_REGEXP_CELLREF.'$/i', $token, $matches)) {
3702                $cellRef = null;
3703//                echo 'Element '.$token.' is a Cell reference<br />';
3704                if (isset($matches[8])) {
3705//                    echo 'Reference is a Range of cells<br />';
3706                    if ($pCell === null) {
3707//                        We can't access the range, so return a REF error
3708                        $cellValue = PHPExcel_Calculation_Functions::REF();
3709                    } else {
3710                        $cellRef = $matches[6].$matches[7].':'.$matches[9].$matches[10];
3711                        if ($matches[2] > '') {
3712                            $matches[2] = trim($matches[2], "\"'");
3713                            if ((strpos($matches[2], '[') !== false) || (strpos($matches[2], ']') !== false)) {
3714                                //    It's a Reference to an external workbook (not currently supported)
3715                                return $this->raiseFormulaError('Unable to access External Workbook');
3716                            }
3717                            $matches[2] = trim($matches[2], "\"'");
3718//                            echo '$cellRef='.$cellRef.' in worksheet '.$matches[2].'<br />';
3719                            $this->_debugLog->writeDebugLog('Evaluating Cell Range ', $cellRef, ' in worksheet ', $matches[2]);
3720                            if ($pCellParent !== null) {
3721                                $cellValue = $this->extractCellRange($cellRef, $this->workbook->getSheetByName($matches[2]), false);
3722                            } else {
3723                                return $this->raiseFormulaError('Unable to access Cell Reference');
3724                            }
3725                            $this->_debugLog->writeDebugLog('Evaluation Result for cells ', $cellRef, ' in worksheet ', $matches[2], ' is ', $this->showTypeDetails($cellValue));
3726//                            $cellRef = $matches[2].'!'.$cellRef;
3727                        } else {
3728//                            echo '$cellRef='.$cellRef.' in current worksheet<br />';
3729                            $this->_debugLog->writeDebugLog('Evaluating Cell Range ', $cellRef, ' in current worksheet');
3730                            if ($pCellParent !== null) {
3731                                $cellValue = $this->extractCellRange($cellRef, $pCellWorksheet, false);
3732                            } else {
3733                                return $this->raiseFormulaError('Unable to access Cell Reference');
3734                            }
3735                            $this->_debugLog->writeDebugLog('Evaluation Result for cells ', $cellRef, ' is ', $this->showTypeDetails($cellValue));
3736                        }
3737                    }
3738                } else {
3739//                    echo 'Reference is a single Cell<br />';
3740                    if ($pCell === null) {
3741//                        We can't access the cell, so return a REF error
3742                        $cellValue = PHPExcel_Calculation_Functions::REF();
3743                    } else {
3744                        $cellRef = $matches[6].$matches[7];
3745                        if ($matches[2] > '') {
3746                            $matches[2] = trim($matches[2], "\"'");
3747                            if ((strpos($matches[2], '[') !== false) || (strpos($matches[2], ']') !== false)) {
3748                                //    It's a Reference to an external workbook (not currently supported)
3749                                return $this->raiseFormulaError('Unable to access External Workbook');
3750                            }
3751//                            echo '$cellRef='.$cellRef.' in worksheet '.$matches[2].'<br />';
3752                            $this->_debugLog->writeDebugLog('Evaluating Cell ', $cellRef, ' in worksheet ', $matches[2]);
3753                            if ($pCellParent !== null) {
3754                                $cellSheet = $this->workbook->getSheetByName($matches[2]);
3755                                if ($cellSheet && $cellSheet->cellExists($cellRef)) {
3756                                    $cellValue = $this->extractCellRange($cellRef, $this->workbook->getSheetByName($matches[2]), false);
3757                                    $pCell->attach($pCellParent);
3758                                } else {
3759                                    $cellValue = null;
3760                                }
3761                            } else {
3762                                return $this->raiseFormulaError('Unable to access Cell Reference');
3763                            }
3764                            $this->_debugLog->writeDebugLog('Evaluation Result for cell ', $cellRef, ' in worksheet ', $matches[2], ' is ', $this->showTypeDetails($cellValue));
3765//                            $cellRef = $matches[2].'!'.$cellRef;
3766                        } else {
3767//                            echo '$cellRef='.$cellRef.' in current worksheet<br />';
3768                            $this->_debugLog->writeDebugLog('Evaluating Cell ', $cellRef, ' in current worksheet');
3769                            if ($pCellParent->isDataSet($cellRef)) {
3770                                $cellValue = $this->extractCellRange($cellRef, $pCellWorksheet, false);
3771                                $pCell->attach($pCellParent);
3772                            } else {
3773                                $cellValue = null;
3774                            }
3775                            $this->_debugLog->writeDebugLog('Evaluation Result for cell ', $cellRef, ' is ', $this->showTypeDetails($cellValue));
3776                        }
3777                    }
3778                }
3779                $stack->push('Value', $cellValue, $cellRef);
3780
3781            // if the token is a function, pop arguments off the stack, hand them to the function, and push the result back on
3782            } elseif (preg_match('/^'.self::CALCULATION_REGEXP_FUNCTION.'$/i', $token, $matches)) {
3783//                echo 'Token is a function<br />';
3784                $functionName = $matches[1];
3785                $argCount = $stack->pop();
3786                $argCount = $argCount['value'];
3787                if ($functionName != 'MKMATRIX') {
3788                    $this->_debugLog->writeDebugLog('Evaluating Function ', self::localeFunc($functionName), '() with ', (($argCount == 0) ? 'no' : $argCount), ' argument', (($argCount == 1) ? '' : 's'));
3789                }
3790                if ((isset(self::$PHPExcelFunctions[$functionName])) || (isset(self::$controlFunctions[$functionName]))) {    // function
3791                    if (isset(self::$PHPExcelFunctions[$functionName])) {
3792                        $functionCall = self::$PHPExcelFunctions[$functionName]['functionCall'];
3793                        $passByReference = isset(self::$PHPExcelFunctions[$functionName]['passByReference']);
3794                        $passCellReference = isset(self::$PHPExcelFunctions[$functionName]['passCellReference']);
3795                    } elseif (isset(self::$controlFunctions[$functionName])) {
3796                        $functionCall = self::$controlFunctions[$functionName]['functionCall'];
3797                        $passByReference = isset(self::$controlFunctions[$functionName]['passByReference']);
3798                        $passCellReference = isset(self::$controlFunctions[$functionName]['passCellReference']);
3799                    }
3800                    // get the arguments for this function
3801//                    echo 'Function '.$functionName.' expects '.$argCount.' arguments<br />';
3802                    $args = $argArrayVals = array();
3803                    for ($i = 0; $i < $argCount; ++$i) {
3804                        $arg = $stack->pop();
3805                        $a = $argCount - $i - 1;
3806                        if (($passByReference) &&
3807                            (isset(self::$PHPExcelFunctions[$functionName]['passByReference'][$a])) &&
3808                            (self::$PHPExcelFunctions[$functionName]['passByReference'][$a])) {
3809                            if ($arg['reference'] === null) {
3810                                $args[] = $cellID;
3811                                if ($functionName != 'MKMATRIX') {
3812                                    $argArrayVals[] = $this->showValue($cellID);
3813                                }
3814                            } else {
3815                                $args[] = $arg['reference'];
3816                                if ($functionName != 'MKMATRIX') {
3817                                    $argArrayVals[] = $this->showValue($arg['reference']);
3818                                }
3819                            }
3820                        } else {
3821                            $args[] = self::unwrapResult($arg['value']);
3822                            if ($functionName != 'MKMATRIX') {
3823                                $argArrayVals[] = $this->showValue($arg['value']);
3824                            }
3825                        }
3826                    }
3827                    //    Reverse the order of the arguments
3828                    krsort($args);
3829                    if (($passByReference) && ($argCount == 0)) {
3830                        $args[] = $cellID;
3831                        $argArrayVals[] = $this->showValue($cellID);
3832                    }
3833//                    echo 'Arguments are: ';
3834//                    print_r($args);
3835//                    echo '<br />';
3836                    if ($functionName != 'MKMATRIX') {
3837                        if ($this->_debugLog->getWriteDebugLog()) {
3838                            krsort($argArrayVals);
3839                            $this->_debugLog->writeDebugLog('Evaluating ', self::localeFunc($functionName), '( ', implode(self::$localeArgumentSeparator.' ', PHPExcel_Calculation_Functions::flattenArray($argArrayVals)), ' )');
3840                        }
3841                    }
3842                    //    Process each argument in turn, building the return value as an array
3843//                    if (($argCount == 1) && (is_array($args[1])) && ($functionName != 'MKMATRIX')) {
3844//                        $operand1 = $args[1];
3845//                        $this->_debugLog->writeDebugLog('Argument is a matrix: ', $this->showValue($operand1));
3846//                        $result = array();
3847//                        $row = 0;
3848//                        foreach($operand1 as $args) {
3849//                            if (is_array($args)) {
3850//                                foreach($args as $arg) {
3851//                                    $this->_debugLog->writeDebugLog('Evaluating ', self::localeFunc($functionName), '( ', $this->showValue($arg), ' )');
3852//                                    $r = call_user_func_array($functionCall, $arg);
3853//                                    $this->_debugLog->writeDebugLog('Evaluation Result for ', self::localeFunc($functionName), '() function call is ', $this->showTypeDetails($r));
3854//                                    $result[$row][] = $r;
3855//                                }
3856//                                ++$row;
3857//                            } else {
3858//                                $this->_debugLog->writeDebugLog('Evaluating ', self::localeFunc($functionName), '( ', $this->showValue($args), ' )');
3859//                                $r = call_user_func_array($functionCall, $args);
3860//                                $this->_debugLog->writeDebugLog('Evaluation Result for ', self::localeFunc($functionName), '() function call is ', $this->showTypeDetails($r));
3861//                                $result[] = $r;
3862//                            }
3863//                        }
3864//                    } else {
3865                    //    Process the argument with the appropriate function call
3866                    if ($passCellReference) {
3867                        $args[] = $pCell;
3868                    }
3869                    if (strpos($functionCall, '::') !== false) {
3870                        $result = call_user_func_array(explode('::', $functionCall), $args);
3871                    } else {
3872                        foreach ($args as &$arg) {
3873                            $arg = PHPExcel_Calculation_Functions::flattenSingleValue($arg);
3874                        }
3875                        unset($arg);
3876                        $result = call_user_func_array($functionCall, $args);
3877                    }
3878                    if ($functionName != 'MKMATRIX') {
3879                        $this->_debugLog->writeDebugLog('Evaluation Result for ', self::localeFunc($functionName), '() function call is ', $this->showTypeDetails($result));
3880                    }
3881                    $stack->push('Value', self::wrapResult($result));
3882                }
3883
3884            } else {
3885                // if the token is a number, boolean, string or an Excel error, push it onto the stack
3886                if (isset(self::$excelConstants[strtoupper($token)])) {
3887                    $excelConstant = strtoupper($token);
3888//                    echo 'Token is a PHPExcel constant: '.$excelConstant.'<br />';
3889                    $stack->push('Constant Value', self::$excelConstants[$excelConstant]);
3890                    $this->_debugLog->writeDebugLog('Evaluating Constant ', $excelConstant, ' as ', $this->showTypeDetails(self::$excelConstants[$excelConstant]));
3891                } elseif ((is_numeric($token)) || ($token === null) || (is_bool($token)) || ($token == '') || ($token{0} == '"') || ($token{0} == '#')) {
3892//                    echo 'Token is a number, boolean, string, null or an Excel error<br />';
3893                    $stack->push('Value', $token);
3894                // if the token is a named range, push the named range name onto the stack
3895                } elseif (preg_match('/^'.self::CALCULATION_REGEXP_NAMEDRANGE.'$/i', $token, $matches)) {
3896//                    echo 'Token is a named range<br />';
3897                    $namedRange = $matches[6];
3898//                    echo 'Named Range is '.$namedRange.'<br />';
3899                    $this->_debugLog->writeDebugLog('Evaluating Named Range ', $namedRange);
3900                    $cellValue = $this->extractNamedRange($namedRange, ((null !== $pCell) ? $pCellWorksheet : null), false);
3901                    $pCell->attach($pCellParent);
3902                    $this->_debugLog->writeDebugLog('Evaluation Result for named range ', $namedRange, ' is ', $this->showTypeDetails($cellValue));
3903                    $stack->push('Named Range', $cellValue, $namedRange);
3904                } else {
3905                    return $this->raiseFormulaError("undefined variable '$token'");
3906                }
3907            }
3908        }
3909        // when we're out of tokens, the stack should have a single element, the final result
3910        if ($stack->count() != 1) {
3911            return $this->raiseFormulaError("internal error");
3912        }
3913        $output = $stack->pop();
3914        $output = $output['value'];
3915
3916//        if ((is_array($output)) && (self::$returnArrayAsType != self::RETURN_ARRAY_AS_ARRAY)) {
3917//            return array_shift(PHPExcel_Calculation_Functions::flattenArray($output));
3918//        }
3919        return $output;
3920    }
3921
3922
3923    private function validateBinaryOperand($cellID, &$operand, &$stack)
3924    {
3925        if (is_array($operand)) {
3926            if ((count($operand, COUNT_RECURSIVE) - count($operand)) == 1) {
3927                do {
3928                    $operand = array_pop($operand);
3929                } while (is_array($operand));
3930            }
3931        }
3932        //    Numbers, matrices and booleans can pass straight through, as they're already valid
3933        if (is_string($operand)) {
3934            //    We only need special validations for the operand if it is a string
3935            //    Start by stripping off the quotation marks we use to identify true excel string values internally
3936            if ($operand > '' && $operand{0} == '"') {
3937                $operand = self::unwrapResult($operand);
3938            }
3939            //    If the string is a numeric value, we treat it as a numeric, so no further testing
3940            if (!is_numeric($operand)) {
3941                //    If not a numeric, test to see if the value is an Excel error, and so can't be used in normal binary operations
3942                if ($operand > '' && $operand{0} == '#') {
3943                    $stack->push('Value', $operand);
3944                    $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($operand));
3945                    return false;
3946                } elseif (!PHPExcel_Shared_String::convertToNumberIfFraction($operand)) {
3947                    //    If not a numeric or a fraction, then it's a text string, and so can't be used in mathematical binary operations
3948                    $stack->push('Value', '#VALUE!');
3949                    $this->_debugLog->writeDebugLog('Evaluation Result is a ', $this->showTypeDetails('#VALUE!'));
3950                    return false;
3951                }
3952            }
3953        }
3954
3955        //    return a true if the value of the operand is one that we can use in normal binary operations
3956        return true;
3957    }
3958
3959
3960    private function executeBinaryComparisonOperation($cellID, $operand1, $operand2, $operation, &$stack, $recursingArrays = false)
3961    {
3962        //    If we're dealing with matrix operations, we want a matrix result
3963        if ((is_array($operand1)) || (is_array($operand2))) {
3964            $result = array();
3965            if ((is_array($operand1)) && (!is_array($operand2))) {
3966                foreach ($operand1 as $x => $operandData) {
3967                    $this->_debugLog->writeDebugLog('Evaluating Comparison ', $this->showValue($operandData), ' ', $operation, ' ', $this->showValue($operand2));
3968                    $this->executeBinaryComparisonOperation($cellID, $operandData, $operand2, $operation, $stack);
3969                    $r = $stack->pop();
3970                    $result[$x] = $r['value'];
3971                }
3972            } elseif ((!is_array($operand1)) && (is_array($operand2))) {
3973                foreach ($operand2 as $x => $operandData) {
3974                    $this->_debugLog->writeDebugLog('Evaluating Comparison ', $this->showValue($operand1), ' ', $operation, ' ', $this->showValue($operandData));
3975                    $this->executeBinaryComparisonOperation($cellID, $operand1, $operandData, $operation, $stack);
3976                    $r = $stack->pop();
3977                    $result[$x] = $r['value'];
3978                }
3979            } else {
3980                if (!$recursingArrays) {
3981                    self::checkMatrixOperands($operand1, $operand2, 2);
3982                }
3983                foreach ($operand1 as $x => $operandData) {
3984                    $this->_debugLog->writeDebugLog('Evaluating Comparison ', $this->showValue($operandData), ' ', $operation, ' ', $this->showValue($operand2[$x]));
3985                    $this->executeBinaryComparisonOperation($cellID, $operandData, $operand2[$x], $operation, $stack, true);
3986                    $r = $stack->pop();
3987                    $result[$x] = $r['value'];
3988                }
3989            }
3990            //    Log the result details
3991            $this->_debugLog->writeDebugLog('Comparison Evaluation Result is ', $this->showTypeDetails($result));
3992            //    And push the result onto the stack
3993            $stack->push('Array', $result);
3994            return true;
3995        }
3996
3997        //    Simple validate the two operands if they are string values
3998        if (is_string($operand1) && $operand1 > '' && $operand1{0} == '"') {
3999            $operand1 = self::unwrapResult($operand1);
4000        }
4001        if (is_string($operand2) && $operand2 > '' && $operand2{0} == '"') {
4002            $operand2 = self::unwrapResult($operand2);
4003        }
4004
4005        // Use case insensitive comparaison if not OpenOffice mode
4006        if (PHPExcel_Calculation_Functions::getCompatibilityMode() != PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE) {
4007            if (is_string($operand1)) {
4008                $operand1 = strtoupper($operand1);
4009            }
4010            if (is_string($operand2)) {
4011                $operand2 = strtoupper($operand2);
4012            }
4013        }
4014
4015        $useLowercaseFirstComparison = is_string($operand1) && is_string($operand2) && PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE;
4016
4017        //    execute the necessary operation
4018        switch ($operation) {
4019            //    Greater than
4020            case '>':
4021                if ($useLowercaseFirstComparison) {
4022                    $result = $this->strcmpLowercaseFirst($operand1, $operand2) > 0;
4023                } else {
4024                    $result = ($operand1 > $operand2);
4025                }
4026                break;
4027            //    Less than
4028            case '<':
4029                if ($useLowercaseFirstComparison) {
4030                    $result = $this->strcmpLowercaseFirst($operand1, $operand2) < 0;
4031                } else {
4032                    $result = ($operand1 < $operand2);
4033                }
4034                break;
4035            //    Equality
4036            case '=':
4037                if (is_numeric($operand1) && is_numeric($operand2)) {
4038                    $result = (abs($operand1 - $operand2) < $this->delta);
4039                } else {
4040                    $result = strcmp($operand1, $operand2) == 0;
4041                }
4042                break;
4043            //    Greater than or equal
4044            case '>=':
4045                if (is_numeric($operand1) && is_numeric($operand2)) {
4046                    $result = ((abs($operand1 - $operand2) < $this->delta) || ($operand1 > $operand2));
4047                } elseif ($useLowercaseFirstComparison) {
4048                    $result = $this->strcmpLowercaseFirst($operand1, $operand2) >= 0;
4049                } else {
4050                    $result = strcmp($operand1, $operand2) >= 0;
4051                }
4052                break;
4053            //    Less than or equal
4054            case '<=':
4055                if (is_numeric($operand1) && is_numeric($operand2)) {
4056                    $result = ((abs($operand1 - $operand2) < $this->delta) || ($operand1 < $operand2));
4057                } elseif ($useLowercaseFirstComparison) {
4058                    $result = $this->strcmpLowercaseFirst($operand1, $operand2) <= 0;
4059                } else {
4060                    $result = strcmp($operand1, $operand2) <= 0;
4061                }
4062                break;
4063            //    Inequality
4064            case '<>':
4065                if (is_numeric($operand1) && is_numeric($operand2)) {
4066                    $result = (abs($operand1 - $operand2) > 1E-14);
4067                } else {
4068                    $result = strcmp($operand1, $operand2) != 0;
4069                }
4070                break;
4071        }
4072
4073        //    Log the result details
4074        $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($result));
4075        //    And push the result onto the stack
4076        $stack->push('Value', $result);
4077        return true;
4078    }
4079
4080    /**
4081     * Compare two strings in the same way as strcmp() except that lowercase come before uppercase letters
4082     * @param    string    $str1    First string value for the comparison
4083     * @param    string    $str2    Second string value for the comparison
4084     * @return   integer
4085     */
4086    private function strcmpLowercaseFirst($str1, $str2)
4087    {
4088        $inversedStr1 = PHPExcel_Shared_String::StrCaseReverse($str1);
4089        $inversedStr2 = PHPExcel_Shared_String::StrCaseReverse($str2);
4090
4091        return strcmp($inversedStr1, $inversedStr2);
4092    }
4093
4094    private function executeNumericBinaryOperation($cellID, $operand1, $operand2, $operation, $matrixFunction, &$stack)
4095    {
4096        //    Validate the two operands
4097        if (!$this->validateBinaryOperand($cellID, $operand1, $stack)) {
4098            return false;
4099        }
4100        if (!$this->validateBinaryOperand($cellID, $operand2, $stack)) {
4101            return false;
4102        }
4103
4104        //    If either of the operands is a matrix, we need to treat them both as matrices
4105        //        (converting the other operand to a matrix if need be); then perform the required
4106        //        matrix operation
4107        if ((is_array($operand1)) || (is_array($operand2))) {
4108            //    Ensure that both operands are arrays/matrices of the same size
4109            self::checkMatrixOperands($operand1, $operand2, 2);
4110
4111            try {
4112                //    Convert operand 1 from a PHP array to a matrix
4113                $matrix = new PHPExcel_Shared_JAMA_Matrix($operand1);
4114                //    Perform the required operation against the operand 1 matrix, passing in operand 2
4115                $matrixResult = $matrix->$matrixFunction($operand2);
4116                $result = $matrixResult->getArray();
4117            } catch (PHPExcel_Exception $ex) {
4118                $this->_debugLog->writeDebugLog('JAMA Matrix Exception: ', $ex->getMessage());
4119                $result = '#VALUE!';
4120            }
4121        } else {
4122            if ((PHPExcel_Calculation_Functions::getCompatibilityMode() != PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE) &&
4123                ((is_string($operand1) && !is_numeric($operand1) && strlen($operand1)>0) ||
4124                 (is_string($operand2) && !is_numeric($operand2) && strlen($operand2)>0))) {
4125                $result = PHPExcel_Calculation_Functions::VALUE();
4126            } else {
4127                //    If we're dealing with non-matrix operations, execute the necessary operation
4128                switch ($operation) {
4129                    //    Addition
4130                    case '+':
4131                        $result = $operand1 + $operand2;
4132                        break;
4133                    //    Subtraction
4134                    case '-':
4135                        $result = $operand1 - $operand2;
4136                        break;
4137                    //    Multiplication
4138                    case '*':
4139                        $result = $operand1 * $operand2;
4140                        break;
4141                    //    Division
4142                    case '/':
4143                        if ($operand2 == 0) {
4144                            //    Trap for Divide by Zero error
4145                            $stack->push('Value', '#DIV/0!');
4146                            $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails('#DIV/0!'));
4147                            return false;
4148                        } else {
4149                            $result = $operand1 / $operand2;
4150                        }
4151                        break;
4152                    //    Power
4153                    case '^':
4154                        $result = pow($operand1, $operand2);
4155                        break;
4156                }
4157            }
4158        }
4159
4160        //    Log the result details
4161        $this->_debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($result));
4162        //    And push the result onto the stack
4163        $stack->push('Value', $result);
4164        return true;
4165    }
4166
4167
4168    // trigger an error, but nicely, if need be
4169    protected function raiseFormulaError($errorMessage)
4170    {
4171        $this->formulaError = $errorMessage;
4172        $this->cyclicReferenceStack->clear();
4173        if (!$this->suppressFormulaErrors) {
4174            throw new PHPExcel_Calculation_Exception($errorMessage);
4175        }
4176        trigger_error($errorMessage, E_USER_ERROR);
4177    }
4178
4179
4180    /**
4181     * Extract range values
4182     *
4183     * @param    string                &$pRange    String based range representation
4184     * @param    PHPExcel_Worksheet    $pSheet        Worksheet
4185     * @param    boolean                $resetLog    Flag indicating whether calculation log should be reset or not
4186     * @return  mixed                Array of values in range if range contains more than one element. Otherwise, a single value is returned.
4187     * @throws    PHPExcel_Calculation_Exception
4188     */
4189    public function extractCellRange(&$pRange = 'A1', PHPExcel_Worksheet $pSheet = null, $resetLog = true)
4190    {
4191        // Return value
4192        $returnValue = array ();
4193
4194//        echo 'extractCellRange('.$pRange.')', PHP_EOL;
4195        if ($pSheet !== null) {
4196            $pSheetName = $pSheet->getTitle();
4197//            echo 'Passed sheet name is '.$pSheetName.PHP_EOL;
4198//            echo 'Range reference is '.$pRange.PHP_EOL;
4199            if (strpos($pRange, '!') !== false) {
4200//                echo '$pRange reference includes sheet reference', PHP_EOL;
4201                list($pSheetName, $pRange) = PHPExcel_Worksheet::extractSheetTitle($pRange, true);
4202//                echo 'New sheet name is '.$pSheetName, PHP_EOL;
4203//                echo 'Adjusted Range reference is '.$pRange, PHP_EOL;
4204                $pSheet = $this->workbook->getSheetByName($pSheetName);
4205            }
4206
4207            // Extract range
4208            $aReferences = PHPExcel_Cell::extractAllCellReferencesInRange($pRange);
4209            $pRange = $pSheetName.'!'.$pRange;
4210            if (!isset($aReferences[1])) {
4211                //    Single cell in range
4212                sscanf($aReferences[0], '%[A-Z]%d', $currentCol, $currentRow);
4213                $cellValue = null;
4214                if ($pSheet->cellExists($aReferences[0])) {
4215                    $returnValue[$currentRow][$currentCol] = $pSheet->getCell($aReferences[0])->getCalculatedValue($resetLog);
4216                } else {
4217                    $returnValue[$currentRow][$currentCol] = null;
4218                }
4219            } else {
4220                // Extract cell data for all cells in the range
4221                foreach ($aReferences as $reference) {
4222                    // Extract range
4223                    sscanf($reference, '%[A-Z]%d', $currentCol, $currentRow);
4224                    $cellValue = null;
4225                    if ($pSheet->cellExists($reference)) {
4226                        $returnValue[$currentRow][$currentCol] = $pSheet->getCell($reference)->getCalculatedValue($resetLog);
4227                    } else {
4228                        $returnValue[$currentRow][$currentCol] = null;
4229                    }
4230                }
4231            }
4232        }
4233
4234        return $returnValue;
4235    }
4236
4237
4238    /**
4239     * Extract range values
4240     *
4241     * @param    string                &$pRange    String based range representation
4242     * @param    PHPExcel_Worksheet    $pSheet        Worksheet
4243     * @return  mixed                Array of values in range if range contains more than one element. Otherwise, a single value is returned.
4244     * @param    boolean                $resetLog    Flag indicating whether calculation log should be reset or not
4245     * @throws    PHPExcel_Calculation_Exception
4246     */
4247    public function extractNamedRange(&$pRange = 'A1', PHPExcel_Worksheet $pSheet = null, $resetLog = true)
4248    {
4249        // Return value
4250        $returnValue = array ();
4251
4252//        echo 'extractNamedRange('.$pRange.')<br />';
4253        if ($pSheet !== null) {
4254            $pSheetName = $pSheet->getTitle();
4255//            echo 'Current sheet name is '.$pSheetName.'<br />';
4256//            echo 'Range reference is '.$pRange.'<br />';
4257            if (strpos($pRange, '!') !== false) {
4258//                echo '$pRange reference includes sheet reference', PHP_EOL;
4259                list($pSheetName, $pRange) = PHPExcel_Worksheet::extractSheetTitle($pRange, true);
4260//                echo 'New sheet name is '.$pSheetName, PHP_EOL;
4261//                echo 'Adjusted Range reference is '.$pRange, PHP_EOL;
4262                $pSheet = $this->workbook->getSheetByName($pSheetName);
4263            }
4264
4265            // Named range?
4266            $namedRange = PHPExcel_NamedRange::resolveRange($pRange, $pSheet);
4267            if ($namedRange !== null) {
4268                $pSheet = $namedRange->getWorksheet();
4269//                echo 'Named Range '.$pRange.' (';
4270                $pRange = $namedRange->getRange();
4271                $splitRange = PHPExcel_Cell::splitRange($pRange);
4272                //    Convert row and column references
4273                if (ctype_alpha($splitRange[0][0])) {
4274                    $pRange = $splitRange[0][0] . '1:' . $splitRange[0][1] . $namedRange->getWorksheet()->getHighestRow();
4275                } elseif (ctype_digit($splitRange[0][0])) {
4276                    $pRange = 'A' . $splitRange[0][0] . ':' . $namedRange->getWorksheet()->getHighestColumn() . $splitRange[0][1];
4277                }
4278//                echo $pRange.') is in sheet '.$namedRange->getWorksheet()->getTitle().'<br />';
4279
4280//                if ($pSheet->getTitle() != $namedRange->getWorksheet()->getTitle()) {
4281//                    if (!$namedRange->getLocalOnly()) {
4282//                        $pSheet = $namedRange->getWorksheet();
4283//                    } else {
4284//                        return $returnValue;
4285//                    }
4286//                }
4287            } else {
4288                return PHPExcel_Calculation_Functions::REF();
4289            }
4290
4291            // Extract range
4292            $aReferences = PHPExcel_Cell::extractAllCellReferencesInRange($pRange);
4293//            var_dump($aReferences);
4294            if (!isset($aReferences[1])) {
4295                //    Single cell (or single column or row) in range
4296                list($currentCol, $currentRow) = PHPExcel_Cell::coordinateFromString($aReferences[0]);
4297                $cellValue = null;
4298                if ($pSheet->cellExists($aReferences[0])) {
4299                    $returnValue[$currentRow][$currentCol] = $pSheet->getCell($aReferences[0])->getCalculatedValue($resetLog);
4300                } else {
4301                    $returnValue[$currentRow][$currentCol] = null;
4302                }
4303            } else {
4304                // Extract cell data for all cells in the range
4305                foreach ($aReferences as $reference) {
4306                    // Extract range
4307                    list($currentCol, $currentRow) = PHPExcel_Cell::coordinateFromString($reference);
4308//                    echo 'NAMED RANGE: $currentCol='.$currentCol.' $currentRow='.$currentRow.'<br />';
4309                    $cellValue = null;
4310                    if ($pSheet->cellExists($reference)) {
4311                        $returnValue[$currentRow][$currentCol] = $pSheet->getCell($reference)->getCalculatedValue($resetLog);
4312                    } else {
4313                        $returnValue[$currentRow][$currentCol] = null;
4314                    }
4315                }
4316            }
4317//                print_r($returnValue);
4318//            echo '<br />';
4319        }
4320
4321        return $returnValue;
4322    }
4323
4324
4325    /**
4326     * Is a specific function implemented?
4327     *
4328     * @param    string    $pFunction    Function Name
4329     * @return    boolean
4330     */
4331    public function isImplemented($pFunction = '')
4332    {
4333        $pFunction = strtoupper($pFunction);
4334        if (isset(self::$PHPExcelFunctions[$pFunction])) {
4335            return (self::$PHPExcelFunctions[$pFunction]['functionCall'] != 'PHPExcel_Calculation_Functions::DUMMY');
4336        } else {
4337            return false;
4338        }
4339    }
4340
4341
4342    /**
4343     * Get a list of all implemented functions as an array of function objects
4344     *
4345     * @return    array of PHPExcel_Calculation_Function
4346     */
4347    public function listFunctions()
4348    {
4349        $returnValue = array();
4350
4351        foreach (self::$PHPExcelFunctions as $functionName => $function) {
4352            if ($function['functionCall'] != 'PHPExcel_Calculation_Functions::DUMMY') {
4353                $returnValue[$functionName] = new PHPExcel_Calculation_Function(
4354                    $function['category'],
4355                    $functionName,
4356                    $function['functionCall']
4357                );
4358            }
4359        }
4360
4361        return $returnValue;
4362    }
4363
4364
4365    /**
4366     * Get a list of all Excel function names
4367     *
4368     * @return    array
4369     */
4370    public function listAllFunctionNames()
4371    {
4372        return array_keys(self::$PHPExcelFunctions);
4373    }
4374
4375    /**
4376     * Get a list of implemented Excel function names
4377     *
4378     * @return    array
4379     */
4380    public function listFunctionNames()
4381    {
4382        $returnValue = array();
4383        foreach (self::$PHPExcelFunctions as $functionName => $function) {
4384            if ($function['functionCall'] != 'PHPExcel_Calculation_Functions::DUMMY') {
4385                $returnValue[] = $functionName;
4386            }
4387        }
4388
4389        return $returnValue;
4390    }
4391}
4392