Passed
Push — master ( f1e82a...6d739f )
by Mark
26:54 queued 20:06
created

Statistical::AVERAGEA()   B

Complexity

Conditions 11
Paths 12

Size

Total Lines 27
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 11.0295

Importance

Changes 0
Metric Value
cc 11
eloc 16
nc 12
nop 1
dl 0
loc 27
ccs 15
cts 16
cp 0.9375
crap 11.0295
rs 7.3166
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 9
    private static function checkTrendArrays(&$array1, &$array2)
17
    {
18 9
        if (!is_array($array1)) {
19
            $array1 = [$array1];
20
        }
21 9
        if (!is_array($array2)) {
22
            $array2 = [$array2];
23
        }
24
25 9
        $array1 = Functions::flattenArray($array1);
26 9
        $array2 = Functions::flattenArray($array2);
27 9
        foreach ($array1 as $key => $value) {
28 9
            if ((is_bool($value)) || (is_string($value)) || ($value === null)) {
29
                unset($array1[$key], $array2[$key]);
30
            }
31
        }
32 9
        foreach ($array2 as $key => $value) {
33 9
            if ((is_bool($value)) || (is_string($value)) || ($value === null)) {
34
                unset($array1[$key], $array2[$key]);
35
            }
36
        }
37 9
        $array1 = array_merge($array1);
38 9
        $array2 = array_merge($array2);
39
40 9
        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 6
    private static function incompleteBeta($x, $p, $q)
58
    {
59 6
        if ($x <= 0.0) {
60
            return 0.0;
61 6
        } elseif ($x >= 1.0) {
62 3
            return 1.0;
63 6
        } elseif (($p <= 0.0) || ($q <= 0.0) || (($p + $q) > self::LOG_GAMMA_X_MAX_VALUE)) {
64
            return 0.0;
65
        }
66 6
        $beta_gam = exp((0 - self::logBeta($p, $q)) + $p * log($x) + $q * log(1.0 - $x));
67 6
        if ($x < ($p + 1.0) / ($p + $q + 2.0)) {
68 1
            return $beta_gam * self::betaFraction($x, $p, $q) / $p;
69
        }
70
71 6
        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 6
    private static function logBeta($p, $q)
92
    {
93 6
        if ($p != self::$logBetaCacheP || $q != self::$logBetaCacheQ) {
94 6
            self::$logBetaCacheP = $p;
95 6
            self::$logBetaCacheQ = $q;
96 6
            if (($p <= 0.0) || ($q <= 0.0) || (($p + $q) > self::LOG_GAMMA_X_MAX_VALUE)) {
97
                self::$logBetaCacheResult = 0.0;
98
            } else {
99 6
                self::$logBetaCacheResult = self::logGamma($p) + self::logGamma($q) - self::logGamma($p + $q);
100
            }
101
        }
102
103 6
        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 6
    private static function betaFraction($x, $p, $q)
119
    {
120 6
        $c = 1.0;
121 6
        $sum_pq = $p + $q;
122 6
        $p_plus = $p + 1.0;
123 6
        $p_minus = $p - 1.0;
124 6
        $h = 1.0 - $sum_pq * $x / $p_plus;
125 6
        if (abs($h) < self::XMININ) {
126
            $h = self::XMININ;
127
        }
128 6
        $h = 1.0 / $h;
129 6
        $frac = $h;
130 6
        $m = 1;
131 6
        $delta = 0.0;
132 6
        while ($m <= self::MAX_ITERATIONS && abs($delta - 1.0) > Functions::PRECISION) {
133 6
            $m2 = 2 * $m;
134
            // even index for d
135 6
            $d = $m * ($q - $m) * $x / (($p_minus + $m2) * ($p + $m2));
136 6
            $h = 1.0 + $d * $h;
137 6
            if (abs($h) < self::XMININ) {
138
                $h = self::XMININ;
139
            }
140 6
            $h = 1.0 / $h;
141 6
            $c = 1.0 + $d / $c;
142 6
            if (abs($c) < self::XMININ) {
143
                $c = self::XMININ;
144
            }
145 6
            $frac *= $h * $c;
146
            // odd index for d
147 6
            $d = -($p + $m) * ($sum_pq + $m) * $x / (($p + $m2) * ($p_plus + $m2));
148 6
            $h = 1.0 + $d * $h;
149 6
            if (abs($h) < self::XMININ) {
150
                $h = self::XMININ;
151
            }
152 6
            $h = 1.0 / $h;
153 6
            $c = 1.0 + $d / $c;
154 6
            if (abs($c) < self::XMININ) {
155
                $c = self::XMININ;
156
            }
157 6
            $delta = $h * $c;
158 6
            $frac *= $delta;
159 6
            ++$m;
160
        }
161
162 6
        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 6
    private static function logGamma($x)
216
    {
217
        // Log Gamma related constants
218 6
        static $lg_d1 = -0.5772156649015328605195174;
219 6
        static $lg_d2 = 0.4227843350984671393993777;
220 6
        static $lg_d4 = 1.791759469228055000094023;
221
222 6
        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 6
        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 6
        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 6
        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 6
        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 6
        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 6
        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 6
        static $lg_frtbig = 2.25e76;
294 6
        static $pnt68 = 0.6796875;
295
296 6
        if ($x == self::$logGammaCacheX) {
297
            return self::$logGammaCacheResult;
298
        }
299 6
        $y = $x;
300 6
        if ($y > 0.0 && $y <= self::LOG_GAMMA_X_MAX_VALUE) {
301 6
            if ($y <= self::EPS) {
302
                $res = -log($y);
303 6
            } 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 6
            } 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 6
            } elseif ($y <= 12.0) {
345
                // ----------------------
346
                //    4.0 .LT. X .LE. 12.0
347
                // ----------------------
348 6
                $xm4 = $y - 4.0;
349 6
                $xden = -1.0;
350 6
                $xnum = 0.0;
351 6
                for ($i = 0; $i < 8; ++$i) {
352 6
                    $xnum = $xnum * $xm4 + $lg_p4[$i];
353 6
                    $xden = $xden * $xm4 + $lg_q4[$i];
354
                }
355 6
                $res = $lg_d4 + $xm4 * ($xnum / $xden);
356
            } else {
357
                // ---------------------------------
358
                //    Evaluate for argument .GE. 12.0
359
                // ---------------------------------
360 6
                $res = 0.0;
361 6
                if ($y <= $lg_frtbig) {
362 6
                    $res = $lg_c[6];
363 6
                    $ysq = $y * $y;
364 6
                    for ($i = 0; $i < 6; ++$i) {
365 6
                        $res = $res / $ysq + $lg_c[$i];
366
                    }
367 6
                    $res /= $y;
368 6
                    $corr = log($y);
369 6
                    $res = $res + log(self::SQRT2PI) - 0.5 * $corr;
370 6
                    $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 6
        self::$logGammaCacheX = $x;
383 6
        self::$logGammaCacheResult = $res;
384
385 6
        return $res;
386
    }
387
388
    //
389
    //    Private implementation of the incomplete Gamma function
390
    //
391 6
    private static function incompleteGamma($a, $x)
392
    {
393 6
        static $max = 32;
394 6
        $summer = 0;
395 6
        for ($n = 0; $n <= $max; ++$n) {
396 6
            $divisor = $a;
397 6
            for ($i = 1; $i <= $n; ++$i) {
398 6
                $divisor *= ($a + $i);
399
            }
400 6
            $summer += (pow($x, $n) / $divisor);
401
        }
402
403 6
        return pow($x, $a) * exp(0 - $x) * $summer;
404
    }
405
406
    //
407
    //    Private implementation of the Gamma function
408
    //
409 6
    private static function gamma($data)
410
    {
411 6
        if ($data == 0.0) {
412
            return 0;
413
        }
414
415 6
        static $p0 = 1.000000000190015;
416 6
        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 6
        $y = $x = $data;
426 6
        $tmp = $x + 5.5;
427 6
        $tmp -= ($x + 0.5) * log($tmp);
428
429 6
        $summer = $p0;
430 6
        for ($j = 1; $j <= 6; ++$j) {
431 6
            $summer += ($p[$j] / ++$y);
432
        }
433
434 6
        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 1
    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 1
        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 1
        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 1
        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 1
        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 1
        $p_low = 0.02425; //Use lower region approx. below this
495 1
        $p_high = 1 - $p_low; //Use upper region approx. above this
496
497 1
        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 1
        } elseif ($p_low <= $p && $p <= $p_high) {
504
            //    Rational approximation for central region.
505 1
            $q = $p - 0.5;
506 1
            $r = $q * $q;
507
508 1
            return ((((($a[1] * $r + $a[2]) * $r + $a[3]) * $r + $a[4]) * $r + $a[5]) * $r + $a[6]) * $q /
509 1
                   ((((($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|string
535
     */
536 4
    public static function AVEDEV(...$args)
537
    {
538 4
        $aArgs = Functions::flattenArrayIndexed($args);
539
540
        // Return value
541 4
        $returnValue = null;
542
543 4
        $aMean = self::AVERAGE($aArgs);
544 4
        if ($aMean != Functions::DIV0()) {
545 4
            $aCount = 0;
546 4
            foreach ($aArgs as $k => $arg) {
547 4
                if ((is_bool($arg)) &&
548 4
                    ((!Functions::isCellValue($k)) || (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE))) {
549
                    $arg = (int) $arg;
550
                }
551
                // Is it a numeric value?
552 4
                if ((is_numeric($arg)) && (!is_string($arg))) {
553 4
                    if ($returnValue === null) {
554 4
                        $returnValue = abs($arg - $aMean);
555
                    } else {
556 4
                        $returnValue += abs($arg - $aMean);
557
                    }
558 4
                    ++$aCount;
559
                }
560
            }
561
562
            // Return
563 4
            if ($aCount == 0) {
564
                return Functions::DIV0();
565
            }
566
567 4
            return $returnValue / $aCount;
568
        }
569
570
        return Functions::NAN();
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|string
586
     */
587 21
    public static function AVERAGE(...$args)
588
    {
589 21
        $returnValue = $aCount = 0;
590
591
        // Loop through arguments
592 21
        foreach (Functions::flattenArrayIndexed($args) as $k => $arg) {
593 21
            if ((is_bool($arg)) &&
594 21
                ((!Functions::isCellValue($k)) || (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE))) {
595
                $arg = (int) $arg;
596
            }
597
            // Is it a numeric value?
598 21
            if ((is_numeric($arg)) && (!is_string($arg))) {
599 21
                $returnValue += $arg;
600 21
                ++$aCount;
601
            }
602
        }
603
604
        // Return
605 21
        if ($aCount > 0) {
606 21
            return $returnValue / $aCount;
607
        }
608
609
        return Functions::DIV0();
610
    }
611
612
    /**
613
     * AVERAGEA.
614
     *
615
     * Returns the average of its arguments, including numbers, text, and logical values
616
     *
617
     * Excel Function:
618
     *        AVERAGEA(value1[,value2[, ...]])
619
     *
620
     * @category Statistical Functions
621
     *
622
     * @param mixed ...$args Data values
623
     *
624
     * @return float|string
625
     */
626 6
    public static function AVERAGEA(...$args)
627
    {
628 6
        $returnValue = null;
629
630 6
        $aCount = 0;
631
        // Loop through arguments
632 6
        foreach (Functions::flattenArrayIndexed($args) as $k => $arg) {
633 6
            if ((is_bool($arg)) &&
634 6
                (!Functions::isMatrixValue($k))) {
635
            } else {
636 6
                if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
637 6
                    if (is_bool($arg)) {
638 3
                        $arg = (int) $arg;
639 5
                    } elseif (is_string($arg)) {
640 2
                        $arg = 0;
641
                    }
642 6
                    $returnValue += $arg;
643 6
                    ++$aCount;
644
                }
645
            }
646
        }
647
648 6
        if ($aCount > 0) {
649 6
            return $returnValue / $aCount;
650
        }
651
652
        return Functions::DIV0();
653
    }
654
655
    /**
656
     * AVERAGEIF.
657
     *
658
     * Returns the average value from a range of cells that contain numbers within the list of arguments
659
     *
660
     * Excel Function:
661
     *        AVERAGEIF(value1[,value2[, ...]],condition)
662
     *
663
     * @category Mathematical and Trigonometric Functions
664
     *
665
     * @param mixed $aArgs Data values
666
     * @param string $condition the criteria that defines which cells will be checked
667
     * @param mixed[] $averageArgs Data values
668
     *
669
     * @return float|string
670
     */
671 9
    public static function AVERAGEIF($aArgs, $condition, $averageArgs = [])
672
    {
673 9
        $returnValue = 0;
674
675 9
        $aArgs = Functions::flattenArray($aArgs);
676 9
        $averageArgs = Functions::flattenArray($averageArgs);
677 9
        if (empty($averageArgs)) {
678 5
            $averageArgs = $aArgs;
679
        }
680 9
        $condition = Functions::ifCondition($condition);
681 9
        $conditionIsNumeric = strpos($condition, '"') === false;
682
683
        // Loop through arguments
684 9
        $aCount = 0;
685 9
        foreach ($aArgs as $key => $arg) {
686 9
            if (!is_numeric($arg)) {
687 1
                if ($conditionIsNumeric) {
688 1
                    continue;
689
                }
690
                $arg = Calculation::wrapResult(strtoupper($arg));
691 9
            } elseif (!$conditionIsNumeric) {
692
                continue;
693
            }
694 9
            $testCondition = '=' . $arg . $condition;
695 9
            if (Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
696 8
                $returnValue += $averageArgs[$key];
697 8
                ++$aCount;
698
            }
699
        }
700
701 9
        if ($aCount > 0) {
702 8
            return $returnValue / $aCount;
703
        }
704
705 1
        return Functions::DIV0();
706
    }
707
708
    /**
709
     * BETADIST.
710
     *
711
     * Returns the beta distribution.
712
     *
713
     * @param float $value Value at which you want to evaluate the distribution
714
     * @param float $alpha Parameter to the distribution
715
     * @param float $beta Parameter to the distribution
716
     * @param mixed $rMin
717
     * @param mixed $rMax
718
     *
719
     * @return float|string
720
     */
721 6
    public static function BETADIST($value, $alpha, $beta, $rMin = 0, $rMax = 1)
722
    {
723 6
        $value = Functions::flattenSingleValue($value);
724 6
        $alpha = Functions::flattenSingleValue($alpha);
725 6
        $beta = Functions::flattenSingleValue($beta);
726 6
        $rMin = Functions::flattenSingleValue($rMin);
727 6
        $rMax = Functions::flattenSingleValue($rMax);
728
729 6
        if ((is_numeric($value)) && (is_numeric($alpha)) && (is_numeric($beta)) && (is_numeric($rMin)) && (is_numeric($rMax))) {
730 6
            if (($value < $rMin) || ($value > $rMax) || ($alpha <= 0) || ($beta <= 0) || ($rMin == $rMax)) {
731
                return Functions::NAN();
732
            }
733 6
            if ($rMin > $rMax) {
734
                $tmp = $rMin;
735
                $rMin = $rMax;
736
                $rMax = $tmp;
737
            }
738 6
            $value -= $rMin;
739 6
            $value /= ($rMax - $rMin);
740
741 6
            return self::incompleteBeta($value, $alpha, $beta);
742
        }
743
744
        return Functions::VALUE();
745
    }
746
747
    /**
748
     * BETAINV.
749
     *
750
     * Returns the inverse of the beta distribution.
751
     *
752
     * @param float $probability Probability at which you want to evaluate the distribution
753
     * @param float $alpha Parameter to the distribution
754
     * @param float $beta Parameter to the distribution
755
     * @param float $rMin Minimum value
756
     * @param float $rMax Maximum value
757
     *
758
     * @return float|string
759
     */
760 3
    public static function BETAINV($probability, $alpha, $beta, $rMin = 0, $rMax = 1)
761
    {
762 3
        $probability = Functions::flattenSingleValue($probability);
763 3
        $alpha = Functions::flattenSingleValue($alpha);
764 3
        $beta = Functions::flattenSingleValue($beta);
765 3
        $rMin = Functions::flattenSingleValue($rMin);
766 3
        $rMax = Functions::flattenSingleValue($rMax);
767
768 3
        if ((is_numeric($probability)) && (is_numeric($alpha)) && (is_numeric($beta)) && (is_numeric($rMin)) && (is_numeric($rMax))) {
769 3
            if (($alpha <= 0) || ($beta <= 0) || ($rMin == $rMax) || ($probability <= 0) || ($probability > 1)) {
770
                return Functions::NAN();
771
            }
772 3
            if ($rMin > $rMax) {
773
                $tmp = $rMin;
774
                $rMin = $rMax;
775
                $rMax = $tmp;
776
            }
777 3
            $a = 0;
778 3
            $b = 2;
779
780 3
            $i = 0;
781 3
            while ((($b - $a) > Functions::PRECISION) && ($i++ < self::MAX_ITERATIONS)) {
782 3
                $guess = ($a + $b) / 2;
783 3
                $result = self::BETADIST($guess, $alpha, $beta);
784 3
                if (($result == $probability) || ($result == 0)) {
785
                    $b = $a;
786 3
                } elseif ($result > $probability) {
787 3
                    $b = $guess;
788
                } else {
789 3
                    $a = $guess;
790
                }
791
            }
792 3
            if ($i == self::MAX_ITERATIONS) {
793
                return Functions::NA();
794
            }
795
796 3
            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...
797
        }
798
799
        return Functions::VALUE();
800
    }
801
802
    /**
803
     * BINOMDIST.
804
     *
805
     * Returns the individual term binomial distribution probability. Use BINOMDIST in problems with
806
     *        a fixed number of tests or trials, when the outcomes of any trial are only success or failure,
807
     *        when trials are independent, and when the probability of success is constant throughout the
808
     *        experiment. For example, BINOMDIST can calculate the probability that two of the next three
809
     *        babies born are male.
810
     *
811
     * @param float $value Number of successes in trials
812
     * @param float $trials Number of trials
813
     * @param float $probability Probability of success on each trial
814
     * @param bool $cumulative
815
     *
816
     * @return float|string
817
     *
818
     * @todo    Cumulative distribution function
819
     */
820 4
    public static function BINOMDIST($value, $trials, $probability, $cumulative)
821
    {
822 4
        $value = floor(Functions::flattenSingleValue($value));
823 4
        $trials = floor(Functions::flattenSingleValue($trials));
824 4
        $probability = Functions::flattenSingleValue($probability);
825
826 4
        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...
827 4
            if (($value < 0) || ($value > $trials)) {
828
                return Functions::NAN();
829
            }
830 4
            if (($probability < 0) || ($probability > 1)) {
831
                return Functions::NAN();
832
            }
833 4
            if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
0 ignored issues
show
introduced by
The condition is_bool($cumulative) is always true.
Loading history...
834 4
                if ($cumulative) {
835 2
                    $summer = 0;
836 2
                    for ($i = 0; $i <= $value; ++$i) {
837 2
                        $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

837
                        $summer += MathTrig::COMBIN(/** @scrutinizer ignore-type */ $trials, $i) * pow($probability, $i) * pow(1 - $probability, $trials - $i);
Loading history...
838
                    }
839
840 2
                    return $summer;
841
                }
842
843 2
                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

843
                return MathTrig::COMBIN($trials, /** @scrutinizer ignore-type */ $value) * pow($probability, $value) * pow(1 - $probability, $trials - $value);
Loading history...
844
            }
845
        }
846
847
        return Functions::VALUE();
848
    }
849
850
    /**
851
     * CHIDIST.
852
     *
853
     * Returns the one-tailed probability of the chi-squared distribution.
854
     *
855
     * @param float $value Value for the function
856
     * @param float $degrees degrees of freedom
857
     *
858
     * @return float|string
859
     */
860 6
    public static function CHIDIST($value, $degrees)
861
    {
862 6
        $value = Functions::flattenSingleValue($value);
863 6
        $degrees = floor(Functions::flattenSingleValue($degrees));
864
865 6
        if ((is_numeric($value)) && (is_numeric($degrees))) {
866 6
            if ($degrees < 1) {
867
                return Functions::NAN();
868
            }
869 6
            if ($value < 0) {
870
                if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
871
                    return 1;
872
                }
873
874
                return Functions::NAN();
875
            }
876
877 6
            return 1 - (self::incompleteGamma($degrees / 2, $value / 2) / self::gamma($degrees / 2));
878
        }
879
880
        return Functions::VALUE();
881
    }
882
883
    /**
884
     * CHIINV.
885
     *
886
     * Returns the one-tailed probability of the chi-squared distribution.
887
     *
888
     * @param float $probability Probability for the function
889
     * @param float $degrees degrees of freedom
890
     *
891
     * @return float|string
892
     */
893 3
    public static function CHIINV($probability, $degrees)
894
    {
895 3
        $probability = Functions::flattenSingleValue($probability);
896 3
        $degrees = floor(Functions::flattenSingleValue($degrees));
897
898 3
        if ((is_numeric($probability)) && (is_numeric($degrees))) {
899 3
            $xLo = 100;
900 3
            $xHi = 0;
901
902 3
            $x = $xNew = 1;
903 3
            $dx = 1;
904 3
            $i = 0;
905
906 3
            while ((abs($dx) > Functions::PRECISION) && ($i++ < self::MAX_ITERATIONS)) {
907
                // Apply Newton-Raphson step
908 3
                $result = self::CHIDIST($x, $degrees);
909 3
                $error = $result - $probability;
910 3
                if ($error == 0.0) {
911 1
                    $dx = 0;
912 3
                } elseif ($error < 0.0) {
913 3
                    $xLo = $x;
914
                } else {
915 3
                    $xHi = $x;
916
                }
917
                // Avoid division by zero
918 3
                if ($result != 0.0) {
919 3
                    $dx = $error / $result;
920 3
                    $xNew = $x - $dx;
921
                }
922
                // If the NR fails to converge (which for example may be the
923
                // case if the initial guess is too rough) we apply a bisection
924
                // step to determine a more narrow interval around the root.
925 3
                if (($xNew < $xLo) || ($xNew > $xHi) || ($result == 0.0)) {
926 3
                    $xNew = ($xLo + $xHi) / 2;
927 3
                    $dx = $xNew - $x;
928
                }
929 3
                $x = $xNew;
930
            }
931 3
            if ($i == self::MAX_ITERATIONS) {
932
                return Functions::NA();
933
            }
934
935 3
            return round($x, 12);
936
        }
937
938
        return Functions::VALUE();
939
    }
940
941
    /**
942
     * CONFIDENCE.
943
     *
944
     * Returns the confidence interval for a population mean
945
     *
946
     * @param float $alpha
947
     * @param float $stdDev Standard Deviation
948
     * @param float $size
949
     *
950
     * @return float|string
951
     */
952 1
    public static function CONFIDENCE($alpha, $stdDev, $size)
953
    {
954 1
        $alpha = Functions::flattenSingleValue($alpha);
955 1
        $stdDev = Functions::flattenSingleValue($stdDev);
956 1
        $size = floor(Functions::flattenSingleValue($size));
957
958 1
        if ((is_numeric($alpha)) && (is_numeric($stdDev)) && (is_numeric($size))) {
959 1
            if (($alpha <= 0) || ($alpha >= 1)) {
960
                return Functions::NAN();
961
            }
962 1
            if (($stdDev <= 0) || ($size < 1)) {
963
                return Functions::NAN();
964
            }
965
966 1
            return self::NORMSINV(1 - $alpha / 2) * $stdDev / sqrt($size);
967
        }
968
969
        return Functions::VALUE();
970
    }
971
972
    /**
973
     * CORREL.
974
     *
975
     * Returns covariance, the average of the products of deviations for each data point pair.
976
     *
977
     * @param mixed $yValues array of mixed Data Series Y
978
     * @param null|mixed $xValues array of mixed Data Series X
979
     *
980
     * @return float|string
981
     */
982 1
    public static function CORREL($yValues, $xValues = null)
983
    {
984 1
        if (($xValues === null) || (!is_array($yValues)) || (!is_array($xValues))) {
985
            return Functions::VALUE();
986
        }
987 1
        if (!self::checkTrendArrays($yValues, $xValues)) {
988
            return Functions::VALUE();
989
        }
990 1
        $yValueCount = count($yValues);
991 1
        $xValueCount = count($xValues);
992
993 1
        if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
994
            return Functions::NA();
995 1
        } elseif ($yValueCount == 1) {
996
            return Functions::DIV0();
997
        }
998
999 1
        $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
1000
1001 1
        return $bestFitLinear->getCorrelation();
1002
    }
1003
1004
    /**
1005
     * COUNT.
1006
     *
1007
     * Counts the number of cells that contain numbers within the list of arguments
1008
     *
1009
     * Excel Function:
1010
     *        COUNT(value1[,value2[, ...]])
1011
     *
1012
     * @category Statistical Functions
1013
     *
1014
     * @param mixed ...$args Data values
1015
     *
1016
     * @return int
1017
     */
1018 5
    public static function COUNT(...$args)
1019
    {
1020 5
        $returnValue = 0;
1021
1022
        // Loop through arguments
1023 5
        $aArgs = Functions::flattenArrayIndexed($args);
1024 5
        foreach ($aArgs as $k => $arg) {
1025 5
            if ((is_bool($arg)) &&
1026 5
                ((!Functions::isCellValue($k)) || (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE))) {
1027
                $arg = (int) $arg;
1028
            }
1029
            // Is it a numeric value?
1030 5
            if ((is_numeric($arg)) && (!is_string($arg))) {
1031 5
                ++$returnValue;
1032
            }
1033
        }
1034
1035 5
        return $returnValue;
1036
    }
1037
1038
    /**
1039
     * COUNTA.
1040
     *
1041
     * Counts the number of cells that are not empty within the list of arguments
1042
     *
1043
     * Excel Function:
1044
     *        COUNTA(value1[,value2[, ...]])
1045
     *
1046
     * @category Statistical Functions
1047
     *
1048
     * @param mixed ...$args Data values
1049
     *
1050
     * @return int
1051
     */
1052 3
    public static function COUNTA(...$args)
1053
    {
1054 3
        $returnValue = 0;
1055
1056
        // Loop through arguments
1057 3
        $aArgs = Functions::flattenArray($args);
1058 3
        foreach ($aArgs as $arg) {
1059
            // Is it a numeric, boolean or string value?
1060 3
            if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
1061 3
                ++$returnValue;
1062
            }
1063
        }
1064
1065 3
        return $returnValue;
1066
    }
1067
1068
    /**
1069
     * COUNTBLANK.
1070
     *
1071
     * Counts the number of empty cells within the list of arguments
1072
     *
1073
     * Excel Function:
1074
     *        COUNTBLANK(value1[,value2[, ...]])
1075
     *
1076
     * @category Statistical Functions
1077
     *
1078
     * @param mixed ...$args Data values
1079
     *
1080
     * @return int
1081
     */
1082
    public static function COUNTBLANK(...$args)
1083
    {
1084
        $returnValue = 0;
1085
1086
        // Loop through arguments
1087
        $aArgs = Functions::flattenArray($args);
1088
        foreach ($aArgs as $arg) {
1089
            // Is it a blank cell?
1090
            if (($arg === null) || ((is_string($arg)) && ($arg == ''))) {
1091
                ++$returnValue;
1092
            }
1093
        }
1094
1095
        return $returnValue;
1096
    }
1097
1098
    /**
1099
     * COUNTIF.
1100
     *
1101
     * Counts the number of cells that contain numbers within the list of arguments
1102
     *
1103
     * Excel Function:
1104
     *        COUNTIF(value1[,value2[, ...]],condition)
1105
     *
1106
     * @category Statistical Functions
1107
     *
1108
     * @param mixed $aArgs Data values
1109
     * @param string $condition the criteria that defines which cells will be counted
1110
     *
1111
     * @return int
1112
     */
1113 6
    public static function COUNTIF($aArgs, $condition)
1114
    {
1115 6
        $returnValue = 0;
1116
1117 6
        $aArgs = Functions::flattenArray($aArgs);
1118 6
        $condition = Functions::ifCondition($condition);
1119 6
        $conditionIsNumeric = strpos($condition, '"') === false;
1120
        // Loop through arguments
1121 6
        foreach ($aArgs as $arg) {
1122 6
            if (!is_numeric($arg)) {
1123 4
                if ($conditionIsNumeric) {
1124 2
                    continue;
1125
                }
1126 2
                $arg = Calculation::wrapResult(strtoupper($arg));
1127 4
            } elseif (!$conditionIsNumeric) {
1128
                continue;
1129
            }
1130 6
            $testCondition = '=' . $arg . $condition;
1131 6
            if (Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
1132
                // Is it a value within our criteria
1133 6
                ++$returnValue;
1134
            }
1135
        }
1136
1137 6
        return $returnValue;
1138
    }
1139
1140
    /**
1141
     * COUNTIFS.
1142
     *
1143
     * Counts the number of cells that contain numbers within the list of arguments
1144
     *
1145
     * Excel Function:
1146
     *        COUNTIFS(criteria_range1, criteria1, [criteria_range2, criteria2]…)
1147
     *
1148
     * @category Statistical Functions
1149
     *
1150
     * @param mixed $args Criterias
1151
     *
1152
     * @return int
1153
     */
1154 4
    public static function COUNTIFS(...$args)
1155
    {
1156 4
        $arrayList = $args;
1157
1158
        // Return value
1159 4
        $returnValue = 0;
1160
1161 4
        if (empty($arrayList)) {
1162
            return $returnValue;
1163
        }
1164
1165 4
        $aArgsArray = [];
1166 4
        $conditions = [];
1167
1168 4
        while (count($arrayList) > 0) {
1169 4
            $aArgsArray[] = Functions::flattenArray(array_shift($arrayList));
1170 4
            $conditions[] = Functions::ifCondition(array_shift($arrayList));
1171
        }
1172
1173
        // Loop through each arg and see if arguments and conditions are true
1174 4
        foreach (array_keys($aArgsArray[0]) as $index) {
1175 4
            $valid = true;
1176
1177 4
            foreach ($conditions as $cidx => $condition) {
1178 4
                $conditionIsNumeric = strpos($condition, '"') === false;
1179 4
                $arg = $aArgsArray[$cidx][$index];
1180
1181
                // Loop through arguments
1182 4
                if (!is_numeric($arg)) {
1183 4
                    if ($conditionIsNumeric) {
1184 1
                        $valid = false;
1185
1186 1
                        break; // if false found, don't need to check other conditions
1187
                    }
1188 3
                    $arg = Calculation::wrapResult(strtoupper($arg));
1189 1
                } elseif (!$conditionIsNumeric) {
1190
                    $valid = false;
1191
1192
                    break; // if false found, don't need to check other conditions
1193
                }
1194 4
                $testCondition = '=' . $arg . $condition;
1195 4
                if (!Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
1196
                    // Is not a value within our criteria
1197 4
                    $valid = false;
1198
1199 4
                    break; // if false found, don't need to check other conditions
1200
                }
1201
            }
1202
1203 4
            if ($valid) {
1204 4
                ++$returnValue;
1205
            }
1206
        }
1207
1208
        // Return
1209 4
        return $returnValue;
1210
    }
1211
1212
    /**
1213
     * COVAR.
1214
     *
1215
     * Returns covariance, the average of the products of deviations for each data point pair.
1216
     *
1217
     * @param mixed $yValues array of mixed Data Series Y
1218
     * @param mixed $xValues array of mixed Data Series X
1219
     *
1220
     * @return float
1221
     */
1222 2
    public static function COVAR($yValues, $xValues)
1223
    {
1224 2
        if (!self::checkTrendArrays($yValues, $xValues)) {
1225
            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...
1226
        }
1227 2
        $yValueCount = count($yValues);
1228 2
        $xValueCount = count($xValues);
1229
1230 2
        if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
1231
            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...
1232 2
        } elseif ($yValueCount == 1) {
1233
            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...
1234
        }
1235
1236 2
        $bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
1237
1238 2
        return $bestFitLinear->getCovariance();
1239
    }
1240
1241
    /**
1242
     * CRITBINOM.
1243
     *
1244
     * Returns the smallest value for which the cumulative binomial distribution is greater
1245
     *        than or equal to a criterion value
1246
     *
1247
     * See https://support.microsoft.com/en-us/help/828117/ for details of the algorithm used
1248
     *
1249
     * @param float $trials number of Bernoulli trials
1250
     * @param float $probability probability of a success on each trial
1251
     * @param float $alpha criterion value
1252
     *
1253
     * @return int
1254
     *
1255
     * @todo    Warning. This implementation differs from the algorithm detailed on the MS
1256
     *            web site in that $CumPGuessMinus1 = $CumPGuess - 1 rather than $CumPGuess - $PGuess
1257
     *            This eliminates a potential endless loop error, but may have an adverse affect on the
1258
     *            accuracy of the function (although all my tests have so far returned correct results).
1259
     */
1260
    public static function CRITBINOM($trials, $probability, $alpha)
1261
    {
1262
        $trials = floor(Functions::flattenSingleValue($trials));
1263
        $probability = Functions::flattenSingleValue($probability);
1264
        $alpha = Functions::flattenSingleValue($alpha);
1265
1266
        if ((is_numeric($trials)) && (is_numeric($probability)) && (is_numeric($alpha))) {
1267
            if ($trials < 0) {
1268
                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...
1269
            } elseif (($probability < 0) || ($probability > 1)) {
1270
                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...
1271
            } elseif (($alpha < 0) || ($alpha > 1)) {
1272
                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...
1273
            } elseif ($alpha <= 0.5) {
1274
                $t = sqrt(log(1 / ($alpha * $alpha)));
1275
                $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));
1276
            } else {
1277
                $t = sqrt(log(1 / pow(1 - $alpha, 2)));
1278
                $trialsApprox = $t - (2.515517 + 0.802853 * $t + 0.010328 * $t * $t) / (1 + 1.432788 * $t + 0.189269 * $t * $t + 0.001308 * $t * $t * $t);
1279
            }
1280
            $Guess = floor($trials * $probability + $trialsApprox * sqrt($trials * $probability * (1 - $probability)));
1281
            if ($Guess < 0) {
1282
                $Guess = 0;
1283
            } elseif ($Guess > $trials) {
1284
                $Guess = $trials;
1285
            }
1286
1287
            $TotalUnscaledProbability = $UnscaledPGuess = $UnscaledCumPGuess = 0.0;
1288
            $EssentiallyZero = 10e-12;
1289
1290
            $m = floor($trials * $probability);
1291
            ++$TotalUnscaledProbability;
1292
            if ($m == $Guess) {
1293
                ++$UnscaledPGuess;
1294
            }
1295
            if ($m <= $Guess) {
1296
                ++$UnscaledCumPGuess;
1297
            }
1298
1299
            $PreviousValue = 1;
1300
            $Done = false;
1301
            $k = $m + 1;
1302
            while ((!$Done) && ($k <= $trials)) {
1303
                $CurrentValue = $PreviousValue * ($trials - $k + 1) * $probability / ($k * (1 - $probability));
1304
                $TotalUnscaledProbability += $CurrentValue;
1305
                if ($k == $Guess) {
1306
                    $UnscaledPGuess += $CurrentValue;
1307
                }
1308
                if ($k <= $Guess) {
1309
                    $UnscaledCumPGuess += $CurrentValue;
1310
                }
1311
                if ($CurrentValue <= $EssentiallyZero) {
1312
                    $Done = true;
1313
                }
1314
                $PreviousValue = $CurrentValue;
1315
                ++$k;
1316
            }
1317
1318
            $PreviousValue = 1;
1319
            $Done = false;
1320
            $k = $m - 1;
1321
            while ((!$Done) && ($k >= 0)) {
1322
                $CurrentValue = $PreviousValue * $k + 1 * (1 - $probability) / (($trials - $k) * $probability);
1323
                $TotalUnscaledProbability += $CurrentValue;
1324
                if ($k == $Guess) {
1325
                    $UnscaledPGuess += $CurrentValue;
1326
                }
1327
                if ($k <= $Guess) {
1328
                    $UnscaledCumPGuess += $CurrentValue;
1329
                }
1330
                if ($CurrentValue <= $EssentiallyZero) {
1331
                    $Done = true;
1332
                }
1333
                $PreviousValue = $CurrentValue;
1334
                --$k;
1335
            }
1336
1337
            $PGuess = $UnscaledPGuess / $TotalUnscaledProbability;
1338
            $CumPGuess = $UnscaledCumPGuess / $TotalUnscaledProbability;
1339
1340
            $CumPGuessMinus1 = $CumPGuess - 1;
1341
1342
            while (true) {
1343
                if (($CumPGuessMinus1 < $alpha) && ($CumPGuess >= $alpha)) {
1344
                    return $Guess;
1345
                } elseif (($CumPGuessMinus1 < $alpha) && ($CumPGuess < $alpha)) {
1346
                    $PGuessPlus1 = $PGuess * ($trials - $Guess) * $probability / $Guess / (1 - $probability);
1347
                    $CumPGuessMinus1 = $CumPGuess;
1348
                    $CumPGuess = $CumPGuess + $PGuessPlus1;
1349
                    $PGuess = $PGuessPlus1;
1350
                    ++$Guess;
1351
                } elseif (($CumPGuessMinus1 >= $alpha) && ($CumPGuess >= $alpha)) {
1352
                    $PGuessMinus1 = $PGuess * $Guess * (1 - $probability) / ($trials - $Guess + 1) / $probability;
1353
                    $CumPGuess = $CumPGuessMinus1;
1354
                    $CumPGuessMinus1 = $CumPGuessMinus1 - $PGuess;
1355
                    $PGuess = $PGuessMinus1;
1356
                    --$Guess;
1357
                }
1358
            }
1359
        }
1360
1361
        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...
1362
    }
1363
1364
    /**
1365
     * DEVSQ.
1366
     *
1367
     * Returns the sum of squares of deviations of data points from their sample mean.
1368
     *
1369
     * Excel Function:
1370
     *        DEVSQ(value1[,value2[, ...]])
1371
     *
1372
     * @category Statistical Functions
1373
     *
1374
     * @param mixed ...$args Data values
1375
     *
1376
     * @return float|string
1377
     */
1378 1
    public static function DEVSQ(...$args)
1379
    {
1380 1
        $aArgs = Functions::flattenArrayIndexed($args);
1381
1382
        // Return value
1383 1
        $returnValue = null;
1384
1385 1
        $aMean = self::AVERAGE($aArgs);
1386 1
        if ($aMean != Functions::DIV0()) {
1387 1
            $aCount = -1;
1388 1
            foreach ($aArgs as $k => $arg) {
1389
                // Is it a numeric value?
1390 1
                if ((is_bool($arg)) &&
1391 1
                    ((!Functions::isCellValue($k)) ||
1392 1
                    (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE))) {
1393
                    $arg = (int) $arg;
1394
                }
1395 1
                if ((is_numeric($arg)) && (!is_string($arg))) {
1396 1
                    if ($returnValue === null) {
1397 1
                        $returnValue = pow(($arg - $aMean), 2);
1398
                    } else {
1399 1
                        $returnValue += pow(($arg - $aMean), 2);
1400
                    }
1401 1
                    ++$aCount;
1402
                }
1403
            }
1404
1405
            // Return
1406 1
            if ($returnValue === null) {
1407
                return Functions::NAN();
1408
            }
1409
1410 1
            return $returnValue;
1411
        }
1412
1413
        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

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

1796
            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

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

2528
            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

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

3719
        $dataSet = Functions::flattenArrayIndexed(/** @scrutinizer ignore-type */ $dataSet);
Loading history...
3720
        $m0 = Functions::flattenSingleValue($m0);
3721
        $sigma = Functions::flattenSingleValue($sigma);
3722
3723
        if ($sigma === null) {
3724
            $sigma = self::STDEV($dataSet);
3725
        }
3726
        $n = count($dataSet);
3727
3728
        return 1 - self::NORMSDIST((self::AVERAGE($dataSet) - $m0) / ($sigma / sqrt($n)));
3729
    }
3730
}
3731