Test Failed
Push — main ( c8394f...8477f1 )
by Rafael
66:21
created

Engineering   F

Complexity

Total Complexity 258

Size/Duplication

Total Lines 2801
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 1221
dl 0
loc 2801
rs 0.8
c 0
b 0
f 0
wmc 258

How to fix   Complexity   

Complex Class

Complex classes like Engineering often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Engineering, and based on these observations, apply Extract Interface, too.

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