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
12/** EULER */
13define('EULER', 2.71828182845904523536);
14
15/**
16 * PHPExcel_Calculation_Engineering
17 *
18 * Copyright (c) 2006 - 2015 PHPExcel
19 *
20 * This library is free software; you can redistribute it and/or
21 * modify it under the terms of the GNU Lesser General Public
22 * License as published by the Free Software Foundation; either
23 * version 2.1 of the License, or (at your option) any later version.
24 *
25 * This library is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
28 * Lesser General Public License for more details.
29 *
30 * You should have received a copy of the GNU Lesser General Public
31 * License along with this library; if not, write to the Free Software
32 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
33 *
34 * @category    PHPExcel
35 * @package        PHPExcel_Calculation
36 * @copyright    Copyright (c) 2006 - 2015 PHPExcel (http://www.codeplex.com/PHPExcel)
37 * @license        http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt    LGPL
38 * @version        ##VERSION##, ##DATE##
39 */
40class PHPExcel_Calculation_Engineering
41{
42    /**
43     * Details of the Units of measure that can be used in CONVERTUOM()
44     *
45     * @var mixed[]
46     */
47    private static $conversionUnits = array(
48        'g'     => array('Group' => 'Mass',        'Unit Name' => 'Gram',                     'AllowPrefix' => true),
49        'sg'    => array('Group' => 'Mass',        'Unit Name' => 'Slug',                     'AllowPrefix' => false),
50        'lbm'   => array('Group' => 'Mass',        'Unit Name' => 'Pound mass (avoirdupois)', 'AllowPrefix' => false),
51        'u'     => array('Group' => 'Mass',        'Unit Name' => 'U (atomic mass unit)',     'AllowPrefix' => true),
52        'ozm'   => array('Group' => 'Mass',        'Unit Name' => 'Ounce mass (avoirdupois)', 'AllowPrefix' => false),
53        'm'     => array('Group' => 'Distance',    'Unit Name' => 'Meter',                    'AllowPrefix' => true),
54        'mi'    => array('Group' => 'Distance',    'Unit Name' => 'Statute mile',             'AllowPrefix' => false),
55        'Nmi'   => array('Group' => 'Distance',    'Unit Name' => 'Nautical mile',            'AllowPrefix' => false),
56        'in'    => array('Group' => 'Distance',    'Unit Name' => 'Inch',                     'AllowPrefix' => false),
57        'ft'    => array('Group' => 'Distance',    'Unit Name' => 'Foot',                     'AllowPrefix' => false),
58        'yd'    => array('Group' => 'Distance',    'Unit Name' => 'Yard',                     'AllowPrefix' => false),
59        'ang'   => array('Group' => 'Distance',    'Unit Name' => 'Angstrom',                 'AllowPrefix' => true),
60        'Pica'  => array('Group' => 'Distance',    'Unit Name' => 'Pica (1/72 in)',           'AllowPrefix' => false),
61        'yr'    => array('Group' => 'Time',        'Unit Name' => 'Year',                     'AllowPrefix' => false),
62        'day'   => array('Group' => 'Time',        'Unit Name' => 'Day',                      'AllowPrefix' => false),
63        'hr'    => array('Group' => 'Time',        'Unit Name' => 'Hour',                     'AllowPrefix' => false),
64        'mn'    => array('Group' => 'Time',        'Unit Name' => 'Minute',                   'AllowPrefix' => false),
65        'sec'   => array('Group' => 'Time',        'Unit Name' => 'Second',                   'AllowPrefix' => true),
66        'Pa'    => array('Group' => 'Pressure',    'Unit Name' => 'Pascal',                   'AllowPrefix' => true),
67        'p'     => array('Group' => 'Pressure',    'Unit Name' => 'Pascal',                   'AllowPrefix' => true),
68        'atm'   => array('Group' => 'Pressure',    'Unit Name' => 'Atmosphere',               'AllowPrefix' => true),
69        'at'    => array('Group' => 'Pressure',    'Unit Name' => 'Atmosphere',               'AllowPrefix' => true),
70        'mmHg'  => array('Group' => 'Pressure',    'Unit Name' => 'mm of Mercury',            'AllowPrefix' => true),
71        'N'     => array('Group' => 'Force',       'Unit Name' => 'Newton',                   'AllowPrefix' => true),
72        'dyn'   => array('Group' => 'Force',       'Unit Name' => 'Dyne',                     'AllowPrefix' => true),
73        'dy'    => array('Group' => 'Force',       'Unit Name' => 'Dyne',                     'AllowPrefix' => true),
74        'lbf'   => array('Group' => 'Force',       'Unit Name' => 'Pound force',              'AllowPrefix' => false),
75        'J'     => array('Group' => 'Energy',      'Unit Name' => 'Joule',                    'AllowPrefix' => true),
76        'e'     => array('Group' => 'Energy',      'Unit Name' => 'Erg',                      'AllowPrefix' => true),
77        'c'     => array('Group' => 'Energy',      'Unit Name' => 'Thermodynamic calorie',    'AllowPrefix' => true),
78        'cal'   => array('Group' => 'Energy',      'Unit Name' => 'IT calorie',               'AllowPrefix' => true),
79        'eV'    => array('Group' => 'Energy',      'Unit Name' => 'Electron volt',            'AllowPrefix' => true),
80        'ev'    => array('Group' => 'Energy',      'Unit Name' => 'Electron volt',            'AllowPrefix' => true),
81        'HPh'   => array('Group' => 'Energy',      'Unit Name' => 'Horsepower-hour',          'AllowPrefix' => false),
82        'hh'    => array('Group' => 'Energy',      'Unit Name' => 'Horsepower-hour',          'AllowPrefix' => false),
83        'Wh'    => array('Group' => 'Energy',      'Unit Name' => 'Watt-hour',                'AllowPrefix' => true),
84        'wh'    => array('Group' => 'Energy',      'Unit Name' => 'Watt-hour',                'AllowPrefix' => true),
85        'flb'   => array('Group' => 'Energy',      'Unit Name' => 'Foot-pound',               'AllowPrefix' => false),
86        'BTU'   => array('Group' => 'Energy',      'Unit Name' => 'BTU',                      'AllowPrefix' => false),
87        'btu'   => array('Group' => 'Energy',      'Unit Name' => 'BTU',                      'AllowPrefix' => false),
88        'HP'    => array('Group' => 'Power',       'Unit Name' => 'Horsepower',               'AllowPrefix' => false),
89        'h'     => array('Group' => 'Power',       'Unit Name' => 'Horsepower',               'AllowPrefix' => false),
90        'W'     => array('Group' => 'Power',       'Unit Name' => 'Watt',                     'AllowPrefix' => true),
91        'w'     => array('Group' => 'Power',       'Unit Name' => 'Watt',                     'AllowPrefix' => true),
92        'T'     => array('Group' => 'Magnetism',   'Unit Name' => 'Tesla',                    'AllowPrefix' => true),
93        'ga'    => array('Group' => 'Magnetism',   'Unit Name' => 'Gauss',                    'AllowPrefix' => true),
94        'C'     => array('Group' => 'Temperature', 'Unit Name' => 'Celsius',                  'AllowPrefix' => false),
95        'cel'   => array('Group' => 'Temperature', 'Unit Name' => 'Celsius',                  'AllowPrefix' => false),
96        'F'     => array('Group' => 'Temperature', 'Unit Name' => 'Fahrenheit',               'AllowPrefix' => false),
97        'fah'   => array('Group' => 'Temperature', 'Unit Name' => 'Fahrenheit',               'AllowPrefix' => false),
98        'K'     => array('Group' => 'Temperature', 'Unit Name' => 'Kelvin',                   'AllowPrefix' => false),
99        'kel'   => array('Group' => 'Temperature', 'Unit Name' => 'Kelvin',                   'AllowPrefix' => false),
100        'tsp'   => array('Group' => 'Liquid',      'Unit Name' => 'Teaspoon',                 'AllowPrefix' => false),
101        'tbs'   => array('Group' => 'Liquid',      'Unit Name' => 'Tablespoon',               'AllowPrefix' => false),
102        'oz'    => array('Group' => 'Liquid',      'Unit Name' => 'Fluid Ounce',              'AllowPrefix' => false),
103        'cup'   => array('Group' => 'Liquid',      'Unit Name' => 'Cup',                      'AllowPrefix' => false),
104        'pt'    => array('Group' => 'Liquid',      'Unit Name' => 'U.S. Pint',                'AllowPrefix' => false),
105        'us_pt' => array('Group' => 'Liquid',      'Unit Name' => 'U.S. Pint',                'AllowPrefix' => false),
106        'uk_pt' => array('Group' => 'Liquid',      'Unit Name' => 'U.K. Pint',                'AllowPrefix' => false),
107        'qt'    => array('Group' => 'Liquid',      'Unit Name' => 'Quart',                    'AllowPrefix' => false),
108        'gal'   => array('Group' => 'Liquid',      'Unit Name' => 'Gallon',                   'AllowPrefix' => false),
109        'l'     => array('Group' => 'Liquid',      'Unit Name' => 'Litre',                    'AllowPrefix' => true),
110        'lt'    => array('Group' => 'Liquid',      'Unit Name' => 'Litre',                    'AllowPrefix' => true),
111    );
112
113    /**
114     * Details of the Multiplier prefixes that can be used with Units of Measure in CONVERTUOM()
115     *
116     * @var mixed[]
117     */
118    private static $conversionMultipliers = array(
119        'Y' => array('multiplier' => 1E24,  'name' => 'yotta'),
120        'Z' => array('multiplier' => 1E21,  'name' => 'zetta'),
121        'E' => array('multiplier' => 1E18,  'name' => 'exa'),
122        'P' => array('multiplier' => 1E15,  'name' => 'peta'),
123        'T' => array('multiplier' => 1E12,  'name' => 'tera'),
124        'G' => array('multiplier' => 1E9,   'name' => 'giga'),
125        'M' => array('multiplier' => 1E6,   'name' => 'mega'),
126        'k' => array('multiplier' => 1E3,   'name' => 'kilo'),
127        'h' => array('multiplier' => 1E2,   'name' => 'hecto'),
128        'e' => array('multiplier' => 1E1,   'name' => 'deka'),
129        'd' => array('multiplier' => 1E-1,  'name' => 'deci'),
130        'c' => array('multiplier' => 1E-2,  'name' => 'centi'),
131        'm' => array('multiplier' => 1E-3,  'name' => 'milli'),
132        'u' => array('multiplier' => 1E-6,  'name' => 'micro'),
133        'n' => array('multiplier' => 1E-9,  'name' => 'nano'),
134        'p' => array('multiplier' => 1E-12, 'name' => 'pico'),
135        'f' => array('multiplier' => 1E-15, 'name' => 'femto'),
136        'a' => array('multiplier' => 1E-18, 'name' => 'atto'),
137        'z' => array('multiplier' => 1E-21, 'name' => 'zepto'),
138        'y' => array('multiplier' => 1E-24, 'name' => 'yocto'),
139    );
140
141    /**
142     * Details of the Units of measure conversion factors, organised by group
143     *
144     * @var mixed[]
145     */
146    private static $unitConversions = array(
147        'Mass' => array(
148            'g' => array(
149                'g'   => 1.0,
150                'sg'  => 6.85220500053478E-05,
151                'lbm' => 2.20462291469134E-03,
152                'u'   => 6.02217000000000E+23,
153                'ozm' => 3.52739718003627E-02,
154            ),
155            'sg' => array(
156                'g'   => 1.45938424189287E+04,
157                'sg'  => 1.0,
158                'lbm' => 3.21739194101647E+01,
159                'u'   => 8.78866000000000E+27,
160                'ozm' => 5.14782785944229E+02,
161            ),
162            'lbm' => array(
163                'g'   => 4.5359230974881148E+02,
164                'sg'  => 3.10810749306493E-02,
165                'lbm' => 1.0,
166                'u'   => 2.73161000000000E+26,
167                'ozm' => 1.60000023429410E+01,
168            ),
169            'u' => array(
170                'g'   => 1.66053100460465E-24,
171                'sg'  => 1.13782988532950E-28,
172                'lbm' => 3.66084470330684E-27,
173                'u'   => 1.0,
174                'ozm' => 5.85735238300524E-26,
175            ),
176            'ozm' => array(
177                'g'   => 2.83495152079732E+01,
178                'sg'  => 1.94256689870811E-03,
179                'lbm' => 6.24999908478882E-02,
180                'u'   => 1.70725600000000E+25,
181                'ozm' => 1.0,
182            ),
183        ),
184        'Distance' => array(
185            'm' => array(
186                'm'    => 1.0,
187                'mi'   => 6.21371192237334E-04,
188                'Nmi'  => 5.39956803455724E-04,
189                'in'   => 3.93700787401575E+01,
190                'ft'   => 3.28083989501312E+00,
191                'yd'   => 1.09361329797891E+00,
192                'ang'  => 1.00000000000000E+10,
193                'Pica' => 2.83464566929116E+03,
194            ),
195            'mi' => array(
196                'm'    => 1.60934400000000E+03,
197                'mi'   => 1.0,
198                'Nmi'  => 8.68976241900648E-01,
199                'in'   => 6.33600000000000E+04,
200                'ft'   => 5.28000000000000E+03,
201                'yd'   => 1.76000000000000E+03,
202                'ang'  => 1.60934400000000E+13,
203                'Pica' => 4.56191999999971E+06,
204            ),
205            'Nmi' => array(
206                'm'    => 1.85200000000000E+03,
207                'mi'   => 1.15077944802354E+00,
208                'Nmi'  => 1.0,
209                'in'   => 7.29133858267717E+04,
210                'ft'   => 6.07611548556430E+03,
211                'yd'   => 2.02537182785694E+03,
212                'ang'  => 1.85200000000000E+13,
213                'Pica' => 5.24976377952723E+06,
214            ),
215            'in' => array(
216                'm'    => 2.54000000000000E-02,
217                'mi'   => 1.57828282828283E-05,
218                'Nmi'  => 1.37149028077754E-05,
219                'in'   => 1.0,
220                'ft'   => 8.33333333333333E-02,
221                'yd'   => 2.77777777686643E-02,
222                'ang'  => 2.54000000000000E+08,
223                'Pica' => 7.19999999999955E+01,
224            ),
225            'ft' => array(
226                'm'    => 3.04800000000000E-01,
227                'mi'   => 1.89393939393939E-04,
228                'Nmi'  => 1.64578833693305E-04,
229                'in'   => 1.20000000000000E+01,
230                'ft'   => 1.0,
231                'yd'   => 3.33333333223972E-01,
232                'ang'  => 3.04800000000000E+09,
233                'Pica' => 8.63999999999946E+02,
234            ),
235            'yd' => array(
236                'm'    => 9.14400000300000E-01,
237                'mi'   => 5.68181818368230E-04,
238                'Nmi'  => 4.93736501241901E-04,
239                'in'   => 3.60000000118110E+01,
240                'ft'   => 3.00000000000000E+00,
241                'yd'   => 1.0,
242                'ang'  => 9.14400000300000E+09,
243                'Pica' => 2.59200000085023E+03,
244            ),
245            'ang' => array(
246                'm'    => 1.00000000000000E-10,
247                'mi'   => 6.21371192237334E-14,
248                'Nmi'  => 5.39956803455724E-14,
249                'in'   => 3.93700787401575E-09,
250                'ft'   => 3.28083989501312E-10,
251                'yd'   => 1.09361329797891E-10,
252                'ang'  => 1.0,
253                'Pica' => 2.83464566929116E-07,
254            ),
255            'Pica' => array(
256                'm'    => 3.52777777777800E-04,
257                'mi'   => 2.19205948372629E-07,
258                'Nmi'  => 1.90484761219114E-07,
259                'in'   => 1.38888888888898E-02,
260                'ft'   => 1.15740740740748E-03,
261                'yd'   => 3.85802469009251E-04,
262                'ang'  => 3.52777777777800E+06,
263                'Pica' => 1.0,
264            ),
265        ),
266        'Time' => array(
267            'yr' => array(
268                'yr'  => 1.0,
269                'day' => 365.25,
270                'hr'  => 8766.0,
271                'mn'  => 525960.0,
272                'sec' => 31557600.0,
273            ),
274            'day' => array(
275                'yr'  => 2.73785078713210E-03,
276                'day' => 1.0,
277                'hr'  => 24.0,
278                'mn'  => 1440.0,
279                'sec' => 86400.0,
280            ),
281            'hr' => array(
282                'yr'  => 1.14077116130504E-04,
283                'day' => 4.16666666666667E-02,
284                'hr'  => 1.0,
285                'mn'  => 60.0,
286                'sec' => 3600.0,
287            ),
288            'mn' => array(
289                'yr'  => 1.90128526884174E-06,
290                'day' => 6.94444444444444E-04,
291                'hr'  => 1.66666666666667E-02,
292                'mn'  => 1.0,
293                'sec' => 60.0,
294            ),
295            'sec' => array(
296                'yr'  => 3.16880878140289E-08,
297                'day' => 1.15740740740741E-05,
298                'hr'  => 2.77777777777778E-04,
299                'mn'  => 1.66666666666667E-02,
300                'sec' => 1.0,
301            ),
302        ),
303        'Pressure' => array(
304            'Pa' => array(
305                'Pa'   => 1.0,
306                'p'    => 1.0,
307                'atm'  => 9.86923299998193E-06,
308                'at'   => 9.86923299998193E-06,
309                'mmHg' => 7.50061707998627E-03,
310            ),
311            'p' => array(
312                'Pa'   => 1.0,
313                'p'    => 1.0,
314                'atm'  => 9.86923299998193E-06,
315                'at'   => 9.86923299998193E-06,
316                'mmHg' => 7.50061707998627E-03,
317            ),
318            'atm' => array(
319                'Pa'   => 1.01324996583000E+05,
320                'p'    => 1.01324996583000E+05,
321                'atm'  => 1.0,
322                'at'   => 1.0,
323                'mmHg' => 760.0,
324            ),
325            'at' => array(
326                'Pa'   => 1.01324996583000E+05,
327                'p'    => 1.01324996583000E+05,
328                'atm'  => 1.0,
329                'at'   => 1.0,
330                'mmHg' => 760.0,
331            ),
332            'mmHg' => array(
333                'Pa'   => 1.33322363925000E+02,
334                'p'    => 1.33322363925000E+02,
335                'atm'  => 1.31578947368421E-03,
336                'at'   => 1.31578947368421E-03,
337                'mmHg' => 1.0,
338            ),
339        ),
340        'Force' => array(
341            'N' => array(
342                'N'   => 1.0,
343                'dyn' => 1.0E+5,
344                'dy'  => 1.0E+5,
345                'lbf' => 2.24808923655339E-01,
346            ),
347            'dyn' => array(
348                'N'   => 1.0E-5,
349                'dyn' => 1.0,
350                'dy'  => 1.0,
351                'lbf' => 2.24808923655339E-06,
352            ),
353            'dy' => array(
354                'N'   => 1.0E-5,
355                'dyn' => 1.0,
356                'dy'  => 1.0,
357                'lbf' => 2.24808923655339E-06,
358            ),
359            'lbf' => array(
360                'N'   => 4.448222,
361                'dyn' => 4.448222E+5,
362                'dy'  => 4.448222E+5,
363                'lbf' => 1.0,
364            ),
365        ),
366        'Energy' => array(
367            'J' => array(
368                'J'   => 1.0,
369                'e'   => 9.99999519343231E+06,
370                'c'   => 2.39006249473467E-01,
371                'cal' => 2.38846190642017E-01,
372                'eV'  => 6.24145700000000E+18,
373                'ev'  => 6.24145700000000E+18,
374                'HPh' => 3.72506430801000E-07,
375                'hh'  => 3.72506430801000E-07,
376                'Wh'  => 2.77777916238711E-04,
377                'wh'  => 2.77777916238711E-04,
378                'flb' => 2.37304222192651E+01,
379                'BTU' => 9.47815067349015E-04,
380                'btu' => 9.47815067349015E-04,
381            ),
382            'e' => array(
383                'J'   => 1.00000048065700E-07,
384                'e'   => 1.0,
385                'c'   => 2.39006364353494E-08,
386                'cal' => 2.38846305445111E-08,
387                'eV'  => 6.24146000000000E+11,
388                'ev'  => 6.24146000000000E+11,
389                'HPh' => 3.72506609848824E-14,
390                'hh'  => 3.72506609848824E-14,
391                'Wh'  => 2.77778049754611E-11,
392                'wh'  => 2.77778049754611E-11,
393                'flb' => 2.37304336254586E-06,
394                'BTU' => 9.47815522922962E-11,
395                'btu' => 9.47815522922962E-11,
396            ),
397            'c' => array(
398                'J'   => 4.18399101363672E+00,
399                'e'   => 4.18398900257312E+07,
400                'c'   => 1.0,
401                'cal' => 9.99330315287563E-01,
402                'eV'  => 2.61142000000000E+19,
403                'ev'  => 2.61142000000000E+19,
404                'HPh' => 1.55856355899327E-06,
405                'hh'  => 1.55856355899327E-06,
406                'Wh'  => 1.16222030532950E-03,
407                'wh'  => 1.16222030532950E-03,
408                'flb' => 9.92878733152102E+01,
409                'BTU' => 3.96564972437776E-03,
410                'btu' => 3.96564972437776E-03,
411            ),
412            'cal' => array(
413                'J'   => 4.18679484613929E+00,
414                'e'   => 4.18679283372801E+07,
415                'c'   => 1.00067013349059E+00,
416                'cal' => 1.0,
417                'eV'  => 2.61317000000000E+19,
418                'ev'  => 2.61317000000000E+19,
419                'HPh' => 1.55960800463137E-06,
420                'hh'  => 1.55960800463137E-06,
421                'Wh'  => 1.16299914807955E-03,
422                'wh'  => 1.16299914807955E-03,
423                'flb' => 9.93544094443283E+01,
424                'BTU' => 3.96830723907002E-03,
425                'btu' => 3.96830723907002E-03,
426            ),
427            'eV' => array(
428                'J'   => 1.60219000146921E-19,
429                'e'   => 1.60218923136574E-12,
430                'c'   => 3.82933423195043E-20,
431                'cal' => 3.82676978535648E-20,
432                'eV'  => 1.0,
433                'ev'  => 1.0,
434                'HPh' => 5.96826078912344E-26,
435                'hh'  => 5.96826078912344E-26,
436                'Wh'  => 4.45053000026614E-23,
437                'wh'  => 4.45053000026614E-23,
438                'flb' => 3.80206452103492E-18,
439                'BTU' => 1.51857982414846E-22,
440                'btu' => 1.51857982414846E-22,
441            ),
442            'ev' => array(
443                'J'   => 1.60219000146921E-19,
444                'e'   => 1.60218923136574E-12,
445                'c'   => 3.82933423195043E-20,
446                'cal' => 3.82676978535648E-20,
447                'eV'  => 1.0,
448                'ev'  => 1.0,
449                'HPh' => 5.96826078912344E-26,
450                'hh'  => 5.96826078912344E-26,
451                'Wh'  => 4.45053000026614E-23,
452                'wh'  => 4.45053000026614E-23,
453                'flb' => 3.80206452103492E-18,
454                'BTU' => 1.51857982414846E-22,
455                'btu' => 1.51857982414846E-22,
456            ),
457            'HPh' => array(
458                'J'   => 2.68451741316170E+06,
459                'e'   => 2.68451612283024E+13,
460                'c'   => 6.41616438565991E+05,
461                'cal' => 6.41186757845835E+05,
462                'eV'  => 1.67553000000000E+25,
463                'ev'  => 1.67553000000000E+25,
464                'HPh' => 1.0,
465                'hh'  => 1.0,
466                'Wh'  => 7.45699653134593E+02,
467                'wh'  => 7.45699653134593E+02,
468                'flb' => 6.37047316692964E+07,
469                'BTU' => 2.54442605275546E+03,
470                'btu' => 2.54442605275546E+03,
471            ),
472            'hh' => array(
473                'J'   => 2.68451741316170E+06,
474                'e'   => 2.68451612283024E+13,
475                'c'   => 6.41616438565991E+05,
476                'cal' => 6.41186757845835E+05,
477                'eV'  => 1.67553000000000E+25,
478                'ev'  => 1.67553000000000E+25,
479                'HPh' => 1.0,
480                'hh'  => 1.0,
481                'Wh'  => 7.45699653134593E+02,
482                'wh'  => 7.45699653134593E+02,
483                'flb' => 6.37047316692964E+07,
484                'BTU' => 2.54442605275546E+03,
485                'btu' => 2.54442605275546E+03,
486            ),
487            'Wh' => array(
488                'J'   => 3.59999820554720E+03,
489                'e'   => 3.59999647518369E+10,
490                'c'   => 8.60422069219046E+02,
491                'cal' => 8.59845857713046E+02,
492                'eV'  => 2.24692340000000E+22,
493                'ev'  => 2.24692340000000E+22,
494                'HPh' => 1.34102248243839E-03,
495                'hh'  => 1.34102248243839E-03,
496                'Wh'  => 1.0,
497                'wh'  => 1.0,
498                'flb' => 8.54294774062316E+04,
499                'BTU' => 3.41213254164705E+00,
500                'btu' => 3.41213254164705E+00,
501            ),
502            'wh' => array(
503                'J'   => 3.59999820554720E+03,
504                'e'   => 3.59999647518369E+10,
505                'c'   => 8.60422069219046E+02,
506                'cal' => 8.59845857713046E+02,
507                'eV'  => 2.24692340000000E+22,
508                'ev'  => 2.24692340000000E+22,
509                'HPh' => 1.34102248243839E-03,
510                'hh'  => 1.34102248243839E-03,
511                'Wh'  => 1.0,
512                'wh'  => 1.0,
513                'flb' => 8.54294774062316E+04,
514                'BTU' => 3.41213254164705E+00,
515                'btu' => 3.41213254164705E+00,
516            ),
517            'flb' => array(
518                'J'   => 4.21400003236424E-02,
519                'e'   => 4.21399800687660E+05,
520                'c'   => 1.00717234301644E-02,
521                'cal' => 1.00649785509554E-02,
522                'eV'  => 2.63015000000000E+17,
523                'ev'  => 2.63015000000000E+17,
524                'HPh' => 1.56974211145130E-08,
525                'hh'  => 1.56974211145130E-08,
526                'Wh'  => 1.17055614802000E-05,
527                'wh'  => 1.17055614802000E-05,
528                'flb' => 1.0,
529                'BTU' => 3.99409272448406E-05,
530                'btu' => 3.99409272448406E-05,
531            ),
532            'BTU' => array(
533                'J'   => 1.05505813786749E+03,
534                'e'   => 1.05505763074665E+10,
535                'c'   => 2.52165488508168E+02,
536                'cal' => 2.51996617135510E+02,
537                'eV'  => 6.58510000000000E+21,
538                'ev'  => 6.58510000000000E+21,
539                'HPh' => 3.93015941224568E-04,
540                'hh'  => 3.93015941224568E-04,
541                'Wh'  => 2.93071851047526E-01,
542                'wh'  => 2.93071851047526E-01,
543                'flb' => 2.50369750774671E+04,
544                'BTU' => 1.0,
545                'btu' => 1.0,
546            ),
547            'btu' => array(
548                'J'   => 1.05505813786749E+03,
549                'e'   => 1.05505763074665E+10,
550                'c'   => 2.52165488508168E+02,
551                'cal' => 2.51996617135510E+02,
552                'eV'  => 6.58510000000000E+21,
553                'ev'  => 6.58510000000000E+21,
554                'HPh' => 3.93015941224568E-04,
555                'hh'  => 3.93015941224568E-04,
556                'Wh'  => 2.93071851047526E-01,
557                'wh'  => 2.93071851047526E-01,
558                'flb' => 2.50369750774671E+04,
559                'BTU' => 1.0,
560                'btu' => 1.0,
561            ),
562        ),
563        'Power' => array(
564            'HP' => array(
565                'HP' => 1.0,
566                'h'  => 1.0,
567                'W'  => 7.45701000000000E+02,
568                'w'  => 7.45701000000000E+02,
569            ),
570            'h' => array(
571                'HP' => 1.0,
572                'h'  => 1.0,
573                'W'  => 7.45701000000000E+02,
574                'w'  => 7.45701000000000E+02,
575            ),
576            'W' => array(
577                'HP' => 1.34102006031908E-03,
578                'h'  => 1.34102006031908E-03,
579                'W'  => 1.0,
580                'w'  => 1.0,
581            ),
582            'w' => array(
583                'HP' => 1.34102006031908E-03,
584                'h'  => 1.34102006031908E-03,
585                'W'  => 1.0,
586                'w'  => 1.0,
587            ),
588        ),
589        'Magnetism' => array(
590            'T' => array(
591                'T'  => 1.0,
592                'ga' => 10000.0,
593            ),
594            'ga' => array(
595                'T'  => 0.0001,
596                'ga' => 1.0,
597            ),
598        ),
599        'Liquid' => array(
600            'tsp' => array(
601                'tsp'   => 1.0,
602                'tbs'   => 3.33333333333333E-01,
603                'oz'    => 1.66666666666667E-01,
604                'cup'   => 2.08333333333333E-02,
605                'pt'    => 1.04166666666667E-02,
606                'us_pt' => 1.04166666666667E-02,
607                'uk_pt' => 8.67558516821960E-03,
608                'qt'    => 5.20833333333333E-03,
609                'gal'   => 1.30208333333333E-03,
610                'l'     => 4.92999408400710E-03,
611                'lt'    => 4.92999408400710E-03,
612            ),
613            'tbs' => array(
614                'tsp'   => 3.00000000000000E+00,
615                'tbs'   => 1.0,
616                'oz'    => 5.00000000000000E-01,
617                'cup'   => 6.25000000000000E-02,
618                'pt'    => 3.12500000000000E-02,
619                'us_pt' => 3.12500000000000E-02,
620                'uk_pt' => 2.60267555046588E-02,
621                'qt'    => 1.56250000000000E-02,
622                'gal'   => 3.90625000000000E-03,
623                'l'     => 1.47899822520213E-02,
624                'lt'    => 1.47899822520213E-02,
625            ),
626            'oz' => array(
627                'tsp'   => 6.00000000000000E+00,
628                'tbs'   => 2.00000000000000E+00,
629                'oz'    => 1.0,
630                'cup'   => 1.25000000000000E-01,
631                'pt'    => 6.25000000000000E-02,
632                'us_pt' => 6.25000000000000E-02,
633                'uk_pt' => 5.20535110093176E-02,
634                'qt'    => 3.12500000000000E-02,
635                'gal'   => 7.81250000000000E-03,
636                'l'     => 2.95799645040426E-02,
637                'lt'    => 2.95799645040426E-02,
638            ),
639            'cup' => array(
640                'tsp'   => 4.80000000000000E+01,
641                'tbs'   => 1.60000000000000E+01,
642                'oz'    => 8.00000000000000E+00,
643                'cup'   => 1.0,
644                'pt'    => 5.00000000000000E-01,
645                'us_pt' => 5.00000000000000E-01,
646                'uk_pt' => 4.16428088074541E-01,
647                'qt'    => 2.50000000000000E-01,
648                'gal'   => 6.25000000000000E-02,
649                'l'     => 2.36639716032341E-01,
650                'lt'    => 2.36639716032341E-01,
651            ),
652            'pt' => array(
653                'tsp'   => 9.60000000000000E+01,
654                'tbs'   => 3.20000000000000E+01,
655                'oz'    => 1.60000000000000E+01,
656                'cup'   => 2.00000000000000E+00,
657                'pt'    => 1.0,
658                'us_pt' => 1.0,
659                'uk_pt' => 8.32856176149081E-01,
660                'qt'    => 5.00000000000000E-01,
661                'gal'   => 1.25000000000000E-01,
662                'l'     => 4.73279432064682E-01,
663                'lt'    => 4.73279432064682E-01,
664            ),
665            'us_pt' => array(
666                'tsp'   => 9.60000000000000E+01,
667                'tbs'   => 3.20000000000000E+01,
668                'oz'    => 1.60000000000000E+01,
669                'cup'   => 2.00000000000000E+00,
670                'pt'    => 1.0,
671                'us_pt' => 1.0,
672                'uk_pt' => 8.32856176149081E-01,
673                'qt'    => 5.00000000000000E-01,
674                'gal'   => 1.25000000000000E-01,
675                'l'     => 4.73279432064682E-01,
676                'lt'    => 4.73279432064682E-01,
677            ),
678            'uk_pt' => array(
679                'tsp'   => 1.15266000000000E+02,
680                'tbs'   => 3.84220000000000E+01,
681                'oz'    => 1.92110000000000E+01,
682                'cup'   => 2.40137500000000E+00,
683                'pt'    => 1.20068750000000E+00,
684                'us_pt' => 1.20068750000000E+00,
685                'uk_pt' => 1.0,
686                'qt'    => 6.00343750000000E-01,
687                'gal'   => 1.50085937500000E-01,
688                'l'     => 5.68260698087162E-01,
689                'lt'    => 5.68260698087162E-01,
690            ),
691            'qt' => array(
692                'tsp'   => 1.92000000000000E+02,
693                'tbs'   => 6.40000000000000E+01,
694                'oz'    => 3.20000000000000E+01,
695                'cup'   => 4.00000000000000E+00,
696                'pt'    => 2.00000000000000E+00,
697                'us_pt' => 2.00000000000000E+00,
698                'uk_pt' => 1.66571235229816E+00,
699                'qt'    => 1.0,
700                'gal'   => 2.50000000000000E-01,
701                'l'     => 9.46558864129363E-01,
702                'lt'    => 9.46558864129363E-01,
703            ),
704            'gal' => array(
705                'tsp'   => 7.68000000000000E+02,
706                'tbs'   => 2.56000000000000E+02,
707                'oz'    => 1.28000000000000E+02,
708                'cup'   => 1.60000000000000E+01,
709                'pt'    => 8.00000000000000E+00,
710                'us_pt' => 8.00000000000000E+00,
711                'uk_pt' => 6.66284940919265E+00,
712                'qt'    => 4.00000000000000E+00,
713                'gal'   => 1.0,
714                'l'     => 3.78623545651745E+00,
715                'lt'    => 3.78623545651745E+00,
716            ),
717            'l' => array(
718                'tsp'   => 2.02840000000000E+02,
719                'tbs'   => 6.76133333333333E+01,
720                'oz'    => 3.38066666666667E+01,
721                'cup'   => 4.22583333333333E+00,
722                'pt'    => 2.11291666666667E+00,
723                'us_pt' => 2.11291666666667E+00,
724                'uk_pt' => 1.75975569552166E+00,
725                'qt'    => 1.05645833333333E+00,
726                'gal'   => 2.64114583333333E-01,
727                'l'     => 1.0,
728                'lt'    => 1.0,
729            ),
730            'lt' => array(
731                'tsp'   => 2.02840000000000E+02,
732                'tbs'   => 6.76133333333333E+01,
733                'oz'    => 3.38066666666667E+01,
734                'cup'   => 4.22583333333333E+00,
735                'pt'    => 2.11291666666667E+00,
736                'us_pt' => 2.11291666666667E+00,
737                'uk_pt' => 1.75975569552166E+00,
738                'qt'    => 1.05645833333333E+00,
739                'gal'   => 2.64114583333333E-01,
740                'l'     => 1.0,
741                'lt'    => 1.0,
742            ),
743        ),
744    );
745
746
747    /**
748     * parseComplex
749     *
750     * Parses a complex number into its real and imaginary parts, and an I or J suffix
751     *
752     * @param    string        $complexNumber    The complex number
753     * @return    string[]    Indexed on "real", "imaginary" and "suffix"
754     */
755    public static function parseComplex($complexNumber)
756    {
757        $workString = (string) $complexNumber;
758
759        $realNumber = $imaginary = 0;
760        //    Extract the suffix, if there is one
761        $suffix = substr($workString, -1);
762        if (!is_numeric($suffix)) {
763            $workString = substr($workString, 0, -1);
764        } else {
765            $suffix = '';
766        }
767
768        //    Split the input into its Real and Imaginary components
769        $leadingSign = 0;
770        if (strlen($workString) > 0) {
771            $leadingSign = (($workString{0} == '+') || ($workString{0} == '-')) ? 1 : 0;
772        }
773        $power = '';
774        $realNumber = strtok($workString, '+-');
775        if (strtoupper(substr($realNumber, -1)) == 'E') {
776            $power = strtok('+-');
777            ++$leadingSign;
778        }
779
780        $realNumber = substr($workString, 0, strlen($realNumber)+strlen($power)+$leadingSign);
781
782        if ($suffix != '') {
783            $imaginary = substr($workString, strlen($realNumber));
784
785            if (($imaginary == '') && (($realNumber == '') || ($realNumber == '+') || ($realNumber == '-'))) {
786                $imaginary = $realNumber.'1';
787                $realNumber = '0';
788            } elseif ($imaginary == '') {
789                $imaginary = $realNumber;
790                $realNumber = '0';
791            } elseif (($imaginary == '+') || ($imaginary == '-')) {
792                $imaginary .= '1';
793            }
794        }
795
796        return array(
797            'real'   => $realNumber,
798            'imaginary' => $imaginary,
799            'suffix' => $suffix
800        );
801    }
802
803
804    /**
805     * Cleans the leading characters in a complex number string
806     *
807     * @param    string        $complexNumber    The complex number to clean
808     * @return    string        The "cleaned" complex number
809     */
810    private static function cleanComplex($complexNumber)
811    {
812        if ($complexNumber{0} == '+') {
813            $complexNumber = substr($complexNumber, 1);
814        }
815        if ($complexNumber{0} == '0') {
816            $complexNumber = substr($complexNumber, 1);
817        }
818        if ($complexNumber{0} == '.') {
819            $complexNumber = '0'.$complexNumber;
820        }
821        if ($complexNumber{0} == '+') {
822            $complexNumber = substr($complexNumber, 1);
823        }
824        return $complexNumber;
825    }
826
827    /**
828     * Formats a number base string value with leading zeroes
829     *
830     * @param    string        $xVal        The "number" to pad
831     * @param    integer        $places        The length that we want to pad this value
832     * @return    string        The padded "number"
833     */
834    private static function nbrConversionFormat($xVal, $places)
835    {
836        if (!is_null($places)) {
837            if (strlen($xVal) <= $places) {
838                return substr(str_pad($xVal, $places, '0', STR_PAD_LEFT), -10);
839            } else {
840                return PHPExcel_Calculation_Functions::NaN();
841            }
842        }
843
844        return substr($xVal, -10);
845    }
846
847    /**
848     *    BESSELI
849     *
850     *    Returns the modified Bessel function In(x), which is equivalent to the Bessel function evaluated
851     *        for purely imaginary arguments
852     *
853     *    Excel Function:
854     *        BESSELI(x,ord)
855     *
856     *    @access    public
857     *    @category Engineering Functions
858     *    @param    float        $x        The value at which to evaluate the function.
859     *                                If x is nonnumeric, BESSELI returns the #VALUE! error value.
860     *    @param    integer        $ord    The order of the Bessel function.
861     *                                If ord is not an integer, it is truncated.
862     *                                If $ord is nonnumeric, BESSELI returns the #VALUE! error value.
863     *                                If $ord < 0, BESSELI returns the #NUM! error value.
864     *    @return    float
865     *
866     */
867    public static function BESSELI($x, $ord)
868    {
869        $x    = (is_null($x))    ? 0.0 :    PHPExcel_Calculation_Functions::flattenSingleValue($x);
870        $ord    = (is_null($ord))    ? 0.0 :    PHPExcel_Calculation_Functions::flattenSingleValue($ord);
871
872        if ((is_numeric($x)) && (is_numeric($ord))) {
873            $ord    = floor($ord);
874            if ($ord < 0) {
875                return PHPExcel_Calculation_Functions::NaN();
876            }
877
878            if (abs($x) <= 30) {
879                $fResult = $fTerm = pow($x / 2, $ord) / PHPExcel_Calculation_MathTrig::FACT($ord);
880                $ordK = 1;
881                $fSqrX = ($x * $x) / 4;
882                do {
883                    $fTerm *= $fSqrX;
884                    $fTerm /= ($ordK * ($ordK + $ord));
885                    $fResult += $fTerm;
886                } while ((abs($fTerm) > 1e-12) && (++$ordK < 100));
887            } else {
888                $f_2_PI = 2 * M_PI;
889
890                $fXAbs = abs($x);
891                $fResult = exp($fXAbs) / sqrt($f_2_PI * $fXAbs);
892                if (($ord & 1) && ($x < 0)) {
893                    $fResult = -$fResult;
894                }
895            }
896            return (is_nan($fResult)) ? PHPExcel_Calculation_Functions::NaN() : $fResult;
897        }
898        return PHPExcel_Calculation_Functions::VALUE();
899    }
900
901
902    /**
903     *    BESSELJ
904     *
905     *    Returns the Bessel function
906     *
907     *    Excel Function:
908     *        BESSELJ(x,ord)
909     *
910     *    @access    public
911     *    @category Engineering Functions
912     *    @param    float        $x        The value at which to evaluate the function.
913     *                                If x is nonnumeric, BESSELJ returns the #VALUE! error value.
914     *    @param    integer        $ord    The order of the Bessel function. If n is not an integer, it is truncated.
915     *                                If $ord is nonnumeric, BESSELJ returns the #VALUE! error value.
916     *                                If $ord < 0, BESSELJ returns the #NUM! error value.
917     *    @return    float
918     *
919     */
920    public static function BESSELJ($x, $ord)
921    {
922        $x    = (is_null($x))    ? 0.0 :    PHPExcel_Calculation_Functions::flattenSingleValue($x);
923        $ord    = (is_null($ord))    ? 0.0 :    PHPExcel_Calculation_Functions::flattenSingleValue($ord);
924
925        if ((is_numeric($x)) && (is_numeric($ord))) {
926            $ord    = floor($ord);
927            if ($ord < 0) {
928                return PHPExcel_Calculation_Functions::NaN();
929            }
930
931            $fResult = 0;
932            if (abs($x) <= 30) {
933                $fResult = $fTerm = pow($x / 2, $ord) / PHPExcel_Calculation_MathTrig::FACT($ord);
934                $ordK = 1;
935                $fSqrX = ($x * $x) / -4;
936                do {
937                    $fTerm *= $fSqrX;
938                    $fTerm /= ($ordK * ($ordK + $ord));
939                    $fResult += $fTerm;
940                } while ((abs($fTerm) > 1e-12) && (++$ordK < 100));
941            } else {
942                $f_PI_DIV_2 = M_PI / 2;
943                $f_PI_DIV_4 = M_PI / 4;
944
945                $fXAbs = abs($x);
946                $fResult = sqrt(M_2DIVPI / $fXAbs) * cos($fXAbs - $ord * $f_PI_DIV_2 - $f_PI_DIV_4);
947                if (($ord & 1) && ($x < 0)) {
948                    $fResult = -$fResult;
949                }
950            }
951            return (is_nan($fResult)) ? PHPExcel_Calculation_Functions::NaN() : $fResult;
952        }
953        return PHPExcel_Calculation_Functions::VALUE();
954    }
955
956
957    private static function besselK0($fNum)
958    {
959        if ($fNum <= 2) {
960            $fNum2 = $fNum * 0.5;
961            $y = ($fNum2 * $fNum2);
962            $fRet = -log($fNum2) * self::BESSELI($fNum, 0) +
963                (-0.57721566 + $y * (0.42278420 + $y * (0.23069756 + $y * (0.3488590e-1 + $y * (0.262698e-2 + $y *
964                (0.10750e-3 + $y * 0.74e-5))))));
965        } else {
966            $y = 2 / $fNum;
967            $fRet = exp(-$fNum) / sqrt($fNum) *
968                (1.25331414 + $y * (-0.7832358e-1 + $y * (0.2189568e-1 + $y * (-0.1062446e-1 + $y *
969                (0.587872e-2 + $y * (-0.251540e-2 + $y * 0.53208e-3))))));
970        }
971        return $fRet;
972    }
973
974
975    private static function besselK1($fNum)
976    {
977        if ($fNum <= 2) {
978            $fNum2 = $fNum * 0.5;
979            $y = ($fNum2 * $fNum2);
980            $fRet = log($fNum2) * self::BESSELI($fNum, 1) +
981                (1 + $y * (0.15443144 + $y * (-0.67278579 + $y * (-0.18156897 + $y * (-0.1919402e-1 + $y *
982                (-0.110404e-2 + $y * (-0.4686e-4))))))) / $fNum;
983        } else {
984            $y = 2 / $fNum;
985            $fRet = exp(-$fNum) / sqrt($fNum) *
986                (1.25331414 + $y * (0.23498619 + $y * (-0.3655620e-1 + $y * (0.1504268e-1 + $y * (-0.780353e-2 + $y *
987                (0.325614e-2 + $y * (-0.68245e-3)))))));
988        }
989        return $fRet;
990    }
991
992
993    /**
994     *    BESSELK
995     *
996     *    Returns the modified Bessel function Kn(x), which is equivalent to the Bessel functions evaluated
997     *        for purely imaginary arguments.
998     *
999     *    Excel Function:
1000     *        BESSELK(x,ord)
1001     *
1002     *    @access    public
1003     *    @category Engineering Functions
1004     *    @param    float        $x        The value at which to evaluate the function.
1005     *                                If x is nonnumeric, BESSELK returns the #VALUE! error value.
1006     *    @param    integer        $ord    The order of the Bessel function. If n is not an integer, it is truncated.
1007     *                                If $ord is nonnumeric, BESSELK returns the #VALUE! error value.
1008     *                                If $ord < 0, BESSELK returns the #NUM! error value.
1009     *    @return    float
1010     *
1011     */
1012    public static function BESSELK($x, $ord)
1013    {
1014        $x        = (is_null($x))        ? 0.0 :    PHPExcel_Calculation_Functions::flattenSingleValue($x);
1015        $ord    = (is_null($ord))    ? 0.0 :    PHPExcel_Calculation_Functions::flattenSingleValue($ord);
1016
1017        if ((is_numeric($x)) && (is_numeric($ord))) {
1018            if (($ord < 0) || ($x == 0.0)) {
1019                return PHPExcel_Calculation_Functions::NaN();
1020            }
1021
1022            switch (floor($ord)) {
1023                case 0:
1024                    return self::besselK0($x);
1025                case 1:
1026                    return self::besselK1($x);
1027                default:
1028                    $fTox    = 2 / $x;
1029                    $fBkm    = self::besselK0($x);
1030                    $fBk    = self::besselK1($x);
1031                    for ($n = 1; $n < $ord; ++$n) {
1032                        $fBkp    = $fBkm + $n * $fTox * $fBk;
1033                        $fBkm    = $fBk;
1034                        $fBk    = $fBkp;
1035                    }
1036            }
1037            return (is_nan($fBk)) ? PHPExcel_Calculation_Functions::NaN() : $fBk;
1038        }
1039        return PHPExcel_Calculation_Functions::VALUE();
1040    }
1041
1042
1043    private static function besselY0($fNum)
1044    {
1045        if ($fNum < 8.0) {
1046            $y = ($fNum * $fNum);
1047            $f1 = -2957821389.0 + $y * (7062834065.0 + $y * (-512359803.6 + $y * (10879881.29 + $y * (-86327.92757 + $y * 228.4622733))));
1048            $f2 = 40076544269.0 + $y * (745249964.8 + $y * (7189466.438 + $y * (47447.26470 + $y * (226.1030244 + $y))));
1049            $fRet = $f1 / $f2 + 0.636619772 * self::BESSELJ($fNum, 0) * log($fNum);
1050        } else {
1051            $z = 8.0 / $fNum;
1052            $y = ($z * $z);
1053            $xx = $fNum - 0.785398164;
1054            $f1 = 1 + $y * (-0.1098628627e-2 + $y * (0.2734510407e-4 + $y * (-0.2073370639e-5 + $y * 0.2093887211e-6)));
1055            $f2 = -0.1562499995e-1 + $y * (0.1430488765e-3 + $y * (-0.6911147651e-5 + $y * (0.7621095161e-6 + $y * (-0.934945152e-7))));
1056            $fRet = sqrt(0.636619772 / $fNum) * (sin($xx) * $f1 + $z * cos($xx) * $f2);
1057        }
1058        return $fRet;
1059    }
1060
1061
1062    private static function besselY1($fNum)
1063    {
1064        if ($fNum < 8.0) {
1065            $y = ($fNum * $fNum);
1066            $f1 = $fNum * (-0.4900604943e13 + $y * (0.1275274390e13 + $y * (-0.5153438139e11 + $y * (0.7349264551e9 + $y *
1067                (-0.4237922726e7 + $y * 0.8511937935e4)))));
1068            $f2 = 0.2499580570e14 + $y * (0.4244419664e12 + $y * (0.3733650367e10 + $y * (0.2245904002e8 + $y *
1069                (0.1020426050e6 + $y * (0.3549632885e3 + $y)))));
1070            $fRet = $f1 / $f2 + 0.636619772 * ( self::BESSELJ($fNum, 1) * log($fNum) - 1 / $fNum);
1071        } else {
1072            $fRet = sqrt(0.636619772 / $fNum) * sin($fNum - 2.356194491);
1073        }
1074        return $fRet;
1075    }
1076
1077
1078    /**
1079     *    BESSELY
1080     *
1081     *    Returns the Bessel function, which is also called the Weber function or the Neumann function.
1082     *
1083     *    Excel Function:
1084     *        BESSELY(x,ord)
1085     *
1086     *    @access    public
1087     *    @category Engineering Functions
1088     *    @param    float        $x        The value at which to evaluate the function.
1089     *                                If x is nonnumeric, BESSELK returns the #VALUE! error value.
1090     *    @param    integer        $ord    The order of the Bessel function. If n is not an integer, it is truncated.
1091     *                                If $ord is nonnumeric, BESSELK returns the #VALUE! error value.
1092     *                                If $ord < 0, BESSELK returns the #NUM! error value.
1093     *
1094     *    @return    float
1095     */
1096    public static function BESSELY($x, $ord)
1097    {
1098        $x        = (is_null($x))        ? 0.0 :    PHPExcel_Calculation_Functions::flattenSingleValue($x);
1099        $ord    = (is_null($ord))    ? 0.0 :    PHPExcel_Calculation_Functions::flattenSingleValue($ord);
1100
1101        if ((is_numeric($x)) && (is_numeric($ord))) {
1102            if (($ord < 0) || ($x == 0.0)) {
1103                return PHPExcel_Calculation_Functions::NaN();
1104            }
1105
1106            switch (floor($ord)) {
1107                case 0:
1108                    return self::besselY0($x);
1109                case 1:
1110                    return self::besselY1($x);
1111                default:
1112                    $fTox    = 2 / $x;
1113                    $fBym    = self::besselY0($x);
1114                    $fBy    = self::besselY1($x);
1115                    for ($n = 1; $n < $ord; ++$n) {
1116                        $fByp    = $n * $fTox * $fBy - $fBym;
1117                        $fBym    = $fBy;
1118                        $fBy    = $fByp;
1119                    }
1120            }
1121            return (is_nan($fBy)) ? PHPExcel_Calculation_Functions::NaN() : $fBy;
1122        }
1123        return PHPExcel_Calculation_Functions::VALUE();
1124    }
1125
1126
1127    /**
1128     * BINTODEC
1129     *
1130     * Return a binary value as decimal.
1131     *
1132     * Excel Function:
1133     *        BIN2DEC(x)
1134     *
1135     * @access    public
1136     * @category Engineering Functions
1137     * @param    string        $x        The binary number (as a string) that you want to convert. The number
1138     *                                cannot contain more than 10 characters (10 bits). The most significant
1139     *                                bit of number is the sign bit. The remaining 9 bits are magnitude bits.
1140     *                                Negative numbers are represented using two's-complement notation.
1141     *                                If number is not a valid binary number, or if number contains more than
1142     *                                10 characters (10 bits), BIN2DEC returns the #NUM! error value.
1143     * @return    string
1144     */
1145    public static function BINTODEC($x)
1146    {
1147        $x    = PHPExcel_Calculation_Functions::flattenSingleValue($x);
1148
1149        if (is_bool($x)) {
1150            if (PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE) {
1151                $x = (int) $x;
1152            } else {
1153                return PHPExcel_Calculation_Functions::VALUE();
1154            }
1155        }
1156        if (PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_GNUMERIC) {
1157            $x = floor($x);
1158        }
1159        $x = (string) $x;
1160        if (strlen($x) > preg_match_all('/[01]/', $x, $out)) {
1161            return PHPExcel_Calculation_Functions::NaN();
1162        }
1163        if (strlen($x) > 10) {
1164            return PHPExcel_Calculation_Functions::NaN();
1165        } elseif (strlen($x) == 10) {
1166            //    Two's Complement
1167            $x = substr($x, -9);
1168            return '-'.(512-bindec($x));
1169        }
1170        return bindec($x);
1171    }
1172
1173
1174    /**
1175     * BINTOHEX
1176     *
1177     * Return a binary value as hex.
1178     *
1179     * Excel Function:
1180     *        BIN2HEX(x[,places])
1181     *
1182     * @access    public
1183     * @category Engineering Functions
1184     * @param    string        $x        The binary number (as a string) that you want to convert. The number
1185     *                                cannot contain more than 10 characters (10 bits). The most significant
1186     *                                bit of number is the sign bit. The remaining 9 bits are magnitude bits.
1187     *                                Negative numbers are represented using two's-complement notation.
1188     *                                If number is not a valid binary number, or if number contains more than
1189     *                                10 characters (10 bits), BIN2HEX returns the #NUM! error value.
1190     * @param    integer        $places    The number of characters to use. If places is omitted, BIN2HEX uses the
1191     *                                minimum number of characters necessary. Places is useful for padding the
1192     *                                return value with leading 0s (zeros).
1193     *                                If places is not an integer, it is truncated.
1194     *                                If places is nonnumeric, BIN2HEX returns the #VALUE! error value.
1195     *                                If places is negative, BIN2HEX returns the #NUM! error value.
1196     * @return    string
1197     */
1198    public static function BINTOHEX($x, $places = null)
1199    {
1200        $x    = PHPExcel_Calculation_Functions::flattenSingleValue($x);
1201        $places    = PHPExcel_Calculation_Functions::flattenSingleValue($places);
1202
1203        if (is_bool($x)) {
1204            if (PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE) {
1205                $x = (int) $x;
1206            } else {
1207                return PHPExcel_Calculation_Functions::VALUE();
1208            }
1209        }
1210        if (PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_GNUMERIC) {
1211            $x = floor($x);
1212        }
1213        $x = (string) $x;
1214        if (strlen($x) > preg_match_all('/[01]/', $x, $out)) {
1215            return PHPExcel_Calculation_Functions::NaN();
1216        }
1217        if (strlen($x) > 10) {
1218            return PHPExcel_Calculation_Functions::NaN();
1219        } elseif (strlen($x) == 10) {
1220            //    Two's Complement
1221            return str_repeat('F', 8).substr(strtoupper(dechex(bindec(substr($x, -9)))), -2);
1222        }
1223        $hexVal = (string) strtoupper(dechex(bindec($x)));
1224
1225        return self::nbrConversionFormat($hexVal, $places);
1226    }
1227
1228
1229    /**
1230     * BINTOOCT
1231     *
1232     * Return a binary value as octal.
1233     *
1234     * Excel Function:
1235     *        BIN2OCT(x[,places])
1236     *
1237     * @access    public
1238     * @category Engineering Functions
1239     * @param    string        $x        The binary number (as a string) that you want to convert. The number
1240     *                                cannot contain more than 10 characters (10 bits). The most significant
1241     *                                bit of number is the sign bit. The remaining 9 bits are magnitude bits.
1242     *                                Negative numbers are represented using two's-complement notation.
1243     *                                If number is not a valid binary number, or if number contains more than
1244     *                                10 characters (10 bits), BIN2OCT returns the #NUM! error value.
1245     * @param    integer        $places    The number of characters to use. If places is omitted, BIN2OCT uses the
1246     *                                minimum number of characters necessary. Places is useful for padding the
1247     *                                return value with leading 0s (zeros).
1248     *                                If places is not an integer, it is truncated.
1249     *                                If places is nonnumeric, BIN2OCT returns the #VALUE! error value.
1250     *                                If places is negative, BIN2OCT returns the #NUM! error value.
1251     * @return    string
1252     */
1253    public static function BINTOOCT($x, $places = null)
1254    {
1255        $x    = PHPExcel_Calculation_Functions::flattenSingleValue($x);
1256        $places    = PHPExcel_Calculation_Functions::flattenSingleValue($places);
1257
1258        if (is_bool($x)) {
1259            if (PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE) {
1260                $x = (int) $x;
1261            } else {
1262                return PHPExcel_Calculation_Functions::VALUE();
1263            }
1264        }
1265        if (PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_GNUMERIC) {
1266            $x = floor($x);
1267        }
1268        $x = (string) $x;
1269        if (strlen($x) > preg_match_all('/[01]/', $x, $out)) {
1270            return PHPExcel_Calculation_Functions::NaN();
1271        }
1272        if (strlen($x) > 10) {
1273            return PHPExcel_Calculation_Functions::NaN();
1274        } elseif (strlen($x) == 10) {
1275            //    Two's Complement
1276            return str_repeat('7', 7).substr(strtoupper(decoct(bindec(substr($x, -9)))), -3);
1277        }
1278        $octVal = (string) decoct(bindec($x));
1279
1280        return self::nbrConversionFormat($octVal, $places);
1281    }
1282
1283
1284    /**
1285     * DECTOBIN
1286     *
1287     * Return a decimal value as binary.
1288     *
1289     * Excel Function:
1290     *        DEC2BIN(x[,places])
1291     *
1292     * @access    public
1293     * @category Engineering Functions
1294     * @param    string        $x        The decimal integer you want to convert. If number is negative,
1295     *                                valid place values are ignored and DEC2BIN returns a 10-character
1296     *                                (10-bit) binary number in which the most significant bit is the sign
1297     *                                bit. The remaining 9 bits are magnitude bits. Negative numbers are
1298     *                                represented using two's-complement notation.
1299     *                                If number < -512 or if number > 511, DEC2BIN returns the #NUM! error
1300     *                                value.
1301     *                                If number is nonnumeric, DEC2BIN returns the #VALUE! error value.
1302     *                                If DEC2BIN requires more than places characters, it returns the #NUM!
1303     *                                error value.
1304     * @param    integer        $places    The number of characters to use. If places is omitted, DEC2BIN uses
1305     *                                the minimum number of characters necessary. Places is useful for
1306     *                                padding the return value with leading 0s (zeros).
1307     *                                If places is not an integer, it is truncated.
1308     *                                If places is nonnumeric, DEC2BIN returns the #VALUE! error value.
1309     *                                If places is zero or negative, DEC2BIN returns the #NUM! error value.
1310     * @return    string
1311     */
1312    public static function DECTOBIN($x, $places = null)
1313    {
1314        $x    = PHPExcel_Calculation_Functions::flattenSingleValue($x);
1315        $places    = PHPExcel_Calculation_Functions::flattenSingleValue($places);
1316
1317        if (is_bool($x)) {
1318            if (PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE) {
1319                $x = (int) $x;
1320            } else {
1321                return PHPExcel_Calculation_Functions::VALUE();
1322            }
1323        }
1324        $x = (string) $x;
1325        if (strlen($x) > preg_match_all('/[-0123456789.]/', $x, $out)) {
1326            return PHPExcel_Calculation_Functions::VALUE();
1327        }
1328        $x = (string) floor($x);
1329        $r = decbin($x);
1330        if (strlen($r) == 32) {
1331            //    Two's Complement
1332            $r = substr($r, -10);
1333        } elseif (strlen($r) > 11) {
1334            return PHPExcel_Calculation_Functions::NaN();
1335        }
1336
1337        return self::nbrConversionFormat($r, $places);
1338    }
1339
1340
1341    /**
1342     * DECTOHEX
1343     *
1344     * Return a decimal value as hex.
1345     *
1346     * Excel Function:
1347     *        DEC2HEX(x[,places])
1348     *
1349     * @access    public
1350     * @category Engineering Functions
1351     * @param    string        $x        The decimal integer you want to convert. If number is negative,
1352     *                                places is ignored and DEC2HEX returns a 10-character (40-bit)
1353     *                                hexadecimal number in which the most significant bit is the sign
1354     *                                bit. The remaining 39 bits are magnitude bits. Negative numbers
1355     *                                are represented using two's-complement notation.
1356     *                                If number < -549,755,813,888 or if number > 549,755,813,887,
1357     *                                DEC2HEX returns the #NUM! error value.
1358     *                                If number is nonnumeric, DEC2HEX returns the #VALUE! error value.
1359     *                                If DEC2HEX requires more than places characters, it returns the
1360     *                                #NUM! error value.
1361     * @param    integer        $places    The number of characters to use. If places is omitted, DEC2HEX uses
1362     *                                the minimum number of characters necessary. Places is useful for
1363     *                                padding the return value with leading 0s (zeros).
1364     *                                If places is not an integer, it is truncated.
1365     *                                If places is nonnumeric, DEC2HEX returns the #VALUE! error value.
1366     *                                If places is zero or negative, DEC2HEX returns the #NUM! error value.
1367     * @return    string
1368     */
1369    public static function DECTOHEX($x, $places = null)
1370    {
1371        $x    = PHPExcel_Calculation_Functions::flattenSingleValue($x);
1372        $places    = PHPExcel_Calculation_Functions::flattenSingleValue($places);
1373
1374        if (is_bool($x)) {
1375            if (PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE) {
1376                $x = (int) $x;
1377            } else {
1378                return PHPExcel_Calculation_Functions::VALUE();
1379            }
1380        }
1381        $x = (string) $x;
1382        if (strlen($x) > preg_match_all('/[-0123456789.]/', $x, $out)) {
1383            return PHPExcel_Calculation_Functions::VALUE();
1384        }
1385        $x = (string) floor($x);
1386        $r = strtoupper(dechex($x));
1387        if (strlen($r) == 8) {
1388            //    Two's Complement
1389            $r = 'FF'.$r;
1390        }
1391
1392        return self::nbrConversionFormat($r, $places);
1393    }
1394
1395
1396    /**
1397     * DECTOOCT
1398     *
1399     * Return an decimal value as octal.
1400     *
1401     * Excel Function:
1402     *        DEC2OCT(x[,places])
1403     *
1404     * @access    public
1405     * @category Engineering Functions
1406     * @param    string        $x        The decimal integer you want to convert. If number is negative,
1407     *                                places is ignored and DEC2OCT returns a 10-character (30-bit)
1408     *                                octal number in which the most significant bit is the sign bit.
1409     *                                The remaining 29 bits are magnitude bits. Negative numbers are
1410     *                                represented using two's-complement notation.
1411     *                                If number < -536,870,912 or if number > 536,870,911, DEC2OCT
1412     *                                returns the #NUM! error value.
1413     *                                If number is nonnumeric, DEC2OCT returns the #VALUE! error value.
1414     *                                If DEC2OCT requires more than places characters, it returns the
1415     *                                #NUM! error value.
1416     * @param    integer        $places    The number of characters to use. If places is omitted, DEC2OCT uses
1417     *                                the minimum number of characters necessary. Places is useful for
1418     *                                padding the return value with leading 0s (zeros).
1419     *                                If places is not an integer, it is truncated.
1420     *                                If places is nonnumeric, DEC2OCT returns the #VALUE! error value.
1421     *                                If places is zero or negative, DEC2OCT returns the #NUM! error value.
1422     * @return    string
1423     */
1424    public static function DECTOOCT($x, $places = null)
1425    {
1426        $x    = PHPExcel_Calculation_Functions::flattenSingleValue($x);
1427        $places    = PHPExcel_Calculation_Functions::flattenSingleValue($places);
1428
1429        if (is_bool($x)) {
1430            if (PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE) {
1431                $x = (int) $x;
1432            } else {
1433                return PHPExcel_Calculation_Functions::VALUE();
1434            }
1435        }
1436        $x = (string) $x;
1437        if (strlen($x) > preg_match_all('/[-0123456789.]/', $x, $out)) {
1438            return PHPExcel_Calculation_Functions::VALUE();
1439        }
1440        $x = (string) floor($x);
1441        $r = decoct($x);
1442        if (strlen($r) == 11) {
1443            //    Two's Complement
1444            $r = substr($r, -10);
1445        }
1446
1447        return self::nbrConversionFormat($r, $places);
1448    }
1449
1450
1451    /**
1452     * HEXTOBIN
1453     *
1454     * Return a hex value as binary.
1455     *
1456     * Excel Function:
1457     *        HEX2BIN(x[,places])
1458     *
1459     * @access    public
1460     * @category Engineering Functions
1461     * @param    string        $x            the hexadecimal number you want to convert. Number cannot
1462     *                                    contain more than 10 characters. The most significant bit of
1463     *                                    number is the sign bit (40th bit from the right). The remaining
1464     *                                    9 bits are magnitude bits. Negative numbers are represented
1465     *                                    using two's-complement notation.
1466     *                                    If number is negative, HEX2BIN ignores places and returns a
1467     *                                    10-character binary number.
1468     *                                    If number is negative, it cannot be less than FFFFFFFE00, and
1469     *                                    if number is positive, it cannot be greater than 1FF.
1470     *                                    If number is not a valid hexadecimal number, HEX2BIN returns
1471     *                                    the #NUM! error value.
1472     *                                    If HEX2BIN requires more than places characters, it returns
1473     *                                    the #NUM! error value.
1474     * @param    integer        $places        The number of characters to use. If places is omitted,
1475     *                                    HEX2BIN uses the minimum number of characters necessary. Places
1476     *                                    is useful for padding the return value with leading 0s (zeros).
1477     *                                    If places is not an integer, it is truncated.
1478     *                                    If places is nonnumeric, HEX2BIN returns the #VALUE! error value.
1479     *                                    If places is negative, HEX2BIN returns the #NUM! error value.
1480     * @return    string
1481     */
1482    public static function HEXTOBIN($x, $places = null)
1483    {
1484        $x    = PHPExcel_Calculation_Functions::flattenSingleValue($x);
1485        $places    = PHPExcel_Calculation_Functions::flattenSingleValue($places);
1486
1487        if (is_bool($x)) {
1488            return PHPExcel_Calculation_Functions::VALUE();
1489        }
1490        $x = (string) $x;
1491        if (strlen($x) > preg_match_all('/[0123456789ABCDEF]/', strtoupper($x), $out)) {
1492            return PHPExcel_Calculation_Functions::NaN();
1493        }
1494        $binVal = decbin(hexdec($x));
1495
1496        return substr(self::nbrConversionFormat($binVal, $places), -10);
1497    }
1498
1499
1500    /**
1501     * HEXTODEC
1502     *
1503     * Return a hex value as decimal.
1504     *
1505     * Excel Function:
1506     *        HEX2DEC(x)
1507     *
1508     * @access    public
1509     * @category Engineering Functions
1510     * @param    string        $x        The hexadecimal number you want to convert. This number cannot
1511     *                                contain more than 10 characters (40 bits). The most significant
1512     *                                bit of number is the sign bit. The remaining 39 bits are magnitude
1513     *                                bits. Negative numbers are represented using two's-complement
1514     *                                notation.
1515     *                                If number is not a valid hexadecimal number, HEX2DEC returns the
1516     *                                #NUM! error value.
1517     * @return    string
1518     */
1519    public static function HEXTODEC($x)
1520    {
1521        $x    = PHPExcel_Calculation_Functions::flattenSingleValue($x);
1522
1523        if (is_bool($x)) {
1524            return PHPExcel_Calculation_Functions::VALUE();
1525        }
1526        $x = (string) $x;
1527        if (strlen($x) > preg_match_all('/[0123456789ABCDEF]/', strtoupper($x), $out)) {
1528            return PHPExcel_Calculation_Functions::NaN();
1529        }
1530        return hexdec($x);
1531    }
1532
1533
1534    /**
1535     * HEXTOOCT
1536     *
1537     * Return a hex value as octal.
1538     *
1539     * Excel Function:
1540     *        HEX2OCT(x[,places])
1541     *
1542     * @access    public
1543     * @category Engineering Functions
1544     * @param    string        $x            The hexadecimal number you want to convert. Number cannot
1545     *                                    contain more than 10 characters. The most significant bit of
1546     *                                    number is the sign bit. The remaining 39 bits are magnitude
1547     *                                    bits. Negative numbers are represented using two's-complement
1548     *                                    notation.
1549     *                                    If number is negative, HEX2OCT ignores places and returns a
1550     *                                    10-character octal number.
1551     *                                    If number is negative, it cannot be less than FFE0000000, and
1552     *                                    if number is positive, it cannot be greater than 1FFFFFFF.
1553     *                                    If number is not a valid hexadecimal number, HEX2OCT returns
1554     *                                    the #NUM! error value.
1555     *                                    If HEX2OCT requires more than places characters, it returns
1556     *                                    the #NUM! error value.
1557     * @param    integer        $places        The number of characters to use. If places is omitted, HEX2OCT
1558     *                                    uses the minimum number of characters necessary. Places is
1559     *                                    useful for padding the return value with leading 0s (zeros).
1560     *                                    If places is not an integer, it is truncated.
1561     *                                    If places is nonnumeric, HEX2OCT returns the #VALUE! error
1562     *                                    value.
1563     *                                    If places is negative, HEX2OCT returns the #NUM! error value.
1564     * @return    string
1565     */
1566    public static function HEXTOOCT($x, $places = null)
1567    {
1568        $x    = PHPExcel_Calculation_Functions::flattenSingleValue($x);
1569        $places    = PHPExcel_Calculation_Functions::flattenSingleValue($places);
1570
1571        if (is_bool($x)) {
1572            return PHPExcel_Calculation_Functions::VALUE();
1573        }
1574        $x = (string) $x;
1575        if (strlen($x) > preg_match_all('/[0123456789ABCDEF]/', strtoupper($x), $out)) {
1576            return PHPExcel_Calculation_Functions::NaN();
1577        }
1578        $octVal = decoct(hexdec($x));
1579
1580        return self::nbrConversionFormat($octVal, $places);
1581    }    //    function HEXTOOCT()
1582
1583
1584    /**
1585     * OCTTOBIN
1586     *
1587     * Return an octal value as binary.
1588     *
1589     * Excel Function:
1590     *        OCT2BIN(x[,places])
1591     *
1592     * @access    public
1593     * @category Engineering Functions
1594     * @param    string        $x            The octal number you want to convert. Number may not
1595     *                                    contain more than 10 characters. The most significant
1596     *                                    bit of number is the sign bit. The remaining 29 bits
1597     *                                    are magnitude bits. Negative numbers are represented
1598     *                                    using two's-complement notation.
1599     *                                    If number is negative, OCT2BIN ignores places and returns
1600     *                                    a 10-character binary number.
1601     *                                    If number is negative, it cannot be less than 7777777000,
1602     *                                    and if number is positive, it cannot be greater than 777.
1603     *                                    If number is not a valid octal number, OCT2BIN returns
1604     *                                    the #NUM! error value.
1605     *                                    If OCT2BIN requires more than places characters, it
1606     *                                    returns the #NUM! error value.
1607     * @param    integer        $places        The number of characters to use. If places is omitted,
1608     *                                    OCT2BIN uses the minimum number of characters necessary.
1609     *                                    Places is useful for padding the return value with
1610     *                                    leading 0s (zeros).
1611     *                                    If places is not an integer, it is truncated.
1612     *                                    If places is nonnumeric, OCT2BIN returns the #VALUE!
1613     *                                    error value.
1614     *                                    If places is negative, OCT2BIN returns the #NUM! error
1615     *                                    value.
1616     * @return    string
1617     */
1618    public static function OCTTOBIN($x, $places = null)
1619    {
1620        $x    = PHPExcel_Calculation_Functions::flattenSingleValue($x);
1621        $places    = PHPExcel_Calculation_Functions::flattenSingleValue($places);
1622
1623        if (is_bool($x)) {
1624            return PHPExcel_Calculation_Functions::VALUE();
1625        }
1626        $x = (string) $x;
1627        if (preg_match_all('/[01234567]/', $x, $out) != strlen($x)) {
1628            return PHPExcel_Calculation_Functions::NaN();
1629        }
1630        $r = decbin(octdec($x));
1631
1632        return self::nbrConversionFormat($r, $places);
1633    }
1634
1635
1636    /**
1637     * OCTTODEC
1638     *
1639     * Return an octal value as decimal.
1640     *
1641     * Excel Function:
1642     *        OCT2DEC(x)
1643     *
1644     * @access    public
1645     * @category Engineering Functions
1646     * @param    string        $x        The octal number you want to convert. Number may not contain
1647     *                                more than 10 octal characters (30 bits). The most significant
1648     *                                bit of number is the sign bit. The remaining 29 bits are
1649     *                                magnitude bits. Negative numbers are represented using
1650     *                                two's-complement notation.
1651     *                                If number is not a valid octal number, OCT2DEC returns the
1652     *                                #NUM! error value.
1653     * @return    string
1654     */
1655    public static function OCTTODEC($x)
1656    {
1657        $x    = PHPExcel_Calculation_Functions::flattenSingleValue($x);
1658
1659        if (is_bool($x)) {
1660            return PHPExcel_Calculation_Functions::VALUE();
1661        }
1662        $x = (string) $x;
1663        if (preg_match_all('/[01234567]/', $x, $out) != strlen($x)) {
1664            return PHPExcel_Calculation_Functions::NaN();
1665        }
1666        return octdec($x);
1667    }
1668
1669
1670    /**
1671     * OCTTOHEX
1672     *
1673     * Return an octal value as hex.
1674     *
1675     * Excel Function:
1676     *        OCT2HEX(x[,places])
1677     *
1678     * @access    public
1679     * @category Engineering Functions
1680     * @param    string        $x            The octal number you want to convert. Number may not contain
1681     *                                    more than 10 octal characters (30 bits). The most significant
1682     *                                    bit of number is the sign bit. The remaining 29 bits are
1683     *                                    magnitude bits. Negative numbers are represented using
1684     *                                    two's-complement notation.
1685     *                                    If number is negative, OCT2HEX ignores places and returns a
1686     *                                    10-character hexadecimal number.
1687     *                                    If number is not a valid octal number, OCT2HEX returns the
1688     *                                    #NUM! error value.
1689     *                                    If OCT2HEX requires more than places characters, it returns
1690     *                                    the #NUM! error value.
1691     * @param    integer        $places        The number of characters to use. If places is omitted, OCT2HEX
1692     *                                    uses the minimum number of characters necessary. Places is useful
1693     *                                    for padding the return value with leading 0s (zeros).
1694     *                                    If places is not an integer, it is truncated.
1695     *                                    If places is nonnumeric, OCT2HEX returns the #VALUE! error value.
1696     *                                    If places is negative, OCT2HEX returns the #NUM! error value.
1697     * @return    string
1698     */
1699    public static function OCTTOHEX($x, $places = null)
1700    {
1701        $x    = PHPExcel_Calculation_Functions::flattenSingleValue($x);
1702        $places    = PHPExcel_Calculation_Functions::flattenSingleValue($places);
1703
1704        if (is_bool($x)) {
1705            return PHPExcel_Calculation_Functions::VALUE();
1706        }
1707        $x = (string) $x;
1708        if (preg_match_all('/[01234567]/', $x, $out) != strlen($x)) {
1709            return PHPExcel_Calculation_Functions::NaN();
1710        }
1711        $hexVal = strtoupper(dechex(octdec($x)));
1712
1713        return self::nbrConversionFormat($hexVal, $places);
1714    }
1715
1716
1717    /**
1718     * COMPLEX
1719     *
1720     * Converts real and imaginary coefficients into a complex number of the form x + yi or x + yj.
1721     *
1722     * Excel Function:
1723     *        COMPLEX(realNumber,imaginary[,places])
1724     *
1725     * @access    public
1726     * @category Engineering Functions
1727     * @param    float        $realNumber        The real coefficient of the complex number.
1728     * @param    float        $imaginary        The imaginary coefficient of the complex number.
1729     * @param    string        $suffix            The suffix for the imaginary component of the complex number.
1730     *                                        If omitted, the suffix is assumed to be "i".
1731     * @return    string
1732     */
1733    public static function COMPLEX($realNumber = 0.0, $imaginary = 0.0, $suffix = 'i')
1734    {
1735        $realNumber = (is_null($realNumber)) ? 0.0 : PHPExcel_Calculation_Functions::flattenSingleValue($realNumber);
1736        $imaginary  = (is_null($imaginary))  ? 0.0 : PHPExcel_Calculation_Functions::flattenSingleValue($imaginary);
1737        $suffix     = (is_null($suffix))     ? 'i' : PHPExcel_Calculation_Functions::flattenSingleValue($suffix);
1738
1739        if (((is_numeric($realNumber)) && (is_numeric($imaginary))) &&
1740            (($suffix == 'i') || ($suffix == 'j') || ($suffix == ''))) {
1741            $realNumber    = (float) $realNumber;
1742            $imaginary    = (float) $imaginary;
1743
1744            if ($suffix == '') {
1745                $suffix = 'i';
1746            }
1747            if ($realNumber == 0.0) {
1748                if ($imaginary == 0.0) {
1749                    return (string) '0';
1750                } elseif ($imaginary == 1.0) {
1751                    return (string) $suffix;
1752                } elseif ($imaginary == -1.0) {
1753                    return (string) '-'.$suffix;
1754                }
1755                return (string) $imaginary.$suffix;
1756            } elseif ($imaginary == 0.0) {
1757                return (string) $realNumber;
1758            } elseif ($imaginary == 1.0) {
1759                return (string) $realNumber.'+'.$suffix;
1760            } elseif ($imaginary == -1.0) {
1761                return (string) $realNumber.'-'.$suffix;
1762            }
1763            if ($imaginary > 0) {
1764                $imaginary = (string) '+'.$imaginary;
1765            }
1766            return (string) $realNumber.$imaginary.$suffix;
1767        }
1768
1769        return PHPExcel_Calculation_Functions::VALUE();
1770    }
1771
1772
1773    /**
1774     * IMAGINARY
1775     *
1776     * Returns the imaginary coefficient of a complex number in x + yi or x + yj text format.
1777     *
1778     * Excel Function:
1779     *        IMAGINARY(complexNumber)
1780     *
1781     * @access    public
1782     * @category Engineering Functions
1783     * @param    string        $complexNumber    The complex number for which you want the imaginary
1784     *                                         coefficient.
1785     * @return    float
1786     */
1787    public static function IMAGINARY($complexNumber)
1788    {
1789        $complexNumber    = PHPExcel_Calculation_Functions::flattenSingleValue($complexNumber);
1790
1791        $parsedComplex = self::parseComplex($complexNumber);
1792        return $parsedComplex['imaginary'];
1793    }
1794
1795
1796    /**
1797     * IMREAL
1798     *
1799     * Returns the real coefficient of a complex number in x + yi or x + yj text format.
1800     *
1801     * Excel Function:
1802     *        IMREAL(complexNumber)
1803     *
1804     * @access    public
1805     * @category Engineering Functions
1806     * @param    string        $complexNumber    The complex number for which you want the real coefficient.
1807     * @return    float
1808     */
1809    public static function IMREAL($complexNumber)
1810    {
1811        $complexNumber    = PHPExcel_Calculation_Functions::flattenSingleValue($complexNumber);
1812
1813        $parsedComplex = self::parseComplex($complexNumber);
1814        return $parsedComplex['real'];
1815    }
1816
1817
1818    /**
1819     * IMABS
1820     *
1821     * Returns the absolute value (modulus) of a complex number in x + yi or x + yj text format.
1822     *
1823     * Excel Function:
1824     *        IMABS(complexNumber)
1825     *
1826     * @param    string        $complexNumber    The complex number for which you want the absolute value.
1827     * @return    float
1828     */
1829    public static function IMABS($complexNumber)
1830    {
1831        $complexNumber = PHPExcel_Calculation_Functions::flattenSingleValue($complexNumber);
1832
1833        $parsedComplex = self::parseComplex($complexNumber);
1834
1835        return sqrt(
1836            ($parsedComplex['real'] * $parsedComplex['real']) +
1837            ($parsedComplex['imaginary'] * $parsedComplex['imaginary'])
1838        );
1839    }
1840
1841
1842    /**
1843     * IMARGUMENT
1844     *
1845     * Returns the argument theta of a complex number, i.e. the angle in radians from the real
1846     * axis to the representation of the number in polar coordinates.
1847     *
1848     * Excel Function:
1849     *        IMARGUMENT(complexNumber)
1850     *
1851     * @param    string        $complexNumber    The complex number for which you want the argument theta.
1852     * @return    float
1853     */
1854    public static function IMARGUMENT($complexNumber)
1855    {
1856        $complexNumber    = PHPExcel_Calculation_Functions::flattenSingleValue($complexNumber);
1857
1858        $parsedComplex = self::parseComplex($complexNumber);
1859
1860        if ($parsedComplex['real'] == 0.0) {
1861            if ($parsedComplex['imaginary'] == 0.0) {
1862                return 0.0;
1863            } elseif ($parsedComplex['imaginary'] < 0.0) {
1864                return M_PI / -2;
1865            } else {
1866                return M_PI / 2;
1867            }
1868        } elseif ($parsedComplex['real'] > 0.0) {
1869            return atan($parsedComplex['imaginary'] / $parsedComplex['real']);
1870        } elseif ($parsedComplex['imaginary'] < 0.0) {
1871            return 0 - (M_PI - atan(abs($parsedComplex['imaginary']) / abs($parsedComplex['real'])));
1872        } else {
1873            return M_PI - atan($parsedComplex['imaginary'] / abs($parsedComplex['real']));
1874        }
1875    }
1876
1877
1878    /**
1879     * IMCONJUGATE
1880     *
1881     * Returns the complex conjugate of a complex number in x + yi or x + yj text format.
1882     *
1883     * Excel Function:
1884     *        IMCONJUGATE(complexNumber)
1885     *
1886     * @param    string        $complexNumber    The complex number for which you want the conjugate.
1887     * @return    string
1888     */
1889    public static function IMCONJUGATE($complexNumber)
1890    {
1891        $complexNumber    = PHPExcel_Calculation_Functions::flattenSingleValue($complexNumber);
1892
1893        $parsedComplex = self::parseComplex($complexNumber);
1894
1895        if ($parsedComplex['imaginary'] == 0.0) {
1896            return $parsedComplex['real'];
1897        } else {
1898            return self::cleanComplex(
1899                self::COMPLEX(
1900                    $parsedComplex['real'],
1901                    0 - $parsedComplex['imaginary'],
1902                    $parsedComplex['suffix']
1903                )
1904            );
1905        }
1906    }
1907
1908
1909    /**
1910     * IMCOS
1911     *
1912     * Returns the cosine of a complex number in x + yi or x + yj text format.
1913     *
1914     * Excel Function:
1915     *        IMCOS(complexNumber)
1916     *
1917     * @param    string        $complexNumber    The complex number for which you want the cosine.
1918     * @return    string|float
1919     */
1920    public static function IMCOS($complexNumber)
1921    {
1922        $complexNumber    = PHPExcel_Calculation_Functions::flattenSingleValue($complexNumber);
1923
1924        $parsedComplex = self::parseComplex($complexNumber);
1925
1926        if ($parsedComplex['imaginary'] == 0.0) {
1927            return cos($parsedComplex['real']);
1928        } else {
1929            return self::IMCONJUGATE(
1930                self::COMPLEX(
1931                    cos($parsedComplex['real']) * cosh($parsedComplex['imaginary']),
1932                    sin($parsedComplex['real']) * sinh($parsedComplex['imaginary']),
1933                    $parsedComplex['suffix']
1934                )
1935            );
1936        }
1937    }
1938
1939
1940    /**
1941     * IMSIN
1942     *
1943     * Returns the sine of a complex number in x + yi or x + yj text format.
1944     *
1945     * Excel Function:
1946     *        IMSIN(complexNumber)
1947     *
1948     * @param    string        $complexNumber    The complex number for which you want the sine.
1949     * @return    string|float
1950     */
1951    public static function IMSIN($complexNumber)
1952    {
1953        $complexNumber    = PHPExcel_Calculation_Functions::flattenSingleValue($complexNumber);
1954
1955        $parsedComplex = self::parseComplex($complexNumber);
1956
1957        if ($parsedComplex['imaginary'] == 0.0) {
1958            return sin($parsedComplex['real']);
1959        } else {
1960            return self::COMPLEX(
1961                sin($parsedComplex['real']) * cosh($parsedComplex['imaginary']),
1962                cos($parsedComplex['real']) * sinh($parsedComplex['imaginary']),
1963                $parsedComplex['suffix']
1964            );
1965        }
1966    }
1967
1968
1969    /**
1970     * IMSQRT
1971     *
1972     * Returns the square root of a complex number in x + yi or x + yj text format.
1973     *
1974     * Excel Function:
1975     *        IMSQRT(complexNumber)
1976     *
1977     * @param    string        $complexNumber    The complex number for which you want the square root.
1978     * @return    string
1979     */
1980    public static function IMSQRT($complexNumber)
1981    {
1982        $complexNumber    = PHPExcel_Calculation_Functions::flattenSingleValue($complexNumber);
1983
1984        $parsedComplex = self::parseComplex($complexNumber);
1985
1986        $theta = self::IMARGUMENT($complexNumber);
1987        $d1 = cos($theta / 2);
1988        $d2 = sin($theta / 2);
1989        $r = sqrt(sqrt(($parsedComplex['real'] * $parsedComplex['real']) + ($parsedComplex['imaginary'] * $parsedComplex['imaginary'])));
1990
1991        if ($parsedComplex['suffix'] == '') {
1992            return self::COMPLEX($d1 * $r, $d2 * $r);
1993        } else {
1994            return self::COMPLEX($d1 * $r, $d2 * $r, $parsedComplex['suffix']);
1995        }
1996    }
1997
1998
1999    /**
2000     * IMLN
2001     *
2002     * Returns the natural logarithm of a complex number in x + yi or x + yj text format.
2003     *
2004     * Excel Function:
2005     *        IMLN(complexNumber)
2006     *
2007     * @param    string        $complexNumber    The complex number for which you want the natural logarithm.
2008     * @return    string
2009     */
2010    public static function IMLN($complexNumber)
2011    {
2012        $complexNumber    = PHPExcel_Calculation_Functions::flattenSingleValue($complexNumber);
2013
2014        $parsedComplex = self::parseComplex($complexNumber);
2015
2016        if (($parsedComplex['real'] == 0.0) && ($parsedComplex['imaginary'] == 0.0)) {
2017            return PHPExcel_Calculation_Functions::NaN();
2018        }
2019
2020        $logR = log(sqrt(($parsedComplex['real'] * $parsedComplex['real']) + ($parsedComplex['imaginary'] * $parsedComplex['imaginary'])));
2021        $t = self::IMARGUMENT($complexNumber);
2022
2023        if ($parsedComplex['suffix'] == '') {
2024            return self::COMPLEX($logR, $t);
2025        } else {
2026            return self::COMPLEX($logR, $t, $parsedComplex['suffix']);
2027        }
2028    }
2029
2030
2031    /**
2032     * IMLOG10
2033     *
2034     * Returns the common logarithm (base 10) of a complex number in x + yi or x + yj text format.
2035     *
2036     * Excel Function:
2037     *        IMLOG10(complexNumber)
2038     *
2039     * @param    string        $complexNumber    The complex number for which you want the common logarithm.
2040     * @return    string
2041     */
2042    public static function IMLOG10($complexNumber)
2043    {
2044        $complexNumber = PHPExcel_Calculation_Functions::flattenSingleValue($complexNumber);
2045
2046        $parsedComplex = self::parseComplex($complexNumber);
2047
2048        if (($parsedComplex['real'] == 0.0) && ($parsedComplex['imaginary'] == 0.0)) {
2049            return PHPExcel_Calculation_Functions::NaN();
2050        } elseif (($parsedComplex['real'] > 0.0) && ($parsedComplex['imaginary'] == 0.0)) {
2051            return log10($parsedComplex['real']);
2052        }
2053
2054        return self::IMPRODUCT(log10(EULER), self::IMLN($complexNumber));
2055    }
2056
2057
2058    /**
2059     * IMLOG2
2060     *
2061     * Returns the base-2 logarithm of a complex number in x + yi or x + yj text format.
2062     *
2063     * Excel Function:
2064     *        IMLOG2(complexNumber)
2065     *
2066     * @param    string        $complexNumber    The complex number for which you want the base-2 logarithm.
2067     * @return    string
2068     */
2069    public static function IMLOG2($complexNumber)
2070    {
2071        $complexNumber    = PHPExcel_Calculation_Functions::flattenSingleValue($complexNumber);
2072
2073        $parsedComplex = self::parseComplex($complexNumber);
2074
2075        if (($parsedComplex['real'] == 0.0) && ($parsedComplex['imaginary'] == 0.0)) {
2076            return PHPExcel_Calculation_Functions::NaN();
2077        } elseif (($parsedComplex['real'] > 0.0) && ($parsedComplex['imaginary'] == 0.0)) {
2078            return log($parsedComplex['real'], 2);
2079        }
2080
2081        return self::IMPRODUCT(log(EULER, 2), self::IMLN($complexNumber));
2082    }
2083
2084
2085    /**
2086     * IMEXP
2087     *
2088     * Returns the exponential of a complex number in x + yi or x + yj text format.
2089     *
2090     * Excel Function:
2091     *        IMEXP(complexNumber)
2092     *
2093     * @param    string        $complexNumber    The complex number for which you want the exponential.
2094     * @return    string
2095     */
2096    public static function IMEXP($complexNumber)
2097    {
2098        $complexNumber = PHPExcel_Calculation_Functions::flattenSingleValue($complexNumber);
2099
2100        $parsedComplex = self::parseComplex($complexNumber);
2101
2102        if (($parsedComplex['real'] == 0.0) && ($parsedComplex['imaginary'] == 0.0)) {
2103            return '1';
2104        }
2105
2106        $e = exp($parsedComplex['real']);
2107        $eX = $e * cos($parsedComplex['imaginary']);
2108        $eY = $e * sin($parsedComplex['imaginary']);
2109
2110        if ($parsedComplex['suffix'] == '') {
2111            return self::COMPLEX($eX, $eY);
2112        } else {
2113            return self::COMPLEX($eX, $eY, $parsedComplex['suffix']);
2114        }
2115    }
2116
2117
2118    /**
2119     * IMPOWER
2120     *
2121     * Returns a complex number in x + yi or x + yj text format raised to a power.
2122     *
2123     * Excel Function:
2124     *        IMPOWER(complexNumber,realNumber)
2125     *
2126     * @param    string        $complexNumber    The complex number you want to raise to a power.
2127     * @param    float        $realNumber        The power to which you want to raise the complex number.
2128     * @return    string
2129     */
2130    public static function IMPOWER($complexNumber, $realNumber)
2131    {
2132        $complexNumber = PHPExcel_Calculation_Functions::flattenSingleValue($complexNumber);
2133        $realNumber    = PHPExcel_Calculation_Functions::flattenSingleValue($realNumber);
2134
2135        if (!is_numeric($realNumber)) {
2136            return PHPExcel_Calculation_Functions::VALUE();
2137        }
2138
2139        $parsedComplex = self::parseComplex($complexNumber);
2140
2141        $r = sqrt(($parsedComplex['real'] * $parsedComplex['real']) + ($parsedComplex['imaginary'] * $parsedComplex['imaginary']));
2142        $rPower = pow($r, $realNumber);
2143        $theta = self::IMARGUMENT($complexNumber) * $realNumber;
2144        if ($theta == 0) {
2145            return 1;
2146        } elseif ($parsedComplex['imaginary'] == 0.0) {
2147            return self::COMPLEX($rPower * cos($theta), $rPower * sin($theta), $parsedComplex['suffix']);
2148        } else {
2149            return self::COMPLEX($rPower * cos($theta), $rPower * sin($theta), $parsedComplex['suffix']);
2150        }
2151    }
2152
2153
2154    /**
2155     * IMDIV
2156     *
2157     * Returns the quotient of two complex numbers in x + yi or x + yj text format.
2158     *
2159     * Excel Function:
2160     *        IMDIV(complexDividend,complexDivisor)
2161     *
2162     * @param    string        $complexDividend    The complex numerator or dividend.
2163     * @param    string        $complexDivisor        The complex denominator or divisor.
2164     * @return    string
2165     */
2166    public static function IMDIV($complexDividend, $complexDivisor)
2167    {
2168        $complexDividend    = PHPExcel_Calculation_Functions::flattenSingleValue($complexDividend);
2169        $complexDivisor    = PHPExcel_Calculation_Functions::flattenSingleValue($complexDivisor);
2170
2171        $parsedComplexDividend = self::parseComplex($complexDividend);
2172        $parsedComplexDivisor = self::parseComplex($complexDivisor);
2173
2174        if (($parsedComplexDividend['suffix'] != '') && ($parsedComplexDivisor['suffix'] != '') &&
2175            ($parsedComplexDividend['suffix'] != $parsedComplexDivisor['suffix'])) {
2176            return PHPExcel_Calculation_Functions::NaN();
2177        }
2178        if (($parsedComplexDividend['suffix'] != '') && ($parsedComplexDivisor['suffix'] == '')) {
2179            $parsedComplexDivisor['suffix'] = $parsedComplexDividend['suffix'];
2180        }
2181
2182        $d1 = ($parsedComplexDividend['real'] * $parsedComplexDivisor['real']) + ($parsedComplexDividend['imaginary'] * $parsedComplexDivisor['imaginary']);
2183        $d2 = ($parsedComplexDividend['imaginary'] * $parsedComplexDivisor['real']) - ($parsedComplexDividend['real'] * $parsedComplexDivisor['imaginary']);
2184        $d3 = ($parsedComplexDivisor['real'] * $parsedComplexDivisor['real']) + ($parsedComplexDivisor['imaginary'] * $parsedComplexDivisor['imaginary']);
2185
2186        $r = $d1 / $d3;
2187        $i = $d2 / $d3;
2188
2189        if ($i > 0.0) {
2190            return self::cleanComplex($r.'+'.$i.$parsedComplexDivisor['suffix']);
2191        } elseif ($i < 0.0) {
2192            return self::cleanComplex($r.$i.$parsedComplexDivisor['suffix']);
2193        } else {
2194            return $r;
2195        }
2196    }
2197
2198
2199    /**
2200     * IMSUB
2201     *
2202     * Returns the difference of two complex numbers in x + yi or x + yj text format.
2203     *
2204     * Excel Function:
2205     *        IMSUB(complexNumber1,complexNumber2)
2206     *
2207     * @param    string        $complexNumber1        The complex number from which to subtract complexNumber2.
2208     * @param    string        $complexNumber2        The complex number to subtract from complexNumber1.
2209     * @return    string
2210     */
2211    public static function IMSUB($complexNumber1, $complexNumber2)
2212    {
2213        $complexNumber1    = PHPExcel_Calculation_Functions::flattenSingleValue($complexNumber1);
2214        $complexNumber2    = PHPExcel_Calculation_Functions::flattenSingleValue($complexNumber2);
2215
2216        $parsedComplex1 = self::parseComplex($complexNumber1);
2217        $parsedComplex2 = self::parseComplex($complexNumber2);
2218
2219        if ((($parsedComplex1['suffix'] != '') && ($parsedComplex2['suffix'] != '')) &&
2220            ($parsedComplex1['suffix'] != $parsedComplex2['suffix'])) {
2221            return PHPExcel_Calculation_Functions::NaN();
2222        } elseif (($parsedComplex1['suffix'] == '') && ($parsedComplex2['suffix'] != '')) {
2223            $parsedComplex1['suffix'] = $parsedComplex2['suffix'];
2224        }
2225
2226        $d1 = $parsedComplex1['real'] - $parsedComplex2['real'];
2227        $d2 = $parsedComplex1['imaginary'] - $parsedComplex2['imaginary'];
2228
2229        return self::COMPLEX($d1, $d2, $parsedComplex1['suffix']);
2230    }
2231
2232
2233    /**
2234     * IMSUM
2235     *
2236     * Returns the sum of two or more complex numbers in x + yi or x + yj text format.
2237     *
2238     * Excel Function:
2239     *        IMSUM(complexNumber[,complexNumber[,...]])
2240     *
2241     * @param    string        $complexNumber,...    Series of complex numbers to add
2242     * @return    string
2243     */
2244    public static function IMSUM()
2245    {
2246        // Return value
2247        $returnValue = self::parseComplex('0');
2248        $activeSuffix = '';
2249
2250        // Loop through the arguments
2251        $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
2252        foreach ($aArgs as $arg) {
2253            $parsedComplex = self::parseComplex($arg);
2254
2255            if ($activeSuffix == '') {
2256                $activeSuffix = $parsedComplex['suffix'];
2257            } elseif (($parsedComplex['suffix'] != '') && ($activeSuffix != $parsedComplex['suffix'])) {
2258                return PHPExcel_Calculation_Functions::VALUE();
2259            }
2260
2261            $returnValue['real'] += $parsedComplex['real'];
2262            $returnValue['imaginary'] += $parsedComplex['imaginary'];
2263        }
2264
2265        if ($returnValue['imaginary'] == 0.0) {
2266            $activeSuffix = '';
2267        }
2268        return self::COMPLEX($returnValue['real'], $returnValue['imaginary'], $activeSuffix);
2269    }
2270
2271
2272    /**
2273     * IMPRODUCT
2274     *
2275     * Returns the product of two or more complex numbers in x + yi or x + yj text format.
2276     *
2277     * Excel Function:
2278     *        IMPRODUCT(complexNumber[,complexNumber[,...]])
2279     *
2280     * @param    string        $complexNumber,...    Series of complex numbers to multiply
2281     * @return    string
2282     */
2283    public static function IMPRODUCT()
2284    {
2285        // Return value
2286        $returnValue = self::parseComplex('1');
2287        $activeSuffix = '';
2288
2289        // Loop through the arguments
2290        $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
2291        foreach ($aArgs as $arg) {
2292            $parsedComplex = self::parseComplex($arg);
2293
2294            $workValue = $returnValue;
2295            if (($parsedComplex['suffix'] != '') && ($activeSuffix == '')) {
2296                $activeSuffix = $parsedComplex['suffix'];
2297            } elseif (($parsedComplex['suffix'] != '') && ($activeSuffix != $parsedComplex['suffix'])) {
2298                return PHPExcel_Calculation_Functions::NaN();
2299            }
2300            $returnValue['real'] = ($workValue['real'] * $parsedComplex['real']) - ($workValue['imaginary'] * $parsedComplex['imaginary']);
2301            $returnValue['imaginary'] = ($workValue['real'] * $parsedComplex['imaginary']) + ($workValue['imaginary'] * $parsedComplex['real']);
2302        }
2303
2304        if ($returnValue['imaginary'] == 0.0) {
2305            $activeSuffix = '';
2306        }
2307        return self::COMPLEX($returnValue['real'], $returnValue['imaginary'], $activeSuffix);
2308    }
2309
2310
2311    /**
2312     *    DELTA
2313     *
2314     *    Tests whether two values are equal. Returns 1 if number1 = number2; returns 0 otherwise.
2315     *    Use this function to filter a set of values. For example, by summing several DELTA
2316     *    functions you calculate the count of equal pairs. This function is also known as the
2317     *    Kronecker Delta function.
2318     *
2319     *    Excel Function:
2320     *        DELTA(a[,b])
2321     *
2322     *    @param    float        $a    The first number.
2323     *    @param    float        $b    The second number. If omitted, b is assumed to be zero.
2324     *    @return    int
2325     */
2326    public static function DELTA($a, $b = 0)
2327    {
2328        $a = PHPExcel_Calculation_Functions::flattenSingleValue($a);
2329        $b = PHPExcel_Calculation_Functions::flattenSingleValue($b);
2330
2331        return (int) ($a == $b);
2332    }
2333
2334
2335    /**
2336     *    GESTEP
2337     *
2338     *    Excel Function:
2339     *        GESTEP(number[,step])
2340     *
2341     *    Returns 1 if number >= step; returns 0 (zero) otherwise
2342     *    Use this function to filter a set of values. For example, by summing several GESTEP
2343     *    functions you calculate the count of values that exceed a threshold.
2344     *
2345     *    @param    float        $number        The value to test against step.
2346     *    @param    float        $step        The threshold value.
2347     *                                    If you omit a value for step, GESTEP uses zero.
2348     *    @return    int
2349     */
2350    public static function GESTEP($number, $step = 0)
2351    {
2352        $number    = PHPExcel_Calculation_Functions::flattenSingleValue($number);
2353        $step    = PHPExcel_Calculation_Functions::flattenSingleValue($step);
2354
2355        return (int) ($number >= $step);
2356    }
2357
2358
2359    //
2360    //    Private method to calculate the erf value
2361    //
2362    private static $twoSqrtPi = 1.128379167095512574;
2363
2364    public static function erfVal($x)
2365    {
2366        if (abs($x) > 2.2) {
2367            return 1 - self::erfcVal($x);
2368        }
2369        $sum = $term = $x;
2370        $xsqr = ($x * $x);
2371        $j = 1;
2372        do {
2373            $term *= $xsqr / $j;
2374            $sum -= $term / (2 * $j + 1);
2375            ++$j;
2376            $term *= $xsqr / $j;
2377            $sum += $term / (2 * $j + 1);
2378            ++$j;
2379            if ($sum == 0.0) {
2380                break;
2381            }
2382        } while (abs($term / $sum) > PRECISION);
2383        return self::$twoSqrtPi * $sum;
2384    }
2385
2386
2387    /**
2388     *    ERF
2389     *
2390     *    Returns the error function integrated between the lower and upper bound arguments.
2391     *
2392     *    Note: In Excel 2007 or earlier, if you input a negative value for the upper or lower bound arguments,
2393     *            the function would return a #NUM! error. However, in Excel 2010, the function algorithm was
2394     *            improved, so that it can now calculate the function for both positive and negative ranges.
2395     *            PHPExcel follows Excel 2010 behaviour, and accepts nagative arguments.
2396     *
2397     *    Excel Function:
2398     *        ERF(lower[,upper])
2399     *
2400     *    @param    float        $lower    lower bound for integrating ERF
2401     *    @param    float        $upper    upper bound for integrating ERF.
2402     *                                If omitted, ERF integrates between zero and lower_limit
2403     *    @return    float
2404     */
2405    public static function ERF($lower, $upper = null)
2406    {
2407        $lower    = PHPExcel_Calculation_Functions::flattenSingleValue($lower);
2408        $upper    = PHPExcel_Calculation_Functions::flattenSingleValue($upper);
2409
2410        if (is_numeric($lower)) {
2411            if (is_null($upper)) {
2412                return self::erfVal($lower);
2413            }
2414            if (is_numeric($upper)) {
2415                return self::erfVal($upper) - self::erfVal($lower);
2416            }
2417        }
2418        return PHPExcel_Calculation_Functions::VALUE();
2419    }
2420
2421
2422    //
2423    //    Private method to calculate the erfc value
2424    //
2425    private static $oneSqrtPi = 0.564189583547756287;
2426
2427    private static function erfcVal($x)
2428    {
2429        if (abs($x) < 2.2) {
2430            return 1 - self::erfVal($x);
2431        }
2432        if ($x < 0) {
2433            return 2 - self::ERFC(-$x);
2434        }
2435        $a = $n = 1;
2436        $b = $c = $x;
2437        $d = ($x * $x) + 0.5;
2438        $q1 = $q2 = $b / $d;
2439        $t = 0;
2440        do {
2441            $t = $a * $n + $b * $x;
2442            $a = $b;
2443            $b = $t;
2444            $t = $c * $n + $d * $x;
2445            $c = $d;
2446            $d = $t;
2447            $n += 0.5;
2448            $q1 = $q2;
2449            $q2 = $b / $d;
2450        } while ((abs($q1 - $q2) / $q2) > PRECISION);
2451        return self::$oneSqrtPi * exp(-$x * $x) * $q2;
2452    }
2453
2454
2455    /**
2456     *    ERFC
2457     *
2458     *    Returns the complementary ERF function integrated between x and infinity
2459     *
2460     *    Note: In Excel 2007 or earlier, if you input a negative value for the lower bound argument,
2461     *        the function would return a #NUM! error. However, in Excel 2010, the function algorithm was
2462     *        improved, so that it can now calculate the function for both positive and negative x values.
2463     *            PHPExcel follows Excel 2010 behaviour, and accepts nagative arguments.
2464     *
2465     *    Excel Function:
2466     *        ERFC(x)
2467     *
2468     *    @param    float    $x    The lower bound for integrating ERFC
2469     *    @return    float
2470     */
2471    public static function ERFC($x)
2472    {
2473        $x = PHPExcel_Calculation_Functions::flattenSingleValue($x);
2474
2475        if (is_numeric($x)) {
2476            return self::erfcVal($x);
2477        }
2478        return PHPExcel_Calculation_Functions::VALUE();
2479    }
2480
2481
2482    /**
2483     *    getConversionGroups
2484     *    Returns a list of the different conversion groups for UOM conversions
2485     *
2486     *    @return    array
2487     */
2488    public static function getConversionGroups()
2489    {
2490        $conversionGroups = array();
2491        foreach (self::$conversionUnits as $conversionUnit) {
2492            $conversionGroups[] = $conversionUnit['Group'];
2493        }
2494        return array_merge(array_unique($conversionGroups));
2495    }
2496
2497
2498    /**
2499     *    getConversionGroupUnits
2500     *    Returns an array of units of measure, for a specified conversion group, or for all groups
2501     *
2502     *    @param    string    $group    The group whose units of measure you want to retrieve
2503     *    @return    array
2504     */
2505    public static function getConversionGroupUnits($group = null)
2506    {
2507        $conversionGroups = array();
2508        foreach (self::$conversionUnits as $conversionUnit => $conversionGroup) {
2509            if ((is_null($group)) || ($conversionGroup['Group'] == $group)) {
2510                $conversionGroups[$conversionGroup['Group']][] = $conversionUnit;
2511            }
2512        }
2513        return $conversionGroups;
2514    }
2515
2516
2517    /**
2518     *    getConversionGroupUnitDetails
2519     *
2520     *    @param    string    $group    The group whose units of measure you want to retrieve
2521     *    @return    array
2522     */
2523    public static function getConversionGroupUnitDetails($group = null)
2524    {
2525        $conversionGroups = array();
2526        foreach (self::$conversionUnits as $conversionUnit => $conversionGroup) {
2527            if ((is_null($group)) || ($conversionGroup['Group'] == $group)) {
2528                $conversionGroups[$conversionGroup['Group']][] = array(
2529                    'unit'        => $conversionUnit,
2530                    'description' => $conversionGroup['Unit Name']
2531                );
2532            }
2533        }
2534        return $conversionGroups;
2535    }
2536
2537
2538    /**
2539     *    getConversionMultipliers
2540     *    Returns an array of the Multiplier prefixes that can be used with Units of Measure in CONVERTUOM()
2541     *
2542     *    @return    array of mixed
2543     */
2544    public static function getConversionMultipliers()
2545    {
2546        return self::$conversionMultipliers;
2547    }
2548
2549
2550    /**
2551     *    CONVERTUOM
2552     *
2553     *    Converts a number from one measurement system to another.
2554     *    For example, CONVERT can translate a table of distances in miles to a table of distances
2555     *    in kilometers.
2556     *
2557     *    Excel Function:
2558     *        CONVERT(value,fromUOM,toUOM)
2559     *
2560     *    @param    float        $value        The value in fromUOM to convert.
2561     *    @param    string        $fromUOM    The units for value.
2562     *    @param    string        $toUOM        The units for the result.
2563     *
2564     *    @return    float
2565     */
2566    public static function CONVERTUOM($value, $fromUOM, $toUOM)
2567    {
2568        $value   = PHPExcel_Calculation_Functions::flattenSingleValue($value);
2569        $fromUOM = PHPExcel_Calculation_Functions::flattenSingleValue($fromUOM);
2570        $toUOM   = PHPExcel_Calculation_Functions::flattenSingleValue($toUOM);
2571
2572        if (!is_numeric($value)) {
2573            return PHPExcel_Calculation_Functions::VALUE();
2574        }
2575        $fromMultiplier = 1.0;
2576        if (isset(self::$conversionUnits[$fromUOM])) {
2577            $unitGroup1 = self::$conversionUnits[$fromUOM]['Group'];
2578        } else {
2579            $fromMultiplier = substr($fromUOM, 0, 1);
2580            $fromUOM = substr($fromUOM, 1);
2581            if (isset(self::$conversionMultipliers[$fromMultiplier])) {
2582                $fromMultiplier = self::$conversionMultipliers[$fromMultiplier]['multiplier'];
2583            } else {
2584                return PHPExcel_Calculation_Functions::NA();
2585            }
2586            if ((isset(self::$conversionUnits[$fromUOM])) && (self::$conversionUnits[$fromUOM]['AllowPrefix'])) {
2587                $unitGroup1 = self::$conversionUnits[$fromUOM]['Group'];
2588            } else {
2589                return PHPExcel_Calculation_Functions::NA();
2590            }
2591        }
2592        $value *= $fromMultiplier;
2593
2594        $toMultiplier = 1.0;
2595        if (isset(self::$conversionUnits[$toUOM])) {
2596            $unitGroup2 = self::$conversionUnits[$toUOM]['Group'];
2597        } else {
2598            $toMultiplier = substr($toUOM, 0, 1);
2599            $toUOM = substr($toUOM, 1);
2600            if (isset(self::$conversionMultipliers[$toMultiplier])) {
2601                $toMultiplier = self::$conversionMultipliers[$toMultiplier]['multiplier'];
2602            } else {
2603                return PHPExcel_Calculation_Functions::NA();
2604            }
2605            if ((isset(self::$conversionUnits[$toUOM])) && (self::$conversionUnits[$toUOM]['AllowPrefix'])) {
2606                $unitGroup2 = self::$conversionUnits[$toUOM]['Group'];
2607            } else {
2608                return PHPExcel_Calculation_Functions::NA();
2609            }
2610        }
2611        if ($unitGroup1 != $unitGroup2) {
2612            return PHPExcel_Calculation_Functions::NA();
2613        }
2614
2615        if (($fromUOM == $toUOM) && ($fromMultiplier == $toMultiplier)) {
2616            //    We've already factored $fromMultiplier into the value, so we need
2617            //        to reverse it again
2618            return $value / $fromMultiplier;
2619        } elseif ($unitGroup1 == 'Temperature') {
2620            if (($fromUOM == 'F') || ($fromUOM == 'fah')) {
2621                if (($toUOM == 'F') || ($toUOM == 'fah')) {
2622                    return $value;
2623                } else {
2624                    $value = (($value - 32) / 1.8);
2625                    if (($toUOM == 'K') || ($toUOM == 'kel')) {
2626                        $value += 273.15;
2627                    }
2628                    return $value;
2629                }
2630            } elseif ((($fromUOM == 'K') || ($fromUOM == 'kel')) &&
2631                      (($toUOM == 'K') || ($toUOM == 'kel'))) {
2632                        return $value;
2633            } elseif ((($fromUOM == 'C') || ($fromUOM == 'cel')) &&
2634                      (($toUOM == 'C') || ($toUOM == 'cel'))) {
2635                    return $value;
2636            }
2637            if (($toUOM == 'F') || ($toUOM == 'fah')) {
2638                if (($fromUOM == 'K') || ($fromUOM == 'kel')) {
2639                    $value -= 273.15;
2640                }
2641                return ($value * 1.8) + 32;
2642            }
2643            if (($toUOM == 'C') || ($toUOM == 'cel')) {
2644                return $value - 273.15;
2645            }
2646            return $value + 273.15;
2647        }
2648        return ($value * self::$unitConversions[$unitGroup1][$fromUOM][$toUOM]) / $toMultiplier;
2649    }
2650}
2651