Passed
Push — master ( 221906...42fc71 )
by Mark
26:13
created

Statistical::MINIFS()   B

Complexity

Conditions 8
Paths 32

Size

Total Lines 43
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 22
CRAP Score 8

Importance

Changes 0
Metric Value
cc 8
eloc 21
nc 32
nop 1
dl 0
loc 43
ccs 22
cts 22
cp 1
crap 8
rs 8.4444
c 0
b 0
f 0
1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet\Calculation;
4
5
use PhpOffice\PhpSpreadsheet\Shared\Trend\Trend;
6
7
class Statistical
8
{
9
    const LOG_GAMMA_X_MAX_VALUE = 2.55e305;
10
    const XMININ = 2.23e-308;
11
    const EPS = 2.22e-16;
12
    const MAX_VALUE = 1.2e308;
13
    const MAX_ITERATIONS = 256;
14
    const SQRT2PI = 2.5066282746310005024157652848110452530069867406099;
15
16
    private static function checkTrendArrays(&$array1, &$array2)
17
    {
18
        if (!is_array($array1)) {
19
            $array1 = [$array1];
20
        }
21
        if (!is_array($array2)) {
22
            $array2 = [$array2];
23
        }
24
25
        $array1 = Functions::flattenArray($array1);
26
        $array2 = Functions::flattenArray($array2);
27
        foreach ($array1 as $key => $value) {
28
            if ((is_bool($value)) || (is_string($value)) || ($value === null)) {
29
                unset($array1[$key], $array2[$key]);
30
            }
31
        }
32
        foreach ($array2 as $key => $value) {
33
            if ((is_bool($value)) || (is_string($value)) || ($value === null)) {
34
                unset($array1[$key], $array2[$key]);
35
            }
36
        }
37
        $array1 = array_merge($array1);
38
        $array2 = array_merge($array2);
39
40
        return true;
41
    }
42
43
    /**
44
     * Incomplete beta function.
45
     *
46
     * @author Jaco van Kooten
47
     * @author Paul Meagher
48
     *
49
     * The computation is based on formulas from Numerical Recipes, Chapter 6.4 (W.H. Press et al, 1992).
50
     *
51
     * @param mixed $x require 0<=x<=1
52
     * @param mixed $p require p>0
53
     * @param mixed $q require q>0
54
     *
55
     * @return float 0 if x<0, p<=0, q<=0 or p+q>2.55E305 and 1 if x>1 to avoid errors and over/underflow
56
     */
57
    private static function incompleteBeta($x, $p, $q)
58
    {
59
        if ($x <= 0.0) {
60
            return 0.0;
61
        } elseif ($x >= 1.0) {
62
            return 1.0;
63
        } elseif (($p <= 0.0) || ($q <= 0.0) || (($p + $q) > self::LOG_GAMMA_X_MAX_VALUE)) {
64
            return 0.0;
65
        }
66
        $beta_gam = exp((0 - self::logBeta($p, $q)) + $p * log($x) + $q * log(1.0 - $x));
67
        if ($x < ($p + 1.0) / ($p + $q + 2.0)) {
68
            return $beta_gam * self::betaFraction($x, $p, $q) / $p;
69
        }
70
71
        return 1.0 - ($beta_gam * self::betaFraction(1 - $x, $q, $p) / $q);
72
    }
73
74
    // Function cache for logBeta function
75
    private static $logBetaCacheP = 0.0;
76
77
    private static $logBetaCacheQ = 0.0;
78
79
    private static $logBetaCacheResult = 0.0;
80
81
    /**
82
     * The natural logarithm of the beta function.
83
     *
84
     * @param mixed $p require p>0
85
     * @param mixed $q require q>0
86
     *
87
     * @return float 0 if p<=0, q<=0 or p+q>2.55E305 to avoid errors and over/underflow
88
     *
89
     * @author Jaco van Kooten
90
     */
91
    private static function logBeta($p, $q)
92
    {
93
        if ($p != self::$logBetaCacheP || $q != self::$logBetaCacheQ) {
94
            self::$logBetaCacheP = $p;
95
            self::$logBetaCacheQ = $q;
96
            if (($p <= 0.0) || ($q <= 0.0) || (($p + $q) > self::LOG_GAMMA_X_MAX_VALUE)) {
97
                self::$logBetaCacheResult = 0.0;
98
            } else {
99
                self::$logBetaCacheResult = self::logGamma($p) + self::logGamma($q) - self::logGamma($p + $q);
100
            }
101
        }
102
103
        return self::$logBetaCacheResult;
104
    }
105
106
    /**
107
     * Evaluates of continued fraction part of incomplete beta function.
108
     * Based on an idea from Numerical Recipes (W.H. Press et al, 1992).
109
     *
110
     * @author Jaco van Kooten
111
     *
112
     * @param mixed $x
113
     * @param mixed $p
114
     * @param mixed $q
115
     *
116
     * @return float
117
     */
118
    private static function betaFraction($x, $p, $q)
119
    {
120
        $c = 1.0;
121
        $sum_pq = $p + $q;
122
        $p_plus = $p + 1.0;
123
        $p_minus = $p - 1.0;
124
        $h = 1.0 - $sum_pq * $x / $p_plus;
125
        if (abs($h) < self::XMININ) {
126
            $h = self::XMININ;
127
        }
128
        $h = 1.0 / $h;
129
        $frac = $h;
130
        $m = 1;
131
        $delta = 0.0;
132
        while ($m <= self::MAX_ITERATIONS && abs($delta - 1.0) > Functions::PRECISION) {
133
            $m2 = 2 * $m;
134
            // even index for d
135
            $d = $m * ($q - $m) * $x / (($p_minus + $m2) * ($p + $m2));
136
            $h = 1.0 + $d * $h;
137
            if (abs($h) < self::XMININ) {
138
                $h = self::XMININ;
139
            }
140
            $h = 1.0 / $h;
141
            $c = 1.0 + $d / $c;
142
            if (abs($c) < self::XMININ) {
143
                $c = self::XMININ;
144
            }
145
            $frac *= $h * $c;
146
            // odd index for d
147
            $d = -($p + $m) * ($sum_pq + $m) * $x / (($p + $m2) * ($p_plus + $m2));
148
            $h = 1.0 + $d * $h;
149
            if (abs($h) < self::XMININ) {
150
                $h = self::XMININ;
151
            }
152
            $h = 1.0 / $h;
153
            $c = 1.0 + $d / $c;
154
            if (abs($c) < self::XMININ) {
155
                $c = self::XMININ;
156
            }
157
            $delta = $h * $c;
158
            $frac *= $delta;
159
            ++$m;
160
        }
161
162
        return $frac;
163
    }
164
165
    /**
166
     * logGamma function.
167
     *
168
     * @version 1.1
169
     *
170
     * @author Jaco van Kooten
171
     *
172
     * Original author was Jaco van Kooten. Ported to PHP by Paul Meagher.
173
     *
174
     * The natural logarithm of the gamma function. <br />
175
     * Based on public domain NETLIB (Fortran) code by W. J. Cody and L. Stoltz <br />
176
     * Applied Mathematics Division <br />
177
     * Argonne National Laboratory <br />
178
     * Argonne, IL 60439 <br />
179
     * <p>
180
     * References:
181
     * <ol>
182
     * <li>W. J. Cody and K. E. Hillstrom, 'Chebyshev Approximations for the Natural
183
     *     Logarithm of the Gamma Function,' Math. Comp. 21, 1967, pp. 198-203.</li>
184
     * <li>K. E. Hillstrom, ANL/AMD Program ANLC366S, DGAMMA/DLGAMA, May, 1969.</li>
185
     * <li>Hart, Et. Al., Computer Approximations, Wiley and sons, New York, 1968.</li>
186
     * </ol>
187
     * </p>
188
     * <p>
189
     * From the original documentation:
190
     * </p>
191
     * <p>
192
     * This routine calculates the LOG(GAMMA) function for a positive real argument X.
193
     * Computation is based on an algorithm outlined in references 1 and 2.
194
     * The program uses rational functions that theoretically approximate LOG(GAMMA)
195
     * to at least 18 significant decimal digits. The approximation for X > 12 is from
196
     * reference 3, while approximations for X < 12.0 are similar to those in reference
197
     * 1, but are unpublished. The accuracy achieved depends on the arithmetic system,
198
     * the compiler, the intrinsic functions, and proper selection of the
199
     * machine-dependent constants.
200
     * </p>
201
     * <p>
202
     * Error returns: <br />
203
     * The program returns the value XINF for X .LE. 0.0 or when overflow would occur.
204
     * The computation is believed to be free of underflow and overflow.
205
     * </p>
206
     *
207
     * @return float MAX_VALUE for x < 0.0 or when overflow would occur, i.e. x > 2.55E305
208
     */
209
210
    // Function cache for logGamma
211
    private static $logGammaCacheResult = 0.0;
212
213
    private static $logGammaCacheX = 0.0;
214
215
    private static function logGamma($x)
216
    {
217
        // Log Gamma related constants
218
        static $lg_d1 = -0.5772156649015328605195174;
219
        static $lg_d2 = 0.4227843350984671393993777;
220
        static $lg_d4 = 1.791759469228055000094023;
221
222
        static $lg_p1 = [
223
            4.945235359296727046734888,
224
            201.8112620856775083915565,
225
            2290.838373831346393026739,
226
            11319.67205903380828685045,
227
            28557.24635671635335736389,
228
            38484.96228443793359990269,
229
            26377.48787624195437963534,
230
            7225.813979700288197698961,
231
        ];
232
        static $lg_p2 = [
233
            4.974607845568932035012064,
234
            542.4138599891070494101986,
235
            15506.93864978364947665077,
236
            184793.2904445632425417223,
237
            1088204.76946882876749847,
238
            3338152.967987029735917223,
239
            5106661.678927352456275255,
240
            3074109.054850539556250927,
241
        ];
242
        static $lg_p4 = [
243
            14745.02166059939948905062,
244
            2426813.369486704502836312,
245
            121475557.4045093227939592,
246
            2663432449.630976949898078,
247
            29403789566.34553899906876,
248
            170266573776.5398868392998,
249
            492612579337.743088758812,
250
            560625185622.3951465078242,
251
        ];
252
        static $lg_q1 = [
253
            67.48212550303777196073036,
254
            1113.332393857199323513008,
255
            7738.757056935398733233834,
256
            27639.87074403340708898585,
257
            54993.10206226157329794414,
258
            61611.22180066002127833352,
259
            36351.27591501940507276287,
260
            8785.536302431013170870835,
261
        ];
262
        static $lg_q2 = [
263
            183.0328399370592604055942,
264
            7765.049321445005871323047,
265
            133190.3827966074194402448,
266
            1136705.821321969608938755,
267
            5267964.117437946917577538,
268
            13467014.54311101692290052,
269
            17827365.30353274213975932,
270
            9533095.591844353613395747,
271
        ];
272
        static $lg_q4 = [
273
            2690.530175870899333379843,
274
            639388.5654300092398984238,
275
            41355999.30241388052042842,
276
            1120872109.61614794137657,
277
            14886137286.78813811542398,
278
            101680358627.2438228077304,
279
            341747634550.7377132798597,
280
            446315818741.9713286462081,
281
        ];
282
        static $lg_c = [
283
            -0.001910444077728,
284
            8.4171387781295e-4,
285
            -5.952379913043012e-4,
286
            7.93650793500350248e-4,
287
            -0.002777777777777681622553,
288
            0.08333333333333333331554247,
289
            0.0057083835261,
290
        ];
291
292
        // Rough estimate of the fourth root of logGamma_xBig
293
        static $lg_frtbig = 2.25e76;
294
        static $pnt68 = 0.6796875;
295
296
        if ($x == self::$logGammaCacheX) {
297
            return self::$logGammaCacheResult;
298
        }
299
        $y = $x;
300
        if ($y > 0.0 && $y <= self::LOG_GAMMA_X_MAX_VALUE) {
301
            if ($y <= self::EPS) {
302
                $res = -log($y);
303
            } elseif ($y <= 1.5) {
304
                // ---------------------
305
                //    EPS .LT. X .LE. 1.5
306
                // ---------------------
307
                if ($y < $pnt68) {
308
                    $corr = -log($y);
309
                    $xm1 = $y;
310
                } else {
311
                    $corr = 0.0;
312
                    $xm1 = $y - 1.0;
313
                }
314
                if ($y <= 0.5 || $y >= $pnt68) {
315
                    $xden = 1.0;
316
                    $xnum = 0.0;
317
                    for ($i = 0; $i < 8; ++$i) {
318
                        $xnum = $xnum * $xm1 + $lg_p1[$i];
319
                        $xden = $xden * $xm1 + $lg_q1[$i];
320
                    }
321
                    $res = $corr + $xm1 * ($lg_d1 + $xm1 * ($xnum / $xden));
322
                } else {
323
                    $xm2 = $y - 1.0;
324
                    $xden = 1.0;
325
                    $xnum = 0.0;
326
                    for ($i = 0; $i < 8; ++$i) {
327
                        $xnum = $xnum * $xm2 + $lg_p2[$i];
328
                        $xden = $xden * $xm2 + $lg_q2[$i];
329
                    }
330
                    $res = $corr + $xm2 * ($lg_d2 + $xm2 * ($xnum / $xden));
331
                }
332
            } elseif ($y <= 4.0) {
333
                // ---------------------
334
                //    1.5 .LT. X .LE. 4.0
335
                // ---------------------
336
                $xm2 = $y - 2.0;
337
                $xden = 1.0;
338
                $xnum = 0.0;
339
                for ($i = 0; $i < 8; ++$i) {
340
                    $xnum = $xnum * $xm2 + $lg_p2[$i];
341
                    $xden = $xden * $xm2 + $lg_q2[$i];
342
                }
343
                $res = $xm2 * ($lg_d2 + $xm2 * ($xnum / $xden));
344
            } elseif ($y <= 12.0) {
345
                // ----------------------
346
                //    4.0 .LT. X .LE. 12.0
347
                // ----------------------
348
                $xm4 = $y - 4.0;
349
                $xden = -1.0;
350
                $xnum = 0.0;
351
                for ($i = 0; $i < 8; ++$i) {
352
                    $xnum = $xnum * $xm4 + $lg_p4[$i];
353
                    $xden = $xden * $xm4 + $lg_q4[$i];
354
                }
355
                $res = $lg_d4 + $xm4 * ($xnum / $xden);
356
            } else {
357
                // ---------------------------------
358
                //    Evaluate for argument .GE. 12.0
359
                // ---------------------------------
360
                $res = 0.0;
361
                if ($y <= $lg_frtbig) {
362
                    $res = $lg_c[6];
363
                    $ysq = $y * $y;
364
                    for ($i = 0; $i < 6; ++$i) {
365
                        $res = $res / $ysq + $lg_c[$i];
366
                    }
367
                    $res /= $y;
368
                    $corr = log($y);
369
                    $res = $res + log(self::SQRT2PI) - 0.5 * $corr;
370
                    $res += $y * ($corr - 1.0);
371
                }
372
            }
373
        } else {
374
            // --------------------------
375
            //    Return for bad arguments
376
            // --------------------------
377
            $res = self::MAX_VALUE;
378
        }
379
        // ------------------------------
380
        //    Final adjustments and return
381
        // ------------------------------
382
        self::$logGammaCacheX = $x;
383
        self::$logGammaCacheResult = $res;
384
385
        return $res;
386
    }
387
388
    //
389
    //    Private implementation of the incomplete Gamma function
390
    //
391
    private static function incompleteGamma($a, $x)
392
    {
393
        static $max = 32;
394
        $summer = 0;
395
        for ($n = 0; $n <= $max; ++$n) {
396
            $divisor = $a;
397
            for ($i = 1; $i <= $n; ++$i) {
398
                $divisor *= ($a + $i);
399
            }
400
            $summer += (pow($x, $n) / $divisor);
401
        }
402
403
        return pow($x, $a) * exp(0 - $x) * $summer;
404
    }
405
406
    //
407
    //    Private implementation of the Gamma function
408
    //
409
    private static function gamma($data)
410
    {
411
        if ($data == 0.0) {
412
            return 0;
413
        }
414
415
        static $p0 = 1.000000000190015;
416
        static $p = [
417
            1 => 76.18009172947146,
418
            2 => -86.50532032941677,
419
            3 => 24.01409824083091,
420
            4 => -1.231739572450155,
421
            5 => 1.208650973866179e-3,
422
            6 => -5.395239384953e-6,
423
        ];
424
425
        $y = $x = $data;
426
        $tmp = $x + 5.5;
427
        $tmp -= ($x + 0.5) * log($tmp);
428
429
        $summer = $p0;
430
        for ($j = 1; $j <= 6; ++$j) {
431
            $summer += ($p[$j] / ++$y);
432
        }
433
434
        return exp(0 - $tmp + log(self::SQRT2PI * $summer / $x));
435
    }
436
437
    /*
438
     *                                inverse_ncdf.php
439
     *                            -------------------
440
     *    begin                : Friday, January 16, 2004
441
     *    copyright            : (C) 2004 Michael Nickerson
442
     *    email                : [email protected]
443
     *
444
     */
445
    private static function inverseNcdf($p)
446
    {
447
        //    Inverse ncdf approximation by Peter J. Acklam, implementation adapted to
448
        //    PHP by Michael Nickerson, using Dr. Thomas Ziegler's C implementation as
449
        //    a guide. http://home.online.no/~pjacklam/notes/invnorm/index.html
450
        //    I have not checked the accuracy of this implementation. Be aware that PHP
451
        //    will truncate the coeficcients to 14 digits.
452
453
        //    You have permission to use and distribute this function freely for
454
        //    whatever purpose you want, but please show common courtesy and give credit
455
        //    where credit is due.
456
457
        //    Input paramater is $p - probability - where 0 < p < 1.
458
459
        //    Coefficients in rational approximations
460
        static $a = [
461
            1 => -3.969683028665376e+01,
462
            2 => 2.209460984245205e+02,
463
            3 => -2.759285104469687e+02,
464
            4 => 1.383577518672690e+02,
465
            5 => -3.066479806614716e+01,
466
            6 => 2.506628277459239e+00,
467
        ];
468
469
        static $b = [
470
            1 => -5.447609879822406e+01,
471
            2 => 1.615858368580409e+02,
472
            3 => -1.556989798598866e+02,
473
            4 => 6.680131188771972e+01,
474
            5 => -1.328068155288572e+01,
475
        ];
476
477
        static $c = [
478
            1 => -7.784894002430293e-03,
479
            2 => -3.223964580411365e-01,
480
            3 => -2.400758277161838e+00,
481
            4 => -2.549732539343734e+00,
482
            5 => 4.374664141464968e+00,
483
            6 => 2.938163982698783e+00,
484
        ];
485
486
        static $d = [
487
            1 => 7.784695709041462e-03,
488
            2 => 3.224671290700398e-01,
489
            3 => 2.445134137142996e+00,
490
            4 => 3.754408661907416e+00,
491
        ];
492
493
        //    Define lower and upper region break-points.
494
        $p_low = 0.02425; //Use lower region approx. below this
495
        $p_high = 1 - $p_low; //Use upper region approx. above this
496
497
        if (0 < $p && $p < $p_low) {
498
            //    Rational approximation for lower region.
499
            $q = sqrt(-2 * log($p));
500
501
            return ((((($c[1] * $q + $c[2]) * $q + $c[3]) * $q + $c[4]) * $q + $c[5]) * $q + $c[6]) /
502
                    (((($d[1] * $q + $d[2]) * $q + $d[3]) * $q + $d[4]) * $q + 1);
503
        } elseif ($p_low <= $p && $p <= $p_high) {
504
            //    Rational approximation for central region.
505
            $q = $p - 0.5;
506
            $r = $q * $q;
507
508
            return ((((($a[1] * $r + $a[2]) * $r + $a[3]) * $r + $a[4]) * $r + $a[5]) * $r + $a[6]) * $q /
509
                   ((((($b[1] * $r + $b[2]) * $r + $b[3]) * $r + $b[4]) * $r + $b[5]) * $r + 1);
510
        } elseif ($p_high < $p && $p < 1) {
511
            //    Rational approximation for upper region.
512
            $q = sqrt(-2 * log(1 - $p));
513
514
            return -((((($c[1] * $q + $c[2]) * $q + $c[3]) * $q + $c[4]) * $q + $c[5]) * $q + $c[6]) /
515
                     (((($d[1] * $q + $d[2]) * $q + $d[3]) * $q + $d[4]) * $q + 1);
516
        }
517
        //    If 0 < p < 1, return a null value
518
        return Functions::NULL();
519
    }
520
521
    /**
522
     * AVEDEV.
523
     *
524
     * Returns the average of the absolute deviations of data points from their mean.
525
     * AVEDEV is a measure of the variability in a data set.
526
     *
527
     * Excel Function:
528
     *        AVEDEV(value1[,value2[, ...]])
529
     *
530
     * @category Statistical Functions
531
     *
532
     * @param mixed ...$args Data values
533
     *
534
     * @return float
535
     */
536
    public static function AVEDEV(...$args)
537
    {
538
        $aArgs = Functions::flattenArrayIndexed($args);
539
540
        // Return value
541
        $returnValue = null;
542
543
        $aMean = self::AVERAGE($aArgs);
544
        if ($aMean != Functions::DIV0()) {
545
            $aCount = 0;
546
            foreach ($aArgs as $k => $arg) {
547
                if ((is_bool($arg)) &&
548
                    ((!Functions::isCellValue($k)) || (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE))) {
549
                    $arg = (int) $arg;
550
                }
551
                // Is it a numeric value?
552
                if ((is_numeric($arg)) && (!is_string($arg))) {
553
                    if ($returnValue === null) {
554
                        $returnValue = abs($arg - $aMean);
555
                    } else {
556
                        $returnValue += abs($arg - $aMean);
557
                    }
558
                    ++$aCount;
559
                }
560
            }
561
562
            // Return
563
            if ($aCount == 0) {
564
                return Functions::DIV0();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...ation\Functions::DIV0() returns the type string which is incompatible with the documented return type double.
Loading history...
565
            }
566
567
            return $returnValue / $aCount;
568
        }
569
570
        return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type double.
Loading history...
571
    }
572
573
    /**
574
     * AVERAGE.
575
     *
576
     * Returns the average (arithmetic mean) of the arguments
577
     *
578
     * Excel Function:
579
     *        AVERAGE(value1[,value2[, ...]])
580
     *
581
     * @category Statistical Functions
582
     *
583
     * @param mixed ...$args Data values
584
     *
585
     * @return float
586
     */
587 11
    public static function AVERAGE(...$args)
588
    {
589 11
        $returnValue = $aCount = 0;
590
591
        // Loop through arguments
592 11
        foreach (Functions::flattenArrayIndexed($args) as $k => $arg) {
593 11
            if ((is_bool($arg)) &&
594 11
                ((!Functions::isCellValue($k)) || (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE))) {
595
                $arg = (int) $arg;
596
            }
597
            // Is it a numeric value?
598 11
            if ((is_numeric($arg)) && (!is_string($arg))) {
599 11
                if ($returnValue === null) {
600
                    $returnValue = $arg;
601
                } else {
602 11
                    $returnValue += $arg;
603
                }
604 11
                ++$aCount;
605
            }
606
        }
607
608
        // Return
609 11
        if ($aCount > 0) {
610 11
            return $returnValue / $aCount;
611
        }
612
613
        return Functions::DIV0();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...ation\Functions::DIV0() returns the type string which is incompatible with the documented return type double.
Loading history...
614
    }
615
616
    /**
617
     * AVERAGEA.
618
     *
619
     * Returns the average of its arguments, including numbers, text, and logical values
620
     *
621
     * Excel Function:
622
     *        AVERAGEA(value1[,value2[, ...]])
623
     *
624
     * @category Statistical Functions
625
     *
626
     * @param mixed ...$args Data values
627
     *
628
     * @return float
629
     */
630 1
    public static function AVERAGEA(...$args)
631
    {
632 1
        $returnValue = null;
633
634 1
        $aCount = 0;
635
        // Loop through arguments
636 1
        foreach (Functions::flattenArrayIndexed($args) as $k => $arg) {
637 1
            if ((is_bool($arg)) &&
638 1
                (!Functions::isMatrixValue($k))) {
639
            } else {
640 1
                if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
641 1
                    if (is_bool($arg)) {
642 1
                        $arg = (int) $arg;
643 1
                    } elseif (is_string($arg)) {
644 1
                        $arg = 0;
645
                    }
646 1
                    if ($returnValue === null) {
647 1
                        $returnValue = $arg;
648
                    } else {
649 1
                        $returnValue += $arg;
650
                    }
651 1
                    ++$aCount;
652
                }
653
            }
654
        }
655
656 1
        if ($aCount > 0) {
657 1
            return $returnValue / $aCount;
658
        }
659
660
        return Functions::DIV0();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...ation\Functions::DIV0() returns the type string which is incompatible with the documented return type double.
Loading history...
661
    }
662
663
    /**
664
     * AVERAGEIF.
665
     *
666
     * Returns the average value from a range of cells that contain numbers within the list of arguments
667
     *
668
     * Excel Function:
669
     *        AVERAGEIF(value1[,value2[, ...]],condition)
670
     *
671
     * @category Mathematical and Trigonometric Functions
672
     *
673
     * @param mixed $aArgs Data values
674
     * @param string $condition the criteria that defines which cells will be checked
675
     * @param mixed[] $averageArgs Data values
676
     *
677
     * @return float
678
     */
679
    public static function AVERAGEIF($aArgs, $condition, $averageArgs = [])
680
    {
681
        $returnValue = 0;
682
683
        $aArgs = Functions::flattenArray($aArgs);
684
        $averageArgs = Functions::flattenArray($averageArgs);
685
        if (empty($averageArgs)) {
686
            $averageArgs = $aArgs;
0 ignored issues
show
Unused Code introduced by
The assignment to $averageArgs is dead and can be removed.
Loading history...
687
        }
688
        $condition = Functions::ifCondition($condition);
689
        // Loop through arguments
690
        $aCount = 0;
691
        foreach ($aArgs as $key => $arg) {
692
            if (!is_numeric($arg)) {
693
                $arg = Calculation::wrapResult(strtoupper($arg));
694
            }
695
            $testCondition = '=' . $arg . $condition;
696
            if (Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
697
                if (($returnValue === null) || ($arg > $returnValue)) {
698
                    $returnValue += $arg;
699
                    ++$aCount;
700
                }
701
            }
702
        }
703
704
        if ($aCount > 0) {
705
            return $returnValue / $aCount;
706
        }
707
708
        return Functions::DIV0();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...ation\Functions::DIV0() returns the type string which is incompatible with the documented return type double.
Loading history...
709
    }
710
711
    /**
712
     * BETADIST.
713
     *
714
     * Returns the beta distribution.
715
     *
716
     * @param float $value Value at which you want to evaluate the distribution
717
     * @param float $alpha Parameter to the distribution
718
     * @param float $beta Parameter to the distribution
719
     * @param mixed $rMin
720
     * @param mixed $rMax
721
     *
722
     * @return float
723
     */
724
    public static function BETADIST($value, $alpha, $beta, $rMin = 0, $rMax = 1)
725
    {
726
        $value = Functions::flattenSingleValue($value);
727
        $alpha = Functions::flattenSingleValue($alpha);
728
        $beta = Functions::flattenSingleValue($beta);
729
        $rMin = Functions::flattenSingleValue($rMin);
730
        $rMax = Functions::flattenSingleValue($rMax);
731
732
        if ((is_numeric($value)) && (is_numeric($alpha)) && (is_numeric($beta)) && (is_numeric($rMin)) && (is_numeric($rMax))) {
733
            if (($value < $rMin) || ($value > $rMax) || ($alpha <= 0) || ($beta <= 0) || ($rMin == $rMax)) {
734
                return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type double.
Loading history...
735
            }
736
            if ($rMin > $rMax) {
737
                $tmp = $rMin;
738
                $rMin = $rMax;
739
                $rMax = $tmp;
740
            }
741
            $value -= $rMin;
742
            $value /= ($rMax - $rMin);
743
744
            return self::incompleteBeta($value, $alpha, $beta);
745
        }
746
747
        return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type double.
Loading history...
748
    }
749
750
    /**
751
     * BETAINV.
752
     *
753
     * Returns the inverse of the beta distribution.
754
     *
755
     * @param float $probability Probability at which you want to evaluate the distribution
756
     * @param float $alpha Parameter to the distribution
757
     * @param float $beta Parameter to the distribution
758
     * @param float $rMin Minimum value
759
     * @param float $rMax Maximum value
760
     *
761
     * @return float
762
     */
763
    public static function BETAINV($probability, $alpha, $beta, $rMin = 0, $rMax = 1)
764
    {
765
        $probability = Functions::flattenSingleValue($probability);
766
        $alpha = Functions::flattenSingleValue($alpha);
767
        $beta = Functions::flattenSingleValue($beta);
768
        $rMin = Functions::flattenSingleValue($rMin);
769
        $rMax = Functions::flattenSingleValue($rMax);
770
771
        if ((is_numeric($probability)) && (is_numeric($alpha)) && (is_numeric($beta)) && (is_numeric($rMin)) && (is_numeric($rMax))) {
772
            if (($alpha <= 0) || ($beta <= 0) || ($rMin == $rMax) || ($probability <= 0) || ($probability > 1)) {
773
                return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type double.
Loading history...
774
            }
775
            if ($rMin > $rMax) {
776
                $tmp = $rMin;
777
                $rMin = $rMax;
778
                $rMax = $tmp;
779
            }
780
            $a = 0;
781
            $b = 2;
782
783
            $i = 0;
784
            while ((($b - $a) > Functions::PRECISION) && ($i++ < self::MAX_ITERATIONS)) {
785
                $guess = ($a + $b) / 2;
786
                $result = self::BETADIST($guess, $alpha, $beta);
787
                if (($result == $probability) || ($result == 0)) {
788
                    $b = $a;
789
                } elseif ($result > $probability) {
790
                    $b = $guess;
791
                } else {
792
                    $a = $guess;
793
                }
794
            }
795
            if ($i == self::MAX_ITERATIONS) {
796
                return Functions::NA();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...ulation\Functions::NA() returns the type string which is incompatible with the documented return type double.
Loading history...
797
            }
798
799
            return round($rMin + $guess * ($rMax - $rMin), 12);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $guess does not seem to be defined for all execution paths leading up to this point.
Loading history...
800
        }
801
802
        return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type double.
Loading history...
803
    }
804
805
    /**
806
     * BINOMDIST.
807
     *
808
     * Returns the individual term binomial distribution probability. Use BINOMDIST in problems with
809
     *        a fixed number of tests or trials, when the outcomes of any trial are only success or failure,
810
     *        when trials are independent, and when the probability of success is constant throughout the
811
     *        experiment. For example, BINOMDIST can calculate the probability that two of the next three
812
     *        babies born are male.
813
     *
814
     * @param float $value Number of successes in trials
815
     * @param float $trials Number of trials
816
     * @param float $probability Probability of success on each trial
817
     * @param bool $cumulative
818
     *
819
     * @return float
820
     *
821
     * @todo    Cumulative distribution function
822
     */
823
    public static function BINOMDIST($value, $trials, $probability, $cumulative)
824
    {
825
        $value = floor(Functions::flattenSingleValue($value));
826
        $trials = floor(Functions::flattenSingleValue($trials));
827
        $probability = Functions::flattenSingleValue($probability);
828
829
        if ((is_numeric($value)) && (is_numeric($trials)) && (is_numeric($probability))) {
0 ignored issues
show
introduced by
The condition is_numeric($trials) is always true.
Loading history...
830
            if (($value < 0) || ($value > $trials)) {
831
                return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type double.
Loading history...
832
            }
833
            if (($probability < 0) || ($probability > 1)) {
834
                return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type double.
Loading history...
835
            }
836
            if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
0 ignored issues
show
introduced by
The condition is_bool($cumulative) is always true.
Loading history...
837
                if ($cumulative) {
838
                    $summer = 0;
839
                    for ($i = 0; $i <= $value; ++$i) {
840
                        $summer += MathTrig::COMBIN($trials, $i) * pow($probability, $i) * pow(1 - $probability, $trials - $i);
0 ignored issues
show
Bug introduced by
$trials of type double is incompatible with the type integer expected by parameter $numObjs of PhpOffice\PhpSpreadsheet...tion\MathTrig::COMBIN(). ( Ignorable by Annotation )

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

840
                        $summer += MathTrig::COMBIN(/** @scrutinizer ignore-type */ $trials, $i) * pow($probability, $i) * pow(1 - $probability, $trials - $i);
Loading history...
841
                    }
842
843
                    return $summer;
844
                }
845
846
                return MathTrig::COMBIN($trials, $value) * pow($probability, $value) * pow(1 - $probability, $trials - $value);
0 ignored issues
show
Bug introduced by
$value of type double is incompatible with the type integer expected by parameter $numInSet of PhpOffice\PhpSpreadsheet...tion\MathTrig::COMBIN(). ( Ignorable by Annotation )

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

846
                return MathTrig::COMBIN($trials, /** @scrutinizer ignore-type */ $value) * pow($probability, $value) * pow(1 - $probability, $trials - $value);
Loading history...
847
            }
848
        }
849
850
        return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type double.
Loading history...
851
    }
852
853
    /**
854
     * CHIDIST.
855
     *
856
     * Returns the one-tailed probability of the chi-squared distribution.
857
     *
858
     * @param float $value Value for the function
859
     * @param float $degrees degrees of freedom
860
     *
861
     * @return float
862
     */
863
    public static function CHIDIST($value, $degrees)
864
    {
865
        $value = Functions::flattenSingleValue($value);
866
        $degrees = floor(Functions::flattenSingleValue($degrees));
867
868
        if ((is_numeric($value)) && (is_numeric($degrees))) {
869
            if ($degrees < 1) {
870
                return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type double.
Loading history...
871
            }
872
            if ($value < 0) {
873
                if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
874
                    return 1;
875
                }
876
877
                return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type double.
Loading history...
878
            }
879
880
            return 1 - (self::incompleteGamma($degrees / 2, $value / 2) / self::gamma($degrees / 2));
881
        }
882
883
        return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type double.
Loading history...
884
    }
885
886
    /**
887
     * CHIINV.
888
     *
889
     * Returns the one-tailed probability of the chi-squared distribution.
890
     *
891
     * @param float $probability Probability for the function
892
     * @param float $degrees degrees of freedom
893
     *
894
     * @return float
895
     */
896
    public static function CHIINV($probability, $degrees)
897
    {
898
        $probability = Functions::flattenSingleValue($probability);
899
        $degrees = floor(Functions::flattenSingleValue($degrees));
900
901
        if ((is_numeric($probability)) && (is_numeric($degrees))) {
902
            $xLo = 100;
903
            $xHi = 0;
904
905
            $x = $xNew = 1;
906
            $dx = 1;
907
            $i = 0;
908
909
            while ((abs($dx) > Functions::PRECISION) && ($i++ < self::MAX_ITERATIONS)) {
910
                // Apply Newton-Raphson step
911
                $result = self::CHIDIST($x, $degrees);
912
                $error = $result - $probability;
913
                if ($error == 0.0) {
914
                    $dx = 0;
915
                } elseif ($error < 0.0) {
916
                    $xLo = $x;
917
                } else {
918
                    $xHi = $x;
919
                }
920
                // Avoid division by zero
921
                if ($result != 0.0) {
922
                    $dx = $error / $result;
923
                    $xNew = $x - $dx;
924
                }
925
                // If the NR fails to converge (which for example may be the
926
                // case if the initial guess is too rough) we apply a bisection
927
                // step to determine a more narrow interval around the root.
928
                if (($xNew < $xLo) || ($xNew > $xHi) || ($result == 0.0)) {
929
                    $xNew = ($xLo + $xHi) / 2;
930
                    $dx = $xNew - $x;
931
                }
932
                $x = $xNew;
933
            }
934
            if ($i == self::MAX_ITERATIONS) {
935
                return Functions::NA();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...ulation\Functions::NA() returns the type string which is incompatible with the documented return type double.
Loading history...
936
            }
937
938
            return round($x, 12);
939
        }
940
941
        return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type double.
Loading history...
942
    }
943
944
    /**
945
     * CONFIDENCE.
946
     *
947
     * Returns the confidence interval for a population mean
948
     *
949
     * @param float $alpha
950
     * @param float $stdDev Standard Deviation
951
     * @param float $size
952
     *
953
     * @return float
954
     */
955
    public static function CONFIDENCE($alpha, $stdDev, $size)
956
    {
957
        $alpha = Functions::flattenSingleValue($alpha);
958
        $stdDev = Functions::flattenSingleValue($stdDev);
959
        $size = floor(Functions::flattenSingleValue($size));
960
961
        if ((is_numeric($alpha)) && (is_numeric($stdDev)) && (is_numeric($size))) {
962
            if (($alpha <= 0) || ($alpha >= 1)) {
963
                return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type double.
Loading history...
964
            }
965
            if (($stdDev <= 0) || ($size < 1)) {
966
                return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type double.
Loading history...
967
            }
968
969
            return self::NORMSINV(1 - $alpha / 2) * $stdDev / sqrt($size);
970
        }
971
972
        return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type double.
Loading history...
973
    }
974
975
    /**
976
     * CORREL.
977
     *
978
     * Returns covariance, the average of the products of deviations for each data point pair.
979
     *
980
     * @param mixed $yValues array of mixed Data Series Y
981
     * @param null|mixed $xValues array of mixed Data Series X
982
     *
983
     * @return float
984
     */
985
    public static function CORREL($yValues, $xValues = null)
986
    {
987
        if (($xValues === null) || (!is_array($yValues)) || (!is_array($xValues))) {
988
            return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type double.
Loading history...
989
        }
990
        if (!self::checkTrendArrays($yValues, $xValues)) {
991
            return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type double.
Loading history...
992
        }
993
        $yValueCount = count($yValues);
994
        $xValueCount = count($xValues);
995
996
        if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
997
            return Functions::NA();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...ulation\Functions::NA() returns the type string which is incompatible with the documented return type double.
Loading history...
998
        } elseif ($yValueCount == 1) {
999
            return Functions::DIV0();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...ation\Functions::DIV0() returns the type string which is incompatible with the documented return type double.
Loading history...
1000
        }
1001
1002
        $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
1003
1004
        return $bestFitLinear->getCorrelation();
1005
    }
1006
1007
    /**
1008
     * COUNT.
1009
     *
1010
     * Counts the number of cells that contain numbers within the list of arguments
1011
     *
1012
     * Excel Function:
1013
     *        COUNT(value1[,value2[, ...]])
1014
     *
1015
     * @category Statistical Functions
1016
     *
1017
     * @param mixed ...$args Data values
1018
     *
1019
     * @return int
1020
     */
1021 5
    public static function COUNT(...$args)
1022
    {
1023 5
        $returnValue = 0;
1024
1025
        // Loop through arguments
1026 5
        $aArgs = Functions::flattenArrayIndexed($args);
1027 5
        foreach ($aArgs as $k => $arg) {
1028 5
            if ((is_bool($arg)) &&
1029 5
                ((!Functions::isCellValue($k)) || (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE))) {
1030
                $arg = (int) $arg;
1031
            }
1032
            // Is it a numeric value?
1033 5
            if ((is_numeric($arg)) && (!is_string($arg))) {
1034 5
                ++$returnValue;
1035
            }
1036
        }
1037
1038 5
        return $returnValue;
1039
    }
1040
1041
    /**
1042
     * COUNTA.
1043
     *
1044
     * Counts the number of cells that are not empty within the list of arguments
1045
     *
1046
     * Excel Function:
1047
     *        COUNTA(value1[,value2[, ...]])
1048
     *
1049
     * @category Statistical Functions
1050
     *
1051
     * @param mixed ...$args Data values
1052
     *
1053
     * @return int
1054
     */
1055 3
    public static function COUNTA(...$args)
1056
    {
1057 3
        $returnValue = 0;
1058
1059
        // Loop through arguments
1060 3
        $aArgs = Functions::flattenArray($args);
1061 3
        foreach ($aArgs as $arg) {
1062
            // Is it a numeric, boolean or string value?
1063 3
            if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
1064 3
                ++$returnValue;
1065
            }
1066
        }
1067
1068 3
        return $returnValue;
1069
    }
1070
1071
    /**
1072
     * COUNTBLANK.
1073
     *
1074
     * Counts the number of empty cells within the list of arguments
1075
     *
1076
     * Excel Function:
1077
     *        COUNTBLANK(value1[,value2[, ...]])
1078
     *
1079
     * @category Statistical Functions
1080
     *
1081
     * @param mixed ...$args Data values
1082
     *
1083
     * @return int
1084
     */
1085
    public static function COUNTBLANK(...$args)
1086
    {
1087
        $returnValue = 0;
1088
1089
        // Loop through arguments
1090
        $aArgs = Functions::flattenArray($args);
1091
        foreach ($aArgs as $arg) {
1092
            // Is it a blank cell?
1093
            if (($arg === null) || ((is_string($arg)) && ($arg == ''))) {
1094
                ++$returnValue;
1095
            }
1096
        }
1097
1098
        return $returnValue;
1099
    }
1100
1101
    /**
1102
     * COUNTIF.
1103
     *
1104
     * Counts the number of cells that contain numbers within the list of arguments
1105
     *
1106
     * Excel Function:
1107
     *        COUNTIF(value1[,value2[, ...]],condition)
1108
     *
1109
     * @category Statistical Functions
1110
     *
1111
     * @param mixed $aArgs Data values
1112
     * @param string $condition the criteria that defines which cells will be counted
1113
     *
1114
     * @return int
1115
     */
1116
    public static function COUNTIF($aArgs, $condition)
1117
    {
1118
        $returnValue = 0;
1119
1120
        $aArgs = Functions::flattenArray($aArgs);
1121
        $condition = Functions::ifCondition($condition);
1122
        // Loop through arguments
1123
        foreach ($aArgs as $arg) {
1124
            if (!is_numeric($arg)) {
1125
                $arg = Calculation::wrapResult(strtoupper($arg));
1126
            }
1127
            $testCondition = '=' . $arg . $condition;
1128
            if (Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
1129
                // Is it a value within our criteria
1130
                ++$returnValue;
1131
            }
1132
        }
1133
1134
        return $returnValue;
1135
    }
1136
1137
    /**
1138
     * COUNTIFS.
1139
     *
1140
     * Counts the number of cells that contain numbers within the list of arguments
1141
     *
1142
     * Excel Function:
1143
     *        COUNTIFS(criteria_range1, criteria1, [criteria_range2, criteria2]…)
1144
     *
1145
     * @category Statistical Functions
1146
     *
1147
     * @param mixed $args Criterias
1148
     *
1149
     * @return int
1150
     */
1151 2
    public static function COUNTIFS(...$args)
1152
    {
1153 2
        $arrayList = $args;
1154
1155
        // Return value
1156 2
        $returnValue = 0;
1157
1158 2
        if (!$arrayList) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $arrayList of type array<integer,mixed> is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1159
            return $returnValue;
1160
        }
1161
1162 2
        $aArgsArray = [];
1163 2
        $conditions = [];
1164
1165 2
        while (count($arrayList) > 0) {
1166 2
            $aArgsArray[] = Functions::flattenArray(array_shift($arrayList));
1167 2
            $conditions[] = Functions::ifCondition(array_shift($arrayList));
1168
        }
1169
1170
        // Loop through each arg and see if arguments and conditions are true
1171 2
        foreach (array_keys($aArgsArray[0]) as $index) {
1172 2
            $valid = true;
1173
1174 2
            foreach ($conditions as $cidx => $condition) {
1175 2
                $arg = $aArgsArray[$cidx][$index];
1176
1177
                // Loop through arguments
1178 2
                if (!is_numeric($arg)) {
1179 2
                    $arg = Calculation::wrapResult(strtoupper($arg));
1180
                }
1181 2
                $testCondition = '=' . $arg . $condition;
1182 2
                if (!Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
1183
                    // Is not a value within our criteria
1184 2
                    $valid = false;
1185
1186 2
                    break; // if false found, don't need to check other conditions
1187
                }
1188
            }
1189
1190 2
            if ($valid) {
1191 2
                ++$returnValue;
1192
            }
1193
        }
1194
1195
        // Return
1196 2
        return $returnValue;
1197
    }
1198
1199
    /**
1200
     * COVAR.
1201
     *
1202
     * Returns covariance, the average of the products of deviations for each data point pair.
1203
     *
1204
     * @param mixed $yValues array of mixed Data Series Y
1205
     * @param mixed $xValues array of mixed Data Series X
1206
     *
1207
     * @return float
1208
     */
1209
    public static function COVAR($yValues, $xValues)
1210
    {
1211
        if (!self::checkTrendArrays($yValues, $xValues)) {
1212
            return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type double.
Loading history...
1213
        }
1214
        $yValueCount = count($yValues);
1215
        $xValueCount = count($xValues);
1216
1217
        if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
1218
            return Functions::NA();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...ulation\Functions::NA() returns the type string which is incompatible with the documented return type double.
Loading history...
1219
        } elseif ($yValueCount == 1) {
1220
            return Functions::DIV0();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...ation\Functions::DIV0() returns the type string which is incompatible with the documented return type double.
Loading history...
1221
        }
1222
1223
        $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
1224
1225
        return $bestFitLinear->getCovariance();
1226
    }
1227
1228
    /**
1229
     * CRITBINOM.
1230
     *
1231
     * Returns the smallest value for which the cumulative binomial distribution is greater
1232
     *        than or equal to a criterion value
1233
     *
1234
     * See https://support.microsoft.com/en-us/help/828117/ for details of the algorithm used
1235
     *
1236
     * @param float $trials number of Bernoulli trials
1237
     * @param float $probability probability of a success on each trial
1238
     * @param float $alpha criterion value
1239
     *
1240
     * @return int
1241
     *
1242
     * @todo    Warning. This implementation differs from the algorithm detailed on the MS
1243
     *            web site in that $CumPGuessMinus1 = $CumPGuess - 1 rather than $CumPGuess - $PGuess
1244
     *            This eliminates a potential endless loop error, but may have an adverse affect on the
1245
     *            accuracy of the function (although all my tests have so far returned correct results).
1246
     */
1247
    public static function CRITBINOM($trials, $probability, $alpha)
1248
    {
1249
        $trials = floor(Functions::flattenSingleValue($trials));
1250
        $probability = Functions::flattenSingleValue($probability);
1251
        $alpha = Functions::flattenSingleValue($alpha);
1252
1253
        if ((is_numeric($trials)) && (is_numeric($probability)) && (is_numeric($alpha))) {
1254
            if ($trials < 0) {
1255
                return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type integer.
Loading history...
1256
            } elseif (($probability < 0) || ($probability > 1)) {
1257
                return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type integer.
Loading history...
1258
            } elseif (($alpha < 0) || ($alpha > 1)) {
1259
                return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type integer.
Loading history...
1260
            } elseif ($alpha <= 0.5) {
1261
                $t = sqrt(log(1 / ($alpha * $alpha)));
1262
                $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));
1263
            } else {
1264
                $t = sqrt(log(1 / pow(1 - $alpha, 2)));
1265
                $trialsApprox = $t - (2.515517 + 0.802853 * $t + 0.010328 * $t * $t) / (1 + 1.432788 * $t + 0.189269 * $t * $t + 0.001308 * $t * $t * $t);
1266
            }
1267
            $Guess = floor($trials * $probability + $trialsApprox * sqrt($trials * $probability * (1 - $probability)));
1268
            if ($Guess < 0) {
1269
                $Guess = 0;
1270
            } elseif ($Guess > $trials) {
1271
                $Guess = $trials;
1272
            }
1273
1274
            $TotalUnscaledProbability = $UnscaledPGuess = $UnscaledCumPGuess = 0.0;
1275
            $EssentiallyZero = 10e-12;
1276
1277
            $m = floor($trials * $probability);
1278
            ++$TotalUnscaledProbability;
1279
            if ($m == $Guess) {
1280
                ++$UnscaledPGuess;
1281
            }
1282
            if ($m <= $Guess) {
1283
                ++$UnscaledCumPGuess;
1284
            }
1285
1286
            $PreviousValue = 1;
1287
            $Done = false;
1288
            $k = $m + 1;
1289
            while ((!$Done) && ($k <= $trials)) {
1290
                $CurrentValue = $PreviousValue * ($trials - $k + 1) * $probability / ($k * (1 - $probability));
1291
                $TotalUnscaledProbability += $CurrentValue;
1292
                if ($k == $Guess) {
1293
                    $UnscaledPGuess += $CurrentValue;
1294
                }
1295
                if ($k <= $Guess) {
1296
                    $UnscaledCumPGuess += $CurrentValue;
1297
                }
1298
                if ($CurrentValue <= $EssentiallyZero) {
1299
                    $Done = true;
1300
                }
1301
                $PreviousValue = $CurrentValue;
1302
                ++$k;
1303
            }
1304
1305
            $PreviousValue = 1;
1306
            $Done = false;
1307
            $k = $m - 1;
1308
            while ((!$Done) && ($k >= 0)) {
1309
                $CurrentValue = $PreviousValue * $k + 1 * (1 - $probability) / (($trials - $k) * $probability);
1310
                $TotalUnscaledProbability += $CurrentValue;
1311
                if ($k == $Guess) {
1312
                    $UnscaledPGuess += $CurrentValue;
1313
                }
1314
                if ($k <= $Guess) {
1315
                    $UnscaledCumPGuess += $CurrentValue;
1316
                }
1317
                if ($CurrentValue <= $EssentiallyZero) {
1318
                    $Done = true;
1319
                }
1320
                $PreviousValue = $CurrentValue;
1321
                --$k;
1322
            }
1323
1324
            $PGuess = $UnscaledPGuess / $TotalUnscaledProbability;
1325
            $CumPGuess = $UnscaledCumPGuess / $TotalUnscaledProbability;
1326
1327
            $CumPGuessMinus1 = $CumPGuess - 1;
1328
1329
            while (true) {
1330
                if (($CumPGuessMinus1 < $alpha) && ($CumPGuess >= $alpha)) {
1331
                    return $Guess;
1332
                } elseif (($CumPGuessMinus1 < $alpha) && ($CumPGuess < $alpha)) {
1333
                    $PGuessPlus1 = $PGuess * ($trials - $Guess) * $probability / $Guess / (1 - $probability);
1334
                    $CumPGuessMinus1 = $CumPGuess;
1335
                    $CumPGuess = $CumPGuess + $PGuessPlus1;
1336
                    $PGuess = $PGuessPlus1;
1337
                    ++$Guess;
1338
                } elseif (($CumPGuessMinus1 >= $alpha) && ($CumPGuess >= $alpha)) {
1339
                    $PGuessMinus1 = $PGuess * $Guess * (1 - $probability) / ($trials - $Guess + 1) / $probability;
1340
                    $CumPGuess = $CumPGuessMinus1;
1341
                    $CumPGuessMinus1 = $CumPGuessMinus1 - $PGuess;
1342
                    $PGuess = $PGuessMinus1;
1343
                    --$Guess;
1344
                }
1345
            }
1346
        }
1347
1348
        return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type integer.
Loading history...
1349
    }
1350
1351
    /**
1352
     * DEVSQ.
1353
     *
1354
     * Returns the sum of squares of deviations of data points from their sample mean.
1355
     *
1356
     * Excel Function:
1357
     *        DEVSQ(value1[,value2[, ...]])
1358
     *
1359
     * @category Statistical Functions
1360
     *
1361
     * @param mixed ...$args Data values
1362
     *
1363
     * @return float
1364
     */
1365 1
    public static function DEVSQ(...$args)
1366
    {
1367 1
        $aArgs = Functions::flattenArrayIndexed($args);
1368
1369
        // Return value
1370 1
        $returnValue = null;
1371
1372 1
        $aMean = self::AVERAGE($aArgs);
1373 1
        if ($aMean != Functions::DIV0()) {
1374 1
            $aCount = -1;
1375 1
            foreach ($aArgs as $k => $arg) {
1376
                // Is it a numeric value?
1377 1
                if ((is_bool($arg)) &&
1378 1
                    ((!Functions::isCellValue($k)) ||
1379 1
                    (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE))) {
1380
                    $arg = (int) $arg;
1381
                }
1382 1
                if ((is_numeric($arg)) && (!is_string($arg))) {
1383 1
                    if ($returnValue === null) {
1384 1
                        $returnValue = pow(($arg - $aMean), 2);
1385
                    } else {
1386 1
                        $returnValue += pow(($arg - $aMean), 2);
1387
                    }
1388 1
                    ++$aCount;
1389
                }
1390
            }
1391
1392
            // Return
1393 1
            if ($returnValue === null) {
1394
                return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type double.
Loading history...
1395
            }
1396
1397 1
            return $returnValue;
1398
        }
1399
1400
        return self::NA();
0 ignored issues
show
Bug introduced by
The method NA() does not exist on PhpOffice\PhpSpreadsheet\Calculation\Statistical. ( Ignorable by Annotation )

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

1400
        return self::/** @scrutinizer ignore-call */ NA();

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...
1401
    }
1402
1403
    /**
1404
     * EXPONDIST.
1405
     *
1406
     *    Returns the exponential distribution. Use EXPONDIST to model the time between events,
1407
     *        such as how long an automated bank teller takes to deliver cash. For example, you can
1408
     *        use EXPONDIST to determine the probability that the process takes at most 1 minute.
1409
     *
1410
     * @param float $value Value of the function
1411
     * @param float $lambda The parameter value
1412
     * @param bool $cumulative
1413
     *
1414
     * @return float
1415
     */
1416
    public static function EXPONDIST($value, $lambda, $cumulative)
1417
    {
1418
        $value = Functions::flattenSingleValue($value);
1419
        $lambda = Functions::flattenSingleValue($lambda);
1420
        $cumulative = Functions::flattenSingleValue($cumulative);
1421
1422
        if ((is_numeric($value)) && (is_numeric($lambda))) {
1423
            if (($value < 0) || ($lambda < 0)) {
1424
                return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type double.
Loading history...
1425
            }
1426
            if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
1427
                if ($cumulative) {
1428
                    return 1 - exp(0 - $value * $lambda);
1429
                }
1430
1431
                return $lambda * exp(0 - $value * $lambda);
1432
            }
1433
        }
1434
1435
        return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type double.
Loading history...
1436
    }
1437
1438
    /**
1439
     * FISHER.
1440
     *
1441
     * Returns the Fisher transformation at x. This transformation produces a function that
1442
     *        is normally distributed rather than skewed. Use this function to perform hypothesis
1443
     *        testing on the correlation coefficient.
1444
     *
1445
     * @param float $value
1446
     *
1447
     * @return float
1448
     */
1449
    public static function FISHER($value)
1450
    {
1451
        $value = Functions::flattenSingleValue($value);
1452
1453
        if (is_numeric($value)) {
1454
            if (($value <= -1) || ($value >= 1)) {
1455
                return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type double.
Loading history...
1456
            }
1457
1458
            return 0.5 * log((1 + $value) / (1 - $value));
1459
        }
1460
1461
        return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type double.
Loading history...
1462
    }
1463
1464
    /**
1465
     * FISHERINV.
1466
     *
1467
     * Returns the inverse of the Fisher transformation. Use this transformation when
1468
     *        analyzing correlations between ranges or arrays of data. If y = FISHER(x), then
1469
     *        FISHERINV(y) = x.
1470
     *
1471
     * @param float $value
1472
     *
1473
     * @return float
1474
     */
1475
    public static function FISHERINV($value)
1476
    {
1477
        $value = Functions::flattenSingleValue($value);
1478
1479
        if (is_numeric($value)) {
1480
            return (exp(2 * $value) - 1) / (exp(2 * $value) + 1);
1481
        }
1482
1483
        return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type double.
Loading history...
1484
    }
1485
1486
    /**
1487
     * FORECAST.
1488
     *
1489
     * Calculates, or predicts, a future value by using existing values. The predicted value is a y-value for a given x-value.
1490
     *
1491
     * @param float $xValue Value of X for which we want to find Y
1492
     * @param mixed $yValues array of mixed Data Series Y
1493
     * @param mixed $xValues of mixed Data Series X
1494
     *
1495
     * @return float
1496
     */
1497
    public static function FORECAST($xValue, $yValues, $xValues)
1498
    {
1499
        $xValue = Functions::flattenSingleValue($xValue);
1500
        if (!is_numeric($xValue)) {
1501
            return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type double.
Loading history...
1502
        } elseif (!self::checkTrendArrays($yValues, $xValues)) {
1503
            return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type double.
Loading history...
1504
        }
1505
        $yValueCount = count($yValues);
1506
        $xValueCount = count($xValues);
1507
1508
        if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
1509
            return Functions::NA();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...ulation\Functions::NA() returns the type string which is incompatible with the documented return type double.
Loading history...
1510
        } elseif ($yValueCount == 1) {
1511
            return Functions::DIV0();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...ation\Functions::DIV0() returns the type string which is incompatible with the documented return type double.
Loading history...
1512
        }
1513
1514
        $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
1515
1516
        return $bestFitLinear->getValueOfYForX($xValue);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $bestFitLinear->getValueOfYForX($xValue) returns the type false which is incompatible with the documented return type double.
Loading history...
1517
    }
1518
1519
    /**
1520
     * GAMMADIST.
1521
     *
1522
     * Returns the gamma distribution.
1523
     *
1524
     * @param float $value Value at which you want to evaluate the distribution
1525
     * @param float $a Parameter to the distribution
1526
     * @param float $b Parameter to the distribution
1527
     * @param bool $cumulative
1528
     *
1529
     * @return float
1530
     */
1531
    public static function GAMMADIST($value, $a, $b, $cumulative)
1532
    {
1533
        $value = Functions::flattenSingleValue($value);
1534
        $a = Functions::flattenSingleValue($a);
1535
        $b = Functions::flattenSingleValue($b);
1536
1537
        if ((is_numeric($value)) && (is_numeric($a)) && (is_numeric($b))) {
1538
            if (($value < 0) || ($a <= 0) || ($b <= 0)) {
1539
                return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type double.
Loading history...
1540
            }
1541
            if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
0 ignored issues
show
introduced by
The condition is_bool($cumulative) is always true.
Loading history...
1542
                if ($cumulative) {
1543
                    return self::incompleteGamma($a, $value / $b) / self::gamma($a);
1544
                }
1545
1546
                return (1 / (pow($b, $a) * self::gamma($a))) * pow($value, $a - 1) * exp(0 - ($value / $b));
1547
            }
1548
        }
1549
1550
        return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type double.
Loading history...
1551
    }
1552
1553
    /**
1554
     * GAMMAINV.
1555
     *
1556
     * Returns the inverse of the beta distribution.
1557
     *
1558
     * @param float $probability Probability at which you want to evaluate the distribution
1559
     * @param float $alpha Parameter to the distribution
1560
     * @param float $beta Parameter to the distribution
1561
     *
1562
     * @return float
1563
     */
1564
    public static function GAMMAINV($probability, $alpha, $beta)
1565
    {
1566
        $probability = Functions::flattenSingleValue($probability);
1567
        $alpha = Functions::flattenSingleValue($alpha);
1568
        $beta = Functions::flattenSingleValue($beta);
1569
1570
        if ((is_numeric($probability)) && (is_numeric($alpha)) && (is_numeric($beta))) {
1571
            if (($alpha <= 0) || ($beta <= 0) || ($probability < 0) || ($probability > 1)) {
1572
                return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type double.
Loading history...
1573
            }
1574
1575
            $xLo = 0;
1576
            $xHi = $alpha * $beta * 5;
1577
1578
            $x = $xNew = 1;
1579
            $error = $pdf = 0;
0 ignored issues
show
Unused Code introduced by
The assignment to $error is dead and can be removed.
Loading history...
Unused Code introduced by
The assignment to $pdf is dead and can be removed.
Loading history...
1580
            $dx = 1024;
1581
            $i = 0;
1582
1583
            while ((abs($dx) > Functions::PRECISION) && ($i++ < self::MAX_ITERATIONS)) {
1584
                // Apply Newton-Raphson step
1585
                $error = self::GAMMADIST($x, $alpha, $beta, true) - $probability;
1586
                if ($error < 0.0) {
1587
                    $xLo = $x;
1588
                } else {
1589
                    $xHi = $x;
1590
                }
1591
                $pdf = self::GAMMADIST($x, $alpha, $beta, false);
1592
                // Avoid division by zero
1593
                if ($pdf != 0.0) {
1594
                    $dx = $error / $pdf;
1595
                    $xNew = $x - $dx;
1596
                }
1597
                // If the NR fails to converge (which for example may be the
1598
                // case if the initial guess is too rough) we apply a bisection
1599
                // step to determine a more narrow interval around the root.
1600
                if (($xNew < $xLo) || ($xNew > $xHi) || ($pdf == 0.0)) {
1601
                    $xNew = ($xLo + $xHi) / 2;
1602
                    $dx = $xNew - $x;
1603
                }
1604
                $x = $xNew;
1605
            }
1606
            if ($i == self::MAX_ITERATIONS) {
1607
                return Functions::NA();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...ulation\Functions::NA() returns the type string which is incompatible with the documented return type double.
Loading history...
1608
            }
1609
1610
            return $x;
1611
        }
1612
1613
        return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type double.
Loading history...
1614
    }
1615
1616
    /**
1617
     * GAMMALN.
1618
     *
1619
     * Returns the natural logarithm of the gamma function.
1620
     *
1621
     * @param float $value
1622
     *
1623
     * @return float
1624
     */
1625
    public static function GAMMALN($value)
1626
    {
1627
        $value = Functions::flattenSingleValue($value);
1628
1629
        if (is_numeric($value)) {
1630
            if ($value <= 0) {
1631
                return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type double.
Loading history...
1632
            }
1633
1634
            return log(self::gamma($value));
1635
        }
1636
1637
        return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type double.
Loading history...
1638
    }
1639
1640
    /**
1641
     * GEOMEAN.
1642
     *
1643
     * Returns the geometric mean of an array or range of positive data. For example, you
1644
     *        can use GEOMEAN to calculate average growth rate given compound interest with
1645
     *        variable rates.
1646
     *
1647
     * Excel Function:
1648
     *        GEOMEAN(value1[,value2[, ...]])
1649
     *
1650
     * @category Statistical Functions
1651
     *
1652
     * @param mixed ...$args Data values
1653
     *
1654
     * @return float
1655
     */
1656
    public static function GEOMEAN(...$args)
1657
    {
1658
        $aArgs = Functions::flattenArray($args);
1659
1660
        $aMean = MathTrig::PRODUCT($aArgs);
1661
        if (is_numeric($aMean) && ($aMean > 0)) {
1662
            $aCount = self::COUNT($aArgs);
1663
            if (self::MIN($aArgs) > 0) {
1664
                return pow($aMean, (1 / $aCount));
1665
            }
1666
        }
1667
1668
        return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type double.
Loading history...
1669
    }
1670
1671
    /**
1672
     * GROWTH.
1673
     *
1674
     * Returns values along a predicted emponential Trend
1675
     *
1676
     * @param mixed[] $yValues Data Series Y
1677
     * @param mixed[] $xValues Data Series X
1678
     * @param mixed[] $newValues Values of X for which we want to find Y
1679
     * @param bool $const a logical value specifying whether to force the intersect to equal 0
1680
     *
1681
     * @return array of float
1682
     */
1683
    public static function GROWTH($yValues, $xValues = [], $newValues = [], $const = true)
1684
    {
1685
        $yValues = Functions::flattenArray($yValues);
1686
        $xValues = Functions::flattenArray($xValues);
1687
        $newValues = Functions::flattenArray($newValues);
1688
        $const = ($const === null) ? true : (bool) Functions::flattenSingleValue($const);
0 ignored issues
show
introduced by
The condition $const === null is always false.
Loading history...
1689
1690
        $bestFitExponential = Trend::calculate(Trend::TREND_EXPONENTIAL, $yValues, $xValues, $const);
1691
        if (empty($newValues)) {
1692
            $newValues = $bestFitExponential->getXValues();
1693
        }
1694
1695
        $returnArray = [];
1696
        foreach ($newValues as $xValue) {
1697
            $returnArray[0][] = $bestFitExponential->getValueOfYForX($xValue);
1698
        }
1699
1700
        return $returnArray;
1701
    }
1702
1703
    /**
1704
     * HARMEAN.
1705
     *
1706
     * Returns the harmonic mean of a data set. The harmonic mean is the reciprocal of the
1707
     *        arithmetic mean of reciprocals.
1708
     *
1709
     * Excel Function:
1710
     *        HARMEAN(value1[,value2[, ...]])
1711
     *
1712
     * @category Statistical Functions
1713
     *
1714
     * @param mixed ...$args Data values
1715
     *
1716
     * @return float
1717
     */
1718
    public static function HARMEAN(...$args)
1719
    {
1720
        // Return value
1721
        $returnValue = Functions::NA();
1722
1723
        // Loop through arguments
1724
        $aArgs = Functions::flattenArray($args);
1725
        if (self::MIN($aArgs) < 0) {
1726
            return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type double.
Loading history...
1727
        }
1728
        $aCount = 0;
1729
        foreach ($aArgs as $arg) {
1730
            // Is it a numeric value?
1731
            if ((is_numeric($arg)) && (!is_string($arg))) {
1732
                if ($arg <= 0) {
1733
                    return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type double.
Loading history...
1734
                }
1735
                if ($returnValue === null) {
1736
                    $returnValue = (1 / $arg);
1737
                } else {
1738
                    $returnValue += (1 / $arg);
1739
                }
1740
                ++$aCount;
1741
            }
1742
        }
1743
1744
        // Return
1745
        if ($aCount > 0) {
1746
            return 1 / ($returnValue / $aCount);
1747
        }
1748
1749
        return $returnValue;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $returnValue also could return the type string which is incompatible with the documented return type double.
Loading history...
1750
    }
1751
1752
    /**
1753
     * HYPGEOMDIST.
1754
     *
1755
     * Returns the hypergeometric distribution. HYPGEOMDIST returns the probability of a given number of
1756
     * sample successes, given the sample size, population successes, and population size.
1757
     *
1758
     * @param float $sampleSuccesses Number of successes in the sample
1759
     * @param float $sampleNumber Size of the sample
1760
     * @param float $populationSuccesses Number of successes in the population
1761
     * @param float $populationNumber Population size
1762
     *
1763
     * @return float
1764
     */
1765
    public static function HYPGEOMDIST($sampleSuccesses, $sampleNumber, $populationSuccesses, $populationNumber)
1766
    {
1767
        $sampleSuccesses = floor(Functions::flattenSingleValue($sampleSuccesses));
1768
        $sampleNumber = floor(Functions::flattenSingleValue($sampleNumber));
1769
        $populationSuccesses = floor(Functions::flattenSingleValue($populationSuccesses));
1770
        $populationNumber = floor(Functions::flattenSingleValue($populationNumber));
1771
1772
        if ((is_numeric($sampleSuccesses)) && (is_numeric($sampleNumber)) && (is_numeric($populationSuccesses)) && (is_numeric($populationNumber))) {
0 ignored issues
show
introduced by
The condition is_numeric($populationNumber) is always true.
Loading history...
introduced by
The condition is_numeric($sampleNumber) is always true.
Loading history...
introduced by
The condition is_numeric($populationSuccesses) is always true.
Loading history...
1773
            if (($sampleSuccesses < 0) || ($sampleSuccesses > $sampleNumber) || ($sampleSuccesses > $populationSuccesses)) {
1774
                return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type double.
Loading history...
1775
            }
1776
            if (($sampleNumber <= 0) || ($sampleNumber > $populationNumber)) {
1777
                return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type double.
Loading history...
1778
            }
1779
            if (($populationSuccesses <= 0) || ($populationSuccesses > $populationNumber)) {
1780
                return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type double.
Loading history...
1781
            }
1782
1783
            return MathTrig::COMBIN($populationSuccesses, $sampleSuccesses) *
0 ignored issues
show
Bug introduced by
$populationSuccesses of type double is incompatible with the type integer expected by parameter $numObjs of PhpOffice\PhpSpreadsheet...tion\MathTrig::COMBIN(). ( Ignorable by Annotation )

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

1783
            return MathTrig::COMBIN(/** @scrutinizer ignore-type */ $populationSuccesses, $sampleSuccesses) *
Loading history...
Bug introduced by
$sampleSuccesses of type double is incompatible with the type integer expected by parameter $numInSet of PhpOffice\PhpSpreadsheet...tion\MathTrig::COMBIN(). ( Ignorable by Annotation )

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

1783
            return MathTrig::COMBIN($populationSuccesses, /** @scrutinizer ignore-type */ $sampleSuccesses) *
Loading history...
1784
                   MathTrig::COMBIN($populationNumber - $populationSuccesses, $sampleNumber - $sampleSuccesses) /
1785
                   MathTrig::COMBIN($populationNumber, $sampleNumber);
1786
        }
1787
1788
        return Functions::VALUE();
1789
    }
1790
1791
    /**
1792
     * INTERCEPT.
1793
     *
1794
     * Calculates the point at which a line will intersect the y-axis by using existing x-values and y-values.
1795
     *
1796
     * @param mixed[] $yValues Data Series Y
1797
     * @param mixed[] $xValues Data Series X
1798
     *
1799
     * @return float
1800
     */
1801
    public static function INTERCEPT($yValues, $xValues)
1802
    {
1803
        if (!self::checkTrendArrays($yValues, $xValues)) {
1804
            return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type double.
Loading history...
1805
        }
1806
        $yValueCount = count($yValues);
1807
        $xValueCount = count($xValues);
1808
1809
        if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
1810
            return Functions::NA();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...ulation\Functions::NA() returns the type string which is incompatible with the documented return type double.
Loading history...
1811
        } elseif ($yValueCount == 1) {
1812
            return Functions::DIV0();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...ation\Functions::DIV0() returns the type string which is incompatible with the documented return type double.
Loading history...
1813
        }
1814
1815
        $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
1816
1817
        return $bestFitLinear->getIntersect();
1818
    }
1819
1820
    /**
1821
     * KURT.
1822
     *
1823
     * Returns the kurtosis of a data set. Kurtosis characterizes the relative peakedness
1824
     * or flatness of a distribution compared with the normal distribution. Positive
1825
     * kurtosis indicates a relatively peaked distribution. Negative kurtosis indicates a
1826
     * relatively flat distribution.
1827
     *
1828
     * @param array ...$args Data Series
1829
     *
1830
     * @return float
1831
     */
1832
    public static function KURT(...$args)
1833
    {
1834
        $aArgs = Functions::flattenArrayIndexed($args);
1835
        $mean = self::AVERAGE($aArgs);
1836
        $stdDev = self::STDEV($aArgs);
1837
1838
        if ($stdDev > 0) {
1839
            $count = $summer = 0;
1840
            // Loop through arguments
1841
            foreach ($aArgs as $k => $arg) {
1842
                if ((is_bool($arg)) &&
1843
                    (!Functions::isMatrixValue($k))) {
1844
                } else {
1845
                    // Is it a numeric value?
1846
                    if ((is_numeric($arg)) && (!is_string($arg))) {
1847
                        $summer += pow((($arg - $mean) / $stdDev), 4);
1848
                        ++$count;
1849
                    }
1850
                }
1851
            }
1852
1853
            // Return
1854
            if ($count > 3) {
1855
                return $summer * ($count * ($count + 1) / (($count - 1) * ($count - 2) * ($count - 3))) - (3 * pow($count - 1, 2) / (($count - 2) * ($count - 3)));
1856
            }
1857
        }
1858
1859
        return Functions::DIV0();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...ation\Functions::DIV0() returns the type string which is incompatible with the documented return type double.
Loading history...
1860
    }
1861
1862
    /**
1863
     * LARGE.
1864
     *
1865
     * Returns the nth largest value in a data set. You can use this function to
1866
     *        select a value based on its relative standing.
1867
     *
1868
     * Excel Function:
1869
     *        LARGE(value1[,value2[, ...]],entry)
1870
     *
1871
     * @category Statistical Functions
1872
     *
1873
     * @param mixed $args Data values
1874
     * @param int $entry Position (ordered from the largest) in the array or range of data to return
1875
     *
1876
     * @return float
1877
     */
1878
    public static function LARGE(...$args)
1879
    {
1880
        $aArgs = Functions::flattenArray($args);
1881
1882
        // Calculate
1883
        $entry = floor(array_pop($aArgs));
1884
1885
        if ((is_numeric($entry)) && (!is_string($entry))) {
0 ignored issues
show
introduced by
The condition is_string($entry) is always false.
Loading history...
1886
            $mArgs = [];
1887
            foreach ($aArgs as $arg) {
1888
                // Is it a numeric value?
1889
                if ((is_numeric($arg)) && (!is_string($arg))) {
1890
                    $mArgs[] = $arg;
1891
                }
1892
            }
1893
            $count = self::COUNT($mArgs);
1894
            $entry = floor(--$entry);
1895
            if (($entry < 0) || ($entry >= $count) || ($count == 0)) {
1896
                return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type double.
Loading history...
1897
            }
1898
            rsort($mArgs);
1899
1900
            return $mArgs[$entry];
1901
        }
1902
1903
        return Functions::VALUE();
1904
    }
1905
1906
    /**
1907
     * LINEST.
1908
     *
1909
     * Calculates the statistics for a line by using the "least squares" method to calculate a straight line that best fits your data,
1910
     *        and then returns an array that describes the line.
1911
     *
1912
     * @param mixed[] $yValues Data Series Y
1913
     * @param null|mixed[] $xValues Data Series X
1914
     * @param bool $const a logical value specifying whether to force the intersect to equal 0
1915
     * @param bool $stats a logical value specifying whether to return additional regression statistics
1916
     *
1917
     * @return array
1918
     */
1919
    public static function LINEST($yValues, $xValues = null, $const = true, $stats = false)
1920
    {
1921
        $const = ($const === null) ? true : (bool) Functions::flattenSingleValue($const);
0 ignored issues
show
introduced by
The condition $const === null is always false.
Loading history...
1922
        $stats = ($stats === null) ? false : (bool) Functions::flattenSingleValue($stats);
0 ignored issues
show
introduced by
The condition $stats === null is always false.
Loading history...
1923
        if ($xValues === null) {
1924
            $xValues = range(1, count(Functions::flattenArray($yValues)));
1925
        }
1926
1927
        if (!self::checkTrendArrays($yValues, $xValues)) {
1928
            return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type array.
Loading history...
1929
        }
1930
        $yValueCount = count($yValues);
1931
        $xValueCount = count($xValues);
1932
1933
        if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
1934
            return Functions::NA();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...ulation\Functions::NA() returns the type string which is incompatible with the documented return type array.
Loading history...
1935
        } elseif ($yValueCount == 1) {
1936
            return 0;
0 ignored issues
show
Bug Best Practice introduced by
The expression return 0 returns the type integer which is incompatible with the documented return type array.
Loading history...
1937
        }
1938
1939
        $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues, $const);
1940
        if ($stats) {
1941
            return [
1942
                [
1943
                    $bestFitLinear->getSlope(),
1944
                    $bestFitLinear->getSlopeSE(),
1945
                    $bestFitLinear->getGoodnessOfFit(),
1946
                    $bestFitLinear->getF(),
1947
                    $bestFitLinear->getSSRegression(),
1948
                ],
1949
                [
1950
                    $bestFitLinear->getIntersect(),
1951
                    $bestFitLinear->getIntersectSE(),
1952
                    $bestFitLinear->getStdevOfResiduals(),
1953
                    $bestFitLinear->getDFResiduals(),
1954
                    $bestFitLinear->getSSResiduals(),
1955
                ],
1956
            ];
1957
        }
1958
1959
        return [
1960
                $bestFitLinear->getSlope(),
1961
                $bestFitLinear->getIntersect(),
1962
            ];
1963
    }
1964
1965
    /**
1966
     * LOGEST.
1967
     *
1968
     * Calculates an exponential curve that best fits the X and Y data series,
1969
     *        and then returns an array that describes the line.
1970
     *
1971
     * @param mixed[] $yValues Data Series Y
1972
     * @param null|mixed[] $xValues Data Series X
1973
     * @param bool $const a logical value specifying whether to force the intersect to equal 0
1974
     * @param bool $stats a logical value specifying whether to return additional regression statistics
1975
     *
1976
     * @return array
1977
     */
1978
    public static function LOGEST($yValues, $xValues = null, $const = true, $stats = false)
1979
    {
1980
        $const = ($const === null) ? true : (bool) Functions::flattenSingleValue($const);
0 ignored issues
show
introduced by
The condition $const === null is always false.
Loading history...
1981
        $stats = ($stats === null) ? false : (bool) Functions::flattenSingleValue($stats);
0 ignored issues
show
introduced by
The condition $stats === null is always false.
Loading history...
1982
        if ($xValues === null) {
1983
            $xValues = range(1, count(Functions::flattenArray($yValues)));
1984
        }
1985
1986
        if (!self::checkTrendArrays($yValues, $xValues)) {
1987
            return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type array.
Loading history...
1988
        }
1989
        $yValueCount = count($yValues);
1990
        $xValueCount = count($xValues);
1991
1992
        foreach ($yValues as $value) {
1993
            if ($value <= 0.0) {
1994
                return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type array.
Loading history...
1995
            }
1996
        }
1997
1998
        if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
1999
            return Functions::NA();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...ulation\Functions::NA() returns the type string which is incompatible with the documented return type array.
Loading history...
2000
        } elseif ($yValueCount == 1) {
2001
            return 1;
0 ignored issues
show
Bug Best Practice introduced by
The expression return 1 returns the type integer which is incompatible with the documented return type array.
Loading history...
2002
        }
2003
2004
        $bestFitExponential = Trend::calculate(Trend::TREND_EXPONENTIAL, $yValues, $xValues, $const);
2005
        if ($stats) {
2006
            return [
2007
                [
2008
                    $bestFitExponential->getSlope(),
2009
                    $bestFitExponential->getSlopeSE(),
2010
                    $bestFitExponential->getGoodnessOfFit(),
2011
                    $bestFitExponential->getF(),
2012
                    $bestFitExponential->getSSRegression(),
2013
                ],
2014
                [
2015
                    $bestFitExponential->getIntersect(),
2016
                    $bestFitExponential->getIntersectSE(),
2017
                    $bestFitExponential->getStdevOfResiduals(),
2018
                    $bestFitExponential->getDFResiduals(),
2019
                    $bestFitExponential->getSSResiduals(),
2020
                ],
2021
            ];
2022
        }
2023
2024
        return [
2025
                $bestFitExponential->getSlope(),
2026
                $bestFitExponential->getIntersect(),
2027
            ];
2028
    }
2029
2030
    /**
2031
     * LOGINV.
2032
     *
2033
     * Returns the inverse of the normal cumulative distribution
2034
     *
2035
     * @param float $probability
2036
     * @param float $mean
2037
     * @param float $stdDev
2038
     *
2039
     * @return float
2040
     *
2041
     * @todo    Try implementing P J Acklam's refinement algorithm for greater
2042
     *            accuracy if I can get my head round the mathematics
2043
     *            (as described at) http://home.online.no/~pjacklam/notes/invnorm/
2044
     */
2045
    public static function LOGINV($probability, $mean, $stdDev)
2046
    {
2047
        $probability = Functions::flattenSingleValue($probability);
2048
        $mean = Functions::flattenSingleValue($mean);
2049
        $stdDev = Functions::flattenSingleValue($stdDev);
2050
2051
        if ((is_numeric($probability)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
2052
            if (($probability < 0) || ($probability > 1) || ($stdDev <= 0)) {
2053
                return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type double.
Loading history...
2054
            }
2055
2056
            return exp($mean + $stdDev * self::NORMSINV($probability));
2057
        }
2058
2059
        return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type double.
Loading history...
2060
    }
2061
2062
    /**
2063
     * LOGNORMDIST.
2064
     *
2065
     * Returns the cumulative lognormal distribution of x, where ln(x) is normally distributed
2066
     * with parameters mean and standard_dev.
2067
     *
2068
     * @param float $value
2069
     * @param float $mean
2070
     * @param float $stdDev
2071
     *
2072
     * @return float
2073
     */
2074
    public static function LOGNORMDIST($value, $mean, $stdDev)
2075
    {
2076
        $value = Functions::flattenSingleValue($value);
2077
        $mean = Functions::flattenSingleValue($mean);
2078
        $stdDev = Functions::flattenSingleValue($stdDev);
2079
2080
        if ((is_numeric($value)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
2081
            if (($value <= 0) || ($stdDev <= 0)) {
2082
                return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type double.
Loading history...
2083
            }
2084
2085
            return self::NORMSDIST((log($value) - $mean) / $stdDev);
2086
        }
2087
2088
        return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type double.
Loading history...
2089
    }
2090
2091
    /**
2092
     * MAX.
2093
     *
2094
     * MAX returns the value of the element of the values passed that has the highest value,
2095
     *        with negative numbers considered smaller than positive numbers.
2096
     *
2097
     * Excel Function:
2098
     *        MAX(value1[,value2[, ...]])
2099
     *
2100
     * @category Statistical Functions
2101
     *
2102
     * @param mixed ...$args Data values
2103
     *
2104
     * @return float
2105
     */
2106 5
    public static function MAX(...$args)
2107
    {
2108 5
        $returnValue = null;
2109
2110
        // Loop through arguments
2111 5
        $aArgs = Functions::flattenArray($args);
2112 5
        foreach ($aArgs as $arg) {
2113
            // Is it a numeric value?
2114 5
            if ((is_numeric($arg)) && (!is_string($arg))) {
2115 5
                if (($returnValue === null) || ($arg > $returnValue)) {
2116 5
                    $returnValue = $arg;
2117
                }
2118
            }
2119
        }
2120
2121 5
        if ($returnValue === null) {
2122
            return 0;
2123
        }
2124
2125 5
        return $returnValue;
2126
    }
2127
2128
    /**
2129
     * MAXA.
2130
     *
2131
     * Returns the greatest value in a list of arguments, including numbers, text, and logical values
2132
     *
2133
     * Excel Function:
2134
     *        MAXA(value1[,value2[, ...]])
2135
     *
2136
     * @category Statistical Functions
2137
     *
2138
     * @param mixed ...$args Data values
2139
     *
2140
     * @return float
2141
     */
2142 1
    public static function MAXA(...$args)
2143
    {
2144 1
        $returnValue = null;
2145
2146
        // Loop through arguments
2147 1
        $aArgs = Functions::flattenArray($args);
2148 1
        foreach ($aArgs as $arg) {
2149
            // Is it a numeric value?
2150 1
            if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
2151 1
                if (is_bool($arg)) {
2152 1
                    $arg = (int) $arg;
2153 1
                } elseif (is_string($arg)) {
2154 1
                    $arg = 0;
2155
                }
2156 1
                if (($returnValue === null) || ($arg > $returnValue)) {
2157 1
                    $returnValue = $arg;
2158
                }
2159
            }
2160
        }
2161
2162 1
        if ($returnValue === null) {
2163
            return 0;
2164
        }
2165
2166 1
        return $returnValue;
2167
    }
2168
2169
    /**
2170
     * MAXIFS.
2171
     *
2172
     * Counts the maximum value within a range of cells that contain numbers within the list of arguments
2173
     *
2174
     * Excel Function:
2175
     *        MAXIFS(max_range, criteria_range1, criteria1, [criteria_range2, criteria2], ...)
2176
     *
2177
     * @category Statistical Functions
2178
     *
2179
     * @param mixed $args Data range and criterias
2180
     *
2181
     * @return float
2182
     */
2183 2
    public static function MAXIFS(...$args)
2184
    {
2185 2
        $arrayList = $args;
2186
2187
        // Return value
2188 2
        $returnValue = null;
2189
2190 2
        $maxArgs = Functions::flattenArray(array_shift($arrayList));
2191 2
        $aArgsArray = [];
2192 2
        $conditions = [];
2193
2194 2
        while (count($arrayList) > 0) {
2195 2
            $aArgsArray[] = Functions::flattenArray(array_shift($arrayList));
2196 2
            $conditions[] = Functions::ifCondition(array_shift($arrayList));
2197
        }
2198
2199
        // Loop through each arg and see if arguments and conditions are true
2200 2
        foreach ($maxArgs as $index => $value) {
2201 2
            $valid = true;
2202
2203 2
            foreach ($conditions as $cidx => $condition) {
2204 2
                $arg = $aArgsArray[$cidx][$index];
2205
2206
                // Loop through arguments
2207 2
                if (!is_numeric($arg)) {
2208 2
                    $arg = Calculation::wrapResult(strtoupper($arg));
2209
                }
2210 2
                $testCondition = '=' . $arg . $condition;
2211 2
                if (!Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
2212
                    // Is not a value within our criteria
2213 2
                    $valid = false;
2214
2215 2
                    break; // if false found, don't need to check other conditions
2216
                }
2217
            }
2218
2219 2
            if ($valid) {
2220 2
                $returnValue = $returnValue === null ? $value : max($value, $returnValue);
2221
            }
2222
        }
2223
2224
        // Return
2225 2
        return $returnValue;
2226
    }
2227
2228
    /**
2229
     * MEDIAN.
2230
     *
2231
     * Returns the median of the given numbers. The median is the number in the middle of a set of numbers.
2232
     *
2233
     * Excel Function:
2234
     *        MEDIAN(value1[,value2[, ...]])
2235
     *
2236
     * @category Statistical Functions
2237
     *
2238
     * @param mixed ...$args Data values
2239
     *
2240
     * @return float
2241
     */
2242 1
    public static function MEDIAN(...$args)
2243
    {
2244 1
        $returnValue = Functions::NAN();
2245
2246 1
        $mArgs = [];
2247
        // Loop through arguments
2248 1
        $aArgs = Functions::flattenArray($args);
2249 1
        foreach ($aArgs as $arg) {
2250
            // Is it a numeric value?
2251 1
            if ((is_numeric($arg)) && (!is_string($arg))) {
2252 1
                $mArgs[] = $arg;
2253
            }
2254
        }
2255
2256 1
        $mValueCount = count($mArgs);
2257 1
        if ($mValueCount > 0) {
2258 1
            sort($mArgs, SORT_NUMERIC);
2259 1
            $mValueCount = $mValueCount / 2;
2260 1
            if ($mValueCount == floor($mValueCount)) {
2261 1
                $returnValue = ($mArgs[$mValueCount--] + $mArgs[$mValueCount]) / 2;
2262
            } else {
2263 1
                $mValueCount = floor($mValueCount);
2264 1
                $returnValue = $mArgs[$mValueCount];
2265
            }
2266
        }
2267
2268 1
        return $returnValue;
2269
    }
2270
2271
    /**
2272
     * MIN.
2273
     *
2274
     * MIN returns the value of the element of the values passed that has the smallest value,
2275
     *        with negative numbers considered smaller than positive numbers.
2276
     *
2277
     * Excel Function:
2278
     *        MIN(value1[,value2[, ...]])
2279
     *
2280
     * @category Statistical Functions
2281
     *
2282
     * @param mixed ...$args Data values
2283
     *
2284
     * @return float
2285
     */
2286 5
    public static function MIN(...$args)
2287
    {
2288 5
        $returnValue = null;
2289
2290
        // Loop through arguments
2291 5
        $aArgs = Functions::flattenArray($args);
2292 5
        foreach ($aArgs as $arg) {
2293
            // Is it a numeric value?
2294 5
            if ((is_numeric($arg)) && (!is_string($arg))) {
2295 5
                if (($returnValue === null) || ($arg < $returnValue)) {
2296 5
                    $returnValue = $arg;
2297
                }
2298
            }
2299
        }
2300
2301 5
        if ($returnValue === null) {
2302
            return 0;
2303
        }
2304
2305 5
        return $returnValue;
2306
    }
2307
2308
    /**
2309
     * MINA.
2310
     *
2311
     * Returns the smallest value in a list of arguments, including numbers, text, and logical values
2312
     *
2313
     * Excel Function:
2314
     *        MINA(value1[,value2[, ...]])
2315
     *
2316
     * @category Statistical Functions
2317
     *
2318
     * @param mixed ...$args Data values
2319
     *
2320
     * @return float
2321
     */
2322 1
    public static function MINA(...$args)
2323
    {
2324 1
        $returnValue = null;
2325
2326
        // Loop through arguments
2327 1
        $aArgs = Functions::flattenArray($args);
2328 1
        foreach ($aArgs as $arg) {
2329
            // Is it a numeric value?
2330 1
            if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
2331 1
                if (is_bool($arg)) {
2332 1
                    $arg = (int) $arg;
2333 1
                } elseif (is_string($arg)) {
2334 1
                    $arg = 0;
2335
                }
2336 1
                if (($returnValue === null) || ($arg < $returnValue)) {
2337 1
                    $returnValue = $arg;
2338
                }
2339
            }
2340
        }
2341
2342 1
        if ($returnValue === null) {
2343
            return 0;
2344
        }
2345
2346 1
        return $returnValue;
2347
    }
2348
2349
    /**
2350
     * MINIFS.
2351
     *
2352
     * Returns the minimum value within a range of cells that contain numbers within the list of arguments
2353
     *
2354
     * Excel Function:
2355
     *        MINIFS(min_range, criteria_range1, criteria1, [criteria_range2, criteria2], ...)
2356
     *
2357
     * @category Statistical Functions
2358
     *
2359
     * @param mixed $args Data range and criterias
2360
     *
2361
     * @return float
2362
     */
2363 2
    public static function MINIFS(...$args)
2364
    {
2365 2
        $arrayList = $args;
2366
2367
        // Return value
2368 2
        $returnValue = null;
2369
2370 2
        $minArgs = Functions::flattenArray(array_shift($arrayList));
2371 2
        $aArgsArray = [];
2372 2
        $conditions = [];
2373
2374 2
        while (count($arrayList) > 0) {
2375 2
            $aArgsArray[] = Functions::flattenArray(array_shift($arrayList));
2376 2
            $conditions[] = Functions::ifCondition(array_shift($arrayList));
2377
        }
2378
2379
        // Loop through each arg and see if arguments and conditions are true
2380 2
        foreach ($minArgs as $index => $value) {
2381 2
            $valid = true;
2382
2383 2
            foreach ($conditions as $cidx => $condition) {
2384 2
                $arg = $aArgsArray[$cidx][$index];
2385
2386
                // Loop through arguments
2387 2
                if (!is_numeric($arg)) {
2388 2
                    $arg = Calculation::wrapResult(strtoupper($arg));
2389
                }
2390 2
                $testCondition = '=' . $arg . $condition;
2391 2
                if (!Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
2392
                    // Is not a value within our criteria
2393 2
                    $valid = false;
2394
2395 2
                    break; // if false found, don't need to check other conditions
2396
                }
2397
            }
2398
2399 2
            if ($valid) {
2400 2
                $returnValue = $returnValue === null ? $value : min($value, $returnValue);
2401
            }
2402
        }
2403
2404
        // Return
2405 2
        return $returnValue;
2406
    }
2407
2408
    //
2409
    //    Special variant of array_count_values that isn't limited to strings and integers,
2410
    //        but can work with floating point numbers as values
2411
    //
2412 1
    private static function modeCalc($data)
2413
    {
2414 1
        $frequencyArray = [];
2415 1
        foreach ($data as $datum) {
2416 1
            $found = false;
2417 1
            foreach ($frequencyArray as $key => $value) {
2418 1
                if ((string) $value['value'] == (string) $datum) {
2419 1
                    ++$frequencyArray[$key]['frequency'];
2420 1
                    $found = true;
2421
2422 1
                    break;
2423
                }
2424
            }
2425 1
            if (!$found) {
2426 1
                $frequencyArray[] = [
2427 1
                    'value' => $datum,
2428 1
                    'frequency' => 1,
2429
                ];
2430
            }
2431
        }
2432
2433 1
        foreach ($frequencyArray as $key => $value) {
2434 1
            $frequencyList[$key] = $value['frequency'];
2435 1
            $valueList[$key] = $value['value'];
2436
        }
2437 1
        array_multisort($frequencyList, SORT_DESC, $valueList, SORT_ASC, SORT_NUMERIC, $frequencyArray);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $valueList seems to be defined by a foreach iteration on line 2433. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
2438
2439 1
        if ($frequencyArray[0]['frequency'] == 1) {
2440 1
            return Functions::NA();
2441
        }
2442
2443 1
        return $frequencyArray[0]['value'];
2444
    }
2445
2446
    /**
2447
     * MODE.
2448
     *
2449
     * Returns the most frequently occurring, or repetitive, value in an array or range of data
2450
     *
2451
     * Excel Function:
2452
     *        MODE(value1[,value2[, ...]])
2453
     *
2454
     * @category Statistical Functions
2455
     *
2456
     * @param mixed ...$args Data values
2457
     *
2458
     * @return float
2459
     */
2460 1
    public static function MODE(...$args)
2461
    {
2462 1
        $returnValue = Functions::NA();
2463
2464
        // Loop through arguments
2465 1
        $aArgs = Functions::flattenArray($args);
2466
2467 1
        $mArgs = [];
2468 1
        foreach ($aArgs as $arg) {
2469
            // Is it a numeric value?
2470 1
            if ((is_numeric($arg)) && (!is_string($arg))) {
2471 1
                $mArgs[] = $arg;
2472
            }
2473
        }
2474
2475 1
        if (!empty($mArgs)) {
2476 1
            return self::modeCalc($mArgs);
0 ignored issues
show
Bug Best Practice introduced by
The expression return self::modeCalc($mArgs) also could return the type string which is incompatible with the documented return type double.
Loading history...
2477
        }
2478
2479
        return $returnValue;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $returnValue returns the type string which is incompatible with the documented return type double.
Loading history...
2480
    }
2481
2482
    /**
2483
     * NEGBINOMDIST.
2484
     *
2485
     * Returns the negative binomial distribution. NEGBINOMDIST returns the probability that
2486
     *        there will be number_f failures before the number_s-th success, when the constant
2487
     *        probability of a success is probability_s. This function is similar to the binomial
2488
     *        distribution, except that the number of successes is fixed, and the number of trials is
2489
     *        variable. Like the binomial, trials are assumed to be independent.
2490
     *
2491
     * @param float $failures Number of Failures
2492
     * @param float $successes Threshold number of Successes
2493
     * @param float $probability Probability of success on each trial
2494
     *
2495
     * @return float
2496
     */
2497
    public static function NEGBINOMDIST($failures, $successes, $probability)
2498
    {
2499
        $failures = floor(Functions::flattenSingleValue($failures));
2500
        $successes = floor(Functions::flattenSingleValue($successes));
2501
        $probability = Functions::flattenSingleValue($probability);
2502
2503
        if ((is_numeric($failures)) && (is_numeric($successes)) && (is_numeric($probability))) {
0 ignored issues
show
introduced by
The condition is_numeric($successes) is always true.
Loading history...
2504
            if (($failures < 0) || ($successes < 1)) {
2505
                return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type double.
Loading history...
2506
            } elseif (($probability < 0) || ($probability > 1)) {
2507
                return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type double.
Loading history...
2508
            }
2509
            if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
2510
                if (($failures + $successes - 1) <= 0) {
2511
                    return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type double.
Loading history...
2512
                }
2513
            }
2514
2515
            return (MathTrig::COMBIN($failures + $successes - 1, $successes - 1)) * (pow($probability, $successes)) * (pow(1 - $probability, $failures));
0 ignored issues
show
Bug introduced by
$successes - 1 of type double is incompatible with the type integer expected by parameter $numInSet of PhpOffice\PhpSpreadsheet...tion\MathTrig::COMBIN(). ( Ignorable by Annotation )

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

2515
            return (MathTrig::COMBIN($failures + $successes - 1, /** @scrutinizer ignore-type */ $successes - 1)) * (pow($probability, $successes)) * (pow(1 - $probability, $failures));
Loading history...
Bug introduced by
$failures + $successes - 1 of type double is incompatible with the type integer expected by parameter $numObjs of PhpOffice\PhpSpreadsheet...tion\MathTrig::COMBIN(). ( Ignorable by Annotation )

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

2515
            return (MathTrig::COMBIN(/** @scrutinizer ignore-type */ $failures + $successes - 1, $successes - 1)) * (pow($probability, $successes)) * (pow(1 - $probability, $failures));
Loading history...
2516
        }
2517
2518
        return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type double.
Loading history...
2519
    }
2520
2521
    /**
2522
     * NORMDIST.
2523
     *
2524
     * Returns the normal distribution for the specified mean and standard deviation. This
2525
     * function has a very wide range of applications in statistics, including hypothesis
2526
     * testing.
2527
     *
2528
     * @param float $value
2529
     * @param float $mean Mean Value
2530
     * @param float $stdDev Standard Deviation
2531
     * @param bool $cumulative
2532
     *
2533
     * @return float
2534
     */
2535
    public static function NORMDIST($value, $mean, $stdDev, $cumulative)
2536
    {
2537
        $value = Functions::flattenSingleValue($value);
2538
        $mean = Functions::flattenSingleValue($mean);
2539
        $stdDev = Functions::flattenSingleValue($stdDev);
2540
2541
        if ((is_numeric($value)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
2542
            if ($stdDev < 0) {
2543
                return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type double.
Loading history...
2544
            }
2545
            if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
0 ignored issues
show
introduced by
The condition is_bool($cumulative) is always true.
Loading history...
2546
                if ($cumulative) {
2547
                    return 0.5 * (1 + Engineering::erfVal(($value - $mean) / ($stdDev * sqrt(2))));
2548
                }
2549
2550
                return (1 / (self::SQRT2PI * $stdDev)) * exp(0 - (pow($value - $mean, 2) / (2 * ($stdDev * $stdDev))));
2551
            }
2552
        }
2553
2554
        return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type double.
Loading history...
2555
    }
2556
2557
    /**
2558
     * NORMINV.
2559
     *
2560
     * Returns the inverse of the normal cumulative distribution for the specified mean and standard deviation.
2561
     *
2562
     * @param float $probability
2563
     * @param float $mean Mean Value
2564
     * @param float $stdDev Standard Deviation
2565
     *
2566
     * @return float
2567
     */
2568
    public static function NORMINV($probability, $mean, $stdDev)
2569
    {
2570
        $probability = Functions::flattenSingleValue($probability);
2571
        $mean = Functions::flattenSingleValue($mean);
2572
        $stdDev = Functions::flattenSingleValue($stdDev);
2573
2574
        if ((is_numeric($probability)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
2575
            if (($probability < 0) || ($probability > 1)) {
2576
                return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type double.
Loading history...
2577
            }
2578
            if ($stdDev < 0) {
2579
                return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type double.
Loading history...
2580
            }
2581
2582
            return (self::inverseNcdf($probability) * $stdDev) + $mean;
2583
        }
2584
2585
        return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type double.
Loading history...
2586
    }
2587
2588
    /**
2589
     * NORMSDIST.
2590
     *
2591
     * Returns the standard normal cumulative distribution function. The distribution has
2592
     * a mean of 0 (zero) and a standard deviation of one. Use this function in place of a
2593
     * table of standard normal curve areas.
2594
     *
2595
     * @param float $value
2596
     *
2597
     * @return float
2598
     */
2599
    public static function NORMSDIST($value)
2600
    {
2601
        $value = Functions::flattenSingleValue($value);
2602
2603
        return self::NORMDIST($value, 0, 1, true);
2604
    }
2605
2606
    /**
2607
     * NORMSINV.
2608
     *
2609
     * Returns the inverse of the standard normal cumulative distribution
2610
     *
2611
     * @param float $value
2612
     *
2613
     * @return float
2614
     */
2615
    public static function NORMSINV($value)
2616
    {
2617
        return self::NORMINV($value, 0, 1);
2618
    }
2619
2620
    /**
2621
     * PERCENTILE.
2622
     *
2623
     * Returns the nth percentile of values in a range..
2624
     *
2625
     * Excel Function:
2626
     *        PERCENTILE(value1[,value2[, ...]],entry)
2627
     *
2628
     * @category Statistical Functions
2629
     *
2630
     * @param mixed $args Data values
2631
     * @param float $entry Percentile value in the range 0..1, inclusive.
2632
     *
2633
     * @return float
2634
     */
2635
    public static function PERCENTILE(...$args)
2636
    {
2637
        $aArgs = Functions::flattenArray($args);
2638
2639
        // Calculate
2640
        $entry = array_pop($aArgs);
2641
2642
        if ((is_numeric($entry)) && (!is_string($entry))) {
2643
            if (($entry < 0) || ($entry > 1)) {
2644
                return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type double.
Loading history...
2645
            }
2646
            $mArgs = [];
2647
            foreach ($aArgs as $arg) {
2648
                // Is it a numeric value?
2649
                if ((is_numeric($arg)) && (!is_string($arg))) {
2650
                    $mArgs[] = $arg;
2651
                }
2652
            }
2653
            $mValueCount = count($mArgs);
2654
            if ($mValueCount > 0) {
2655
                sort($mArgs);
2656
                $count = self::COUNT($mArgs);
2657
                $index = $entry * ($count - 1);
2658
                $iBase = floor($index);
2659
                if ($index == $iBase) {
2660
                    return $mArgs[$index];
2661
                }
2662
                $iNext = $iBase + 1;
2663
                $iProportion = $index - $iBase;
2664
2665
                return $mArgs[$iBase] + (($mArgs[$iNext] - $mArgs[$iBase]) * $iProportion);
2666
            }
2667
        }
2668
2669
        return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type double.
Loading history...
2670
    }
2671
2672
    /**
2673
     * PERCENTRANK.
2674
     *
2675
     * Returns the rank of a value in a data set as a percentage of the data set.
2676
     *
2677
     * @param float[] $valueSet An array of, or a reference to, a list of numbers
2678
     * @param int $value the number whose rank you want to find
2679
     * @param int $significance the number of significant digits for the returned percentage value
2680
     *
2681
     * @return float
2682
     */
2683
    public static function PERCENTRANK($valueSet, $value, $significance = 3)
2684
    {
2685
        $valueSet = Functions::flattenArray($valueSet);
2686
        $value = Functions::flattenSingleValue($value);
2687
        $significance = ($significance === null) ? 3 : (int) Functions::flattenSingleValue($significance);
0 ignored issues
show
introduced by
The condition $significance === null is always false.
Loading history...
2688
2689
        foreach ($valueSet as $key => $valueEntry) {
2690
            if (!is_numeric($valueEntry)) {
2691
                unset($valueSet[$key]);
2692
            }
2693
        }
2694
        sort($valueSet, SORT_NUMERIC);
2695
        $valueCount = count($valueSet);
2696
        if ($valueCount == 0) {
2697
            return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type double.
Loading history...
2698
        }
2699
2700
        $valueAdjustor = $valueCount - 1;
2701
        if (($value < $valueSet[0]) || ($value > $valueSet[$valueAdjustor])) {
2702
            return Functions::NA();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...ulation\Functions::NA() returns the type string which is incompatible with the documented return type double.
Loading history...
2703
        }
2704
2705
        $pos = array_search($value, $valueSet);
2706
        if ($pos === false) {
2707
            $pos = 0;
2708
            $testValue = $valueSet[0];
2709
            while ($testValue < $value) {
2710
                $testValue = $valueSet[++$pos];
2711
            }
2712
            --$pos;
2713
            $pos += (($value - $valueSet[$pos]) / ($testValue - $valueSet[$pos]));
2714
        }
2715
2716
        return round($pos / $valueAdjustor, $significance);
2717
    }
2718
2719
    /**
2720
     * PERMUT.
2721
     *
2722
     * Returns the number of permutations for a given number of objects that can be
2723
     *        selected from number objects. A permutation is any set or subset of objects or
2724
     *        events where internal order is significant. Permutations are different from
2725
     *        combinations, for which the internal order is not significant. Use this function
2726
     *        for lottery-style probability calculations.
2727
     *
2728
     * @param int $numObjs Number of different objects
2729
     * @param int $numInSet Number of objects in each permutation
2730
     *
2731
     * @return int Number of permutations
2732
     */
2733
    public static function PERMUT($numObjs, $numInSet)
2734
    {
2735
        $numObjs = Functions::flattenSingleValue($numObjs);
2736
        $numInSet = Functions::flattenSingleValue($numInSet);
2737
2738
        if ((is_numeric($numObjs)) && (is_numeric($numInSet))) {
2739
            $numInSet = floor($numInSet);
2740
            if ($numObjs < $numInSet) {
2741
                return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type integer.
Loading history...
2742
            }
2743
2744
            return round(MathTrig::FACT($numObjs) / MathTrig::FACT($numObjs - $numInSet));
2745
        }
2746
2747
        return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type integer.
Loading history...
2748
    }
2749
2750
    /**
2751
     * POISSON.
2752
     *
2753
     * Returns the Poisson distribution. A common application of the Poisson distribution
2754
     * is predicting the number of events over a specific time, such as the number of
2755
     * cars arriving at a toll plaza in 1 minute.
2756
     *
2757
     * @param float $value
2758
     * @param float $mean Mean Value
2759
     * @param bool $cumulative
2760
     *
2761
     * @return float
2762
     */
2763
    public static function POISSON($value, $mean, $cumulative)
2764
    {
2765
        $value = Functions::flattenSingleValue($value);
2766
        $mean = Functions::flattenSingleValue($mean);
2767
2768
        if ((is_numeric($value)) && (is_numeric($mean))) {
2769
            if (($value < 0) || ($mean <= 0)) {
2770
                return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type double.
Loading history...
2771
            }
2772
            if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
0 ignored issues
show
introduced by
The condition is_bool($cumulative) is always true.
Loading history...
2773
                if ($cumulative) {
2774
                    $summer = 0;
2775
                    $floor = floor($value);
2776
                    for ($i = 0; $i <= $floor; ++$i) {
2777
                        $summer += pow($mean, $i) / MathTrig::FACT($i);
2778
                    }
2779
2780
                    return exp(0 - $mean) * $summer;
2781
                }
2782
2783
                return (exp(0 - $mean) * pow($mean, $value)) / MathTrig::FACT($value);
2784
            }
2785
        }
2786
2787
        return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type double.
Loading history...
2788
    }
2789
2790
    /**
2791
     * QUARTILE.
2792
     *
2793
     * Returns the quartile of a data set.
2794
     *
2795
     * Excel Function:
2796
     *        QUARTILE(value1[,value2[, ...]],entry)
2797
     *
2798
     * @category Statistical Functions
2799
     *
2800
     * @param mixed $args Data values
2801
     * @param int $entry Quartile value in the range 1..3, inclusive.
2802
     *
2803
     * @return float
2804
     */
2805
    public static function QUARTILE(...$args)
2806
    {
2807
        $aArgs = Functions::flattenArray($args);
2808
2809
        // Calculate
2810
        $entry = floor(array_pop($aArgs));
2811
2812
        if ((is_numeric($entry)) && (!is_string($entry))) {
0 ignored issues
show
introduced by
The condition is_string($entry) is always false.
Loading history...
2813
            $entry /= 4;
2814
            if (($entry < 0) || ($entry > 1)) {
2815
                return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type double.
Loading history...
2816
            }
2817
2818
            return self::PERCENTILE($aArgs, $entry);
2819
        }
2820
2821
        return Functions::VALUE();
2822
    }
2823
2824
    /**
2825
     * RANK.
2826
     *
2827
     * Returns the rank of a number in a list of numbers.
2828
     *
2829
     * @param int $value the number whose rank you want to find
2830
     * @param float[] $valueSet An array of, or a reference to, a list of numbers
2831
     * @param int $order Order to sort the values in the value set
2832
     *
2833
     * @return float
2834
     */
2835
    public static function RANK($value, $valueSet, $order = 0)
2836
    {
2837
        $value = Functions::flattenSingleValue($value);
2838
        $valueSet = Functions::flattenArray($valueSet);
2839
        $order = ($order === null) ? 0 : (int) Functions::flattenSingleValue($order);
0 ignored issues
show
introduced by
The condition $order === null is always false.
Loading history...
2840
2841
        foreach ($valueSet as $key => $valueEntry) {
2842
            if (!is_numeric($valueEntry)) {
2843
                unset($valueSet[$key]);
2844
            }
2845
        }
2846
2847
        if ($order == 0) {
2848
            rsort($valueSet, SORT_NUMERIC);
2849
        } else {
2850
            sort($valueSet, SORT_NUMERIC);
2851
        }
2852
        $pos = array_search($value, $valueSet);
2853
        if ($pos === false) {
2854
            return Functions::NA();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...ulation\Functions::NA() returns the type string which is incompatible with the documented return type double.
Loading history...
2855
        }
2856
2857
        return ++$pos;
2858
    }
2859
2860
    /**
2861
     * RSQ.
2862
     *
2863
     * Returns the square of the Pearson product moment correlation coefficient through data points in known_y's and known_x's.
2864
     *
2865
     * @param mixed[] $yValues Data Series Y
2866
     * @param mixed[] $xValues Data Series X
2867
     *
2868
     * @return float
2869
     */
2870
    public static function RSQ($yValues, $xValues)
2871
    {
2872
        if (!self::checkTrendArrays($yValues, $xValues)) {
2873
            return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type double.
Loading history...
2874
        }
2875
        $yValueCount = count($yValues);
2876
        $xValueCount = count($xValues);
2877
2878
        if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
2879
            return Functions::NA();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...ulation\Functions::NA() returns the type string which is incompatible with the documented return type double.
Loading history...
2880
        } elseif ($yValueCount == 1) {
2881
            return Functions::DIV0();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...ation\Functions::DIV0() returns the type string which is incompatible with the documented return type double.
Loading history...
2882
        }
2883
2884
        $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
2885
2886
        return $bestFitLinear->getGoodnessOfFit();
2887
    }
2888
2889
    /**
2890
     * SKEW.
2891
     *
2892
     * Returns the skewness of a distribution. Skewness characterizes the degree of asymmetry
2893
     * of a distribution around its mean. Positive skewness indicates a distribution with an
2894
     * asymmetric tail extending toward more positive values. Negative skewness indicates a
2895
     * distribution with an asymmetric tail extending toward more negative values.
2896
     *
2897
     * @param array ...$args Data Series
2898
     *
2899
     * @return float
2900
     */
2901
    public static function SKEW(...$args)
2902
    {
2903
        $aArgs = Functions::flattenArrayIndexed($args);
2904
        $mean = self::AVERAGE($aArgs);
2905
        $stdDev = self::STDEV($aArgs);
2906
2907
        $count = $summer = 0;
2908
        // Loop through arguments
2909
        foreach ($aArgs as $k => $arg) {
2910
            if ((is_bool($arg)) &&
2911
                (!Functions::isMatrixValue($k))) {
2912
            } else {
2913
                // Is it a numeric value?
2914
                if ((is_numeric($arg)) && (!is_string($arg))) {
2915
                    $summer += pow((($arg - $mean) / $stdDev), 3);
2916
                    ++$count;
2917
                }
2918
            }
2919
        }
2920
2921
        if ($count > 2) {
2922
            return $summer * ($count / (($count - 1) * ($count - 2)));
2923
        }
2924
2925
        return Functions::DIV0();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...ation\Functions::DIV0() returns the type string which is incompatible with the documented return type double.
Loading history...
2926
    }
2927
2928
    /**
2929
     * SLOPE.
2930
     *
2931
     * Returns the slope of the linear regression line through data points in known_y's and known_x's.
2932
     *
2933
     * @param mixed[] $yValues Data Series Y
2934
     * @param mixed[] $xValues Data Series X
2935
     *
2936
     * @return float
2937
     */
2938
    public static function SLOPE($yValues, $xValues)
2939
    {
2940
        if (!self::checkTrendArrays($yValues, $xValues)) {
2941
            return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type double.
Loading history...
2942
        }
2943
        $yValueCount = count($yValues);
2944
        $xValueCount = count($xValues);
2945
2946
        if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
2947
            return Functions::NA();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...ulation\Functions::NA() returns the type string which is incompatible with the documented return type double.
Loading history...
2948
        } elseif ($yValueCount == 1) {
2949
            return Functions::DIV0();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...ation\Functions::DIV0() returns the type string which is incompatible with the documented return type double.
Loading history...
2950
        }
2951
2952
        $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
2953
2954
        return $bestFitLinear->getSlope();
2955
    }
2956
2957
    /**
2958
     * SMALL.
2959
     *
2960
     * Returns the nth smallest value in a data set. You can use this function to
2961
     *        select a value based on its relative standing.
2962
     *
2963
     * Excel Function:
2964
     *        SMALL(value1[,value2[, ...]],entry)
2965
     *
2966
     * @category Statistical Functions
2967
     *
2968
     * @param mixed $args Data values
2969
     * @param int $entry Position (ordered from the smallest) in the array or range of data to return
2970
     *
2971
     * @return float
2972
     */
2973
    public static function SMALL(...$args)
2974
    {
2975
        $aArgs = Functions::flattenArray($args);
2976
2977
        // Calculate
2978
        $entry = array_pop($aArgs);
2979
2980
        if ((is_numeric($entry)) && (!is_string($entry))) {
2981
            $mArgs = [];
2982
            foreach ($aArgs as $arg) {
2983
                // Is it a numeric value?
2984
                if ((is_numeric($arg)) && (!is_string($arg))) {
2985
                    $mArgs[] = $arg;
2986
                }
2987
            }
2988
            $count = self::COUNT($mArgs);
2989
            $entry = floor(--$entry);
2990
            if (($entry < 0) || ($entry >= $count) || ($count == 0)) {
2991
                return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type double.
Loading history...
2992
            }
2993
            sort($mArgs);
2994
2995
            return $mArgs[$entry];
2996
        }
2997
2998
        return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type double.
Loading history...
2999
    }
3000
3001
    /**
3002
     * STANDARDIZE.
3003
     *
3004
     * Returns a normalized value from a distribution characterized by mean and standard_dev.
3005
     *
3006
     * @param float $value Value to normalize
3007
     * @param float $mean Mean Value
3008
     * @param float $stdDev Standard Deviation
3009
     *
3010
     * @return float Standardized value
3011
     */
3012
    public static function STANDARDIZE($value, $mean, $stdDev)
3013
    {
3014
        $value = Functions::flattenSingleValue($value);
3015
        $mean = Functions::flattenSingleValue($mean);
3016
        $stdDev = Functions::flattenSingleValue($stdDev);
3017
3018
        if ((is_numeric($value)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
3019
            if ($stdDev <= 0) {
3020
                return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type double.
Loading history...
3021
            }
3022
3023
            return ($value - $mean) / $stdDev;
3024
        }
3025
3026
        return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type double.
Loading history...
3027
    }
3028
3029
    /**
3030
     * STDEV.
3031
     *
3032
     * Estimates standard deviation based on a sample. The standard deviation is a measure of how
3033
     *        widely values are dispersed from the average value (the mean).
3034
     *
3035
     * Excel Function:
3036
     *        STDEV(value1[,value2[, ...]])
3037
     *
3038
     * @category Statistical Functions
3039
     *
3040
     * @param mixed ...$args Data values
3041
     *
3042
     * @return float
3043
     */
3044 4
    public static function STDEV(...$args)
3045
    {
3046 4
        $aArgs = Functions::flattenArrayIndexed($args);
3047
3048
        // Return value
3049 4
        $returnValue = null;
3050
3051 4
        $aMean = self::AVERAGE($aArgs);
3052 4
        if ($aMean !== null) {
0 ignored issues
show
introduced by
The condition $aMean !== null is always true.
Loading history...
3053 4
            $aCount = -1;
3054 4
            foreach ($aArgs as $k => $arg) {
3055 4
                if ((is_bool($arg)) &&
3056 4
                    ((!Functions::isCellValue($k)) || (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE))) {
3057
                    $arg = (int) $arg;
3058
                }
3059
                // Is it a numeric value?
3060 4
                if ((is_numeric($arg)) && (!is_string($arg))) {
3061 4
                    if ($returnValue === null) {
3062 4
                        $returnValue = pow(($arg - $aMean), 2);
3063
                    } else {
3064 4
                        $returnValue += pow(($arg - $aMean), 2);
3065
                    }
3066 4
                    ++$aCount;
3067
                }
3068
            }
3069
3070
            // Return
3071 4
            if (($aCount > 0) && ($returnValue >= 0)) {
3072 4
                return sqrt($returnValue / $aCount);
3073
            }
3074
        }
3075
3076
        return Functions::DIV0();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...ation\Functions::DIV0() returns the type string which is incompatible with the documented return type double.
Loading history...
3077
    }
3078
3079
    /**
3080
     * STDEVA.
3081
     *
3082
     * Estimates standard deviation based on a sample, including numbers, text, and logical values
3083
     *
3084
     * Excel Function:
3085
     *        STDEVA(value1[,value2[, ...]])
3086
     *
3087
     * @category Statistical Functions
3088
     *
3089
     * @param mixed ...$args Data values
3090
     *
3091
     * @return float
3092
     */
3093 1
    public static function STDEVA(...$args)
3094
    {
3095 1
        $aArgs = Functions::flattenArrayIndexed($args);
3096
3097 1
        $returnValue = null;
3098
3099 1
        $aMean = self::AVERAGEA($aArgs);
3100 1
        if ($aMean !== null) {
0 ignored issues
show
introduced by
The condition $aMean !== null is always true.
Loading history...
3101 1
            $aCount = -1;
3102 1
            foreach ($aArgs as $k => $arg) {
3103 1
                if ((is_bool($arg)) &&
3104 1
                    (!Functions::isMatrixValue($k))) {
3105
                } else {
3106
                    // Is it a numeric value?
3107 1
                    if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) & ($arg != '')))) {
0 ignored issues
show
Bug introduced by
Are you sure you want to use the bitwise & or did you mean &&?
Loading history...
3108 1
                        if (is_bool($arg)) {
3109 1
                            $arg = (int) $arg;
3110 1
                        } elseif (is_string($arg)) {
3111 1
                            $arg = 0;
3112
                        }
3113 1
                        if ($returnValue === null) {
3114 1
                            $returnValue = pow(($arg - $aMean), 2);
3115
                        } else {
3116 1
                            $returnValue += pow(($arg - $aMean), 2);
3117
                        }
3118 1
                        ++$aCount;
3119
                    }
3120
                }
3121
            }
3122
3123 1
            if (($aCount > 0) && ($returnValue >= 0)) {
3124 1
                return sqrt($returnValue / $aCount);
3125
            }
3126
        }
3127
3128
        return Functions::DIV0();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...ation\Functions::DIV0() returns the type string which is incompatible with the documented return type double.
Loading history...
3129
    }
3130
3131
    /**
3132
     * STDEVP.
3133
     *
3134
     * Calculates standard deviation based on the entire population
3135
     *
3136
     * Excel Function:
3137
     *        STDEVP(value1[,value2[, ...]])
3138
     *
3139
     * @category Statistical Functions
3140
     *
3141
     * @param mixed ...$args Data values
3142
     *
3143
     * @return float
3144
     */
3145 4
    public static function STDEVP(...$args)
3146
    {
3147 4
        $aArgs = Functions::flattenArrayIndexed($args);
3148
3149 4
        $returnValue = null;
3150
3151 4
        $aMean = self::AVERAGE($aArgs);
3152 4
        if ($aMean !== null) {
0 ignored issues
show
introduced by
The condition $aMean !== null is always true.
Loading history...
3153 4
            $aCount = 0;
3154 4
            foreach ($aArgs as $k => $arg) {
3155 4
                if ((is_bool($arg)) &&
3156 4
                    ((!Functions::isCellValue($k)) || (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE))) {
3157
                    $arg = (int) $arg;
3158
                }
3159
                // Is it a numeric value?
3160 4
                if ((is_numeric($arg)) && (!is_string($arg))) {
3161 4
                    if ($returnValue === null) {
3162 4
                        $returnValue = pow(($arg - $aMean), 2);
3163
                    } else {
3164 4
                        $returnValue += pow(($arg - $aMean), 2);
3165
                    }
3166 4
                    ++$aCount;
3167
                }
3168
            }
3169
3170 4
            if (($aCount > 0) && ($returnValue >= 0)) {
3171 4
                return sqrt($returnValue / $aCount);
3172
            }
3173
        }
3174
3175
        return Functions::DIV0();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...ation\Functions::DIV0() returns the type string which is incompatible with the documented return type double.
Loading history...
3176
    }
3177
3178
    /**
3179
     * STDEVPA.
3180
     *
3181
     * Calculates standard deviation based on the entire population, including numbers, text, and logical values
3182
     *
3183
     * Excel Function:
3184
     *        STDEVPA(value1[,value2[, ...]])
3185
     *
3186
     * @category Statistical Functions
3187
     *
3188
     * @param mixed ...$args Data values
3189
     *
3190
     * @return float
3191
     */
3192 1
    public static function STDEVPA(...$args)
3193
    {
3194 1
        $aArgs = Functions::flattenArrayIndexed($args);
3195
3196 1
        $returnValue = null;
3197
3198 1
        $aMean = self::AVERAGEA($aArgs);
3199 1
        if ($aMean !== null) {
0 ignored issues
show
introduced by
The condition $aMean !== null is always true.
Loading history...
3200 1
            $aCount = 0;
3201 1
            foreach ($aArgs as $k => $arg) {
3202 1
                if ((is_bool($arg)) &&
3203 1
                    (!Functions::isMatrixValue($k))) {
3204
                } else {
3205
                    // Is it a numeric value?
3206 1
                    if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) & ($arg != '')))) {
0 ignored issues
show
Bug introduced by
Are you sure you want to use the bitwise & or did you mean &&?
Loading history...
3207 1
                        if (is_bool($arg)) {
3208 1
                            $arg = (int) $arg;
3209 1
                        } elseif (is_string($arg)) {
3210 1
                            $arg = 0;
3211
                        }
3212 1
                        if ($returnValue === null) {
3213 1
                            $returnValue = pow(($arg - $aMean), 2);
3214
                        } else {
3215 1
                            $returnValue += pow(($arg - $aMean), 2);
3216
                        }
3217 1
                        ++$aCount;
3218
                    }
3219
                }
3220
            }
3221
3222 1
            if (($aCount > 0) && ($returnValue >= 0)) {
3223 1
                return sqrt($returnValue / $aCount);
3224
            }
3225
        }
3226
3227
        return Functions::DIV0();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...ation\Functions::DIV0() returns the type string which is incompatible with the documented return type double.
Loading history...
3228
    }
3229
3230
    /**
3231
     * STEYX.
3232
     *
3233
     * Returns the standard error of the predicted y-value for each x in the regression.
3234
     *
3235
     * @param mixed[] $yValues Data Series Y
3236
     * @param mixed[] $xValues Data Series X
3237
     *
3238
     * @return float
3239
     */
3240
    public static function STEYX($yValues, $xValues)
3241
    {
3242
        if (!self::checkTrendArrays($yValues, $xValues)) {
3243
            return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type double.
Loading history...
3244
        }
3245
        $yValueCount = count($yValues);
3246
        $xValueCount = count($xValues);
3247
3248
        if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
3249
            return Functions::NA();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...ulation\Functions::NA() returns the type string which is incompatible with the documented return type double.
Loading history...
3250
        } elseif ($yValueCount == 1) {
3251
            return Functions::DIV0();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...ation\Functions::DIV0() returns the type string which is incompatible with the documented return type double.
Loading history...
3252
        }
3253
3254
        $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
3255
3256
        return $bestFitLinear->getStdevOfResiduals();
3257
    }
3258
3259
    /**
3260
     * TDIST.
3261
     *
3262
     * Returns the probability of Student's T distribution.
3263
     *
3264
     * @param float $value Value for the function
3265
     * @param float $degrees degrees of freedom
3266
     * @param float $tails number of tails (1 or 2)
3267
     *
3268
     * @return float
3269
     */
3270
    public static function TDIST($value, $degrees, $tails)
3271
    {
3272
        $value = Functions::flattenSingleValue($value);
3273
        $degrees = floor(Functions::flattenSingleValue($degrees));
3274
        $tails = floor(Functions::flattenSingleValue($tails));
3275
3276
        if ((is_numeric($value)) && (is_numeric($degrees)) && (is_numeric($tails))) {
3277
            if (($value < 0) || ($degrees < 1) || ($tails < 1) || ($tails > 2)) {
3278
                return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type double.
Loading history...
3279
            }
3280
            //    tdist, which finds the probability that corresponds to a given value
3281
            //    of t with k degrees of freedom. This algorithm is translated from a
3282
            //    pascal function on p81 of "Statistical Computing in Pascal" by D
3283
            //    Cooke, A H Craven & G M Clark (1985: Edward Arnold (Pubs.) Ltd:
3284
            //    London). The above Pascal algorithm is itself a translation of the
3285
            //    fortran algoritm "AS 3" by B E Cooper of the Atlas Computer
3286
            //    Laboratory as reported in (among other places) "Applied Statistics
3287
            //    Algorithms", editied by P Griffiths and I D Hill (1985; Ellis
3288
            //    Horwood Ltd.; W. Sussex, England).
3289
            $tterm = $degrees;
3290
            $ttheta = atan2($value, sqrt($tterm));
3291
            $tc = cos($ttheta);
3292
            $ts = sin($ttheta);
3293
            $tsum = 0;
0 ignored issues
show
Unused Code introduced by
The assignment to $tsum is dead and can be removed.
Loading history...
3294
3295
            if (($degrees % 2) == 1) {
3296
                $ti = 3;
3297
                $tterm = $tc;
3298
            } else {
3299
                $ti = 2;
3300
                $tterm = 1;
3301
            }
3302
3303
            $tsum = $tterm;
3304
            while ($ti < $degrees) {
3305
                $tterm *= $tc * $tc * ($ti - 1) / $ti;
3306
                $tsum += $tterm;
3307
                $ti += 2;
3308
            }
3309
            $tsum *= $ts;
3310
            if (($degrees % 2) == 1) {
3311
                $tsum = Functions::M_2DIVPI * ($tsum + $ttheta);
3312
            }
3313
            $tValue = 0.5 * (1 + $tsum);
3314
            if ($tails == 1) {
3315
                return 1 - abs($tValue);
3316
            }
3317
3318
            return 1 - abs((1 - $tValue) - $tValue);
3319
        }
3320
3321
        return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type double.
Loading history...
3322
    }
3323
3324
    /**
3325
     * TINV.
3326
     *
3327
     * Returns the one-tailed probability of the chi-squared distribution.
3328
     *
3329
     * @param float $probability Probability for the function
3330
     * @param float $degrees degrees of freedom
3331
     *
3332
     * @return float
3333
     */
3334
    public static function TINV($probability, $degrees)
3335
    {
3336
        $probability = Functions::flattenSingleValue($probability);
3337
        $degrees = floor(Functions::flattenSingleValue($degrees));
3338
3339
        if ((is_numeric($probability)) && (is_numeric($degrees))) {
3340
            $xLo = 100;
3341
            $xHi = 0;
3342
3343
            $x = $xNew = 1;
3344
            $dx = 1;
3345
            $i = 0;
3346
3347
            while ((abs($dx) > Functions::PRECISION) && ($i++ < self::MAX_ITERATIONS)) {
3348
                // Apply Newton-Raphson step
3349
                $result = self::TDIST($x, $degrees, 2);
3350
                $error = $result - $probability;
3351
                if ($error == 0.0) {
3352
                    $dx = 0;
3353
                } elseif ($error < 0.0) {
3354
                    $xLo = $x;
3355
                } else {
3356
                    $xHi = $x;
3357
                }
3358
                // Avoid division by zero
3359
                if ($result != 0.0) {
3360
                    $dx = $error / $result;
3361
                    $xNew = $x - $dx;
3362
                }
3363
                // If the NR fails to converge (which for example may be the
3364
                // case if the initial guess is too rough) we apply a bisection
3365
                // step to determine a more narrow interval around the root.
3366
                if (($xNew < $xLo) || ($xNew > $xHi) || ($result == 0.0)) {
3367
                    $xNew = ($xLo + $xHi) / 2;
3368
                    $dx = $xNew - $x;
3369
                }
3370
                $x = $xNew;
3371
            }
3372
            if ($i == self::MAX_ITERATIONS) {
3373
                return Functions::NA();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...ulation\Functions::NA() returns the type string which is incompatible with the documented return type double.
Loading history...
3374
            }
3375
3376
            return round($x, 12);
3377
        }
3378
3379
        return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type double.
Loading history...
3380
    }
3381
3382
    /**
3383
     * TREND.
3384
     *
3385
     * Returns values along a linear Trend
3386
     *
3387
     * @param mixed[] $yValues Data Series Y
3388
     * @param mixed[] $xValues Data Series X
3389
     * @param mixed[] $newValues Values of X for which we want to find Y
3390
     * @param bool $const a logical value specifying whether to force the intersect to equal 0
3391
     *
3392
     * @return array of float
3393
     */
3394
    public static function TREND($yValues, $xValues = [], $newValues = [], $const = true)
3395
    {
3396
        $yValues = Functions::flattenArray($yValues);
3397
        $xValues = Functions::flattenArray($xValues);
3398
        $newValues = Functions::flattenArray($newValues);
3399
        $const = ($const === null) ? true : (bool) Functions::flattenSingleValue($const);
0 ignored issues
show
introduced by
The condition $const === null is always false.
Loading history...
3400
3401
        $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues, $const);
3402
        if (empty($newValues)) {
3403
            $newValues = $bestFitLinear->getXValues();
3404
        }
3405
3406
        $returnArray = [];
3407
        foreach ($newValues as $xValue) {
3408
            $returnArray[0][] = $bestFitLinear->getValueOfYForX($xValue);
3409
        }
3410
3411
        return $returnArray;
3412
    }
3413
3414
    /**
3415
     * TRIMMEAN.
3416
     *
3417
     * Returns the mean of the interior of a data set. TRIMMEAN calculates the mean
3418
     *        taken by excluding a percentage of data points from the top and bottom tails
3419
     *        of a data set.
3420
     *
3421
     * Excel Function:
3422
     *        TRIMEAN(value1[,value2[, ...]], $discard)
3423
     *
3424
     * @category Statistical Functions
3425
     *
3426
     * @param mixed $args Data values
3427
     * @param float $discard Percentage to discard
3428
     *
3429
     * @return float
3430
     */
3431
    public static function TRIMMEAN(...$args)
3432
    {
3433
        $aArgs = Functions::flattenArray($args);
3434
3435
        // Calculate
3436
        $percent = array_pop($aArgs);
3437
3438
        if ((is_numeric($percent)) && (!is_string($percent))) {
3439
            if (($percent < 0) || ($percent > 1)) {
3440
                return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type double.
Loading history...
3441
            }
3442
            $mArgs = [];
3443
            foreach ($aArgs as $arg) {
3444
                // Is it a numeric value?
3445
                if ((is_numeric($arg)) && (!is_string($arg))) {
3446
                    $mArgs[] = $arg;
3447
                }
3448
            }
3449
            $discard = floor(self::COUNT($mArgs) * $percent / 2);
3450
            sort($mArgs);
3451
            for ($i = 0; $i < $discard; ++$i) {
3452
                array_pop($mArgs);
3453
                array_shift($mArgs);
3454
            }
3455
3456
            return self::AVERAGE($mArgs);
3457
        }
3458
3459
        return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type double.
Loading history...
3460
    }
3461
3462
    /**
3463
     * VARFunc.
3464
     *
3465
     * Estimates variance based on a sample.
3466
     *
3467
     * Excel Function:
3468
     *        VAR(value1[,value2[, ...]])
3469
     *
3470
     * @category Statistical Functions
3471
     *
3472
     * @param mixed ...$args Data values
3473
     *
3474
     * @return float
3475
     */
3476 4
    public static function VARFunc(...$args)
3477
    {
3478 4
        $returnValue = Functions::DIV0();
3479
3480 4
        $summerA = $summerB = 0;
3481
3482
        // Loop through arguments
3483 4
        $aArgs = Functions::flattenArray($args);
3484 4
        $aCount = 0;
3485 4
        foreach ($aArgs as $arg) {
3486 4
            if (is_bool($arg)) {
3487 1
                $arg = (int) $arg;
3488
            }
3489
            // Is it a numeric value?
3490 4
            if ((is_numeric($arg)) && (!is_string($arg))) {
3491 4
                $summerA += ($arg * $arg);
3492 4
                $summerB += $arg;
3493 4
                ++$aCount;
3494
            }
3495
        }
3496
3497 4
        if ($aCount > 1) {
3498 4
            $summerA *= $aCount;
3499 4
            $summerB *= $summerB;
3500 4
            $returnValue = ($summerA - $summerB) / ($aCount * ($aCount - 1));
3501
        }
3502
3503 4
        return $returnValue;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $returnValue also could return the type string which is incompatible with the documented return type double.
Loading history...
3504
    }
3505
3506
    /**
3507
     * VARA.
3508
     *
3509
     * Estimates variance based on a sample, including numbers, text, and logical values
3510
     *
3511
     * Excel Function:
3512
     *        VARA(value1[,value2[, ...]])
3513
     *
3514
     * @category Statistical Functions
3515
     *
3516
     * @param mixed ...$args Data values
3517
     *
3518
     * @return float
3519
     */
3520 1
    public static function VARA(...$args)
3521
    {
3522 1
        $returnValue = Functions::DIV0();
3523
3524 1
        $summerA = $summerB = 0;
3525
3526
        // Loop through arguments
3527 1
        $aArgs = Functions::flattenArrayIndexed($args);
3528 1
        $aCount = 0;
3529 1
        foreach ($aArgs as $k => $arg) {
3530 1
            if ((is_string($arg)) &&
3531 1
                (Functions::isValue($k))) {
3532
                return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type double.
Loading history...
3533 1
            } elseif ((is_string($arg)) &&
3534 1
                (!Functions::isMatrixValue($k))) {
3535
            } else {
3536
                // Is it a numeric value?
3537 1
                if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) & ($arg != '')))) {
0 ignored issues
show
Bug introduced by
Are you sure you want to use the bitwise & or did you mean &&?
Loading history...
3538 1
                    if (is_bool($arg)) {
3539 1
                        $arg = (int) $arg;
3540 1
                    } elseif (is_string($arg)) {
3541 1
                        $arg = 0;
3542
                    }
3543 1
                    $summerA += ($arg * $arg);
3544 1
                    $summerB += $arg;
3545 1
                    ++$aCount;
3546
                }
3547
            }
3548
        }
3549
3550 1
        if ($aCount > 1) {
3551 1
            $summerA *= $aCount;
3552 1
            $summerB *= $summerB;
3553 1
            $returnValue = ($summerA - $summerB) / ($aCount * ($aCount - 1));
3554
        }
3555
3556 1
        return $returnValue;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $returnValue also could return the type string which is incompatible with the documented return type double.
Loading history...
3557
    }
3558
3559
    /**
3560
     * VARP.
3561
     *
3562
     * Calculates variance based on the entire population
3563
     *
3564
     * Excel Function:
3565
     *        VARP(value1[,value2[, ...]])
3566
     *
3567
     * @category Statistical Functions
3568
     *
3569
     * @param mixed ...$args Data values
3570
     *
3571
     * @return float
3572
     */
3573 4
    public static function VARP(...$args)
3574
    {
3575
        // Return value
3576 4
        $returnValue = Functions::DIV0();
3577
3578 4
        $summerA = $summerB = 0;
3579
3580
        // Loop through arguments
3581 4
        $aArgs = Functions::flattenArray($args);
3582 4
        $aCount = 0;
3583 4
        foreach ($aArgs as $arg) {
3584 4
            if (is_bool($arg)) {
3585 1
                $arg = (int) $arg;
3586
            }
3587
            // Is it a numeric value?
3588 4
            if ((is_numeric($arg)) && (!is_string($arg))) {
3589 4
                $summerA += ($arg * $arg);
3590 4
                $summerB += $arg;
3591 4
                ++$aCount;
3592
            }
3593
        }
3594
3595 4
        if ($aCount > 0) {
3596 4
            $summerA *= $aCount;
3597 4
            $summerB *= $summerB;
3598 4
            $returnValue = ($summerA - $summerB) / ($aCount * $aCount);
3599
        }
3600
3601 4
        return $returnValue;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $returnValue also could return the type string which is incompatible with the documented return type double.
Loading history...
3602
    }
3603
3604
    /**
3605
     * VARPA.
3606
     *
3607
     * Calculates variance based on the entire population, including numbers, text, and logical values
3608
     *
3609
     * Excel Function:
3610
     *        VARPA(value1[,value2[, ...]])
3611
     *
3612
     * @category Statistical Functions
3613
     *
3614
     * @param mixed ...$args Data values
3615
     *
3616
     * @return float
3617
     */
3618 1
    public static function VARPA(...$args)
3619
    {
3620 1
        $returnValue = Functions::DIV0();
3621
3622 1
        $summerA = $summerB = 0;
3623
3624
        // Loop through arguments
3625 1
        $aArgs = Functions::flattenArrayIndexed($args);
3626 1
        $aCount = 0;
3627 1
        foreach ($aArgs as $k => $arg) {
3628 1
            if ((is_string($arg)) &&
3629 1
                (Functions::isValue($k))) {
3630
                return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type double.
Loading history...
3631 1
            } elseif ((is_string($arg)) &&
3632 1
                (!Functions::isMatrixValue($k))) {
3633
            } else {
3634
                // Is it a numeric value?
3635 1
                if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) & ($arg != '')))) {
0 ignored issues
show
Bug introduced by
Are you sure you want to use the bitwise & or did you mean &&?
Loading history...
3636 1
                    if (is_bool($arg)) {
3637 1
                        $arg = (int) $arg;
3638 1
                    } elseif (is_string($arg)) {
3639 1
                        $arg = 0;
3640
                    }
3641 1
                    $summerA += ($arg * $arg);
3642 1
                    $summerB += $arg;
3643 1
                    ++$aCount;
3644
                }
3645
            }
3646
        }
3647
3648 1
        if ($aCount > 0) {
3649 1
            $summerA *= $aCount;
3650 1
            $summerB *= $summerB;
3651 1
            $returnValue = ($summerA - $summerB) / ($aCount * $aCount);
3652
        }
3653
3654 1
        return $returnValue;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $returnValue also could return the type string which is incompatible with the documented return type double.
Loading history...
3655
    }
3656
3657
    /**
3658
     * WEIBULL.
3659
     *
3660
     * Returns the Weibull distribution. Use this distribution in reliability
3661
     * analysis, such as calculating a device's mean time to failure.
3662
     *
3663
     * @param float $value
3664
     * @param float $alpha Alpha Parameter
3665
     * @param float $beta Beta Parameter
3666
     * @param bool $cumulative
3667
     *
3668
     * @return float
3669
     */
3670
    public static function WEIBULL($value, $alpha, $beta, $cumulative)
3671
    {
3672
        $value = Functions::flattenSingleValue($value);
3673
        $alpha = Functions::flattenSingleValue($alpha);
3674
        $beta = Functions::flattenSingleValue($beta);
3675
3676
        if ((is_numeric($value)) && (is_numeric($alpha)) && (is_numeric($beta))) {
3677
            if (($value < 0) || ($alpha <= 0) || ($beta <= 0)) {
3678
                return Functions::NAN();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...lation\Functions::NAN() returns the type string which is incompatible with the documented return type double.
Loading history...
3679
            }
3680
            if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
0 ignored issues
show
introduced by
The condition is_bool($cumulative) is always true.
Loading history...
3681
                if ($cumulative) {
3682
                    return 1 - exp(0 - pow($value / $beta, $alpha));
3683
                }
3684
3685
                return ($alpha / pow($beta, $alpha)) * pow($value, $alpha - 1) * exp(0 - pow($value / $beta, $alpha));
3686
            }
3687
        }
3688
3689
        return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The expression return PhpOffice\PhpSpre...tion\Functions::VALUE() returns the type string which is incompatible with the documented return type double.
Loading history...
3690
    }
3691
3692
    /**
3693
     * ZTEST.
3694
     *
3695
     * Returns the Weibull distribution. Use this distribution in reliability
3696
     * analysis, such as calculating a device's mean time to failure.
3697
     *
3698
     * @param float $dataSet
3699
     * @param float $m0 Alpha Parameter
3700
     * @param float $sigma Beta Parameter
3701
     *
3702
     * @return float
3703
     */
3704
    public static function ZTEST($dataSet, $m0, $sigma = null)
3705
    {
3706
        $dataSet = Functions::flattenArrayIndexed($dataSet);
0 ignored issues
show
Bug introduced by
$dataSet of type double is incompatible with the type array expected by parameter $array of PhpOffice\PhpSpreadsheet...::flattenArrayIndexed(). ( Ignorable by Annotation )

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

3706
        $dataSet = Functions::flattenArrayIndexed(/** @scrutinizer ignore-type */ $dataSet);
Loading history...
3707
        $m0 = Functions::flattenSingleValue($m0);
3708
        $sigma = Functions::flattenSingleValue($sigma);
3709
3710
        if ($sigma === null) {
3711
            $sigma = self::STDEV($dataSet);
3712
        }
3713
        $n = count($dataSet);
3714
3715
        return 1 - self::NORMSDIST((self::AVERAGE($dataSet) - $m0) / ($sigma / sqrt($n)));
3716
    }
3717
}
3718