Passed
Push — master ( 5f7ed9...f1e82a )
by Mark
28:04
created

Statistical::AVEDEV()   B

Complexity

Conditions 10
Paths 15

Size

Total Lines 35
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 10.3936

Importance

Changes 0
Metric Value
cc 10
eloc 19
nc 15
nop 1
dl 0
loc 35
ccs 16
cts 19
cp 0.8421
crap 10.3936
rs 7.6666
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
    private static function checkTrendArrays(&$array1, &$array2)
17
    {
18
        if (!is_array($array1)) {
19
            $array1 = [$array1];
20
        }
21
        if (!is_array($array2)) {
22
            $array2 = [$array2];
23
        }
24
25
        $array1 = Functions::flattenArray($array1);
26
        $array2 = Functions::flattenArray($array2);
27
        foreach ($array1 as $key => $value) {
28
            if ((is_bool($value)) || (is_string($value)) || ($value === null)) {
29
                unset($array1[$key], $array2[$key]);
30
            }
31
        }
32
        foreach ($array2 as $key => $value) {
33
            if ((is_bool($value)) || (is_string($value)) || ($value === null)) {
34
                unset($array1[$key], $array2[$key]);
35
            }
36
        }
37
        $array1 = array_merge($array1);
38
        $array2 = array_merge($array2);
39
40
        return true;
41
    }
42
43
    /**
44
     * Incomplete beta function.
45
     *
46
     * @author Jaco van Kooten
47
     * @author Paul Meagher
48
     *
49
     * The computation is based on formulas from Numerical Recipes, Chapter 6.4 (W.H. Press et al, 1992).
50
     *
51
     * @param mixed $x require 0<=x<=1
52
     * @param mixed $p require p>0
53
     * @param mixed $q require q>0
54
     *
55
     * @return float 0 if x<0, p<=0, q<=0 or p+q>2.55E305 and 1 if x>1 to avoid errors and over/underflow
56
     */
57 4
    private static function incompleteBeta($x, $p, $q)
58
    {
59 4
        if ($x <= 0.0) {
60
            return 0.0;
61 4
        } elseif ($x >= 1.0) {
62 2
            return 1.0;
63 4
        } elseif (($p <= 0.0) || ($q <= 0.0) || (($p + $q) > self::LOG_GAMMA_X_MAX_VALUE)) {
64
            return 0.0;
65
        }
66 4
        $beta_gam = exp((0 - self::logBeta($p, $q)) + $p * log($x) + $q * log(1.0 - $x));
67 4
        if ($x < ($p + 1.0) / ($p + $q + 2.0)) {
68 1
            return $beta_gam * self::betaFraction($x, $p, $q) / $p;
69
        }
70
71 4
        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 4
    private static function logBeta($p, $q)
92
    {
93 4
        if ($p != self::$logBetaCacheP || $q != self::$logBetaCacheQ) {
94 4
            self::$logBetaCacheP = $p;
95 4
            self::$logBetaCacheQ = $q;
96 4
            if (($p <= 0.0) || ($q <= 0.0) || (($p + $q) > self::LOG_GAMMA_X_MAX_VALUE)) {
97
                self::$logBetaCacheResult = 0.0;
98
            } else {
99 4
                self::$logBetaCacheResult = self::logGamma($p) + self::logGamma($q) - self::logGamma($p + $q);
100
            }
101
        }
102
103 4
        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 4
    private static function betaFraction($x, $p, $q)
119
    {
120 4
        $c = 1.0;
121 4
        $sum_pq = $p + $q;
122 4
        $p_plus = $p + 1.0;
123 4
        $p_minus = $p - 1.0;
124 4
        $h = 1.0 - $sum_pq * $x / $p_plus;
125 4
        if (abs($h) < self::XMININ) {
126
            $h = self::XMININ;
127
        }
128 4
        $h = 1.0 / $h;
129 4
        $frac = $h;
130 4
        $m = 1;
131 4
        $delta = 0.0;
132 4
        while ($m <= self::MAX_ITERATIONS && abs($delta - 1.0) > Functions::PRECISION) {
133 4
            $m2 = 2 * $m;
134
            // even index for d
135 4
            $d = $m * ($q - $m) * $x / (($p_minus + $m2) * ($p + $m2));
136 4
            $h = 1.0 + $d * $h;
137 4
            if (abs($h) < self::XMININ) {
138
                $h = self::XMININ;
139
            }
140 4
            $h = 1.0 / $h;
141 4
            $c = 1.0 + $d / $c;
142 4
            if (abs($c) < self::XMININ) {
143
                $c = self::XMININ;
144
            }
145 4
            $frac *= $h * $c;
146
            // odd index for d
147 4
            $d = -($p + $m) * ($sum_pq + $m) * $x / (($p + $m2) * ($p_plus + $m2));
148 4
            $h = 1.0 + $d * $h;
149 4
            if (abs($h) < self::XMININ) {
150
                $h = self::XMININ;
151
            }
152 4
            $h = 1.0 / $h;
153 4
            $c = 1.0 + $d / $c;
154 4
            if (abs($c) < self::XMININ) {
155
                $c = self::XMININ;
156
            }
157 4
            $delta = $h * $c;
158 4
            $frac *= $delta;
159 4
            ++$m;
160
        }
161
162 4
        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 4
    private static function logGamma($x)
216
    {
217
        // Log Gamma related constants
218 4
        static $lg_d1 = -0.5772156649015328605195174;
219 4
        static $lg_d2 = 0.4227843350984671393993777;
220 4
        static $lg_d4 = 1.791759469228055000094023;
221
222 4
        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 4
        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 4
        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 4
        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 4
        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 4
        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 4
        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 4
        static $lg_frtbig = 2.25e76;
294 4
        static $pnt68 = 0.6796875;
295
296 4
        if ($x == self::$logGammaCacheX) {
297
            return self::$logGammaCacheResult;
298
        }
299 4
        $y = $x;
300 4
        if ($y > 0.0 && $y <= self::LOG_GAMMA_X_MAX_VALUE) {
301 4
            if ($y <= self::EPS) {
302
                $res = -log($y);
303 4
            } 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 4
            } 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 4
            } elseif ($y <= 12.0) {
345
                // ----------------------
346
                //    4.0 .LT. X .LE. 12.0
347
                // ----------------------
348 4
                $xm4 = $y - 4.0;
349 4
                $xden = -1.0;
350 4
                $xnum = 0.0;
351 4
                for ($i = 0; $i < 8; ++$i) {
352 4
                    $xnum = $xnum * $xm4 + $lg_p4[$i];
353 4
                    $xden = $xden * $xm4 + $lg_q4[$i];
354
                }
355 4
                $res = $lg_d4 + $xm4 * ($xnum / $xden);
356
            } else {
357
                // ---------------------------------
358
                //    Evaluate for argument .GE. 12.0
359
                // ---------------------------------
360 4
                $res = 0.0;
361 4
                if ($y <= $lg_frtbig) {
362 4
                    $res = $lg_c[6];
363 4
                    $ysq = $y * $y;
364 4
                    for ($i = 0; $i < 6; ++$i) {
365 4
                        $res = $res / $ysq + $lg_c[$i];
366
                    }
367 4
                    $res /= $y;
368 4
                    $corr = log($y);
369 4
                    $res = $res + log(self::SQRT2PI) - 0.5 * $corr;
370 4
                    $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 4
        self::$logGammaCacheX = $x;
383 4
        self::$logGammaCacheResult = $res;
384
385 4
        return $res;
386
    }
387
388
    //
389
    //    Private implementation of the incomplete Gamma function
390
    //
391
    private static function incompleteGamma($a, $x)
392
    {
393
        static $max = 32;
394
        $summer = 0;
395
        for ($n = 0; $n <= $max; ++$n) {
396
            $divisor = $a;
397
            for ($i = 1; $i <= $n; ++$i) {
398
                $divisor *= ($a + $i);
399
            }
400
            $summer += (pow($x, $n) / $divisor);
401
        }
402
403
        return pow($x, $a) * exp(0 - $x) * $summer;
404
    }
405
406
    //
407
    //    Private implementation of the Gamma function
408
    //
409
    private static function gamma($data)
410
    {
411
        if ($data == 0.0) {
412
            return 0;
413
        }
414
415
        static $p0 = 1.000000000190015;
416
        static $p = [
417
            1 => 76.18009172947146,
418
            2 => -86.50532032941677,
419
            3 => 24.01409824083091,
420
            4 => -1.231739572450155,
421
            5 => 1.208650973866179e-3,
422
            6 => -5.395239384953e-6,
423
        ];
424
425
        $y = $x = $data;
426
        $tmp = $x + 5.5;
427
        $tmp -= ($x + 0.5) * log($tmp);
428
429
        $summer = $p0;
430
        for ($j = 1; $j <= 6; ++$j) {
431
            $summer += ($p[$j] / ++$y);
432
        }
433
434
        return exp(0 - $tmp + log(self::SQRT2PI * $summer / $x));
435
    }
436
437
    /*
438
     *                                inverse_ncdf.php
439
     *                            -------------------
440
     *    begin                : Friday, January 16, 2004
441
     *    copyright            : (C) 2004 Michael Nickerson
442
     *    email                : [email protected]
443
     *
444
     */
445
    private static function inverseNcdf($p)
446
    {
447
        //    Inverse ncdf approximation by Peter J. Acklam, implementation adapted to
448
        //    PHP by Michael Nickerson, using Dr. Thomas Ziegler's C implementation as
449
        //    a guide. http://home.online.no/~pjacklam/notes/invnorm/index.html
450
        //    I have not checked the accuracy of this implementation. Be aware that PHP
451
        //    will truncate the coeficcients to 14 digits.
452
453
        //    You have permission to use and distribute this function freely for
454
        //    whatever purpose you want, but please show common courtesy and give credit
455
        //    where credit is due.
456
457
        //    Input paramater is $p - probability - where 0 < p < 1.
458
459
        //    Coefficients in rational approximations
460
        static $a = [
461
            1 => -3.969683028665376e+01,
462
            2 => 2.209460984245205e+02,
463
            3 => -2.759285104469687e+02,
464
            4 => 1.383577518672690e+02,
465
            5 => -3.066479806614716e+01,
466
            6 => 2.506628277459239e+00,
467
        ];
468
469
        static $b = [
470
            1 => -5.447609879822406e+01,
471
            2 => 1.615858368580409e+02,
472
            3 => -1.556989798598866e+02,
473
            4 => 6.680131188771972e+01,
474
            5 => -1.328068155288572e+01,
475
        ];
476
477
        static $c = [
478
            1 => -7.784894002430293e-03,
479
            2 => -3.223964580411365e-01,
480
            3 => -2.400758277161838e+00,
481
            4 => -2.549732539343734e+00,
482
            5 => 4.374664141464968e+00,
483
            6 => 2.938163982698783e+00,
484
        ];
485
486
        static $d = [
487
            1 => 7.784695709041462e-03,
488
            2 => 3.224671290700398e-01,
489
            3 => 2.445134137142996e+00,
490
            4 => 3.754408661907416e+00,
491
        ];
492
493
        //    Define lower and upper region break-points.
494
        $p_low = 0.02425; //Use lower region approx. below this
495
        $p_high = 1 - $p_low; //Use upper region approx. above this
496
497
        if (0 < $p && $p < $p_low) {
498
            //    Rational approximation for lower region.
499
            $q = sqrt(-2 * log($p));
500
501
            return ((((($c[1] * $q + $c[2]) * $q + $c[3]) * $q + $c[4]) * $q + $c[5]) * $q + $c[6]) /
502
                    (((($d[1] * $q + $d[2]) * $q + $d[3]) * $q + $d[4]) * $q + 1);
503
        } elseif ($p_low <= $p && $p <= $p_high) {
504
            //    Rational approximation for central region.
505
            $q = $p - 0.5;
506
            $r = $q * $q;
507
508
            return ((((($a[1] * $r + $a[2]) * $r + $a[3]) * $r + $a[4]) * $r + $a[5]) * $r + $a[6]) * $q /
509
                   ((((($b[1] * $r + $b[2]) * $r + $b[3]) * $r + $b[4]) * $r + $b[5]) * $r + 1);
510
        } elseif ($p_high < $p && $p < 1) {
511
            //    Rational approximation for upper region.
512
            $q = sqrt(-2 * log(1 - $p));
513
514
            return -((((($c[1] * $q + $c[2]) * $q + $c[3]) * $q + $c[4]) * $q + $c[5]) * $q + $c[6]) /
515
                     (((($d[1] * $q + $d[2]) * $q + $d[3]) * $q + $d[4]) * $q + 1);
516
        }
517
        //    If 0 < p < 1, return a null value
518
        return Functions::NULL();
519
    }
520
521
    /**
522
     * AVEDEV.
523
     *
524
     * Returns the average of the absolute deviations of data points from their mean.
525
     * AVEDEV is a measure of the variability in a data set.
526
     *
527
     * Excel Function:
528
     *        AVEDEV(value1[,value2[, ...]])
529
     *
530
     * @category Statistical Functions
531
     *
532
     * @param mixed ...$args Data values
533
     *
534
     * @return float|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
                if ($returnValue === null) {
600
                    $returnValue = $arg;
601
                } else {
602 21
                    $returnValue += $arg;
603
                }
604 21
                ++$aCount;
605
            }
606
        }
607
608
        // Return
609 21
        if ($aCount > 0) {
610 21
            return $returnValue / $aCount;
611
        }
612
613
        return Functions::DIV0();
614
    }
615
616
    /**
617
     * AVERAGEA.
618
     *
619
     * Returns the average of its arguments, including numbers, text, and logical values
620
     *
621
     * Excel Function:
622
     *        AVERAGEA(value1[,value2[, ...]])
623
     *
624
     * @category Statistical Functions
625
     *
626
     * @param mixed ...$args Data values
627
     *
628
     * @return float|string
629
     */
630 6
    public static function AVERAGEA(...$args)
631
    {
632 6
        $returnValue = null;
633
634 6
        $aCount = 0;
635
        // Loop through arguments
636 6
        foreach (Functions::flattenArrayIndexed($args) as $k => $arg) {
637 6
            if ((is_bool($arg)) &&
638 6
                (!Functions::isMatrixValue($k))) {
639
            } else {
640 6
                if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
641 6
                    if (is_bool($arg)) {
642 3
                        $arg = (int) $arg;
643 5
                    } elseif (is_string($arg)) {
644 2
                        $arg = 0;
645
                    }
646 6
                    if ($returnValue === null) {
647 6
                        $returnValue = $arg;
648
                    } else {
649 6
                        $returnValue += $arg;
650
                    }
651 6
                    ++$aCount;
652
                }
653
            }
654
        }
655
656 6
        if ($aCount > 0) {
657 6
            return $returnValue / $aCount;
658
        }
659
660
        return Functions::DIV0();
661
    }
662
663
    /**
664
     * AVERAGEIF.
665
     *
666
     * Returns the average value from a range of cells that contain numbers within the list of arguments
667
     *
668
     * Excel Function:
669
     *        AVERAGEIF(value1[,value2[, ...]],condition)
670
     *
671
     * @category Mathematical and Trigonometric Functions
672
     *
673
     * @param mixed $aArgs Data values
674
     * @param string $condition the criteria that defines which cells will be checked
675
     * @param mixed[] $averageArgs Data values
676
     *
677
     * @return float|string
678
     */
679 8
    public static function AVERAGEIF($aArgs, $condition, $averageArgs = [])
680
    {
681 8
        $returnValue = 0;
682
683 8
        $aArgs = Functions::flattenArray($aArgs);
684 8
        $averageArgs = Functions::flattenArray($averageArgs);
685 8
        if (empty($averageArgs)) {
686 4
            $averageArgs = $aArgs;
687
        }
688 8
        $condition = Functions::ifCondition($condition);
689 8
        $conditionIsNumeric = strpos($condition, '"') === false;
690
691
        // Loop through arguments
692 8
        $aCount = 0;
693 8
        foreach ($aArgs as $key => $arg) {
694 8
            if (!is_numeric($arg)) {
695
                if ($conditionIsNumeric) {
696
                    continue;
697
                }
698
                $arg = Calculation::wrapResult(strtoupper($arg));
699 8
            } elseif (!$conditionIsNumeric) {
700
                continue;
701
            }
702 8
            $testCondition = '=' . $arg . $condition;
703 8
            if (Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
704 7
                $returnValue += $averageArgs[$key];
705 7
                ++$aCount;
706
            }
707
        }
708
709 8
        if ($aCount > 0) {
710 7
            return $returnValue / $aCount;
711
        }
712
713 1
        return Functions::DIV0();
714
    }
715
716
    /**
717
     * BETADIST.
718
     *
719
     * Returns the beta distribution.
720
     *
721
     * @param float $value Value at which you want to evaluate the distribution
722
     * @param float $alpha Parameter to the distribution
723
     * @param float $beta Parameter to the distribution
724
     * @param mixed $rMin
725
     * @param mixed $rMax
726
     *
727
     * @return float|string
728
     */
729 4
    public static function BETADIST($value, $alpha, $beta, $rMin = 0, $rMax = 1)
730
    {
731 4
        $value = Functions::flattenSingleValue($value);
732 4
        $alpha = Functions::flattenSingleValue($alpha);
733 4
        $beta = Functions::flattenSingleValue($beta);
734 4
        $rMin = Functions::flattenSingleValue($rMin);
735 4
        $rMax = Functions::flattenSingleValue($rMax);
736
737 4
        if ((is_numeric($value)) && (is_numeric($alpha)) && (is_numeric($beta)) && (is_numeric($rMin)) && (is_numeric($rMax))) {
738 4
            if (($value < $rMin) || ($value > $rMax) || ($alpha <= 0) || ($beta <= 0) || ($rMin == $rMax)) {
739
                return Functions::NAN();
740
            }
741 4
            if ($rMin > $rMax) {
742
                $tmp = $rMin;
743
                $rMin = $rMax;
744
                $rMax = $tmp;
745
            }
746 4
            $value -= $rMin;
747 4
            $value /= ($rMax - $rMin);
748
749 4
            return self::incompleteBeta($value, $alpha, $beta);
750
        }
751
752
        return Functions::VALUE();
753
    }
754
755
    /**
756
     * BETAINV.
757
     *
758
     * Returns the inverse of the beta distribution.
759
     *
760
     * @param float $probability Probability at which you want to evaluate the distribution
761
     * @param float $alpha Parameter to the distribution
762
     * @param float $beta Parameter to the distribution
763
     * @param float $rMin Minimum value
764
     * @param float $rMax Maximum value
765
     *
766
     * @return float|string
767
     */
768 2
    public static function BETAINV($probability, $alpha, $beta, $rMin = 0, $rMax = 1)
769
    {
770 2
        $probability = Functions::flattenSingleValue($probability);
771 2
        $alpha = Functions::flattenSingleValue($alpha);
772 2
        $beta = Functions::flattenSingleValue($beta);
773 2
        $rMin = Functions::flattenSingleValue($rMin);
774 2
        $rMax = Functions::flattenSingleValue($rMax);
775
776 2
        if ((is_numeric($probability)) && (is_numeric($alpha)) && (is_numeric($beta)) && (is_numeric($rMin)) && (is_numeric($rMax))) {
777 2
            if (($alpha <= 0) || ($beta <= 0) || ($rMin == $rMax) || ($probability <= 0) || ($probability > 1)) {
778
                return Functions::NAN();
779
            }
780 2
            if ($rMin > $rMax) {
781
                $tmp = $rMin;
782
                $rMin = $rMax;
783
                $rMax = $tmp;
784
            }
785 2
            $a = 0;
786 2
            $b = 2;
787
788 2
            $i = 0;
789 2
            while ((($b - $a) > Functions::PRECISION) && ($i++ < self::MAX_ITERATIONS)) {
790 2
                $guess = ($a + $b) / 2;
791 2
                $result = self::BETADIST($guess, $alpha, $beta);
792 2
                if (($result == $probability) || ($result == 0)) {
793
                    $b = $a;
794 2
                } elseif ($result > $probability) {
795 2
                    $b = $guess;
796
                } else {
797 2
                    $a = $guess;
798
                }
799
            }
800 2
            if ($i == self::MAX_ITERATIONS) {
801
                return Functions::NA();
802
            }
803
804 2
            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...
805
        }
806
807
        return Functions::VALUE();
808
    }
809
810
    /**
811
     * BINOMDIST.
812
     *
813
     * Returns the individual term binomial distribution probability. Use BINOMDIST in problems with
814
     *        a fixed number of tests or trials, when the outcomes of any trial are only success or failure,
815
     *        when trials are independent, and when the probability of success is constant throughout the
816
     *        experiment. For example, BINOMDIST can calculate the probability that two of the next three
817
     *        babies born are male.
818
     *
819
     * @param float $value Number of successes in trials
820
     * @param float $trials Number of trials
821
     * @param float $probability Probability of success on each trial
822
     * @param bool $cumulative
823
     *
824
     * @return float
825
     *
826
     * @todo    Cumulative distribution function
827
     */
828
    public static function BINOMDIST($value, $trials, $probability, $cumulative)
829
    {
830
        $value = floor(Functions::flattenSingleValue($value));
831
        $trials = floor(Functions::flattenSingleValue($trials));
832
        $probability = Functions::flattenSingleValue($probability);
833
834
        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...
835
            if (($value < 0) || ($value > $trials)) {
836
                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...
837
            }
838
            if (($probability < 0) || ($probability > 1)) {
839
                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...
840
            }
841
            if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
0 ignored issues
show
introduced by
The condition is_bool($cumulative) is always true.
Loading history...
842
                if ($cumulative) {
843
                    $summer = 0;
844
                    for ($i = 0; $i <= $value; ++$i) {
845
                        $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

845
                        $summer += MathTrig::COMBIN(/** @scrutinizer ignore-type */ $trials, $i) * pow($probability, $i) * pow(1 - $probability, $trials - $i);
Loading history...
846
                    }
847
848
                    return $summer;
849
                }
850
851
                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

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

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

1804
            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

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

2536
            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

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

3727
        $dataSet = Functions::flattenArrayIndexed(/** @scrutinizer ignore-type */ $dataSet);
Loading history...
3728
        $m0 = Functions::flattenSingleValue($m0);
3729
        $sigma = Functions::flattenSingleValue($sigma);
3730
3731
        if ($sigma === null) {
3732
            $sigma = self::STDEV($dataSet);
3733
        }
3734
        $n = count($dataSet);
3735
3736
        return 1 - self::NORMSDIST((self::AVERAGE($dataSet) - $m0) / ($sigma / sqrt($n)));
3737
    }
3738
}
3739