Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.2.x will end 22 April 2024 (12 months).
  • Bug fixes for security issues in 4.2.x will end 7 October 2024 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.1.x is supported too.

Differences Between: [Versions 400 and 402] [Versions 401 and 402]

   1  <?php
   2  
   3  namespace PhpOffice\PhpSpreadsheet\Calculation\Statistical\Distributions;
   4  
   5  use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
   6  use PhpOffice\PhpSpreadsheet\Calculation\Engineering;
   7  use PhpOffice\PhpSpreadsheet\Calculation\Exception;
   8  use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
   9  
  10  class Normal
  11  {
  12      use ArrayEnabled;
  13  
  14      public const SQRT2PI = 2.5066282746310005024157652848110452530069867406099;
  15  
  16      /**
  17       * NORMDIST.
  18       *
  19       * Returns the normal distribution for the specified mean and standard deviation. This
  20       * function has a very wide range of applications in statistics, including hypothesis
  21       * testing.
  22       *
  23       * @param mixed $value Float value for which we want the probability
  24       *                      Or can be an array of values
  25       * @param mixed $mean Mean value as a float
  26       *                      Or can be an array of values
  27       * @param mixed $stdDev Standard Deviation as a float
  28       *                      Or can be an array of values
  29       * @param mixed $cumulative Boolean value indicating if we want the cdf (true) or the pdf (false)
  30       *                      Or can be an array of values
  31       *
  32       * @return array|float|string The result, or a string containing an error
  33       *         If an array of numbers is passed as an argument, then the returned result will also be an array
  34       *            with the same dimensions
  35       */
  36      public static function distribution($value, $mean, $stdDev, $cumulative)
  37      {
  38          if (is_array($value) || is_array($mean) || is_array($stdDev) || is_array($cumulative)) {
  39              return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $mean, $stdDev, $cumulative);
  40          }
  41  
  42          try {
  43              $value = DistributionValidations::validateFloat($value);
  44              $mean = DistributionValidations::validateFloat($mean);
  45              $stdDev = DistributionValidations::validateFloat($stdDev);
  46              $cumulative = DistributionValidations::validateBool($cumulative);
  47          } catch (Exception $e) {
  48              return $e->getMessage();
  49          }
  50  
  51          if ($stdDev < 0) {
  52              return ExcelError::NAN();
  53          }
  54  
  55          if ($cumulative) {
  56              return 0.5 * (1 + Engineering\Erf::erfValue(($value - $mean) / ($stdDev * sqrt(2))));
  57          }
  58  
  59          return (1 / (self::SQRT2PI * $stdDev)) * exp(0 - (($value - $mean) ** 2 / (2 * ($stdDev * $stdDev))));
  60      }
  61  
  62      /**
  63       * NORMINV.
  64       *
  65       * Returns the inverse of the normal cumulative distribution for the specified mean and standard deviation.
  66       *
  67       * @param mixed $probability Float probability for which we want the value
  68       *                      Or can be an array of values
  69       * @param mixed $mean Mean Value as a float
  70       *                      Or can be an array of values
  71       * @param mixed $stdDev Standard Deviation as a float
  72       *                      Or can be an array of values
  73       *
  74       * @return array|float|string The result, or a string containing an error
  75       *         If an array of numbers is passed as an argument, then the returned result will also be an array
  76       *            with the same dimensions
  77       */
  78      public static function inverse($probability, $mean, $stdDev)
  79      {
  80          if (is_array($probability) || is_array($mean) || is_array($stdDev)) {
  81              return self::evaluateArrayArguments([self::class, __FUNCTION__], $probability, $mean, $stdDev);
  82          }
  83  
  84          try {
  85              $probability = DistributionValidations::validateProbability($probability);
  86              $mean = DistributionValidations::validateFloat($mean);
  87              $stdDev = DistributionValidations::validateFloat($stdDev);
  88          } catch (Exception $e) {
  89              return $e->getMessage();
  90          }
  91  
  92          if ($stdDev < 0) {
  93              return ExcelError::NAN();
  94          }
  95  
  96          return (self::inverseNcdf($probability) * $stdDev) + $mean;
  97      }
  98  
  99      /*
 100       *                                inverse_ncdf.php
 101       *                            -------------------
 102       *    begin                : Friday, January 16, 2004
 103       *    copyright            : (C) 2004 Michael Nickerson
 104       *    email                : nickersonm@yahoo.com
 105       *
 106       */
 107      private static function inverseNcdf(float $p): float
 108      {
 109          //    Inverse ncdf approximation by Peter J. Acklam, implementation adapted to
 110          //    PHP by Michael Nickerson, using Dr. Thomas Ziegler's C implementation as
 111          //    a guide. http://home.online.no/~pjacklam/notes/invnorm/index.html
 112          //    I have not checked the accuracy of this implementation. Be aware that PHP
 113          //    will truncate the coeficcients to 14 digits.
 114  
 115          //    You have permission to use and distribute this function freely for
 116          //    whatever purpose you want, but please show common courtesy and give credit
 117          //    where credit is due.
 118  
 119          //    Input paramater is $p - probability - where 0 < p < 1.
 120  
 121          //    Coefficients in rational approximations
 122          static $a = [
 123              1 => -3.969683028665376e+01,
 124              2 => 2.209460984245205e+02,
 125              3 => -2.759285104469687e+02,
 126              4 => 1.383577518672690e+02,
 127              5 => -3.066479806614716e+01,
 128              6 => 2.506628277459239e+00,
 129          ];
 130  
 131          static $b = [
 132              1 => -5.447609879822406e+01,
 133              2 => 1.615858368580409e+02,
 134              3 => -1.556989798598866e+02,
 135              4 => 6.680131188771972e+01,
 136              5 => -1.328068155288572e+01,
 137          ];
 138  
 139          static $c = [
 140              1 => -7.784894002430293e-03,
 141              2 => -3.223964580411365e-01,
 142              3 => -2.400758277161838e+00,
 143              4 => -2.549732539343734e+00,
 144              5 => 4.374664141464968e+00,
 145              6 => 2.938163982698783e+00,
 146          ];
 147  
 148          static $d = [
 149              1 => 7.784695709041462e-03,
 150              2 => 3.224671290700398e-01,
 151              3 => 2.445134137142996e+00,
 152              4 => 3.754408661907416e+00,
 153          ];
 154  
 155          //    Define lower and upper region break-points.
 156          $p_low = 0.02425; //Use lower region approx. below this
 157          $p_high = 1 - $p_low; //Use upper region approx. above this
 158  
 159          if (0 < $p && $p < $p_low) {
 160              //    Rational approximation for lower region.
 161              $q = sqrt(-2 * log($p));
 162  
 163              return ((((($c[1] * $q + $c[2]) * $q + $c[3]) * $q + $c[4]) * $q + $c[5]) * $q + $c[6]) /
 164                  (((($d[1] * $q + $d[2]) * $q + $d[3]) * $q + $d[4]) * $q + 1);
 165          } elseif ($p_high < $p && $p < 1) {
 166              //    Rational approximation for upper region.
 167              $q = sqrt(-2 * log(1 - $p));
 168  
 169              return -((((($c[1] * $q + $c[2]) * $q + $c[3]) * $q + $c[4]) * $q + $c[5]) * $q + $c[6]) /
 170                  (((($d[1] * $q + $d[2]) * $q + $d[3]) * $q + $d[4]) * $q + 1);
 171          }
 172  
 173          //    Rational approximation for central region.
 174          $q = $p - 0.5;
 175          $r = $q * $q;
 176  
 177          return ((((($a[1] * $r + $a[2]) * $r + $a[3]) * $r + $a[4]) * $r + $a[5]) * $r + $a[6]) * $q /
 178                  ((((($b[1] * $r + $b[2]) * $r + $b[3]) * $r + $b[4]) * $r + $b[5]) * $r + 1);
 179      }
 180  }