Completed
Push — develop ( 3ee9cc...870d86 )
by Adrien
29:45
created

Statistical::COUNTBLANK()   B

Complexity

Conditions 5
Paths 3

Size

Total Lines 15
Code Lines 7

Duplication

Lines 15
Ratio 100 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
cc 5
eloc 7
nc 3
nop 1
dl 15
loc 15
ccs 0
cts 7
cp 0
crap 30
rs 8.8571
c 0
b 0
f 0
1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet\Calculation;
4
5
/* LOG_GAMMA_X_MAX_VALUE */
6 3
define('LOG_GAMMA_X_MAX_VALUE', 2.55e305);
7
8
/* XMININ */
9 3
define('XMININ', 2.23e-308);
10
11
/* EPS */
12 3
define('EPS', 2.22e-16);
13
14
/* SQRT2PI */
15 3
define('SQRT2PI', 2.5066282746310005024157652848110452530069867406099);
16
17
/**
18
 * Copyright (c) 2006 - 2016 PhpSpreadsheet.
19
 *
20
 * This library is free software; you can redistribute it and/or
21
 * modify it under the terms of the GNU Lesser General Public
22
 * License as published by the Free Software Foundation; either
23
 * version 2.1 of the License, or (at your option) any later version.
24
 *
25
 * This library is distributed in the hope that it will be useful,
26
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
28
 * Lesser General Public License for more details.
29
 *
30
 * You should have received a copy of the GNU Lesser General Public
31
 * License along with this library; if not, write to the Free Software
32
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
33
 *
34
 * @category    PhpSpreadsheet
35
 *
36
 * @copyright   Copyright (c) 2006 - 2016 PhpSpreadsheet (https://github.com/PHPOffice/PhpSpreadsheet)
37
 * @license     http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt    LGPL
38
 */
39
class Statistical
40
{
41
    private static function checkTrendArrays(&$array1, &$array2)
42
    {
43
        if (!is_array($array1)) {
44
            $array1 = [$array1];
45
        }
46
        if (!is_array($array2)) {
47
            $array2 = [$array2];
48
        }
49
50
        $array1 = Functions::flattenArray($array1);
51
        $array2 = Functions::flattenArray($array2);
52 View Code Duplication
        foreach ($array1 as $key => $value) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
53
            if ((is_bool($value)) || (is_string($value)) || (is_null($value))) {
54
                unset($array1[$key], $array2[$key]);
55
            }
56
        }
57 View Code Duplication
        foreach ($array2 as $key => $value) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
58
            if ((is_bool($value)) || (is_string($value)) || (is_null($value))) {
59
                unset($array1[$key], $array2[$key]);
60
            }
61
        }
62
        $array1 = array_merge($array1);
63
        $array2 = array_merge($array2);
64
65
        return true;
66
    }
67
68
    /**
69
     * Beta function.
70
     *
71
     * @author Jaco van Kooten
72
     *
73
     * @param p require p>0
74
     * @param q require q>0
75
     * @param mixed $p
76
     * @param mixed $q
77
     *
78
     * @return 0 if p<=0, q<=0 or p+q>2.55E305 to avoid errors and over/underflow
0 ignored issues
show
Documentation introduced by
The doc-type 0 could not be parsed: Unknown type name "0" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
79
     */
80
    private static function beta($p, $q)
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
81
    {
82 View Code Duplication
        if ($p <= 0.0 || $q <= 0.0 || ($p + $q) > LOG_GAMMA_X_MAX_VALUE) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
83
            return 0.0;
84
        }
85
86
        return exp(self::logBeta($p, $q));
87
    }
88
89
    /**
90
     * Incomplete beta function.
91
     *
92
     * @author Jaco van Kooten
93
     * @author Paul Meagher
94
     *
95
     * The computation is based on formulas from Numerical Recipes, Chapter 6.4 (W.H. Press et al, 1992).
96
     *
97
     * @param x require 0<=x<=1
98
     * @param p require p>0
99
     * @param q require q>0
100
     * @param mixed $x
101
     * @param mixed $p
102
     * @param mixed $q
103
     *
104
     * @return 0 if x<0, p<=0, q<=0 or p+q>2.55E305 and 1 if x>1 to avoid errors and over/underflow
0 ignored issues
show
Documentation introduced by
The doc-type 0 could not be parsed: Unknown type name "0" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
105
     */
106
    private static function incompleteBeta($x, $p, $q)
107
    {
108
        if ($x <= 0.0) {
109
            return 0.0;
110
        } elseif ($x >= 1.0) {
111
            return 1.0;
112 View Code Duplication
        } elseif (($p <= 0.0) || ($q <= 0.0) || (($p + $q) > LOG_GAMMA_X_MAX_VALUE)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
113
            return 0.0;
114
        }
115
        $beta_gam = exp((0 - self::logBeta($p, $q)) + $p * log($x) + $q * log(1.0 - $x));
116
        if ($x < ($p + 1.0) / ($p + $q + 2.0)) {
117
            return $beta_gam * self::betaFraction($x, $p, $q) / $p;
118
        }
119
120
        return 1.0 - ($beta_gam * self::betaFraction(1 - $x, $q, $p) / $q);
121
    }
122
123
    // Function cache for logBeta function
124
    private static $logBetaCacheP = 0.0;
125
    private static $logBetaCacheQ = 0.0;
126
    private static $logBetaCacheResult = 0.0;
127
128
    /**
129
     * The natural logarithm of the beta function.
130
     *
131
     * @param p require p>0
132
     * @param q require q>0
133
     * @param mixed $p
134
     * @param mixed $q
135
     *
136
     * @return 0 if p<=0, q<=0 or p+q>2.55E305 to avoid errors and over/underflow
0 ignored issues
show
Documentation introduced by
The doc-type 0 could not be parsed: Unknown type name "0" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
137
     *
138
     * @author Jaco van Kooten
139
     */
140
    private static function logBeta($p, $q)
141
    {
142
        if ($p != self::$logBetaCacheP || $q != self::$logBetaCacheQ) {
143
            self::$logBetaCacheP = $p;
144
            self::$logBetaCacheQ = $q;
145
            if (($p <= 0.0) || ($q <= 0.0) || (($p + $q) > LOG_GAMMA_X_MAX_VALUE)) {
146
                self::$logBetaCacheResult = 0.0;
147
            } else {
148
                self::$logBetaCacheResult = self::logGamma($p) + self::logGamma($q) - self::logGamma($p + $q);
149
            }
150
        }
151
152
        return self::$logBetaCacheResult;
153
    }
154
155
    /**
156
     * Evaluates of continued fraction part of incomplete beta function.
157
     * Based on an idea from Numerical Recipes (W.H. Press et al, 1992).
158
     *
159
     * @author Jaco van Kooten
160
     *
161
     * @param mixed $x
162
     * @param mixed $p
163
     * @param mixed $q
164
     */
165
    private static function betaFraction($x, $p, $q)
166
    {
167
        $c = 1.0;
168
        $sum_pq = $p + $q;
169
        $p_plus = $p + 1.0;
170
        $p_minus = $p - 1.0;
171
        $h = 1.0 - $sum_pq * $x / $p_plus;
172
        if (abs($h) < XMININ) {
173
            $h = XMININ;
174
        }
175
        $h = 1.0 / $h;
176
        $frac = $h;
177
        $m = 1;
178
        $delta = 0.0;
179
        while ($m <= MAX_ITERATIONS && abs($delta - 1.0) > PRECISION) {
180
            $m2 = 2 * $m;
181
            // even index for d
182
            $d = $m * ($q - $m) * $x / (($p_minus + $m2) * ($p + $m2));
183
            $h = 1.0 + $d * $h;
184
            if (abs($h) < XMININ) {
185
                $h = XMININ;
186
            }
187
            $h = 1.0 / $h;
188
            $c = 1.0 + $d / $c;
189
            if (abs($c) < XMININ) {
190
                $c = XMININ;
191
            }
192
            $frac *= $h * $c;
193
            // odd index for d
194
            $d = -($p + $m) * ($sum_pq + $m) * $x / (($p + $m2) * ($p_plus + $m2));
195
            $h = 1.0 + $d * $h;
196
            if (abs($h) < XMININ) {
197
                $h = XMININ;
198
            }
199
            $h = 1.0 / $h;
200
            $c = 1.0 + $d / $c;
201
            if (abs($c) < XMININ) {
202
                $c = XMININ;
203
            }
204
            $delta = $h * $c;
205
            $frac *= $delta;
206
            ++$m;
207
        }
208
209
        return $frac;
210
    }
211
212
    /**
213
     * logGamma function.
214
     *
215
     * @version 1.1
216
     *
217
     * @author Jaco van Kooten
218
     *
219
     * Original author was Jaco van Kooten. Ported to PHP by Paul Meagher.
220
     *
221
     * The natural logarithm of the gamma function. <br />
222
     * Based on public domain NETLIB (Fortran) code by W. J. Cody and L. Stoltz <br />
223
     * Applied Mathematics Division <br />
224
     * Argonne National Laboratory <br />
225
     * Argonne, IL 60439 <br />
226
     * <p>
227
     * References:
228
     * <ol>
229
     * <li>W. J. Cody and K. E. Hillstrom, 'Chebyshev Approximations for the Natural
230
     *     Logarithm of the Gamma Function,' Math. Comp. 21, 1967, pp. 198-203.</li>
231
     * <li>K. E. Hillstrom, ANL/AMD Program ANLC366S, DGAMMA/DLGAMA, May, 1969.</li>
232
     * <li>Hart, Et. Al., Computer Approximations, Wiley and sons, New York, 1968.</li>
233
     * </ol>
234
     * </p>
235
     * <p>
236
     * From the original documentation:
237
     * </p>
238
     * <p>
239
     * This routine calculates the LOG(GAMMA) function for a positive real argument X.
240
     * Computation is based on an algorithm outlined in references 1 and 2.
241
     * The program uses rational functions that theoretically approximate LOG(GAMMA)
242
     * to at least 18 significant decimal digits. The approximation for X > 12 is from
243
     * reference 3, while approximations for X < 12.0 are similar to those in reference
244
     * 1, but are unpublished. The accuracy achieved depends on the arithmetic system,
245
     * the compiler, the intrinsic functions, and proper selection of the
246
     * machine-dependent constants.
247
     * </p>
248
     * <p>
249
     * Error returns: <br />
250
     * The program returns the value XINF for X .LE. 0.0 or when overflow would occur.
251
     * The computation is believed to be free of underflow and overflow.
252
     * </p>
253
     *
254
     * @return MAX_VALUE for x < 0.0 or when overflow would occur, i.e. x > 2.55E305
255
     */
256
257
    // Function cache for logGamma
258
    private static $logGammaCacheResult = 0.0;
259
    private static $logGammaCacheX = 0.0;
260
261
    private static function logGamma($x)
262
    {
263
        // Log Gamma related constants
264
        static $lg_d1 = -0.5772156649015328605195174;
265
        static $lg_d2 = 0.4227843350984671393993777;
266
        static $lg_d4 = 1.791759469228055000094023;
267
268
        static $lg_p1 = [
269
            4.945235359296727046734888,
270
            201.8112620856775083915565,
271
            2290.838373831346393026739,
272
            11319.67205903380828685045,
273
            28557.24635671635335736389,
274
            38484.96228443793359990269,
275
            26377.48787624195437963534,
276
            7225.813979700288197698961,
277
        ];
278
        static $lg_p2 = [
279
            4.974607845568932035012064,
280
            542.4138599891070494101986,
281
            15506.93864978364947665077,
282
            184793.2904445632425417223,
283
            1088204.76946882876749847,
284
            3338152.967987029735917223,
285
            5106661.678927352456275255,
286
            3074109.054850539556250927,
287
        ];
288
        static $lg_p4 = [
289
            14745.02166059939948905062,
290
            2426813.369486704502836312,
291
            121475557.4045093227939592,
292
            2663432449.630976949898078,
293
            29403789566.34553899906876,
294
            170266573776.5398868392998,
295
            492612579337.743088758812,
296
            560625185622.3951465078242,
297
        ];
298
        static $lg_q1 = [
299
            67.48212550303777196073036,
300
            1113.332393857199323513008,
301
            7738.757056935398733233834,
302
            27639.87074403340708898585,
303
            54993.10206226157329794414,
304
            61611.22180066002127833352,
305
            36351.27591501940507276287,
306
            8785.536302431013170870835,
307
        ];
308
        static $lg_q2 = [
309
            183.0328399370592604055942,
310
            7765.049321445005871323047,
311
            133190.3827966074194402448,
312
            1136705.821321969608938755,
313
            5267964.117437946917577538,
314
            13467014.54311101692290052,
315
            17827365.30353274213975932,
316
            9533095.591844353613395747,
317
        ];
318
        static $lg_q4 = [
319
            2690.530175870899333379843,
320
            639388.5654300092398984238,
321
            41355999.30241388052042842,
322
            1120872109.61614794137657,
323
            14886137286.78813811542398,
324
            101680358627.2438228077304,
325
            341747634550.7377132798597,
326
            446315818741.9713286462081,
327
        ];
328
        static $lg_c = [
329
            -0.001910444077728,
330
            8.4171387781295e-4,
331
            -5.952379913043012e-4,
332
            7.93650793500350248e-4,
333
            -0.002777777777777681622553,
334
            0.08333333333333333331554247,
335
            0.0057083835261,
336
        ];
337
338
        // Rough estimate of the fourth root of logGamma_xBig
339
        static $lg_frtbig = 2.25e76;
340
        static $pnt68 = 0.6796875;
341
342
        if ($x == self::$logGammaCacheX) {
343
            return self::$logGammaCacheResult;
344
        }
345
        $y = $x;
346
        if ($y > 0.0 && $y <= LOG_GAMMA_X_MAX_VALUE) {
347
            if ($y <= EPS) {
348
                $res = -log(y);
349
            } elseif ($y <= 1.5) {
350
                // ---------------------
351
                //    EPS .LT. X .LE. 1.5
352
                // ---------------------
353
                if ($y < $pnt68) {
354
                    $corr = -log($y);
355
                    $xm1 = $y;
356
                } else {
357
                    $corr = 0.0;
358
                    $xm1 = $y - 1.0;
359
                }
360
                if ($y <= 0.5 || $y >= $pnt68) {
361
                    $xden = 1.0;
362
                    $xnum = 0.0;
363
                    for ($i = 0; $i < 8; ++$i) {
364
                        $xnum = $xnum * $xm1 + $lg_p1[$i];
365
                        $xden = $xden * $xm1 + $lg_q1[$i];
366
                    }
367
                    $res = $corr + $xm1 * ($lg_d1 + $xm1 * ($xnum / $xden));
368 View Code Duplication
                } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
369
                    $xm2 = $y - 1.0;
370
                    $xden = 1.0;
371
                    $xnum = 0.0;
372
                    for ($i = 0; $i < 8; ++$i) {
373
                        $xnum = $xnum * $xm2 + $lg_p2[$i];
374
                        $xden = $xden * $xm2 + $lg_q2[$i];
375
                    }
376
                    $res = $corr + $xm2 * ($lg_d2 + $xm2 * ($xnum / $xden));
377
                }
378 View Code Duplication
            } elseif ($y <= 4.0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
379
                // ---------------------
380
                //    1.5 .LT. X .LE. 4.0
381
                // ---------------------
382
                $xm2 = $y - 2.0;
383
                $xden = 1.0;
384
                $xnum = 0.0;
385
                for ($i = 0; $i < 8; ++$i) {
386
                    $xnum = $xnum * $xm2 + $lg_p2[$i];
387
                    $xden = $xden * $xm2 + $lg_q2[$i];
388
                }
389
                $res = $xm2 * ($lg_d2 + $xm2 * ($xnum / $xden));
390
            } elseif ($y <= 12.0) {
391
                // ----------------------
392
                //    4.0 .LT. X .LE. 12.0
393
                // ----------------------
394
                $xm4 = $y - 4.0;
395
                $xden = -1.0;
396
                $xnum = 0.0;
397
                for ($i = 0; $i < 8; ++$i) {
398
                    $xnum = $xnum * $xm4 + $lg_p4[$i];
399
                    $xden = $xden * $xm4 + $lg_q4[$i];
400
                }
401
                $res = $lg_d4 + $xm4 * ($xnum / $xden);
402
            } else {
403
                // ---------------------------------
404
                //    Evaluate for argument .GE. 12.0
405
                // ---------------------------------
406
                $res = 0.0;
407
                if ($y <= $lg_frtbig) {
408
                    $res = $lg_c[6];
409
                    $ysq = $y * $y;
410
                    for ($i = 0; $i < 6; ++$i) {
411
                        $res = $res / $ysq + $lg_c[$i];
412
                    }
413
                    $res /= $y;
414
                    $corr = log($y);
415
                    $res = $res + log(SQRT2PI) - 0.5 * $corr;
416
                    $res += $y * ($corr - 1.0);
417
                }
418
            }
419
        } else {
420
            // --------------------------
421
            //    Return for bad arguments
422
            // --------------------------
423
            $res = MAX_VALUE;
424
        }
425
        // ------------------------------
426
        //    Final adjustments and return
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
427
        // ------------------------------
428
        self::$logGammaCacheX = $x;
429
        self::$logGammaCacheResult = $res;
430
431
        return $res;
432
    }
433
434
    //
435
    //    Private implementation of the incomplete Gamma function
436
    //
437
    private static function incompleteGamma($a, $x)
438
    {
439
        static $max = 32;
440
        $summer = 0;
441
        for ($n = 0; $n <= $max; ++$n) {
442
            $divisor = $a;
443
            for ($i = 1; $i <= $n; ++$i) {
444
                $divisor *= ($a + $i);
445
            }
446
            $summer += (pow($x, $n) / $divisor);
447
        }
448
449
        return pow($x, $a) * exp(0 - $x) * $summer;
450
    }
451
452
    //
453
    //    Private implementation of the Gamma function
454
    //
455
    private static function gamma($data)
456
    {
457
        if ($data == 0.0) {
458
            return 0;
459
        }
460
461
        static $p0 = 1.000000000190015;
462
        static $p = [
463
            1 => 76.18009172947146,
464
            2 => -86.50532032941677,
465
            3 => 24.01409824083091,
466
            4 => -1.231739572450155,
467
            5 => 1.208650973866179e-3,
468
            6 => -5.395239384953e-6,
469
        ];
470
471
        $y = $x = $data;
472
        $tmp = $x + 5.5;
473
        $tmp -= ($x + 0.5) * log($tmp);
474
475
        $summer = $p0;
476
        for ($j = 1; $j <= 6; ++$j) {
477
            $summer += ($p[$j] / ++$y);
478
        }
479
480
        return exp(0 - $tmp + log(SQRT2PI * $summer / $x));
481
    }
482
483
    /***************************************************************************
484
     *                                inverse_ncdf.php
485
     *                            -------------------
486
     *    begin                : Friday, January 16, 2004
487
     *    copyright            : (C) 2004 Michael Nickerson
488
     *    email                : [email protected]
489
     *
490
     ***************************************************************************/
491
    private static function inverseNcdf($p)
492
    {
493
        //    Inverse ncdf approximation by Peter J. Acklam, implementation adapted to
494
        //    PHP by Michael Nickerson, using Dr. Thomas Ziegler's C implementation as
495
        //    a guide. http://home.online.no/~pjacklam/notes/invnorm/index.html
496
        //    I have not checked the accuracy of this implementation. Be aware that PHP
497
        //    will truncate the coeficcients to 14 digits.
498
499
        //    You have permission to use and distribute this function freely for
500
        //    whatever purpose you want, but please show common courtesy and give credit
501
        //    where credit is due.
502
503
        //    Input paramater is $p - probability - where 0 < p < 1.
504
505
        //    Coefficients in rational approximations
506
        static $a = [
507
            1 => -3.969683028665376e+01,
508
            2 => 2.209460984245205e+02,
509
            3 => -2.759285104469687e+02,
510
            4 => 1.383577518672690e+02,
511
            5 => -3.066479806614716e+01,
512
            6 => 2.506628277459239e+00,
513
        ];
514
515
        static $b = [
516
            1 => -5.447609879822406e+01,
517
            2 => 1.615858368580409e+02,
518
            3 => -1.556989798598866e+02,
519
            4 => 6.680131188771972e+01,
520
            5 => -1.328068155288572e+01,
521
        ];
522
523
        static $c = [
524
            1 => -7.784894002430293e-03,
525
            2 => -3.223964580411365e-01,
526
            3 => -2.400758277161838e+00,
527
            4 => -2.549732539343734e+00,
528
            5 => 4.374664141464968e+00,
529
            6 => 2.938163982698783e+00,
530
        ];
531
532
        static $d = [
533
            1 => 7.784695709041462e-03,
534
            2 => 3.224671290700398e-01,
535
            3 => 2.445134137142996e+00,
536
            4 => 3.754408661907416e+00,
537
        ];
538
539
        //    Define lower and upper region break-points.
540
        $p_low = 0.02425; //Use lower region approx. below this
541
        $p_high = 1 - $p_low; //Use upper region approx. above this
542
543
        if (0 < $p && $p < $p_low) {
544
            //    Rational approximation for lower region.
545
            $q = sqrt(-2 * log($p));
546
547
            return ((((($c[1] * $q + $c[2]) * $q + $c[3]) * $q + $c[4]) * $q + $c[5]) * $q + $c[6]) /
548
                    (((($d[1] * $q + $d[2]) * $q + $d[3]) * $q + $d[4]) * $q + 1);
549
        } elseif ($p_low <= $p && $p <= $p_high) {
550
            //    Rational approximation for central region.
551
            $q = $p - 0.5;
552
            $r = $q * $q;
553
554
            return ((((($a[1] * $r + $a[2]) * $r + $a[3]) * $r + $a[4]) * $r + $a[5]) * $r + $a[6]) * $q /
555
                   ((((($b[1] * $r + $b[2]) * $r + $b[3]) * $r + $b[4]) * $r + $b[5]) * $r + 1);
556
        } elseif ($p_high < $p && $p < 1) {
557
            //    Rational approximation for upper region.
558
            $q = sqrt(-2 * log(1 - $p));
559
560
            return -((((($c[1] * $q + $c[2]) * $q + $c[3]) * $q + $c[4]) * $q + $c[5]) * $q + $c[6]) /
561
                     (((($d[1] * $q + $d[2]) * $q + $d[3]) * $q + $d[4]) * $q + 1);
562
        }
563
        //    If 0 < p < 1, return a null value
564
        return Functions::NULL();
565
    }
566
567
    private static function inverseNcdf2($prob)
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
568
    {
569
        //    Approximation of inverse standard normal CDF developed by
570
        //    B. Moro, "The Full Monte," Risk 8(2), Feb 1995, 57-58.
0 ignored issues
show
Unused Code Comprehensibility introduced by
44% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
571
572
        $a1 = 2.50662823884;
573
        $a2 = -18.61500062529;
574
        $a3 = 41.39119773534;
575
        $a4 = -25.44106049637;
576
577
        $b1 = -8.4735109309;
578
        $b2 = 23.08336743743;
579
        $b3 = -21.06224101826;
580
        $b4 = 3.13082909833;
581
582
        $c1 = 0.337475482272615;
583
        $c2 = 0.976169019091719;
584
        $c3 = 0.160797971491821;
585
        $c4 = 2.76438810333863E-02;
586
        $c5 = 3.8405729373609E-03;
587
        $c6 = 3.951896511919E-04;
588
        $c7 = 3.21767881768E-05;
589
        $c8 = 2.888167364E-07;
590
        $c9 = 3.960315187E-07;
591
592
        $y = $prob - 0.5;
593
        if (abs($y) < 0.42) {
594
            $z = ($y * $y);
595
            $z = $y * ((($a4 * $z + $a3) * $z + $a2) * $z + $a1) / (((($b4 * $z + $b3) * $z + $b2) * $z + $b1) * $z + 1);
596
        } else {
597
            if ($y > 0) {
598
                $z = log(-log(1 - $prob));
599
            } else {
600
                $z = log(-log($prob));
601
            }
602
            $z = $c1 + $z * ($c2 + $z * ($c3 + $z * ($c4 + $z * ($c5 + $z * ($c6 + $z * ($c7 + $z * ($c8 + $z * $c9)))))));
603
            if ($y < 0) {
604
                $z = -$z;
605
            }
606
        }
607
608
        return $z;
609
    }
610
611
    //    function inverseNcdf2()
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
612
613
    private static function inverseNcdf3($p)
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
614
    {
615
        //    ALGORITHM AS241 APPL. STATIST. (1988) VOL. 37, NO. 3.
616
        //    Produces the normal deviate Z corresponding to a given lower
617
        //    tail area of P; Z is accurate to about 1 part in 10**16.
618
        //
619
        //    This is a PHP version of the original FORTRAN code that can
620
        //    be found at http://lib.stat.cmu.edu/apstat/
621
        $split1 = 0.425;
0 ignored issues
show
Unused Code introduced by
$split1 is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
622
        $split2 = 5;
623
        $const1 = 0.180625;
624
        $const2 = 1.6;
625
626
        //    coefficients for p close to 0.5
627
        $a0 = 3.3871328727963666080;
628
        $a1 = 1.3314166789178437745E+2;
629
        $a2 = 1.9715909503065514427E+3;
630
        $a3 = 1.3731693765509461125E+4;
631
        $a4 = 4.5921953931549871457E+4;
632
        $a5 = 6.7265770927008700853E+4;
633
        $a6 = 3.3430575583588128105E+4;
634
        $a7 = 2.5090809287301226727E+3;
635
636
        $b1 = 4.2313330701600911252E+1;
637
        $b2 = 6.8718700749205790830E+2;
638
        $b3 = 5.3941960214247511077E+3;
639
        $b4 = 2.1213794301586595867E+4;
640
        $b5 = 3.9307895800092710610E+4;
641
        $b6 = 2.8729085735721942674E+4;
642
        $b7 = 5.2264952788528545610E+3;
643
644
        //    coefficients for p not close to 0, 0.5 or 1.
645
        $c0 = 1.42343711074968357734;
646
        $c1 = 4.63033784615654529590;
647
        $c2 = 5.76949722146069140550;
648
        $c3 = 3.64784832476320460504;
649
        $c4 = 1.27045825245236838258;
650
        $c5 = 2.41780725177450611770E-1;
651
        $c6 = 2.27238449892691845833E-2;
652
        $c7 = 7.74545014278341407640E-4;
653
654
        $d1 = 2.05319162663775882187;
655
        $d2 = 1.67638483018380384940;
656
        $d3 = 6.89767334985100004550E-1;
657
        $d4 = 1.48103976427480074590E-1;
658
        $d5 = 1.51986665636164571966E-2;
659
        $d6 = 5.47593808499534494600E-4;
660
        $d7 = 1.05075007164441684324E-9;
661
662
        //    coefficients for p near 0 or 1.
663
        $e0 = 6.65790464350110377720;
664
        $e1 = 5.46378491116411436990;
665
        $e2 = 1.78482653991729133580;
666
        $e3 = 2.96560571828504891230E-1;
667
        $e4 = 2.65321895265761230930E-2;
668
        $e5 = 1.24266094738807843860E-3;
669
        $e6 = 2.71155556874348757815E-5;
670
        $e7 = 2.01033439929228813265E-7;
671
672
        $f1 = 5.99832206555887937690E-1;
673
        $f2 = 1.36929880922735805310E-1;
674
        $f3 = 1.48753612908506148525E-2;
675
        $f4 = 7.86869131145613259100E-4;
676
        $f5 = 1.84631831751005468180E-5;
677
        $f6 = 1.42151175831644588870E-7;
678
        $f7 = 2.04426310338993978564E-15;
679
680
        $q = $p - 0.5;
681
682
        //    computation for p close to 0.5
683
        if (abs($q) <= split1) {
684
            $R = $const1 - $q * $q;
685
            $z = $q * ((((((($a7 * $R + $a6) * $R + $a5) * $R + $a4) * $R + $a3) * $R + $a2) * $R + $a1) * $R + $a0) /
686
                      ((((((($b7 * $R + $b6) * $R + $b5) * $R + $b4) * $R + $b3) * $R + $b2) * $R + $b1) * $R + 1);
687
        } else {
688
            if ($q < 0) {
689
                $R = $p;
690
            } else {
691
                $R = 1 - $p;
692
            }
693
            $R = pow(-log($R), 2);
694
695
            //    computation for p not close to 0, 0.5 or 1.
696
            if ($R <= $split2) {
697
                $R = $R - $const2;
698
                $z = ((((((($c7 * $R + $c6) * $R + $c5) * $R + $c4) * $R + $c3) * $R + $c2) * $R + $c1) * $R + $c0) /
699
                     ((((((($d7 * $R + $d6) * $R + $d5) * $R + $d4) * $R + $d3) * $R + $d2) * $R + $d1) * $R + 1);
700
            } else {
701
                //    computation for p near 0 or 1.
702
                $R = $R - $split2;
703
                $z = ((((((($e7 * $R + $e6) * $R + $e5) * $R + $e4) * $R + $e3) * $R + $e2) * $R + $e1) * $R + $e0) /
704
                     ((((((($f7 * $R + $f6) * $R + $f5) * $R + $f4) * $R + $f3) * $R + $f2) * $R + $f1) * $R + 1);
705
            }
706
            if ($q < 0) {
707
                $z = -$z;
708
            }
709
        }
710
711
        return $z;
712
    }
713
714
    /**
715
     * AVEDEV.
716
     *
717
     * Returns the average of the absolute deviations of data points from their mean.
718
     * AVEDEV is a measure of the variability in a data set.
719
     *
720
     * Excel Function:
721
     *        AVEDEV(value1[,value2[, ...]])
722
     *
723
     * @category Statistical Functions
724
     *
725
     * @param mixed $args Data values
726
     *
727
     * @return float
728
     */
729 View Code Duplication
    public static function AVEDEV(...$args)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
730
    {
731
        $aArgs = Functions::flattenArrayIndexed($args);
732
733
        // Return value
734
        $returnValue = null;
735
736
        $aMean = self::AVERAGE($aArgs);
737
        if ($aMean != Functions::DIV0()) {
738
            $aCount = 0;
739
            foreach ($aArgs as $k => $arg) {
740
                if ((is_bool($arg)) &&
741
                    ((!Functions::isCellValue($k)) || (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE))) {
742
                    $arg = (int) $arg;
743
                }
744
                // Is it a numeric value?
745
                if ((is_numeric($arg)) && (!is_string($arg))) {
746
                    if (is_null($returnValue)) {
747
                        $returnValue = abs($arg - $aMean);
748
                    } else {
749
                        $returnValue += abs($arg - $aMean);
750
                    }
751
                    ++$aCount;
752
                }
753
            }
754
755
            // Return
756
            if ($aCount == 0) {
757
                return Functions::DIV0();
758
            }
759
760
            return $returnValue / $aCount;
761
        }
762
763
        return Functions::NAN();
764
    }
765
766
    /**
767
     * AVERAGE.
768
     *
769
     * Returns the average (arithmetic mean) of the arguments
770
     *
771
     * Excel Function:
772
     *        AVERAGE(value1[,value2[, ...]])
773
     *
774
     * @category Statistical Functions
775
     *
776
     * @param mixed $args Data values
777
     *
778
     * @return float
779
     */
780 1
    public static function AVERAGE(...$args)
781
    {
782 1
        $returnValue = $aCount = 0;
783
784
        // Loop through arguments
785 1
        foreach (Functions::flattenArrayIndexed($args) as $k => $arg) {
786 1
            if ((is_bool($arg)) &&
787
                ((!Functions::isCellValue($k)) || (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE))) {
788
                $arg = (int) $arg;
789
            }
790
            // Is it a numeric value?
791 1
            if ((is_numeric($arg)) && (!is_string($arg))) {
792 1
                if (is_null($returnValue)) {
793
                    $returnValue = $arg;
794
                } else {
795 1
                    $returnValue += $arg;
796
                }
797 1
                ++$aCount;
798
            }
799
        }
800
801
        // Return
802 1
        if ($aCount > 0) {
803 1
            return $returnValue / $aCount;
804
        }
805
806
        return Functions::DIV0();
807
    }
808
809
    /**
810
     * AVERAGEA.
811
     *
812
     * Returns the average of its arguments, including numbers, text, and logical values
813
     *
814
     * Excel Function:
815
     *        AVERAGEA(value1[,value2[, ...]])
816
     *
817
     * @category Statistical Functions
818
     *
819
     * @param mixed $args Data values
820
     *
821
     * @return float
822
     */
823
    public static function AVERAGEA(...$args)
824
    {
825
        $returnValue = null;
826
827
        $aCount = 0;
828
        // Loop through arguments
829
        foreach (Functions::flattenArrayIndexed($args) as $k => $arg) {
830
            if ((is_bool($arg)) &&
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
831
                (!Functions::isMatrixValue($k))) {
832
            } else {
833
                if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
834
                    if (is_bool($arg)) {
835
                        $arg = (int) $arg;
836
                    } elseif (is_string($arg)) {
837
                        $arg = 0;
838
                    }
839
                    if (is_null($returnValue)) {
840
                        $returnValue = $arg;
841
                    } else {
842
                        $returnValue += $arg;
843
                    }
844
                    ++$aCount;
845
                }
846
            }
847
        }
848
849
        if ($aCount > 0) {
850
            return $returnValue / $aCount;
851
        }
852
853
        return Functions::DIV0();
854
    }
855
856
    /**
857
     * AVERAGEIF.
858
     *
859
     * Returns the average value from a range of cells that contain numbers within the list of arguments
860
     *
861
     * Excel Function:
862
     *        AVERAGEIF(value1[,value2[, ...]],condition)
863
     *
864
     * @category Mathematical and Trigonometric Functions
865
     *
866
     * @param mixed $aArgs Data values
867
     * @param string $condition the criteria that defines which cells will be checked
868
     * @param mixed[] $averageArgs Data values
869
     *
870
     * @return float
871
     */
872
    public static function AVERAGEIF($aArgs, $condition, $averageArgs = [])
873
    {
874
        $returnValue = 0;
875
876
        $aArgs = Functions::flattenArray($aArgs);
877
        $averageArgs = Functions::flattenArray($averageArgs);
878
        if (empty($averageArgs)) {
879
            $averageArgs = $aArgs;
0 ignored issues
show
Unused Code introduced by
$averageArgs is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
880
        }
881
        $condition = Functions::ifCondition($condition);
882
        // Loop through arguments
883
        $aCount = 0;
884
        foreach ($aArgs as $key => $arg) {
885
            if (!is_numeric($arg)) {
886
                $arg = \PhpOffice\PhpSpreadsheet\Calculation::wrapResult(strtoupper($arg));
887
            }
888
            $testCondition = '=' . $arg . $condition;
889
            if (\PhpOffice\PhpSpreadsheet\Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
890
                if ((is_null($returnValue)) || ($arg > $returnValue)) {
891
                    $returnValue += $arg;
892
                    ++$aCount;
893
                }
894
            }
895
        }
896
897
        if ($aCount > 0) {
898
            return $returnValue / $aCount;
899
        }
900
901
        return Functions::DIV0();
902
    }
903
904
    /**
905
     * BETADIST.
906
     *
907
     * Returns the beta distribution.
908
     *
909
     * @param float $value Value at which you want to evaluate the distribution
910
     * @param float $alpha Parameter to the distribution
911
     * @param float $beta Parameter to the distribution
912
     * @param mixed $rMin
913
     * @param mixed $rMax
914
     *
915
     * @return float
916
     */
917
    public static function BETADIST($value, $alpha, $beta, $rMin = 0, $rMax = 1)
918
    {
919
        $value = Functions::flattenSingleValue($value);
920
        $alpha = Functions::flattenSingleValue($alpha);
921
        $beta = Functions::flattenSingleValue($beta);
922
        $rMin = Functions::flattenSingleValue($rMin);
923
        $rMax = Functions::flattenSingleValue($rMax);
924
925
        if ((is_numeric($value)) && (is_numeric($alpha)) && (is_numeric($beta)) && (is_numeric($rMin)) && (is_numeric($rMax))) {
926
            if (($value < $rMin) || ($value > $rMax) || ($alpha <= 0) || ($beta <= 0) || ($rMin == $rMax)) {
927
                return Functions::NAN();
928
            }
929
            if ($rMin > $rMax) {
930
                $tmp = $rMin;
931
                $rMin = $rMax;
932
                $rMax = $tmp;
933
            }
934
            $value -= $rMin;
935
            $value /= ($rMax - $rMin);
936
937
            return self::incompleteBeta($value, $alpha, $beta);
0 ignored issues
show
Documentation introduced by
$value is of type integer|double, but the function expects a object<PhpOffice\PhpSpre...et\Calculation\require>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
$alpha is of type integer|double|string, but the function expects a object<PhpOffice\PhpSpre...et\Calculation\require>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
$beta is of type integer|double|string, but the function expects a object<PhpOffice\PhpSpre...et\Calculation\require>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
938
        }
939
940
        return Functions::VALUE();
941
    }
942
943
    /**
944
     * BETAINV.
945
     *
946
     * Returns the inverse of the beta distribution.
947
     *
948
     * @param float $probability Probability at which you want to evaluate the distribution
949
     * @param float $alpha Parameter to the distribution
950
     * @param float $beta Parameter to the distribution
951
     * @param float $rMin Minimum value
952
     * @param float $rMax Maximum value
953
     *
954
     * @return float
955
     */
956
    public static function BETAINV($probability, $alpha, $beta, $rMin = 0, $rMax = 1)
957
    {
958
        $probability = Functions::flattenSingleValue($probability);
959
        $alpha = Functions::flattenSingleValue($alpha);
960
        $beta = Functions::flattenSingleValue($beta);
961
        $rMin = Functions::flattenSingleValue($rMin);
962
        $rMax = Functions::flattenSingleValue($rMax);
963
964
        if ((is_numeric($probability)) && (is_numeric($alpha)) && (is_numeric($beta)) && (is_numeric($rMin)) && (is_numeric($rMax))) {
965 View Code Duplication
            if (($alpha <= 0) || ($beta <= 0) || ($rMin == $rMax) || ($probability <= 0) || ($probability > 1)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
966
                return Functions::NAN();
967
            }
968
            if ($rMin > $rMax) {
969
                $tmp = $rMin;
970
                $rMin = $rMax;
971
                $rMax = $tmp;
972
            }
973
            $a = 0;
974
            $b = 2;
975
976
            $i = 0;
977
            while ((($b - $a) > PRECISION) && ($i++ < MAX_ITERATIONS)) {
978
                $guess = ($a + $b) / 2;
979
                $result = self::BETADIST($guess, $alpha, $beta);
980
                if (($result == $probability) || ($result == 0)) {
981
                    $b = $a;
982
                } elseif ($result > $probability) {
983
                    $b = $guess;
984
                } else {
985
                    $a = $guess;
986
                }
987
            }
988
            if ($i == MAX_ITERATIONS) {
989
                return Functions::NA();
990
            }
991
992
            return round($rMin + $guess * ($rMax - $rMin), 12);
0 ignored issues
show
Bug introduced by
The variable $guess does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
993
        }
994
995
        return Functions::VALUE();
996
    }
997
998
    /**
999
     * BINOMDIST.
1000
     *
1001
     * Returns the individual term binomial distribution probability. Use BINOMDIST in problems with
1002
     *        a fixed number of tests or trials, when the outcomes of any trial are only success or failure,
1003
     *        when trials are independent, and when the probability of success is constant throughout the
1004
     *        experiment. For example, BINOMDIST can calculate the probability that two of the next three
1005
     *        babies born are male.
1006
     *
1007
     * @param float $value Number of successes in trials
1008
     * @param float $trials Number of trials
1009
     * @param float $probability Probability of success on each trial
1010
     * @param bool $cumulative
1011
     *
1012
     * @return float
1013
     *
1014
     * @todo    Cumulative distribution function
1015
     */
1016
    public static function BINOMDIST($value, $trials, $probability, $cumulative)
1017
    {
1018
        $value = floor(Functions::flattenSingleValue($value));
1019
        $trials = floor(Functions::flattenSingleValue($trials));
1020
        $probability = Functions::flattenSingleValue($probability);
1021
1022
        if ((is_numeric($value)) && (is_numeric($trials)) && (is_numeric($probability))) {
1023
            if (($value < 0) || ($value > $trials)) {
1024
                return Functions::NAN();
1025
            }
1026
            if (($probability < 0) || ($probability > 1)) {
1027
                return Functions::NAN();
1028
            }
1029
            if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
1030
                if ($cumulative) {
1031
                    $summer = 0;
1032
                    for ($i = 0; $i <= $value; ++$i) {
1033
                        $summer += MathTrig::COMBIN($trials, $i) * pow($probability, $i) * pow(1 - $probability, $trials - $i);
1034
                    }
1035
1036
                    return $summer;
1037
                }
1038
1039
                return MathTrig::COMBIN($trials, $value) * pow($probability, $value) * pow(1 - $probability, $trials - $value);
1040
            }
1041
        }
1042
1043
        return Functions::VALUE();
1044
    }
1045
1046
    /**
1047
     * CHIDIST.
1048
     *
1049
     * Returns the one-tailed probability of the chi-squared distribution.
1050
     *
1051
     * @param float $value Value for the function
1052
     * @param float $degrees degrees of freedom
1053
     *
1054
     * @return float
1055
     */
1056
    public static function CHIDIST($value, $degrees)
1057
    {
1058
        $value = Functions::flattenSingleValue($value);
1059
        $degrees = floor(Functions::flattenSingleValue($degrees));
1060
1061
        if ((is_numeric($value)) && (is_numeric($degrees))) {
1062
            if ($degrees < 1) {
1063
                return Functions::NAN();
1064
            }
1065
            if ($value < 0) {
1066
                if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
1067
                    return 1;
1068
                }
1069
1070
                return Functions::NAN();
1071
            }
1072
1073
            return 1 - (self::incompleteGamma($degrees / 2, $value / 2) / self::gamma($degrees / 2));
1074
        }
1075
1076
        return Functions::VALUE();
1077
    }
1078
1079
    /**
1080
     * CHIINV.
1081
     *
1082
     * Returns the one-tailed probability of the chi-squared distribution.
1083
     *
1084
     * @param float $probability Probability for the function
1085
     * @param float $degrees degrees of freedom
1086
     *
1087
     * @return float
1088
     */
1089 View Code Duplication
    public static function CHIINV($probability, $degrees)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1090
    {
1091
        $probability = Functions::flattenSingleValue($probability);
1092
        $degrees = floor(Functions::flattenSingleValue($degrees));
1093
1094
        if ((is_numeric($probability)) && (is_numeric($degrees))) {
1095
            $xLo = 100;
1096
            $xHi = 0;
1097
1098
            $x = $xNew = 1;
1099
            $dx = 1;
1100
            $i = 0;
1101
1102
            while ((abs($dx) > PRECISION) && ($i++ < MAX_ITERATIONS)) {
1103
                // Apply Newton-Raphson step
1104
                $result = self::CHIDIST($x, $degrees);
1105
                $error = $result - $probability;
1106
                if ($error == 0.0) {
1107
                    $dx = 0;
1108
                } elseif ($error < 0.0) {
1109
                    $xLo = $x;
1110
                } else {
1111
                    $xHi = $x;
1112
                }
1113
                // Avoid division by zero
1114
                if ($result != 0.0) {
1115
                    $dx = $error / $result;
1116
                    $xNew = $x - $dx;
1117
                }
1118
                // If the NR fails to converge (which for example may be the
1119
                // case if the initial guess is too rough) we apply a bisection
1120
                // step to determine a more narrow interval around the root.
1121
                if (($xNew < $xLo) || ($xNew > $xHi) || ($result == 0.0)) {
1122
                    $xNew = ($xLo + $xHi) / 2;
1123
                    $dx = $xNew - $x;
1124
                }
1125
                $x = $xNew;
1126
            }
1127
            if ($i == MAX_ITERATIONS) {
1128
                return Functions::NA();
1129
            }
1130
1131
            return round($x, 12);
1132
        }
1133
1134
        return Functions::VALUE();
1135
    }
1136
1137
    /**
1138
     * CONFIDENCE.
1139
     *
1140
     * Returns the confidence interval for a population mean
1141
     *
1142
     * @param float $alpha
1143
     * @param float $stdDev Standard Deviation
1144
     * @param float $size
1145
     *
1146
     * @return float
1147
     */
1148
    public static function CONFIDENCE($alpha, $stdDev, $size)
1149
    {
1150
        $alpha = Functions::flattenSingleValue($alpha);
1151
        $stdDev = Functions::flattenSingleValue($stdDev);
1152
        $size = floor(Functions::flattenSingleValue($size));
1153
1154
        if ((is_numeric($alpha)) && (is_numeric($stdDev)) && (is_numeric($size))) {
1155
            if (($alpha <= 0) || ($alpha >= 1)) {
1156
                return Functions::NAN();
1157
            }
1158
            if (($stdDev <= 0) || ($size < 1)) {
1159
                return Functions::NAN();
1160
            }
1161
1162
            return self::NORMSINV(1 - $alpha / 2) * $stdDev / sqrt($size);
1163
        }
1164
1165
        return Functions::VALUE();
1166
    }
1167
1168
    /**
1169
     * CORREL.
1170
     *
1171
     * Returns covariance, the average of the products of deviations for each data point pair.
1172
     *
1173
     * @param array of mixed Data Series Y
1174
     * @param array of mixed Data Series X
1175
     * @param mixed $yValues
1176
     * @param null|mixed $xValues
1177
     *
1178
     * @return float
1179
     */
1180
    public static function CORREL($yValues, $xValues = null)
1181
    {
1182
        if ((is_null($xValues)) || (!is_array($yValues)) || (!is_array($xValues))) {
1183
            return Functions::VALUE();
1184
        }
1185
        if (!self::checkTrendArrays($yValues, $xValues)) {
1186
            return Functions::VALUE();
1187
        }
1188
        $yValueCount = count($yValues);
1189
        $xValueCount = count($xValues);
1190
1191
        if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
1192
            return Functions::NA();
1193
        } elseif ($yValueCount == 1) {
1194
            return Functions::DIV0();
1195
        }
1196
1197
        $bestFitLinear = \PhpOffice\PhpSpreadsheet\Shared\trend\trend::calculate(\PhpOffice\PhpSpreadsheet\Shared\trend\trend::TREND_LINEAR, $yValues, $xValues);
1198
1199
        return $bestFitLinear->getCorrelation();
1200
    }
1201
1202
    /**
1203
     * COUNT.
1204
     *
1205
     * Counts the number of cells that contain numbers within the list of arguments
1206
     *
1207
     * Excel Function:
1208
     *        COUNT(value1[,value2[, ...]])
1209
     *
1210
     * @category Statistical Functions
1211
     *
1212
     * @param mixed $args Data values
1213
     *
1214
     * @return int
1215
     */
1216 1
    public static function COUNT(...$args)
1217
    {
1218 1
        $returnValue = 0;
1219
1220
        // Loop through arguments
1221 1
        $aArgs = Functions::flattenArrayIndexed($args);
1222 1
        foreach ($aArgs as $k => $arg) {
1223 1
            if ((is_bool($arg)) &&
1224 1
                ((!Functions::isCellValue($k)) || (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE))) {
1225
                $arg = (int) $arg;
1226
            }
1227
            // Is it a numeric value?
1228 1
            if ((is_numeric($arg)) && (!is_string($arg))) {
1229 1
                ++$returnValue;
1230
            }
1231
        }
1232
1233 1
        return $returnValue;
1234
    }
1235
1236
    /**
1237
     * COUNTA.
1238
     *
1239
     * Counts the number of cells that are not empty within the list of arguments
1240
     *
1241
     * Excel Function:
1242
     *        COUNTA(value1[,value2[, ...]])
1243
     *
1244
     * @category Statistical Functions
1245
     *
1246
     * @param mixed $args Data values
1247
     *
1248
     * @return int
1249
     */
1250
    public static function COUNTA(...$args)
1251
    {
1252
        $returnValue = 0;
1253
1254
        // Loop through arguments
1255
        $aArgs = Functions::flattenArray($args);
1256
        foreach ($aArgs as $arg) {
1257
            // Is it a numeric, boolean or string value?
1258
            if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
1259
                ++$returnValue;
1260
            }
1261
        }
1262
1263
        return $returnValue;
1264
    }
1265
1266
    /**
1267
     * COUNTBLANK.
1268
     *
1269
     * Counts the number of empty cells within the list of arguments
1270
     *
1271
     * Excel Function:
1272
     *        COUNTBLANK(value1[,value2[, ...]])
1273
     *
1274
     * @category Statistical Functions
1275
     *
1276
     * @param mixed $args Data values
1277
     *
1278
     * @return int
1279
     */
1280 View Code Duplication
    public static function COUNTBLANK(...$args)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1281
    {
1282
        $returnValue = 0;
1283
1284
        // Loop through arguments
1285
        $aArgs = Functions::flattenArray($args);
1286
        foreach ($aArgs as $arg) {
1287
            // Is it a blank cell?
1288
            if ((is_null($arg)) || ((is_string($arg)) && ($arg == ''))) {
1289
                ++$returnValue;
1290
            }
1291
        }
1292
1293
        return $returnValue;
1294
    }
1295
1296
    /**
1297
     * COUNTIF.
1298
     *
1299
     * Counts the number of cells that contain numbers within the list of arguments
1300
     *
1301
     * Excel Function:
1302
     *        COUNTIF(value1[,value2[, ...]],condition)
1303
     *
1304
     * @category Statistical Functions
1305
     *
1306
     * @param mixed $aArgs Data values
1307
     * @param string $condition the criteria that defines which cells will be counted
1308
     *
1309
     * @return int
1310
     */
1311
    public static function COUNTIF($aArgs, $condition)
1312
    {
1313
        $returnValue = 0;
1314
1315
        $aArgs = Functions::flattenArray($aArgs);
1316
        $condition = Functions::ifCondition($condition);
1317
        // Loop through arguments
1318
        foreach ($aArgs as $arg) {
1319
            if (!is_numeric($arg)) {
1320
                $arg = \PhpOffice\PhpSpreadsheet\Calculation::wrapResult(strtoupper($arg));
1321
            }
1322
            $testCondition = '=' . $arg . $condition;
1323
            if (\PhpOffice\PhpSpreadsheet\Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
1324
                // Is it a value within our criteria
1325
                ++$returnValue;
1326
            }
1327
        }
1328
1329
        return $returnValue;
1330
    }
1331
1332
    /**
1333
     * COVAR.
1334
     *
1335
     * Returns covariance, the average of the products of deviations for each data point pair.
1336
     *
1337
     * @param array of mixed Data Series Y
1338
     * @param array of mixed Data Series X
1339
     * @param mixed $yValues
1340
     * @param mixed $xValues
1341
     *
1342
     * @return float
1343
     */
1344 View Code Duplication
    public static function COVAR($yValues, $xValues)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1345
    {
1346
        if (!self::checkTrendArrays($yValues, $xValues)) {
1347
            return Functions::VALUE();
1348
        }
1349
        $yValueCount = count($yValues);
1350
        $xValueCount = count($xValues);
1351
1352
        if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
1353
            return Functions::NA();
1354
        } elseif ($yValueCount == 1) {
1355
            return Functions::DIV0();
1356
        }
1357
1358
        $bestFitLinear = \PhpOffice\PhpSpreadsheet\Shared\trend\trend::calculate(\PhpOffice\PhpSpreadsheet\Shared\trend\trend::TREND_LINEAR, $yValues, $xValues);
1359
1360
        return $bestFitLinear->getCovariance();
1361
    }
1362
1363
    /**
1364
     * CRITBINOM.
1365
     *
1366
     * Returns the smallest value for which the cumulative binomial distribution is greater
1367
     *        than or equal to a criterion value
1368
     *
1369
     * See http://support.microsoft.com/kb/828117/ for details of the algorithm used
1370
     *
1371
     * @param float $trials number of Bernoulli trials
1372
     * @param float $probability probability of a success on each trial
1373
     * @param float $alpha criterion value
1374
     *
1375
     * @return int
1376
     *
1377
     * @todo    Warning. This implementation differs from the algorithm detailed on the MS
1378
     *            web site in that $CumPGuessMinus1 = $CumPGuess - 1 rather than $CumPGuess - $PGuess
1379
     *            This eliminates a potential endless loop error, but may have an adverse affect on the
1380
     *            accuracy of the function (although all my tests have so far returned correct results).
1381
     */
1382
    public static function CRITBINOM($trials, $probability, $alpha)
1383
    {
1384
        $trials = floor(Functions::flattenSingleValue($trials));
1385
        $probability = Functions::flattenSingleValue($probability);
1386
        $alpha = Functions::flattenSingleValue($alpha);
1387
1388
        if ((is_numeric($trials)) && (is_numeric($probability)) && (is_numeric($alpha))) {
1389
            if ($trials < 0) {
1390
                return Functions::NAN();
1391
            } elseif (($probability < 0) || ($probability > 1)) {
1392
                return Functions::NAN();
1393
            } elseif (($alpha < 0) || ($alpha > 1)) {
1394
                return Functions::NAN();
1395
            } elseif ($alpha <= 0.5) {
1396
                $t = sqrt(log(1 / ($alpha * $alpha)));
1397
                $trialsApprox = 0 - ($t + (2.515517 + 0.802853 * $t + 0.010328 * $t * $t) / (1 + 1.432788 * $t + 0.189269 * $t * $t + 0.001308 * $t * $t * $t));
1398
            } else {
1399
                $t = sqrt(log(1 / pow(1 - $alpha, 2)));
1400
                $trialsApprox = $t - (2.515517 + 0.802853 * $t + 0.010328 * $t * $t) / (1 + 1.432788 * $t + 0.189269 * $t * $t + 0.001308 * $t * $t * $t);
1401
            }
1402
            $Guess = floor($trials * $probability + $trialsApprox * sqrt($trials * $probability * (1 - $probability)));
1403
            if ($Guess < 0) {
1404
                $Guess = 0;
1405
            } elseif ($Guess > $trials) {
1406
                $Guess = $trials;
1407
            }
1408
1409
            $TotalUnscaledProbability = $UnscaledPGuess = $UnscaledCumPGuess = 0.0;
1410
            $EssentiallyZero = 10e-12;
1411
1412
            $m = floor($trials * $probability);
1413
            ++$TotalUnscaledProbability;
1414
            if ($m == $Guess) {
1415
                ++$UnscaledPGuess;
1416
            }
1417
            if ($m <= $Guess) {
1418
                ++$UnscaledCumPGuess;
1419
            }
1420
1421
            $PreviousValue = 1;
1422
            $Done = false;
1423
            $k = $m + 1;
1424 View Code Duplication
            while ((!$Done) && ($k <= $trials)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1425
                $CurrentValue = $PreviousValue * ($trials - $k + 1) * $probability / ($k * (1 - $probability));
1426
                $TotalUnscaledProbability += $CurrentValue;
1427
                if ($k == $Guess) {
1428
                    $UnscaledPGuess += $CurrentValue;
1429
                }
1430
                if ($k <= $Guess) {
1431
                    $UnscaledCumPGuess += $CurrentValue;
1432
                }
1433
                if ($CurrentValue <= $EssentiallyZero) {
1434
                    $Done = true;
1435
                }
1436
                $PreviousValue = $CurrentValue;
1437
                ++$k;
1438
            }
1439
1440
            $PreviousValue = 1;
1441
            $Done = false;
1442
            $k = $m - 1;
1443 View Code Duplication
            while ((!$Done) && ($k >= 0)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1444
                $CurrentValue = $PreviousValue * $k + 1 * (1 - $probability) / (($trials - $k) * $probability);
1445
                $TotalUnscaledProbability += $CurrentValue;
1446
                if ($k == $Guess) {
1447
                    $UnscaledPGuess += $CurrentValue;
1448
                }
1449
                if ($k <= $Guess) {
1450
                    $UnscaledCumPGuess += $CurrentValue;
1451
                }
1452
                if ($CurrentValue <= $EssentiallyZero) {
1453
                    $Done = true;
1454
                }
1455
                $PreviousValue = $CurrentValue;
1456
                --$k;
1457
            }
1458
1459
            $PGuess = $UnscaledPGuess / $TotalUnscaledProbability;
1460
            $CumPGuess = $UnscaledCumPGuess / $TotalUnscaledProbability;
1461
1462
            $CumPGuessMinus1 = $CumPGuess - 1;
1463
1464
            while (true) {
1465
                if (($CumPGuessMinus1 < $alpha) && ($CumPGuess >= $alpha)) {
1466
                    return $Guess;
1467
                } elseif (($CumPGuessMinus1 < $alpha) && ($CumPGuess < $alpha)) {
1468
                    $PGuessPlus1 = $PGuess * ($trials - $Guess) * $probability / $Guess / (1 - $probability);
1469
                    $CumPGuessMinus1 = $CumPGuess;
1470
                    $CumPGuess = $CumPGuess + $PGuessPlus1;
1471
                    $PGuess = $PGuessPlus1;
1472
                    ++$Guess;
1473
                } elseif (($CumPGuessMinus1 >= $alpha) && ($CumPGuess >= $alpha)) {
1474
                    $PGuessMinus1 = $PGuess * $Guess * (1 - $probability) / ($trials - $Guess + 1) / $probability;
1475
                    $CumPGuess = $CumPGuessMinus1;
1476
                    $CumPGuessMinus1 = $CumPGuessMinus1 - $PGuess;
1477
                    $PGuess = $PGuessMinus1;
1478
                    --$Guess;
1479
                }
1480
            }
1481
        }
1482
1483
        return Functions::VALUE();
1484
    }
1485
1486
    /**
1487
     * DEVSQ.
1488
     *
1489
     * Returns the sum of squares of deviations of data points from their sample mean.
1490
     *
1491
     * Excel Function:
1492
     *        DEVSQ(value1[,value2[, ...]])
1493
     *
1494
     * @category Statistical Functions
1495
     *
1496
     * @param mixed $args Data values
1497
     *
1498
     * @return float
1499
     */
1500 View Code Duplication
    public static function DEVSQ(...$args)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1501
    {
1502
        $aArgs = Functions::flattenArrayIndexed($args);
1503
1504
        // Return value
1505
        $returnValue = null;
1506
1507
        $aMean = self::AVERAGE($aArgs);
1508
        if ($aMean != Functions::DIV0()) {
1509
            $aCount = -1;
1510
            foreach ($aArgs as $k => $arg) {
1511
                // Is it a numeric value?
1512
                if ((is_bool($arg)) &&
1513
                    ((!Functions::isCellValue($k)) ||
1514
                    (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE))) {
1515
                    $arg = (int) $arg;
1516
                }
1517
                if ((is_numeric($arg)) && (!is_string($arg))) {
1518
                    if (is_null($returnValue)) {
1519
                        $returnValue = pow(($arg - $aMean), 2);
1520
                    } else {
1521
                        $returnValue += pow(($arg - $aMean), 2);
1522
                    }
1523
                    ++$aCount;
1524
                }
1525
            }
1526
1527
            // Return
1528
            if (is_null($returnValue)) {
1529
                return Functions::NAN();
1530
            }
1531
1532
            return $returnValue;
1533
        }
1534
1535
        return self::NA();
0 ignored issues
show
Bug introduced by
The method NA() does not seem to exist on object<PhpOffice\PhpSpre...alculation\Statistical>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
1536
    }
1537
1538
    /**
1539
     * EXPONDIST.
1540
     *
1541
     *    Returns the exponential distribution. Use EXPONDIST to model the time between events,
1542
     *        such as how long an automated bank teller takes to deliver cash. For example, you can
1543
     *        use EXPONDIST to determine the probability that the process takes at most 1 minute.
1544
     *
1545
     * @param float $value Value of the function
1546
     * @param float $lambda The parameter value
1547
     * @param bool $cumulative
1548
     *
1549
     * @return float
1550
     */
1551
    public static function EXPONDIST($value, $lambda, $cumulative)
1552
    {
1553
        $value = Functions::flattenSingleValue($value);
1554
        $lambda = Functions::flattenSingleValue($lambda);
1555
        $cumulative = Functions::flattenSingleValue($cumulative);
1556
1557
        if ((is_numeric($value)) && (is_numeric($lambda))) {
1558
            if (($value < 0) || ($lambda < 0)) {
1559
                return Functions::NAN();
1560
            }
1561
            if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
1562
                if ($cumulative) {
1563
                    return 1 - exp(0 - $value * $lambda);
1564
                }
1565
1566
                return $lambda * exp(0 - $value * $lambda);
1567
            }
1568
        }
1569
1570
        return Functions::VALUE();
1571
    }
1572
1573
    /**
1574
     * FISHER.
1575
     *
1576
     * Returns the Fisher transformation at x. This transformation produces a function that
1577
     *        is normally distributed rather than skewed. Use this function to perform hypothesis
1578
     *        testing on the correlation coefficient.
1579
     *
1580
     * @param float $value
1581
     *
1582
     * @return float
1583
     */
1584
    public static function FISHER($value)
1585
    {
1586
        $value = Functions::flattenSingleValue($value);
1587
1588
        if (is_numeric($value)) {
1589
            if (($value <= -1) || ($value >= 1)) {
1590
                return Functions::NAN();
1591
            }
1592
1593
            return 0.5 * log((1 + $value) / (1 - $value));
1594
        }
1595
1596
        return Functions::VALUE();
1597
    }
1598
1599
    /**
1600
     * FISHERINV.
1601
     *
1602
     * Returns the inverse of the Fisher transformation. Use this transformation when
1603
     *        analyzing correlations between ranges or arrays of data. If y = FISHER(x), then
1604
     *        FISHERINV(y) = x.
1605
     *
1606
     * @param float $value
1607
     *
1608
     * @return float
1609
     */
1610
    public static function FISHERINV($value)
1611
    {
1612
        $value = Functions::flattenSingleValue($value);
1613
1614
        if (is_numeric($value)) {
1615
            return (exp(2 * $value) - 1) / (exp(2 * $value) + 1);
1616
        }
1617
1618
        return Functions::VALUE();
1619
    }
1620
1621
    /**
1622
     * FORECAST.
1623
     *
1624
     * Calculates, or predicts, a future value by using existing values. The predicted value is a y-value for a given x-value.
1625
     *
1626
     * @param float Value of X for which we want to find Y
1627
     * @param array of mixed Data Series Y
1628
     * @param array of mixed Data Series X
1629
     * @param mixed $xValue
1630
     * @param mixed $yValues
1631
     * @param mixed $xValues
1632
     *
1633
     * @return float
1634
     */
1635
    public static function FORECAST($xValue, $yValues, $xValues)
1636
    {
1637
        $xValue = Functions::flattenSingleValue($xValue);
1638
        if (!is_numeric($xValue)) {
1639
            return Functions::VALUE();
1640
        } elseif (!self::checkTrendArrays($yValues, $xValues)) {
1641
            return Functions::VALUE();
1642
        }
1643
        $yValueCount = count($yValues);
1644
        $xValueCount = count($xValues);
1645
1646
        if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
1647
            return Functions::NA();
1648
        } elseif ($yValueCount == 1) {
1649
            return Functions::DIV0();
1650
        }
1651
1652
        $bestFitLinear = \PhpOffice\PhpSpreadsheet\Shared\trend\trend::calculate(\PhpOffice\PhpSpreadsheet\Shared\trend\trend::TREND_LINEAR, $yValues, $xValues);
1653
1654
        return $bestFitLinear->getValueOfYForX($xValue);
1655
    }
1656
1657
    /**
1658
     * GAMMADIST.
1659
     *
1660
     * Returns the gamma distribution.
1661
     *
1662
     * @param float $value Value at which you want to evaluate the distribution
1663
     * @param float $a Parameter to the distribution
1664
     * @param float $b Parameter to the distribution
1665
     * @param bool $cumulative
1666
     *
1667
     * @return float
1668
     */
1669
    public static function GAMMADIST($value, $a, $b, $cumulative)
1670
    {
1671
        $value = Functions::flattenSingleValue($value);
1672
        $a = Functions::flattenSingleValue($a);
1673
        $b = Functions::flattenSingleValue($b);
1674
1675
        if ((is_numeric($value)) && (is_numeric($a)) && (is_numeric($b))) {
1676
            if (($value < 0) || ($a <= 0) || ($b <= 0)) {
1677
                return Functions::NAN();
1678
            }
1679
            if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
1680
                if ($cumulative) {
1681
                    return self::incompleteGamma($a, $value / $b) / self::gamma($a);
1682
                }
1683
1684
                return (1 / (pow($b, $a) * self::gamma($a))) * pow($value, $a - 1) * exp(0 - ($value / $b));
1685
            }
1686
        }
1687
1688
        return Functions::VALUE();
1689
    }
1690
1691
    /**
1692
     * GAMMAINV.
1693
     *
1694
     * Returns the inverse of the beta distribution.
1695
     *
1696
     * @param float $probability Probability at which you want to evaluate the distribution
1697
     * @param float $alpha Parameter to the distribution
1698
     * @param float $beta Parameter to the distribution
1699
     *
1700
     * @return float
1701
     */
1702
    public static function GAMMAINV($probability, $alpha, $beta)
1703
    {
1704
        $probability = Functions::flattenSingleValue($probability);
1705
        $alpha = Functions::flattenSingleValue($alpha);
1706
        $beta = Functions::flattenSingleValue($beta);
1707
1708
        if ((is_numeric($probability)) && (is_numeric($alpha)) && (is_numeric($beta))) {
1709 View Code Duplication
            if (($alpha <= 0) || ($beta <= 0) || ($probability < 0) || ($probability > 1)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1710
                return Functions::NAN();
1711
            }
1712
1713
            $xLo = 0;
1714
            $xHi = $alpha * $beta * 5;
1715
1716
            $x = $xNew = 1;
1717
            $error = $pdf = 0;
0 ignored issues
show
Unused Code introduced by
$pdf is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
Unused Code introduced by
$error is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1718
            $dx = 1024;
1719
            $i = 0;
1720
1721
            while ((abs($dx) > PRECISION) && ($i++ < MAX_ITERATIONS)) {
1722
                // Apply Newton-Raphson step
1723
                $error = self::GAMMADIST($x, $alpha, $beta, true) - $probability;
1724
                if ($error < 0.0) {
1725
                    $xLo = $x;
1726
                } else {
1727
                    $xHi = $x;
1728
                }
1729
                $pdf = self::GAMMADIST($x, $alpha, $beta, false);
1730
                // Avoid division by zero
1731
                if ($pdf != 0.0) {
1732
                    $dx = $error / $pdf;
1733
                    $xNew = $x - $dx;
1734
                }
1735
                // If the NR fails to converge (which for example may be the
1736
                // case if the initial guess is too rough) we apply a bisection
1737
                // step to determine a more narrow interval around the root.
1738
                if (($xNew < $xLo) || ($xNew > $xHi) || ($pdf == 0.0)) {
1739
                    $xNew = ($xLo + $xHi) / 2;
1740
                    $dx = $xNew - $x;
1741
                }
1742
                $x = $xNew;
1743
            }
1744
            if ($i == MAX_ITERATIONS) {
1745
                return Functions::NA();
1746
            }
1747
1748
            return $x;
1749
        }
1750
1751
        return Functions::VALUE();
1752
    }
1753
1754
    /**
1755
     * GAMMALN.
1756
     *
1757
     * Returns the natural logarithm of the gamma function.
1758
     *
1759
     * @param float $value
1760
     *
1761
     * @return float
1762
     */
1763 View Code Duplication
    public static function GAMMALN($value)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1764
    {
1765
        $value = Functions::flattenSingleValue($value);
1766
1767
        if (is_numeric($value)) {
1768
            if ($value <= 0) {
1769
                return Functions::NAN();
1770
            }
1771
1772
            return log(self::gamma($value));
1773
        }
1774
1775
        return Functions::VALUE();
1776
    }
1777
1778
    /**
1779
     * GEOMEAN.
1780
     *
1781
     * Returns the geometric mean of an array or range of positive data. For example, you
1782
     *        can use GEOMEAN to calculate average growth rate given compound interest with
1783
     *        variable rates.
1784
     *
1785
     * Excel Function:
1786
     *        GEOMEAN(value1[,value2[, ...]])
1787
     *
1788
     * @category Statistical Functions
1789
     *
1790
     * @param mixed $args Data values
1791
     *
1792
     * @return float
1793
     */
1794
    public static function GEOMEAN(...$args)
1795
    {
1796
        $aArgs = Functions::flattenArray($args);
1797
1798
        $aMean = MathTrig::PRODUCT($aArgs);
1799
        if (is_numeric($aMean) && ($aMean > 0)) {
1800
            $aCount = self::COUNT($aArgs);
1801
            if (self::MIN($aArgs) > 0) {
1802
                return pow($aMean, (1 / $aCount));
1803
            }
1804
        }
1805
1806
        return Functions::NAN();
1807
    }
1808
1809
    /**
1810
     * GROWTH.
1811
     *
1812
     * Returns values along a predicted emponential Trend
1813
     *
1814
     * @param array of mixed Data Series Y
1815
     * @param array of mixed Data Series X
1816
     * @param array of mixed Values of X for which we want to find Y
1817
     * @param bool a logical value specifying whether to force the intersect to equal 0
1818
     * @param mixed $yValues
1819
     * @param mixed $xValues
1820
     * @param mixed $newValues
1821
     * @param mixed $const
1822
     *
1823
     * @return array of float
1824
     */
1825 View Code Duplication
    public static function GROWTH($yValues, $xValues = [], $newValues = [], $const = true)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1826
    {
1827
        $yValues = Functions::flattenArray($yValues);
1828
        $xValues = Functions::flattenArray($xValues);
1829
        $newValues = Functions::flattenArray($newValues);
1830
        $const = (is_null($const)) ? true : (bool) Functions::flattenSingleValue($const);
1831
1832
        $bestFitExponential = \PhpOffice\PhpSpreadsheet\Shared\trend\trend::calculate(\PhpOffice\PhpSpreadsheet\Shared\trend\trend::TREND_EXPONENTIAL, $yValues, $xValues, $const);
1833
        if (empty($newValues)) {
1834
            $newValues = $bestFitExponential->getXValues();
1835
        }
1836
1837
        $returnArray = [];
1838
        foreach ($newValues as $xValue) {
1839
            $returnArray[0][] = $bestFitExponential->getValueOfYForX($xValue);
1840
        }
1841
1842
        return $returnArray;
1843
    }
1844
1845
    /**
1846
     * HARMEAN.
1847
     *
1848
     * Returns the harmonic mean of a data set. The harmonic mean is the reciprocal of the
1849
     *        arithmetic mean of reciprocals.
1850
     *
1851
     * Excel Function:
1852
     *        HARMEAN(value1[,value2[, ...]])
1853
     *
1854
     * @category Statistical Functions
1855
     *
1856
     * @param mixed $args Data values
1857
     *
1858
     * @return float
1859
     */
1860
    public static function HARMEAN(...$args)
1861
    {
1862
        // Return value
1863
        $returnValue = Functions::NA();
1864
1865
        // Loop through arguments
1866
        $aArgs = Functions::flattenArray($args);
1867
        if (self::MIN($aArgs) < 0) {
1868
            return Functions::NAN();
1869
        }
1870
        $aCount = 0;
1871
        foreach ($aArgs as $arg) {
1872
            // Is it a numeric value?
1873
            if ((is_numeric($arg)) && (!is_string($arg))) {
1874
                if ($arg <= 0) {
1875
                    return Functions::NAN();
1876
                }
1877
                if (is_null($returnValue)) {
1878
                    $returnValue = (1 / $arg);
1879
                } else {
1880
                    $returnValue += (1 / $arg);
1881
                }
1882
                ++$aCount;
1883
            }
1884
        }
1885
1886
        // Return
1887
        if ($aCount > 0) {
1888
            return 1 / ($returnValue / $aCount);
1889
        }
1890
1891
        return $returnValue;
1892
    }
1893
1894
    /**
1895
     * HYPGEOMDIST.
1896
     *
1897
     * Returns the hypergeometric distribution. HYPGEOMDIST returns the probability of a given number of
1898
     * sample successes, given the sample size, population successes, and population size.
1899
     *
1900
     * @param float $sampleSuccesses Number of successes in the sample
1901
     * @param float $sampleNumber Size of the sample
1902
     * @param float $populationSuccesses Number of successes in the population
1903
     * @param float $populationNumber Population size
1904
     *
1905
     * @return float
1906
     */
1907
    public static function HYPGEOMDIST($sampleSuccesses, $sampleNumber, $populationSuccesses, $populationNumber)
1908
    {
1909
        $sampleSuccesses = floor(Functions::flattenSingleValue($sampleSuccesses));
1910
        $sampleNumber = floor(Functions::flattenSingleValue($sampleNumber));
1911
        $populationSuccesses = floor(Functions::flattenSingleValue($populationSuccesses));
1912
        $populationNumber = floor(Functions::flattenSingleValue($populationNumber));
1913
1914
        if ((is_numeric($sampleSuccesses)) && (is_numeric($sampleNumber)) && (is_numeric($populationSuccesses)) && (is_numeric($populationNumber))) {
1915
            if (($sampleSuccesses < 0) || ($sampleSuccesses > $sampleNumber) || ($sampleSuccesses > $populationSuccesses)) {
1916
                return Functions::NAN();
1917
            }
1918
            if (($sampleNumber <= 0) || ($sampleNumber > $populationNumber)) {
1919
                return Functions::NAN();
1920
            }
1921
            if (($populationSuccesses <= 0) || ($populationSuccesses > $populationNumber)) {
1922
                return Functions::NAN();
1923
            }
1924
1925
            return MathTrig::COMBIN($populationSuccesses, $sampleSuccesses) *
1926
                   MathTrig::COMBIN($populationNumber - $populationSuccesses, $sampleNumber - $sampleSuccesses) /
1927
                   MathTrig::COMBIN($populationNumber, $sampleNumber);
1928
        }
1929
1930
        return Functions::VALUE();
1931
    }
1932
1933
    /**
1934
     * INTERCEPT.
1935
     *
1936
     * Calculates the point at which a line will intersect the y-axis by using existing x-values and y-values.
1937
     *
1938
     * @param array of mixed Data Series Y
1939
     * @param array of mixed Data Series X
1940
     * @param mixed $yValues
1941
     * @param mixed $xValues
1942
     *
1943
     * @return float
1944
     */
1945 View Code Duplication
    public static function INTERCEPT($yValues, $xValues)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1946
    {
1947
        if (!self::checkTrendArrays($yValues, $xValues)) {
1948
            return Functions::VALUE();
1949
        }
1950
        $yValueCount = count($yValues);
1951
        $xValueCount = count($xValues);
1952
1953
        if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
1954
            return Functions::NA();
1955
        } elseif ($yValueCount == 1) {
1956
            return Functions::DIV0();
1957
        }
1958
1959
        $bestFitLinear = \PhpOffice\PhpSpreadsheet\Shared\trend\trend::calculate(\PhpOffice\PhpSpreadsheet\Shared\trend\trend::TREND_LINEAR, $yValues, $xValues);
1960
1961
        return $bestFitLinear->getIntersect();
1962
    }
1963
1964
    /**
1965
     * KURT.
1966
     *
1967
     * Returns the kurtosis of a data set. Kurtosis characterizes the relative peakedness
1968
     * or flatness of a distribution compared with the normal distribution. Positive
1969
     * kurtosis indicates a relatively peaked distribution. Negative kurtosis indicates a
1970
     * relatively flat distribution.
1971
     *
1972
     * @param array Data Series
1973
     *
1974
     * @return float
1975
     */
1976
    public static function KURT(...$args)
1977
    {
1978
        $aArgs = Functions::flattenArrayIndexed($args);
1979
        $mean = self::AVERAGE($aArgs);
1980
        $stdDev = self::STDEV($aArgs);
1981
1982
        if ($stdDev > 0) {
1983
            $count = $summer = 0;
1984
            // Loop through arguments
1985 View Code Duplication
            foreach ($aArgs as $k => $arg) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1986
                if ((is_bool($arg)) &&
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
1987
                    (!Functions::isMatrixValue($k))) {
1988
                } else {
1989
                    // Is it a numeric value?
1990
                    if ((is_numeric($arg)) && (!is_string($arg))) {
1991
                        $summer += pow((($arg - $mean) / $stdDev), 4);
1992
                        ++$count;
1993
                    }
1994
                }
1995
            }
1996
1997
            // Return
1998
            if ($count > 3) {
1999
                return $summer * ($count * ($count + 1) / (($count - 1) * ($count - 2) * ($count - 3))) - (3 * pow($count - 1, 2) / (($count - 2) * ($count - 3)));
2000
            }
2001
        }
2002
2003
        return Functions::DIV0();
2004
    }
2005
2006
    /**
2007
     * LARGE.
2008
     *
2009
     * Returns the nth largest value in a data set. You can use this function to
2010
     *        select a value based on its relative standing.
2011
     *
2012
     * Excel Function:
2013
     *        LARGE(value1[,value2[, ...]],entry)
2014
     *
2015
     * @category Statistical Functions
2016
     *
2017
     * @param mixed $args Data values
2018
     * @param int $entry Position (ordered from the largest) in the array or range of data to return
0 ignored issues
show
Bug introduced by
There is no parameter named $entry. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
2019
     *
2020
     * @return float
2021
     */
2022 View Code Duplication
    public static function LARGE(...$args)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2023
    {
2024
        $aArgs = Functions::flattenArray($args);
2025
2026
        // Calculate
2027
        $entry = floor(array_pop($aArgs));
2028
2029
        if ((is_numeric($entry)) && (!is_string($entry))) {
2030
            $mArgs = [];
2031
            foreach ($aArgs as $arg) {
2032
                // Is it a numeric value?
2033
                if ((is_numeric($arg)) && (!is_string($arg))) {
2034
                    $mArgs[] = $arg;
2035
                }
2036
            }
2037
            $count = self::COUNT($mArgs);
2038
            $entry = floor(--$entry);
2039
            if (($entry < 0) || ($entry >= $count) || ($count == 0)) {
2040
                return Functions::NAN();
2041
            }
2042
            rsort($mArgs);
2043
2044
            return $mArgs[$entry];
2045
        }
2046
2047
        return Functions::VALUE();
2048
    }
2049
2050
    /**
2051
     * LINEST.
2052
     *
2053
     * Calculates the statistics for a line by using the "least squares" method to calculate a straight line that best fits your data,
2054
     *        and then returns an array that describes the line.
2055
     *
2056
     * @param array of mixed Data Series Y
2057
     * @param array of mixed Data Series X
2058
     * @param bool a logical value specifying whether to force the intersect to equal 0
2059
     * @param bool a logical value specifying whether to return additional regression statistics
2060
     * @param mixed $yValues
2061
     * @param null|mixed $xValues
2062
     * @param mixed $const
2063
     * @param mixed $stats
2064
     *
2065
     * @return array
2066
     */
2067
    public static function LINEST($yValues, $xValues = null, $const = true, $stats = false)
2068
    {
2069
        $const = (is_null($const)) ? true : (bool) Functions::flattenSingleValue($const);
2070
        $stats = (is_null($stats)) ? false : (bool) Functions::flattenSingleValue($stats);
2071
        if (is_null($xValues)) {
2072
            $xValues = range(1, count(Functions::flattenArray($yValues)));
2073
        }
2074
2075
        if (!self::checkTrendArrays($yValues, $xValues)) {
2076
            return Functions::VALUE();
2077
        }
2078
        $yValueCount = count($yValues);
2079
        $xValueCount = count($xValues);
2080
2081
        if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
2082
            return Functions::NA();
2083
        } elseif ($yValueCount == 1) {
2084
            return 0;
2085
        }
2086
2087
        $bestFitLinear = \PhpOffice\PhpSpreadsheet\Shared\trend\trend::calculate(\PhpOffice\PhpSpreadsheet\Shared\trend\trend::TREND_LINEAR, $yValues, $xValues, $const);
2088 View Code Duplication
        if ($stats) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2089
            return [
2090
                [
2091
                    $bestFitLinear->getSlope(),
2092
                    $bestFitLinear->getSlopeSE(),
2093
                    $bestFitLinear->getGoodnessOfFit(),
2094
                    $bestFitLinear->getF(),
2095
                    $bestFitLinear->getSSRegression(),
2096
                ],
2097
                [
2098
                    $bestFitLinear->getIntersect(),
2099
                    $bestFitLinear->getIntersectSE(),
2100
                    $bestFitLinear->getStdevOfResiduals(),
2101
                    $bestFitLinear->getDFResiduals(),
2102
                    $bestFitLinear->getSSResiduals(),
2103
                ],
2104
            ];
2105
        }
2106
2107
        return [
2108
                $bestFitLinear->getSlope(),
2109
                $bestFitLinear->getIntersect(),
2110
            ];
2111
    }
2112
2113
    /**
2114
     * LOGEST.
2115
     *
2116
     * Calculates an exponential curve that best fits the X and Y data series,
2117
     *        and then returns an array that describes the line.
2118
     *
2119
     * @param array of mixed Data Series Y
2120
     * @param array of mixed Data Series X
2121
     * @param bool a logical value specifying whether to force the intersect to equal 0
2122
     * @param bool a logical value specifying whether to return additional regression statistics
2123
     * @param mixed $yValues
2124
     * @param null|mixed $xValues
2125
     * @param mixed $const
2126
     * @param mixed $stats
2127
     *
2128
     * @return array
2129
     */
2130
    public static function LOGEST($yValues, $xValues = null, $const = true, $stats = false)
2131
    {
2132
        $const = (is_null($const)) ? true : (bool) Functions::flattenSingleValue($const);
2133
        $stats = (is_null($stats)) ? false : (bool) Functions::flattenSingleValue($stats);
2134
        if (is_null($xValues)) {
2135
            $xValues = range(1, count(Functions::flattenArray($yValues)));
2136
        }
2137
2138
        if (!self::checkTrendArrays($yValues, $xValues)) {
2139
            return Functions::VALUE();
2140
        }
2141
        $yValueCount = count($yValues);
2142
        $xValueCount = count($xValues);
2143
2144
        foreach ($yValues as $value) {
2145
            if ($value <= 0.0) {
2146
                return Functions::NAN();
2147
            }
2148
        }
2149
2150
        if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
2151
            return Functions::NA();
2152
        } elseif ($yValueCount == 1) {
2153
            return 1;
2154
        }
2155
2156
        $bestFitExponential = \PhpOffice\PhpSpreadsheet\Shared\trend\trend::calculate(\PhpOffice\PhpSpreadsheet\Shared\trend\trend::TREND_EXPONENTIAL, $yValues, $xValues, $const);
2157 View Code Duplication
        if ($stats) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2158
            return [
2159
                [
2160
                    $bestFitExponential->getSlope(),
2161
                    $bestFitExponential->getSlopeSE(),
2162
                    $bestFitExponential->getGoodnessOfFit(),
2163
                    $bestFitExponential->getF(),
2164
                    $bestFitExponential->getSSRegression(),
2165
                ],
2166
                [
2167
                    $bestFitExponential->getIntersect(),
2168
                    $bestFitExponential->getIntersectSE(),
2169
                    $bestFitExponential->getStdevOfResiduals(),
2170
                    $bestFitExponential->getDFResiduals(),
2171
                    $bestFitExponential->getSSResiduals(),
2172
                ],
2173
            ];
2174
        }
2175
2176
        return [
2177
                $bestFitExponential->getSlope(),
2178
                $bestFitExponential->getIntersect(),
2179
            ];
2180
    }
2181
2182
    /**
2183
     * LOGINV.
2184
     *
2185
     * Returns the inverse of the normal cumulative distribution
2186
     *
2187
     * @param float $probability
2188
     * @param float $mean
2189
     * @param float $stdDev
2190
     *
2191
     * @return float
2192
     *
2193
     * @todo    Try implementing P J Acklam's refinement algorithm for greater
2194
     *            accuracy if I can get my head round the mathematics
2195
     *            (as described at) http://home.online.no/~pjacklam/notes/invnorm/
2196
     */
2197 View Code Duplication
    public static function LOGINV($probability, $mean, $stdDev)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2198
    {
2199
        $probability = Functions::flattenSingleValue($probability);
2200
        $mean = Functions::flattenSingleValue($mean);
2201
        $stdDev = Functions::flattenSingleValue($stdDev);
2202
2203
        if ((is_numeric($probability)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
2204
            if (($probability < 0) || ($probability > 1) || ($stdDev <= 0)) {
2205
                return Functions::NAN();
2206
            }
2207
2208
            return exp($mean + $stdDev * self::NORMSINV($probability));
2209
        }
2210
2211
        return Functions::VALUE();
2212
    }
2213
2214
    /**
2215
     * LOGNORMDIST.
2216
     *
2217
     * Returns the cumulative lognormal distribution of x, where ln(x) is normally distributed
2218
     * with parameters mean and standard_dev.
2219
     *
2220
     * @param float $value
2221
     * @param float $mean
2222
     * @param float $stdDev
2223
     *
2224
     * @return float
2225
     */
2226
    public static function LOGNORMDIST($value, $mean, $stdDev)
2227
    {
2228
        $value = Functions::flattenSingleValue($value);
2229
        $mean = Functions::flattenSingleValue($mean);
2230
        $stdDev = Functions::flattenSingleValue($stdDev);
2231
2232
        if ((is_numeric($value)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
2233
            if (($value <= 0) || ($stdDev <= 0)) {
2234
                return Functions::NAN();
2235
            }
2236
2237
            return self::NORMSDIST((log($value) - $mean) / $stdDev);
2238
        }
2239
2240
        return Functions::VALUE();
2241
    }
2242
2243
    /**
2244
     * MAX.
2245
     *
2246
     * MAX returns the value of the element of the values passed that has the highest value,
2247
     *        with negative numbers considered smaller than positive numbers.
2248
     *
2249
     * Excel Function:
2250
     *        MAX(value1[,value2[, ...]])
2251
     *
2252
     * @category Statistical Functions
2253
     *
2254
     * @param mixed $args Data values
2255
     *
2256
     * @return float
2257
     */
2258 1 View Code Duplication
    public static function MAX(...$args)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2259
    {
2260 1
        $returnValue = null;
2261
2262
        // Loop through arguments
2263 1
        $aArgs = Functions::flattenArray($args);
2264 1
        foreach ($aArgs as $arg) {
2265
            // Is it a numeric value?
2266 1
            if ((is_numeric($arg)) && (!is_string($arg))) {
2267 1
                if ((is_null($returnValue)) || ($arg > $returnValue)) {
2268 1
                    $returnValue = $arg;
2269
                }
2270
            }
2271
        }
2272
2273 1
        if (is_null($returnValue)) {
2274
            return 0;
2275
        }
2276
2277 1
        return $returnValue;
2278
    }
2279
2280
    /**
2281
     * MAXA.
2282
     *
2283
     * Returns the greatest value in a list of arguments, including numbers, text, and logical values
2284
     *
2285
     * Excel Function:
2286
     *        MAXA(value1[,value2[, ...]])
2287
     *
2288
     * @category Statistical Functions
2289
     *
2290
     * @param mixed $args Data values
2291
     *
2292
     * @return float
2293
     */
2294 View Code Duplication
    public static function MAXA(...$args)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2295
    {
2296
        $returnValue = null;
2297
2298
        // Loop through arguments
2299
        $aArgs = Functions::flattenArray($args);
2300
        foreach ($aArgs as $arg) {
2301
            // Is it a numeric value?
2302
            if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
2303
                if (is_bool($arg)) {
2304
                    $arg = (int) $arg;
2305
                } elseif (is_string($arg)) {
2306
                    $arg = 0;
2307
                }
2308
                if ((is_null($returnValue)) || ($arg > $returnValue)) {
2309
                    $returnValue = $arg;
2310
                }
2311
            }
2312
        }
2313
2314
        if (is_null($returnValue)) {
2315
            return 0;
2316
        }
2317
2318
        return $returnValue;
2319
    }
2320
2321
    /**
2322
     * MAXIF.
2323
     *
2324
     * Counts the maximum value within a range of cells that contain numbers within the list of arguments
2325
     *
2326
     * Excel Function:
2327
     *        MAXIF(value1[,value2[, ...]],condition)
2328
     *
2329
     * @category Mathematical and Trigonometric Functions
2330
     *
2331
     * @param mixed $aArgs Data values
2332
     * @param string $condition the criteria that defines which cells will be checked
2333
     * @param mixed $sumArgs
2334
     *
2335
     * @return float
2336
     */
2337 View Code Duplication
    public static function MAXIF($aArgs, $condition, $sumArgs = [])
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2338
    {
2339
        $returnValue = null;
2340
2341
        $aArgs = Functions::flattenArray($aArgs);
2342
        $sumArgs = Functions::flattenArray($sumArgs);
2343
        if (empty($sumArgs)) {
2344
            $sumArgs = $aArgs;
0 ignored issues
show
Unused Code introduced by
$sumArgs is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
2345
        }
2346
        $condition = Functions::ifCondition($condition);
2347
        // Loop through arguments
2348
        foreach ($aArgs as $key => $arg) {
2349
            if (!is_numeric($arg)) {
2350
                $arg = \PhpOffice\PhpSpreadsheet\Calculation::wrapResult(strtoupper($arg));
2351
            }
2352
            $testCondition = '=' . $arg . $condition;
2353
            if (\PhpOffice\PhpSpreadsheet\Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
2354
                if ((is_null($returnValue)) || ($arg > $returnValue)) {
2355
                    $returnValue = $arg;
2356
                }
2357
            }
2358
        }
2359
2360
        return $returnValue;
2361
    }
2362
2363
    /**
2364
     * MEDIAN.
2365
     *
2366
     * Returns the median of the given numbers. The median is the number in the middle of a set of numbers.
2367
     *
2368
     * Excel Function:
2369
     *        MEDIAN(value1[,value2[, ...]])
2370
     *
2371
     * @category Statistical Functions
2372
     *
2373
     * @param mixed $args Data values
2374
     *
2375
     * @return float
2376
     */
2377
    public static function MEDIAN(...$args)
2378
    {
2379
        $returnValue = Functions::NAN();
2380
2381
        $mArgs = [];
2382
        // Loop through arguments
2383
        $aArgs = Functions::flattenArray($args);
2384
        foreach ($aArgs as $arg) {
2385
            // Is it a numeric value?
2386
            if ((is_numeric($arg)) && (!is_string($arg))) {
2387
                $mArgs[] = $arg;
2388
            }
2389
        }
2390
2391
        $mValueCount = count($mArgs);
2392
        if ($mValueCount > 0) {
2393
            sort($mArgs, SORT_NUMERIC);
2394
            $mValueCount = $mValueCount / 2;
2395
            if ($mValueCount == floor($mValueCount)) {
2396
                $returnValue = ($mArgs[$mValueCount--] + $mArgs[$mValueCount]) / 2;
2397
            } else {
2398
                $mValueCount = floor($mValueCount);
2399
                $returnValue = $mArgs[$mValueCount];
2400
            }
2401
        }
2402
2403
        return $returnValue;
2404
    }
2405
2406
    /**
2407
     * MIN.
2408
     *
2409
     * MIN returns the value of the element of the values passed that has the smallest value,
2410
     *        with negative numbers considered smaller than positive numbers.
2411
     *
2412
     * Excel Function:
2413
     *        MIN(value1[,value2[, ...]])
2414
     *
2415
     * @category Statistical Functions
2416
     *
2417
     * @param mixed $args Data values
2418
     *
2419
     * @return float
2420
     */
2421 1 View Code Duplication
    public static function MIN(...$args)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2422
    {
2423 1
        $returnValue = null;
2424
2425
        // Loop through arguments
2426 1
        $aArgs = Functions::flattenArray($args);
2427 1
        foreach ($aArgs as $arg) {
2428
            // Is it a numeric value?
2429 1
            if ((is_numeric($arg)) && (!is_string($arg))) {
2430 1
                if ((is_null($returnValue)) || ($arg < $returnValue)) {
2431 1
                    $returnValue = $arg;
2432
                }
2433
            }
2434
        }
2435
2436 1
        if (is_null($returnValue)) {
2437
            return 0;
2438
        }
2439
2440 1
        return $returnValue;
2441
    }
2442
2443
    /**
2444
     * MINA.
2445
     *
2446
     * Returns the smallest value in a list of arguments, including numbers, text, and logical values
2447
     *
2448
     * Excel Function:
2449
     *        MINA(value1[,value2[, ...]])
2450
     *
2451
     * @category Statistical Functions
2452
     *
2453
     * @param mixed $args Data values
2454
     *
2455
     * @return float
2456
     */
2457 View Code Duplication
    public static function MINA(...$args)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2458
    {
2459
        $returnValue = null;
2460
2461
        // Loop through arguments
2462
        $aArgs = Functions::flattenArray($args);
2463
        foreach ($aArgs as $arg) {
2464
            // Is it a numeric value?
2465
            if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
2466
                if (is_bool($arg)) {
2467
                    $arg = (int) $arg;
2468
                } elseif (is_string($arg)) {
2469
                    $arg = 0;
2470
                }
2471
                if ((is_null($returnValue)) || ($arg < $returnValue)) {
2472
                    $returnValue = $arg;
2473
                }
2474
            }
2475
        }
2476
2477
        if (is_null($returnValue)) {
2478
            return 0;
2479
        }
2480
2481
        return $returnValue;
2482
    }
2483
2484
    /**
2485
     * MINIF.
2486
     *
2487
     * Returns the minimum value within a range of cells that contain numbers within the list of arguments
2488
     *
2489
     * Excel Function:
2490
     *        MINIF(value1[,value2[, ...]],condition)
2491
     *
2492
     * @category Mathematical and Trigonometric Functions
2493
     *
2494
     * @param mixed $aArgs Data values
2495
     * @param string $condition the criteria that defines which cells will be checked
2496
     * @param mixed $sumArgs
2497
     *
2498
     * @return float
2499
     */
2500 View Code Duplication
    public static function MINIF($aArgs, $condition, $sumArgs = [])
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2501
    {
2502
        $returnValue = null;
2503
2504
        $aArgs = Functions::flattenArray($aArgs);
2505
        $sumArgs = Functions::flattenArray($sumArgs);
2506
        if (empty($sumArgs)) {
2507
            $sumArgs = $aArgs;
0 ignored issues
show
Unused Code introduced by
$sumArgs is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
2508
        }
2509
        $condition = Functions::ifCondition($condition);
2510
        // Loop through arguments
2511
        foreach ($aArgs as $key => $arg) {
2512
            if (!is_numeric($arg)) {
2513
                $arg = \PhpOffice\PhpSpreadsheet\Calculation::wrapResult(strtoupper($arg));
2514
            }
2515
            $testCondition = '=' . $arg . $condition;
2516
            if (\PhpOffice\PhpSpreadsheet\Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
2517
                if ((is_null($returnValue)) || ($arg < $returnValue)) {
2518
                    $returnValue = $arg;
2519
                }
2520
            }
2521
        }
2522
2523
        return $returnValue;
2524
    }
2525
2526
    //
2527
    //    Special variant of array_count_values that isn't limited to strings and integers,
2528
    //        but can work with floating point numbers as values
2529
    //
2530
    private static function modeCalc($data)
2531
    {
2532
        $frequencyArray = [];
2533
        foreach ($data as $datum) {
2534
            $found = false;
2535
            foreach ($frequencyArray as $key => $value) {
2536
                if ((string) $value['value'] == (string) $datum) {
2537
                    ++$frequencyArray[$key]['frequency'];
2538
                    $found = true;
2539
                    break;
2540
                }
2541
            }
2542
            if (!$found) {
2543
                $frequencyArray[] = [
2544
                    'value' => $datum,
2545
                    'frequency' => 1,
2546
                ];
2547
            }
2548
        }
2549
2550
        foreach ($frequencyArray as $key => $value) {
2551
            $frequencyList[$key] = $value['frequency'];
0 ignored issues
show
Coding Style Comprehensibility introduced by
$frequencyList was never initialized. Although not strictly required by PHP, it is generally a good practice to add $frequencyList = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
2552
            $valueList[$key] = $value['value'];
0 ignored issues
show
Coding Style Comprehensibility introduced by
$valueList was never initialized. Although not strictly required by PHP, it is generally a good practice to add $valueList = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
2553
        }
2554
        array_multisort($frequencyList, SORT_DESC, $valueList, SORT_ASC, SORT_NUMERIC, $frequencyArray);
0 ignored issues
show
Bug introduced by
The variable $valueList does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
2555
2556
        if ($frequencyArray[0]['frequency'] == 1) {
2557
            return Functions::NA();
2558
        }
2559
2560
        return $frequencyArray[0]['value'];
2561
    }
2562
2563
    /**
2564
     * MODE.
2565
     *
2566
     * Returns the most frequently occurring, or repetitive, value in an array or range of data
2567
     *
2568
     * Excel Function:
2569
     *        MODE(value1[,value2[, ...]])
2570
     *
2571
     * @category Statistical Functions
2572
     *
2573
     * @param mixed $args Data values
2574
     *
2575
     * @return float
2576
     */
2577
    public static function MODE(...$args)
2578
    {
2579
        $returnValue = Functions::NA();
2580
2581
        // Loop through arguments
2582
        $aArgs = Functions::flattenArray($args);
2583
2584
        $mArgs = [];
2585
        foreach ($aArgs as $arg) {
2586
            // Is it a numeric value?
2587
            if ((is_numeric($arg)) && (!is_string($arg))) {
2588
                $mArgs[] = $arg;
2589
            }
2590
        }
2591
2592
        if (!empty($mArgs)) {
2593
            return self::modeCalc($mArgs);
2594
        }
2595
2596
        return $returnValue;
2597
    }
2598
2599
    /**
2600
     * NEGBINOMDIST.
2601
     *
2602
     * Returns the negative binomial distribution. NEGBINOMDIST returns the probability that
2603
     *        there will be number_f failures before the number_s-th success, when the constant
2604
     *        probability of a success is probability_s. This function is similar to the binomial
2605
     *        distribution, except that the number of successes is fixed, and the number of trials is
2606
     *        variable. Like the binomial, trials are assumed to be independent.
2607
     *
2608
     * @param float $failures Number of Failures
2609
     * @param float $successes Threshold number of Successes
2610
     * @param float $probability Probability of success on each trial
2611
     *
2612
     * @return float
2613
     */
2614
    public static function NEGBINOMDIST($failures, $successes, $probability)
2615
    {
2616
        $failures = floor(Functions::flattenSingleValue($failures));
2617
        $successes = floor(Functions::flattenSingleValue($successes));
2618
        $probability = Functions::flattenSingleValue($probability);
2619
2620
        if ((is_numeric($failures)) && (is_numeric($successes)) && (is_numeric($probability))) {
2621
            if (($failures < 0) || ($successes < 1)) {
2622
                return Functions::NAN();
2623
            } elseif (($probability < 0) || ($probability > 1)) {
2624
                return Functions::NAN();
2625
            }
2626
            if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
2627
                if (($failures + $successes - 1) <= 0) {
2628
                    return Functions::NAN();
2629
                }
2630
            }
2631
2632
            return (MathTrig::COMBIN($failures + $successes - 1, $successes - 1)) * (pow($probability, $successes)) * (pow(1 - $probability, $failures));
2633
        }
2634
2635
        return Functions::VALUE();
2636
    }
2637
2638
    /**
2639
     * NORMDIST.
2640
     *
2641
     * Returns the normal distribution for the specified mean and standard deviation. This
2642
     * function has a very wide range of applications in statistics, including hypothesis
2643
     * testing.
2644
     *
2645
     * @param float $value
2646
     * @param float $mean Mean Value
2647
     * @param float $stdDev Standard Deviation
2648
     * @param bool $cumulative
2649
     *
2650
     * @return float
2651
     */
2652
    public static function NORMDIST($value, $mean, $stdDev, $cumulative)
2653
    {
2654
        $value = Functions::flattenSingleValue($value);
2655
        $mean = Functions::flattenSingleValue($mean);
2656
        $stdDev = Functions::flattenSingleValue($stdDev);
2657
2658
        if ((is_numeric($value)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
2659
            if ($stdDev < 0) {
2660
                return Functions::NAN();
2661
            }
2662
            if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
2663
                if ($cumulative) {
2664
                    return 0.5 * (1 + Engineering::erfVal(($value - $mean) / ($stdDev * sqrt(2))));
2665
                }
2666
2667
                return (1 / (SQRT2PI * $stdDev)) * exp(0 - (pow($value - $mean, 2) / (2 * ($stdDev * $stdDev))));
2668
            }
2669
        }
2670
2671
        return Functions::VALUE();
2672
    }
2673
2674
    /**
2675
     * NORMINV.
2676
     *
2677
     * Returns the inverse of the normal cumulative distribution for the specified mean and standard deviation.
2678
     *
2679
     * @param float $probability
2680
     * @param float $mean Mean Value
2681
     * @param float $stdDev Standard Deviation
2682
     *
2683
     * @return float
2684
     */
2685 View Code Duplication
    public static function NORMINV($probability, $mean, $stdDev)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2686
    {
2687
        $probability = Functions::flattenSingleValue($probability);
2688
        $mean = Functions::flattenSingleValue($mean);
2689
        $stdDev = Functions::flattenSingleValue($stdDev);
2690
2691
        if ((is_numeric($probability)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
2692
            if (($probability < 0) || ($probability > 1)) {
2693
                return Functions::NAN();
2694
            }
2695
            if ($stdDev < 0) {
2696
                return Functions::NAN();
2697
            }
2698
2699
            return (self::inverseNcdf($probability) * $stdDev) + $mean;
2700
        }
2701
2702
        return Functions::VALUE();
2703
    }
2704
2705
    /**
2706
     * NORMSDIST.
2707
     *
2708
     * Returns the standard normal cumulative distribution function. The distribution has
2709
     * a mean of 0 (zero) and a standard deviation of one. Use this function in place of a
2710
     * table of standard normal curve areas.
2711
     *
2712
     * @param float $value
2713
     *
2714
     * @return float
2715
     */
2716
    public static function NORMSDIST($value)
2717
    {
2718
        $value = Functions::flattenSingleValue($value);
2719
2720
        return self::NORMDIST($value, 0, 1, true);
2721
    }
2722
2723
    /**
2724
     * NORMSINV.
2725
     *
2726
     * Returns the inverse of the standard normal cumulative distribution
2727
     *
2728
     * @param float $value
2729
     *
2730
     * @return float
2731
     */
2732
    public static function NORMSINV($value)
2733
    {
2734
        return self::NORMINV($value, 0, 1);
2735
    }
2736
2737
    /**
2738
     * PERCENTILE.
2739
     *
2740
     * Returns the nth percentile of values in a range..
2741
     *
2742
     * Excel Function:
2743
     *        PERCENTILE(value1[,value2[, ...]],entry)
2744
     *
2745
     * @category Statistical Functions
2746
     *
2747
     * @param mixed $args Data values
2748
     * @param float $entry Percentile value in the range 0..1, inclusive.
0 ignored issues
show
Bug introduced by
There is no parameter named $entry. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
2749
     *
2750
     * @return float
2751
     */
2752
    public static function PERCENTILE(...$args)
2753
    {
2754
        $aArgs = Functions::flattenArray($args);
2755
2756
        // Calculate
2757
        $entry = array_pop($aArgs);
2758
2759
        if ((is_numeric($entry)) && (!is_string($entry))) {
2760
            if (($entry < 0) || ($entry > 1)) {
2761
                return Functions::NAN();
2762
            }
2763
            $mArgs = [];
2764
            foreach ($aArgs as $arg) {
2765
                // Is it a numeric value?
2766
                if ((is_numeric($arg)) && (!is_string($arg))) {
2767
                    $mArgs[] = $arg;
2768
                }
2769
            }
2770
            $mValueCount = count($mArgs);
2771
            if ($mValueCount > 0) {
2772
                sort($mArgs);
2773
                $count = self::COUNT($mArgs);
2774
                $index = $entry * ($count - 1);
2775
                $iBase = floor($index);
2776
                if ($index == $iBase) {
2777
                    return $mArgs[$index];
2778
                }
2779
                $iNext = $iBase + 1;
2780
                $iProportion = $index - $iBase;
2781
2782
                return $mArgs[$iBase] + (($mArgs[$iNext] - $mArgs[$iBase]) * $iProportion);
2783
            }
2784
        }
2785
2786
        return Functions::VALUE();
2787
    }
2788
2789
    /**
2790
     * PERCENTRANK.
2791
     *
2792
     * Returns the rank of a value in a data set as a percentage of the data set.
2793
     *
2794
     * @param array of number        An array of, or a reference to, a list of numbers
2795
     * @param number the number whose rank you want to find
2796
     * @param number the number of significant digits for the returned percentage value
2797
     * @param mixed $valueSet
2798
     * @param mixed $value
2799
     * @param mixed $significance
2800
     *
2801
     * @return float
2802
     */
2803
    public static function PERCENTRANK($valueSet, $value, $significance = 3)
2804
    {
2805
        $valueSet = Functions::flattenArray($valueSet);
2806
        $value = Functions::flattenSingleValue($value);
2807
        $significance = (is_null($significance)) ? 3 : (int) Functions::flattenSingleValue($significance);
2808
2809
        foreach ($valueSet as $key => $valueEntry) {
2810
            if (!is_numeric($valueEntry)) {
2811
                unset($valueSet[$key]);
2812
            }
2813
        }
2814
        sort($valueSet, SORT_NUMERIC);
2815
        $valueCount = count($valueSet);
2816
        if ($valueCount == 0) {
2817
            return Functions::NAN();
2818
        }
2819
2820
        $valueAdjustor = $valueCount - 1;
2821
        if (($value < $valueSet[0]) || ($value > $valueSet[$valueAdjustor])) {
2822
            return Functions::NA();
2823
        }
2824
2825
        $pos = array_search($value, $valueSet);
2826
        if ($pos === false) {
2827
            $pos = 0;
2828
            $testValue = $valueSet[0];
2829
            while ($testValue < $value) {
2830
                $testValue = $valueSet[++$pos];
2831
            }
2832
            --$pos;
2833
            $pos += (($value - $valueSet[$pos]) / ($testValue - $valueSet[$pos]));
2834
        }
2835
2836
        return round($pos / $valueAdjustor, $significance);
2837
    }
2838
2839
    /**
2840
     * PERMUT.
2841
     *
2842
     * Returns the number of permutations for a given number of objects that can be
2843
     *        selected from number objects. A permutation is any set or subset of objects or
2844
     *        events where internal order is significant. Permutations are different from
2845
     *        combinations, for which the internal order is not significant. Use this function
2846
     *        for lottery-style probability calculations.
2847
     *
2848
     * @param int $numObjs Number of different objects
2849
     * @param int $numInSet Number of objects in each permutation
2850
     *
2851
     * @return int Number of permutations
2852
     */
2853 View Code Duplication
    public static function PERMUT($numObjs, $numInSet)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2854
    {
2855
        $numObjs = Functions::flattenSingleValue($numObjs);
2856
        $numInSet = Functions::flattenSingleValue($numInSet);
2857
2858
        if ((is_numeric($numObjs)) && (is_numeric($numInSet))) {
2859
            $numInSet = floor($numInSet);
2860
            if ($numObjs < $numInSet) {
2861
                return Functions::NAN();
2862
            }
2863
2864
            return round(MathTrig::FACT($numObjs) / MathTrig::FACT($numObjs - $numInSet));
2865
        }
2866
2867
        return Functions::VALUE();
2868
    }
2869
2870
    /**
2871
     * POISSON.
2872
     *
2873
     * Returns the Poisson distribution. A common application of the Poisson distribution
2874
     * is predicting the number of events over a specific time, such as the number of
2875
     * cars arriving at a toll plaza in 1 minute.
2876
     *
2877
     * @param float $value
2878
     * @param float $mean Mean Value
2879
     * @param bool $cumulative
2880
     *
2881
     * @return float
2882
     */
2883
    public static function POISSON($value, $mean, $cumulative)
2884
    {
2885
        $value = Functions::flattenSingleValue($value);
2886
        $mean = Functions::flattenSingleValue($mean);
2887
2888
        if ((is_numeric($value)) && (is_numeric($mean))) {
2889
            if (($value < 0) || ($mean <= 0)) {
2890
                return Functions::NAN();
2891
            }
2892
            if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
2893
                if ($cumulative) {
2894
                    $summer = 0;
2895
                    for ($i = 0; $i <= floor($value); ++$i) {
2896
                        $summer += pow($mean, $i) / MathTrig::FACT($i);
2897
                    }
2898
2899
                    return exp(0 - $mean) * $summer;
2900
                }
2901
2902
                return (exp(0 - $mean) * pow($mean, $value)) / MathTrig::FACT($value);
2903
            }
2904
        }
2905
2906
        return Functions::VALUE();
2907
    }
2908
2909
    /**
2910
     * QUARTILE.
2911
     *
2912
     * Returns the quartile of a data set.
2913
     *
2914
     * Excel Function:
2915
     *        QUARTILE(value1[,value2[, ...]],entry)
2916
     *
2917
     * @category Statistical Functions
2918
     *
2919
     * @param mixed $args Data values
2920
     * @param int $entry Quartile value in the range 1..3, inclusive.
0 ignored issues
show
Bug introduced by
There is no parameter named $entry. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
2921
     *
2922
     * @return float
2923
     */
2924
    public static function QUARTILE(...$args)
2925
    {
2926
        $aArgs = Functions::flattenArray($args);
2927
2928
        // Calculate
2929
        $entry = floor(array_pop($aArgs));
2930
2931
        if ((is_numeric($entry)) && (!is_string($entry))) {
2932
            $entry /= 4;
2933
            if (($entry < 0) || ($entry > 1)) {
2934
                return Functions::NAN();
2935
            }
2936
2937
            return self::PERCENTILE($aArgs, $entry);
2938
        }
2939
2940
        return Functions::VALUE();
2941
    }
2942
2943
    /**
2944
     * RANK.
2945
     *
2946
     * Returns the rank of a number in a list of numbers.
2947
     *
2948
     * @param number the number whose rank you want to find
2949
     * @param array of number        An array of, or a reference to, a list of numbers
2950
     * @param mixed Order to sort the values in the value set
2951
     * @param mixed $value
2952
     * @param mixed $valueSet
2953
     * @param mixed $order
2954
     *
2955
     * @return float
2956
     */
2957
    public static function RANK($value, $valueSet, $order = 0)
2958
    {
2959
        $value = Functions::flattenSingleValue($value);
2960
        $valueSet = Functions::flattenArray($valueSet);
2961
        $order = (is_null($order)) ? 0 : (int) Functions::flattenSingleValue($order);
2962
2963
        foreach ($valueSet as $key => $valueEntry) {
2964
            if (!is_numeric($valueEntry)) {
2965
                unset($valueSet[$key]);
2966
            }
2967
        }
2968
2969
        if ($order == 0) {
2970
            rsort($valueSet, SORT_NUMERIC);
2971
        } else {
2972
            sort($valueSet, SORT_NUMERIC);
2973
        }
2974
        $pos = array_search($value, $valueSet);
2975
        if ($pos === false) {
2976
            return Functions::NA();
2977
        }
2978
2979
        return ++$pos;
2980
    }
2981
2982
    /**
2983
     * RSQ.
2984
     *
2985
     * Returns the square of the Pearson product moment correlation coefficient through data points in known_y's and known_x's.
2986
     *
2987
     * @param array of mixed Data Series Y
2988
     * @param array of mixed Data Series X
2989
     * @param mixed $yValues
2990
     * @param mixed $xValues
2991
     *
2992
     * @return float
2993
     */
2994 View Code Duplication
    public static function RSQ($yValues, $xValues)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2995
    {
2996
        if (!self::checkTrendArrays($yValues, $xValues)) {
2997
            return Functions::VALUE();
2998
        }
2999
        $yValueCount = count($yValues);
3000
        $xValueCount = count($xValues);
3001
3002
        if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
3003
            return Functions::NA();
3004
        } elseif ($yValueCount == 1) {
3005
            return Functions::DIV0();
3006
        }
3007
3008
        $bestFitLinear = \PhpOffice\PhpSpreadsheet\Shared\trend\trend::calculate(\PhpOffice\PhpSpreadsheet\Shared\trend\trend::TREND_LINEAR, $yValues, $xValues);
3009
3010
        return $bestFitLinear->getGoodnessOfFit();
3011
    }
3012
3013
    /**
3014
     * SKEW.
3015
     *
3016
     * Returns the skewness of a distribution. Skewness characterizes the degree of asymmetry
3017
     * of a distribution around its mean. Positive skewness indicates a distribution with an
3018
     * asymmetric tail extending toward more positive values. Negative skewness indicates a
3019
     * distribution with an asymmetric tail extending toward more negative values.
3020
     *
3021
     * @param array Data Series
3022
     *
3023
     * @return float
3024
     */
3025
    public static function SKEW(...$args)
3026
    {
3027
        $aArgs = Functions::flattenArrayIndexed($args);
3028
        $mean = self::AVERAGE($aArgs);
3029
        $stdDev = self::STDEV($aArgs);
3030
3031
        $count = $summer = 0;
3032
        // Loop through arguments
3033 View Code Duplication
        foreach ($aArgs as $k => $arg) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3034
            if ((is_bool($arg)) &&
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
3035
                (!Functions::isMatrixValue($k))) {
3036
            } else {
3037
                // Is it a numeric value?
3038
                if ((is_numeric($arg)) && (!is_string($arg))) {
3039
                    $summer += pow((($arg - $mean) / $stdDev), 3);
3040
                    ++$count;
3041
                }
3042
            }
3043
        }
3044
3045
        if ($count > 2) {
3046
            return $summer * ($count / (($count - 1) * ($count - 2)));
3047
        }
3048
3049
        return Functions::DIV0();
3050
    }
3051
3052
    /**
3053
     * SLOPE.
3054
     *
3055
     * Returns the slope of the linear regression line through data points in known_y's and known_x's.
3056
     *
3057
     * @param array of mixed Data Series Y
3058
     * @param array of mixed Data Series X
3059
     * @param mixed $yValues
3060
     * @param mixed $xValues
3061
     *
3062
     * @return float
3063
     */
3064 View Code Duplication
    public static function SLOPE($yValues, $xValues)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3065
    {
3066
        if (!self::checkTrendArrays($yValues, $xValues)) {
3067
            return Functions::VALUE();
3068
        }
3069
        $yValueCount = count($yValues);
3070
        $xValueCount = count($xValues);
3071
3072
        if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
3073
            return Functions::NA();
3074
        } elseif ($yValueCount == 1) {
3075
            return Functions::DIV0();
3076
        }
3077
3078
        $bestFitLinear = \PhpOffice\PhpSpreadsheet\Shared\trend\trend::calculate(\PhpOffice\PhpSpreadsheet\Shared\trend\trend::TREND_LINEAR, $yValues, $xValues);
3079
3080
        return $bestFitLinear->getSlope();
3081
    }
3082
3083
    /**
3084
     * SMALL.
3085
     *
3086
     * Returns the nth smallest value in a data set. You can use this function to
3087
     *        select a value based on its relative standing.
3088
     *
3089
     * Excel Function:
3090
     *        SMALL(value1[,value2[, ...]],entry)
3091
     *
3092
     * @category Statistical Functions
3093
     *
3094
     * @param mixed $args Data values
3095
     * @param int $entry Position (ordered from the smallest) in the array or range of data to return
0 ignored issues
show
Bug introduced by
There is no parameter named $entry. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
3096
     *
3097
     * @return float
3098
     */
3099 View Code Duplication
    public static function SMALL(...$args)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3100
    {
3101
        $aArgs = Functions::flattenArray($args);
3102
3103
        // Calculate
3104
        $entry = array_pop($aArgs);
3105
3106
        if ((is_numeric($entry)) && (!is_string($entry))) {
3107
            $mArgs = [];
3108
            foreach ($aArgs as $arg) {
3109
                // Is it a numeric value?
3110
                if ((is_numeric($arg)) && (!is_string($arg))) {
3111
                    $mArgs[] = $arg;
3112
                }
3113
            }
3114
            $count = self::COUNT($mArgs);
3115
            $entry = floor(--$entry);
3116
            if (($entry < 0) || ($entry >= $count) || ($count == 0)) {
3117
                return Functions::NAN();
3118
            }
3119
            sort($mArgs);
3120
3121
            return $mArgs[$entry];
3122
        }
3123
3124
        return Functions::VALUE();
3125
    }
3126
3127
    /**
3128
     * STANDARDIZE.
3129
     *
3130
     * Returns a normalized value from a distribution characterized by mean and standard_dev.
3131
     *
3132
     * @param float $value Value to normalize
3133
     * @param float $mean Mean Value
3134
     * @param float $stdDev Standard Deviation
3135
     *
3136
     * @return float Standardized value
3137
     */
3138 View Code Duplication
    public static function STANDARDIZE($value, $mean, $stdDev)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3139
    {
3140
        $value = Functions::flattenSingleValue($value);
3141
        $mean = Functions::flattenSingleValue($mean);
3142
        $stdDev = Functions::flattenSingleValue($stdDev);
3143
3144
        if ((is_numeric($value)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
3145
            if ($stdDev <= 0) {
3146
                return Functions::NAN();
3147
            }
3148
3149
            return ($value - $mean) / $stdDev;
3150
        }
3151
3152
        return Functions::VALUE();
3153
    }
3154
3155
    /**
3156
     * STDEV.
3157
     *
3158
     * Estimates standard deviation based on a sample. The standard deviation is a measure of how
3159
     *        widely values are dispersed from the average value (the mean).
3160
     *
3161
     * Excel Function:
3162
     *        STDEV(value1[,value2[, ...]])
3163
     *
3164
     * @category Statistical Functions
3165
     *
3166
     * @param mixed $args Data values
3167
     *
3168
     * @return float
3169
     */
3170 View Code Duplication
    public static function STDEV(...$args)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3171
    {
3172
        $aArgs = Functions::flattenArrayIndexed($args);
3173
3174
        // Return value
3175
        $returnValue = null;
3176
3177
        $aMean = self::AVERAGE($aArgs);
3178
        if (!is_null($aMean)) {
3179
            $aCount = -1;
3180
            foreach ($aArgs as $k => $arg) {
3181
                if ((is_bool($arg)) &&
3182
                    ((!Functions::isCellValue($k)) || (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE))) {
3183
                    $arg = (int) $arg;
3184
                }
3185
                // Is it a numeric value?
3186
                if ((is_numeric($arg)) && (!is_string($arg))) {
3187
                    if (is_null($returnValue)) {
3188
                        $returnValue = pow(($arg - $aMean), 2);
3189
                    } else {
3190
                        $returnValue += pow(($arg - $aMean), 2);
3191
                    }
3192
                    ++$aCount;
3193
                }
3194
            }
3195
3196
            // Return
3197
            if (($aCount > 0) && ($returnValue >= 0)) {
3198
                return sqrt($returnValue / $aCount);
3199
            }
3200
        }
3201
3202
        return Functions::DIV0();
3203
    }
3204
3205
    /**
3206
     * STDEVA.
3207
     *
3208
     * Estimates standard deviation based on a sample, including numbers, text, and logical values
3209
     *
3210
     * Excel Function:
3211
     *        STDEVA(value1[,value2[, ...]])
3212
     *
3213
     * @category Statistical Functions
3214
     *
3215
     * @param mixed $args Data values
3216
     *
3217
     * @return float
3218
     */
3219 View Code Duplication
    public static function STDEVA(...$args)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3220
    {
3221
        $aArgs = Functions::flattenArrayIndexed($args);
3222
3223
        $returnValue = null;
3224
3225
        $aMean = self::AVERAGEA($aArgs);
3226
        if (!is_null($aMean)) {
3227
            $aCount = -1;
3228
            foreach ($aArgs as $k => $arg) {
3229
                if ((is_bool($arg)) &&
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
3230
                    (!Functions::isMatrixValue($k))) {
3231
                } else {
3232
                    // Is it a numeric value?
3233
                    if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) & ($arg != '')))) {
3234
                        if (is_bool($arg)) {
3235
                            $arg = (int) $arg;
3236
                        } elseif (is_string($arg)) {
3237
                            $arg = 0;
3238
                        }
3239
                        if (is_null($returnValue)) {
3240
                            $returnValue = pow(($arg - $aMean), 2);
3241
                        } else {
3242
                            $returnValue += pow(($arg - $aMean), 2);
3243
                        }
3244
                        ++$aCount;
3245
                    }
3246
                }
3247
            }
3248
3249
            if (($aCount > 0) && ($returnValue >= 0)) {
3250
                return sqrt($returnValue / $aCount);
3251
            }
3252
        }
3253
3254
        return Functions::DIV0();
3255
    }
3256
3257
    /**
3258
     * STDEVP.
3259
     *
3260
     * Calculates standard deviation based on the entire population
3261
     *
3262
     * Excel Function:
3263
     *        STDEVP(value1[,value2[, ...]])
3264
     *
3265
     * @category Statistical Functions
3266
     *
3267
     * @param mixed $args Data values
3268
     *
3269
     * @return float
3270
     */
3271 View Code Duplication
    public static function STDEVP(...$args)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3272
    {
3273
        $aArgs = Functions::flattenArrayIndexed($args);
3274
3275
        $returnValue = null;
3276
3277
        $aMean = self::AVERAGE($aArgs);
3278
        if (!is_null($aMean)) {
3279
            $aCount = 0;
3280
            foreach ($aArgs as $k => $arg) {
3281
                if ((is_bool($arg)) &&
3282
                    ((!Functions::isCellValue($k)) || (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE))) {
3283
                    $arg = (int) $arg;
3284
                }
3285
                // Is it a numeric value?
3286
                if ((is_numeric($arg)) && (!is_string($arg))) {
3287
                    if (is_null($returnValue)) {
3288
                        $returnValue = pow(($arg - $aMean), 2);
3289
                    } else {
3290
                        $returnValue += pow(($arg - $aMean), 2);
3291
                    }
3292
                    ++$aCount;
3293
                }
3294
            }
3295
3296
            if (($aCount > 0) && ($returnValue >= 0)) {
3297
                return sqrt($returnValue / $aCount);
3298
            }
3299
        }
3300
3301
        return Functions::DIV0();
3302
    }
3303
3304
    /**
3305
     * STDEVPA.
3306
     *
3307
     * Calculates standard deviation based on the entire population, including numbers, text, and logical values
3308
     *
3309
     * Excel Function:
3310
     *        STDEVPA(value1[,value2[, ...]])
3311
     *
3312
     * @category Statistical Functions
3313
     *
3314
     * @param mixed $args Data values
3315
     *
3316
     * @return float
3317
     */
3318 View Code Duplication
    public static function STDEVPA(...$args)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3319
    {
3320
        $aArgs = Functions::flattenArrayIndexed($args);
3321
3322
        $returnValue = null;
3323
3324
        $aMean = self::AVERAGEA($aArgs);
3325
        if (!is_null($aMean)) {
3326
            $aCount = 0;
3327
            foreach ($aArgs as $k => $arg) {
3328
                if ((is_bool($arg)) &&
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
3329
                    (!Functions::isMatrixValue($k))) {
3330
                } else {
3331
                    // Is it a numeric value?
3332
                    if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) & ($arg != '')))) {
3333
                        if (is_bool($arg)) {
3334
                            $arg = (int) $arg;
3335
                        } elseif (is_string($arg)) {
3336
                            $arg = 0;
3337
                        }
3338
                        if (is_null($returnValue)) {
3339
                            $returnValue = pow(($arg - $aMean), 2);
3340
                        } else {
3341
                            $returnValue += pow(($arg - $aMean), 2);
3342
                        }
3343
                        ++$aCount;
3344
                    }
3345
                }
3346
            }
3347
3348
            if (($aCount > 0) && ($returnValue >= 0)) {
3349
                return sqrt($returnValue / $aCount);
3350
            }
3351
        }
3352
3353
        return Functions::DIV0();
3354
    }
3355
3356
    /**
3357
     * STEYX.
3358
     *
3359
     * Returns the standard error of the predicted y-value for each x in the regression.
3360
     *
3361
     * @param array of mixed Data Series Y
3362
     * @param array of mixed Data Series X
3363
     * @param mixed $yValues
3364
     * @param mixed $xValues
3365
     *
3366
     * @return float
3367
     */
3368 View Code Duplication
    public static function STEYX($yValues, $xValues)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3369
    {
3370
        if (!self::checkTrendArrays($yValues, $xValues)) {
3371
            return Functions::VALUE();
3372
        }
3373
        $yValueCount = count($yValues);
3374
        $xValueCount = count($xValues);
3375
3376
        if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
3377
            return Functions::NA();
3378
        } elseif ($yValueCount == 1) {
3379
            return Functions::DIV0();
3380
        }
3381
3382
        $bestFitLinear = \PhpOffice\PhpSpreadsheet\Shared\trend\trend::calculate(\PhpOffice\PhpSpreadsheet\Shared\trend\trend::TREND_LINEAR, $yValues, $xValues);
3383
3384
        return $bestFitLinear->getStdevOfResiduals();
3385
    }
3386
3387
    /**
3388
     * TDIST.
3389
     *
3390
     * Returns the probability of Student's T distribution.
3391
     *
3392
     * @param float $value Value for the function
3393
     * @param float $degrees degrees of freedom
3394
     * @param float $tails number of tails (1 or 2)
3395
     *
3396
     * @return float
3397
     */
3398
    public static function TDIST($value, $degrees, $tails)
3399
    {
3400
        $value = Functions::flattenSingleValue($value);
3401
        $degrees = floor(Functions::flattenSingleValue($degrees));
3402
        $tails = floor(Functions::flattenSingleValue($tails));
3403
3404
        if ((is_numeric($value)) && (is_numeric($degrees)) && (is_numeric($tails))) {
3405
            if (($value < 0) || ($degrees < 1) || ($tails < 1) || ($tails > 2)) {
3406
                return Functions::NAN();
3407
            }
3408
            //    tdist, which finds the probability that corresponds to a given value
3409
            //    of t with k degrees of freedom. This algorithm is translated from a
3410
            //    pascal function on p81 of "Statistical Computing in Pascal" by D
3411
            //    Cooke, A H Craven & G M Clark (1985: Edward Arnold (Pubs.) Ltd:
3412
            //    London). The above Pascal algorithm is itself a translation of the
3413
            //    fortran algoritm "AS 3" by B E Cooper of the Atlas Computer
3414
            //    Laboratory as reported in (among other places) "Applied Statistics
3415
            //    Algorithms", editied by P Griffiths and I D Hill (1985; Ellis
3416
            //    Horwood Ltd.; W. Sussex, England).
3417
            $tterm = $degrees;
3418
            $ttheta = atan2($value, sqrt($tterm));
3419
            $tc = cos($ttheta);
3420
            $ts = sin($ttheta);
3421
            $tsum = 0;
0 ignored issues
show
Unused Code introduced by
$tsum is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
3422
3423
            if (($degrees % 2) == 1) {
3424
                $ti = 3;
3425
                $tterm = $tc;
3426
            } else {
3427
                $ti = 2;
3428
                $tterm = 1;
3429
            }
3430
3431
            $tsum = $tterm;
3432
            while ($ti < $degrees) {
3433
                $tterm *= $tc * $tc * ($ti - 1) / $ti;
3434
                $tsum += $tterm;
3435
                $ti += 2;
3436
            }
3437
            $tsum *= $ts;
3438
            if (($degrees % 2) == 1) {
3439
                $tsum = M_2DIVPI * ($tsum + $ttheta);
3440
            }
3441
            $tValue = 0.5 * (1 + $tsum);
3442
            if ($tails == 1) {
3443
                return 1 - abs($tValue);
3444
            }
3445
3446
            return 1 - abs((1 - $tValue) - $tValue);
3447
        }
3448
3449
        return Functions::VALUE();
3450
    }
3451
3452
    /**
3453
     * TINV.
3454
     *
3455
     * Returns the one-tailed probability of the chi-squared distribution.
3456
     *
3457
     * @param float $probability Probability for the function
3458
     * @param float $degrees degrees of freedom
3459
     *
3460
     * @return float
3461
     */
3462 View Code Duplication
    public static function TINV($probability, $degrees)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3463
    {
3464
        $probability = Functions::flattenSingleValue($probability);
3465
        $degrees = floor(Functions::flattenSingleValue($degrees));
3466
3467
        if ((is_numeric($probability)) && (is_numeric($degrees))) {
3468
            $xLo = 100;
3469
            $xHi = 0;
3470
3471
            $x = $xNew = 1;
3472
            $dx = 1;
3473
            $i = 0;
3474
3475
            while ((abs($dx) > PRECISION) && ($i++ < MAX_ITERATIONS)) {
3476
                // Apply Newton-Raphson step
3477
                $result = self::TDIST($x, $degrees, 2);
3478
                $error = $result - $probability;
3479
                if ($error == 0.0) {
3480
                    $dx = 0;
3481
                } elseif ($error < 0.0) {
3482
                    $xLo = $x;
3483
                } else {
3484
                    $xHi = $x;
3485
                }
3486
                // Avoid division by zero
3487
                if ($result != 0.0) {
3488
                    $dx = $error / $result;
3489
                    $xNew = $x - $dx;
3490
                }
3491
                // If the NR fails to converge (which for example may be the
3492
                // case if the initial guess is too rough) we apply a bisection
3493
                // step to determine a more narrow interval around the root.
3494
                if (($xNew < $xLo) || ($xNew > $xHi) || ($result == 0.0)) {
3495
                    $xNew = ($xLo + $xHi) / 2;
3496
                    $dx = $xNew - $x;
3497
                }
3498
                $x = $xNew;
3499
            }
3500
            if ($i == MAX_ITERATIONS) {
3501
                return Functions::NA();
3502
            }
3503
3504
            return round($x, 12);
3505
        }
3506
3507
        return Functions::VALUE();
3508
    }
3509
3510
    /**
3511
     * TREND.
3512
     *
3513
     * Returns values along a linear Trend
3514
     *
3515
     * @param array of mixed Data Series Y
3516
     * @param array of mixed Data Series X
3517
     * @param array of mixed Values of X for which we want to find Y
3518
     * @param bool a logical value specifying whether to force the intersect to equal 0
3519
     * @param mixed $yValues
3520
     * @param mixed $xValues
3521
     * @param mixed $newValues
3522
     * @param mixed $const
3523
     *
3524
     * @return array of float
3525
     */
3526 View Code Duplication
    public static function TREND($yValues, $xValues = [], $newValues = [], $const = true)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3527
    {
3528
        $yValues = Functions::flattenArray($yValues);
3529
        $xValues = Functions::flattenArray($xValues);
3530
        $newValues = Functions::flattenArray($newValues);
3531
        $const = (is_null($const)) ? true : (bool) Functions::flattenSingleValue($const);
3532
3533
        $bestFitLinear = \PhpOffice\PhpSpreadsheet\Shared\trend\trend::calculate(\PhpOffice\PhpSpreadsheet\Shared\trend\trend::TREND_LINEAR, $yValues, $xValues, $const);
3534
        if (empty($newValues)) {
3535
            $newValues = $bestFitLinear->getXValues();
3536
        }
3537
3538
        $returnArray = [];
3539
        foreach ($newValues as $xValue) {
3540
            $returnArray[0][] = $bestFitLinear->getValueOfYForX($xValue);
3541
        }
3542
3543
        return $returnArray;
3544
    }
3545
3546
    /**
3547
     * TRIMMEAN.
3548
     *
3549
     * Returns the mean of the interior of a data set. TRIMMEAN calculates the mean
3550
     *        taken by excluding a percentage of data points from the top and bottom tails
3551
     *        of a data set.
3552
     *
3553
     * Excel Function:
3554
     *        TRIMEAN(value1[,value2[, ...]], $discard)
3555
     *
3556
     * @category Statistical Functions
3557
     *
3558
     * @param mixed $args Data values
3559
     * @param float $discard Percentage to discard
0 ignored issues
show
Bug introduced by
There is no parameter named $discard. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
3560
     *
3561
     * @return float
3562
     */
3563
    public static function TRIMMEAN(...$args)
3564
    {
3565
        $aArgs = Functions::flattenArray($args);
3566
3567
        // Calculate
3568
        $percent = array_pop($aArgs);
3569
3570
        if ((is_numeric($percent)) && (!is_string($percent))) {
3571
            if (($percent < 0) || ($percent > 1)) {
3572
                return Functions::NAN();
3573
            }
3574
            $mArgs = [];
3575
            foreach ($aArgs as $arg) {
3576
                // Is it a numeric value?
3577
                if ((is_numeric($arg)) && (!is_string($arg))) {
3578
                    $mArgs[] = $arg;
3579
                }
3580
            }
3581
            $discard = floor(self::COUNT($mArgs) * $percent / 2);
3582
            sort($mArgs);
3583
            for ($i = 0; $i < $discard; ++$i) {
3584
                array_pop($mArgs);
3585
                array_shift($mArgs);
3586
            }
3587
3588
            return self::AVERAGE($mArgs);
3589
        }
3590
3591
        return Functions::VALUE();
3592
    }
3593
3594
    /**
3595
     * VARFunc.
3596
     *
3597
     * Estimates variance based on a sample.
3598
     *
3599
     * Excel Function:
3600
     *        VAR(value1[,value2[, ...]])
3601
     *
3602
     * @category Statistical Functions
3603
     *
3604
     * @param mixed $args Data values
3605
     *
3606
     * @return float
3607
     */
3608 View Code Duplication
    public static function VARFunc(...$args)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3609
    {
3610
        $returnValue = Functions::DIV0();
3611
3612
        $summerA = $summerB = 0;
3613
3614
        // Loop through arguments
3615
        $aArgs = Functions::flattenArray($args);
3616
        $aCount = 0;
3617
        foreach ($aArgs as $arg) {
3618
            if (is_bool($arg)) {
3619
                $arg = (int) $arg;
3620
            }
3621
            // Is it a numeric value?
3622
            if ((is_numeric($arg)) && (!is_string($arg))) {
3623
                $summerA += ($arg * $arg);
3624
                $summerB += $arg;
3625
                ++$aCount;
3626
            }
3627
        }
3628
3629
        if ($aCount > 1) {
3630
            $summerA *= $aCount;
3631
            $summerB *= $summerB;
3632
            $returnValue = ($summerA - $summerB) / ($aCount * ($aCount - 1));
3633
        }
3634
3635
        return $returnValue;
3636
    }
3637
3638
    /**
3639
     * VARA.
3640
     *
3641
     * Estimates variance based on a sample, including numbers, text, and logical values
3642
     *
3643
     * Excel Function:
3644
     *        VARA(value1[,value2[, ...]])
3645
     *
3646
     * @category Statistical Functions
3647
     *
3648
     * @param mixed $args Data values
3649
     *
3650
     * @return float
3651
     */
3652 View Code Duplication
    public static function VARA(...$args)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3653
    {
3654
        $returnValue = Functions::DIV0();
3655
3656
        $summerA = $summerB = 0;
3657
3658
        // Loop through arguments
3659
        $aArgs = Functions::flattenArrayIndexed($args);
3660
        $aCount = 0;
3661
        foreach ($aArgs as $k => $arg) {
3662
            if ((is_string($arg)) &&
3663
                (Functions::isValue($k))) {
3664
                return Functions::VALUE();
3665
            } elseif ((is_string($arg)) &&
0 ignored issues
show
Unused Code introduced by
This elseif statement is empty, and could be removed.

This check looks for the bodies of elseif statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These elseif bodies can be removed. If you have an empty elseif but statements in the else branch, consider inverting the condition.

Loading history...
3666
                (!Functions::isMatrixValue($k))) {
3667
            } else {
3668
                // Is it a numeric value?
3669
                if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) & ($arg != '')))) {
3670
                    if (is_bool($arg)) {
3671
                        $arg = (int) $arg;
3672
                    } elseif (is_string($arg)) {
3673
                        $arg = 0;
3674
                    }
3675
                    $summerA += ($arg * $arg);
3676
                    $summerB += $arg;
3677
                    ++$aCount;
3678
                }
3679
            }
3680
        }
3681
3682
        if ($aCount > 1) {
3683
            $summerA *= $aCount;
3684
            $summerB *= $summerB;
3685
            $returnValue = ($summerA - $summerB) / ($aCount * ($aCount - 1));
3686
        }
3687
3688
        return $returnValue;
3689
    }
3690
3691
    /**
3692
     * VARP.
3693
     *
3694
     * Calculates variance based on the entire population
3695
     *
3696
     * Excel Function:
3697
     *        VARP(value1[,value2[, ...]])
3698
     *
3699
     * @category Statistical Functions
3700
     *
3701
     * @param mixed $args Data values
3702
     *
3703
     * @return float
3704
     */
3705 View Code Duplication
    public static function VARP(...$args)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3706
    {
3707
        // Return value
3708
        $returnValue = Functions::DIV0();
3709
3710
        $summerA = $summerB = 0;
3711
3712
        // Loop through arguments
3713
        $aArgs = Functions::flattenArray($args);
3714
        $aCount = 0;
3715
        foreach ($aArgs as $arg) {
3716
            if (is_bool($arg)) {
3717
                $arg = (int) $arg;
3718
            }
3719
            // Is it a numeric value?
3720
            if ((is_numeric($arg)) && (!is_string($arg))) {
3721
                $summerA += ($arg * $arg);
3722
                $summerB += $arg;
3723
                ++$aCount;
3724
            }
3725
        }
3726
3727
        if ($aCount > 0) {
3728
            $summerA *= $aCount;
3729
            $summerB *= $summerB;
3730
            $returnValue = ($summerA - $summerB) / ($aCount * $aCount);
3731
        }
3732
3733
        return $returnValue;
3734
    }
3735
3736
    /**
3737
     * VARPA.
3738
     *
3739
     * Calculates variance based on the entire population, including numbers, text, and logical values
3740
     *
3741
     * Excel Function:
3742
     *        VARPA(value1[,value2[, ...]])
3743
     *
3744
     * @category Statistical Functions
3745
     *
3746
     * @param mixed $args Data values
3747
     *
3748
     * @return float
3749
     */
3750 View Code Duplication
    public static function VARPA(...$args)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3751
    {
3752
        $returnValue = Functions::DIV0();
3753
3754
        $summerA = $summerB = 0;
3755
3756
        // Loop through arguments
3757
        $aArgs = Functions::flattenArrayIndexed($args);
3758
        $aCount = 0;
3759
        foreach ($aArgs as $k => $arg) {
3760
            if ((is_string($arg)) &&
3761
                (Functions::isValue($k))) {
3762
                return Functions::VALUE();
3763
            } elseif ((is_string($arg)) &&
0 ignored issues
show
Unused Code introduced by
This elseif statement is empty, and could be removed.

This check looks for the bodies of elseif statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These elseif bodies can be removed. If you have an empty elseif but statements in the else branch, consider inverting the condition.

Loading history...
3764
                (!Functions::isMatrixValue($k))) {
3765
            } else {
3766
                // Is it a numeric value?
3767
                if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) & ($arg != '')))) {
3768
                    if (is_bool($arg)) {
3769
                        $arg = (int) $arg;
3770
                    } elseif (is_string($arg)) {
3771
                        $arg = 0;
3772
                    }
3773
                    $summerA += ($arg * $arg);
3774
                    $summerB += $arg;
3775
                    ++$aCount;
3776
                }
3777
            }
3778
        }
3779
3780
        if ($aCount > 0) {
3781
            $summerA *= $aCount;
3782
            $summerB *= $summerB;
3783
            $returnValue = ($summerA - $summerB) / ($aCount * $aCount);
3784
        }
3785
3786
        return $returnValue;
3787
    }
3788
3789
    /**
3790
     * WEIBULL.
3791
     *
3792
     * Returns the Weibull distribution. Use this distribution in reliability
3793
     * analysis, such as calculating a device's mean time to failure.
3794
     *
3795
     * @param float $value
3796
     * @param float $alpha Alpha Parameter
3797
     * @param float $beta Beta Parameter
3798
     * @param bool $cumulative
3799
     *
3800
     * @return float
3801
     */
3802
    public static function WEIBULL($value, $alpha, $beta, $cumulative)
3803
    {
3804
        $value = Functions::flattenSingleValue($value);
3805
        $alpha = Functions::flattenSingleValue($alpha);
3806
        $beta = Functions::flattenSingleValue($beta);
3807
3808
        if ((is_numeric($value)) && (is_numeric($alpha)) && (is_numeric($beta))) {
3809
            if (($value < 0) || ($alpha <= 0) || ($beta <= 0)) {
3810
                return Functions::NAN();
3811
            }
3812
            if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
3813
                if ($cumulative) {
3814
                    return 1 - exp(0 - pow($value / $beta, $alpha));
3815
                }
3816
3817
                return ($alpha / pow($beta, $alpha)) * pow($value, $alpha - 1) * exp(0 - pow($value / $beta, $alpha));
3818
            }
3819
        }
3820
3821
        return Functions::VALUE();
3822
    }
3823
3824
    /**
3825
     * ZTEST.
3826
     *
3827
     * Returns the Weibull distribution. Use this distribution in reliability
3828
     * analysis, such as calculating a device's mean time to failure.
3829
     *
3830
     * @param float $dataSet
3831
     * @param float $m0 Alpha Parameter
3832
     * @param float $sigma Beta Parameter
3833
     *
3834
     * @return float
3835
     */
3836
    public static function ZTEST($dataSet, $m0, $sigma = null)
3837
    {
3838
        $dataSet = Functions::flattenArrayIndexed($dataSet);
0 ignored issues
show
Documentation introduced by
$dataSet is of type double, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
3839
        $m0 = Functions::flattenSingleValue($m0);
3840
        $sigma = Functions::flattenSingleValue($sigma);
3841
3842
        if (is_null($sigma)) {
3843
            $sigma = self::STDEV($dataSet);
3844
        }
3845
        $n = count($dataSet);
3846
3847
        return 1 - self::NORMSDIST((self::AVERAGE($dataSet) - $m0) / ($sigma / sqrt($n)));
3848
    }
3849
}
3850