Passed
Push — develop ( 9b44cf...67cdee )
by Mark
31:30
created

Engineering::IMCOS()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 9
nc 2
nop 1
dl 0
loc 15
ccs 10
cts 10
cp 1
crap 2
rs 9.9666
c 0
b 0
f 0
1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet\Calculation;
4
5
class Engineering
6
{
7
    /**
8
     * EULER.
9
     */
10
    const EULER = 2.71828182845904523536;
11
12
    /**
13
     * Details of the Units of measure that can be used in CONVERTUOM().
14
     *
15
     * @var mixed[]
16
     */
17
    private static $conversionUnits = [
18
        'g' => ['Group' => 'Mass', 'Unit Name' => 'Gram', 'AllowPrefix' => true],
19
        'sg' => ['Group' => 'Mass', 'Unit Name' => 'Slug', 'AllowPrefix' => false],
20
        'lbm' => ['Group' => 'Mass', 'Unit Name' => 'Pound mass (avoirdupois)', 'AllowPrefix' => false],
21
        'u' => ['Group' => 'Mass', 'Unit Name' => 'U (atomic mass unit)', 'AllowPrefix' => true],
22
        'ozm' => ['Group' => 'Mass', 'Unit Name' => 'Ounce mass (avoirdupois)', 'AllowPrefix' => false],
23
        'm' => ['Group' => 'Distance', 'Unit Name' => 'Meter', 'AllowPrefix' => true],
24
        'mi' => ['Group' => 'Distance', 'Unit Name' => 'Statute mile', 'AllowPrefix' => false],
25
        'Nmi' => ['Group' => 'Distance', 'Unit Name' => 'Nautical mile', 'AllowPrefix' => false],
26
        'in' => ['Group' => 'Distance', 'Unit Name' => 'Inch', 'AllowPrefix' => false],
27
        'ft' => ['Group' => 'Distance', 'Unit Name' => 'Foot', 'AllowPrefix' => false],
28
        'yd' => ['Group' => 'Distance', 'Unit Name' => 'Yard', 'AllowPrefix' => false],
29
        'ang' => ['Group' => 'Distance', 'Unit Name' => 'Angstrom', 'AllowPrefix' => true],
30
        'Pica' => ['Group' => 'Distance', 'Unit Name' => 'Pica (1/72 in)', 'AllowPrefix' => false],
31
        'yr' => ['Group' => 'Time', 'Unit Name' => 'Year', 'AllowPrefix' => false],
32
        'day' => ['Group' => 'Time', 'Unit Name' => 'Day', 'AllowPrefix' => false],
33
        'hr' => ['Group' => 'Time', 'Unit Name' => 'Hour', 'AllowPrefix' => false],
34
        'mn' => ['Group' => 'Time', 'Unit Name' => 'Minute', 'AllowPrefix' => false],
35
        'sec' => ['Group' => 'Time', 'Unit Name' => 'Second', 'AllowPrefix' => true],
36
        'Pa' => ['Group' => 'Pressure', 'Unit Name' => 'Pascal', 'AllowPrefix' => true],
37
        'p' => ['Group' => 'Pressure', 'Unit Name' => 'Pascal', 'AllowPrefix' => true],
38
        'atm' => ['Group' => 'Pressure', 'Unit Name' => 'Atmosphere', 'AllowPrefix' => true],
39
        'at' => ['Group' => 'Pressure', 'Unit Name' => 'Atmosphere', 'AllowPrefix' => true],
40
        'mmHg' => ['Group' => 'Pressure', 'Unit Name' => 'mm of Mercury', 'AllowPrefix' => true],
41
        'N' => ['Group' => 'Force', 'Unit Name' => 'Newton', 'AllowPrefix' => true],
42
        'dyn' => ['Group' => 'Force', 'Unit Name' => 'Dyne', 'AllowPrefix' => true],
43
        'dy' => ['Group' => 'Force', 'Unit Name' => 'Dyne', 'AllowPrefix' => true],
44
        'lbf' => ['Group' => 'Force', 'Unit Name' => 'Pound force', 'AllowPrefix' => false],
45
        'J' => ['Group' => 'Energy', 'Unit Name' => 'Joule', 'AllowPrefix' => true],
46
        'e' => ['Group' => 'Energy', 'Unit Name' => 'Erg', 'AllowPrefix' => true],
47
        'c' => ['Group' => 'Energy', 'Unit Name' => 'Thermodynamic calorie', 'AllowPrefix' => true],
48
        'cal' => ['Group' => 'Energy', 'Unit Name' => 'IT calorie', 'AllowPrefix' => true],
49
        'eV' => ['Group' => 'Energy', 'Unit Name' => 'Electron volt', 'AllowPrefix' => true],
50
        'ev' => ['Group' => 'Energy', 'Unit Name' => 'Electron volt', 'AllowPrefix' => true],
51
        'HPh' => ['Group' => 'Energy', 'Unit Name' => 'Horsepower-hour', 'AllowPrefix' => false],
52
        'hh' => ['Group' => 'Energy', 'Unit Name' => 'Horsepower-hour', 'AllowPrefix' => false],
53
        'Wh' => ['Group' => 'Energy', 'Unit Name' => 'Watt-hour', 'AllowPrefix' => true],
54
        'wh' => ['Group' => 'Energy', 'Unit Name' => 'Watt-hour', 'AllowPrefix' => true],
55
        'flb' => ['Group' => 'Energy', 'Unit Name' => 'Foot-pound', 'AllowPrefix' => false],
56
        'BTU' => ['Group' => 'Energy', 'Unit Name' => 'BTU', 'AllowPrefix' => false],
57
        'btu' => ['Group' => 'Energy', 'Unit Name' => 'BTU', 'AllowPrefix' => false],
58
        'HP' => ['Group' => 'Power', 'Unit Name' => 'Horsepower', 'AllowPrefix' => false],
59
        'h' => ['Group' => 'Power', 'Unit Name' => 'Horsepower', 'AllowPrefix' => false],
60
        'W' => ['Group' => 'Power', 'Unit Name' => 'Watt', 'AllowPrefix' => true],
61
        'w' => ['Group' => 'Power', 'Unit Name' => 'Watt', 'AllowPrefix' => true],
62
        'T' => ['Group' => 'Magnetism', 'Unit Name' => 'Tesla', 'AllowPrefix' => true],
63
        'ga' => ['Group' => 'Magnetism', 'Unit Name' => 'Gauss', 'AllowPrefix' => true],
64
        'C' => ['Group' => 'Temperature', 'Unit Name' => 'Celsius', 'AllowPrefix' => false],
65
        'cel' => ['Group' => 'Temperature', 'Unit Name' => 'Celsius', 'AllowPrefix' => false],
66
        'F' => ['Group' => 'Temperature', 'Unit Name' => 'Fahrenheit', 'AllowPrefix' => false],
67
        'fah' => ['Group' => 'Temperature', 'Unit Name' => 'Fahrenheit', 'AllowPrefix' => false],
68
        'K' => ['Group' => 'Temperature', 'Unit Name' => 'Kelvin', 'AllowPrefix' => false],
69
        'kel' => ['Group' => 'Temperature', 'Unit Name' => 'Kelvin', 'AllowPrefix' => false],
70
        'tsp' => ['Group' => 'Liquid', 'Unit Name' => 'Teaspoon', 'AllowPrefix' => false],
71
        'tbs' => ['Group' => 'Liquid', 'Unit Name' => 'Tablespoon', 'AllowPrefix' => false],
72
        'oz' => ['Group' => 'Liquid', 'Unit Name' => 'Fluid Ounce', 'AllowPrefix' => false],
73
        'cup' => ['Group' => 'Liquid', 'Unit Name' => 'Cup', 'AllowPrefix' => false],
74
        'pt' => ['Group' => 'Liquid', 'Unit Name' => 'U.S. Pint', 'AllowPrefix' => false],
75
        'us_pt' => ['Group' => 'Liquid', 'Unit Name' => 'U.S. Pint', 'AllowPrefix' => false],
76
        'uk_pt' => ['Group' => 'Liquid', 'Unit Name' => 'U.K. Pint', 'AllowPrefix' => false],
77
        'qt' => ['Group' => 'Liquid', 'Unit Name' => 'Quart', 'AllowPrefix' => false],
78
        'gal' => ['Group' => 'Liquid', 'Unit Name' => 'Gallon', 'AllowPrefix' => false],
79
        'l' => ['Group' => 'Liquid', 'Unit Name' => 'Litre', 'AllowPrefix' => true],
80
        'lt' => ['Group' => 'Liquid', 'Unit Name' => 'Litre', 'AllowPrefix' => true],
81
    ];
82
83
    /**
84
     * Details of the Multiplier prefixes that can be used with Units of Measure in CONVERTUOM().
85
     *
86
     * @var mixed[]
87
     */
88
    private static $conversionMultipliers = [
89
        'Y' => ['multiplier' => 1E24, 'name' => 'yotta'],
90
        'Z' => ['multiplier' => 1E21, 'name' => 'zetta'],
91
        'E' => ['multiplier' => 1E18, 'name' => 'exa'],
92
        'P' => ['multiplier' => 1E15, 'name' => 'peta'],
93
        'T' => ['multiplier' => 1E12, 'name' => 'tera'],
94
        'G' => ['multiplier' => 1E9, 'name' => 'giga'],
95
        'M' => ['multiplier' => 1E6, 'name' => 'mega'],
96
        'k' => ['multiplier' => 1E3, 'name' => 'kilo'],
97
        'h' => ['multiplier' => 1E2, 'name' => 'hecto'],
98
        'e' => ['multiplier' => 1E1, 'name' => 'deka'],
99
        'd' => ['multiplier' => 1E-1, 'name' => 'deci'],
100
        'c' => ['multiplier' => 1E-2, 'name' => 'centi'],
101
        'm' => ['multiplier' => 1E-3, 'name' => 'milli'],
102
        'u' => ['multiplier' => 1E-6, 'name' => 'micro'],
103
        'n' => ['multiplier' => 1E-9, 'name' => 'nano'],
104
        'p' => ['multiplier' => 1E-12, 'name' => 'pico'],
105
        'f' => ['multiplier' => 1E-15, 'name' => 'femto'],
106
        'a' => ['multiplier' => 1E-18, 'name' => 'atto'],
107
        'z' => ['multiplier' => 1E-21, 'name' => 'zepto'],
108
        'y' => ['multiplier' => 1E-24, 'name' => 'yocto'],
109
    ];
110
111
    /**
112
     * Details of the Units of measure conversion factors, organised by group.
113
     *
114
     * @var mixed[]
115
     */
116
    private static $unitConversions = [
117
        'Mass' => [
118
            'g' => [
119
                'g' => 1.0,
120
                'sg' => 6.85220500053478E-05,
121
                'lbm' => 2.20462291469134E-03,
122
                'u' => 6.02217000000000E+23,
123
                'ozm' => 3.52739718003627E-02,
124
            ],
125
            'sg' => [
126
                'g' => 1.45938424189287E+04,
127
                'sg' => 1.0,
128
                'lbm' => 3.21739194101647E+01,
129
                'u' => 8.78866000000000E+27,
130
                'ozm' => 5.14782785944229E+02,
131
            ],
132
            'lbm' => [
133
                'g' => 4.5359230974881148E+02,
134
                'sg' => 3.10810749306493E-02,
135
                'lbm' => 1.0,
136
                'u' => 2.73161000000000E+26,
137
                'ozm' => 1.60000023429410E+01,
138
            ],
139
            'u' => [
140
                'g' => 1.66053100460465E-24,
141
                'sg' => 1.13782988532950E-28,
142
                'lbm' => 3.66084470330684E-27,
143
                'u' => 1.0,
144
                'ozm' => 5.85735238300524E-26,
145
            ],
146
            'ozm' => [
147
                'g' => 2.83495152079732E+01,
148
                'sg' => 1.94256689870811E-03,
149
                'lbm' => 6.24999908478882E-02,
150
                'u' => 1.70725600000000E+25,
151
                'ozm' => 1.0,
152
            ],
153
        ],
154
        'Distance' => [
155
            'm' => [
156
                'm' => 1.0,
157
                'mi' => 6.21371192237334E-04,
158
                'Nmi' => 5.39956803455724E-04,
159
                'in' => 3.93700787401575E+01,
160
                'ft' => 3.28083989501312E+00,
161
                'yd' => 1.09361329797891E+00,
162
                'ang' => 1.00000000000000E+10,
163
                'Pica' => 2.83464566929116E+03,
164
            ],
165
            'mi' => [
166
                'm' => 1.60934400000000E+03,
167
                'mi' => 1.0,
168
                'Nmi' => 8.68976241900648E-01,
169
                'in' => 6.33600000000000E+04,
170
                'ft' => 5.28000000000000E+03,
171
                'yd' => 1.76000000000000E+03,
172
                'ang' => 1.60934400000000E+13,
173
                'Pica' => 4.56191999999971E+06,
174
            ],
175
            'Nmi' => [
176
                'm' => 1.85200000000000E+03,
177
                'mi' => 1.15077944802354E+00,
178
                'Nmi' => 1.0,
179
                'in' => 7.29133858267717E+04,
180
                'ft' => 6.07611548556430E+03,
181
                'yd' => 2.02537182785694E+03,
182
                'ang' => 1.85200000000000E+13,
183
                'Pica' => 5.24976377952723E+06,
184
            ],
185
            'in' => [
186
                'm' => 2.54000000000000E-02,
187
                'mi' => 1.57828282828283E-05,
188
                'Nmi' => 1.37149028077754E-05,
189
                'in' => 1.0,
190
                'ft' => 8.33333333333333E-02,
191
                'yd' => 2.77777777686643E-02,
192
                'ang' => 2.54000000000000E+08,
193
                'Pica' => 7.19999999999955E+01,
194
            ],
195
            'ft' => [
196
                'm' => 3.04800000000000E-01,
197
                'mi' => 1.89393939393939E-04,
198
                'Nmi' => 1.64578833693305E-04,
199
                'in' => 1.20000000000000E+01,
200
                'ft' => 1.0,
201
                'yd' => 3.33333333223972E-01,
202
                'ang' => 3.04800000000000E+09,
203
                'Pica' => 8.63999999999946E+02,
204
            ],
205
            'yd' => [
206
                'm' => 9.14400000300000E-01,
207
                'mi' => 5.68181818368230E-04,
208
                'Nmi' => 4.93736501241901E-04,
209
                'in' => 3.60000000118110E+01,
210
                'ft' => 3.00000000000000E+00,
211
                'yd' => 1.0,
212
                'ang' => 9.14400000300000E+09,
213
                'Pica' => 2.59200000085023E+03,
214
            ],
215
            'ang' => [
216
                'm' => 1.00000000000000E-10,
217
                'mi' => 6.21371192237334E-14,
218
                'Nmi' => 5.39956803455724E-14,
219
                'in' => 3.93700787401575E-09,
220
                'ft' => 3.28083989501312E-10,
221
                'yd' => 1.09361329797891E-10,
222
                'ang' => 1.0,
223
                'Pica' => 2.83464566929116E-07,
224
            ],
225
            'Pica' => [
226
                'm' => 3.52777777777800E-04,
227
                'mi' => 2.19205948372629E-07,
228
                'Nmi' => 1.90484761219114E-07,
229
                'in' => 1.38888888888898E-02,
230
                'ft' => 1.15740740740748E-03,
231
                'yd' => 3.85802469009251E-04,
232
                'ang' => 3.52777777777800E+06,
233
                'Pica' => 1.0,
234
            ],
235
        ],
236
        'Time' => [
237
            'yr' => [
238
                'yr' => 1.0,
239
                'day' => 365.25,
240
                'hr' => 8766.0,
241
                'mn' => 525960.0,
242
                'sec' => 31557600.0,
243
            ],
244
            'day' => [
245
                'yr' => 2.73785078713210E-03,
246
                'day' => 1.0,
247
                'hr' => 24.0,
248
                'mn' => 1440.0,
249
                'sec' => 86400.0,
250
            ],
251
            'hr' => [
252
                'yr' => 1.14077116130504E-04,
253
                'day' => 4.16666666666667E-02,
254
                'hr' => 1.0,
255
                'mn' => 60.0,
256
                'sec' => 3600.0,
257
            ],
258
            'mn' => [
259
                'yr' => 1.90128526884174E-06,
260
                'day' => 6.94444444444444E-04,
261
                'hr' => 1.66666666666667E-02,
262
                'mn' => 1.0,
263
                'sec' => 60.0,
264
            ],
265
            'sec' => [
266
                'yr' => 3.16880878140289E-08,
267
                'day' => 1.15740740740741E-05,
268
                'hr' => 2.77777777777778E-04,
269
                'mn' => 1.66666666666667E-02,
270
                'sec' => 1.0,
271
            ],
272
        ],
273
        'Pressure' => [
274
            'Pa' => [
275
                'Pa' => 1.0,
276
                'p' => 1.0,
277
                'atm' => 9.86923299998193E-06,
278
                'at' => 9.86923299998193E-06,
279
                'mmHg' => 7.50061707998627E-03,
280
            ],
281
            'p' => [
282
                'Pa' => 1.0,
283
                'p' => 1.0,
284
                'atm' => 9.86923299998193E-06,
285
                'at' => 9.86923299998193E-06,
286
                'mmHg' => 7.50061707998627E-03,
287
            ],
288
            'atm' => [
289
                'Pa' => 1.01324996583000E+05,
290
                'p' => 1.01324996583000E+05,
291
                'atm' => 1.0,
292
                'at' => 1.0,
293
                'mmHg' => 760.0,
294
            ],
295
            'at' => [
296
                'Pa' => 1.01324996583000E+05,
297
                'p' => 1.01324996583000E+05,
298
                'atm' => 1.0,
299
                'at' => 1.0,
300
                'mmHg' => 760.0,
301
            ],
302
            'mmHg' => [
303
                'Pa' => 1.33322363925000E+02,
304
                'p' => 1.33322363925000E+02,
305
                'atm' => 1.31578947368421E-03,
306
                'at' => 1.31578947368421E-03,
307
                'mmHg' => 1.0,
308
            ],
309
        ],
310
        'Force' => [
311
            'N' => [
312
                'N' => 1.0,
313
                'dyn' => 1.0E+5,
314
                'dy' => 1.0E+5,
315
                'lbf' => 2.24808923655339E-01,
316
            ],
317
            'dyn' => [
318
                'N' => 1.0E-5,
319
                'dyn' => 1.0,
320
                'dy' => 1.0,
321
                'lbf' => 2.24808923655339E-06,
322
            ],
323
            'dy' => [
324
                'N' => 1.0E-5,
325
                'dyn' => 1.0,
326
                'dy' => 1.0,
327
                'lbf' => 2.24808923655339E-06,
328
            ],
329
            'lbf' => [
330
                'N' => 4.448222,
331
                'dyn' => 4.448222E+5,
332
                'dy' => 4.448222E+5,
333
                'lbf' => 1.0,
334
            ],
335
        ],
336
        'Energy' => [
337
            'J' => [
338
                'J' => 1.0,
339
                'e' => 9.99999519343231E+06,
340
                'c' => 2.39006249473467E-01,
341
                'cal' => 2.38846190642017E-01,
342
                'eV' => 6.24145700000000E+18,
343
                'ev' => 6.24145700000000E+18,
344
                'HPh' => 3.72506430801000E-07,
345
                'hh' => 3.72506430801000E-07,
346
                'Wh' => 2.77777916238711E-04,
347
                'wh' => 2.77777916238711E-04,
348
                'flb' => 2.37304222192651E+01,
349
                'BTU' => 9.47815067349015E-04,
350
                'btu' => 9.47815067349015E-04,
351
            ],
352
            'e' => [
353
                'J' => 1.00000048065700E-07,
354
                'e' => 1.0,
355
                'c' => 2.39006364353494E-08,
356
                'cal' => 2.38846305445111E-08,
357
                'eV' => 6.24146000000000E+11,
358
                'ev' => 6.24146000000000E+11,
359
                'HPh' => 3.72506609848824E-14,
360
                'hh' => 3.72506609848824E-14,
361
                'Wh' => 2.77778049754611E-11,
362
                'wh' => 2.77778049754611E-11,
363
                'flb' => 2.37304336254586E-06,
364
                'BTU' => 9.47815522922962E-11,
365
                'btu' => 9.47815522922962E-11,
366
            ],
367
            'c' => [
368
                'J' => 4.18399101363672E+00,
369
                'e' => 4.18398900257312E+07,
370
                'c' => 1.0,
371
                'cal' => 9.99330315287563E-01,
372
                'eV' => 2.61142000000000E+19,
373
                'ev' => 2.61142000000000E+19,
374
                'HPh' => 1.55856355899327E-06,
375
                'hh' => 1.55856355899327E-06,
376
                'Wh' => 1.16222030532950E-03,
377
                'wh' => 1.16222030532950E-03,
378
                'flb' => 9.92878733152102E+01,
379
                'BTU' => 3.96564972437776E-03,
380
                'btu' => 3.96564972437776E-03,
381
            ],
382
            'cal' => [
383
                'J' => 4.18679484613929E+00,
384
                'e' => 4.18679283372801E+07,
385
                'c' => 1.00067013349059E+00,
386
                'cal' => 1.0,
387
                'eV' => 2.61317000000000E+19,
388
                'ev' => 2.61317000000000E+19,
389
                'HPh' => 1.55960800463137E-06,
390
                'hh' => 1.55960800463137E-06,
391
                'Wh' => 1.16299914807955E-03,
392
                'wh' => 1.16299914807955E-03,
393
                'flb' => 9.93544094443283E+01,
394
                'BTU' => 3.96830723907002E-03,
395
                'btu' => 3.96830723907002E-03,
396
            ],
397
            'eV' => [
398
                'J' => 1.60219000146921E-19,
399
                'e' => 1.60218923136574E-12,
400
                'c' => 3.82933423195043E-20,
401
                'cal' => 3.82676978535648E-20,
402
                'eV' => 1.0,
403
                'ev' => 1.0,
404
                'HPh' => 5.96826078912344E-26,
405
                'hh' => 5.96826078912344E-26,
406
                'Wh' => 4.45053000026614E-23,
407
                'wh' => 4.45053000026614E-23,
408
                'flb' => 3.80206452103492E-18,
409
                'BTU' => 1.51857982414846E-22,
410
                'btu' => 1.51857982414846E-22,
411
            ],
412
            'ev' => [
413
                'J' => 1.60219000146921E-19,
414
                'e' => 1.60218923136574E-12,
415
                'c' => 3.82933423195043E-20,
416
                'cal' => 3.82676978535648E-20,
417
                'eV' => 1.0,
418
                'ev' => 1.0,
419
                'HPh' => 5.96826078912344E-26,
420
                'hh' => 5.96826078912344E-26,
421
                'Wh' => 4.45053000026614E-23,
422
                'wh' => 4.45053000026614E-23,
423
                'flb' => 3.80206452103492E-18,
424
                'BTU' => 1.51857982414846E-22,
425
                'btu' => 1.51857982414846E-22,
426
            ],
427
            'HPh' => [
428
                'J' => 2.68451741316170E+06,
429
                'e' => 2.68451612283024E+13,
430
                'c' => 6.41616438565991E+05,
431
                'cal' => 6.41186757845835E+05,
432
                'eV' => 1.67553000000000E+25,
433
                'ev' => 1.67553000000000E+25,
434
                'HPh' => 1.0,
435
                'hh' => 1.0,
436
                'Wh' => 7.45699653134593E+02,
437
                'wh' => 7.45699653134593E+02,
438
                'flb' => 6.37047316692964E+07,
439
                'BTU' => 2.54442605275546E+03,
440
                'btu' => 2.54442605275546E+03,
441
            ],
442
            'hh' => [
443
                'J' => 2.68451741316170E+06,
444
                'e' => 2.68451612283024E+13,
445
                'c' => 6.41616438565991E+05,
446
                'cal' => 6.41186757845835E+05,
447
                'eV' => 1.67553000000000E+25,
448
                'ev' => 1.67553000000000E+25,
449
                'HPh' => 1.0,
450
                'hh' => 1.0,
451
                'Wh' => 7.45699653134593E+02,
452
                'wh' => 7.45699653134593E+02,
453
                'flb' => 6.37047316692964E+07,
454
                'BTU' => 2.54442605275546E+03,
455
                'btu' => 2.54442605275546E+03,
456
            ],
457
            'Wh' => [
458
                'J' => 3.59999820554720E+03,
459
                'e' => 3.59999647518369E+10,
460
                'c' => 8.60422069219046E+02,
461
                'cal' => 8.59845857713046E+02,
462
                'eV' => 2.24692340000000E+22,
463
                'ev' => 2.24692340000000E+22,
464
                'HPh' => 1.34102248243839E-03,
465
                'hh' => 1.34102248243839E-03,
466
                'Wh' => 1.0,
467
                'wh' => 1.0,
468
                'flb' => 8.54294774062316E+04,
469
                'BTU' => 3.41213254164705E+00,
470
                'btu' => 3.41213254164705E+00,
471
            ],
472
            'wh' => [
473
                'J' => 3.59999820554720E+03,
474
                'e' => 3.59999647518369E+10,
475
                'c' => 8.60422069219046E+02,
476
                'cal' => 8.59845857713046E+02,
477
                'eV' => 2.24692340000000E+22,
478
                'ev' => 2.24692340000000E+22,
479
                'HPh' => 1.34102248243839E-03,
480
                'hh' => 1.34102248243839E-03,
481
                'Wh' => 1.0,
482
                'wh' => 1.0,
483
                'flb' => 8.54294774062316E+04,
484
                'BTU' => 3.41213254164705E+00,
485
                'btu' => 3.41213254164705E+00,
486
            ],
487
            'flb' => [
488
                'J' => 4.21400003236424E-02,
489
                'e' => 4.21399800687660E+05,
490
                'c' => 1.00717234301644E-02,
491
                'cal' => 1.00649785509554E-02,
492
                'eV' => 2.63015000000000E+17,
493
                'ev' => 2.63015000000000E+17,
494
                'HPh' => 1.56974211145130E-08,
495
                'hh' => 1.56974211145130E-08,
496
                'Wh' => 1.17055614802000E-05,
497
                'wh' => 1.17055614802000E-05,
498
                'flb' => 1.0,
499
                'BTU' => 3.99409272448406E-05,
500
                'btu' => 3.99409272448406E-05,
501
            ],
502
            'BTU' => [
503
                'J' => 1.05505813786749E+03,
504
                'e' => 1.05505763074665E+10,
505
                'c' => 2.52165488508168E+02,
506
                'cal' => 2.51996617135510E+02,
507
                'eV' => 6.58510000000000E+21,
508
                'ev' => 6.58510000000000E+21,
509
                'HPh' => 3.93015941224568E-04,
510
                'hh' => 3.93015941224568E-04,
511
                'Wh' => 2.93071851047526E-01,
512
                'wh' => 2.93071851047526E-01,
513
                'flb' => 2.50369750774671E+04,
514
                'BTU' => 1.0,
515
                'btu' => 1.0,
516
            ],
517
            'btu' => [
518
                'J' => 1.05505813786749E+03,
519
                'e' => 1.05505763074665E+10,
520
                'c' => 2.52165488508168E+02,
521
                'cal' => 2.51996617135510E+02,
522
                'eV' => 6.58510000000000E+21,
523
                'ev' => 6.58510000000000E+21,
524
                'HPh' => 3.93015941224568E-04,
525
                'hh' => 3.93015941224568E-04,
526
                'Wh' => 2.93071851047526E-01,
527
                'wh' => 2.93071851047526E-01,
528
                'flb' => 2.50369750774671E+04,
529
                'BTU' => 1.0,
530
                'btu' => 1.0,
531
            ],
532
        ],
533
        'Power' => [
534
            'HP' => [
535
                'HP' => 1.0,
536
                'h' => 1.0,
537
                'W' => 7.45701000000000E+02,
538
                'w' => 7.45701000000000E+02,
539
            ],
540
            'h' => [
541
                'HP' => 1.0,
542
                'h' => 1.0,
543
                'W' => 7.45701000000000E+02,
544
                'w' => 7.45701000000000E+02,
545
            ],
546
            'W' => [
547
                'HP' => 1.34102006031908E-03,
548
                'h' => 1.34102006031908E-03,
549
                'W' => 1.0,
550
                'w' => 1.0,
551
            ],
552
            'w' => [
553
                'HP' => 1.34102006031908E-03,
554
                'h' => 1.34102006031908E-03,
555
                'W' => 1.0,
556
                'w' => 1.0,
557
            ],
558
        ],
559
        'Magnetism' => [
560
            'T' => [
561
                'T' => 1.0,
562
                'ga' => 10000.0,
563
            ],
564
            'ga' => [
565
                'T' => 0.0001,
566
                'ga' => 1.0,
567
            ],
568
        ],
569
        'Liquid' => [
570
            'tsp' => [
571
                'tsp' => 1.0,
572
                'tbs' => 3.33333333333333E-01,
573
                'oz' => 1.66666666666667E-01,
574
                'cup' => 2.08333333333333E-02,
575
                'pt' => 1.04166666666667E-02,
576
                'us_pt' => 1.04166666666667E-02,
577
                'uk_pt' => 8.67558516821960E-03,
578
                'qt' => 5.20833333333333E-03,
579
                'gal' => 1.30208333333333E-03,
580
                'l' => 4.92999408400710E-03,
581
                'lt' => 4.92999408400710E-03,
582
            ],
583
            'tbs' => [
584
                'tsp' => 3.00000000000000E+00,
585
                'tbs' => 1.0,
586
                'oz' => 5.00000000000000E-01,
587
                'cup' => 6.25000000000000E-02,
588
                'pt' => 3.12500000000000E-02,
589
                'us_pt' => 3.12500000000000E-02,
590
                'uk_pt' => 2.60267555046588E-02,
591
                'qt' => 1.56250000000000E-02,
592
                'gal' => 3.90625000000000E-03,
593
                'l' => 1.47899822520213E-02,
594
                'lt' => 1.47899822520213E-02,
595
            ],
596
            'oz' => [
597
                'tsp' => 6.00000000000000E+00,
598
                'tbs' => 2.00000000000000E+00,
599
                'oz' => 1.0,
600
                'cup' => 1.25000000000000E-01,
601
                'pt' => 6.25000000000000E-02,
602
                'us_pt' => 6.25000000000000E-02,
603
                'uk_pt' => 5.20535110093176E-02,
604
                'qt' => 3.12500000000000E-02,
605
                'gal' => 7.81250000000000E-03,
606
                'l' => 2.95799645040426E-02,
607
                'lt' => 2.95799645040426E-02,
608
            ],
609
            'cup' => [
610
                'tsp' => 4.80000000000000E+01,
611
                'tbs' => 1.60000000000000E+01,
612
                'oz' => 8.00000000000000E+00,
613
                'cup' => 1.0,
614
                'pt' => 5.00000000000000E-01,
615
                'us_pt' => 5.00000000000000E-01,
616
                'uk_pt' => 4.16428088074541E-01,
617
                'qt' => 2.50000000000000E-01,
618
                'gal' => 6.25000000000000E-02,
619
                'l' => 2.36639716032341E-01,
620
                'lt' => 2.36639716032341E-01,
621
            ],
622
            'pt' => [
623
                'tsp' => 9.60000000000000E+01,
624
                'tbs' => 3.20000000000000E+01,
625
                'oz' => 1.60000000000000E+01,
626
                'cup' => 2.00000000000000E+00,
627
                'pt' => 1.0,
628
                'us_pt' => 1.0,
629
                'uk_pt' => 8.32856176149081E-01,
630
                'qt' => 5.00000000000000E-01,
631
                'gal' => 1.25000000000000E-01,
632
                'l' => 4.73279432064682E-01,
633
                'lt' => 4.73279432064682E-01,
634
            ],
635
            'us_pt' => [
636
                'tsp' => 9.60000000000000E+01,
637
                'tbs' => 3.20000000000000E+01,
638
                'oz' => 1.60000000000000E+01,
639
                'cup' => 2.00000000000000E+00,
640
                'pt' => 1.0,
641
                'us_pt' => 1.0,
642
                'uk_pt' => 8.32856176149081E-01,
643
                'qt' => 5.00000000000000E-01,
644
                'gal' => 1.25000000000000E-01,
645
                'l' => 4.73279432064682E-01,
646
                'lt' => 4.73279432064682E-01,
647
            ],
648
            'uk_pt' => [
649
                'tsp' => 1.15266000000000E+02,
650
                'tbs' => 3.84220000000000E+01,
651
                'oz' => 1.92110000000000E+01,
652
                'cup' => 2.40137500000000E+00,
653
                'pt' => 1.20068750000000E+00,
654
                'us_pt' => 1.20068750000000E+00,
655
                'uk_pt' => 1.0,
656
                'qt' => 6.00343750000000E-01,
657
                'gal' => 1.50085937500000E-01,
658
                'l' => 5.68260698087162E-01,
659
                'lt' => 5.68260698087162E-01,
660
            ],
661
            'qt' => [
662
                'tsp' => 1.92000000000000E+02,
663
                'tbs' => 6.40000000000000E+01,
664
                'oz' => 3.20000000000000E+01,
665
                'cup' => 4.00000000000000E+00,
666
                'pt' => 2.00000000000000E+00,
667
                'us_pt' => 2.00000000000000E+00,
668
                'uk_pt' => 1.66571235229816E+00,
669
                'qt' => 1.0,
670
                'gal' => 2.50000000000000E-01,
671
                'l' => 9.46558864129363E-01,
672
                'lt' => 9.46558864129363E-01,
673
            ],
674
            'gal' => [
675
                'tsp' => 7.68000000000000E+02,
676
                'tbs' => 2.56000000000000E+02,
677
                'oz' => 1.28000000000000E+02,
678
                'cup' => 1.60000000000000E+01,
679
                'pt' => 8.00000000000000E+00,
680
                'us_pt' => 8.00000000000000E+00,
681
                'uk_pt' => 6.66284940919265E+00,
682
                'qt' => 4.00000000000000E+00,
683
                'gal' => 1.0,
684
                'l' => 3.78623545651745E+00,
685
                'lt' => 3.78623545651745E+00,
686
            ],
687
            'l' => [
688
                'tsp' => 2.02840000000000E+02,
689
                'tbs' => 6.76133333333333E+01,
690
                'oz' => 3.38066666666667E+01,
691
                'cup' => 4.22583333333333E+00,
692
                'pt' => 2.11291666666667E+00,
693
                'us_pt' => 2.11291666666667E+00,
694
                'uk_pt' => 1.75975569552166E+00,
695
                'qt' => 1.05645833333333E+00,
696
                'gal' => 2.64114583333333E-01,
697
                'l' => 1.0,
698
                'lt' => 1.0,
699
            ],
700
            'lt' => [
701
                'tsp' => 2.02840000000000E+02,
702
                'tbs' => 6.76133333333333E+01,
703
                'oz' => 3.38066666666667E+01,
704
                'cup' => 4.22583333333333E+00,
705
                'pt' => 2.11291666666667E+00,
706
                'us_pt' => 2.11291666666667E+00,
707
                'uk_pt' => 1.75975569552166E+00,
708
                'qt' => 1.05645833333333E+00,
709
                'gal' => 2.64114583333333E-01,
710
                'l' => 1.0,
711
                'lt' => 1.0,
712
            ],
713
        ],
714
    ];
715
716
    /**
717
     * parseComplex.
718
     *
719
     * Parses a complex number into its real and imaginary parts, and an I or J suffix
720
     *
721
     * @param string $complexNumber The complex number
722
     *
723
     * @return string[] Indexed on "real", "imaginary" and "suffix"
724
     */
725 354
    public static function parseComplex($complexNumber)
726
    {
727 354
        $workString = (string) $complexNumber;
728
729 354
        $realNumber = $imaginary = 0;
0 ignored issues
show
Unused Code introduced by
The assignment to $realNumber is dead and can be removed.
Loading history...
730
        //    Extract the suffix, if there is one
731 354
        $suffix = substr($workString, -1);
732 354
        if (!is_numeric($suffix)) {
733 289
            $workString = substr($workString, 0, -1);
734
        } else {
735 142
            $suffix = '';
736
        }
737
738
        //    Split the input into its Real and Imaginary components
739 354
        $leadingSign = 0;
740 354
        if (strlen($workString) > 0) {
741 345
            $leadingSign = (($workString[0] == '+') || ($workString[0] == '-')) ? 1 : 0;
742
        }
743 354
        $power = '';
744 354
        $realNumber = strtok($workString, '+-');
745 354
        if (strtoupper(substr($realNumber, -1)) == 'E') {
746 8
            $power = strtok('+-');
747 8
            ++$leadingSign;
748
        }
749
750 354
        $realNumber = substr($workString, 0, strlen($realNumber) + strlen($power) + $leadingSign);
751
752 354
        if ($suffix != '') {
753 289
            $imaginary = substr($workString, strlen($realNumber));
754
755 289
            if (($imaginary == '') && (($realNumber == '') || ($realNumber == '+') || ($realNumber == '-'))) {
756 24
                $imaginary = $realNumber . '1';
757 24
                $realNumber = '0';
758 269
            } elseif ($imaginary == '') {
759 30
                $imaginary = $realNumber;
760 30
                $realNumber = '0';
761 243
            } elseif (($imaginary == '+') || ($imaginary == '-')) {
762 96
                $imaginary .= '1';
763
            }
764
        }
765
766
        return [
767 354
            'real' => $realNumber,
768 354
            'imaginary' => $imaginary,
769 354
            'suffix' => $suffix,
770
        ];
771
    }
772
773
    /**
774
     * Cleans the leading characters in a complex number string.
775
     *
776
     * @param string $complexNumber The complex number to clean
777
     *
778
     * @return string The "cleaned" complex number
779
     */
780 38
    private static function cleanComplex($complexNumber)
781
    {
782 38
        if ($complexNumber[0] == '+') {
783
            $complexNumber = substr($complexNumber, 1);
784
        }
785 38
        if ($complexNumber[0] == '0') {
786 4
            $complexNumber = substr($complexNumber, 1);
787
        }
788 38
        if ($complexNumber[0] == '.') {
789 4
            $complexNumber = '0' . $complexNumber;
790
        }
791 38
        if ($complexNumber[0] == '+') {
792
            $complexNumber = substr($complexNumber, 1);
793
        }
794
795 38
        return $complexNumber;
796
    }
797
798
    /**
799
     * Formats a number base string value with leading zeroes.
800
     *
801
     * @param string $xVal The "number" to pad
802
     * @param int $places The length that we want to pad this value
803
     *
804
     * @return string The padded "number"
805
     */
806 82
    private static function nbrConversionFormat($xVal, $places)
807
    {
808 82
        if ($places !== null) {
0 ignored issues
show
introduced by
The condition $places !== null is always true.
Loading history...
809 22
            if (is_numeric($places)) {
0 ignored issues
show
introduced by
The condition is_numeric($places) is always true.
Loading history...
810 18
                $places = (int) $places;
811
            } else {
812 4
                return Functions::VALUE();
813
            }
814 18
            if ($places < 0) {
815 4
                return Functions::NAN();
816
            }
817 14
            if (strlen($xVal) <= $places) {
818 14
                return substr(str_pad($xVal, $places, '0', STR_PAD_LEFT), -10);
819
            }
820
821
            return Functions::NAN();
822
        }
823
824 60
        return substr($xVal, -10);
825
    }
826
827
    /**
828
     * BESSELI.
829
     *
830
     *    Returns the modified Bessel function In(x), which is equivalent to the Bessel function evaluated
831
     *        for purely imaginary arguments
832
     *
833
     *    Excel Function:
834
     *        BESSELI(x,ord)
835
     *
836
     * @category Engineering Functions
837
     *
838
     * @param float $x The value at which to evaluate the function.
839
     *                                If x is nonnumeric, BESSELI returns the #VALUE! error value.
840
     * @param int $ord The order of the Bessel function.
841
     *                                If ord is not an integer, it is truncated.
842
     *                                If $ord is nonnumeric, BESSELI returns the #VALUE! error value.
843
     *                                If $ord < 0, BESSELI returns the #NUM! error value.
844
     *
845
     * @return float
846
     */
847 74
    public static function BESSELI($x, $ord)
848
    {
849 74
        $x = ($x === null) ? 0.0 : Functions::flattenSingleValue($x);
0 ignored issues
show
introduced by
The condition $x === null is always false.
Loading history...
850 74
        $ord = ($ord === null) ? 0.0 : Functions::flattenSingleValue($ord);
0 ignored issues
show
introduced by
The condition $ord === null is always false.
Loading history...
851
852 74
        if ((is_numeric($x)) && (is_numeric($ord))) {
853 70
            $ord = floor($ord);
854 70
            if ($ord < 0) {
855 2
                return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type double.
Loading history...
856
            }
857
858 68
            if (abs($x) <= 30) {
859 68
                $fResult = $fTerm = pow($x / 2, $ord) / MathTrig::FACT($ord);
860 68
                $ordK = 1;
861 68
                $fSqrX = ($x * $x) / 4;
862
                do {
863 68
                    $fTerm *= $fSqrX;
864 68
                    $fTerm /= ($ordK * ($ordK + $ord));
865 68
                    $fResult += $fTerm;
866 68
                } while ((abs($fTerm) > 1e-12) && (++$ordK < 100));
867
            } else {
868
                $f_2_PI = 2 * M_PI;
869
870
                $fXAbs = abs($x);
871
                $fResult = exp($fXAbs) / sqrt($f_2_PI * $fXAbs);
872
                if (($ord & 1) && ($x < 0)) {
873
                    $fResult = -$fResult;
874
                }
875
            }
876
877 68
            return (is_nan($fResult)) ? Functions::NAN() : $fResult;
0 ignored issues
show
Bug Best Practice introduced by
The expression return is_nan($fResult) ...tions::NAN() : $fResult also could return the type string which is incompatible with the documented return type double.
Loading history...
878
        }
879
880 4
        return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type double.
Loading history...
881
    }
882
883
    /**
884
     * BESSELJ.
885
     *
886
     *    Returns the Bessel function
887
     *
888
     *    Excel Function:
889
     *        BESSELJ(x,ord)
890
     *
891
     * @category Engineering Functions
892
     *
893
     * @param float $x The value at which to evaluate the function.
894
     *                                If x is nonnumeric, BESSELJ returns the #VALUE! error value.
895
     * @param int $ord The order of the Bessel function. If n is not an integer, it is truncated.
896
     *                                If $ord is nonnumeric, BESSELJ returns the #VALUE! error value.
897
     *                                If $ord < 0, BESSELJ returns the #NUM! error value.
898
     *
899
     * @return float
900
     */
901 50
    public static function BESSELJ($x, $ord)
902
    {
903 50
        $x = ($x === null) ? 0.0 : Functions::flattenSingleValue($x);
0 ignored issues
show
introduced by
The condition $x === null is always false.
Loading history...
904 50
        $ord = ($ord === null) ? 0.0 : Functions::flattenSingleValue($ord);
0 ignored issues
show
introduced by
The condition $ord === null is always false.
Loading history...
905
906 50
        if ((is_numeric($x)) && (is_numeric($ord))) {
907 48
            $ord = floor($ord);
908 48
            if ($ord < 0) {
909 1
                return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type double.
Loading history...
910
            }
911
912 47
            $fResult = 0;
913 47
            if (abs($x) <= 30) {
914 47
                $fResult = $fTerm = pow($x / 2, $ord) / MathTrig::FACT($ord);
915 47
                $ordK = 1;
916 47
                $fSqrX = ($x * $x) / -4;
917
                do {
918 47
                    $fTerm *= $fSqrX;
919 47
                    $fTerm /= ($ordK * ($ordK + $ord));
920 47
                    $fResult += $fTerm;
921 47
                } while ((abs($fTerm) > 1e-12) && (++$ordK < 100));
922
            } else {
923
                $f_PI_DIV_2 = M_PI / 2;
924
                $f_PI_DIV_4 = M_PI / 4;
925
926
                $fXAbs = abs($x);
927
                $fResult = sqrt(Functions::M_2DIVPI / $fXAbs) * cos($fXAbs - $ord * $f_PI_DIV_2 - $f_PI_DIV_4);
928
                if (($ord & 1) && ($x < 0)) {
929
                    $fResult = -$fResult;
930
                }
931
            }
932
933 47
            return (is_nan($fResult)) ? Functions::NAN() : $fResult;
0 ignored issues
show
Bug Best Practice introduced by
The expression return is_nan($fResult) ...tions::NAN() : $fResult also could return the type string which is incompatible with the documented return type double.
Loading history...
934
        }
935
936 2
        return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type double.
Loading history...
937
    }
938
939 22
    private static function besselK0($fNum)
940
    {
941 22
        if ($fNum <= 2) {
942 14
            $fNum2 = $fNum * 0.5;
943 14
            $y = ($fNum2 * $fNum2);
944 14
            $fRet = -log($fNum2) * self::BESSELI($fNum, 0) +
945
                (-0.57721566 + $y * (0.42278420 + $y * (0.23069756 + $y * (0.3488590e-1 + $y * (0.262698e-2 + $y *
946 14
                                    (0.10750e-3 + $y * 0.74e-5))))));
947
        } else {
948 8
            $y = 2 / $fNum;
949 8
            $fRet = exp(-$fNum) / sqrt($fNum) *
950
                (1.25331414 + $y * (-0.7832358e-1 + $y * (0.2189568e-1 + $y * (-0.1062446e-1 + $y *
951 8
                                (0.587872e-2 + $y * (-0.251540e-2 + $y * 0.53208e-3))))));
952
        }
953
954 22
        return $fRet;
955
    }
956
957 30
    private static function besselK1($fNum)
958
    {
959 30
        if ($fNum <= 2) {
960 17
            $fNum2 = $fNum * 0.5;
961 17
            $y = ($fNum2 * $fNum2);
962 17
            $fRet = log($fNum2) * self::BESSELI($fNum, 1) +
963
                (1 + $y * (0.15443144 + $y * (-0.67278579 + $y * (-0.18156897 + $y * (-0.1919402e-1 + $y *
964 17
                                    (-0.110404e-2 + $y * (-0.4686e-4))))))) / $fNum;
965
        } else {
966 13
            $y = 2 / $fNum;
967 13
            $fRet = exp(-$fNum) / sqrt($fNum) *
968
                (1.25331414 + $y * (0.23498619 + $y * (-0.3655620e-1 + $y * (0.1504268e-1 + $y * (-0.780353e-2 + $y *
969 13
                                    (0.325614e-2 + $y * (-0.68245e-3)))))));
970
        }
971
972 30
        return $fRet;
973
    }
974
975
    /**
976
     * BESSELK.
977
     *
978
     *    Returns the modified Bessel function Kn(x), which is equivalent to the Bessel functions evaluated
979
     *        for purely imaginary arguments.
980
     *
981
     *    Excel Function:
982
     *        BESSELK(x,ord)
983
     *
984
     * @category Engineering Functions
985
     *
986
     * @param float $x The value at which to evaluate the function.
987
     *                                If x is nonnumeric, BESSELK returns the #VALUE! error value.
988
     * @param int $ord The order of the Bessel function. If n is not an integer, it is truncated.
989
     *                                If $ord is nonnumeric, BESSELK returns the #VALUE! error value.
990
     *                                If $ord < 0, BESSELK returns the #NUM! error value.
991
     *
992
     * @return float
993
     */
994 38
    public static function BESSELK($x, $ord)
995
    {
996 38
        $x = ($x === null) ? 0.0 : Functions::flattenSingleValue($x);
0 ignored issues
show
introduced by
The condition $x === null is always false.
Loading history...
997 38
        $ord = ($ord === null) ? 0.0 : Functions::flattenSingleValue($ord);
0 ignored issues
show
introduced by
The condition $ord === null is always false.
Loading history...
998
999 38
        if ((is_numeric($x)) && (is_numeric($ord))) {
1000 36
            if (($ord < 0) || ($x == 0.0)) {
1001 4
                return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type double.
Loading history...
1002
            }
1003
1004 32
            switch (floor($ord)) {
1005 32
                case 0:
1006 2
                    $fBk = self::besselK0($x);
1007
1008 2
                    break;
1009 30
                case 1:
1010 10
                    $fBk = self::besselK1($x);
1011
1012 10
                    break;
1013
                default:
1014 20
                    $fTox = 2 / $x;
1015 20
                    $fBkm = self::besselK0($x);
1016 20
                    $fBk = self::besselK1($x);
1017 20
                    for ($n = 1; $n < $ord; ++$n) {
1018 20
                        $fBkp = $fBkm + $n * $fTox * $fBk;
1019 20
                        $fBkm = $fBk;
1020 20
                        $fBk = $fBkp;
1021
                    }
1022
            }
1023
1024 32
            return (is_nan($fBk)) ? Functions::NAN() : $fBk;
0 ignored issues
show
Bug Best Practice introduced by
The expression return is_nan($fBk) ? Ph...Functions::NAN() : $fBk also could return the type string which is incompatible with the documented return type double.
Loading history...
1025
        }
1026
1027 2
        return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type double.
Loading history...
1028
    }
1029
1030 11
    private static function besselY0($fNum)
1031
    {
1032 11
        if ($fNum < 8.0) {
1033 10
            $y = ($fNum * $fNum);
1034 10
            $f1 = -2957821389.0 + $y * (7062834065.0 + $y * (-512359803.6 + $y * (10879881.29 + $y * (-86327.92757 + $y * 228.4622733))));
1035 10
            $f2 = 40076544269.0 + $y * (745249964.8 + $y * (7189466.438 + $y * (47447.26470 + $y * (226.1030244 + $y))));
1036 10
            $fRet = $f1 / $f2 + 0.636619772 * self::BESSELJ($fNum, 0) * log($fNum);
1037
        } else {
1038 1
            $z = 8.0 / $fNum;
1039 1
            $y = ($z * $z);
1040 1
            $xx = $fNum - 0.785398164;
1041 1
            $f1 = 1 + $y * (-0.1098628627e-2 + $y * (0.2734510407e-4 + $y * (-0.2073370639e-5 + $y * 0.2093887211e-6)));
1042 1
            $f2 = -0.1562499995e-1 + $y * (0.1430488765e-3 + $y * (-0.6911147651e-5 + $y * (0.7621095161e-6 + $y * (-0.934945152e-7))));
1043 1
            $fRet = sqrt(0.636619772 / $fNum) * (sin($xx) * $f1 + $z * cos($xx) * $f2);
1044
        }
1045
1046 11
        return $fRet;
1047
    }
1048
1049 16
    private static function besselY1($fNum)
1050
    {
1051 16
        if ($fNum < 8.0) {
1052 16
            $y = ($fNum * $fNum);
1053
            $f1 = $fNum * (-0.4900604943e13 + $y * (0.1275274390e13 + $y * (-0.5153438139e11 + $y * (0.7349264551e9 + $y *
1054 16
                                (-0.4237922726e7 + $y * 0.8511937935e4)))));
1055
            $f2 = 0.2499580570e14 + $y * (0.4244419664e12 + $y * (0.3733650367e10 + $y * (0.2245904002e8 + $y *
1056 16
                            (0.1020426050e6 + $y * (0.3549632885e3 + $y)))));
1057 16
            $fRet = $f1 / $f2 + 0.636619772 * (self::BESSELJ($fNum, 1) * log($fNum) - 1 / $fNum);
1058
        } else {
1059
            $fRet = sqrt(0.636619772 / $fNum) * sin($fNum - 2.356194491);
1060
        }
1061
1062 16
        return $fRet;
1063
    }
1064
1065
    /**
1066
     * BESSELY.
1067
     *
1068
     * Returns the Bessel function, which is also called the Weber function or the Neumann function.
1069
     *
1070
     *    Excel Function:
1071
     *        BESSELY(x,ord)
1072
     *
1073
     * @category Engineering Functions
1074
     *
1075
     * @param float $x The value at which to evaluate the function.
1076
     *                                If x is nonnumeric, BESSELK returns the #VALUE! error value.
1077
     * @param int $ord The order of the Bessel function. If n is not an integer, it is truncated.
1078
     *                                If $ord is nonnumeric, BESSELK returns the #VALUE! error value.
1079
     *                                If $ord < 0, BESSELK returns the #NUM! error value.
1080
     *
1081
     * @return float
1082
     */
1083 23
    public static function BESSELY($x, $ord)
1084
    {
1085 23
        $x = ($x === null) ? 0.0 : Functions::flattenSingleValue($x);
0 ignored issues
show
introduced by
The condition $x === null is always false.
Loading history...
1086 23
        $ord = ($ord === null) ? 0.0 : Functions::flattenSingleValue($ord);
0 ignored issues
show
introduced by
The condition $ord === null is always false.
Loading history...
1087
1088 23
        if ((is_numeric($x)) && (is_numeric($ord))) {
1089 21
            if (($ord < 0) || ($x == 0.0)) {
1090 3
                return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type double.
Loading history...
1091
            }
1092
1093 18
            switch (floor($ord)) {
1094 18
                case 0:
1095 2
                    $fBy = self::besselY0($x);
1096
1097 2
                    break;
1098 16
                case 1:
1099 7
                    $fBy = self::besselY1($x);
1100
1101 7
                    break;
1102
                default:
1103 9
                    $fTox = 2 / $x;
1104 9
                    $fBym = self::besselY0($x);
1105 9
                    $fBy = self::besselY1($x);
1106 9
                    for ($n = 1; $n < $ord; ++$n) {
1107 9
                        $fByp = $n * $fTox * $fBy - $fBym;
1108 9
                        $fBym = $fBy;
1109 9
                        $fBy = $fByp;
1110
                    }
1111
            }
1112
1113 18
            return (is_nan($fBy)) ? Functions::NAN() : $fBy;
0 ignored issues
show
Bug Best Practice introduced by
The expression return is_nan($fBy) ? Ph...Functions::NAN() : $fBy also could return the type string which is incompatible with the documented return type double.
Loading history...
1114
        }
1115
1116 2
        return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type double.
Loading history...
1117
    }
1118
1119
    /**
1120
     * BINTODEC.
1121
     *
1122
     * Return a binary value as decimal.
1123
     *
1124
     * Excel Function:
1125
     *        BIN2DEC(x)
1126
     *
1127
     * @category Engineering Functions
1128
     *
1129
     * @param string $x The binary number (as a string) that you want to convert. The number
1130
     *                                cannot contain more than 10 characters (10 bits). The most significant
1131
     *                                bit of number is the sign bit. The remaining 9 bits are magnitude bits.
1132
     *                                Negative numbers are represented using two's-complement notation.
1133
     *                                If number is not a valid binary number, or if number contains more than
1134
     *                                10 characters (10 bits), BIN2DEC returns the #NUM! error value.
1135
     *
1136
     * @return string
1137
     */
1138 10
    public static function BINTODEC($x)
1139
    {
1140 10
        $x = Functions::flattenSingleValue($x);
1141
1142 10
        if (is_bool($x)) {
1143 1
            if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
1144
                $x = (int) $x;
1145
            } else {
1146 1
                return Functions::VALUE();
1147
            }
1148
        }
1149 9
        if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
1150
            $x = floor($x);
1 ignored issue
show
Bug introduced by
It seems like $x can also be of type string; however, parameter $value of floor() does only seem to accept double, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1150
            $x = floor(/** @scrutinizer ignore-type */ $x);
Loading history...
1151
        }
1152 9
        $x = (string) $x;
1153 9
        if (strlen($x) > preg_match_all('/[01]/', $x, $out)) {
1154 1
            return Functions::NAN();
1155
        }
1156 8
        if (strlen($x) > 10) {
1157 1
            return Functions::NAN();
1158 7
        } elseif (strlen($x) == 10) {
1159
            //    Two's Complement
1160 2
            $x = substr($x, -9);
1161
1162 2
            return '-' . (512 - bindec($x));
1163
        }
1164
1165 5
        return bindec($x);
1166
    }
1167
1168
    /**
1169
     * BINTOHEX.
1170
     *
1171
     * Return a binary value as hex.
1172
     *
1173
     * Excel Function:
1174
     *        BIN2HEX(x[,places])
1175
     *
1176
     * @category Engineering Functions
1177
     *
1178
     * @param string $x The binary number (as a string) that you want to convert. The number
1179
     *                                cannot contain more than 10 characters (10 bits). The most significant
1180
     *                                bit of number is the sign bit. The remaining 9 bits are magnitude bits.
1181
     *                                Negative numbers are represented using two's-complement notation.
1182
     *                                If number is not a valid binary number, or if number contains more than
1183
     *                                10 characters (10 bits), BIN2HEX returns the #NUM! error value.
1184
     * @param int $places The number of characters to use. If places is omitted, BIN2HEX uses the
1185
     *                                minimum number of characters necessary. Places is useful for padding the
1186
     *                                return value with leading 0s (zeros).
1187
     *                                If places is not an integer, it is truncated.
1188
     *                                If places is nonnumeric, BIN2HEX returns the #VALUE! error value.
1189
     *                                If places is negative, BIN2HEX returns the #NUM! error value.
1190
     *
1191
     * @return string
1192
     */
1193 14
    public static function BINTOHEX($x, $places = null)
1194
    {
1195 14
        $x = Functions::flattenSingleValue($x);
1196 14
        $places = Functions::flattenSingleValue($places);
1197
1198
        // Argument X
1199 14
        if (is_bool($x)) {
1200 1
            if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
1201
                $x = (int) $x;
1202
            } else {
1203 1
                return Functions::VALUE();
1204
            }
1205
        }
1206 13
        if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
1207
            $x = floor($x);
1 ignored issue
show
Bug introduced by
It seems like $x can also be of type string; however, parameter $value of floor() does only seem to accept double, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1207
            $x = floor(/** @scrutinizer ignore-type */ $x);
Loading history...
1208
        }
1209 13
        $x = (string) $x;
1210 13
        if (strlen($x) > preg_match_all('/[01]/', $x, $out)) {
1211 1
            return Functions::NAN();
1212
        }
1213 12
        if (strlen($x) > 10) {
1214 1
            return Functions::NAN();
1215 11
        } elseif (strlen($x) == 10) {
1216
            //    Two's Complement
1217 2
            return str_repeat('F', 8) . substr(strtoupper(dechex(bindec(substr($x, -9)))), -2);
1 ignored issue
show
Bug introduced by
It seems like bindec(substr($x, -9)) can also be of type double; however, parameter $number of dechex() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1217
            return str_repeat('F', 8) . substr(strtoupper(dechex(/** @scrutinizer ignore-type */ bindec(substr($x, -9)))), -2);
Loading history...
1218
        }
1219 9
        $hexVal = (string) strtoupper(dechex(bindec($x)));
1220
1221 9
        return self::nbrConversionFormat($hexVal, $places);
1222
    }
1223
1224
    /**
1225
     * BINTOOCT.
1226
     *
1227
     * Return a binary value as octal.
1228
     *
1229
     * Excel Function:
1230
     *        BIN2OCT(x[,places])
1231
     *
1232
     * @category Engineering Functions
1233
     *
1234
     * @param string $x The binary number (as a string) that you want to convert. The number
1235
     *                                cannot contain more than 10 characters (10 bits). The most significant
1236
     *                                bit of number is the sign bit. The remaining 9 bits are magnitude bits.
1237
     *                                Negative numbers are represented using two's-complement notation.
1238
     *                                If number is not a valid binary number, or if number contains more than
1239
     *                                10 characters (10 bits), BIN2OCT returns the #NUM! error value.
1240
     * @param int $places The number of characters to use. If places is omitted, BIN2OCT uses the
1241
     *                                minimum number of characters necessary. Places is useful for padding the
1242
     *                                return value with leading 0s (zeros).
1243
     *                                If places is not an integer, it is truncated.
1244
     *                                If places is nonnumeric, BIN2OCT returns the #VALUE! error value.
1245
     *                                If places is negative, BIN2OCT returns the #NUM! error value.
1246
     *
1247
     * @return string
1248
     */
1249 15
    public static function BINTOOCT($x, $places = null)
1250
    {
1251 15
        $x = Functions::flattenSingleValue($x);
1252 15
        $places = Functions::flattenSingleValue($places);
1253
1254 15
        if (is_bool($x)) {
1255 1
            if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
1256
                $x = (int) $x;
1257
            } else {
1258 1
                return Functions::VALUE();
1259
            }
1260
        }
1261 14
        if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
1262
            $x = floor($x);
1 ignored issue
show
Bug introduced by
It seems like $x can also be of type string; however, parameter $value of floor() does only seem to accept double, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1262
            $x = floor(/** @scrutinizer ignore-type */ $x);
Loading history...
1263
        }
1264 14
        $x = (string) $x;
1265 14
        if (strlen($x) > preg_match_all('/[01]/', $x, $out)) {
1266 1
            return Functions::NAN();
1267
        }
1268 13
        if (strlen($x) > 10) {
1269 1
            return Functions::NAN();
1270 12
        } elseif (strlen($x) == 10) {
1271
            //    Two's Complement
1272 2
            return str_repeat('7', 7) . substr(strtoupper(decoct(bindec(substr($x, -9)))), -3);
1 ignored issue
show
Bug introduced by
It seems like bindec(substr($x, -9)) can also be of type double; however, parameter $number of decoct() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1272
            return str_repeat('7', 7) . substr(strtoupper(decoct(/** @scrutinizer ignore-type */ bindec(substr($x, -9)))), -3);
Loading history...
1273
        }
1274 10
        $octVal = (string) decoct(bindec($x));
1275
1276 10
        return self::nbrConversionFormat($octVal, $places);
1277
    }
1278
1279
    /**
1280
     * DECTOBIN.
1281
     *
1282
     * Return a decimal value as binary.
1283
     *
1284
     * Excel Function:
1285
     *        DEC2BIN(x[,places])
1286
     *
1287
     * @category Engineering Functions
1288
     *
1289
     * @param string $x The decimal integer you want to convert. If number is negative,
1290
     *                                valid place values are ignored and DEC2BIN returns a 10-character
1291
     *                                (10-bit) binary number in which the most significant bit is the sign
1292
     *                                bit. The remaining 9 bits are magnitude bits. Negative numbers are
1293
     *                                represented using two's-complement notation.
1294
     *                                If number < -512 or if number > 511, DEC2BIN returns the #NUM! error
1295
     *                                value.
1296
     *                                If number is nonnumeric, DEC2BIN returns the #VALUE! error value.
1297
     *                                If DEC2BIN requires more than places characters, it returns the #NUM!
1298
     *                                error value.
1299
     * @param int $places The number of characters to use. If places is omitted, DEC2BIN uses
1300
     *                                the minimum number of characters necessary. Places is useful for
1301
     *                                padding the return value with leading 0s (zeros).
1302
     *                                If places is not an integer, it is truncated.
1303
     *                                If places is nonnumeric, DEC2BIN returns the #VALUE! error value.
1304
     *                                If places is zero or negative, DEC2BIN returns the #NUM! error value.
1305
     *
1306
     * @return string
1307
     */
1308 41
    public static function DECTOBIN($x, $places = null)
1309
    {
1310 41
        $x = Functions::flattenSingleValue($x);
1311 41
        $places = Functions::flattenSingleValue($places);
1312
1313 41
        if (is_bool($x)) {
1314 1
            if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
1315
                $x = (int) $x;
1316
            } else {
1317 1
                return Functions::VALUE();
1318
            }
1319
        }
1320 40
        $x = (string) $x;
1321 40
        if (strlen($x) > preg_match_all('/[-0123456789.]/', $x, $out)) {
1322 1
            return Functions::VALUE();
1323
        }
1324
1325 39
        $x = (string) floor($x);
0 ignored issues
show
Bug introduced by
$x of type string is incompatible with the type double expected by parameter $value of floor(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1325
        $x = (string) floor(/** @scrutinizer ignore-type */ $x);
Loading history...
1326 39
        if ($x < -512 || $x > 511) {
1327 13
            return Functions::NAN();
1328
        }
1329
1330 26
        $r = decbin($x);
0 ignored issues
show
Bug introduced by
$x of type string is incompatible with the type integer expected by parameter $number of decbin(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1330
        $r = decbin(/** @scrutinizer ignore-type */ $x);
Loading history...
1331
        // Two's Complement
1332 26
        $r = substr($r, -10);
1333 26
        if (strlen($r) >= 11) {
1334
            return Functions::NAN();
1335
        }
1336
1337 26
        return self::nbrConversionFormat($r, $places);
1338
    }
1339
1340
    /**
1341
     * DECTOHEX.
1342
     *
1343
     * Return a decimal value as hex.
1344
     *
1345
     * Excel Function:
1346
     *        DEC2HEX(x[,places])
1347
     *
1348
     * @category Engineering Functions
1349
     *
1350
     * @param string $x The decimal integer you want to convert. If number is negative,
1351
     *                                places is ignored and DEC2HEX returns a 10-character (40-bit)
1352
     *                                hexadecimal number in which the most significant bit is the sign
1353
     *                                bit. The remaining 39 bits are magnitude bits. Negative numbers
1354
     *                                are represented using two's-complement notation.
1355
     *                                If number < -549,755,813,888 or if number > 549,755,813,887,
1356
     *                                DEC2HEX returns the #NUM! error value.
1357
     *                                If number is nonnumeric, DEC2HEX returns the #VALUE! error value.
1358
     *                                If DEC2HEX requires more than places characters, it returns the
1359
     *                                #NUM! error value.
1360
     * @param int $places The number of characters to use. If places is omitted, DEC2HEX uses
1361
     *                                the minimum number of characters necessary. Places is useful for
1362
     *                                padding the return value with leading 0s (zeros).
1363
     *                                If places is not an integer, it is truncated.
1364
     *                                If places is nonnumeric, DEC2HEX returns the #VALUE! error value.
1365
     *                                If places is zero or negative, DEC2HEX returns the #NUM! error value.
1366
     *
1367
     * @return string
1368
     */
1369 15
    public static function DECTOHEX($x, $places = null)
1370
    {
1371 15
        $x = Functions::flattenSingleValue($x);
1372 15
        $places = Functions::flattenSingleValue($places);
1373
1374 15
        if (is_bool($x)) {
1375 1
            if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
1376
                $x = (int) $x;
1377
            } else {
1378 1
                return Functions::VALUE();
1379
            }
1380
        }
1381 14
        $x = (string) $x;
1382 14
        if (strlen($x) > preg_match_all('/[-0123456789.]/', $x, $out)) {
1383 1
            return Functions::VALUE();
1384
        }
1385 13
        $x = (string) floor($x);
0 ignored issues
show
Bug introduced by
$x of type string is incompatible with the type double expected by parameter $value of floor(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1385
        $x = (string) floor(/** @scrutinizer ignore-type */ $x);
Loading history...
1386 13
        $r = strtoupper(dechex($x));
0 ignored issues
show
Bug introduced by
$x of type string is incompatible with the type integer expected by parameter $number of dechex(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1386
        $r = strtoupper(dechex(/** @scrutinizer ignore-type */ $x));
Loading history...
1387 13
        if (strlen($r) == 8) {
1388
            //    Two's Complement
1389
            $r = 'FF' . $r;
1390
        }
1391
1392 13
        return self::nbrConversionFormat($r, $places);
1393
    }
1394
1395
    /**
1396
     * DECTOOCT.
1397
     *
1398
     * Return an decimal value as octal.
1399
     *
1400
     * Excel Function:
1401
     *        DEC2OCT(x[,places])
1402
     *
1403
     * @category Engineering Functions
1404
     *
1405
     * @param string $x The decimal integer you want to convert. If number is negative,
1406
     *                                places is ignored and DEC2OCT returns a 10-character (30-bit)
1407
     *                                octal number in which the most significant bit is the sign bit.
1408
     *                                The remaining 29 bits are magnitude bits. Negative numbers are
1409
     *                                represented using two's-complement notation.
1410
     *                                If number < -536,870,912 or if number > 536,870,911, DEC2OCT
1411
     *                                returns the #NUM! error value.
1412
     *                                If number is nonnumeric, DEC2OCT returns the #VALUE! error value.
1413
     *                                If DEC2OCT requires more than places characters, it returns the
1414
     *                                #NUM! error value.
1415
     * @param int $places The number of characters to use. If places is omitted, DEC2OCT uses
1416
     *                                the minimum number of characters necessary. Places is useful for
1417
     *                                padding the return value with leading 0s (zeros).
1418
     *                                If places is not an integer, it is truncated.
1419
     *                                If places is nonnumeric, DEC2OCT returns the #VALUE! error value.
1420
     *                                If places is zero or negative, DEC2OCT returns the #NUM! error value.
1421
     *
1422
     * @return string
1423
     */
1424 20
    public static function DECTOOCT($x, $places = null)
1425
    {
1426 20
        $xorig = $x;
0 ignored issues
show
Unused Code introduced by
The assignment to $xorig is dead and can be removed.
Loading history...
1427 20
        $x = Functions::flattenSingleValue($x);
1428 20
        $places = Functions::flattenSingleValue($places);
1429
1430 20
        if (is_bool($x)) {
1431 1
            if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
1432
                $x = (int) $x;
1433
            } else {
1434 1
                return Functions::VALUE();
1435
            }
1436
        }
1437 19
        $x = (string) $x;
1438 19
        if (strlen($x) > preg_match_all('/[-0123456789.]/', $x, $out)) {
1439 1
            return Functions::VALUE();
1440
        }
1441 18
        $x = (string) floor($x);
0 ignored issues
show
Bug introduced by
$x of type string is incompatible with the type double expected by parameter $value of floor(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1441
        $x = (string) floor(/** @scrutinizer ignore-type */ $x);
Loading history...
1442 18
        $r = decoct($x);
0 ignored issues
show
Bug introduced by
$x of type string is incompatible with the type integer expected by parameter $number of decoct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1442
        $r = decoct(/** @scrutinizer ignore-type */ $x);
Loading history...
1443 18
        if (strlen($r) == 11) {
1444
            //    Two's Complement
1445
            $r = substr($r, -10);
1446
        }
1447
1448 18
        return self::nbrConversionFormat($r, $places);
1449
    }
1450
1451
    /**
1452
     * HEXTOBIN.
1453
     *
1454
     * Return a hex value as binary.
1455
     *
1456
     * Excel Function:
1457
     *        HEX2BIN(x[,places])
1458
     *
1459
     * @category Engineering Functions
1460
     *
1461
     * @param string $x the hexadecimal number you want to convert.
1462
     *                  Number cannot contain more than 10 characters.
1463
     *                  The most significant bit of number is the sign bit (40th bit from the right).
1464
     *                  The remaining 9 bits are magnitude bits.
1465
     *                  Negative numbers are represented using two's-complement notation.
1466
     *                  If number is negative, HEX2BIN ignores places and returns a 10-character binary number.
1467
     *                  If number is negative, it cannot be less than FFFFFFFE00,
1468
     *                      and if number is positive, it cannot be greater than 1FF.
1469
     *                  If number is not a valid hexadecimal number, HEX2BIN returns the #NUM! error value.
1470
     *                  If HEX2BIN requires more than places characters, it returns the #NUM! error value.
1471
     * @param int $places The number of characters to use. If places is omitted,
1472
     *                                    HEX2BIN uses the minimum number of characters necessary. Places
1473
     *                                    is useful for padding the return value with leading 0s (zeros).
1474
     *                                    If places is not an integer, it is truncated.
1475
     *                                    If places is nonnumeric, HEX2BIN returns the #VALUE! error value.
1476
     *                                    If places is negative, HEX2BIN returns the #NUM! error value.
1477
     *
1478
     * @return string
1479
     */
1480 16
    public static function HEXTOBIN($x, $places = null)
1481
    {
1482 16
        $x = Functions::flattenSingleValue($x);
1483 16
        $places = Functions::flattenSingleValue($places);
1484
1485 16
        if (is_bool($x)) {
1486 1
            return Functions::VALUE();
1487
        }
1488 15
        $x = (string) $x;
1489 15
        if (strlen($x) > preg_match_all('/[0123456789ABCDEF]/', strtoupper($x), $out)) {
1490 2
            return Functions::NAN();
1491
        }
1492
1493 13
        return self::DECTOBIN(self::HEXTODEC($x), $places);
1494
    }
1495
1496
    /**
1497
     * HEXTODEC.
1498
     *
1499
     * Return a hex value as decimal.
1500
     *
1501
     * Excel Function:
1502
     *        HEX2DEC(x)
1503
     *
1504
     * @category Engineering Functions
1505
     *
1506
     * @param string $x The hexadecimal number you want to convert. This number cannot
1507
     *                                contain more than 10 characters (40 bits). The most significant
1508
     *                                bit of number is the sign bit. The remaining 39 bits are magnitude
1509
     *                                bits. Negative numbers are represented using two's-complement
1510
     *                                notation.
1511
     *                                If number is not a valid hexadecimal number, HEX2DEC returns the
1512
     *                                #NUM! error value.
1513
     *
1514
     * @return string
1515
     */
1516 37
    public static function HEXTODEC($x)
1517
    {
1518 37
        $x = Functions::flattenSingleValue($x);
1519
1520 37
        if (is_bool($x)) {
1521 1
            return Functions::VALUE();
1522
        }
1523 36
        $x = (string) $x;
1524 36
        if (strlen($x) > preg_match_all('/[0123456789ABCDEF]/', strtoupper($x), $out)) {
1525 3
            return Functions::NAN();
1526
        }
1527
1528 33
        if (strlen($x) > 10) {
1529 1
            return Functions::NAN();
1530
        }
1531
1532 32
        $binX = '';
1533 32
        foreach (str_split($x) as $char) {
1534 32
            $binX .= str_pad(base_convert($char, 16, 2), 4, '0', STR_PAD_LEFT);
1535
        }
1536 32
        if (strlen($binX) == 40 && $binX[0] == '1') {
1537 5
            for ($i = 0; $i < 40; ++$i) {
1538 5
                $binX[$i] = ($binX[$i] == '1' ? '0' : '1');
1539
            }
1540
1541 5
            return (bindec($binX) + 1) * -1;
1542
        }
1543
1544 27
        return bindec($binX);
1545
    }
1546
1547
    /**
1548
     * HEXTOOCT.
1549
     *
1550
     * Return a hex value as octal.
1551
     *
1552
     * Excel Function:
1553
     *        HEX2OCT(x[,places])
1554
     *
1555
     * @category Engineering Functions
1556
     *
1557
     * @param string $x The hexadecimal number you want to convert. Number cannot
1558
     *                                    contain more than 10 characters. The most significant bit of
1559
     *                                    number is the sign bit. The remaining 39 bits are magnitude
1560
     *                                    bits. Negative numbers are represented using two's-complement
1561
     *                                    notation.
1562
     *                                    If number is negative, HEX2OCT ignores places and returns a
1563
     *                                    10-character octal number.
1564
     *                                    If number is negative, it cannot be less than FFE0000000, and
1565
     *                                    if number is positive, it cannot be greater than 1FFFFFFF.
1566
     *                                    If number is not a valid hexadecimal number, HEX2OCT returns
1567
     *                                    the #NUM! error value.
1568
     *                                    If HEX2OCT requires more than places characters, it returns
1569
     *                                    the #NUM! error value.
1570
     * @param int $places The number of characters to use. If places is omitted, HEX2OCT
1571
     *                                    uses the minimum number of characters necessary. Places is
1572
     *                                    useful for padding the return value with leading 0s (zeros).
1573
     *                                    If places is not an integer, it is truncated.
1574
     *                                    If places is nonnumeric, HEX2OCT returns the #VALUE! error
1575
     *                                    value.
1576
     *                                    If places is negative, HEX2OCT returns the #NUM! error value.
1577
     *
1578
     * @return string
1579
     */
1580 13
    public static function HEXTOOCT($x, $places = null)
1581
    {
1582 13
        $x = Functions::flattenSingleValue($x);
1583 13
        $places = Functions::flattenSingleValue($places);
1584
1585 13
        if (is_bool($x)) {
1586 1
            return Functions::VALUE();
1587
        }
1588 12
        $x = (string) $x;
1589 12
        if (strlen($x) > preg_match_all('/[0123456789ABCDEF]/', strtoupper($x), $out)) {
1590 3
            return Functions::NAN();
1591
        }
1592
1593 9
        $decimal = self::HEXTODEC($x);
1594 9
        if ($decimal < -536870912 || $decimal > 536870911) {
1595 1
            return Functions::NAN();
1596
        }
1597
1598 8
        return self::DECTOOCT($decimal, $places);
1599
    }
1600
1601
    /**
1602
     * OCTTOBIN.
1603
     *
1604
     * Return an octal value as binary.
1605
     *
1606
     * Excel Function:
1607
     *        OCT2BIN(x[,places])
1608
     *
1609
     * @category Engineering Functions
1610
     *
1611
     * @param string $x The octal number you want to convert. Number may not
1612
     *                                    contain more than 10 characters. The most significant
1613
     *                                    bit of number is the sign bit. The remaining 29 bits
1614
     *                                    are magnitude bits. Negative numbers are represented
1615
     *                                    using two's-complement notation.
1616
     *                                    If number is negative, OCT2BIN ignores places and returns
1617
     *                                    a 10-character binary number.
1618
     *                                    If number is negative, it cannot be less than 7777777000,
1619
     *                                    and if number is positive, it cannot be greater than 777.
1620
     *                                    If number is not a valid octal number, OCT2BIN returns
1621
     *                                    the #NUM! error value.
1622
     *                                    If OCT2BIN requires more than places characters, it
1623
     *                                    returns the #NUM! error value.
1624
     * @param int $places The number of characters to use. If places is omitted,
1625
     *                                    OCT2BIN uses the minimum number of characters necessary.
1626
     *                                    Places is useful for padding the return value with
1627
     *                                    leading 0s (zeros).
1628
     *                                    If places is not an integer, it is truncated.
1629
     *                                    If places is nonnumeric, OCT2BIN returns the #VALUE!
1630
     *                                    error value.
1631
     *                                    If places is negative, OCT2BIN returns the #NUM! error
1632
     *                                    value.
1633
     *
1634
     * @return string
1635
     */
1636 13
    public static function OCTTOBIN($x, $places = null)
1637
    {
1638 13
        $x = Functions::flattenSingleValue($x);
1639 13
        $places = Functions::flattenSingleValue($places);
1640
1641 13
        if (is_bool($x)) {
1642 1
            return Functions::VALUE();
1643
        }
1644 12
        $x = (string) $x;
1645 12
        if (preg_match_all('/[01234567]/', $x, $out) != strlen($x)) {
1646 2
            return Functions::NAN();
1647
        }
1648
1649 10
        return self::DECTOBIN(self::OCTTODEC($x), $places);
1650
    }
1651
1652
    /**
1653
     * OCTTODEC.
1654
     *
1655
     * Return an octal value as decimal.
1656
     *
1657
     * Excel Function:
1658
     *        OCT2DEC(x)
1659
     *
1660
     * @category Engineering Functions
1661
     *
1662
     * @param string $x The octal number you want to convert. Number may not contain
1663
     *                                more than 10 octal characters (30 bits). The most significant
1664
     *                                bit of number is the sign bit. The remaining 29 bits are
1665
     *                                magnitude bits. Negative numbers are represented using
1666
     *                                two's-complement notation.
1667
     *                                If number is not a valid octal number, OCT2DEC returns the
1668
     *                                #NUM! error value.
1669
     *
1670
     * @return string
1671
     */
1672 25
    public static function OCTTODEC($x)
1673
    {
1674 25
        $x = Functions::flattenSingleValue($x);
1675
1676 25
        if (is_bool($x)) {
1677 1
            return Functions::VALUE();
1678
        }
1679 24
        $x = (string) $x;
1680 24
        if (preg_match_all('/[01234567]/', $x, $out) != strlen($x)) {
1681 2
            return Functions::NAN();
1682
        }
1683 22
        $binX = '';
1684 22
        foreach (str_split($x) as $char) {
1685 22
            $binX .= str_pad(decbin((int) $char), 3, '0', STR_PAD_LEFT);
1686
        }
1687 22
        if (strlen($binX) == 30 && $binX[0] == '1') {
1688 4
            for ($i = 0; $i < 30; ++$i) {
1689 4
                $binX[$i] = ($binX[$i] == '1' ? '0' : '1');
1690
            }
1691
1692 4
            return (bindec($binX) + 1) * -1;
1693
        }
1694
1695 18
        return bindec($binX);
1696
    }
1697
1698
    /**
1699
     * OCTTOHEX.
1700
     *
1701
     * Return an octal value as hex.
1702
     *
1703
     * Excel Function:
1704
     *        OCT2HEX(x[,places])
1705
     *
1706
     * @category Engineering Functions
1707
     *
1708
     * @param string $x The octal number you want to convert. Number may not contain
1709
     *                                    more than 10 octal characters (30 bits). The most significant
1710
     *                                    bit of number is the sign bit. The remaining 29 bits are
1711
     *                                    magnitude bits. Negative numbers are represented using
1712
     *                                    two's-complement notation.
1713
     *                                    If number is negative, OCT2HEX ignores places and returns a
1714
     *                                    10-character hexadecimal number.
1715
     *                                    If number is not a valid octal number, OCT2HEX returns the
1716
     *                                    #NUM! error value.
1717
     *                                    If OCT2HEX requires more than places characters, it returns
1718
     *                                    the #NUM! error value.
1719
     * @param int $places The number of characters to use. If places is omitted, OCT2HEX
1720
     *                                    uses the minimum number of characters necessary. Places is useful
1721
     *                                    for padding the return value with leading 0s (zeros).
1722
     *                                    If places is not an integer, it is truncated.
1723
     *                                    If places is nonnumeric, OCT2HEX returns the #VALUE! error value.
1724
     *                                    If places is negative, OCT2HEX returns the #NUM! error value.
1725
     *
1726
     * @return string
1727
     */
1728 9
    public static function OCTTOHEX($x, $places = null)
1729
    {
1730 9
        $x = Functions::flattenSingleValue($x);
1731 9
        $places = Functions::flattenSingleValue($places);
1732
1733 9
        if (is_bool($x)) {
1734 1
            return Functions::VALUE();
1735
        }
1736 8
        $x = (string) $x;
1737 8
        if (preg_match_all('/[01234567]/', $x, $out) != strlen($x)) {
1738 2
            return Functions::NAN();
1739
        }
1740 6
        $hexVal = strtoupper(dechex(self::OCTTODEC($x)));
0 ignored issues
show
Bug introduced by
self::OCTTODEC($x) of type string is incompatible with the type integer expected by parameter $number of dechex(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1740
        $hexVal = strtoupper(dechex(/** @scrutinizer ignore-type */ self::OCTTODEC($x)));
Loading history...
1741
1742 6
        return self::nbrConversionFormat($hexVal, $places);
1743
    }
1744
1745
    /**
1746
     * COMPLEX.
1747
     *
1748
     * Converts real and imaginary coefficients into a complex number of the form x + yi or x + yj.
1749
     *
1750
     * Excel Function:
1751
     *        COMPLEX(realNumber,imaginary[,places])
1752
     *
1753
     * @category Engineering Functions
1754
     *
1755
     * @param float $realNumber the real coefficient of the complex number
1756
     * @param float $imaginary the imaginary coefficient of the complex number
1757
     * @param string $suffix The suffix for the imaginary component of the complex number.
1758
     *                                        If omitted, the suffix is assumed to be "i".
1759
     *
1760
     * @return string
1761
     */
1762 759
    public static function COMPLEX($realNumber = 0.0, $imaginary = 0.0, $suffix = 'i')
1763
    {
1764 759
        $realNumber = ($realNumber === null) ? 0.0 : Functions::flattenSingleValue($realNumber);
0 ignored issues
show
introduced by
The condition $realNumber === null is always false.
Loading history...
1765 759
        $imaginary = ($imaginary === null) ? 0.0 : Functions::flattenSingleValue($imaginary);
0 ignored issues
show
introduced by
The condition $imaginary === null is always false.
Loading history...
1766 759
        $suffix = ($suffix === null) ? 'i' : Functions::flattenSingleValue($suffix);
0 ignored issues
show
introduced by
The condition $suffix === null is always false.
Loading history...
1767
1768 759
        if (((is_numeric($realNumber)) && (is_numeric($imaginary))) &&
1769 759
            (($suffix == 'i') || ($suffix == 'j') || ($suffix == ''))
1770
        ) {
1771 758
            $realNumber = (float) $realNumber;
1772 758
            $imaginary = (float) $imaginary;
1773
1774 758
            if ($suffix == '') {
1775
                $suffix = 'i';
1776
            }
1777 758
            if ($realNumber == 0.0) {
1778 65
                if ($imaginary == 0.0) {
1779 6
                    return (string) '0';
1780 59
                } elseif ($imaginary == 1.0) {
1781 5
                    return (string) $suffix;
1782 54
                } elseif ($imaginary == -1.0) {
1783 2
                    return (string) '-' . $suffix;
1784
                }
1785
1786 52
                return (string) $imaginary . $suffix;
1787 693
            } elseif ($imaginary == 0.0) {
1788 54
                return (string) $realNumber;
1789 639
            } elseif ($imaginary == 1.0) {
1790 45
                return (string) $realNumber . '+' . $suffix;
1791 594
            } elseif ($imaginary == -1.0) {
1792 8
                return (string) $realNumber . '-' . $suffix;
1793
            }
1794 586
            if ($imaginary > 0) {
1795 329
                $imaginary = (string) '+' . $imaginary;
1796
            }
1797
1798 586
            return (string) $realNumber . $imaginary . $suffix;
1799
        }
1800
1801 1
        return Functions::VALUE();
1802
    }
1803
1804
    /**
1805
     * IMAGINARY.
1806
     *
1807
     * Returns the imaginary coefficient of a complex number in x + yi or x + yj text format.
1808
     *
1809
     * Excel Function:
1810
     *        IMAGINARY(complexNumber)
1811
     *
1812
     * @category Engineering Functions
1813
     *
1814
     * @param string $complexNumber the complex number for which you want the imaginary
1815
     *                                         coefficient
1816
     *
1817
     * @return float
1818
     */
1819 30
    public static function IMAGINARY($complexNumber)
1820
    {
1821 30
        $complexNumber = Functions::flattenSingleValue($complexNumber);
1822
1823 30
        $parsedComplex = self::parseComplex($complexNumber);
1824
1825 30
        return $parsedComplex['imaginary'];
0 ignored issues
show
Bug Best Practice introduced by
The expression return $parsedComplex['imaginary'] returns the type string which is incompatible with the documented return type double.
Loading history...
1826
    }
1827
1828
    /**
1829
     * IMREAL.
1830
     *
1831
     * Returns the real coefficient of a complex number in x + yi or x + yj text format.
1832
     *
1833
     * Excel Function:
1834
     *        IMREAL(complexNumber)
1835
     *
1836
     * @category Engineering Functions
1837
     *
1838
     * @param string $complexNumber the complex number for which you want the real coefficient
1839
     *
1840
     * @return float
1841
     */
1842 30
    public static function IMREAL($complexNumber)
1843
    {
1844 30
        $complexNumber = Functions::flattenSingleValue($complexNumber);
1845
1846 30
        $parsedComplex = self::parseComplex($complexNumber);
1847
1848 30
        return $parsedComplex['real'];
0 ignored issues
show
Bug Best Practice introduced by
The expression return $parsedComplex['real'] returns the type string which is incompatible with the documented return type double.
Loading history...
1849
    }
1850
1851
    /**
1852
     * IMABS.
1853
     *
1854
     * Returns the absolute value (modulus) of a complex number in x + yi or x + yj text format.
1855
     *
1856
     * Excel Function:
1857
     *        IMABS(complexNumber)
1858
     *
1859
     * @param string $complexNumber the complex number for which you want the absolute value
1860
     *
1861
     * @return float
1862
     */
1863 27
    public static function IMABS($complexNumber)
1864
    {
1865 27
        $complexNumber = Functions::flattenSingleValue($complexNumber);
1866
1867 27
        $parsedComplex = self::parseComplex($complexNumber);
1868
1869 27
        return sqrt(
1870 27
            ($parsedComplex['real'] * $parsedComplex['real']) +
1871 27
            ($parsedComplex['imaginary'] * $parsedComplex['imaginary'])
1872
        );
1873
    }
1874
1875
    /**
1876
     * IMARGUMENT.
1877
     *
1878
     * Returns the argument theta of a complex number, i.e. the angle in radians from the real
1879
     * axis to the representation of the number in polar coordinates.
1880
     *
1881
     * Excel Function:
1882
     *        IMARGUMENT(complexNumber)
1883
     *
1884
     * @param string $complexNumber the complex number for which you want the argument theta
1885
     *
1886
     * @return float
1887
     */
1888 128
    public static function IMARGUMENT($complexNumber)
1889
    {
1890 128
        $complexNumber = Functions::flattenSingleValue($complexNumber);
1891 128
        $parsedComplex = self::parseComplex($complexNumber);
1892 128
        if ($parsedComplex['real'] == 0.0) {
1893 22
            if ($parsedComplex['imaginary'] == 0.0) {
1894 2
                return Functions::DIV0();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...ation\Functions::DIV0() returns the type string which is incompatible with the documented return type double.
Loading history...
1895 20
            } elseif ($parsedComplex['imaginary'] < 0.0) {
1896 10
                return M_PI / -2;
1897
            }
1898
1899 10
            return M_PI / 2;
1900 106
        } elseif ($parsedComplex['real'] > 0.0) {
1901 52
            return atan($parsedComplex['imaginary'] / $parsedComplex['real']);
1902 54
        } elseif ($parsedComplex['imaginary'] < 0.0) {
1903 20
            return 0 - (M_PI - atan(abs($parsedComplex['imaginary']) / abs($parsedComplex['real'])));
1904
        }
1905
1906 34
        return M_PI - atan($parsedComplex['imaginary'] / abs($parsedComplex['real']));
1907
    }
1908
1909
    /**
1910
     * IMCONJUGATE.
1911
     *
1912
     * Returns the complex conjugate of a complex number in x + yi or x + yj text format.
1913
     *
1914
     * Excel Function:
1915
     *        IMCONJUGATE(complexNumber)
1916
     *
1917
     * @param string $complexNumber the complex number for which you want the conjugate
1918
     *
1919
     * @return string
1920
     */
1921 47
    public static function IMCONJUGATE($complexNumber)
1922
    {
1923 47
        $complexNumber = Functions::flattenSingleValue($complexNumber);
1924
1925 47
        $parsedComplex = self::parseComplex($complexNumber);
1926
1927 47
        if ($parsedComplex['imaginary'] == 0.0) {
1928 9
            return $parsedComplex['real'];
1929
        }
1930
1931 38
        return self::cleanComplex(
1932 38
            self::COMPLEX(
1933 38
                $parsedComplex['real'],
0 ignored issues
show
Bug introduced by
$parsedComplex['real'] of type string is incompatible with the type double expected by parameter $realNumber of PhpOffice\PhpSpreadsheet...\Engineering::COMPLEX(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1933
                /** @scrutinizer ignore-type */ $parsedComplex['real'],
Loading history...
1934 38
                0 - $parsedComplex['imaginary'],
1935 38
                $parsedComplex['suffix']
1936
            )
1937
        );
1938
    }
1939
1940
    /**
1941
     * IMCOS.
1942
     *
1943
     * Returns the cosine of a complex number in x + yi or x + yj text format.
1944
     *
1945
     * Excel Function:
1946
     *        IMCOS(complexNumber)
1947
     *
1948
     * @param string $complexNumber the complex number for which you want the cosine
1949
     *
1950
     * @return float|string
1951
     */
1952 27
    public static function IMCOS($complexNumber)
1953
    {
1954 27
        $complexNumber = Functions::flattenSingleValue($complexNumber);
1955
1956 27
        $parsedComplex = self::parseComplex($complexNumber);
1957
1958 27
        if ($parsedComplex['imaginary'] == 0.0) {
1959 6
            return cos($parsedComplex['real']);
0 ignored issues
show
Bug introduced by
$parsedComplex['real'] of type string is incompatible with the type double expected by parameter $arg of cos(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1959
            return cos(/** @scrutinizer ignore-type */ $parsedComplex['real']);
Loading history...
1960
        }
1961
1962 21
        return self::IMCONJUGATE(
1963 21
            self::COMPLEX(
1964 21
                cos($parsedComplex['real']) * cosh($parsedComplex['imaginary']),
0 ignored issues
show
Bug introduced by
$parsedComplex['imaginary'] of type string is incompatible with the type double expected by parameter $arg of cosh(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1964
                cos($parsedComplex['real']) * cosh(/** @scrutinizer ignore-type */ $parsedComplex['imaginary']),
Loading history...
1965 21
                sin($parsedComplex['real']) * sinh($parsedComplex['imaginary']),
0 ignored issues
show
Bug introduced by
$parsedComplex['imaginary'] of type string is incompatible with the type double expected by parameter $arg of sinh(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1965
                sin($parsedComplex['real']) * sinh(/** @scrutinizer ignore-type */ $parsedComplex['imaginary']),
Loading history...
Bug introduced by
$parsedComplex['real'] of type string is incompatible with the type double expected by parameter $arg of sin(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1965
                sin(/** @scrutinizer ignore-type */ $parsedComplex['real']) * sinh($parsedComplex['imaginary']),
Loading history...
1966 21
                $parsedComplex['suffix']
1967
            )
1968
        );
1969
    }
1970
1971
    /**
1972
     * IMSIN.
1973
     *
1974
     * Returns the sine of a complex number in x + yi or x + yj text format.
1975
     *
1976
     * Excel Function:
1977
     *        IMSIN(complexNumber)
1978
     *
1979
     * @param string $complexNumber the complex number for which you want the sine
1980
     *
1981
     * @return float|string
1982
     */
1983 27
    public static function IMSIN($complexNumber)
1984
    {
1985 27
        $complexNumber = Functions::flattenSingleValue($complexNumber);
1986
1987 27
        $parsedComplex = self::parseComplex($complexNumber);
1988
1989 27
        if ($parsedComplex['imaginary'] == 0.0) {
1990 6
            return sin($parsedComplex['real']);
0 ignored issues
show
Bug introduced by
$parsedComplex['real'] of type string is incompatible with the type double expected by parameter $arg of sin(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1990
            return sin(/** @scrutinizer ignore-type */ $parsedComplex['real']);
Loading history...
1991
        }
1992
1993 21
        return self::COMPLEX(
1994 21
            sin($parsedComplex['real']) * cosh($parsedComplex['imaginary']),
0 ignored issues
show
Bug introduced by
$parsedComplex['imaginary'] of type string is incompatible with the type double expected by parameter $arg of cosh(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1994
            sin($parsedComplex['real']) * cosh(/** @scrutinizer ignore-type */ $parsedComplex['imaginary']),
Loading history...
1995 21
            cos($parsedComplex['real']) * sinh($parsedComplex['imaginary']),
0 ignored issues
show
Bug introduced by
$parsedComplex['real'] of type string is incompatible with the type double expected by parameter $arg of cos(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1995
            cos(/** @scrutinizer ignore-type */ $parsedComplex['real']) * sinh($parsedComplex['imaginary']),
Loading history...
Bug introduced by
$parsedComplex['imaginary'] of type string is incompatible with the type double expected by parameter $arg of sinh(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1995
            cos($parsedComplex['real']) * sinh(/** @scrutinizer ignore-type */ $parsedComplex['imaginary']),
Loading history...
1996 21
            $parsedComplex['suffix']
1997
        );
1998
    }
1999
2000
    /**
2001
     * IMSQRT.
2002
     *
2003
     * Returns the square root of a complex number in x + yi or x + yj text format.
2004
     *
2005
     * Excel Function:
2006
     *        IMSQRT(complexNumber)
2007
     *
2008
     * @param string $complexNumber the complex number for which you want the square root
2009
     *
2010
     * @return string
2011
     */
2012 28
    public static function IMSQRT($complexNumber)
2013
    {
2014 28
        $complexNumber = Functions::flattenSingleValue($complexNumber);
2015
2016 28
        $parsedComplex = self::parseComplex($complexNumber);
2017
2018 28
        $theta = self::IMARGUMENT($complexNumber);
2019 28
        if ($theta === Functions::DIV0()) {
0 ignored issues
show
introduced by
The condition $theta === PhpOffice\Php...ation\Functions::DIV0() is always false.
Loading history...
2020 1
            return '0';
2021
        }
2022
2023 27
        $d1 = cos($theta / 2);
2024 27
        $d2 = sin($theta / 2);
2025 27
        $r = sqrt(sqrt(($parsedComplex['real'] * $parsedComplex['real']) + ($parsedComplex['imaginary'] * $parsedComplex['imaginary'])));
2026
2027 27
        if ($parsedComplex['suffix'] == '') {
2028 5
            return self::COMPLEX($d1 * $r, $d2 * $r);
2029
        }
2030
2031 22
        return self::COMPLEX($d1 * $r, $d2 * $r, $parsedComplex['suffix']);
2032
    }
2033
2034
    /**
2035
     * IMLN.
2036
     *
2037
     * Returns the natural logarithm of a complex number in x + yi or x + yj text format.
2038
     *
2039
     * Excel Function:
2040
     *        IMLN(complexNumber)
2041
     *
2042
     * @param string $complexNumber the complex number for which you want the natural logarithm
2043
     *
2044
     * @return string
2045
     */
2046 75
    public static function IMLN($complexNumber)
2047
    {
2048 75
        $complexNumber = Functions::flattenSingleValue($complexNumber);
2049
2050 75
        $parsedComplex = self::parseComplex($complexNumber);
2051
2052 75
        if (($parsedComplex['real'] == 0.0) && ($parsedComplex['imaginary'] == 0.0)) {
2053 1
            return Functions::NAN();
2054
        }
2055
2056 74
        $logR = log(sqrt(($parsedComplex['real'] * $parsedComplex['real']) + ($parsedComplex['imaginary'] * $parsedComplex['imaginary'])));
2057 74
        $t = self::IMARGUMENT($complexNumber);
2058
2059 74
        if ($parsedComplex['suffix'] == '') {
2060 8
            return self::COMPLEX($logR, $t);
2061
        }
2062
2063 66
        return self::COMPLEX($logR, $t, $parsedComplex['suffix']);
2064
    }
2065
2066
    /**
2067
     * IMLOG10.
2068
     *
2069
     * Returns the common logarithm (base 10) of a complex number in x + yi or x + yj text format.
2070
     *
2071
     * Excel Function:
2072
     *        IMLOG10(complexNumber)
2073
     *
2074
     * @param string $complexNumber the complex number for which you want the common logarithm
2075
     *
2076
     * @return string
2077
     */
2078 27
    public static function IMLOG10($complexNumber)
2079
    {
2080 27
        $complexNumber = Functions::flattenSingleValue($complexNumber);
2081
2082 27
        $parsedComplex = self::parseComplex($complexNumber);
2083
2084 27
        if (($parsedComplex['real'] == 0.0) && ($parsedComplex['imaginary'] == 0.0)) {
2085 1
            return Functions::NAN();
2086 26
        } elseif (($parsedComplex['real'] > 0.0) && ($parsedComplex['imaginary'] == 0.0)) {
2087 2
            return log10($parsedComplex['real']);
2088
        }
2089
2090 24
        return self::IMPRODUCT(log10(self::EULER), self::IMLN($complexNumber));
2091
    }
2092
2093
    /**
2094
     * IMLOG2.
2095
     *
2096
     * Returns the base-2 logarithm of a complex number in x + yi or x + yj text format.
2097
     *
2098
     * Excel Function:
2099
     *        IMLOG2(complexNumber)
2100
     *
2101
     * @param string $complexNumber the complex number for which you want the base-2 logarithm
2102
     *
2103
     * @return string
2104
     */
2105 27
    public static function IMLOG2($complexNumber)
2106
    {
2107 27
        $complexNumber = Functions::flattenSingleValue($complexNumber);
2108
2109 27
        $parsedComplex = self::parseComplex($complexNumber);
2110
2111 27
        if (($parsedComplex['real'] == 0.0) && ($parsedComplex['imaginary'] == 0.0)) {
2112 1
            return Functions::NAN();
2113 26
        } elseif (($parsedComplex['real'] > 0.0) && ($parsedComplex['imaginary'] == 0.0)) {
2114 2
            return log($parsedComplex['real'], 2);
2115
        }
2116
2117 24
        return self::IMPRODUCT(log(self::EULER, 2), self::IMLN($complexNumber));
2118
    }
2119
2120
    /**
2121
     * IMEXP.
2122
     *
2123
     * Returns the exponential of a complex number in x + yi or x + yj text format.
2124
     *
2125
     * Excel Function:
2126
     *        IMEXP(complexNumber)
2127
     *
2128
     * @param string $complexNumber the complex number for which you want the exponential
2129
     *
2130
     * @return string
2131
     */
2132 27
    public static function IMEXP($complexNumber)
2133
    {
2134 27
        $complexNumber = Functions::flattenSingleValue($complexNumber);
2135
2136 27
        $parsedComplex = self::parseComplex($complexNumber);
2137
2138 27
        if (($parsedComplex['real'] == 0.0) && ($parsedComplex['imaginary'] == 0.0)) {
2139 1
            return '1';
2140
        }
2141
2142 26
        $e = exp($parsedComplex['real']);
2143 26
        $eX = $e * cos($parsedComplex['imaginary']);
2144 26
        $eY = $e * sin($parsedComplex['imaginary']);
2145
2146 26
        if ($parsedComplex['suffix'] == '') {
2147 4
            return self::COMPLEX($eX, $eY);
2148
        }
2149
2150 22
        return self::COMPLEX($eX, $eY, $parsedComplex['suffix']);
2151
    }
2152
2153
    /**
2154
     * IMPOWER.
2155
     *
2156
     * Returns a complex number in x + yi or x + yj text format raised to a power.
2157
     *
2158
     * Excel Function:
2159
     *        IMPOWER(complexNumber,realNumber)
2160
     *
2161
     * @param string $complexNumber the complex number you want to raise to a power
2162
     * @param float $realNumber the power to which you want to raise the complex number
2163
     *
2164
     * @return string
2165
     */
2166
    public static function IMPOWER($complexNumber, $realNumber)
2167
    {
2168
        $complexNumber = Functions::flattenSingleValue($complexNumber);
2169
        $realNumber = Functions::flattenSingleValue($realNumber);
2170
2171
        if (!is_numeric($realNumber)) {
2172
            return Functions::VALUE();
2173
        }
2174
2175
        $parsedComplex = self::parseComplex($complexNumber);
2176
2177
        $r = sqrt(($parsedComplex['real'] * $parsedComplex['real']) + ($parsedComplex['imaginary'] * $parsedComplex['imaginary']));
2178
        $rPower = pow($r, $realNumber);
2179
        $theta = self::IMARGUMENT($complexNumber) * $realNumber;
2180
        if ($theta == 0) {
2181
            return 1;
2182
        } elseif ($parsedComplex['imaginary'] == 0.0) {
2183
            return self::COMPLEX($rPower * cos($theta), $rPower * sin($theta), $parsedComplex['suffix']);
2184
        }
2185
2186
        return self::COMPLEX($rPower * cos($theta), $rPower * sin($theta), $parsedComplex['suffix']);
2187
    }
2188
2189
    /**
2190
     * IMDIV.
2191
     *
2192
     * Returns the quotient of two complex numbers in x + yi or x + yj text format.
2193
     *
2194
     * Excel Function:
2195
     *        IMDIV(complexDividend,complexDivisor)
2196
     *
2197
     * @param string $complexDividend the complex numerator or dividend
2198
     * @param string $complexDivisor the complex denominator or divisor
2199
     *
2200
     * @return string
2201
     */
2202
    public static function IMDIV($complexDividend, $complexDivisor)
2203
    {
2204
        $complexDividend = Functions::flattenSingleValue($complexDividend);
2205
        $complexDivisor = Functions::flattenSingleValue($complexDivisor);
2206
2207
        $parsedComplexDividend = self::parseComplex($complexDividend);
2208
        $parsedComplexDivisor = self::parseComplex($complexDivisor);
2209
2210
        if (($parsedComplexDividend['suffix'] != '') && ($parsedComplexDivisor['suffix'] != '') &&
2211
            ($parsedComplexDividend['suffix'] != $parsedComplexDivisor['suffix'])
2212
        ) {
2213
            return Functions::NAN();
2214
        }
2215
        if (($parsedComplexDividend['suffix'] != '') && ($parsedComplexDivisor['suffix'] == '')) {
2216
            $parsedComplexDivisor['suffix'] = $parsedComplexDividend['suffix'];
2217
        }
2218
2219
        $d1 = ($parsedComplexDividend['real'] * $parsedComplexDivisor['real']) + ($parsedComplexDividend['imaginary'] * $parsedComplexDivisor['imaginary']);
2220
        $d2 = ($parsedComplexDividend['imaginary'] * $parsedComplexDivisor['real']) - ($parsedComplexDividend['real'] * $parsedComplexDivisor['imaginary']);
2221
        $d3 = ($parsedComplexDivisor['real'] * $parsedComplexDivisor['real']) + ($parsedComplexDivisor['imaginary'] * $parsedComplexDivisor['imaginary']);
2222
2223
        $r = $d1 / $d3;
2224
        $i = $d2 / $d3;
2225
2226
        if ($i > 0.0) {
2227
            return self::cleanComplex($r . '+' . $i . $parsedComplexDivisor['suffix']);
2228
        } elseif ($i < 0.0) {
2229
            return self::cleanComplex($r . $i . $parsedComplexDivisor['suffix']);
2230
        }
2231
2232
        return $r;
2233
    }
2234
2235
    /**
2236
     * IMSUB.
2237
     *
2238
     * Returns the difference of two complex numbers in x + yi or x + yj text format.
2239
     *
2240
     * Excel Function:
2241
     *        IMSUB(complexNumber1,complexNumber2)
2242
     *
2243
     * @param string $complexNumber1 the complex number from which to subtract complexNumber2
2244
     * @param string $complexNumber2 the complex number to subtract from complexNumber1
2245
     *
2246
     * @return string
2247
     */
2248
    public static function IMSUB($complexNumber1, $complexNumber2)
2249
    {
2250
        $complexNumber1 = Functions::flattenSingleValue($complexNumber1);
2251
        $complexNumber2 = Functions::flattenSingleValue($complexNumber2);
2252
2253
        $parsedComplex1 = self::parseComplex($complexNumber1);
2254
        $parsedComplex2 = self::parseComplex($complexNumber2);
2255
2256
        if ((($parsedComplex1['suffix'] != '') && ($parsedComplex2['suffix'] != '')) &&
2257
            ($parsedComplex1['suffix'] != $parsedComplex2['suffix'])
2258
        ) {
2259
            return Functions::NAN();
2260
        } elseif (($parsedComplex1['suffix'] == '') && ($parsedComplex2['suffix'] != '')) {
2261
            $parsedComplex1['suffix'] = $parsedComplex2['suffix'];
2262
        }
2263
2264
        $d1 = $parsedComplex1['real'] - $parsedComplex2['real'];
2265
        $d2 = $parsedComplex1['imaginary'] - $parsedComplex2['imaginary'];
2266
2267
        return self::COMPLEX($d1, $d2, $parsedComplex1['suffix']);
2268
    }
2269
2270
    /**
2271
     * IMSUM.
2272
     *
2273
     * Returns the sum of two or more complex numbers in x + yi or x + yj text format.
2274
     *
2275
     * Excel Function:
2276
     *        IMSUM(complexNumber[,complexNumber[,...]])
2277
     *
2278
     * @param string ...$complexNumbers Series of complex numbers to add
2279
     *
2280
     * @return string
2281
     */
2282 10
    public static function IMSUM(...$complexNumbers)
2283
    {
2284
        // Return value
2285 10
        $returnValue = self::parseComplex('0');
2286 10
        $activeSuffix = '';
2287
2288
        // Loop through the arguments
2289 10
        $aArgs = Functions::flattenArray($complexNumbers);
2290 10
        foreach ($aArgs as $arg) {
2291 10
            $parsedComplex = self::parseComplex($arg);
2292
2293 10
            if ($activeSuffix == '') {
2294 10
                $activeSuffix = $parsedComplex['suffix'];
2295 10
            } elseif (($parsedComplex['suffix'] != '') && ($activeSuffix != $parsedComplex['suffix'])) {
2296 2
                return Functions::NAN();
2297
            }
2298
2299 10
            $returnValue['real'] += $parsedComplex['real'];
2300 10
            $returnValue['imaginary'] += $parsedComplex['imaginary'];
2301
        }
2302
2303 8
        if ($returnValue['imaginary'] == 0.0) {
2304
            $activeSuffix = '';
2305
        }
2306
2307 8
        return self::COMPLEX($returnValue['real'], $returnValue['imaginary'], $activeSuffix);
2308
    }
2309
2310
    /**
2311
     * IMPRODUCT.
2312
     *
2313
     * Returns the product of two or more complex numbers in x + yi or x + yj text format.
2314
     *
2315
     * Excel Function:
2316
     *        IMPRODUCT(complexNumber[,complexNumber[,...]])
2317
     *
2318
     * @param string ...$complexNumbers Series of complex numbers to multiply
2319
     *
2320
     * @return string
2321
     */
2322 63
    public static function IMPRODUCT(...$complexNumbers)
2323
    {
2324
        // Return value
2325 63
        $returnValue = self::parseComplex('1');
2326 63
        $activeSuffix = '';
2327
2328
        // Loop through the arguments
2329 63
        $aArgs = Functions::flattenArray($complexNumbers);
2330 63
        foreach ($aArgs as $arg) {
2331 63
            $parsedComplex = self::parseComplex($arg);
2332
2333 63
            $workValue = $returnValue;
2334 63
            if (($parsedComplex['suffix'] != '') && ($activeSuffix == '')) {
2335 63
                $activeSuffix = $parsedComplex['suffix'];
2336 62
            } elseif (($parsedComplex['suffix'] != '') && ($activeSuffix != $parsedComplex['suffix'])) {
2337 1
                return Functions::NAN();
2338
            }
2339 63
            $returnValue['real'] = ($workValue['real'] * $parsedComplex['real']) - ($workValue['imaginary'] * $parsedComplex['imaginary']);
2340 63
            $returnValue['imaginary'] = ($workValue['real'] * $parsedComplex['imaginary']) + ($workValue['imaginary'] * $parsedComplex['real']);
2341
        }
2342
2343 62
        if ($returnValue['imaginary'] == 0.0) {
2344
            $activeSuffix = '';
2345
        }
2346
2347 62
        return self::COMPLEX($returnValue['real'], $returnValue['imaginary'], $activeSuffix);
2348
    }
2349
2350
    /**
2351
     * DELTA.
2352
     *
2353
     * Tests whether two values are equal. Returns 1 if number1 = number2; returns 0 otherwise.
2354
     *    Use this function to filter a set of values. For example, by summing several DELTA
2355
     *    functions you calculate the count of equal pairs. This function is also known as the
2356
     * Kronecker Delta function.
2357
     *
2358
     *    Excel Function:
2359
     *        DELTA(a[,b])
2360
     *
2361
     * @param float $a the first number
2362
     * @param float $b The second number. If omitted, b is assumed to be zero.
2363
     *
2364
     * @return int
2365
     */
2366 25
    public static function DELTA($a, $b = 0)
2367
    {
2368 25
        $a = Functions::flattenSingleValue($a);
2369 25
        $b = Functions::flattenSingleValue($b);
2370
2371 25
        return (int) ($a == $b);
2372
    }
2373
2374
    /**
2375
     * GESTEP.
2376
     *
2377
     *    Excel Function:
2378
     *        GESTEP(number[,step])
2379
     *
2380
     *    Returns 1 if number >= step; returns 0 (zero) otherwise
2381
     *    Use this function to filter a set of values. For example, by summing several GESTEP
2382
     * functions you calculate the count of values that exceed a threshold.
2383
     *
2384
     * @param float $number the value to test against step
2385
     * @param float $step The threshold value.
2386
     *                                    If you omit a value for step, GESTEP uses zero.
2387
     *
2388
     * @return int
2389
     */
2390 81
    public static function GESTEP($number, $step = 0)
2391
    {
2392 81
        $number = Functions::flattenSingleValue($number);
2393 81
        $step = Functions::flattenSingleValue($step);
2394
2395 81
        return (int) ($number >= $step);
2396
    }
2397
2398
    //
2399
    //    Private method to calculate the erf value
2400
    //
2401
    private static $twoSqrtPi = 1.128379167095512574;
2402
2403 152
    public static function erfVal($x)
2404
    {
2405 152
        if (abs($x) > 2.2) {
2406 67
            return 1 - self::erfcVal($x);
2407
        }
2408 127
        $sum = $term = $x;
2409 127
        $xsqr = ($x * $x);
2410 127
        $j = 1;
2411
        do {
2412 127
            $term *= $xsqr / $j;
2413 127
            $sum -= $term / (2 * $j + 1);
2414 127
            ++$j;
2415 127
            $term *= $xsqr / $j;
2416 127
            $sum += $term / (2 * $j + 1);
2417 127
            ++$j;
2418 127
            if ($sum == 0.0) {
2419 20
                break;
2420
            }
2421 115
        } while (abs($term / $sum) > Functions::PRECISION);
2422
2423 127
        return self::$twoSqrtPi * $sum;
2424
    }
2425
2426
    /**
2427
     * Validate arguments passed to the bitwise functions.
2428
     *
2429
     * @param mixed $value
2430
     *
2431
     * @throws Exception
2432
     *
2433
     * @return int
2434
     */
2435 21
    private static function validateBitwiseArgument($value)
2436
    {
2437 21
        $value = Functions::flattenSingleValue($value);
2438
2439 21
        if (is_int($value)) {
2440 14
            return $value;
2441 7
        } elseif (is_numeric($value)) {
2442 3
            if ($value == (int) ($value)) {
2443 2
                $value = (int) ($value);
2444 2
                if (($value > pow(2, 48) - 1) || ($value < 0)) {
2445 1
                    throw new Exception(Functions::NAN());
2446
                }
2447
2448 2
                return $value;
2449
            }
2450
2451 1
            throw new Exception(Functions::NAN());
2452
        }
2453
2454 4
        throw new Exception(Functions::VALUE());
2455
    }
2456
2457
    /**
2458
     * BITAND.
2459
     *
2460
     * Returns the bitwise AND of two integer values.
2461
     *
2462
     * Excel Function:
2463
     *        BITAND(number1, number2)
2464
     *
2465
     * @category Engineering Functions
2466
     *
2467
     * @param int $number1
2468
     * @param int $number2
2469
     *
2470
     * @return int|string
2471
     */
2472 4
    public static function BITAND($number1, $number2)
2473
    {
2474
        try {
2475 4
            $number1 = self::validateBitwiseArgument($number1);
2476 3
            $number2 = self::validateBitwiseArgument($number2);
2477 1
        } catch (Exception $e) {
2478 1
            return $e->getMessage();
2479
        }
2480
2481 3
        return $number1 & $number2;
2482
    }
2483
2484
    /**
2485
     * BITOR.
2486
     *
2487
     * Returns the bitwise OR of two integer values.
2488
     *
2489
     * Excel Function:
2490
     *        BITOR(number1, number2)
2491
     *
2492
     * @category Engineering Functions
2493
     *
2494
     * @param int $number1
2495
     * @param int $number2
2496
     *
2497
     * @return int|string
2498
     */
2499 5
    public static function BITOR($number1, $number2)
2500
    {
2501
        try {
2502 5
            $number1 = self::validateBitwiseArgument($number1);
2503 4
            $number2 = self::validateBitwiseArgument($number2);
2504 1
        } catch (Exception $e) {
2505 1
            return $e->getMessage();
2506
        }
2507
2508 4
        return $number1 | $number2;
2509
    }
2510
2511
    /**
2512
     * BITXOR.
2513
     *
2514
     * Returns the bitwise XOR of two integer values.
2515
     *
2516
     * Excel Function:
2517
     *        BITXOR(number1, number2)
2518
     *
2519
     * @category Engineering Functions
2520
     *
2521
     * @param int $number1
2522
     * @param int $number2
2523
     *
2524
     * @return int|string
2525
     */
2526 5
    public static function BITXOR($number1, $number2)
2527
    {
2528
        try {
2529 5
            $number1 = self::validateBitwiseArgument($number1);
2530 4
            $number2 = self::validateBitwiseArgument($number2);
2531 2
        } catch (Exception $e) {
2532 2
            return $e->getMessage();
2533
        }
2534
2535 3
        return $number1 ^ $number2;
2536
    }
2537
2538
    /**
2539
     * BITLSHIFT.
2540
     *
2541
     * Returns the number value shifted left by shift_amount bits.
2542
     *
2543
     * Excel Function:
2544
     *        BITLSHIFT(number, shift_amount)
2545
     *
2546
     * @category Engineering Functions
2547
     *
2548
     * @param int $number
2549
     * @param int $shiftAmount
2550
     *
2551
     * @return int|string
2552
     */
2553 4
    public static function BITLSHIFT($number, $shiftAmount)
2554
    {
2555
        try {
2556 4
            $number = self::validateBitwiseArgument($number);
2557 1
        } catch (Exception $e) {
2558 1
            return $e->getMessage();
2559
        }
2560
2561 3
        $shiftAmount = Functions::flattenSingleValue($shiftAmount);
2562
2563 3
        $result = $number << $shiftAmount;
2564 3
        if ($result > pow(2, 48) - 1) {
2565 1
            return Functions::NAN();
2566
        }
2567
2568 2
        return $result;
2569
    }
2570
2571
    /**
2572
     * BITRSHIFT.
2573
     *
2574
     * Returns the number value shifted right by shift_amount bits.
2575
     *
2576
     * Excel Function:
2577
     *        BITRSHIFT(number, shift_amount)
2578
     *
2579
     * @category Engineering Functions
2580
     *
2581
     * @param int $number
2582
     * @param int $shiftAmount
2583
     *
2584
     * @return int|string
2585
     */
2586 3
    public static function BITRSHIFT($number, $shiftAmount)
2587
    {
2588
        try {
2589 3
            $number = self::validateBitwiseArgument($number);
2590 1
        } catch (Exception $e) {
2591 1
            return $e->getMessage();
2592
        }
2593
2594 2
        $shiftAmount = Functions::flattenSingleValue($shiftAmount);
2595
2596 2
        return $number >> $shiftAmount;
2597
    }
2598
2599
    /**
2600
     * ERF.
2601
     *
2602
     * Returns the error function integrated between the lower and upper bound arguments.
2603
     *
2604
     *    Note: In Excel 2007 or earlier, if you input a negative value for the upper or lower bound arguments,
2605
     *            the function would return a #NUM! error. However, in Excel 2010, the function algorithm was
2606
     *            improved, so that it can now calculate the function for both positive and negative ranges.
2607
     *            PhpSpreadsheet follows Excel 2010 behaviour, and accepts negative arguments.
2608
     *
2609
     *    Excel Function:
2610
     *        ERF(lower[,upper])
2611
     *
2612
     * @param float $lower lower bound for integrating ERF
2613
     * @param float $upper upper bound for integrating ERF.
2614
     *                                If omitted, ERF integrates between zero and lower_limit
2615
     *
2616
     * @return float|string
2617
     */
2618 127
    public static function ERF($lower, $upper = null)
2619
    {
2620 127
        $lower = Functions::flattenSingleValue($lower);
2621 127
        $upper = Functions::flattenSingleValue($upper);
2622
2623 127
        if (is_numeric($lower)) {
2624 124
            if ($upper === null) {
2625 41
                return self::erfVal($lower);
2626
            }
2627 83
            if (is_numeric($upper)) {
2628 83
                return self::erfVal($upper) - self::erfVal($lower);
2629
            }
2630
        }
2631
2632 3
        return Functions::VALUE();
2633
    }
2634
2635
    /**
2636
     * ERFPRECISE.
2637
     *
2638
     * Returns the error function integrated between the lower and upper bound arguments.
2639
     *
2640
     *    Excel Function:
2641
     *        ERF.PRECISE(limit)
2642
     *
2643
     * @param float $limit bound for integrating ERF
2644
     *
2645
     * @return float|string
2646
     */
2647 4
    public static function ERFPRECISE($limit)
2648
    {
2649 4
        $limit = Functions::flattenSingleValue($limit);
2650
2651 4
        return self::ERF($limit);
2652
    }
2653
2654
    //
2655
    //    Private method to calculate the erfc value
2656
    //
2657
    private static $oneSqrtPi = 0.564189583547756287;
2658
2659 104
    private static function erfcVal($x)
2660
    {
2661 104
        if (abs($x) < 2.2) {
2662 28
            return 1 - self::erfVal($x);
2663
        }
2664 76
        if ($x < 0) {
2665 1
            return 2 - self::ERFC(-$x);
2666
        }
2667 76
        $a = $n = 1;
2668 76
        $b = $c = $x;
2669 76
        $d = ($x * $x) + 0.5;
2670 76
        $q1 = $q2 = $b / $d;
0 ignored issues
show
Unused Code introduced by
The assignment to $q1 is dead and can be removed.
Loading history...
2671 76
        $t = 0;
0 ignored issues
show
Unused Code introduced by
The assignment to $t is dead and can be removed.
Loading history...
2672
        do {
2673 76
            $t = $a * $n + $b * $x;
2674 76
            $a = $b;
2675 76
            $b = $t;
2676 76
            $t = $c * $n + $d * $x;
2677 76
            $c = $d;
2678 76
            $d = $t;
2679 76
            $n += 0.5;
2680 76
            $q1 = $q2;
2681 76
            $q2 = $b / $d;
2682 76
        } while ((abs($q1 - $q2) / $q2) > Functions::PRECISION);
2683
2684 76
        return self::$oneSqrtPi * exp(-$x * $x) * $q2;
2685
    }
2686
2687
    /**
2688
     * ERFC.
2689
     *
2690
     *    Returns the complementary ERF function integrated between x and infinity
2691
     *
2692
     *    Note: In Excel 2007 or earlier, if you input a negative value for the lower bound argument,
2693
     *        the function would return a #NUM! error. However, in Excel 2010, the function algorithm was
2694
     *        improved, so that it can now calculate the function for both positive and negative x values.
2695
     *            PhpSpreadsheet follows Excel 2010 behaviour, and accepts nagative arguments.
2696
     *
2697
     *    Excel Function:
2698
     *        ERFC(x)
2699
     *
2700
     * @param float $x The lower bound for integrating ERFC
2701
     *
2702
     * @return float|string
2703
     */
2704 41
    public static function ERFC($x)
2705
    {
2706 41
        $x = Functions::flattenSingleValue($x);
2707
2708 41
        if (is_numeric($x)) {
2709 38
            return self::erfcVal($x);
2710
        }
2711
2712 3
        return Functions::VALUE();
2713
    }
2714
2715
    /**
2716
     *    getConversionGroups
2717
     * Returns a list of the different conversion groups for UOM conversions.
2718
     *
2719
     * @return array
2720
     */
2721 1
    public static function getConversionGroups()
2722
    {
2723 1
        $conversionGroups = [];
2724 1
        foreach (self::$conversionUnits as $conversionUnit) {
2725 1
            $conversionGroups[] = $conversionUnit['Group'];
2726
        }
2727
2728 1
        return array_merge(array_unique($conversionGroups));
2729
    }
2730
2731
    /**
2732
     *    getConversionGroupUnits
2733
     * Returns an array of units of measure, for a specified conversion group, or for all groups.
2734
     *
2735
     * @param string $group The group whose units of measure you want to retrieve
2736
     *
2737
     * @return array
2738
     */
2739 1
    public static function getConversionGroupUnits($group = null)
2740
    {
2741 1
        $conversionGroups = [];
2742 1
        foreach (self::$conversionUnits as $conversionUnit => $conversionGroup) {
2743 1
            if (($group === null) || ($conversionGroup['Group'] == $group)) {
2744 1
                $conversionGroups[$conversionGroup['Group']][] = $conversionUnit;
2745
            }
2746
        }
2747
2748 1
        return $conversionGroups;
2749
    }
2750
2751
    /**
2752
     * getConversionGroupUnitDetails.
2753
     *
2754
     * @param string $group The group whose units of measure you want to retrieve
2755
     *
2756
     * @return array
2757
     */
2758 1
    public static function getConversionGroupUnitDetails($group = null)
2759
    {
2760 1
        $conversionGroups = [];
2761 1
        foreach (self::$conversionUnits as $conversionUnit => $conversionGroup) {
2762 1
            if (($group === null) || ($conversionGroup['Group'] == $group)) {
2763 1
                $conversionGroups[$conversionGroup['Group']][] = [
2764 1
                    'unit' => $conversionUnit,
2765 1
                    'description' => $conversionGroup['Unit Name'],
2766
                ];
2767
            }
2768
        }
2769
2770 1
        return $conversionGroups;
2771
    }
2772
2773
    /**
2774
     *    getConversionMultipliers
2775
     * Returns an array of the Multiplier prefixes that can be used with Units of Measure in CONVERTUOM().
2776
     *
2777
     * @return array of mixed
2778
     */
2779 1
    public static function getConversionMultipliers()
2780
    {
2781 1
        return self::$conversionMultipliers;
2782
    }
2783
2784
    /**
2785
     * CONVERTUOM.
2786
     *
2787
     * Converts a number from one measurement system to another.
2788
     *    For example, CONVERT can translate a table of distances in miles to a table of distances
2789
     * in kilometers.
2790
     *
2791
     *    Excel Function:
2792
     *        CONVERT(value,fromUOM,toUOM)
2793
     *
2794
     * @param float $value the value in fromUOM to convert
2795
     * @param string $fromUOM the units for value
2796
     * @param string $toUOM the units for the result
2797
     *
2798
     * @return float
2799
     */
2800 24
    public static function CONVERTUOM($value, $fromUOM, $toUOM)
2801
    {
2802 24
        $value = Functions::flattenSingleValue($value);
2803 24
        $fromUOM = Functions::flattenSingleValue($fromUOM);
2804 24
        $toUOM = Functions::flattenSingleValue($toUOM);
2805
2806 24
        if (!is_numeric($value)) {
2807 1
            return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type double.
Loading history...
2808
        }
2809 23
        $fromMultiplier = 1.0;
2810 23
        if (isset(self::$conversionUnits[$fromUOM])) {
2811 16
            $unitGroup1 = self::$conversionUnits[$fromUOM]['Group'];
2812
        } else {
2813 7
            $fromMultiplier = substr($fromUOM, 0, 1);
2814 7
            $fromUOM = substr($fromUOM, 1);
2815 7
            if (isset(self::$conversionMultipliers[$fromMultiplier])) {
2816 6
                $fromMultiplier = self::$conversionMultipliers[$fromMultiplier]['multiplier'];
2817
            } else {
2818 1
                return Functions::NA();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...ulation\Functions::NA() returns the type string which is incompatible with the documented return type double.
Loading history...
2819
            }
2820 6
            if ((isset(self::$conversionUnits[$fromUOM])) && (self::$conversionUnits[$fromUOM]['AllowPrefix'])) {
2821 5
                $unitGroup1 = self::$conversionUnits[$fromUOM]['Group'];
2822
            } else {
2823 1
                return Functions::NA();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...ulation\Functions::NA() returns the type string which is incompatible with the documented return type double.
Loading history...
2824
            }
2825
        }
2826 21
        $value *= $fromMultiplier;
2827
2828 21
        $toMultiplier = 1.0;
2829 21
        if (isset(self::$conversionUnits[$toUOM])) {
2830 14
            $unitGroup2 = self::$conversionUnits[$toUOM]['Group'];
2831
        } else {
2832 7
            $toMultiplier = substr($toUOM, 0, 1);
2833 7
            $toUOM = substr($toUOM, 1);
2834 7
            if (isset(self::$conversionMultipliers[$toMultiplier])) {
2835 6
                $toMultiplier = self::$conversionMultipliers[$toMultiplier]['multiplier'];
2836
            } else {
2837 1
                return Functions::NA();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...ulation\Functions::NA() returns the type string which is incompatible with the documented return type double.
Loading history...
2838
            }
2839 6
            if ((isset(self::$conversionUnits[$toUOM])) && (self::$conversionUnits[$toUOM]['AllowPrefix'])) {
2840 5
                $unitGroup2 = self::$conversionUnits[$toUOM]['Group'];
2841
            } else {
2842 1
                return Functions::NA();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...ulation\Functions::NA() returns the type string which is incompatible with the documented return type double.
Loading history...
2843
            }
2844
        }
2845 19
        if ($unitGroup1 != $unitGroup2) {
2846 2
            return Functions::NA();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...ulation\Functions::NA() returns the type string which is incompatible with the documented return type double.
Loading history...
2847
        }
2848
2849 17
        if (($fromUOM == $toUOM) && ($fromMultiplier == $toMultiplier)) {
2850
            //    We've already factored $fromMultiplier into the value, so we need
2851
            //        to reverse it again
2852 3
            return $value / $fromMultiplier;
2853 14
        } elseif ($unitGroup1 == 'Temperature') {
2854 9
            if (($fromUOM == 'F') || ($fromUOM == 'fah')) {
2855 3
                if (($toUOM == 'F') || ($toUOM == 'fah')) {
2856 1
                    return $value;
2857
                }
2858 2
                $value = (($value - 32) / 1.8);
2859 2
                if (($toUOM == 'K') || ($toUOM == 'kel')) {
2860 1
                    $value += 273.15;
2861
                }
2862
2863 2
                return $value;
2864 6
            } elseif ((($fromUOM == 'K') || ($fromUOM == 'kel')) &&
2865 6
                (($toUOM == 'K') || ($toUOM == 'kel'))
2866
            ) {
2867 1
                return $value;
2868 5
            } elseif ((($fromUOM == 'C') || ($fromUOM == 'cel')) &&
2869 5
                (($toUOM == 'C') || ($toUOM == 'cel'))
2870
            ) {
2871 1
                return $value;
2872
            }
2873 4
            if (($toUOM == 'F') || ($toUOM == 'fah')) {
2874 2
                if (($fromUOM == 'K') || ($fromUOM == 'kel')) {
2875 1
                    $value -= 273.15;
2876
                }
2877
2878 2
                return ($value * 1.8) + 32;
2879
            }
2880 2
            if (($toUOM == 'C') || ($toUOM == 'cel')) {
2881 1
                return $value - 273.15;
2882
            }
2883
2884 1
            return $value + 273.15;
2885
        }
2886
2887 5
        return ($value * self::$unitConversions[$unitGroup1][$fromUOM][$toUOM]) / $toMultiplier;
2888
    }
2889
}
2890