[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/phpexcel/PHPExcel/Calculation/ -> Statistical.php (source)

   1  <?php
   2  
   3  /** PHPExcel root directory */
   4  if (!defined('PHPEXCEL_ROOT')) {
   5      /**
   6       * @ignore
   7       */
   8      define('PHPEXCEL_ROOT', dirname(__FILE__) . '/../../');
   9      require (PHPEXCEL_ROOT . 'PHPExcel/Autoloader.php');
  10  }
  11  
  12  
  13  require_once PHPEXCEL_ROOT . 'PHPExcel/Shared/trend/trendClass.php';
  14  
  15  
  16  /** LOG_GAMMA_X_MAX_VALUE */
  17  define('LOG_GAMMA_X_MAX_VALUE', 2.55e305);
  18  
  19  /** XMININ */
  20  define('XMININ', 2.23e-308);
  21  
  22  /** EPS */
  23  define('EPS', 2.22e-16);
  24  
  25  /** SQRT2PI */
  26  define('SQRT2PI', 2.5066282746310005024157652848110452530069867406099);
  27  
  28  /**
  29   * PHPExcel_Calculation_Statistical
  30   *
  31   * Copyright (c) 2006 - 2015 PHPExcel
  32   *
  33   * This library is free software; you can redistribute it and/or
  34   * modify it under the terms of the GNU Lesser General Public
  35   * License as published by the Free Software Foundation; either
  36   * version 2.1 of the License, or (at your option) any later version.
  37   *
  38   * This library is distributed in the hope that it will be useful,
  39   * but WITHOUT ANY WARRANTY; without even the implied warranty of
  40   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  41   * Lesser General Public License for more details.
  42   *
  43   * You should have received a copy of the GNU Lesser General Public
  44   * License along with this library; if not, write to the Free Software
  45   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  46   *
  47   * @category    PHPExcel
  48   * @package        PHPExcel_Calculation
  49   * @copyright    Copyright (c) 2006 - 2015 PHPExcel (http://www.codeplex.com/PHPExcel)
  50   * @license        http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt    LGPL
  51   * @version        ##VERSION##, ##DATE##
  52   */
  53  class PHPExcel_Calculation_Statistical
  54  {
  55      private static function checkTrendArrays(&$array1, &$array2)
  56      {
  57          if (!is_array($array1)) {
  58              $array1 = array($array1);
  59          }
  60          if (!is_array($array2)) {
  61              $array2 = array($array2);
  62          }
  63  
  64          $array1 = PHPExcel_Calculation_Functions::flattenArray($array1);
  65          $array2 = PHPExcel_Calculation_Functions::flattenArray($array2);
  66          foreach ($array1 as $key => $value) {
  67              if ((is_bool($value)) || (is_string($value)) || (is_null($value))) {
  68                  unset($array1[$key]);
  69                  unset($array2[$key]);
  70              }
  71          }
  72          foreach ($array2 as $key => $value) {
  73              if ((is_bool($value)) || (is_string($value)) || (is_null($value))) {
  74                  unset($array1[$key]);
  75                  unset($array2[$key]);
  76              }
  77          }
  78          $array1 = array_merge($array1);
  79          $array2 = array_merge($array2);
  80  
  81          return true;
  82      }
  83  
  84  
  85      /**
  86       * Beta function.
  87       *
  88       * @author Jaco van Kooten
  89       *
  90       * @param p require p>0
  91       * @param q require q>0
  92       * @return 0 if p<=0, q<=0 or p+q>2.55E305 to avoid errors and over/underflow
  93       */
  94      private static function beta($p, $q)
  95      {
  96          if ($p <= 0.0 || $q <= 0.0 || ($p + $q) > LOG_GAMMA_X_MAX_VALUE) {
  97              return 0.0;
  98          } else {
  99              return exp(self::logBeta($p, $q));
 100          }
 101      }
 102  
 103  
 104      /**
 105       * Incomplete beta function
 106       *
 107       * @author Jaco van Kooten
 108       * @author Paul Meagher
 109       *
 110       * The computation is based on formulas from Numerical Recipes, Chapter 6.4 (W.H. Press et al, 1992).
 111       * @param x require 0<=x<=1
 112       * @param p require p>0
 113       * @param q require q>0
 114       * @return 0 if x<0, p<=0, q<=0 or p+q>2.55E305 and 1 if x>1 to avoid errors and over/underflow
 115       */
 116      private static function incompleteBeta($x, $p, $q)
 117      {
 118          if ($x <= 0.0) {
 119              return 0.0;
 120          } elseif ($x >= 1.0) {
 121              return 1.0;
 122          } elseif (($p <= 0.0) || ($q <= 0.0) || (($p + $q) > LOG_GAMMA_X_MAX_VALUE)) {
 123              return 0.0;
 124          }
 125          $beta_gam = exp((0 - self::logBeta($p, $q)) + $p * log($x) + $q * log(1.0 - $x));
 126          if ($x < ($p + 1.0) / ($p + $q + 2.0)) {
 127              return $beta_gam * self::betaFraction($x, $p, $q) / $p;
 128          } else {
 129              return 1.0 - ($beta_gam * self::betaFraction(1 - $x, $q, $p) / $q);
 130          }
 131      }
 132  
 133  
 134      // Function cache for logBeta function
 135      private static $logBetaCacheP      = 0.0;
 136      private static $logBetaCacheQ      = 0.0;
 137      private static $logBetaCacheResult = 0.0;
 138  
 139      /**
 140       * The natural logarithm of the beta function.
 141       *
 142       * @param p require p>0
 143       * @param q require q>0
 144       * @return 0 if p<=0, q<=0 or p+q>2.55E305 to avoid errors and over/underflow
 145       * @author Jaco van Kooten
 146       */
 147      private static function logBeta($p, $q)
 148      {
 149          if ($p != self::$logBetaCacheP || $q != self::$logBetaCacheQ) {
 150              self::$logBetaCacheP = $p;
 151              self::$logBetaCacheQ = $q;
 152              if (($p <= 0.0) || ($q <= 0.0) || (($p + $q) > LOG_GAMMA_X_MAX_VALUE)) {
 153                  self::$logBetaCacheResult = 0.0;
 154              } else {
 155                  self::$logBetaCacheResult = self::logGamma($p) + self::logGamma($q) - self::logGamma($p + $q);
 156              }
 157          }
 158          return self::$logBetaCacheResult;
 159      }
 160  
 161  
 162      /**
 163       * Evaluates of continued fraction part of incomplete beta function.
 164       * Based on an idea from Numerical Recipes (W.H. Press et al, 1992).
 165       * @author Jaco van Kooten
 166       */
 167      private static function betaFraction($x, $p, $q)
 168      {
 169          $c = 1.0;
 170          $sum_pq = $p + $q;
 171          $p_plus = $p + 1.0;
 172          $p_minus = $p - 1.0;
 173          $h = 1.0 - $sum_pq * $x / $p_plus;
 174          if (abs($h) < XMININ) {
 175              $h = XMININ;
 176          }
 177          $h = 1.0 / $h;
 178          $frac = $h;
 179          $m     = 1;
 180          $delta = 0.0;
 181          while ($m <= MAX_ITERATIONS && abs($delta-1.0) > PRECISION) {
 182              $m2 = 2 * $m;
 183              // even index for d
 184              $d = $m * ($q - $m) * $x / ( ($p_minus + $m2) * ($p + $m2));
 185              $h = 1.0 + $d * $h;
 186              if (abs($h) < XMININ) {
 187                  $h = XMININ;
 188              }
 189              $h = 1.0 / $h;
 190              $c = 1.0 + $d / $c;
 191              if (abs($c) < XMININ) {
 192                  $c = XMININ;
 193              }
 194              $frac *= $h * $c;
 195              // odd index for d
 196              $d = -($p + $m) * ($sum_pq + $m) * $x / (($p + $m2) * ($p_plus + $m2));
 197              $h = 1.0 + $d * $h;
 198              if (abs($h) < XMININ) {
 199                  $h = XMININ;
 200              }
 201              $h = 1.0 / $h;
 202              $c = 1.0 + $d / $c;
 203              if (abs($c) < XMININ) {
 204                  $c = XMININ;
 205              }
 206              $delta = $h * $c;
 207              $frac *= $delta;
 208              ++$m;
 209          }
 210          return $frac;
 211      }
 212  
 213  
 214      /**
 215       * logGamma function
 216       *
 217       * @version 1.1
 218       * @author Jaco van Kooten
 219       *
 220       * Original author was Jaco van Kooten. Ported to PHP by Paul Meagher.
 221       *
 222       * The natural logarithm of the gamma function. <br />
 223       * Based on public domain NETLIB (Fortran) code by W. J. Cody and L. Stoltz <br />
 224       * Applied Mathematics Division <br />
 225       * Argonne National Laboratory <br />
 226       * Argonne, IL 60439 <br />
 227       * <p>
 228       * References:
 229       * <ol>
 230       * <li>W. J. Cody and K. E. Hillstrom, 'Chebyshev Approximations for the Natural
 231       *     Logarithm of the Gamma Function,' Math. Comp. 21, 1967, pp. 198-203.</li>
 232       * <li>K. E. Hillstrom, ANL/AMD Program ANLC366S, DGAMMA/DLGAMA, May, 1969.</li>
 233       * <li>Hart, Et. Al., Computer Approximations, Wiley and sons, New York, 1968.</li>
 234       * </ol>
 235       * </p>
 236       * <p>
 237       * From the original documentation:
 238       * </p>
 239       * <p>
 240       * This routine calculates the LOG(GAMMA) function for a positive real argument X.
 241       * Computation is based on an algorithm outlined in references 1 and 2.
 242       * The program uses rational functions that theoretically approximate LOG(GAMMA)
 243       * to at least 18 significant decimal digits. The approximation for X > 12 is from
 244       * reference 3, while approximations for X < 12.0 are similar to those in reference
 245       * 1, but are unpublished. The accuracy achieved depends on the arithmetic system,
 246       * the compiler, the intrinsic functions, and proper selection of the
 247       * machine-dependent constants.
 248       * </p>
 249       * <p>
 250       * Error returns: <br />
 251       * The program returns the value XINF for X .LE. 0.0 or when overflow would occur.
 252       * The computation is believed to be free of underflow and overflow.
 253       * </p>
 254       * @return MAX_VALUE for x < 0.0 or when overflow would occur, i.e. x > 2.55E305
 255       */
 256  
 257      // Function cache for logGamma
 258      private static $logGammaCacheResult = 0.0;
 259      private static $logGammaCacheX      = 0.0;
 260  
 261      private static function logGamma($x)
 262      {
 263          // Log Gamma related constants
 264          static $lg_d1 = -0.5772156649015328605195174;
 265          static $lg_d2 = 0.4227843350984671393993777;
 266          static $lg_d4 = 1.791759469228055000094023;
 267  
 268          static $lg_p1 = array(
 269              4.945235359296727046734888,
 270              201.8112620856775083915565,
 271              2290.838373831346393026739,
 272              11319.67205903380828685045,
 273              28557.24635671635335736389,
 274              38484.96228443793359990269,
 275              26377.48787624195437963534,
 276              7225.813979700288197698961
 277          );
 278          static $lg_p2 = array(
 279              4.974607845568932035012064,
 280              542.4138599891070494101986,
 281              15506.93864978364947665077,
 282              184793.2904445632425417223,
 283              1088204.76946882876749847,
 284              3338152.967987029735917223,
 285              5106661.678927352456275255,
 286              3074109.054850539556250927
 287          );
 288          static $lg_p4 = array(
 289              14745.02166059939948905062,
 290              2426813.369486704502836312,
 291              121475557.4045093227939592,
 292              2663432449.630976949898078,
 293              29403789566.34553899906876,
 294              170266573776.5398868392998,
 295              492612579337.743088758812,
 296              560625185622.3951465078242
 297          );
 298          static $lg_q1 = array(
 299              67.48212550303777196073036,
 300              1113.332393857199323513008,
 301              7738.757056935398733233834,
 302              27639.87074403340708898585,
 303              54993.10206226157329794414,
 304              61611.22180066002127833352,
 305              36351.27591501940507276287,
 306              8785.536302431013170870835
 307          );
 308          static $lg_q2 = array(
 309              183.0328399370592604055942,
 310              7765.049321445005871323047,
 311              133190.3827966074194402448,
 312              1136705.821321969608938755,
 313              5267964.117437946917577538,
 314              13467014.54311101692290052,
 315              17827365.30353274213975932,
 316              9533095.591844353613395747
 317          );
 318          static $lg_q4 = array(
 319              2690.530175870899333379843,
 320              639388.5654300092398984238,
 321              41355999.30241388052042842,
 322              1120872109.61614794137657,
 323              14886137286.78813811542398,
 324              101680358627.2438228077304,
 325              341747634550.7377132798597,
 326              446315818741.9713286462081
 327          );
 328          static $lg_c  = array(
 329              -0.001910444077728,
 330              8.4171387781295e-4,
 331              -5.952379913043012e-4,
 332              7.93650793500350248e-4,
 333              -0.002777777777777681622553,
 334              0.08333333333333333331554247,
 335              0.0057083835261
 336          );
 337  
 338          // Rough estimate of the fourth root of logGamma_xBig
 339          static $lg_frtbig = 2.25e76;
 340          static $pnt68     = 0.6796875;
 341  
 342  
 343          if ($x == self::$logGammaCacheX) {
 344              return self::$logGammaCacheResult;
 345          }
 346          $y = $x;
 347          if ($y > 0.0 && $y <= LOG_GAMMA_X_MAX_VALUE) {
 348              if ($y <= EPS) {
 349                  $res = -log(y);
 350              } elseif ($y <= 1.5) {
 351                  // ---------------------
 352                  //    EPS .LT. X .LE. 1.5
 353                  // ---------------------
 354                  if ($y < $pnt68) {
 355                      $corr = -log($y);
 356                      $xm1 = $y;
 357                  } else {
 358                      $corr = 0.0;
 359                      $xm1 = $y - 1.0;
 360                  }
 361                  if ($y <= 0.5 || $y >= $pnt68) {
 362                      $xden = 1.0;
 363                      $xnum = 0.0;
 364                      for ($i = 0; $i < 8; ++$i) {
 365                          $xnum = $xnum * $xm1 + $lg_p1[$i];
 366                          $xden = $xden * $xm1 + $lg_q1[$i];
 367                      }
 368                      $res = $corr + $xm1 * ($lg_d1 + $xm1 * ($xnum / $xden));
 369                  } else {
 370                      $xm2 = $y - 1.0;
 371                      $xden = 1.0;
 372                      $xnum = 0.0;
 373                      for ($i = 0; $i < 8; ++$i) {
 374                          $xnum = $xnum * $xm2 + $lg_p2[$i];
 375                          $xden = $xden * $xm2 + $lg_q2[$i];
 376                      }
 377                      $res = $corr + $xm2 * ($lg_d2 + $xm2 * ($xnum / $xden));
 378                  }
 379              } elseif ($y <= 4.0) {
 380                  // ---------------------
 381                  //    1.5 .LT. X .LE. 4.0
 382                  // ---------------------
 383                  $xm2 = $y - 2.0;
 384                  $xden = 1.0;
 385                  $xnum = 0.0;
 386                  for ($i = 0; $i < 8; ++$i) {
 387                      $xnum = $xnum * $xm2 + $lg_p2[$i];
 388                      $xden = $xden * $xm2 + $lg_q2[$i];
 389                  }
 390                  $res = $xm2 * ($lg_d2 + $xm2 * ($xnum / $xden));
 391              } elseif ($y <= 12.0) {
 392                  // ----------------------
 393                  //    4.0 .LT. X .LE. 12.0
 394                  // ----------------------
 395                  $xm4 = $y - 4.0;
 396                  $xden = -1.0;
 397                  $xnum = 0.0;
 398                  for ($i = 0; $i < 8; ++$i) {
 399                      $xnum = $xnum * $xm4 + $lg_p4[$i];
 400                      $xden = $xden * $xm4 + $lg_q4[$i];
 401                  }
 402                  $res = $lg_d4 + $xm4 * ($xnum / $xden);
 403              } else {
 404                  // ---------------------------------
 405                  //    Evaluate for argument .GE. 12.0
 406                  // ---------------------------------
 407                  $res = 0.0;
 408                  if ($y <= $lg_frtbig) {
 409                      $res = $lg_c[6];
 410                      $ysq = $y * $y;
 411                      for ($i = 0; $i < 6; ++$i) {
 412                          $res = $res / $ysq + $lg_c[$i];
 413                      }
 414                      $res /= $y;
 415                      $corr = log($y);
 416                      $res = $res + log(SQRT2PI) - 0.5 * $corr;
 417                      $res += $y * ($corr - 1.0);
 418                  }
 419              }
 420          } else {
 421              // --------------------------
 422              //    Return for bad arguments
 423              // --------------------------
 424              $res = MAX_VALUE;
 425          }
 426          // ------------------------------
 427          //    Final adjustments and return
 428          // ------------------------------
 429          self::$logGammaCacheX = $x;
 430          self::$logGammaCacheResult = $res;
 431          return $res;
 432      }
 433  
 434  
 435      //
 436      //    Private implementation of the incomplete Gamma function
 437      //
 438      private static function incompleteGamma($a, $x)
 439      {
 440          static $max = 32;
 441          $summer = 0;
 442          for ($n=0; $n<=$max; ++$n) {
 443              $divisor = $a;
 444              for ($i=1; $i<=$n; ++$i) {
 445                  $divisor *= ($a + $i);
 446              }
 447              $summer += (pow($x, $n) / $divisor);
 448          }
 449          return pow($x, $a) * exp(0-$x) * $summer;
 450      }
 451  
 452  
 453      //
 454      //    Private implementation of the Gamma function
 455      //
 456      private static function gamma($data)
 457      {
 458          if ($data == 0.0) {
 459              return 0;
 460          }
 461  
 462          static $p0 = 1.000000000190015;
 463          static $p = array(
 464              1 => 76.18009172947146,
 465              2 => -86.50532032941677,
 466              3 => 24.01409824083091,
 467              4 => -1.231739572450155,
 468              5 => 1.208650973866179e-3,
 469              6 => -5.395239384953e-6
 470          );
 471  
 472          $y = $x = $data;
 473          $tmp = $x + 5.5;
 474          $tmp -= ($x + 0.5) * log($tmp);
 475  
 476          $summer = $p0;
 477          for ($j=1; $j<=6; ++$j) {
 478              $summer += ($p[$j] / ++$y);
 479          }
 480          return exp(0 - $tmp + log(SQRT2PI * $summer / $x));
 481      }
 482  
 483  
 484      /***************************************************************************
 485       *                                inverse_ncdf.php
 486       *                            -------------------
 487       *    begin                : Friday, January 16, 2004
 488       *    copyright            : (C) 2004 Michael Nickerson
 489       *    email                : nickersonm@yahoo.com
 490       *
 491       ***************************************************************************/
 492      private static function inverseNcdf($p)
 493      {
 494          //    Inverse ncdf approximation by Peter J. Acklam, implementation adapted to
 495          //    PHP by Michael Nickerson, using Dr. Thomas Ziegler's C implementation as
 496          //    a guide. http://home.online.no/~pjacklam/notes/invnorm/index.html
 497          //    I have not checked the accuracy of this implementation. Be aware that PHP
 498          //    will truncate the coeficcients to 14 digits.
 499  
 500          //    You have permission to use and distribute this function freely for
 501          //    whatever purpose you want, but please show common courtesy and give credit
 502          //    where credit is due.
 503  
 504          //    Input paramater is $p - probability - where 0 < p < 1.
 505  
 506          //    Coefficients in rational approximations
 507          static $a = array(
 508              1 => -3.969683028665376e+01,
 509              2 => 2.209460984245205e+02,
 510              3 => -2.759285104469687e+02,
 511              4 => 1.383577518672690e+02,
 512              5 => -3.066479806614716e+01,
 513              6 => 2.506628277459239e+00
 514          );
 515  
 516          static $b = array(
 517              1 => -5.447609879822406e+01,
 518              2 => 1.615858368580409e+02,
 519              3 => -1.556989798598866e+02,
 520              4 => 6.680131188771972e+01,
 521              5 => -1.328068155288572e+01
 522          );
 523  
 524          static $c = array(
 525              1 => -7.784894002430293e-03,
 526              2 => -3.223964580411365e-01,
 527              3 => -2.400758277161838e+00,
 528              4 => -2.549732539343734e+00,
 529              5 => 4.374664141464968e+00,
 530              6 => 2.938163982698783e+00
 531          );
 532  
 533          static $d = array(
 534              1 => 7.784695709041462e-03,
 535              2 => 3.224671290700398e-01,
 536              3 => 2.445134137142996e+00,
 537              4 => 3.754408661907416e+00
 538          );
 539  
 540          //    Define lower and upper region break-points.
 541          $p_low = 0.02425;            //Use lower region approx. below this
 542          $p_high = 1 - $p_low;        //Use upper region approx. above this
 543  
 544          if (0 < $p && $p < $p_low) {
 545              //    Rational approximation for lower region.
 546              $q = sqrt(-2 * log($p));
 547              return ((((($c[1] * $q + $c[2]) * $q + $c[3]) * $q + $c[4]) * $q + $c[5]) * $q + $c[6]) /
 548                      (((($d[1] * $q + $d[2]) * $q + $d[3]) * $q + $d[4]) * $q + 1);
 549          } elseif ($p_low <= $p && $p <= $p_high) {
 550              //    Rational approximation for central region.
 551              $q = $p - 0.5;
 552              $r = $q * $q;
 553              return ((((($a[1] * $r + $a[2]) * $r + $a[3]) * $r + $a[4]) * $r + $a[5]) * $r + $a[6]) * $q /
 554                     ((((($b[1] * $r + $b[2]) * $r + $b[3]) * $r + $b[4]) * $r + $b[5]) * $r + 1);
 555          } elseif ($p_high < $p && $p < 1) {
 556              //    Rational approximation for upper region.
 557              $q = sqrt(-2 * log(1 - $p));
 558              return -((((($c[1] * $q + $c[2]) * $q + $c[3]) * $q + $c[4]) * $q + $c[5]) * $q + $c[6]) /
 559                       (((($d[1] * $q + $d[2]) * $q + $d[3]) * $q + $d[4]) * $q + 1);
 560          }
 561          //    If 0 < p < 1, return a null value
 562          return PHPExcel_Calculation_Functions::NULL();
 563      }
 564  
 565  
 566      private static function inverseNcdf2($prob)
 567      {
 568          //    Approximation of inverse standard normal CDF developed by
 569          //    B. Moro, "The Full Monte," Risk 8(2), Feb 1995, 57-58.
 570  
 571          $a1 = 2.50662823884;
 572          $a2 = -18.61500062529;
 573          $a3 = 41.39119773534;
 574          $a4 = -25.44106049637;
 575  
 576          $b1 = -8.4735109309;
 577          $b2 = 23.08336743743;
 578          $b3 = -21.06224101826;
 579          $b4 = 3.13082909833;
 580  
 581          $c1 = 0.337475482272615;
 582          $c2 = 0.976169019091719;
 583          $c3 = 0.160797971491821;
 584          $c4 = 2.76438810333863E-02;
 585          $c5 = 3.8405729373609E-03;
 586          $c6 = 3.951896511919E-04;
 587          $c7 = 3.21767881768E-05;
 588          $c8 = 2.888167364E-07;
 589          $c9 = 3.960315187E-07;
 590  
 591          $y = $prob - 0.5;
 592          if (abs($y) < 0.42) {
 593              $z = ($y * $y);
 594              $z = $y * ((($a4 * $z + $a3) * $z + $a2) * $z + $a1) / (((($b4 * $z + $b3) * $z + $b2) * $z + $b1) * $z + 1);
 595          } else {
 596              if ($y > 0) {
 597                  $z = log(-log(1 - $prob));
 598              } else {
 599                  $z = log(-log($prob));
 600              }
 601              $z = $c1 + $z * ($c2 + $z * ($c3 + $z * ($c4 + $z * ($c5 + $z * ($c6 + $z * ($c7 + $z * ($c8 + $z * $c9)))))));
 602              if ($y < 0) {
 603                  $z = -$z;
 604              }
 605          }
 606          return $z;
 607      }    //    function inverseNcdf2()
 608  
 609  
 610      private static function inverseNcdf3($p)
 611      {
 612          //    ALGORITHM AS241 APPL. STATIST. (1988) VOL. 37, NO. 3.
 613          //    Produces the normal deviate Z corresponding to a given lower
 614          //    tail area of P; Z is accurate to about 1 part in 10**16.
 615          //
 616          //    This is a PHP version of the original FORTRAN code that can
 617          //    be found at http://lib.stat.cmu.edu/apstat/
 618          $split1 = 0.425;
 619          $split2 = 5;
 620          $const1 = 0.180625;
 621          $const2 = 1.6;
 622  
 623          //    coefficients for p close to 0.5
 624          $a0 = 3.3871328727963666080;
 625          $a1 = 1.3314166789178437745E+2;
 626          $a2 = 1.9715909503065514427E+3;
 627          $a3 = 1.3731693765509461125E+4;
 628          $a4 = 4.5921953931549871457E+4;
 629          $a5 = 6.7265770927008700853E+4;
 630          $a6 = 3.3430575583588128105E+4;
 631          $a7 = 2.5090809287301226727E+3;
 632  
 633          $b1 = 4.2313330701600911252E+1;
 634          $b2 = 6.8718700749205790830E+2;
 635          $b3 = 5.3941960214247511077E+3;
 636          $b4 = 2.1213794301586595867E+4;
 637          $b5 = 3.9307895800092710610E+4;
 638          $b6 = 2.8729085735721942674E+4;
 639          $b7 = 5.2264952788528545610E+3;
 640  
 641          //    coefficients for p not close to 0, 0.5 or 1.
 642          $c0 = 1.42343711074968357734;
 643          $c1 = 4.63033784615654529590;
 644          $c2 = 5.76949722146069140550;
 645          $c3 = 3.64784832476320460504;
 646          $c4 = 1.27045825245236838258;
 647          $c5 = 2.41780725177450611770E-1;
 648          $c6 = 2.27238449892691845833E-2;
 649          $c7 = 7.74545014278341407640E-4;
 650  
 651          $d1 = 2.05319162663775882187;
 652          $d2 = 1.67638483018380384940;
 653          $d3 = 6.89767334985100004550E-1;
 654          $d4 = 1.48103976427480074590E-1;
 655          $d5 = 1.51986665636164571966E-2;
 656          $d6 = 5.47593808499534494600E-4;
 657          $d7 = 1.05075007164441684324E-9;
 658  
 659          //    coefficients for p near 0 or 1.
 660          $e0 = 6.65790464350110377720;
 661          $e1 = 5.46378491116411436990;
 662          $e2 = 1.78482653991729133580;
 663          $e3 = 2.96560571828504891230E-1;
 664          $e4 = 2.65321895265761230930E-2;
 665          $e5 = 1.24266094738807843860E-3;
 666          $e6 = 2.71155556874348757815E-5;
 667          $e7 = 2.01033439929228813265E-7;
 668  
 669          $f1 = 5.99832206555887937690E-1;
 670          $f2 = 1.36929880922735805310E-1;
 671          $f3 = 1.48753612908506148525E-2;
 672          $f4 = 7.86869131145613259100E-4;
 673          $f5 = 1.84631831751005468180E-5;
 674          $f6 = 1.42151175831644588870E-7;
 675          $f7 = 2.04426310338993978564E-15;
 676  
 677          $q = $p - 0.5;
 678  
 679          //    computation for p close to 0.5
 680          if (abs($q) <= split1) {
 681              $R = $const1 - $q * $q;
 682              $z = $q * ((((((($a7 * $R + $a6) * $R + $a5) * $R + $a4) * $R + $a3) * $R + $a2) * $R + $a1) * $R + $a0) /
 683                        ((((((($b7 * $R + $b6) * $R + $b5) * $R + $b4) * $R + $b3) * $R + $b2) * $R + $b1) * $R + 1);
 684          } else {
 685              if ($q < 0) {
 686                  $R = $p;
 687              } else {
 688                  $R = 1 - $p;
 689              }
 690              $R = pow(-log($R), 2);
 691  
 692              //    computation for p not close to 0, 0.5 or 1.
 693              if ($R <= $split2) {
 694                  $R = $R - $const2;
 695                  $z = ((((((($c7 * $R + $c6) * $R + $c5) * $R + $c4) * $R + $c3) * $R + $c2) * $R + $c1) * $R + $c0) /
 696                       ((((((($d7 * $R + $d6) * $R + $d5) * $R + $d4) * $R + $d3) * $R + $d2) * $R + $d1) * $R + 1);
 697              } else {
 698              //    computation for p near 0 or 1.
 699                  $R = $R - $split2;
 700                  $z = ((((((($e7 * $R + $e6) * $R + $e5) * $R + $e4) * $R + $e3) * $R + $e2) * $R + $e1) * $R + $e0) /
 701                       ((((((($f7 * $R + $f6) * $R + $f5) * $R + $f4) * $R + $f3) * $R + $f2) * $R + $f1) * $R + 1);
 702              }
 703              if ($q < 0) {
 704                  $z = -$z;
 705              }
 706          }
 707          return $z;
 708      }
 709  
 710  
 711      /**
 712       * AVEDEV
 713       *
 714       * Returns the average of the absolute deviations of data points from their mean.
 715       * AVEDEV is a measure of the variability in a data set.
 716       *
 717       * Excel Function:
 718       *        AVEDEV(value1[,value2[, ...]])
 719       *
 720       * @access    public
 721       * @category Statistical Functions
 722       * @param    mixed        $arg,...        Data values
 723       * @return    float
 724       */
 725      public static function AVEDEV()
 726      {
 727          $aArgs = PHPExcel_Calculation_Functions::flattenArrayIndexed(func_get_args());
 728  
 729          // Return value
 730          $returnValue = null;
 731  
 732          $aMean = self::AVERAGE($aArgs);
 733          if ($aMean != PHPExcel_Calculation_Functions::DIV0()) {
 734              $aCount = 0;
 735              foreach ($aArgs as $k => $arg) {
 736                  if ((is_bool($arg)) &&
 737                      ((!PHPExcel_Calculation_Functions::isCellValue($k)) || (PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE))) {
 738                      $arg = (integer) $arg;
 739                  }
 740                  // Is it a numeric value?
 741                  if ((is_numeric($arg)) && (!is_string($arg))) {
 742                      if (is_null($returnValue)) {
 743                          $returnValue = abs($arg - $aMean);
 744                      } else {
 745                          $returnValue += abs($arg - $aMean);
 746                      }
 747                      ++$aCount;
 748                  }
 749              }
 750  
 751              // Return
 752              if ($aCount == 0) {
 753                  return PHPExcel_Calculation_Functions::DIV0();
 754              }
 755              return $returnValue / $aCount;
 756          }
 757          return PHPExcel_Calculation_Functions::NaN();
 758      }
 759  
 760  
 761      /**
 762       * AVERAGE
 763       *
 764       * Returns the average (arithmetic mean) of the arguments
 765       *
 766       * Excel Function:
 767       *        AVERAGE(value1[,value2[, ...]])
 768       *
 769       * @access    public
 770       * @category Statistical Functions
 771       * @param    mixed        $arg,...        Data values
 772       * @return    float
 773       */
 774      public static function AVERAGE()
 775      {
 776          $returnValue = $aCount = 0;
 777  
 778          // Loop through arguments
 779          foreach (PHPExcel_Calculation_Functions::flattenArrayIndexed(func_get_args()) as $k => $arg) {
 780              if ((is_bool($arg)) &&
 781                  ((!PHPExcel_Calculation_Functions::isCellValue($k)) || (PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE))) {
 782                  $arg = (integer) $arg;
 783              }
 784              // Is it a numeric value?
 785              if ((is_numeric($arg)) && (!is_string($arg))) {
 786                  if (is_null($returnValue)) {
 787                      $returnValue = $arg;
 788                  } else {
 789                      $returnValue += $arg;
 790                  }
 791                  ++$aCount;
 792              }
 793          }
 794  
 795          // Return
 796          if ($aCount > 0) {
 797              return $returnValue / $aCount;
 798          } else {
 799              return PHPExcel_Calculation_Functions::DIV0();
 800          }
 801      }
 802  
 803  
 804      /**
 805       * AVERAGEA
 806       *
 807       * Returns the average of its arguments, including numbers, text, and logical values
 808       *
 809       * Excel Function:
 810       *        AVERAGEA(value1[,value2[, ...]])
 811       *
 812       * @access    public
 813       * @category Statistical Functions
 814       * @param    mixed        $arg,...        Data values
 815       * @return    float
 816       */
 817      public static function AVERAGEA()
 818      {
 819          $returnValue = null;
 820  
 821          $aCount = 0;
 822          // Loop through arguments
 823          foreach (PHPExcel_Calculation_Functions::flattenArrayIndexed(func_get_args()) as $k => $arg) {
 824              if ((is_bool($arg)) &&
 825                  (!PHPExcel_Calculation_Functions::isMatrixValue($k))) {
 826              } else {
 827                  if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
 828                      if (is_bool($arg)) {
 829                          $arg = (integer) $arg;
 830                      } elseif (is_string($arg)) {
 831                          $arg = 0;
 832                      }
 833                      if (is_null($returnValue)) {
 834                          $returnValue = $arg;
 835                      } else {
 836                          $returnValue += $arg;
 837                      }
 838                      ++$aCount;
 839                  }
 840              }
 841          }
 842  
 843          if ($aCount > 0) {
 844              return $returnValue / $aCount;
 845          } else {
 846              return PHPExcel_Calculation_Functions::DIV0();
 847          }
 848      }
 849  
 850  
 851      /**
 852       * AVERAGEIF
 853       *
 854       * Returns the average value from a range of cells that contain numbers within the list of arguments
 855       *
 856       * Excel Function:
 857       *        AVERAGEIF(value1[,value2[, ...]],condition)
 858       *
 859       * @access    public
 860       * @category Mathematical and Trigonometric Functions
 861       * @param    mixed        $arg,...        Data values
 862       * @param    string        $condition        The criteria that defines which cells will be checked.
 863       * @param    mixed[]        $averageArgs    Data values
 864       * @return    float
 865       */
 866      public static function AVERAGEIF($aArgs, $condition, $averageArgs = array())
 867      {
 868          $returnValue = 0;
 869  
 870          $aArgs = PHPExcel_Calculation_Functions::flattenArray($aArgs);
 871          $averageArgs = PHPExcel_Calculation_Functions::flattenArray($averageArgs);
 872          if (empty($averageArgs)) {
 873              $averageArgs = $aArgs;
 874          }
 875          $condition = PHPExcel_Calculation_Functions::ifCondition($condition);
 876          // Loop through arguments
 877          $aCount = 0;
 878          foreach ($aArgs as $key => $arg) {
 879              if (!is_numeric($arg)) {
 880                  $arg = PHPExcel_Calculation::wrapResult(strtoupper($arg));
 881              }
 882              $testCondition = '='.$arg.$condition;
 883              if (PHPExcel_Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
 884                  if ((is_null($returnValue)) || ($arg > $returnValue)) {
 885                      $returnValue += $arg;
 886                      ++$aCount;
 887                  }
 888              }
 889          }
 890  
 891          if ($aCount > 0) {
 892              return $returnValue / $aCount;
 893          }
 894          return PHPExcel_Calculation_Functions::DIV0();
 895      }
 896  
 897  
 898      /**
 899       * BETADIST
 900       *
 901       * Returns the beta distribution.
 902       *
 903       * @param    float        $value            Value at which you want to evaluate the distribution
 904       * @param    float        $alpha            Parameter to the distribution
 905       * @param    float        $beta            Parameter to the distribution
 906       * @param    boolean        $cumulative
 907       * @return    float
 908       *
 909       */
 910      public static function BETADIST($value, $alpha, $beta, $rMin = 0, $rMax = 1)
 911      {
 912          $value = PHPExcel_Calculation_Functions::flattenSingleValue($value);
 913          $alpha = PHPExcel_Calculation_Functions::flattenSingleValue($alpha);
 914          $beta  = PHPExcel_Calculation_Functions::flattenSingleValue($beta);
 915          $rMin  = PHPExcel_Calculation_Functions::flattenSingleValue($rMin);
 916          $rMax  = PHPExcel_Calculation_Functions::flattenSingleValue($rMax);
 917  
 918          if ((is_numeric($value)) && (is_numeric($alpha)) && (is_numeric($beta)) && (is_numeric($rMin)) && (is_numeric($rMax))) {
 919              if (($value < $rMin) || ($value > $rMax) || ($alpha <= 0) || ($beta <= 0) || ($rMin == $rMax)) {
 920                  return PHPExcel_Calculation_Functions::NaN();
 921              }
 922              if ($rMin > $rMax) {
 923                  $tmp = $rMin;
 924                  $rMin = $rMax;
 925                  $rMax = $tmp;
 926              }
 927              $value -= $rMin;
 928              $value /= ($rMax - $rMin);
 929              return self::incompleteBeta($value, $alpha, $beta);
 930          }
 931          return PHPExcel_Calculation_Functions::VALUE();
 932      }
 933  
 934  
 935      /**
 936       * BETAINV
 937       *
 938       * Returns the inverse of the beta distribution.
 939       *
 940       * @param    float        $probability    Probability at which you want to evaluate the distribution
 941       * @param    float        $alpha            Parameter to the distribution
 942       * @param    float        $beta            Parameter to the distribution
 943       * @param    float        $rMin            Minimum value
 944       * @param    float        $rMax            Maximum value
 945       * @param    boolean        $cumulative
 946       * @return    float
 947       *
 948       */
 949      public static function BETAINV($probability, $alpha, $beta, $rMin = 0, $rMax = 1)
 950      {
 951          $probability = PHPExcel_Calculation_Functions::flattenSingleValue($probability);
 952          $alpha       = PHPExcel_Calculation_Functions::flattenSingleValue($alpha);
 953          $beta        = PHPExcel_Calculation_Functions::flattenSingleValue($beta);
 954          $rMin        = PHPExcel_Calculation_Functions::flattenSingleValue($rMin);
 955          $rMax        = PHPExcel_Calculation_Functions::flattenSingleValue($rMax);
 956  
 957          if ((is_numeric($probability)) && (is_numeric($alpha)) && (is_numeric($beta)) && (is_numeric($rMin)) && (is_numeric($rMax))) {
 958              if (($alpha <= 0) || ($beta <= 0) || ($rMin == $rMax) || ($probability <= 0) || ($probability > 1)) {
 959                  return PHPExcel_Calculation_Functions::NaN();
 960              }
 961              if ($rMin > $rMax) {
 962                  $tmp = $rMin;
 963                  $rMin = $rMax;
 964                  $rMax = $tmp;
 965              }
 966              $a = 0;
 967              $b = 2;
 968  
 969              $i = 0;
 970              while ((($b - $a) > PRECISION) && ($i++ < MAX_ITERATIONS)) {
 971                  $guess = ($a + $b) / 2;
 972                  $result = self::BETADIST($guess, $alpha, $beta);
 973                  if (($result == $probability) || ($result == 0)) {
 974                      $b = $a;
 975                  } elseif ($result > $probability) {
 976                      $b = $guess;
 977                  } else {
 978                      $a = $guess;
 979                  }
 980              }
 981              if ($i == MAX_ITERATIONS) {
 982                  return PHPExcel_Calculation_Functions::NA();
 983              }
 984              return round($rMin + $guess * ($rMax - $rMin), 12);
 985          }
 986          return PHPExcel_Calculation_Functions::VALUE();
 987      }
 988  
 989  
 990      /**
 991       * BINOMDIST
 992       *
 993       * Returns the individual term binomial distribution probability. Use BINOMDIST in problems with
 994       *        a fixed number of tests or trials, when the outcomes of any trial are only success or failure,
 995       *        when trials are independent, and when the probability of success is constant throughout the
 996       *        experiment. For example, BINOMDIST can calculate the probability that two of the next three
 997       *        babies born are male.
 998       *
 999       * @param    float        $value            Number of successes in trials
1000       * @param    float        $trials            Number of trials
1001       * @param    float        $probability    Probability of success on each trial
1002       * @param    boolean        $cumulative
1003       * @return    float
1004       *
1005       * @todo    Cumulative distribution function
1006       *
1007       */
1008      public static function BINOMDIST($value, $trials, $probability, $cumulative)
1009      {
1010          $value       = floor(PHPExcel_Calculation_Functions::flattenSingleValue($value));
1011          $trials      = floor(PHPExcel_Calculation_Functions::flattenSingleValue($trials));
1012          $probability = PHPExcel_Calculation_Functions::flattenSingleValue($probability);
1013  
1014          if ((is_numeric($value)) && (is_numeric($trials)) && (is_numeric($probability))) {
1015              if (($value < 0) || ($value > $trials)) {
1016                  return PHPExcel_Calculation_Functions::NaN();
1017              }
1018              if (($probability < 0) || ($probability > 1)) {
1019                  return PHPExcel_Calculation_Functions::NaN();
1020              }
1021              if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
1022                  if ($cumulative) {
1023                      $summer = 0;
1024                      for ($i = 0; $i <= $value; ++$i) {
1025                          $summer += PHPExcel_Calculation_MathTrig::COMBIN($trials, $i) * pow($probability, $i) * pow(1 - $probability, $trials - $i);
1026                      }
1027                      return $summer;
1028                  } else {
1029                      return PHPExcel_Calculation_MathTrig::COMBIN($trials, $value) * pow($probability, $value) * pow(1 - $probability, $trials - $value) ;
1030                  }
1031              }
1032          }
1033          return PHPExcel_Calculation_Functions::VALUE();
1034      }
1035  
1036  
1037      /**
1038       * CHIDIST
1039       *
1040       * Returns the one-tailed probability of the chi-squared distribution.
1041       *
1042       * @param    float        $value            Value for the function
1043       * @param    float        $degrees        degrees of freedom
1044       * @return    float
1045       */
1046      public static function CHIDIST($value, $degrees)
1047      {
1048          $value   = PHPExcel_Calculation_Functions::flattenSingleValue($value);
1049          $degrees = floor(PHPExcel_Calculation_Functions::flattenSingleValue($degrees));
1050  
1051          if ((is_numeric($value)) && (is_numeric($degrees))) {
1052              if ($degrees < 1) {
1053                  return PHPExcel_Calculation_Functions::NaN();
1054              }
1055              if ($value < 0) {
1056                  if (PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_GNUMERIC) {
1057                      return 1;
1058                  }
1059                  return PHPExcel_Calculation_Functions::NaN();
1060              }
1061              return 1 - (self::incompleteGamma($degrees/2, $value/2) / self::gamma($degrees/2));
1062          }
1063          return PHPExcel_Calculation_Functions::VALUE();
1064      }
1065  
1066  
1067      /**
1068       * CHIINV
1069       *
1070       * Returns the one-tailed probability of the chi-squared distribution.
1071       *
1072       * @param    float        $probability    Probability for the function
1073       * @param    float        $degrees        degrees of freedom
1074       * @return    float
1075       */
1076      public static function CHIINV($probability, $degrees)
1077      {
1078          $probability = PHPExcel_Calculation_Functions::flattenSingleValue($probability);
1079          $degrees     = floor(PHPExcel_Calculation_Functions::flattenSingleValue($degrees));
1080  
1081          if ((is_numeric($probability)) && (is_numeric($degrees))) {
1082              $xLo = 100;
1083              $xHi = 0;
1084  
1085              $x = $xNew = 1;
1086              $dx    = 1;
1087              $i = 0;
1088  
1089              while ((abs($dx) > PRECISION) && ($i++ < MAX_ITERATIONS)) {
1090                  // Apply Newton-Raphson step
1091                  $result = self::CHIDIST($x, $degrees);
1092                  $error = $result - $probability;
1093                  if ($error == 0.0) {
1094                      $dx = 0;
1095                  } elseif ($error < 0.0) {
1096                      $xLo = $x;
1097                  } else {
1098                      $xHi = $x;
1099                  }
1100                  // Avoid division by zero
1101                  if ($result != 0.0) {
1102                      $dx = $error / $result;
1103                      $xNew = $x - $dx;
1104                  }
1105                  // If the NR fails to converge (which for example may be the
1106                  // case if the initial guess is too rough) we apply a bisection
1107                  // step to determine a more narrow interval around the root.
1108                  if (($xNew < $xLo) || ($xNew > $xHi) || ($result == 0.0)) {
1109                      $xNew = ($xLo + $xHi) / 2;
1110                      $dx = $xNew - $x;
1111                  }
1112                  $x = $xNew;
1113              }
1114              if ($i == MAX_ITERATIONS) {
1115                  return PHPExcel_Calculation_Functions::NA();
1116              }
1117              return round($x, 12);
1118          }
1119          return PHPExcel_Calculation_Functions::VALUE();
1120      }
1121  
1122  
1123      /**
1124       * CONFIDENCE
1125       *
1126       * Returns the confidence interval for a population mean
1127       *
1128       * @param    float        $alpha
1129       * @param    float        $stdDev        Standard Deviation
1130       * @param    float        $size
1131       * @return    float
1132       *
1133       */
1134      public static function CONFIDENCE($alpha, $stdDev, $size)
1135      {
1136          $alpha  = PHPExcel_Calculation_Functions::flattenSingleValue($alpha);
1137          $stdDev = PHPExcel_Calculation_Functions::flattenSingleValue($stdDev);
1138          $size   = floor(PHPExcel_Calculation_Functions::flattenSingleValue($size));
1139  
1140          if ((is_numeric($alpha)) && (is_numeric($stdDev)) && (is_numeric($size))) {
1141              if (($alpha <= 0) || ($alpha >= 1)) {
1142                  return PHPExcel_Calculation_Functions::NaN();
1143              }
1144              if (($stdDev <= 0) || ($size < 1)) {
1145                  return PHPExcel_Calculation_Functions::NaN();
1146              }
1147              return self::NORMSINV(1 - $alpha / 2) * $stdDev / sqrt($size);
1148          }
1149          return PHPExcel_Calculation_Functions::VALUE();
1150      }
1151  
1152  
1153      /**
1154       * CORREL
1155       *
1156       * Returns covariance, the average of the products of deviations for each data point pair.
1157       *
1158       * @param    array of mixed        Data Series Y
1159       * @param    array of mixed        Data Series X
1160       * @return    float
1161       */
1162      public static function CORREL($yValues, $xValues = null)
1163      {
1164          if ((is_null($xValues)) || (!is_array($yValues)) || (!is_array($xValues))) {
1165              return PHPExcel_Calculation_Functions::VALUE();
1166          }
1167          if (!self::checkTrendArrays($yValues, $xValues)) {
1168              return PHPExcel_Calculation_Functions::VALUE();
1169          }
1170          $yValueCount = count($yValues);
1171          $xValueCount = count($xValues);
1172  
1173          if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
1174              return PHPExcel_Calculation_Functions::NA();
1175          } elseif ($yValueCount == 1) {
1176              return PHPExcel_Calculation_Functions::DIV0();
1177          }
1178  
1179          $bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR, $yValues, $xValues);
1180          return $bestFitLinear->getCorrelation();
1181      }
1182  
1183  
1184      /**
1185       * COUNT
1186       *
1187       * Counts the number of cells that contain numbers within the list of arguments
1188       *
1189       * Excel Function:
1190       *        COUNT(value1[,value2[, ...]])
1191       *
1192       * @access    public
1193       * @category Statistical Functions
1194       * @param    mixed        $arg,...        Data values
1195       * @return    int
1196       */
1197      public static function COUNT()
1198      {
1199          $returnValue = 0;
1200  
1201          // Loop through arguments
1202          $aArgs = PHPExcel_Calculation_Functions::flattenArrayIndexed(func_get_args());
1203          foreach ($aArgs as $k => $arg) {
1204              if ((is_bool($arg)) &&
1205                  ((!PHPExcel_Calculation_Functions::isCellValue($k)) || (PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE))) {
1206                  $arg = (integer) $arg;
1207              }
1208              // Is it a numeric value?
1209              if ((is_numeric($arg)) && (!is_string($arg))) {
1210                  ++$returnValue;
1211              }
1212          }
1213  
1214          return $returnValue;
1215      }
1216  
1217  
1218      /**
1219       * COUNTA
1220       *
1221       * Counts the number of cells that are not empty within the list of arguments
1222       *
1223       * Excel Function:
1224       *        COUNTA(value1[,value2[, ...]])
1225       *
1226       * @access    public
1227       * @category Statistical Functions
1228       * @param    mixed        $arg,...        Data values
1229       * @return    int
1230       */
1231      public static function COUNTA()
1232      {
1233          $returnValue = 0;
1234  
1235          // Loop through arguments
1236          $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
1237          foreach ($aArgs as $arg) {
1238              // Is it a numeric, boolean or string value?
1239              if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
1240                  ++$returnValue;
1241              }
1242          }
1243  
1244          return $returnValue;
1245      }
1246  
1247  
1248      /**
1249       * COUNTBLANK
1250       *
1251       * Counts the number of empty cells within the list of arguments
1252       *
1253       * Excel Function:
1254       *        COUNTBLANK(value1[,value2[, ...]])
1255       *
1256       * @access    public
1257       * @category Statistical Functions
1258       * @param    mixed        $arg,...        Data values
1259       * @return    int
1260       */
1261      public static function COUNTBLANK()
1262      {
1263          $returnValue = 0;
1264  
1265          // Loop through arguments
1266          $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
1267          foreach ($aArgs as $arg) {
1268              // Is it a blank cell?
1269              if ((is_null($arg)) || ((is_string($arg)) && ($arg == ''))) {
1270                  ++$returnValue;
1271              }
1272          }
1273  
1274          return $returnValue;
1275      }
1276  
1277  
1278      /**
1279       * COUNTIF
1280       *
1281       * Counts the number of cells that contain numbers within the list of arguments
1282       *
1283       * Excel Function:
1284       *        COUNTIF(value1[,value2[, ...]],condition)
1285       *
1286       * @access    public
1287       * @category Statistical Functions
1288       * @param    mixed        $arg,...        Data values
1289       * @param    string        $condition        The criteria that defines which cells will be counted.
1290       * @return    int
1291       */
1292      public static function COUNTIF($aArgs, $condition)
1293      {
1294          $returnValue = 0;
1295  
1296          $aArgs = PHPExcel_Calculation_Functions::flattenArray($aArgs);
1297          $condition = PHPExcel_Calculation_Functions::ifCondition($condition);
1298          // Loop through arguments
1299          foreach ($aArgs as $arg) {
1300              if (!is_numeric($arg)) {
1301                  $arg = PHPExcel_Calculation::wrapResult(strtoupper($arg));
1302              }
1303              $testCondition = '='.$arg.$condition;
1304              if (PHPExcel_Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
1305                  // Is it a value within our criteria
1306                  ++$returnValue;
1307              }
1308          }
1309  
1310          return $returnValue;
1311      }
1312  
1313  
1314      /**
1315       * COVAR
1316       *
1317       * Returns covariance, the average of the products of deviations for each data point pair.
1318       *
1319       * @param    array of mixed        Data Series Y
1320       * @param    array of mixed        Data Series X
1321       * @return    float
1322       */
1323      public static function COVAR($yValues, $xValues)
1324      {
1325          if (!self::checkTrendArrays($yValues, $xValues)) {
1326              return PHPExcel_Calculation_Functions::VALUE();
1327          }
1328          $yValueCount = count($yValues);
1329          $xValueCount = count($xValues);
1330  
1331          if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
1332              return PHPExcel_Calculation_Functions::NA();
1333          } elseif ($yValueCount == 1) {
1334              return PHPExcel_Calculation_Functions::DIV0();
1335          }
1336  
1337          $bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR, $yValues, $xValues);
1338          return $bestFitLinear->getCovariance();
1339      }
1340  
1341  
1342      /**
1343       * CRITBINOM
1344       *
1345       * Returns the smallest value for which the cumulative binomial distribution is greater
1346       *        than or equal to a criterion value
1347       *
1348       * See http://support.microsoft.com/kb/828117/ for details of the algorithm used
1349       *
1350       * @param    float        $trials            number of Bernoulli trials
1351       * @param    float        $probability    probability of a success on each trial
1352       * @param    float        $alpha            criterion value
1353       * @return    int
1354       *
1355       * @todo    Warning. This implementation differs from the algorithm detailed on the MS
1356       *            web site in that $CumPGuessMinus1 = $CumPGuess - 1 rather than $CumPGuess - $PGuess
1357       *            This eliminates a potential endless loop error, but may have an adverse affect on the
1358       *            accuracy of the function (although all my tests have so far returned correct results).
1359       *
1360       */
1361      public static function CRITBINOM($trials, $probability, $alpha)
1362      {
1363          $trials      = floor(PHPExcel_Calculation_Functions::flattenSingleValue($trials));
1364          $probability = PHPExcel_Calculation_Functions::flattenSingleValue($probability);
1365          $alpha       = PHPExcel_Calculation_Functions::flattenSingleValue($alpha);
1366  
1367          if ((is_numeric($trials)) && (is_numeric($probability)) && (is_numeric($alpha))) {
1368              if ($trials < 0) {
1369                  return PHPExcel_Calculation_Functions::NaN();
1370              } elseif (($probability < 0) || ($probability > 1)) {
1371                  return PHPExcel_Calculation_Functions::NaN();
1372              } elseif (($alpha < 0) || ($alpha > 1)) {
1373                  return PHPExcel_Calculation_Functions::NaN();
1374              } elseif ($alpha <= 0.5) {
1375                  $t = sqrt(log(1 / ($alpha * $alpha)));
1376                  $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));
1377              } else {
1378                  $t = sqrt(log(1 / pow(1 - $alpha, 2)));
1379                  $trialsApprox = $t - (2.515517 + 0.802853 * $t + 0.010328 * $t * $t) / (1 + 1.432788 * $t + 0.189269 * $t * $t + 0.001308 * $t * $t * $t);
1380              }
1381              $Guess = floor($trials * $probability + $trialsApprox * sqrt($trials * $probability * (1 - $probability)));
1382              if ($Guess < 0) {
1383                  $Guess = 0;
1384              } elseif ($Guess > $trials) {
1385                  $Guess = $trials;
1386              }
1387  
1388              $TotalUnscaledProbability = $UnscaledPGuess = $UnscaledCumPGuess = 0.0;
1389              $EssentiallyZero = 10e-12;
1390  
1391              $m = floor($trials * $probability);
1392              ++$TotalUnscaledProbability;
1393              if ($m == $Guess) {
1394                  ++$UnscaledPGuess;
1395              }
1396              if ($m <= $Guess) {
1397                  ++$UnscaledCumPGuess;
1398              }
1399  
1400              $PreviousValue = 1;
1401              $Done = false;
1402              $k = $m + 1;
1403              while ((!$Done) && ($k <= $trials)) {
1404                  $CurrentValue = $PreviousValue * ($trials - $k + 1) * $probability / ($k * (1 - $probability));
1405                  $TotalUnscaledProbability += $CurrentValue;
1406                  if ($k == $Guess) {
1407                      $UnscaledPGuess += $CurrentValue;
1408                  }
1409                  if ($k <= $Guess) {
1410                      $UnscaledCumPGuess += $CurrentValue;
1411                  }
1412                  if ($CurrentValue <= $EssentiallyZero) {
1413                      $Done = true;
1414                  }
1415                  $PreviousValue = $CurrentValue;
1416                  ++$k;
1417              }
1418  
1419              $PreviousValue = 1;
1420              $Done = false;
1421              $k = $m - 1;
1422              while ((!$Done) && ($k >= 0)) {
1423                  $CurrentValue = $PreviousValue * $k + 1 * (1 - $probability) / (($trials - $k) * $probability);
1424                  $TotalUnscaledProbability += $CurrentValue;
1425                  if ($k == $Guess) {
1426                      $UnscaledPGuess += $CurrentValue;
1427                  }
1428                  if ($k <= $Guess) {
1429                      $UnscaledCumPGuess += $CurrentValue;
1430                  }
1431                  if ($CurrentValue <= $EssentiallyZero) {
1432                      $Done = true;
1433                  }
1434                  $PreviousValue = $CurrentValue;
1435                  --$k;
1436              }
1437  
1438              $PGuess = $UnscaledPGuess / $TotalUnscaledProbability;
1439              $CumPGuess = $UnscaledCumPGuess / $TotalUnscaledProbability;
1440  
1441  //            $CumPGuessMinus1 = $CumPGuess - $PGuess;
1442              $CumPGuessMinus1 = $CumPGuess - 1;
1443  
1444              while (true) {
1445                  if (($CumPGuessMinus1 < $alpha) && ($CumPGuess >= $alpha)) {
1446                      return $Guess;
1447                  } elseif (($CumPGuessMinus1 < $alpha) && ($CumPGuess < $alpha)) {
1448                      $PGuessPlus1 = $PGuess * ($trials - $Guess) * $probability / $Guess / (1 - $probability);
1449                      $CumPGuessMinus1 = $CumPGuess;
1450                      $CumPGuess = $CumPGuess + $PGuessPlus1;
1451                      $PGuess = $PGuessPlus1;
1452                      ++$Guess;
1453                  } elseif (($CumPGuessMinus1 >= $alpha) && ($CumPGuess >= $alpha)) {
1454                      $PGuessMinus1 = $PGuess * $Guess * (1 - $probability) / ($trials - $Guess + 1) / $probability;
1455                      $CumPGuess = $CumPGuessMinus1;
1456                      $CumPGuessMinus1 = $CumPGuessMinus1 - $PGuess;
1457                      $PGuess = $PGuessMinus1;
1458                      --$Guess;
1459                  }
1460              }
1461          }
1462          return PHPExcel_Calculation_Functions::VALUE();
1463      }
1464  
1465  
1466      /**
1467       * DEVSQ
1468       *
1469       * Returns the sum of squares of deviations of data points from their sample mean.
1470       *
1471       * Excel Function:
1472       *        DEVSQ(value1[,value2[, ...]])
1473       *
1474       * @access    public
1475       * @category Statistical Functions
1476       * @param    mixed        $arg,...        Data values
1477       * @return    float
1478       */
1479      public static function DEVSQ()
1480      {
1481          $aArgs = PHPExcel_Calculation_Functions::flattenArrayIndexed(func_get_args());
1482  
1483          // Return value
1484          $returnValue = null;
1485  
1486          $aMean = self::AVERAGE($aArgs);
1487          if ($aMean != PHPExcel_Calculation_Functions::DIV0()) {
1488              $aCount = -1;
1489              foreach ($aArgs as $k => $arg) {
1490                  // Is it a numeric value?
1491                  if ((is_bool($arg)) &&
1492                      ((!PHPExcel_Calculation_Functions::isCellValue($k)) ||
1493                      (PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE))) {
1494                      $arg = (integer) $arg;
1495                  }
1496                  if ((is_numeric($arg)) && (!is_string($arg))) {
1497                      if (is_null($returnValue)) {
1498                          $returnValue = pow(($arg - $aMean), 2);
1499                      } else {
1500                          $returnValue += pow(($arg - $aMean), 2);
1501                      }
1502                      ++$aCount;
1503                  }
1504              }
1505  
1506              // Return
1507              if (is_null($returnValue)) {
1508                  return PHPExcel_Calculation_Functions::NaN();
1509              } else {
1510                  return $returnValue;
1511              }
1512          }
1513          return self::NA();
1514      }
1515  
1516  
1517      /**
1518       * EXPONDIST
1519       *
1520       *    Returns the exponential distribution. Use EXPONDIST to model the time between events,
1521       *        such as how long an automated bank teller takes to deliver cash. For example, you can
1522       *        use EXPONDIST to determine the probability that the process takes at most 1 minute.
1523       *
1524       * @param    float        $value            Value of the function
1525       * @param    float        $lambda            The parameter value
1526       * @param    boolean        $cumulative
1527       * @return    float
1528       */
1529      public static function EXPONDIST($value, $lambda, $cumulative)
1530      {
1531          $value    = PHPExcel_Calculation_Functions::flattenSingleValue($value);
1532          $lambda    = PHPExcel_Calculation_Functions::flattenSingleValue($lambda);
1533          $cumulative    = PHPExcel_Calculation_Functions::flattenSingleValue($cumulative);
1534  
1535          if ((is_numeric($value)) && (is_numeric($lambda))) {
1536              if (($value < 0) || ($lambda < 0)) {
1537                  return PHPExcel_Calculation_Functions::NaN();
1538              }
1539              if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
1540                  if ($cumulative) {
1541                      return 1 - exp(0-$value*$lambda);
1542                  } else {
1543                      return $lambda * exp(0-$value*$lambda);
1544                  }
1545              }
1546          }
1547          return PHPExcel_Calculation_Functions::VALUE();
1548      }
1549  
1550  
1551      /**
1552       * FISHER
1553       *
1554       * Returns the Fisher transformation at x. This transformation produces a function that
1555       *        is normally distributed rather than skewed. Use this function to perform hypothesis
1556       *        testing on the correlation coefficient.
1557       *
1558       * @param    float        $value
1559       * @return    float
1560       */
1561      public static function FISHER($value)
1562      {
1563          $value    = PHPExcel_Calculation_Functions::flattenSingleValue($value);
1564  
1565          if (is_numeric($value)) {
1566              if (($value <= -1) || ($value >= 1)) {
1567                  return PHPExcel_Calculation_Functions::NaN();
1568              }
1569              return 0.5 * log((1+$value)/(1-$value));
1570          }
1571          return PHPExcel_Calculation_Functions::VALUE();
1572      }
1573  
1574  
1575      /**
1576       * FISHERINV
1577       *
1578       * Returns the inverse of the Fisher transformation. Use this transformation when
1579       *        analyzing correlations between ranges or arrays of data. If y = FISHER(x), then
1580       *        FISHERINV(y) = x.
1581       *
1582       * @param    float        $value
1583       * @return    float
1584       */
1585      public static function FISHERINV($value)
1586      {
1587          $value    = PHPExcel_Calculation_Functions::flattenSingleValue($value);
1588  
1589          if (is_numeric($value)) {
1590              return (exp(2 * $value) - 1) / (exp(2 * $value) + 1);
1591          }
1592          return PHPExcel_Calculation_Functions::VALUE();
1593      }
1594  
1595  
1596      /**
1597       * FORECAST
1598       *
1599       * Calculates, or predicts, a future value by using existing values. The predicted value is a y-value for a given x-value.
1600       *
1601       * @param    float                Value of X for which we want to find Y
1602       * @param    array of mixed        Data Series Y
1603       * @param    array of mixed        Data Series X
1604       * @return    float
1605       */
1606      public static function FORECAST($xValue, $yValues, $xValues)
1607      {
1608          $xValue    = PHPExcel_Calculation_Functions::flattenSingleValue($xValue);
1609          if (!is_numeric($xValue)) {
1610              return PHPExcel_Calculation_Functions::VALUE();
1611          } elseif (!self::checkTrendArrays($yValues, $xValues)) {
1612              return PHPExcel_Calculation_Functions::VALUE();
1613          }
1614          $yValueCount = count($yValues);
1615          $xValueCount = count($xValues);
1616  
1617          if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
1618              return PHPExcel_Calculation_Functions::NA();
1619          } elseif ($yValueCount == 1) {
1620              return PHPExcel_Calculation_Functions::DIV0();
1621          }
1622  
1623          $bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR, $yValues, $xValues);
1624          return $bestFitLinear->getValueOfYForX($xValue);
1625      }
1626  
1627  
1628      /**
1629       * GAMMADIST
1630       *
1631       * Returns the gamma distribution.
1632       *
1633       * @param    float        $value            Value at which you want to evaluate the distribution
1634       * @param    float        $a                Parameter to the distribution
1635       * @param    float        $b                Parameter to the distribution
1636       * @param    boolean        $cumulative
1637       * @return    float
1638       *
1639       */
1640      public static function GAMMADIST($value, $a, $b, $cumulative)
1641      {
1642          $value = PHPExcel_Calculation_Functions::flattenSingleValue($value);
1643          $a     = PHPExcel_Calculation_Functions::flattenSingleValue($a);
1644          $b     = PHPExcel_Calculation_Functions::flattenSingleValue($b);
1645  
1646          if ((is_numeric($value)) && (is_numeric($a)) && (is_numeric($b))) {
1647              if (($value < 0) || ($a <= 0) || ($b <= 0)) {
1648                  return PHPExcel_Calculation_Functions::NaN();
1649              }
1650              if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
1651                  if ($cumulative) {
1652                      return self::incompleteGamma($a, $value / $b) / self::gamma($a);
1653                  } else {
1654                      return (1 / (pow($b, $a) * self::gamma($a))) * pow($value, $a-1) * exp(0-($value / $b));
1655                  }
1656              }
1657          }
1658          return PHPExcel_Calculation_Functions::VALUE();
1659      }
1660  
1661  
1662      /**
1663       * GAMMAINV
1664       *
1665       * Returns the inverse of the beta distribution.
1666       *
1667       * @param    float        $probability    Probability at which you want to evaluate the distribution
1668       * @param    float        $alpha            Parameter to the distribution
1669       * @param    float        $beta            Parameter to the distribution
1670       * @return    float
1671       *
1672       */
1673      public static function GAMMAINV($probability, $alpha, $beta)
1674      {
1675          $probability = PHPExcel_Calculation_Functions::flattenSingleValue($probability);
1676          $alpha       = PHPExcel_Calculation_Functions::flattenSingleValue($alpha);
1677          $beta        = PHPExcel_Calculation_Functions::flattenSingleValue($beta);
1678  
1679          if ((is_numeric($probability)) && (is_numeric($alpha)) && (is_numeric($beta))) {
1680              if (($alpha <= 0) || ($beta <= 0) || ($probability < 0) || ($probability > 1)) {
1681                  return PHPExcel_Calculation_Functions::NaN();
1682              }
1683  
1684              $xLo = 0;
1685              $xHi = $alpha * $beta * 5;
1686  
1687              $x = $xNew = 1;
1688              $error = $pdf = 0;
1689              $dx    = 1024;
1690              $i = 0;
1691  
1692              while ((abs($dx) > PRECISION) && ($i++ < MAX_ITERATIONS)) {
1693                  // Apply Newton-Raphson step
1694                  $error = self::GAMMADIST($x, $alpha, $beta, true) - $probability;
1695                  if ($error < 0.0) {
1696                      $xLo = $x;
1697                  } else {
1698                      $xHi = $x;
1699                  }
1700                  $pdf = self::GAMMADIST($x, $alpha, $beta, false);
1701                  // Avoid division by zero
1702                  if ($pdf != 0.0) {
1703                      $dx = $error / $pdf;
1704                      $xNew = $x - $dx;
1705                  }
1706                  // If the NR fails to converge (which for example may be the
1707                  // case if the initial guess is too rough) we apply a bisection
1708                  // step to determine a more narrow interval around the root.
1709                  if (($xNew < $xLo) || ($xNew > $xHi) || ($pdf == 0.0)) {
1710                      $xNew = ($xLo + $xHi) / 2;
1711                      $dx = $xNew - $x;
1712                  }
1713                  $x = $xNew;
1714              }
1715              if ($i == MAX_ITERATIONS) {
1716                  return PHPExcel_Calculation_Functions::NA();
1717              }
1718              return $x;
1719          }
1720          return PHPExcel_Calculation_Functions::VALUE();
1721      }
1722  
1723  
1724      /**
1725       * GAMMALN
1726       *
1727       * Returns the natural logarithm of the gamma function.
1728       *
1729       * @param    float        $value
1730       * @return    float
1731       */
1732      public static function GAMMALN($value)
1733      {
1734          $value    = PHPExcel_Calculation_Functions::flattenSingleValue($value);
1735  
1736          if (is_numeric($value)) {
1737              if ($value <= 0) {
1738                  return PHPExcel_Calculation_Functions::NaN();
1739              }
1740              return log(self::gamma($value));
1741          }
1742          return PHPExcel_Calculation_Functions::VALUE();
1743      }
1744  
1745  
1746      /**
1747       * GEOMEAN
1748       *
1749       * Returns the geometric mean of an array or range of positive data. For example, you
1750       *        can use GEOMEAN to calculate average growth rate given compound interest with
1751       *        variable rates.
1752       *
1753       * Excel Function:
1754       *        GEOMEAN(value1[,value2[, ...]])
1755       *
1756       * @access    public
1757       * @category Statistical Functions
1758       * @param    mixed        $arg,...        Data values
1759       * @return    float
1760       */
1761      public static function GEOMEAN()
1762      {
1763          $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
1764  
1765          $aMean = PHPExcel_Calculation_MathTrig::PRODUCT($aArgs);
1766          if (is_numeric($aMean) && ($aMean > 0)) {
1767              $aCount = self::COUNT($aArgs) ;
1768              if (self::MIN($aArgs) > 0) {
1769                  return pow($aMean, (1 / $aCount));
1770              }
1771          }
1772          return PHPExcel_Calculation_Functions::NaN();
1773      }
1774  
1775  
1776      /**
1777       * GROWTH
1778       *
1779       * Returns values along a predicted emponential trend
1780       *
1781       * @param    array of mixed        Data Series Y
1782       * @param    array of mixed        Data Series X
1783       * @param    array of mixed        Values of X for which we want to find Y
1784       * @param    boolean                A logical value specifying whether to force the intersect to equal 0.
1785       * @return    array of float
1786       */
1787      public static function GROWTH($yValues, $xValues = array(), $newValues = array(), $const = true)
1788      {
1789          $yValues = PHPExcel_Calculation_Functions::flattenArray($yValues);
1790          $xValues = PHPExcel_Calculation_Functions::flattenArray($xValues);
1791          $newValues = PHPExcel_Calculation_Functions::flattenArray($newValues);
1792          $const = (is_null($const)) ? true : (boolean) PHPExcel_Calculation_Functions::flattenSingleValue($const);
1793  
1794          $bestFitExponential = trendClass::calculate(trendClass::TREND_EXPONENTIAL, $yValues, $xValues, $const);
1795          if (empty($newValues)) {
1796              $newValues = $bestFitExponential->getXValues();
1797          }
1798  
1799          $returnArray = array();
1800          foreach ($newValues as $xValue) {
1801              $returnArray[0][] = $bestFitExponential->getValueOfYForX($xValue);
1802          }
1803  
1804          return $returnArray;
1805      }
1806  
1807  
1808      /**
1809       * HARMEAN
1810       *
1811       * Returns the harmonic mean of a data set. The harmonic mean is the reciprocal of the
1812       *        arithmetic mean of reciprocals.
1813       *
1814       * Excel Function:
1815       *        HARMEAN(value1[,value2[, ...]])
1816       *
1817       * @access    public
1818       * @category Statistical Functions
1819       * @param    mixed        $arg,...        Data values
1820       * @return    float
1821       */
1822      public static function HARMEAN()
1823      {
1824          // Return value
1825          $returnValue = PHPExcel_Calculation_Functions::NA();
1826  
1827          // Loop through arguments
1828          $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
1829          if (self::MIN($aArgs) < 0) {
1830              return PHPExcel_Calculation_Functions::NaN();
1831          }
1832          $aCount = 0;
1833          foreach ($aArgs as $arg) {
1834              // Is it a numeric value?
1835              if ((is_numeric($arg)) && (!is_string($arg))) {
1836                  if ($arg <= 0) {
1837                      return PHPExcel_Calculation_Functions::NaN();
1838                  }
1839                  if (is_null($returnValue)) {
1840                      $returnValue = (1 / $arg);
1841                  } else {
1842                      $returnValue += (1 / $arg);
1843                  }
1844                  ++$aCount;
1845              }
1846          }
1847  
1848          // Return
1849          if ($aCount > 0) {
1850              return 1 / ($returnValue / $aCount);
1851          } else {
1852              return $returnValue;
1853          }
1854      }
1855  
1856  
1857      /**
1858       * HYPGEOMDIST
1859       *
1860       * Returns the hypergeometric distribution. HYPGEOMDIST returns the probability of a given number of
1861       * sample successes, given the sample size, population successes, and population size.
1862       *
1863       * @param    float        $sampleSuccesses        Number of successes in the sample
1864       * @param    float        $sampleNumber            Size of the sample
1865       * @param    float        $populationSuccesses    Number of successes in the population
1866       * @param    float        $populationNumber        Population size
1867       * @return    float
1868       *
1869       */
1870      public static function HYPGEOMDIST($sampleSuccesses, $sampleNumber, $populationSuccesses, $populationNumber)
1871      {
1872          $sampleSuccesses     = floor(PHPExcel_Calculation_Functions::flattenSingleValue($sampleSuccesses));
1873          $sampleNumber        = floor(PHPExcel_Calculation_Functions::flattenSingleValue($sampleNumber));
1874          $populationSuccesses = floor(PHPExcel_Calculation_Functions::flattenSingleValue($populationSuccesses));
1875          $populationNumber    = floor(PHPExcel_Calculation_Functions::flattenSingleValue($populationNumber));
1876  
1877          if ((is_numeric($sampleSuccesses)) && (is_numeric($sampleNumber)) && (is_numeric($populationSuccesses)) && (is_numeric($populationNumber))) {
1878              if (($sampleSuccesses < 0) || ($sampleSuccesses > $sampleNumber) || ($sampleSuccesses > $populationSuccesses)) {
1879                  return PHPExcel_Calculation_Functions::NaN();
1880              }
1881              if (($sampleNumber <= 0) || ($sampleNumber > $populationNumber)) {
1882                  return PHPExcel_Calculation_Functions::NaN();
1883              }
1884              if (($populationSuccesses <= 0) || ($populationSuccesses > $populationNumber)) {
1885                  return PHPExcel_Calculation_Functions::NaN();
1886              }
1887              return PHPExcel_Calculation_MathTrig::COMBIN($populationSuccesses, $sampleSuccesses) *
1888                     PHPExcel_Calculation_MathTrig::COMBIN($populationNumber - $populationSuccesses, $sampleNumber - $sampleSuccesses) /
1889                     PHPExcel_Calculation_MathTrig::COMBIN($populationNumber, $sampleNumber);
1890          }
1891          return PHPExcel_Calculation_Functions::VALUE();
1892      }
1893  
1894  
1895      /**
1896       * INTERCEPT
1897       *
1898       * Calculates the point at which a line will intersect the y-axis by using existing x-values and y-values.
1899       *
1900       * @param    array of mixed        Data Series Y
1901       * @param    array of mixed        Data Series X
1902       * @return    float
1903       */
1904      public static function INTERCEPT($yValues, $xValues)
1905      {
1906          if (!self::checkTrendArrays($yValues, $xValues)) {
1907              return PHPExcel_Calculation_Functions::VALUE();
1908          }
1909          $yValueCount = count($yValues);
1910          $xValueCount = count($xValues);
1911  
1912          if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
1913              return PHPExcel_Calculation_Functions::NA();
1914          } elseif ($yValueCount == 1) {
1915              return PHPExcel_Calculation_Functions::DIV0();
1916          }
1917  
1918          $bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR, $yValues, $xValues);
1919          return $bestFitLinear->getIntersect();
1920      }
1921  
1922  
1923      /**
1924       * KURT
1925       *
1926       * Returns the kurtosis of a data set. Kurtosis characterizes the relative peakedness
1927       * or flatness of a distribution compared with the normal distribution. Positive
1928       * kurtosis indicates a relatively peaked distribution. Negative kurtosis indicates a
1929       * relatively flat distribution.
1930       *
1931       * @param    array    Data Series
1932       * @return    float
1933       */
1934      public static function KURT()
1935      {
1936          $aArgs = PHPExcel_Calculation_Functions::flattenArrayIndexed(func_get_args());
1937          $mean = self::AVERAGE($aArgs);
1938          $stdDev = self::STDEV($aArgs);
1939  
1940          if ($stdDev > 0) {
1941              $count = $summer = 0;
1942              // Loop through arguments
1943              foreach ($aArgs as $k => $arg) {
1944                  if ((is_bool($arg)) &&
1945                      (!PHPExcel_Calculation_Functions::isMatrixValue($k))) {
1946                  } else {
1947                      // Is it a numeric value?
1948                      if ((is_numeric($arg)) && (!is_string($arg))) {
1949                          $summer += pow((($arg - $mean) / $stdDev), 4);
1950                          ++$count;
1951                      }
1952                  }
1953              }
1954  
1955              // Return
1956              if ($count > 3) {
1957                  return $summer * ($count * ($count+1) / (($count-1) * ($count-2) * ($count-3))) - (3 * pow($count-1, 2) / (($count-2) * ($count-3)));
1958              }
1959          }
1960          return PHPExcel_Calculation_Functions::DIV0();
1961      }
1962  
1963  
1964      /**
1965       * LARGE
1966       *
1967       * Returns the nth largest value in a data set. You can use this function to
1968       *        select a value based on its relative standing.
1969       *
1970       * Excel Function:
1971       *        LARGE(value1[,value2[, ...]],entry)
1972       *
1973       * @access    public
1974       * @category Statistical Functions
1975       * @param    mixed        $arg,...        Data values
1976       * @param    int            $entry            Position (ordered from the largest) in the array or range of data to return
1977       * @return    float
1978       *
1979       */
1980      public static function LARGE()
1981      {
1982          $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
1983  
1984          // Calculate
1985          $entry = floor(array_pop($aArgs));
1986  
1987          if ((is_numeric($entry)) && (!is_string($entry))) {
1988              $mArgs = array();
1989              foreach ($aArgs as $arg) {
1990                  // Is it a numeric value?
1991                  if ((is_numeric($arg)) && (!is_string($arg))) {
1992                      $mArgs[] = $arg;
1993                  }
1994              }
1995              $count = self::COUNT($mArgs);
1996              $entry = floor(--$entry);
1997              if (($entry < 0) || ($entry >= $count) || ($count == 0)) {
1998                  return PHPExcel_Calculation_Functions::NaN();
1999              }
2000              rsort($mArgs);
2001              return $mArgs[$entry];
2002          }
2003          return PHPExcel_Calculation_Functions::VALUE();
2004      }
2005  
2006  
2007      /**
2008       * LINEST
2009       *
2010       * Calculates the statistics for a line by using the "least squares" method to calculate a straight line that best fits your data,
2011       *        and then returns an array that describes the line.
2012       *
2013       * @param    array of mixed        Data Series Y
2014       * @param    array of mixed        Data Series X
2015       * @param    boolean                A logical value specifying whether to force the intersect to equal 0.
2016       * @param    boolean                A logical value specifying whether to return additional regression statistics.
2017       * @return    array
2018       */
2019      public static function LINEST($yValues, $xValues = null, $const = true, $stats = false)
2020      {
2021          $const = (is_null($const)) ? true : (boolean) PHPExcel_Calculation_Functions::flattenSingleValue($const);
2022          $stats = (is_null($stats)) ? false : (boolean) PHPExcel_Calculation_Functions::flattenSingleValue($stats);
2023          if (is_null($xValues)) {
2024              $xValues = range(1, count(PHPExcel_Calculation_Functions::flattenArray($yValues)));
2025          }
2026  
2027          if (!self::checkTrendArrays($yValues, $xValues)) {
2028              return PHPExcel_Calculation_Functions::VALUE();
2029          }
2030          $yValueCount = count($yValues);
2031          $xValueCount = count($xValues);
2032  
2033  
2034          if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
2035              return PHPExcel_Calculation_Functions::NA();
2036          } elseif ($yValueCount == 1) {
2037              return 0;
2038          }
2039  
2040          $bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR, $yValues, $xValues, $const);
2041          if ($stats) {
2042              return array(
2043                  array(
2044                      $bestFitLinear->getSlope(),
2045                      $bestFitLinear->getSlopeSE(),
2046                      $bestFitLinear->getGoodnessOfFit(),
2047                      $bestFitLinear->getF(),
2048                      $bestFitLinear->getSSRegression(),
2049                  ),
2050                  array(
2051                      $bestFitLinear->getIntersect(),
2052                      $bestFitLinear->getIntersectSE(),
2053                      $bestFitLinear->getStdevOfResiduals(),
2054                      $bestFitLinear->getDFResiduals(),
2055                      $bestFitLinear->getSSResiduals()
2056                  )
2057              );
2058          } else {
2059              return array(
2060                  $bestFitLinear->getSlope(),
2061                  $bestFitLinear->getIntersect()
2062              );
2063          }
2064      }
2065  
2066  
2067      /**
2068       * LOGEST
2069       *
2070       * Calculates an exponential curve that best fits the X and Y data series,
2071       *        and then returns an array that describes the line.
2072       *
2073       * @param    array of mixed        Data Series Y
2074       * @param    array of mixed        Data Series X
2075       * @param    boolean                A logical value specifying whether to force the intersect to equal 0.
2076       * @param    boolean                A logical value specifying whether to return additional regression statistics.
2077       * @return    array
2078       */
2079      public static function LOGEST($yValues, $xValues = null, $const = true, $stats = false)
2080      {
2081          $const = (is_null($const)) ? true : (boolean) PHPExcel_Calculation_Functions::flattenSingleValue($const);
2082          $stats = (is_null($stats)) ? false : (boolean) PHPExcel_Calculation_Functions::flattenSingleValue($stats);
2083          if (is_null($xValues)) {
2084              $xValues = range(1, count(PHPExcel_Calculation_Functions::flattenArray($yValues)));
2085          }
2086  
2087          if (!self::checkTrendArrays($yValues, $xValues)) {
2088              return PHPExcel_Calculation_Functions::VALUE();
2089          }
2090          $yValueCount = count($yValues);
2091          $xValueCount = count($xValues);
2092  
2093          foreach ($yValues as $value) {
2094              if ($value <= 0.0) {
2095                  return PHPExcel_Calculation_Functions::NaN();
2096              }
2097          }
2098  
2099  
2100          if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
2101              return PHPExcel_Calculation_Functions::NA();
2102          } elseif ($yValueCount == 1) {
2103              return 1;
2104          }
2105  
2106          $bestFitExponential = trendClass::calculate(trendClass::TREND_EXPONENTIAL, $yValues, $xValues, $const);
2107          if ($stats) {
2108              return array(
2109                  array(
2110                      $bestFitExponential->getSlope(),
2111                      $bestFitExponential->getSlopeSE(),
2112                      $bestFitExponential->getGoodnessOfFit(),
2113                      $bestFitExponential->getF(),
2114                      $bestFitExponential->getSSRegression(),
2115                  ),
2116                  array(
2117                      $bestFitExponential->getIntersect(),
2118                      $bestFitExponential->getIntersectSE(),
2119                      $bestFitExponential->getStdevOfResiduals(),
2120                      $bestFitExponential->getDFResiduals(),
2121                      $bestFitExponential->getSSResiduals()
2122                  )
2123              );
2124          } else {
2125              return array(
2126                  $bestFitExponential->getSlope(),
2127                  $bestFitExponential->getIntersect()
2128              );
2129          }
2130      }
2131  
2132  
2133      /**
2134       * LOGINV
2135       *
2136       * Returns the inverse of the normal cumulative distribution
2137       *
2138       * @param    float        $probability
2139       * @param    float        $mean
2140       * @param    float        $stdDev
2141       * @return    float
2142       *
2143       * @todo    Try implementing P J Acklam's refinement algorithm for greater
2144       *            accuracy if I can get my head round the mathematics
2145       *            (as described at) http://home.online.no/~pjacklam/notes/invnorm/
2146       */
2147      public static function LOGINV($probability, $mean, $stdDev)
2148      {
2149          $probability = PHPExcel_Calculation_Functions::flattenSingleValue($probability);
2150          $mean        = PHPExcel_Calculation_Functions::flattenSingleValue($mean);
2151          $stdDev      = PHPExcel_Calculation_Functions::flattenSingleValue($stdDev);
2152  
2153          if ((is_numeric($probability)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
2154              if (($probability < 0) || ($probability > 1) || ($stdDev <= 0)) {
2155                  return PHPExcel_Calculation_Functions::NaN();
2156              }
2157              return exp($mean + $stdDev * self::NORMSINV($probability));
2158          }
2159          return PHPExcel_Calculation_Functions::VALUE();
2160      }
2161  
2162  
2163      /**
2164       * LOGNORMDIST
2165       *
2166       * Returns the cumulative lognormal distribution of x, where ln(x) is normally distributed
2167       * with parameters mean and standard_dev.
2168       *
2169       * @param    float        $value
2170       * @param    float        $mean
2171       * @param    float        $stdDev
2172       * @return    float
2173       */
2174      public static function LOGNORMDIST($value, $mean, $stdDev)
2175      {
2176          $value  = PHPExcel_Calculation_Functions::flattenSingleValue($value);
2177          $mean   = PHPExcel_Calculation_Functions::flattenSingleValue($mean);
2178          $stdDev = PHPExcel_Calculation_Functions::flattenSingleValue($stdDev);
2179  
2180          if ((is_numeric($value)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
2181              if (($value <= 0) || ($stdDev <= 0)) {
2182                  return PHPExcel_Calculation_Functions::NaN();
2183              }
2184              return self::NORMSDIST((log($value) - $mean) / $stdDev);
2185          }
2186          return PHPExcel_Calculation_Functions::VALUE();
2187      }
2188  
2189  
2190      /**
2191       * MAX
2192       *
2193       * MAX returns the value of the element of the values passed that has the highest value,
2194       *        with negative numbers considered smaller than positive numbers.
2195       *
2196       * Excel Function:
2197       *        MAX(value1[,value2[, ...]])
2198       *
2199       * @access    public
2200       * @category Statistical Functions
2201       * @param    mixed        $arg,...        Data values
2202       * @return    float
2203       */
2204      public static function MAX()
2205      {
2206          $returnValue = null;
2207  
2208          // Loop through arguments
2209          $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
2210          foreach ($aArgs as $arg) {
2211              // Is it a numeric value?
2212              if ((is_numeric($arg)) && (!is_string($arg))) {
2213                  if ((is_null($returnValue)) || ($arg > $returnValue)) {
2214                      $returnValue = $arg;
2215                  }
2216              }
2217          }
2218  
2219          if (is_null($returnValue)) {
2220              return 0;
2221          }
2222          return $returnValue;
2223      }
2224  
2225  
2226      /**
2227       * MAXA
2228       *
2229       * Returns the greatest value in a list of arguments, including numbers, text, and logical values
2230       *
2231       * Excel Function:
2232       *        MAXA(value1[,value2[, ...]])
2233       *
2234       * @access    public
2235       * @category Statistical Functions
2236       * @param    mixed        $arg,...        Data values
2237       * @return    float
2238       */
2239      public static function MAXA()
2240      {
2241          $returnValue = null;
2242  
2243          // Loop through arguments
2244          $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
2245          foreach ($aArgs as $arg) {
2246              // Is it a numeric value?
2247              if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
2248                  if (is_bool($arg)) {
2249                      $arg = (integer) $arg;
2250                  } elseif (is_string($arg)) {
2251                      $arg = 0;
2252                  }
2253                  if ((is_null($returnValue)) || ($arg > $returnValue)) {
2254                      $returnValue = $arg;
2255                  }
2256              }
2257          }
2258  
2259          if (is_null($returnValue)) {
2260              return 0;
2261          }
2262          return $returnValue;
2263      }
2264  
2265  
2266      /**
2267       * MAXIF
2268       *
2269       * Counts the maximum value within a range of cells that contain numbers within the list of arguments
2270       *
2271       * Excel Function:
2272       *        MAXIF(value1[,value2[, ...]],condition)
2273       *
2274       * @access    public
2275       * @category Mathematical and Trigonometric Functions
2276       * @param    mixed        $arg,...        Data values
2277       * @param    string        $condition        The criteria that defines which cells will be checked.
2278       * @return    float
2279       */
2280      public static function MAXIF($aArgs, $condition, $sumArgs = array())
2281      {
2282          $returnValue = null;
2283  
2284          $aArgs = PHPExcel_Calculation_Functions::flattenArray($aArgs);
2285          $sumArgs = PHPExcel_Calculation_Functions::flattenArray($sumArgs);
2286          if (empty($sumArgs)) {
2287              $sumArgs = $aArgs;
2288          }
2289          $condition = PHPExcel_Calculation_Functions::ifCondition($condition);
2290          // Loop through arguments
2291          foreach ($aArgs as $key => $arg) {
2292              if (!is_numeric($arg)) {
2293                  $arg = PHPExcel_Calculation::wrapResult(strtoupper($arg));
2294              }
2295              $testCondition = '='.$arg.$condition;
2296              if (PHPExcel_Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
2297                  if ((is_null($returnValue)) || ($arg > $returnValue)) {
2298                      $returnValue = $arg;
2299                  }
2300              }
2301          }
2302  
2303          return $returnValue;
2304      }
2305  
2306      /**
2307       * MEDIAN
2308       *
2309       * Returns the median of the given numbers. The median is the number in the middle of a set of numbers.
2310       *
2311       * Excel Function:
2312       *        MEDIAN(value1[,value2[, ...]])
2313       *
2314       * @access    public
2315       * @category Statistical Functions
2316       * @param    mixed        $arg,...        Data values
2317       * @return    float
2318       */
2319      public static function MEDIAN()
2320      {
2321          $returnValue = PHPExcel_Calculation_Functions::NaN();
2322  
2323          $mArgs = array();
2324          // Loop through arguments
2325          $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
2326          foreach ($aArgs as $arg) {
2327              // Is it a numeric value?
2328              if ((is_numeric($arg)) && (!is_string($arg))) {
2329                  $mArgs[] = $arg;
2330              }
2331          }
2332  
2333          $mValueCount = count($mArgs);
2334          if ($mValueCount > 0) {
2335              sort($mArgs, SORT_NUMERIC);
2336              $mValueCount = $mValueCount / 2;
2337              if ($mValueCount == floor($mValueCount)) {
2338                  $returnValue = ($mArgs[$mValueCount--] + $mArgs[$mValueCount]) / 2;
2339              } else {
2340                  $mValueCount == floor($mValueCount);
2341                  $returnValue = $mArgs[$mValueCount];
2342              }
2343          }
2344  
2345          return $returnValue;
2346      }
2347  
2348  
2349      /**
2350       * MIN
2351       *
2352       * MIN returns the value of the element of the values passed that has the smallest value,
2353       *        with negative numbers considered smaller than positive numbers.
2354       *
2355       * Excel Function:
2356       *        MIN(value1[,value2[, ...]])
2357       *
2358       * @access    public
2359       * @category Statistical Functions
2360       * @param    mixed        $arg,...        Data values
2361       * @return    float
2362       */
2363      public static function MIN()
2364      {
2365          $returnValue = null;
2366  
2367          // Loop through arguments
2368          $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
2369          foreach ($aArgs as $arg) {
2370              // Is it a numeric value?
2371              if ((is_numeric($arg)) && (!is_string($arg))) {
2372                  if ((is_null($returnValue)) || ($arg < $returnValue)) {
2373                      $returnValue = $arg;
2374                  }
2375              }
2376          }
2377  
2378          if (is_null($returnValue)) {
2379              return 0;
2380          }
2381          return $returnValue;
2382      }
2383  
2384  
2385      /**
2386       * MINA
2387       *
2388       * Returns the smallest value in a list of arguments, including numbers, text, and logical values
2389       *
2390       * Excel Function:
2391       *        MINA(value1[,value2[, ...]])
2392       *
2393       * @access    public
2394       * @category Statistical Functions
2395       * @param    mixed        $arg,...        Data values
2396       * @return    float
2397       */
2398      public static function MINA()
2399      {
2400          $returnValue = null;
2401  
2402          // Loop through arguments
2403          $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
2404          foreach ($aArgs as $arg) {
2405              // Is it a numeric value?
2406              if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) && ($arg != '')))) {
2407                  if (is_bool($arg)) {
2408                      $arg = (integer) $arg;
2409                  } elseif (is_string($arg)) {
2410                      $arg = 0;
2411                  }
2412                  if ((is_null($returnValue)) || ($arg < $returnValue)) {
2413                      $returnValue = $arg;
2414                  }
2415              }
2416          }
2417  
2418          if (is_null($returnValue)) {
2419              return 0;
2420          }
2421          return $returnValue;
2422      }
2423  
2424  
2425      /**
2426       * MINIF
2427       *
2428       * Returns the minimum value within a range of cells that contain numbers within the list of arguments
2429       *
2430       * Excel Function:
2431       *        MINIF(value1[,value2[, ...]],condition)
2432       *
2433       * @access    public
2434       * @category Mathematical and Trigonometric Functions
2435       * @param    mixed        $arg,...        Data values
2436       * @param    string        $condition        The criteria that defines which cells will be checked.
2437       * @return    float
2438       */
2439      public static function MINIF($aArgs, $condition, $sumArgs = array())
2440      {
2441          $returnValue = null;
2442  
2443          $aArgs = PHPExcel_Calculation_Functions::flattenArray($aArgs);
2444          $sumArgs = PHPExcel_Calculation_Functions::flattenArray($sumArgs);
2445          if (empty($sumArgs)) {
2446              $sumArgs = $aArgs;
2447          }
2448          $condition = PHPExcel_Calculation_Functions::ifCondition($condition);
2449          // Loop through arguments
2450          foreach ($aArgs as $key => $arg) {
2451              if (!is_numeric($arg)) {
2452                  $arg = PHPExcel_Calculation::wrapResult(strtoupper($arg));
2453              }
2454              $testCondition = '='.$arg.$condition;
2455              if (PHPExcel_Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
2456                  if ((is_null($returnValue)) || ($arg < $returnValue)) {
2457                      $returnValue = $arg;
2458                  }
2459              }
2460          }
2461  
2462          return $returnValue;
2463      }
2464  
2465  
2466      //
2467      //    Special variant of array_count_values that isn't limited to strings and integers,
2468      //        but can work with floating point numbers as values
2469      //
2470      private static function modeCalc($data)
2471      {
2472          $frequencyArray = array();
2473          foreach ($data as $datum) {
2474              $found = false;
2475              foreach ($frequencyArray as $key => $value) {
2476                  if ((string) $value['value'] == (string) $datum) {
2477                      ++$frequencyArray[$key]['frequency'];
2478                      $found = true;
2479                      break;
2480                  }
2481              }
2482              if (!$found) {
2483                  $frequencyArray[] = array(
2484                      'value'     => $datum,
2485                      'frequency' => 1
2486                  );
2487              }
2488          }
2489  
2490          foreach ($frequencyArray as $key => $value) {
2491              $frequencyList[$key] = $value['frequency'];
2492              $valueList[$key] = $value['value'];
2493          }
2494          array_multisort($frequencyList, SORT_DESC, $valueList, SORT_ASC, SORT_NUMERIC, $frequencyArray);
2495  
2496          if ($frequencyArray[0]['frequency'] == 1) {
2497              return PHPExcel_Calculation_Functions::NA();
2498          }
2499          return $frequencyArray[0]['value'];
2500      }
2501  
2502  
2503      /**
2504       * MODE
2505       *
2506       * Returns the most frequently occurring, or repetitive, value in an array or range of data
2507       *
2508       * Excel Function:
2509       *        MODE(value1[,value2[, ...]])
2510       *
2511       * @access    public
2512       * @category Statistical Functions
2513       * @param    mixed        $arg,...        Data values
2514       * @return    float
2515       */
2516      public static function MODE()
2517      {
2518          $returnValue = PHPExcel_Calculation_Functions::NA();
2519  
2520          // Loop through arguments
2521          $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
2522  
2523          $mArgs = array();
2524          foreach ($aArgs as $arg) {
2525              // Is it a numeric value?
2526              if ((is_numeric($arg)) && (!is_string($arg))) {
2527                  $mArgs[] = $arg;
2528              }
2529          }
2530  
2531          if (!empty($mArgs)) {
2532              return self::modeCalc($mArgs);
2533          }
2534  
2535          return $returnValue;
2536      }
2537  
2538  
2539      /**
2540       * NEGBINOMDIST
2541       *
2542       * Returns the negative binomial distribution. NEGBINOMDIST returns the probability that
2543       *        there will be number_f failures before the number_s-th success, when the constant
2544       *        probability of a success is probability_s. This function is similar to the binomial
2545       *        distribution, except that the number of successes is fixed, and the number of trials is
2546       *        variable. Like the binomial, trials are assumed to be independent.
2547       *
2548       * @param    float        $failures        Number of Failures
2549       * @param    float        $successes        Threshold number of Successes
2550       * @param    float        $probability    Probability of success on each trial
2551       * @return    float
2552       *
2553       */
2554      public static function NEGBINOMDIST($failures, $successes, $probability)
2555      {
2556          $failures    = floor(PHPExcel_Calculation_Functions::flattenSingleValue($failures));
2557          $successes   = floor(PHPExcel_Calculation_Functions::flattenSingleValue($successes));
2558          $probability = PHPExcel_Calculation_Functions::flattenSingleValue($probability);
2559  
2560          if ((is_numeric($failures)) && (is_numeric($successes)) && (is_numeric($probability))) {
2561              if (($failures < 0) || ($successes < 1)) {
2562                  return PHPExcel_Calculation_Functions::NaN();
2563              } elseif (($probability < 0) || ($probability > 1)) {
2564                  return PHPExcel_Calculation_Functions::NaN();
2565              }
2566              if (PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_GNUMERIC) {
2567                  if (($failures + $successes - 1) <= 0) {
2568                      return PHPExcel_Calculation_Functions::NaN();
2569                  }
2570              }
2571              return (PHPExcel_Calculation_MathTrig::COMBIN($failures + $successes - 1, $successes - 1)) * (pow($probability, $successes)) * (pow(1 - $probability, $failures));
2572          }
2573          return PHPExcel_Calculation_Functions::VALUE();
2574      }
2575  
2576  
2577      /**
2578       * NORMDIST
2579       *
2580       * Returns the normal distribution for the specified mean and standard deviation. This
2581       * function has a very wide range of applications in statistics, including hypothesis
2582       * testing.
2583       *
2584       * @param    float        $value
2585       * @param    float        $mean        Mean Value
2586       * @param    float        $stdDev        Standard Deviation
2587       * @param    boolean        $cumulative
2588       * @return    float
2589       *
2590       */
2591      public static function NORMDIST($value, $mean, $stdDev, $cumulative)
2592      {
2593          $value  = PHPExcel_Calculation_Functions::flattenSingleValue($value);
2594          $mean   = PHPExcel_Calculation_Functions::flattenSingleValue($mean);
2595          $stdDev = PHPExcel_Calculation_Functions::flattenSingleValue($stdDev);
2596  
2597          if ((is_numeric($value)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
2598              if ($stdDev < 0) {
2599                  return PHPExcel_Calculation_Functions::NaN();
2600              }
2601              if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
2602                  if ($cumulative) {
2603                      return 0.5 * (1 + PHPExcel_Calculation_Engineering::erfVal(($value - $mean) / ($stdDev * sqrt(2))));
2604                  } else {
2605                      return (1 / (SQRT2PI * $stdDev)) * exp(0 - (pow($value - $mean, 2) / (2 * ($stdDev * $stdDev))));
2606                  }
2607              }
2608          }
2609          return PHPExcel_Calculation_Functions::VALUE();
2610      }
2611  
2612  
2613      /**
2614       * NORMINV
2615       *
2616       * Returns the inverse of the normal cumulative distribution for the specified mean and standard deviation.
2617       *
2618       * @param    float        $value
2619       * @param    float        $mean        Mean Value
2620       * @param    float        $stdDev        Standard Deviation
2621       * @return    float
2622       *
2623       */
2624      public static function NORMINV($probability, $mean, $stdDev)
2625      {
2626          $probability = PHPExcel_Calculation_Functions::flattenSingleValue($probability);
2627          $mean        = PHPExcel_Calculation_Functions::flattenSingleValue($mean);
2628          $stdDev      = PHPExcel_Calculation_Functions::flattenSingleValue($stdDev);
2629  
2630          if ((is_numeric($probability)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
2631              if (($probability < 0) || ($probability > 1)) {
2632                  return PHPExcel_Calculation_Functions::NaN();
2633              }
2634              if ($stdDev < 0) {
2635                  return PHPExcel_Calculation_Functions::NaN();
2636              }
2637              return (self::inverseNcdf($probability) * $stdDev) + $mean;
2638          }
2639          return PHPExcel_Calculation_Functions::VALUE();
2640      }
2641  
2642  
2643      /**
2644       * NORMSDIST
2645       *
2646       * Returns the standard normal cumulative distribution function. The distribution has
2647       * a mean of 0 (zero) and a standard deviation of one. Use this function in place of a
2648       * table of standard normal curve areas.
2649       *
2650       * @param    float        $value
2651       * @return    float
2652       */
2653      public static function NORMSDIST($value)
2654      {
2655          $value = PHPExcel_Calculation_Functions::flattenSingleValue($value);
2656  
2657          return self::NORMDIST($value, 0, 1, true);
2658      }
2659  
2660  
2661      /**
2662       * NORMSINV
2663       *
2664       * Returns the inverse of the standard normal cumulative distribution
2665       *
2666       * @param    float        $value
2667       * @return    float
2668       */
2669      public static function NORMSINV($value)
2670      {
2671          return self::NORMINV($value, 0, 1);
2672      }
2673  
2674  
2675      /**
2676       * PERCENTILE
2677       *
2678       * Returns the nth percentile of values in a range..
2679       *
2680       * Excel Function:
2681       *        PERCENTILE(value1[,value2[, ...]],entry)
2682       *
2683       * @access    public
2684       * @category Statistical Functions
2685       * @param    mixed        $arg,...        Data values
2686       * @param    float        $entry            Percentile value in the range 0..1, inclusive.
2687       * @return    float
2688       */
2689      public static function PERCENTILE()
2690      {
2691          $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
2692  
2693          // Calculate
2694          $entry = array_pop($aArgs);
2695  
2696          if ((is_numeric($entry)) && (!is_string($entry))) {
2697              if (($entry < 0) || ($entry > 1)) {
2698                  return PHPExcel_Calculation_Functions::NaN();
2699              }
2700              $mArgs = array();
2701              foreach ($aArgs as $arg) {
2702                  // Is it a numeric value?
2703                  if ((is_numeric($arg)) && (!is_string($arg))) {
2704                      $mArgs[] = $arg;
2705                  }
2706              }
2707              $mValueCount = count($mArgs);
2708              if ($mValueCount > 0) {
2709                  sort($mArgs);
2710                  $count = self::COUNT($mArgs);
2711                  $index = $entry * ($count-1);
2712                  $iBase = floor($index);
2713                  if ($index == $iBase) {
2714                      return $mArgs[$index];
2715                  } else {
2716                      $iNext = $iBase + 1;
2717                      $iProportion = $index - $iBase;
2718                      return $mArgs[$iBase] + (($mArgs[$iNext] - $mArgs[$iBase]) * $iProportion) ;
2719                  }
2720              }
2721          }
2722          return PHPExcel_Calculation_Functions::VALUE();
2723      }
2724  
2725  
2726      /**
2727       * PERCENTRANK
2728       *
2729       * Returns the rank of a value in a data set as a percentage of the data set.
2730       *
2731       * @param    array of number        An array of, or a reference to, a list of numbers.
2732       * @param    number                The number whose rank you want to find.
2733       * @param    number                The number of significant digits for the returned percentage value.
2734       * @return    float
2735       */
2736      public static function PERCENTRANK($valueSet, $value, $significance = 3)
2737      {
2738          $valueSet     = PHPExcel_Calculation_Functions::flattenArray($valueSet);
2739          $value        = PHPExcel_Calculation_Functions::flattenSingleValue($value);
2740          $significance = (is_null($significance)) ? 3 : (integer) PHPExcel_Calculation_Functions::flattenSingleValue($significance);
2741  
2742          foreach ($valueSet as $key => $valueEntry) {
2743              if (!is_numeric($valueEntry)) {
2744                  unset($valueSet[$key]);
2745              }
2746          }
2747          sort($valueSet, SORT_NUMERIC);
2748          $valueCount = count($valueSet);
2749          if ($valueCount == 0) {
2750              return PHPExcel_Calculation_Functions::NaN();
2751          }
2752  
2753          $valueAdjustor = $valueCount - 1;
2754          if (($value < $valueSet[0]) || ($value > $valueSet[$valueAdjustor])) {
2755              return PHPExcel_Calculation_Functions::NA();
2756          }
2757  
2758          $pos = array_search($value, $valueSet);
2759          if ($pos === false) {
2760              $pos = 0;
2761              $testValue = $valueSet[0];
2762              while ($testValue < $value) {
2763                  $testValue = $valueSet[++$pos];
2764              }
2765              --$pos;
2766              $pos += (($value - $valueSet[$pos]) / ($testValue - $valueSet[$pos]));
2767          }
2768  
2769          return round($pos / $valueAdjustor, $significance);
2770      }
2771  
2772  
2773      /**
2774       * PERMUT
2775       *
2776       * Returns the number of permutations for a given number of objects that can be
2777       *        selected from number objects. A permutation is any set or subset of objects or
2778       *        events where internal order is significant. Permutations are different from
2779       *        combinations, for which the internal order is not significant. Use this function
2780       *        for lottery-style probability calculations.
2781       *
2782       * @param    int        $numObjs    Number of different objects
2783       * @param    int        $numInSet    Number of objects in each permutation
2784       * @return    int        Number of permutations
2785       */
2786      public static function PERMUT($numObjs, $numInSet)
2787      {
2788          $numObjs  = PHPExcel_Calculation_Functions::flattenSingleValue($numObjs);
2789          $numInSet = PHPExcel_Calculation_Functions::flattenSingleValue($numInSet);
2790  
2791          if ((is_numeric($numObjs)) && (is_numeric($numInSet))) {
2792              $numInSet = floor($numInSet);
2793              if ($numObjs < $numInSet) {
2794                  return PHPExcel_Calculation_Functions::NaN();
2795              }
2796              return round(PHPExcel_Calculation_MathTrig::FACT($numObjs) / PHPExcel_Calculation_MathTrig::FACT($numObjs - $numInSet));
2797          }
2798          return PHPExcel_Calculation_Functions::VALUE();
2799      }
2800  
2801  
2802      /**
2803       * POISSON
2804       *
2805       * Returns the Poisson distribution. A common application of the Poisson distribution
2806       * is predicting the number of events over a specific time, such as the number of
2807       * cars arriving at a toll plaza in 1 minute.
2808       *
2809       * @param    float        $value
2810       * @param    float        $mean        Mean Value
2811       * @param    boolean        $cumulative
2812       * @return    float
2813       *
2814       */
2815      public static function POISSON($value, $mean, $cumulative)
2816      {
2817          $value = PHPExcel_Calculation_Functions::flattenSingleValue($value);
2818          $mean  = PHPExcel_Calculation_Functions::flattenSingleValue($mean);
2819  
2820          if ((is_numeric($value)) && (is_numeric($mean))) {
2821              if (($value < 0) || ($mean <= 0)) {
2822                  return PHPExcel_Calculation_Functions::NaN();
2823              }
2824              if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
2825                  if ($cumulative) {
2826                      $summer = 0;
2827                      for ($i = 0; $i <= floor($value); ++$i) {
2828                          $summer += pow($mean, $i) / PHPExcel_Calculation_MathTrig::FACT($i);
2829                      }
2830                      return exp(0-$mean) * $summer;
2831                  } else {
2832                      return (exp(0-$mean) * pow($mean, $value)) / PHPExcel_Calculation_MathTrig::FACT($value);
2833                  }
2834              }
2835          }
2836          return PHPExcel_Calculation_Functions::VALUE();
2837      }
2838  
2839  
2840      /**
2841       * QUARTILE
2842       *
2843       * Returns the quartile of a data set.
2844       *
2845       * Excel Function:
2846       *        QUARTILE(value1[,value2[, ...]],entry)
2847       *
2848       * @access    public
2849       * @category Statistical Functions
2850       * @param    mixed        $arg,...        Data values
2851       * @param    int            $entry            Quartile value in the range 1..3, inclusive.
2852       * @return    float
2853       */
2854      public static function QUARTILE()
2855      {
2856          $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
2857  
2858          // Calculate
2859          $entry = floor(array_pop($aArgs));
2860  
2861          if ((is_numeric($entry)) && (!is_string($entry))) {
2862              $entry /= 4;
2863              if (($entry < 0) || ($entry > 1)) {
2864                  return PHPExcel_Calculation_Functions::NaN();
2865              }
2866              return self::PERCENTILE($aArgs, $entry);
2867          }
2868          return PHPExcel_Calculation_Functions::VALUE();
2869      }
2870  
2871  
2872      /**
2873       * RANK
2874       *
2875       * Returns the rank of a number in a list of numbers.
2876       *
2877       * @param    number                The number whose rank you want to find.
2878       * @param    array of number        An array of, or a reference to, a list of numbers.
2879       * @param    mixed                Order to sort the values in the value set
2880       * @return    float
2881       */
2882      public static function RANK($value, $valueSet, $order = 0)
2883      {
2884          $value = PHPExcel_Calculation_Functions::flattenSingleValue($value);
2885          $valueSet = PHPExcel_Calculation_Functions::flattenArray($valueSet);
2886          $order = (is_null($order)) ? 0 : (integer) PHPExcel_Calculation_Functions::flattenSingleValue($order);
2887  
2888          foreach ($valueSet as $key => $valueEntry) {
2889              if (!is_numeric($valueEntry)) {
2890                  unset($valueSet[$key]);
2891              }
2892          }
2893  
2894          if ($order == 0) {
2895              rsort($valueSet, SORT_NUMERIC);
2896          } else {
2897              sort($valueSet, SORT_NUMERIC);
2898          }
2899          $pos = array_search($value, $valueSet);
2900          if ($pos === false) {
2901              return PHPExcel_Calculation_Functions::NA();
2902          }
2903  
2904          return ++$pos;
2905      }
2906  
2907  
2908      /**
2909       * RSQ
2910       *
2911       * Returns the square of the Pearson product moment correlation coefficient through data points in known_y's and known_x's.
2912       *
2913       * @param    array of mixed        Data Series Y
2914       * @param    array of mixed        Data Series X
2915       * @return    float
2916       */
2917      public static function RSQ($yValues, $xValues)
2918      {
2919          if (!self::checkTrendArrays($yValues, $xValues)) {
2920              return PHPExcel_Calculation_Functions::VALUE();
2921          }
2922          $yValueCount = count($yValues);
2923          $xValueCount = count($xValues);
2924  
2925          if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
2926              return PHPExcel_Calculation_Functions::NA();
2927          } elseif ($yValueCount == 1) {
2928              return PHPExcel_Calculation_Functions::DIV0();
2929          }
2930  
2931          $bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR, $yValues, $xValues);
2932          return $bestFitLinear->getGoodnessOfFit();
2933      }
2934  
2935  
2936      /**
2937       * SKEW
2938       *
2939       * Returns the skewness of a distribution. Skewness characterizes the degree of asymmetry
2940       * of a distribution around its mean. Positive skewness indicates a distribution with an
2941       * asymmetric tail extending toward more positive values. Negative skewness indicates a
2942       * distribution with an asymmetric tail extending toward more negative values.
2943       *
2944       * @param    array    Data Series
2945       * @return    float
2946       */
2947      public static function SKEW()
2948      {
2949          $aArgs = PHPExcel_Calculation_Functions::flattenArrayIndexed(func_get_args());
2950          $mean = self::AVERAGE($aArgs);
2951          $stdDev = self::STDEV($aArgs);
2952  
2953          $count = $summer = 0;
2954          // Loop through arguments
2955          foreach ($aArgs as $k => $arg) {
2956              if ((is_bool($arg)) &&
2957                  (!PHPExcel_Calculation_Functions::isMatrixValue($k))) {
2958              } else {
2959                  // Is it a numeric value?
2960                  if ((is_numeric($arg)) && (!is_string($arg))) {
2961                      $summer += pow((($arg - $mean) / $stdDev), 3);
2962                      ++$count;
2963                  }
2964              }
2965          }
2966  
2967          if ($count > 2) {
2968              return $summer * ($count / (($count-1) * ($count-2)));
2969          }
2970          return PHPExcel_Calculation_Functions::DIV0();
2971      }
2972  
2973  
2974      /**
2975       * SLOPE
2976       *
2977       * Returns the slope of the linear regression line through data points in known_y's and known_x's.
2978       *
2979       * @param    array of mixed        Data Series Y
2980       * @param    array of mixed        Data Series X
2981       * @return    float
2982       */
2983      public static function SLOPE($yValues, $xValues)
2984      {
2985          if (!self::checkTrendArrays($yValues, $xValues)) {
2986              return PHPExcel_Calculation_Functions::VALUE();
2987          }
2988          $yValueCount = count($yValues);
2989          $xValueCount = count($xValues);
2990  
2991          if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
2992              return PHPExcel_Calculation_Functions::NA();
2993          } elseif ($yValueCount == 1) {
2994              return PHPExcel_Calculation_Functions::DIV0();
2995          }
2996  
2997          $bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR, $yValues, $xValues);
2998          return $bestFitLinear->getSlope();
2999      }
3000  
3001  
3002      /**
3003       * SMALL
3004       *
3005       * Returns the nth smallest value in a data set. You can use this function to
3006       *        select a value based on its relative standing.
3007       *
3008       * Excel Function:
3009       *        SMALL(value1[,value2[, ...]],entry)
3010       *
3011       * @access    public
3012       * @category Statistical Functions
3013       * @param    mixed        $arg,...        Data values
3014       * @param    int            $entry            Position (ordered from the smallest) in the array or range of data to return
3015       * @return    float
3016       */
3017      public static function SMALL()
3018      {
3019          $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
3020  
3021          // Calculate
3022          $entry = array_pop($aArgs);
3023  
3024          if ((is_numeric($entry)) && (!is_string($entry))) {
3025              $mArgs = array();
3026              foreach ($aArgs as $arg) {
3027                  // Is it a numeric value?
3028                  if ((is_numeric($arg)) && (!is_string($arg))) {
3029                      $mArgs[] = $arg;
3030                  }
3031              }
3032              $count = self::COUNT($mArgs);
3033              $entry = floor(--$entry);
3034              if (($entry < 0) || ($entry >= $count) || ($count == 0)) {
3035                  return PHPExcel_Calculation_Functions::NaN();
3036              }
3037              sort($mArgs);
3038              return $mArgs[$entry];
3039          }
3040          return PHPExcel_Calculation_Functions::VALUE();
3041      }
3042  
3043  
3044      /**
3045       * STANDARDIZE
3046       *
3047       * Returns a normalized value from a distribution characterized by mean and standard_dev.
3048       *
3049       * @param    float    $value        Value to normalize
3050       * @param    float    $mean        Mean Value
3051       * @param    float    $stdDev        Standard Deviation
3052       * @return    float    Standardized value
3053       */
3054      public static function STANDARDIZE($value, $mean, $stdDev)
3055      {
3056          $value  = PHPExcel_Calculation_Functions::flattenSingleValue($value);
3057          $mean   = PHPExcel_Calculation_Functions::flattenSingleValue($mean);
3058          $stdDev = PHPExcel_Calculation_Functions::flattenSingleValue($stdDev);
3059  
3060          if ((is_numeric($value)) && (is_numeric($mean)) && (is_numeric($stdDev))) {
3061              if ($stdDev <= 0) {
3062                  return PHPExcel_Calculation_Functions::NaN();
3063              }
3064              return ($value - $mean) / $stdDev ;
3065          }
3066          return PHPExcel_Calculation_Functions::VALUE();
3067      }
3068  
3069  
3070      /**
3071       * STDEV
3072       *
3073       * Estimates standard deviation based on a sample. The standard deviation is a measure of how
3074       *        widely values are dispersed from the average value (the mean).
3075       *
3076       * Excel Function:
3077       *        STDEV(value1[,value2[, ...]])
3078       *
3079       * @access    public
3080       * @category Statistical Functions
3081       * @param    mixed        $arg,...        Data values
3082       * @return    float
3083       */
3084      public static function STDEV()
3085      {
3086          $aArgs = PHPExcel_Calculation_Functions::flattenArrayIndexed(func_get_args());
3087  
3088          // Return value
3089          $returnValue = null;
3090  
3091          $aMean = self::AVERAGE($aArgs);
3092          if (!is_null($aMean)) {
3093              $aCount = -1;
3094              foreach ($aArgs as $k => $arg) {
3095                  if ((is_bool($arg)) &&
3096                      ((!PHPExcel_Calculation_Functions::isCellValue($k)) || (PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE))) {
3097                      $arg = (integer) $arg;
3098                  }
3099                  // Is it a numeric value?
3100                  if ((is_numeric($arg)) && (!is_string($arg))) {
3101                      if (is_null($returnValue)) {
3102                          $returnValue = pow(($arg - $aMean), 2);
3103                      } else {
3104                          $returnValue += pow(($arg - $aMean), 2);
3105                      }
3106                      ++$aCount;
3107                  }
3108              }
3109  
3110              // Return
3111              if (($aCount > 0) && ($returnValue >= 0)) {
3112                  return sqrt($returnValue / $aCount);
3113              }
3114          }
3115          return PHPExcel_Calculation_Functions::DIV0();
3116      }
3117  
3118  
3119      /**
3120       * STDEVA
3121       *
3122       * Estimates standard deviation based on a sample, including numbers, text, and logical values
3123       *
3124       * Excel Function:
3125       *        STDEVA(value1[,value2[, ...]])
3126       *
3127       * @access    public
3128       * @category Statistical Functions
3129       * @param    mixed        $arg,...        Data values
3130       * @return    float
3131       */
3132      public static function STDEVA()
3133      {
3134          $aArgs = PHPExcel_Calculation_Functions::flattenArrayIndexed(func_get_args());
3135  
3136          $returnValue = null;
3137  
3138          $aMean = self::AVERAGEA($aArgs);
3139          if (!is_null($aMean)) {
3140              $aCount = -1;
3141              foreach ($aArgs as $k => $arg) {
3142                  if ((is_bool($arg)) &&
3143                      (!PHPExcel_Calculation_Functions::isMatrixValue($k))) {
3144                  } else {
3145                      // Is it a numeric value?
3146                      if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) & ($arg != '')))) {
3147                          if (is_bool($arg)) {
3148                              $arg = (integer) $arg;
3149                          } elseif (is_string($arg)) {
3150                              $arg = 0;
3151                          }
3152                          if (is_null($returnValue)) {
3153                              $returnValue = pow(($arg - $aMean), 2);
3154                          } else {
3155                              $returnValue += pow(($arg - $aMean), 2);
3156                          }
3157                          ++$aCount;
3158                      }
3159                  }
3160              }
3161  
3162              if (($aCount > 0) && ($returnValue >= 0)) {
3163                  return sqrt($returnValue / $aCount);
3164              }
3165          }
3166          return PHPExcel_Calculation_Functions::DIV0();
3167      }
3168  
3169  
3170      /**
3171       * STDEVP
3172       *
3173       * Calculates standard deviation based on the entire population
3174       *
3175       * Excel Function:
3176       *        STDEVP(value1[,value2[, ...]])
3177       *
3178       * @access    public
3179       * @category Statistical Functions
3180       * @param    mixed        $arg,...        Data values
3181       * @return    float
3182       */
3183      public static function STDEVP()
3184      {
3185          $aArgs = PHPExcel_Calculation_Functions::flattenArrayIndexed(func_get_args());
3186  
3187          $returnValue = null;
3188  
3189          $aMean = self::AVERAGE($aArgs);
3190          if (!is_null($aMean)) {
3191              $aCount = 0;
3192              foreach ($aArgs as $k => $arg) {
3193                  if ((is_bool($arg)) &&
3194                      ((!PHPExcel_Calculation_Functions::isCellValue($k)) || (PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE))) {
3195                      $arg = (integer) $arg;
3196                  }
3197                  // Is it a numeric value?
3198                  if ((is_numeric($arg)) && (!is_string($arg))) {
3199                      if (is_null($returnValue)) {
3200                          $returnValue = pow(($arg - $aMean), 2);
3201                      } else {
3202                          $returnValue += pow(($arg - $aMean), 2);
3203                      }
3204                      ++$aCount;
3205                  }
3206              }
3207  
3208              if (($aCount > 0) && ($returnValue >= 0)) {
3209                  return sqrt($returnValue / $aCount);
3210              }
3211          }
3212          return PHPExcel_Calculation_Functions::DIV0();
3213      }
3214  
3215  
3216      /**
3217       * STDEVPA
3218       *
3219       * Calculates standard deviation based on the entire population, including numbers, text, and logical values
3220       *
3221       * Excel Function:
3222       *        STDEVPA(value1[,value2[, ...]])
3223       *
3224       * @access    public
3225       * @category Statistical Functions
3226       * @param    mixed        $arg,...        Data values
3227       * @return    float
3228       */
3229      public static function STDEVPA()
3230      {
3231          $aArgs = PHPExcel_Calculation_Functions::flattenArrayIndexed(func_get_args());
3232  
3233          $returnValue = null;
3234  
3235          $aMean = self::AVERAGEA($aArgs);
3236          if (!is_null($aMean)) {
3237              $aCount = 0;
3238              foreach ($aArgs as $k => $arg) {
3239                  if ((is_bool($arg)) &&
3240                      (!PHPExcel_Calculation_Functions::isMatrixValue($k))) {
3241                  } else {
3242                      // Is it a numeric value?
3243                      if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) & ($arg != '')))) {
3244                          if (is_bool($arg)) {
3245                              $arg = (integer) $arg;
3246                          } elseif (is_string($arg)) {
3247                              $arg = 0;
3248                          }
3249                          if (is_null($returnValue)) {
3250                              $returnValue = pow(($arg - $aMean), 2);
3251                          } else {
3252                              $returnValue += pow(($arg - $aMean), 2);
3253                          }
3254                          ++$aCount;
3255                      }
3256                  }
3257              }
3258  
3259              if (($aCount > 0) && ($returnValue >= 0)) {
3260                  return sqrt($returnValue / $aCount);
3261              }
3262          }
3263          return PHPExcel_Calculation_Functions::DIV0();
3264      }
3265  
3266  
3267      /**
3268       * STEYX
3269       *
3270       * Returns the standard error of the predicted y-value for each x in the regression.
3271       *
3272       * @param    array of mixed        Data Series Y
3273       * @param    array of mixed        Data Series X
3274       * @return    float
3275       */
3276      public static function STEYX($yValues, $xValues)
3277      {
3278          if (!self::checkTrendArrays($yValues, $xValues)) {
3279              return PHPExcel_Calculation_Functions::VALUE();
3280          }
3281          $yValueCount = count($yValues);
3282          $xValueCount = count($xValues);
3283  
3284          if (($yValueCount == 0) || ($yValueCount != $xValueCount)) {
3285              return PHPExcel_Calculation_Functions::NA();
3286          } elseif ($yValueCount == 1) {
3287              return PHPExcel_Calculation_Functions::DIV0();
3288          }
3289  
3290          $bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR, $yValues, $xValues);
3291          return $bestFitLinear->getStdevOfResiduals();
3292      }
3293  
3294  
3295      /**
3296       * TDIST
3297       *
3298       * Returns the probability of Student's T distribution.
3299       *
3300       * @param    float        $value            Value for the function
3301       * @param    float        $degrees        degrees of freedom
3302       * @param    float        $tails            number of tails (1 or 2)
3303       * @return    float
3304       */
3305      public static function TDIST($value, $degrees, $tails)
3306      {
3307          $value        = PHPExcel_Calculation_Functions::flattenSingleValue($value);
3308          $degrees    = floor(PHPExcel_Calculation_Functions::flattenSingleValue($degrees));
3309          $tails        = floor(PHPExcel_Calculation_Functions::flattenSingleValue($tails));
3310  
3311          if ((is_numeric($value)) && (is_numeric($degrees)) && (is_numeric($tails))) {
3312              if (($value < 0) || ($degrees < 1) || ($tails < 1) || ($tails > 2)) {
3313                  return PHPExcel_Calculation_Functions::NaN();
3314              }
3315              //    tdist, which finds the probability that corresponds to a given value
3316              //    of t with k degrees of freedom. This algorithm is translated from a
3317              //    pascal function on p81 of "Statistical Computing in Pascal" by D
3318              //    Cooke, A H Craven & G M Clark (1985: Edward Arnold (Pubs.) Ltd:
3319              //    London). The above Pascal algorithm is itself a translation of the
3320              //    fortran algoritm "AS 3" by B E Cooper of the Atlas Computer
3321              //    Laboratory as reported in (among other places) "Applied Statistics
3322              //    Algorithms", editied by P Griffiths and I D Hill (1985; Ellis
3323              //    Horwood Ltd.; W. Sussex, England).
3324              $tterm = $degrees;
3325              $ttheta = atan2($value, sqrt($tterm));
3326              $tc = cos($ttheta);
3327              $ts = sin($ttheta);
3328              $tsum = 0;
3329  
3330              if (($degrees % 2) == 1) {
3331                  $ti = 3;
3332                  $tterm = $tc;
3333              } else {
3334                  $ti = 2;
3335                  $tterm = 1;
3336              }
3337  
3338              $tsum = $tterm;
3339              while ($ti < $degrees) {
3340                  $tterm *= $tc * $tc * ($ti - 1) / $ti;
3341                  $tsum += $tterm;
3342                  $ti += 2;
3343              }
3344              $tsum *= $ts;
3345              if (($degrees % 2) == 1) {
3346                  $tsum = M_2DIVPI * ($tsum + $ttheta);
3347              }
3348              $tValue = 0.5 * (1 + $tsum);
3349              if ($tails == 1) {
3350                  return 1 - abs($tValue);
3351              } else {
3352                  return 1 - abs((1 - $tValue) - $tValue);
3353              }
3354          }
3355          return PHPExcel_Calculation_Functions::VALUE();
3356      }
3357  
3358  
3359      /**
3360       * TINV
3361       *
3362       * Returns the one-tailed probability of the chi-squared distribution.
3363       *
3364       * @param    float        $probability    Probability for the function
3365       * @param    float        $degrees        degrees of freedom
3366       * @return    float
3367       */
3368      public static function TINV($probability, $degrees)
3369      {
3370          $probability = PHPExcel_Calculation_Functions::flattenSingleValue($probability);
3371          $degrees     = floor(PHPExcel_Calculation_Functions::flattenSingleValue($degrees));
3372  
3373          if ((is_numeric($probability)) && (is_numeric($degrees))) {
3374              $xLo = 100;
3375              $xHi = 0;
3376  
3377              $x = $xNew = 1;
3378              $dx    = 1;
3379              $i = 0;
3380  
3381              while ((abs($dx) > PRECISION) && ($i++ < MAX_ITERATIONS)) {
3382                  // Apply Newton-Raphson step
3383                  $result = self::TDIST($x, $degrees, 2);
3384                  $error = $result - $probability;
3385                  if ($error == 0.0) {
3386                      $dx = 0;
3387                  } elseif ($error < 0.0) {
3388                      $xLo = $x;
3389                  } else {
3390                      $xHi = $x;
3391                  }
3392                  // Avoid division by zero
3393                  if ($result != 0.0) {
3394                      $dx = $error / $result;
3395                      $xNew = $x - $dx;
3396                  }
3397                  // If the NR fails to converge (which for example may be the
3398                  // case if the initial guess is too rough) we apply a bisection
3399                  // step to determine a more narrow interval around the root.
3400                  if (($xNew < $xLo) || ($xNew > $xHi) || ($result == 0.0)) {
3401                      $xNew = ($xLo + $xHi) / 2;
3402                      $dx = $xNew - $x;
3403                  }
3404                  $x = $xNew;
3405              }
3406              if ($i == MAX_ITERATIONS) {
3407                  return PHPExcel_Calculation_Functions::NA();
3408              }
3409              return round($x, 12);
3410          }
3411          return PHPExcel_Calculation_Functions::VALUE();
3412      }
3413  
3414  
3415      /**
3416       * TREND
3417       *
3418       * Returns values along a linear trend
3419       *
3420       * @param    array of mixed        Data Series Y
3421       * @param    array of mixed        Data Series X
3422       * @param    array of mixed        Values of X for which we want to find Y
3423       * @param    boolean                A logical value specifying whether to force the intersect to equal 0.
3424       * @return    array of float
3425       */
3426      public static function TREND($yValues, $xValues = array(), $newValues = array(), $const = true)
3427      {
3428          $yValues = PHPExcel_Calculation_Functions::flattenArray($yValues);
3429          $xValues = PHPExcel_Calculation_Functions::flattenArray($xValues);
3430          $newValues = PHPExcel_Calculation_Functions::flattenArray($newValues);
3431          $const = (is_null($const)) ? true : (boolean) PHPExcel_Calculation_Functions::flattenSingleValue($const);
3432  
3433          $bestFitLinear = trendClass::calculate(trendClass::TREND_LINEAR, $yValues, $xValues, $const);
3434          if (empty($newValues)) {
3435              $newValues = $bestFitLinear->getXValues();
3436          }
3437  
3438          $returnArray = array();
3439          foreach ($newValues as $xValue) {
3440              $returnArray[0][] = $bestFitLinear->getValueOfYForX($xValue);
3441          }
3442  
3443          return $returnArray;
3444      }
3445  
3446  
3447      /**
3448       * TRIMMEAN
3449       *
3450       * Returns the mean of the interior of a data set. TRIMMEAN calculates the mean
3451       *        taken by excluding a percentage of data points from the top and bottom tails
3452       *        of a data set.
3453       *
3454       * Excel Function:
3455       *        TRIMEAN(value1[,value2[, ...]], $discard)
3456       *
3457       * @access    public
3458       * @category Statistical Functions
3459       * @param    mixed        $arg,...        Data values
3460       * @param    float        $discard        Percentage to discard
3461       * @return    float
3462       */
3463      public static function TRIMMEAN()
3464      {
3465          $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
3466  
3467          // Calculate
3468          $percent = array_pop($aArgs);
3469  
3470          if ((is_numeric($percent)) && (!is_string($percent))) {
3471              if (($percent < 0) || ($percent > 1)) {
3472                  return PHPExcel_Calculation_Functions::NaN();
3473              }
3474              $mArgs = array();
3475              foreach ($aArgs as $arg) {
3476                  // Is it a numeric value?
3477                  if ((is_numeric($arg)) && (!is_string($arg))) {
3478                      $mArgs[] = $arg;
3479                  }
3480              }
3481              $discard = floor(self::COUNT($mArgs) * $percent / 2);
3482              sort($mArgs);
3483              for ($i=0; $i < $discard; ++$i) {
3484                  array_pop($mArgs);
3485                  array_shift($mArgs);
3486              }
3487              return self::AVERAGE($mArgs);
3488          }
3489          return PHPExcel_Calculation_Functions::VALUE();
3490      }
3491  
3492  
3493      /**
3494       * VARFunc
3495       *
3496       * Estimates variance based on a sample.
3497       *
3498       * Excel Function:
3499       *        VAR(value1[,value2[, ...]])
3500       *
3501       * @access    public
3502       * @category Statistical Functions
3503       * @param    mixed        $arg,...        Data values
3504       * @return    float
3505       */
3506      public static function VARFunc()
3507      {
3508          $returnValue = PHPExcel_Calculation_Functions::DIV0();
3509  
3510          $summerA = $summerB = 0;
3511  
3512          // Loop through arguments
3513          $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
3514          $aCount = 0;
3515          foreach ($aArgs as $arg) {
3516              if (is_bool($arg)) {
3517                  $arg = (integer) $arg;
3518              }
3519              // Is it a numeric value?
3520              if ((is_numeric($arg)) && (!is_string($arg))) {
3521                  $summerA += ($arg * $arg);
3522                  $summerB += $arg;
3523                  ++$aCount;
3524              }
3525          }
3526  
3527          if ($aCount > 1) {
3528              $summerA *= $aCount;
3529              $summerB *= $summerB;
3530              $returnValue = ($summerA - $summerB) / ($aCount * ($aCount - 1));
3531          }
3532          return $returnValue;
3533      }
3534  
3535  
3536      /**
3537       * VARA
3538       *
3539       * Estimates variance based on a sample, including numbers, text, and logical values
3540       *
3541       * Excel Function:
3542       *        VARA(value1[,value2[, ...]])
3543       *
3544       * @access    public
3545       * @category Statistical Functions
3546       * @param    mixed        $arg,...        Data values
3547       * @return    float
3548       */
3549      public static function VARA()
3550      {
3551          $returnValue = PHPExcel_Calculation_Functions::DIV0();
3552  
3553          $summerA = $summerB = 0;
3554  
3555          // Loop through arguments
3556          $aArgs = PHPExcel_Calculation_Functions::flattenArrayIndexed(func_get_args());
3557          $aCount = 0;
3558          foreach ($aArgs as $k => $arg) {
3559              if ((is_string($arg)) &&
3560                  (PHPExcel_Calculation_Functions::isValue($k))) {
3561                  return PHPExcel_Calculation_Functions::VALUE();
3562              } elseif ((is_string($arg)) &&
3563                  (!PHPExcel_Calculation_Functions::isMatrixValue($k))) {
3564              } else {
3565                  // Is it a numeric value?
3566                  if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) & ($arg != '')))) {
3567                      if (is_bool($arg)) {
3568                          $arg = (integer) $arg;
3569                      } elseif (is_string($arg)) {
3570                          $arg = 0;
3571                      }
3572                      $summerA += ($arg * $arg);
3573                      $summerB += $arg;
3574                      ++$aCount;
3575                  }
3576              }
3577          }
3578  
3579          if ($aCount > 1) {
3580              $summerA *= $aCount;
3581              $summerB *= $summerB;
3582              $returnValue = ($summerA - $summerB) / ($aCount * ($aCount - 1));
3583          }
3584          return $returnValue;
3585      }
3586  
3587  
3588      /**
3589       * VARP
3590       *
3591       * Calculates variance based on the entire population
3592       *
3593       * Excel Function:
3594       *        VARP(value1[,value2[, ...]])
3595       *
3596       * @access    public
3597       * @category Statistical Functions
3598       * @param    mixed        $arg,...        Data values
3599       * @return    float
3600       */
3601      public static function VARP()
3602      {
3603          // Return value
3604          $returnValue = PHPExcel_Calculation_Functions::DIV0();
3605  
3606          $summerA = $summerB = 0;
3607  
3608          // Loop through arguments
3609          $aArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
3610          $aCount = 0;
3611          foreach ($aArgs as $arg) {
3612              if (is_bool($arg)) {
3613                  $arg = (integer) $arg;
3614              }
3615              // Is it a numeric value?
3616              if ((is_numeric($arg)) && (!is_string($arg))) {
3617                  $summerA += ($arg * $arg);
3618                  $summerB += $arg;
3619                  ++$aCount;
3620              }
3621          }
3622  
3623          if ($aCount > 0) {
3624              $summerA *= $aCount;
3625              $summerB *= $summerB;
3626              $returnValue = ($summerA - $summerB) / ($aCount * $aCount);
3627          }
3628          return $returnValue;
3629      }
3630  
3631  
3632      /**
3633       * VARPA
3634       *
3635       * Calculates variance based on the entire population, including numbers, text, and logical values
3636       *
3637       * Excel Function:
3638       *        VARPA(value1[,value2[, ...]])
3639       *
3640       * @access    public
3641       * @category Statistical Functions
3642       * @param    mixed        $arg,...        Data values
3643       * @return    float
3644       */
3645      public static function VARPA()
3646      {
3647          $returnValue = PHPExcel_Calculation_Functions::DIV0();
3648  
3649          $summerA = $summerB = 0;
3650  
3651          // Loop through arguments
3652          $aArgs = PHPExcel_Calculation_Functions::flattenArrayIndexed(func_get_args());
3653          $aCount = 0;
3654          foreach ($aArgs as $k => $arg) {
3655              if ((is_string($arg)) &&
3656                  (PHPExcel_Calculation_Functions::isValue($k))) {
3657                  return PHPExcel_Calculation_Functions::VALUE();
3658              } elseif ((is_string($arg)) &&
3659                  (!PHPExcel_Calculation_Functions::isMatrixValue($k))) {
3660              } else {
3661                  // Is it a numeric value?
3662                  if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) & ($arg != '')))) {
3663                      if (is_bool($arg)) {
3664                          $arg = (integer) $arg;
3665                      } elseif (is_string($arg)) {
3666                          $arg = 0;
3667                      }
3668                      $summerA += ($arg * $arg);
3669                      $summerB += $arg;
3670                      ++$aCount;
3671                  }
3672              }
3673          }
3674  
3675          if ($aCount > 0) {
3676              $summerA *= $aCount;
3677              $summerB *= $summerB;
3678              $returnValue = ($summerA - $summerB) / ($aCount * $aCount);
3679          }
3680          return $returnValue;
3681      }
3682  
3683  
3684      /**
3685       * WEIBULL
3686       *
3687       * Returns the Weibull distribution. Use this distribution in reliability
3688       * analysis, such as calculating a device's mean time to failure.
3689       *
3690       * @param    float        $value
3691       * @param    float        $alpha        Alpha Parameter
3692       * @param    float        $beta        Beta Parameter
3693       * @param    boolean        $cumulative
3694       * @return    float
3695       *
3696       */
3697      public static function WEIBULL($value, $alpha, $beta, $cumulative)
3698      {
3699          $value = PHPExcel_Calculation_Functions::flattenSingleValue($value);
3700          $alpha = PHPExcel_Calculation_Functions::flattenSingleValue($alpha);
3701          $beta  = PHPExcel_Calculation_Functions::flattenSingleValue($beta);
3702  
3703          if ((is_numeric($value)) && (is_numeric($alpha)) && (is_numeric($beta))) {
3704              if (($value < 0) || ($alpha <= 0) || ($beta <= 0)) {
3705                  return PHPExcel_Calculation_Functions::NaN();
3706              }
3707              if ((is_numeric($cumulative)) || (is_bool($cumulative))) {
3708                  if ($cumulative) {
3709                      return 1 - exp(0 - pow($value / $beta, $alpha));
3710                  } else {
3711                      return ($alpha / pow($beta, $alpha)) * pow($value, $alpha - 1) * exp(0 - pow($value / $beta, $alpha));
3712                  }
3713              }
3714          }
3715          return PHPExcel_Calculation_Functions::VALUE();
3716      }
3717  
3718  
3719      /**
3720       * ZTEST
3721       *
3722       * Returns the Weibull distribution. Use this distribution in reliability
3723       * analysis, such as calculating a device's mean time to failure.
3724       *
3725       * @param    float        $dataSet
3726       * @param    float        $m0        Alpha Parameter
3727       * @param    float        $sigma    Beta Parameter
3728       * @param    boolean        $cumulative
3729       * @return    float
3730       *
3731       */
3732      public static function ZTEST($dataSet, $m0, $sigma = null)
3733      {
3734          $dataSet = PHPExcel_Calculation_Functions::flattenArrayIndexed($dataSet);
3735          $m0      = PHPExcel_Calculation_Functions::flattenSingleValue($m0);
3736          $sigma   = PHPExcel_Calculation_Functions::flattenSingleValue($sigma);
3737  
3738          if (is_null($sigma)) {
3739              $sigma = self::STDEV($dataSet);
3740          }
3741          $n = count($dataSet);
3742  
3743          return 1 - self::NORMSDIST((self::AVERAGE($dataSet) - $m0) / ($sigma / SQRT($n)));
3744      }
3745  }


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1