diff --git a/src/PhpSpreadsheet/Calculation/Calculation.php b/src/PhpSpreadsheet/Calculation/Calculation.php index c5dbaa53..80a6fbcc 100644 --- a/src/PhpSpreadsheet/Calculation/Calculation.php +++ b/src/PhpSpreadsheet/Calculation/Calculation.php @@ -328,17 +328,17 @@ class Calculation ], 'AVEDEV' => [ 'category' => Category::CATEGORY_STATISTICAL, - 'functionCall' => [Statistical\Averages::class, 'AVEDEV'], + 'functionCall' => [Statistical\Averages::class, 'averageDeviations'], 'argumentCount' => '1+', ], 'AVERAGE' => [ 'category' => Category::CATEGORY_STATISTICAL, - 'functionCall' => [Statistical\Averages::class, 'AVERAGE'], + 'functionCall' => [Statistical\Averages::class, 'average'], 'argumentCount' => '1+', ], 'AVERAGEA' => [ 'category' => Category::CATEGORY_STATISTICAL, - 'functionCall' => [Statistical\Averages::class, 'AVERAGEA'], + 'functionCall' => [Statistical\Averages::class, 'averageA'], 'argumentCount' => '1+', ], 'AVERAGEIF' => [ @@ -383,7 +383,7 @@ class Calculation ], 'BETADIST' => [ 'category' => Category::CATEGORY_STATISTICAL, - 'functionCall' => [Statistical::class, 'BETADIST'], + 'functionCall' => [Statistical\Distributions\Beta::class, 'distribution'], 'argumentCount' => '3-5', ], 'BETA.DIST' => [ @@ -393,12 +393,12 @@ class Calculation ], 'BETAINV' => [ 'category' => Category::CATEGORY_STATISTICAL, - 'functionCall' => [Statistical::class, 'BETAINV'], + 'functionCall' => [Statistical\Distributions\Beta::class, 'inverse'], 'argumentCount' => '3-5', ], 'BETA.INV' => [ 'category' => Category::CATEGORY_STATISTICAL, - 'functionCall' => [Statistical::class, 'BETAINV'], + 'functionCall' => [Statistical\Distributions\Beta::class, 'inverse'], 'argumentCount' => '3-5', ], 'BIN2DEC' => [ @@ -488,7 +488,7 @@ class Calculation ], 'CHIDIST' => [ 'category' => Category::CATEGORY_STATISTICAL, - 'functionCall' => [Statistical::class, 'CHIDIST'], + 'functionCall' => [Statistical\Distributions\ChiSquared::class, 'distribution'], 'argumentCount' => '2', ], 'CHISQ.DIST' => [ @@ -498,12 +498,12 @@ class Calculation ], 'CHISQ.DIST.RT' => [ 'category' => Category::CATEGORY_STATISTICAL, - 'functionCall' => [Statistical::class, 'CHIDIST'], + 'functionCall' => [Statistical\Distributions\ChiSquared::class, 'distribution'], 'argumentCount' => '2', ], 'CHIINV' => [ 'category' => Category::CATEGORY_STATISTICAL, - 'functionCall' => [Statistical::class, 'CHIINV'], + 'functionCall' => [Statistical\Distributions\ChiSquared::class, 'inverse'], 'argumentCount' => '2', ], 'CHISQ.INV' => [ @@ -513,7 +513,7 @@ class Calculation ], 'CHISQ.INV.RT' => [ 'category' => Category::CATEGORY_STATISTICAL, - 'functionCall' => [Statistical::class, 'CHIINV'], + 'functionCall' => [Statistical\Distributions\ChiSquared::class, 'inverse'], 'argumentCount' => '2', ], 'CHITEST' => [ @@ -1055,12 +1055,12 @@ class Calculation ], 'FISHER' => [ 'category' => Category::CATEGORY_STATISTICAL, - 'functionCall' => [Statistical::class, 'FISHER'], + 'functionCall' => [Statistical\Distributions\Fisher::class, 'distribution'], 'argumentCount' => '1', ], 'FISHERINV' => [ 'category' => Category::CATEGORY_STATISTICAL, - 'functionCall' => [Statistical::class, 'FISHERINV'], + 'functionCall' => [Statistical\Distributions\Fisher::class, 'inverse'], 'argumentCount' => '1', ], 'FIXED' => [ @@ -1147,37 +1147,37 @@ class Calculation ], 'GAMMA' => [ 'category' => Category::CATEGORY_STATISTICAL, - 'functionCall' => [Statistical::class, 'GAMMAFunction'], + 'functionCall' => [Statistical\Distributions\Gamma::class, 'gamma'], 'argumentCount' => '1', ], 'GAMMADIST' => [ 'category' => Category::CATEGORY_STATISTICAL, - 'functionCall' => [Statistical::class, 'GAMMADIST'], + 'functionCall' => [Statistical\Distributions\Gamma::class, 'distribution'], 'argumentCount' => '4', ], 'GAMMA.DIST' => [ 'category' => Category::CATEGORY_STATISTICAL, - 'functionCall' => [Statistical::class, 'GAMMADIST'], + 'functionCall' => [Statistical\Distributions\Gamma::class, 'distribution'], 'argumentCount' => '4', ], 'GAMMAINV' => [ 'category' => Category::CATEGORY_STATISTICAL, - 'functionCall' => [Statistical::class, 'GAMMAINV'], + 'functionCall' => [Statistical\Distributions\Gamma::class, 'inverse'], 'argumentCount' => '3', ], 'GAMMA.INV' => [ 'category' => Category::CATEGORY_STATISTICAL, - 'functionCall' => [Statistical::class, 'GAMMAINV'], + 'functionCall' => [Statistical\Distributions\Gamma::class, 'inverse'], 'argumentCount' => '3', ], 'GAMMALN' => [ 'category' => Category::CATEGORY_STATISTICAL, - 'functionCall' => [Statistical::class, 'GAMMALN'], + 'functionCall' => [Statistical\Distributions\Gamma::class, 'ln'], 'argumentCount' => '1', ], 'GAMMALN.PRECISE' => [ 'category' => Category::CATEGORY_STATISTICAL, - 'functionCall' => [Statistical::class, 'GAMMALN'], + 'functionCall' => [Statistical\Distributions\Gamma::class, 'ln'], 'argumentCount' => '1', ], 'GAUSS' => [ @@ -1646,7 +1646,7 @@ class Calculation ], 'MEDIAN' => [ 'category' => Category::CATEGORY_STATISTICAL, - 'functionCall' => [Statistical::class, 'MEDIAN'], + 'functionCall' => [Statistical\Averages::class, 'median'], 'argumentCount' => '1+', ], 'MEDIANIF' => [ @@ -1706,7 +1706,7 @@ class Calculation ], 'MODE' => [ 'category' => Category::CATEGORY_STATISTICAL, - 'functionCall' => [Statistical::class, 'MODE'], + 'functionCall' => [Statistical\Averages::class, 'mode'], 'argumentCount' => '1+', ], 'MODE.MULT' => [ @@ -1716,7 +1716,7 @@ class Calculation ], 'MODE.SNGL' => [ 'category' => Category::CATEGORY_STATISTICAL, - 'functionCall' => [Statistical::class, 'MODE'], + 'functionCall' => [Statistical\Averages::class, 'mode'], 'argumentCount' => '1+', ], 'MONTH' => [ diff --git a/src/PhpSpreadsheet/Calculation/Database/DAverage.php b/src/PhpSpreadsheet/Calculation/Database/DAverage.php index 738cb78e..e30842dc 100644 --- a/src/PhpSpreadsheet/Calculation/Database/DAverage.php +++ b/src/PhpSpreadsheet/Calculation/Database/DAverage.php @@ -38,7 +38,7 @@ class DAverage extends DatabaseAbstract return null; } - return Averages::AVERAGE( + return Averages::average( self::getFilteredColumn($database, $field, $criteria) ); } diff --git a/src/PhpSpreadsheet/Calculation/Financial/Amortization.php b/src/PhpSpreadsheet/Calculation/Financial/Amortization.php index 76be7e12..7bb7fb40 100644 --- a/src/PhpSpreadsheet/Calculation/Financial/Amortization.php +++ b/src/PhpSpreadsheet/Calculation/Financial/Amortization.php @@ -22,13 +22,13 @@ class Amortization * Excel Function: * AMORDEGRC(cost,purchased,firstPeriod,salvage,period,rate[,basis]) * - * @param float $cost The cost of the asset + * @param mixed (float) $cost The cost of the asset * @param mixed $purchased Date of the purchase of the asset * @param mixed $firstPeriod Date of the end of the first period * @param mixed $salvage The salvage value at the end of the life of the asset - * @param float $period The period - * @param float $rate Rate of depreciation - * @param int $basis The type of day count to use. + * @param mixed (float) $period The period + * @param mixed (float) $rate Rate of depreciation + * @param mixed (int) $basis The type of day count to use. * 0 or omitted US (NASD) 30/360 * 1 Actual/actual * 2 Actual/360 @@ -88,13 +88,13 @@ class Amortization * Excel Function: * AMORLINC(cost,purchased,firstPeriod,salvage,period,rate[,basis]) * - * @param float $cost The cost of the asset + * @param mixed (float) $cost The cost of the asset * @param mixed $purchased Date of the purchase of the asset * @param mixed $firstPeriod Date of the end of the first period * @param mixed $salvage The salvage value at the end of the life of the asset - * @param float $period The period - * @param float $rate Rate of depreciation - * @param int $basis The type of day count to use. + * @param mixed (float) $period The period + * @param mixed (float) $rate Rate of depreciation + * @param mixed (int) $basis The type of day count to use. * 0 or omitted US (NASD) 30/360 * 1 Actual/actual * 2 Actual/360 diff --git a/src/PhpSpreadsheet/Calculation/Financial/Coupons.php b/src/PhpSpreadsheet/Calculation/Financial/Coupons.php index 835ef633..d0efd689 100644 --- a/src/PhpSpreadsheet/Calculation/Financial/Coupons.php +++ b/src/PhpSpreadsheet/Calculation/Financial/Coupons.php @@ -29,12 +29,12 @@ class Coupons * date when the security is traded to the buyer. * @param mixed $maturity The security's maturity date. * The maturity date is the date when the security expires. - * @param int $frequency the number of coupon payments per year. + * @param mixed (int) $frequency the number of coupon payments per year. * Valid frequency values are: * 1 Annual * 2 Semi-Annual * 4 Quarterly - * @param int $basis The type of day count to use. + * @param mixed (int) $basis The type of day count to use. * 0 or omitted US (NASD) 30/360 * 1 Actual/actual * 2 Actual/360 @@ -88,7 +88,7 @@ class Coupons * 1 Annual * 2 Semi-Annual * 4 Quarterly - * @param int $basis The type of day count to use. + * @param mixed (int) $basis The type of day count to use. * 0 or omitted US (NASD) 30/360 * 1 Actual/actual * 2 Actual/360 @@ -153,7 +153,7 @@ class Coupons * 1 Annual * 2 Semi-Annual * 4 Quarterly - * @param int $basis The type of day count to use. + * @param mixed (int) $basis The type of day count to use. * 0 or omitted US (NASD) 30/360 * 1 Actual/actual * 2 Actual/360 @@ -211,7 +211,7 @@ class Coupons * 1 Annual * 2 Semi-Annual * 4 Quarterly - * @param int $basis The type of day count to use. + * @param mixed (int) $basis The type of day count to use. * 0 or omitted US (NASD) 30/360 * 1 Actual/actual * 2 Actual/360 @@ -260,7 +260,7 @@ class Coupons * 1 Annual * 2 Semi-Annual * 4 Quarterly - * @param int $basis The type of day count to use. + * @param mixed (int) $basis The type of day count to use. * 0 or omitted US (NASD) 30/360 * 1 Actual/actual * 2 Actual/360 @@ -309,7 +309,7 @@ class Coupons * 1 Annual * 2 Semi-Annual * 4 Quarterly - * @param int $basis The type of day count to use. + * @param mixed (int) $basis The type of day count to use. * 0 or omitted US (NASD) 30/360 * 1 Actual/actual * 2 Actual/360 diff --git a/src/PhpSpreadsheet/Calculation/Financial/Depreciation.php b/src/PhpSpreadsheet/Calculation/Financial/Depreciation.php index 9236b4d4..173e29bb 100644 --- a/src/PhpSpreadsheet/Calculation/Financial/Depreciation.php +++ b/src/PhpSpreadsheet/Calculation/Financial/Depreciation.php @@ -20,14 +20,14 @@ class Depreciation * Excel Function: * DB(cost,salvage,life,period[,month]) * - * @param float $cost Initial cost of the asset - * @param float $salvage Value at the end of the depreciation. + * @param mixed (float) $cost Initial cost of the asset + * @param mixed (float) $salvage Value at the end of the depreciation. * (Sometimes called the salvage value of the asset) - * @param int $life Number of periods over which the asset is depreciated. + * @param mixed (int) $life Number of periods over which the asset is depreciated. * (Sometimes called the useful life of the asset) - * @param int $period The period for which you want to calculate the + * @param mixed (int) $period The period for which you want to calculate the * depreciation. Period must use the same units as life. - * @param int $month Number of months in the first year. If month is omitted, + * @param mixed (int) $month Number of months in the first year. If month is omitted, * it defaults to 12. * * @return float|string @@ -85,14 +85,14 @@ class Depreciation * Excel Function: * DDB(cost,salvage,life,period[,factor]) * - * @param float $cost Initial cost of the asset - * @param float $salvage Value at the end of the depreciation. + * @param mixed (float) $cost Initial cost of the asset + * @param mixed (float) $salvage Value at the end of the depreciation. * (Sometimes called the salvage value of the asset) - * @param int $life Number of periods over which the asset is depreciated. + * @param mixed (int) $life Number of periods over which the asset is depreciated. * (Sometimes called the useful life of the asset) - * @param int $period The period for which you want to calculate the + * @param mixed (int) $period The period for which you want to calculate the * depreciation. Period must use the same units as life. - * @param float $factor The rate at which the balance declines. + * @param mixed (float) $factor The rate at which the balance declines. * If factor is omitted, it is assumed to be 2 (the * double-declining balance method). * diff --git a/src/PhpSpreadsheet/Calculation/Financial/Dollar.php b/src/PhpSpreadsheet/Calculation/Financial/Dollar.php index e85b00c6..36326a60 100644 --- a/src/PhpSpreadsheet/Calculation/Financial/Dollar.php +++ b/src/PhpSpreadsheet/Calculation/Financial/Dollar.php @@ -16,8 +16,8 @@ class Dollar * Excel Function: * DOLLARDE(fractional_dollar,fraction) * - * @param float $fractionalDollar Fractional Dollar - * @param int $fraction Fraction + * @param mixed (float) $fractionalDollar Fractional Dollar + * @param mixed (int) $fraction Fraction * * @return float|string */ @@ -52,8 +52,8 @@ class Dollar * Excel Function: * DOLLARFR(decimal_dollar,fraction) * - * @param float $decimalDollar Decimal Dollar - * @param int $fraction Fraction + * @param mixed (float) $decimalDollar Decimal Dollar + * @param mixed (int) $fraction Fraction * * @return float|string */ diff --git a/src/PhpSpreadsheet/Calculation/Financial/InterestRate.php b/src/PhpSpreadsheet/Calculation/Financial/InterestRate.php index be7e6fd7..04b43e32 100644 --- a/src/PhpSpreadsheet/Calculation/Financial/InterestRate.php +++ b/src/PhpSpreadsheet/Calculation/Financial/InterestRate.php @@ -15,8 +15,8 @@ class InterestRate * Excel Function: * EFFECT(nominal_rate,npery) * - * @param float $nominalRate Nominal interest rate - * @param int $periodsPerYear Number of compounding payments per year + * @param mixed (float) $nominalRate Nominal interest rate + * @param mixed (int) $periodsPerYear Number of compounding payments per year * * @return float|string */ @@ -43,8 +43,8 @@ class InterestRate * * Returns the nominal interest rate given the effective rate and the number of compounding payments per year. * - * @param float $effectiveRate Effective interest rate - * @param int $periodsPerYear Number of compounding payments per year + * @param mixed (float) $effectiveRate Effective interest rate + * @param mixed (int) $periodsPerYear Number of compounding payments per year * * @return float|string Result, or a string containing an error */ diff --git a/src/PhpSpreadsheet/Calculation/Financial/Securities/Price.php b/src/PhpSpreadsheet/Calculation/Financial/Securities/Price.php index a5f0fb46..14be7f84 100644 --- a/src/PhpSpreadsheet/Calculation/Financial/Securities/Price.php +++ b/src/PhpSpreadsheet/Calculation/Financial/Securities/Price.php @@ -20,14 +20,14 @@ class Price extends BaseValidations * is traded to the buyer. * @param mixed $maturity The security's maturity date. * The maturity date is the date when the security expires. - * @param float $rate the security's annual coupon rate - * @param float $yield the security's annual yield - * @param float $redemption The number of coupon payments per year. + * @param mixed (float) $rate the security's annual coupon rate + * @param mixed (float) $yield the security's annual yield + * @param mixed (float) $redemption The number of coupon payments per year. * For annual payments, frequency = 1; * for semiannual, frequency = 2; * for quarterly, frequency = 4. - * @param int $frequency - * @param int $basis The type of day count to use. + * @param mixed (int) $frequency + * @param mixed (int) $basis The type of day count to use. * 0 or omitted US (NASD) 30/360 * 1 Actual/actual * 2 Actual/360 @@ -87,9 +87,9 @@ class Price extends BaseValidations * is traded to the buyer. * @param mixed $maturity The security's maturity date. * The maturity date is the date when the security expires. - * @param float $discount The security's discount rate - * @param float $redemption The security's redemption value per $100 face value - * @param int $basis The type of day count to use. + * @param mixed (float) $discount The security's discount rate + * @param mixed (float) $redemption The security's redemption value per $100 face value + * @param mixed (int) $basis The type of day count to use. * 0 or omitted US (NASD) 30/360 * 1 Actual/actual * 2 Actual/360 @@ -137,9 +137,9 @@ class Price extends BaseValidations * @param mixed $maturity The security's maturity date. * The maturity date is the date when the security expires. * @param mixed $issue The security's issue date - * @param float $rate The security's interest rate at date of issue - * @param float $yield The security's annual yield - * @param int $basis The type of day count to use. + * @param mixed (float) $rate The security's interest rate at date of issue + * @param mixed (float) $yield The security's annual yield + * @param mixed (int) $basis The type of day count to use. * 0 or omitted US (NASD) 30/360 * 1 Actual/actual * 2 Actual/360 diff --git a/src/PhpSpreadsheet/Calculation/Financial/TreasuryBill.php b/src/PhpSpreadsheet/Calculation/Financial/TreasuryBill.php index 8f8fa530..3177124a 100644 --- a/src/PhpSpreadsheet/Calculation/Financial/TreasuryBill.php +++ b/src/PhpSpreadsheet/Calculation/Financial/TreasuryBill.php @@ -17,7 +17,7 @@ class TreasuryBill * when the Treasury bill is traded to the buyer. * @param mixed $maturity The Treasury bill's maturity date. * The maturity date is the date when the Treasury bill expires. - * @param int $discount The Treasury bill's discount rate + * @param mixed (int) $discount The Treasury bill's discount rate * * @return float|string Result, or a string containing an error */ @@ -65,7 +65,7 @@ class TreasuryBill * when the Treasury bill is traded to the buyer. * @param mixed $maturity The Treasury bill's maturity date. * The maturity date is the date when the Treasury bill expires. - * @param int $discount The Treasury bill's discount rate + * @param mixed (int) $discount The Treasury bill's discount rate * * @return float|string Result, or a string containing an error */ @@ -117,7 +117,7 @@ class TreasuryBill * the Treasury bill is traded to the buyer. * @param mixed $maturity The Treasury bill's maturity date. * The maturity date is the date when the Treasury bill expires. - * @param int $price The Treasury bill's price per $100 face value + * @param mixed (int) $price The Treasury bill's price per $100 face value * * @return float|string */ diff --git a/src/PhpSpreadsheet/Calculation/LookupRef/Address.php b/src/PhpSpreadsheet/Calculation/LookupRef/Address.php index 53c9c9d8..daaebea2 100644 --- a/src/PhpSpreadsheet/Calculation/LookupRef/Address.php +++ b/src/PhpSpreadsheet/Calculation/LookupRef/Address.php @@ -25,15 +25,15 @@ class Address * * @param mixed $row Row number to use in the cell reference * @param mixed $column Column number to use in the cell reference - * @param int $relativity Flag indicating the type of reference to return + * @param mixed (int) $relativity Flag indicating the type of reference to return * 1 or omitted Absolute * 2 Absolute row; relative column * 3 Relative row; absolute column * 4 Relative - * @param bool $referenceStyle A logical value that specifies the A1 or R1C1 reference style. + * @param mixed (bool) $referenceStyle A logical value that specifies the A1 or R1C1 reference style. * TRUE or omitted ADDRESS returns an A1-style reference * FALSE ADDRESS returns an R1C1-style reference - * @param string $sheetName Optional Name of worksheet to use + * @param mixed (string) $sheetName Optional Name of worksheet to use * * @return string */ diff --git a/src/PhpSpreadsheet/Calculation/LookupRef/Indirect.php b/src/PhpSpreadsheet/Calculation/LookupRef/Indirect.php index 690b32e4..c34dd965 100644 --- a/src/PhpSpreadsheet/Calculation/LookupRef/Indirect.php +++ b/src/PhpSpreadsheet/Calculation/LookupRef/Indirect.php @@ -21,7 +21,7 @@ class Indirect * NOTE - INDIRECT() does not yet support the optional a1 parameter introduced in Excel 2010 * * @param null|array|string $cellAddress $cellAddress The cell address of the current cell (containing this formula) - * @param Cell $pCell The current cell (containing this formula) + * @param null|Cell $pCell The current cell (containing this formula) * * @return array|string An array containing a cell or range of cells, or a string on error * diff --git a/src/PhpSpreadsheet/Calculation/MathTrig.php b/src/PhpSpreadsheet/Calculation/MathTrig.php index f3d8351d..94850906 100644 --- a/src/PhpSpreadsheet/Calculation/MathTrig.php +++ b/src/PhpSpreadsheet/Calculation/MathTrig.php @@ -1156,7 +1156,7 @@ class MathTrig $aArgs = self::filterFormulaArgs($cellReference, $aArgs); switch ($subtotal) { case 1: - return Statistical\Averages::AVERAGE($aArgs); + return Statistical\Averages::average($aArgs); case 2: return Statistical\Counts::COUNT($aArgs); case 3: diff --git a/src/PhpSpreadsheet/Calculation/Statistical.php b/src/PhpSpreadsheet/Calculation/Statistical.php index 8a9e3fea..c8e084b5 100644 --- a/src/PhpSpreadsheet/Calculation/Statistical.php +++ b/src/PhpSpreadsheet/Calculation/Statistical.php @@ -12,411 +12,15 @@ use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Permutations; use PhpOffice\PhpSpreadsheet\Calculation\Statistical\StandardDeviations; use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Trends; use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Variances; -use PhpOffice\PhpSpreadsheet\Shared\Trend\Trend; class Statistical { const LOG_GAMMA_X_MAX_VALUE = 2.55e305; - const XMININ = 2.23e-308; const EPS = 2.22e-16; const MAX_VALUE = 1.2e308; const MAX_ITERATIONS = 256; const SQRT2PI = 2.5066282746310005024157652848110452530069867406099; - /** - * Incomplete beta function. - * - * @author Jaco van Kooten - * @author Paul Meagher - * - * The computation is based on formulas from Numerical Recipes, Chapter 6.4 (W.H. Press et al, 1992). - * - * @param mixed $x require 0<=x<=1 - * @param mixed $p require p>0 - * @param mixed $q require q>0 - * - * @return float 0 if x<0, p<=0, q<=0 or p+q>2.55E305 and 1 if x>1 to avoid errors and over/underflow - */ - private static function incompleteBeta($x, $p, $q) - { - if ($x <= 0.0) { - return 0.0; - } elseif ($x >= 1.0) { - return 1.0; - } elseif (($p <= 0.0) || ($q <= 0.0) || (($p + $q) > self::LOG_GAMMA_X_MAX_VALUE)) { - return 0.0; - } - $beta_gam = exp((0 - self::logBeta($p, $q)) + $p * log($x) + $q * log(1.0 - $x)); - if ($x < ($p + 1.0) / ($p + $q + 2.0)) { - return $beta_gam * self::betaFraction($x, $p, $q) / $p; - } - - return 1.0 - ($beta_gam * self::betaFraction(1 - $x, $q, $p) / $q); - } - - // Function cache for logBeta function - private static $logBetaCacheP = 0.0; - - private static $logBetaCacheQ = 0.0; - - private static $logBetaCacheResult = 0.0; - - /** - * The natural logarithm of the beta function. - * - * @param mixed $p require p>0 - * @param mixed $q require q>0 - * - * @return float 0 if p<=0, q<=0 or p+q>2.55E305 to avoid errors and over/underflow - * - * @author Jaco van Kooten - */ - private static function logBeta($p, $q) - { - if ($p != self::$logBetaCacheP || $q != self::$logBetaCacheQ) { - self::$logBetaCacheP = $p; - self::$logBetaCacheQ = $q; - if (($p <= 0.0) || ($q <= 0.0) || (($p + $q) > self::LOG_GAMMA_X_MAX_VALUE)) { - self::$logBetaCacheResult = 0.0; - } else { - self::$logBetaCacheResult = self::logGamma($p) + self::logGamma($q) - self::logGamma($p + $q); - } - } - - return self::$logBetaCacheResult; - } - - /** - * Evaluates of continued fraction part of incomplete beta function. - * Based on an idea from Numerical Recipes (W.H. Press et al, 1992). - * - * @author Jaco van Kooten - * - * @param mixed $x - * @param mixed $p - * @param mixed $q - * - * @return float - */ - private static function betaFraction($x, $p, $q) - { - $c = 1.0; - $sum_pq = $p + $q; - $p_plus = $p + 1.0; - $p_minus = $p - 1.0; - $h = 1.0 - $sum_pq * $x / $p_plus; - if (abs($h) < self::XMININ) { - $h = self::XMININ; - } - $h = 1.0 / $h; - $frac = $h; - $m = 1; - $delta = 0.0; - while ($m <= self::MAX_ITERATIONS && abs($delta - 1.0) > Functions::PRECISION) { - $m2 = 2 * $m; - // even index for d - $d = $m * ($q - $m) * $x / (($p_minus + $m2) * ($p + $m2)); - $h = 1.0 + $d * $h; - if (abs($h) < self::XMININ) { - $h = self::XMININ; - } - $h = 1.0 / $h; - $c = 1.0 + $d / $c; - if (abs($c) < self::XMININ) { - $c = self::XMININ; - } - $frac *= $h * $c; - // odd index for d - $d = -($p + $m) * ($sum_pq + $m) * $x / (($p + $m2) * ($p_plus + $m2)); - $h = 1.0 + $d * $h; - if (abs($h) < self::XMININ) { - $h = self::XMININ; - } - $h = 1.0 / $h; - $c = 1.0 + $d / $c; - if (abs($c) < self::XMININ) { - $c = self::XMININ; - } - $delta = $h * $c; - $frac *= $delta; - ++$m; - } - - return $frac; - } - - /** - * logGamma function. - * - * @version 1.1 - * - * @author Jaco van Kooten - * - * Original author was Jaco van Kooten. Ported to PHP by Paul Meagher. - * - * The natural logarithm of the gamma function.
- * Based on public domain NETLIB (Fortran) code by W. J. Cody and L. Stoltz
- * Applied Mathematics Division
- * Argonne National Laboratory
- * Argonne, IL 60439
- *

- * References: - *

    - *
  1. W. J. Cody and K. E. Hillstrom, 'Chebyshev Approximations for the Natural - * Logarithm of the Gamma Function,' Math. Comp. 21, 1967, pp. 198-203.
  2. - *
  3. K. E. Hillstrom, ANL/AMD Program ANLC366S, DGAMMA/DLGAMA, May, 1969.
  4. - *
  5. Hart, Et. Al., Computer Approximations, Wiley and sons, New York, 1968.
  6. - *
- *

- *

- * From the original documentation: - *

- *

- * This routine calculates the LOG(GAMMA) function for a positive real argument X. - * Computation is based on an algorithm outlined in references 1 and 2. - * The program uses rational functions that theoretically approximate LOG(GAMMA) - * to at least 18 significant decimal digits. The approximation for X > 12 is from - * reference 3, while approximations for X < 12.0 are similar to those in reference - * 1, but are unpublished. The accuracy achieved depends on the arithmetic system, - * the compiler, the intrinsic functions, and proper selection of the - * machine-dependent constants. - *

- *

- * Error returns:
- * The program returns the value XINF for X .LE. 0.0 or when overflow would occur. - * The computation is believed to be free of underflow and overflow. - *

- * - * @return float MAX_VALUE for x < 0.0 or when overflow would occur, i.e. x > 2.55E305 - */ - - // Function cache for logGamma - private static $logGammaCacheResult = 0.0; - - private static $logGammaCacheX = 0.0; - - private static function logGamma($x) - { - // Log Gamma related constants - static $lg_d1 = -0.5772156649015328605195174; - static $lg_d2 = 0.4227843350984671393993777; - static $lg_d4 = 1.791759469228055000094023; - - static $lg_p1 = [ - 4.945235359296727046734888, - 201.8112620856775083915565, - 2290.838373831346393026739, - 11319.67205903380828685045, - 28557.24635671635335736389, - 38484.96228443793359990269, - 26377.48787624195437963534, - 7225.813979700288197698961, - ]; - static $lg_p2 = [ - 4.974607845568932035012064, - 542.4138599891070494101986, - 15506.93864978364947665077, - 184793.2904445632425417223, - 1088204.76946882876749847, - 3338152.967987029735917223, - 5106661.678927352456275255, - 3074109.054850539556250927, - ]; - static $lg_p4 = [ - 14745.02166059939948905062, - 2426813.369486704502836312, - 121475557.4045093227939592, - 2663432449.630976949898078, - 29403789566.34553899906876, - 170266573776.5398868392998, - 492612579337.743088758812, - 560625185622.3951465078242, - ]; - static $lg_q1 = [ - 67.48212550303777196073036, - 1113.332393857199323513008, - 7738.757056935398733233834, - 27639.87074403340708898585, - 54993.10206226157329794414, - 61611.22180066002127833352, - 36351.27591501940507276287, - 8785.536302431013170870835, - ]; - static $lg_q2 = [ - 183.0328399370592604055942, - 7765.049321445005871323047, - 133190.3827966074194402448, - 1136705.821321969608938755, - 5267964.117437946917577538, - 13467014.54311101692290052, - 17827365.30353274213975932, - 9533095.591844353613395747, - ]; - static $lg_q4 = [ - 2690.530175870899333379843, - 639388.5654300092398984238, - 41355999.30241388052042842, - 1120872109.61614794137657, - 14886137286.78813811542398, - 101680358627.2438228077304, - 341747634550.7377132798597, - 446315818741.9713286462081, - ]; - static $lg_c = [ - -0.001910444077728, - 8.4171387781295e-4, - -5.952379913043012e-4, - 7.93650793500350248e-4, - -0.002777777777777681622553, - 0.08333333333333333331554247, - 0.0057083835261, - ]; - - // Rough estimate of the fourth root of logGamma_xBig - static $lg_frtbig = 2.25e76; - static $pnt68 = 0.6796875; - - if ($x == self::$logGammaCacheX) { - return self::$logGammaCacheResult; - } - $y = $x; - if ($y > 0.0 && $y <= self::LOG_GAMMA_X_MAX_VALUE) { - if ($y <= self::EPS) { - $res = -log($y); - } elseif ($y <= 1.5) { - // --------------------- - // EPS .LT. X .LE. 1.5 - // --------------------- - if ($y < $pnt68) { - $corr = -log($y); - $xm1 = $y; - } else { - $corr = 0.0; - $xm1 = $y - 1.0; - } - if ($y <= 0.5 || $y >= $pnt68) { - $xden = 1.0; - $xnum = 0.0; - for ($i = 0; $i < 8; ++$i) { - $xnum = $xnum * $xm1 + $lg_p1[$i]; - $xden = $xden * $xm1 + $lg_q1[$i]; - } - $res = $corr + $xm1 * ($lg_d1 + $xm1 * ($xnum / $xden)); - } else { - $xm2 = $y - 1.0; - $xden = 1.0; - $xnum = 0.0; - for ($i = 0; $i < 8; ++$i) { - $xnum = $xnum * $xm2 + $lg_p2[$i]; - $xden = $xden * $xm2 + $lg_q2[$i]; - } - $res = $corr + $xm2 * ($lg_d2 + $xm2 * ($xnum / $xden)); - } - } elseif ($y <= 4.0) { - // --------------------- - // 1.5 .LT. X .LE. 4.0 - // --------------------- - $xm2 = $y - 2.0; - $xden = 1.0; - $xnum = 0.0; - for ($i = 0; $i < 8; ++$i) { - $xnum = $xnum * $xm2 + $lg_p2[$i]; - $xden = $xden * $xm2 + $lg_q2[$i]; - } - $res = $xm2 * ($lg_d2 + $xm2 * ($xnum / $xden)); - } elseif ($y <= 12.0) { - // ---------------------- - // 4.0 .LT. X .LE. 12.0 - // ---------------------- - $xm4 = $y - 4.0; - $xden = -1.0; - $xnum = 0.0; - for ($i = 0; $i < 8; ++$i) { - $xnum = $xnum * $xm4 + $lg_p4[$i]; - $xden = $xden * $xm4 + $lg_q4[$i]; - } - $res = $lg_d4 + $xm4 * ($xnum / $xden); - } else { - // --------------------------------- - // Evaluate for argument .GE. 12.0 - // --------------------------------- - $res = 0.0; - if ($y <= $lg_frtbig) { - $res = $lg_c[6]; - $ysq = $y * $y; - for ($i = 0; $i < 6; ++$i) { - $res = $res / $ysq + $lg_c[$i]; - } - $res /= $y; - $corr = log($y); - $res = $res + log(self::SQRT2PI) - 0.5 * $corr; - $res += $y * ($corr - 1.0); - } - } - } else { - // -------------------------- - // Return for bad arguments - // -------------------------- - $res = self::MAX_VALUE; - } - // ------------------------------ - // Final adjustments and return - // ------------------------------ - self::$logGammaCacheX = $x; - self::$logGammaCacheResult = $res; - - return $res; - } - - // - // Private implementation of the incomplete Gamma function - // - private static function incompleteGamma($a, $x) - { - static $max = 32; - $summer = 0; - for ($n = 0; $n <= $max; ++$n) { - $divisor = $a; - for ($i = 1; $i <= $n; ++$i) { - $divisor *= ($a + $i); - } - $summer += ($x ** $n / $divisor); - } - - return $x ** $a * exp(0 - $x) * $summer; - } - - // - // Private implementation of the Gamma function - // - private static function gamma($data) - { - if ($data == 0.0) { - return 0; - } - - static $p0 = 1.000000000190015; - static $p = [ - 1 => 76.18009172947146, - 2 => -86.50532032941677, - 3 => 24.01409824083091, - 4 => -1.231739572450155, - 5 => 1.208650973866179e-3, - 6 => -5.395239384953e-6, - ]; - - $y = $x = $data; - $tmp = $x + 5.5; - $tmp -= ($x + 0.5) * log($tmp); - - $summer = $p0; - for ($j = 1; $j <= 6; ++$j) { - $summer += ($p[$j] / ++$y); - } - - return exp(0 - $tmp + log(self::SQRT2PI * $summer / $x)); - } - /* * inverse_ncdf.php * ------------------- @@ -512,16 +116,16 @@ class Statistical * * @Deprecated 1.17.0 * - * @see Statistical\Averages::AVEDEV() - * Use the AVEDEV() method in the Statistical\Averages class instead - * * @param mixed ...$args Data values * * @return float|string + * + *@see Statistical\Averages::averageDeviations() + * Use the averageDeviations() method in the Statistical\Averages class instead */ public static function AVEDEV(...$args) { - return Averages::AVEDEV(...$args); + return Averages::averageDeviations(...$args); } /** @@ -534,8 +138,8 @@ class Statistical * * @Deprecated 1.17.0 * - * @see Statistical\Averages::AVERAGE() - * Use the AVERAGE() method in the Statistical\Averages class instead + * @see Statistical\Averages::average() + * Use the average() method in the Statistical\Averages class instead * * @param mixed ...$args Data values * @@ -543,7 +147,7 @@ class Statistical */ public static function AVERAGE(...$args) { - return Averages::AVERAGE(...$args); + return Averages::average(...$args); } /** @@ -556,16 +160,16 @@ class Statistical * * @Deprecated 1.17.0 * - * @see Statistical\Averages::AVERAGEA() - * Use the AVERAGEA() method in the Statistical\Averages class instead - * * @param mixed ...$args Data values * * @return float|string + * + *@see Statistical\Averages::averageA() + * Use the averageA() method in the Statistical\Averages class instead */ public static function AVERAGEA(...$args) { - return Averages::AVERAGEA(...$args); + return Averages::averageA(...$args); } /** @@ -597,6 +201,11 @@ class Statistical * * Returns the beta distribution. * + * @Deprecated 1.18.0 + * + *@see Statistical\Distributions\Beta::distribution() + * Use the distribution() method in the Statistical\Distributions\Beta class instead + * * @param float $value Value at which you want to evaluate the distribution * @param float $alpha Parameter to the distribution * @param float $beta Parameter to the distribution @@ -607,28 +216,7 @@ class Statistical */ public static function BETADIST($value, $alpha, $beta, $rMin = 0, $rMax = 1) { - $value = Functions::flattenSingleValue($value); - $alpha = Functions::flattenSingleValue($alpha); - $beta = Functions::flattenSingleValue($beta); - $rMin = Functions::flattenSingleValue($rMin); - $rMax = Functions::flattenSingleValue($rMax); - - if ((is_numeric($value)) && (is_numeric($alpha)) && (is_numeric($beta)) && (is_numeric($rMin)) && (is_numeric($rMax))) { - if ($rMin > $rMax) { - $tmp = $rMin; - $rMin = $rMax; - $rMax = $tmp; - } - if (($value < $rMin) || ($value > $rMax) || ($alpha <= 0) || ($beta <= 0) || ($rMin == $rMax)) { - return Functions::NAN(); - } - $value -= $rMin; - $value /= ($rMax - $rMin); - - return self::incompleteBeta($value, $alpha, $beta); - } - - return Functions::VALUE(); + return Statistical\Distributions\Beta::distribution($value, $alpha, $beta, $rMin, $rMax); } /** @@ -636,6 +224,11 @@ class Statistical * * Returns the inverse of the Beta distribution. * + * @Deprecated 1.18.0 + * + * @see Statistical\Distributions\Beta::inverse() + * Use the inverse() method in the Statistical\Distributions\Beta class instead + * * @param float $probability Probability at which you want to evaluate the distribution * @param float $alpha Parameter to the distribution * @param float $beta Parameter to the distribution @@ -646,44 +239,7 @@ class Statistical */ public static function BETAINV($probability, $alpha, $beta, $rMin = 0, $rMax = 1) { - $probability = Functions::flattenSingleValue($probability); - $alpha = Functions::flattenSingleValue($alpha); - $beta = Functions::flattenSingleValue($beta); - $rMin = Functions::flattenSingleValue($rMin); - $rMax = Functions::flattenSingleValue($rMax); - - if ((is_numeric($probability)) && (is_numeric($alpha)) && (is_numeric($beta)) && (is_numeric($rMin)) && (is_numeric($rMax))) { - if ($rMin > $rMax) { - $tmp = $rMin; - $rMin = $rMax; - $rMax = $tmp; - } - if (($alpha <= 0) || ($beta <= 0) || ($rMin == $rMax) || ($probability <= 0) || ($probability > 1)) { - return Functions::NAN(); - } - $a = 0; - $b = 2; - - $i = 0; - while ((($b - $a) > Functions::PRECISION) && ($i++ < self::MAX_ITERATIONS)) { - $guess = ($a + $b) / 2; - $result = self::BETADIST($guess, $alpha, $beta); - if (($result == $probability) || ($result == 0)) { - $b = $a; - } elseif ($result > $probability) { - $b = $guess; - } else { - $a = $guess; - } - } - if ($i == self::MAX_ITERATIONS) { - return Functions::NA(); - } - - return round($rMin + $guess * ($rMax - $rMin), 12); - } - - return Functions::VALUE(); + return Statistical\Distributions\Beta::inverse($probability, $alpha, $beta, $rMin, $rMax); } /** @@ -739,6 +295,11 @@ class Statistical * * Returns the one-tailed probability of the chi-squared distribution. * + * @Deprecated 1.18.0 + * + * @see Statistical\Distributions\ChiSquared::distribution() + * Use the distribution() method in the Statistical\Distributions\ChiSquared class instead + * * @param float $value Value for the function * @param float $degrees degrees of freedom * @@ -746,26 +307,7 @@ class Statistical */ public static function CHIDIST($value, $degrees) { - $value = Functions::flattenSingleValue($value); - $degrees = Functions::flattenSingleValue($degrees); - - if ((is_numeric($value)) && (is_numeric($degrees))) { - $degrees = floor($degrees); - if ($degrees < 1) { - return Functions::NAN(); - } - if ($value < 0) { - if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) { - return 1; - } - - return Functions::NAN(); - } - - return 1 - (self::incompleteGamma($degrees / 2, $value / 2) / self::gamma($degrees / 2)); - } - - return Functions::VALUE(); + return Statistical\Distributions\ChiSquared::distribution($value, $degrees); } /** @@ -773,6 +315,11 @@ class Statistical * * Returns the one-tailed probability of the chi-squared distribution. * + * @Deprecated 1.18.0 + * + * @see Statistical\Distributions\ChiSquared::inverse() + * Use the inverse() method in the Statistical\Distributions\ChiSquared class instead + * * @param float $probability Probability for the function * @param float $degrees degrees of freedom * @@ -780,52 +327,7 @@ class Statistical */ public static function CHIINV($probability, $degrees) { - $probability = Functions::flattenSingleValue($probability); - $degrees = Functions::flattenSingleValue($degrees); - - if ((is_numeric($probability)) && (is_numeric($degrees))) { - $degrees = floor($degrees); - - $xLo = 100; - $xHi = 0; - - $x = $xNew = 1; - $dx = 1; - $i = 0; - - while ((abs($dx) > Functions::PRECISION) && ($i++ < self::MAX_ITERATIONS)) { - // Apply Newton-Raphson step - $result = 1 - (self::incompleteGamma($degrees / 2, $x / 2) / self::gamma($degrees / 2)); - $error = $result - $probability; - if ($error == 0.0) { - $dx = 0; - } elseif ($error < 0.0) { - $xLo = $x; - } else { - $xHi = $x; - } - // Avoid division by zero - if ($result != 0.0) { - $dx = $error / $result; - $xNew = $x - $dx; - } - // If the NR fails to converge (which for example may be the - // case if the initial guess is too rough) we apply a bisection - // step to determine a more narrow interval around the root. - if (($xNew < $xLo) || ($xNew > $xHi) || ($result == 0.0)) { - $xNew = ($xLo + $xHi) / 2; - $dx = $xNew - $x; - } - $x = $xNew; - } - if ($i == self::MAX_ITERATIONS) { - return Functions::NA(); - } - - return round($x, 12); - } - - return Functions::VALUE(); + return Statistical\Distributions\ChiSquared::inverse($probability, $degrees); } /** @@ -1146,7 +648,7 @@ class Statistical // Return value $returnValue = null; - $aMean = Averages::AVERAGE($aArgs); + $aMean = Averages::average($aArgs); if ($aMean != Functions::DIV0()) { $aCount = -1; foreach ($aArgs as $k => $arg) { @@ -1214,16 +716,6 @@ class Statistical return Functions::VALUE(); } - private static function betaFunction($a, $b) - { - return (self::gamma($a) * self::gamma($b)) / self::gamma($a + $b); - } - - private static function regularizedIncompleteBeta($value, $a, $b) - { - return self::incompleteBeta($value, $a, $b) / self::betaFunction($a, $b); - } - /** * F.DIST. * @@ -1259,10 +751,12 @@ class Statistical if ($cumulative) { $adjustedValue = ($u * $value) / ($u * $value + $v); - return self::incompleteBeta($adjustedValue, $u / 2, $v / 2); + return Statistical\Distributions\Beta::incompleteBeta($adjustedValue, $u / 2, $v / 2); } - return (self::gamma(($v + $u) / 2) / (self::gamma($u / 2) * self::gamma($v / 2))) * + return (Statistical\Distributions\Gamma::gammaValue(($v + $u) / 2) / + (Statistical\Distributions\Gamma::gammaValue($u / 2) * + Statistical\Distributions\Gamma::gammaValue($v / 2))) * (($u / $v) ** ($u / 2)) * (($value ** (($u - 2) / 2)) / ((1 + ($u / $v) * $value) ** (($u + $v) / 2))); } @@ -1277,23 +771,18 @@ class Statistical * is normally distributed rather than skewed. Use this function to perform hypothesis * testing on the correlation coefficient. * + * @Deprecated 1.18.0 + * + * @see Statistical\Distributions\Fisher::distribution() + * Use the distribution() method in the Statistical\Distributions\Fisher class instead + * * @param float $value * * @return float|string */ public static function FISHER($value) { - $value = Functions::flattenSingleValue($value); - - if (is_numeric($value)) { - if (($value <= -1) || ($value >= 1)) { - return Functions::NAN(); - } - - return 0.5 * log((1 + $value) / (1 - $value)); - } - - return Functions::VALUE(); + return Statistical\Distributions\Fisher::distribution($value); } /** @@ -1303,19 +792,18 @@ class Statistical * analyzing correlations between ranges or arrays of data. If y = FISHER(x), then * FISHERINV(y) = x. * + * @Deprecated 1.18.0 + * + * @see Statistical\Distributions\Fisher::inverse() + * Use the inverse() method in the Statistical\Distributions\Fisher class instead + * * @param float $value * * @return float|string */ public static function FISHERINV($value) { - $value = Functions::flattenSingleValue($value); - - if (is_numeric($value)) { - return (exp(2 * $value) - 1) / (exp(2 * $value) + 1); - } - - return Functions::VALUE(); + return Statistical\Distributions\Fisher::inverse($value); } /** @@ -1342,7 +830,12 @@ class Statistical /** * GAMMA. * - * Return the gamma function value. + * Returns the gamma function value. + * + * @Deprecated 1.18.0 + * + * @see Statistical\Distributions\Gamma::gamma() + * Use the gamma() method in the Statistical\Distributions\Gamma class instead * * @param float $value * @@ -1350,14 +843,7 @@ class Statistical */ public static function GAMMAFunction($value) { - $value = Functions::flattenSingleValue($value); - if (!is_numeric($value)) { - return Functions::VALUE(); - } elseif ((((int) $value) == ((float) $value)) && $value <= 0.0) { - return Functions::NAN(); - } - - return self::gamma($value); + return Statistical\Distributions\Gamma::gamma($value); } /** @@ -1365,6 +851,11 @@ class Statistical * * Returns the gamma distribution. * + * @Deprecated 1.18.0 + * + * @see Statistical\Distributions\Gamma::distribution() + * Use the distribution() method in the Statistical\Distributions\Gamma class instead + * * @param float $value Value at which you want to evaluate the distribution * @param float $a Parameter to the distribution * @param float $b Parameter to the distribution @@ -1374,24 +865,7 @@ class Statistical */ public static function GAMMADIST($value, $a, $b, $cumulative) { - $value = Functions::flattenSingleValue($value); - $a = Functions::flattenSingleValue($a); - $b = Functions::flattenSingleValue($b); - - if ((is_numeric($value)) && (is_numeric($a)) && (is_numeric($b))) { - if (($value < 0) || ($a <= 0) || ($b <= 0)) { - return Functions::NAN(); - } - if ((is_numeric($cumulative)) || (is_bool($cumulative))) { - if ($cumulative) { - return self::incompleteGamma($a, $value / $b) / self::gamma($a); - } - - return (1 / ($b ** $a * self::gamma($a))) * $value ** ($a - 1) * exp(0 - ($value / $b)); - } - } - - return Functions::VALUE(); + return Statistical\Distributions\Gamma::distribution($value, $a, $b, $cumulative); } /** @@ -1399,6 +873,11 @@ class Statistical * * Returns the inverse of the Gamma distribution. * + * @Deprecated 1.18.0 + * + * @see Statistical\Distributions\Gamma::inverse() + * Use the inverse() method in the Statistical\Distributions\Gamma class instead + * * @param float $probability Probability at which you want to evaluate the distribution * @param float $alpha Parameter to the distribution * @param float $beta Parameter to the distribution @@ -1407,53 +886,7 @@ class Statistical */ public static function GAMMAINV($probability, $alpha, $beta) { - $probability = Functions::flattenSingleValue($probability); - $alpha = Functions::flattenSingleValue($alpha); - $beta = Functions::flattenSingleValue($beta); - - if ((is_numeric($probability)) && (is_numeric($alpha)) && (is_numeric($beta))) { - if (($alpha <= 0) || ($beta <= 0) || ($probability < 0) || ($probability > 1)) { - return Functions::NAN(); - } - - $xLo = 0; - $xHi = $alpha * $beta * 5; - - $x = $xNew = 1; - $dx = 1024; - $i = 0; - - while ((abs($dx) > Functions::PRECISION) && ($i++ < self::MAX_ITERATIONS)) { - // Apply Newton-Raphson step - $error = self::GAMMADIST($x, $alpha, $beta, true) - $probability; - if ($error < 0.0) { - $xLo = $x; - } else { - $xHi = $x; - } - $pdf = self::GAMMADIST($x, $alpha, $beta, false); - // Avoid division by zero - if ($pdf != 0.0) { - $dx = $error / $pdf; - $xNew = $x - $dx; - } - // If the NR fails to converge (which for example may be the - // case if the initial guess is too rough) we apply a bisection - // step to determine a more narrow interval around the root. - if (($xNew < $xLo) || ($xNew > $xHi) || ($pdf == 0.0)) { - $xNew = ($xLo + $xHi) / 2; - $dx = $xNew - $x; - } - $x = $xNew; - } - if ($i == self::MAX_ITERATIONS) { - return Functions::NA(); - } - - return $x; - } - - return Functions::VALUE(); + return Statistical\Distributions\Gamma::inverse($probability, $alpha, $beta); } /** @@ -1461,23 +894,18 @@ class Statistical * * Returns the natural logarithm of the gamma function. * + * @Deprecated 1.18.0 + * + * @see Statistical\Distributions\Gamma::ln() + * Use the ln() method in the Statistical\Distributions\Gamma class instead + * * @param float $value * * @return float|string */ public static function GAMMALN($value) { - $value = Functions::flattenSingleValue($value); - - if (is_numeric($value)) { - if ($value <= 0) { - return Functions::NAN(); - } - - return log(self::gamma($value)); - } - - return Functions::VALUE(); + return Statistical\Distributions\Gamma::ln($value); } /** @@ -1673,7 +1101,7 @@ class Statistical public static function KURT(...$args) { $aArgs = Functions::flattenArrayIndexed($args); - $mean = Averages::AVERAGE($aArgs); + $mean = Averages::average($aArgs); $stdDev = StandardDeviations::STDEV($aArgs); if ($stdDev > 0) { @@ -1962,37 +1390,18 @@ class Statistical * Excel Function: * MEDIAN(value1[,value2[, ...]]) * + * @Deprecated 1.18.0 + * + * @see Statistical\Averages::median() + * Use the median() method in the Statistical\Averages class instead + * * @param mixed ...$args Data values * * @return float|string The result, or a string containing an error */ public static function MEDIAN(...$args) { - $returnValue = Functions::NAN(); - - $mArgs = []; - // Loop through arguments - $aArgs = Functions::flattenArray($args); - foreach ($aArgs as $arg) { - // Is it a numeric value? - if ((is_numeric($arg)) && (!is_string($arg))) { - $mArgs[] = $arg; - } - } - - $mValueCount = count($mArgs); - if ($mValueCount > 0) { - sort($mArgs, SORT_NUMERIC); - $mValueCount = $mValueCount / 2; - if ($mValueCount == floor($mValueCount)) { - $returnValue = ($mArgs[$mValueCount--] + $mArgs[$mValueCount]) / 2; - } else { - $mValueCount = floor($mValueCount); - $returnValue = $mArgs[$mValueCount]; - } - } - - return $returnValue; + return Statistical\Averages::median(...$args); } /** @@ -2062,55 +1471,6 @@ class Statistical return Conditional::MINIFS(...$args); } - // - // Special variant of array_count_values that isn't limited to strings and integers, - // but can work with floating point numbers as values - // - private static function modeCalc($data) - { - $frequencyArray = []; - $index = 0; - $maxfreq = 0; - $maxfreqkey = ''; - $maxfreqdatum = ''; - foreach ($data as $datum) { - $found = false; - ++$index; - foreach ($frequencyArray as $key => $value) { - if ((string) $value['value'] == (string) $datum) { - ++$frequencyArray[$key]['frequency']; - $freq = $frequencyArray[$key]['frequency']; - if ($freq > $maxfreq) { - $maxfreq = $freq; - $maxfreqkey = $key; - $maxfreqdatum = $datum; - } elseif ($freq == $maxfreq) { - if ($frequencyArray[$key]['index'] < $frequencyArray[$maxfreqkey]['index']) { - $maxfreqkey = $key; - $maxfreqdatum = $datum; - } - } - $found = true; - - break; - } - } - if (!$found) { - $frequencyArray[] = [ - 'value' => $datum, - 'frequency' => 1, - 'index' => $index, - ]; - } - } - - if ($maxfreq <= 1) { - return Functions::NA(); - } - - return $maxfreqdatum; - } - /** * MODE. * @@ -2119,30 +1479,18 @@ class Statistical * Excel Function: * MODE(value1[,value2[, ...]]) * + * @Deprecated 1.18.0 + * + * @see Statistical\Averages::mode() + * Use the mode() method in the Statistical\Averages class instead + * * @param mixed ...$args Data values * * @return float|string The result, or a string containing an error */ public static function MODE(...$args) { - $returnValue = Functions::NA(); - - // Loop through arguments - $aArgs = Functions::flattenArray($args); - - $mArgs = []; - foreach ($aArgs as $arg) { - // Is it a numeric value? - if ((is_numeric($arg)) && (!is_string($arg))) { - $mArgs[] = $arg; - } - } - - if (!empty($mArgs)) { - return self::modeCalc($mArgs); - } - - return $returnValue; + return Statistical\Averages::mode(...$args); } /** @@ -2575,7 +1923,7 @@ class Statistical public static function SKEW(...$args) { $aArgs = Functions::flattenArrayIndexed($args); - $mean = Averages::AVERAGE($aArgs); + $mean = Averages::average($aArgs); $stdDev = StandardDeviations::STDEV($aArgs); if ($stdDev === 0.0 || is_string($stdDev)) { @@ -2990,7 +2338,7 @@ class Statistical array_shift($mArgs); } - return Averages::AVERAGE($mArgs); + return Averages::average($mArgs); } return Functions::VALUE(); @@ -3142,6 +2490,6 @@ class Statistical } $n = count($dataSet); - return 1 - self::NORMSDIST((Averages::AVERAGE($dataSet) - $m0) / ($sigma / sqrt($n))); + return 1 - self::NORMSDIST((Averages::average($dataSet) - $m0) / ($sigma / sqrt($n))); } } diff --git a/src/PhpSpreadsheet/Calculation/Statistical/Averages.php b/src/PhpSpreadsheet/Calculation/Statistical/Averages.php index 14c9fef2..1a627e99 100644 --- a/src/PhpSpreadsheet/Calculation/Statistical/Averages.php +++ b/src/PhpSpreadsheet/Calculation/Statistical/Averages.php @@ -19,14 +19,14 @@ class Averages extends AggregateBase * * @return float|string (string if result is an error) */ - public static function AVEDEV(...$args) + public static function averageDeviations(...$args) { $aArgs = Functions::flattenArrayIndexed($args); // Return value $returnValue = 0; - $aMean = self::AVERAGE(...$args); + $aMean = self::average(...$args); if ($aMean === Functions::DIV0()) { return Functions::NAN(); } elseif ($aMean === Functions::VALUE()) { @@ -68,7 +68,7 @@ class Averages extends AggregateBase * * @return float|string (string if result is an error) */ - public static function AVERAGE(...$args) + public static function average(...$args) { $returnValue = $aCount = 0; @@ -107,7 +107,7 @@ class Averages extends AggregateBase * * @return float|string (string if result is an error) */ - public static function AVERAGEA(...$args) + public static function averageA(...$args) { $returnValue = null; @@ -134,4 +134,126 @@ class Averages extends AggregateBase return Functions::DIV0(); } + + /** + * MEDIAN. + * + * Returns the median of the given numbers. The median is the number in the middle of a set of numbers. + * + * Excel Function: + * MEDIAN(value1[,value2[, ...]]) + * + * @param mixed ...$args Data values + * + * @return float|string The result, or a string containing an error + */ + public static function median(...$args) + { + $aArgs = Functions::flattenArray($args); + + $returnValue = Functions::NAN(); + + $aArgs = self::filterArguments($aArgs); + $valueCount = count($aArgs); + if ($valueCount > 0) { + sort($aArgs, SORT_NUMERIC); + $valueCount = $valueCount / 2; + if ($valueCount == floor($valueCount)) { + $returnValue = ($aArgs[$valueCount--] + $aArgs[$valueCount]) / 2; + } else { + $valueCount = floor($valueCount); + $returnValue = $aArgs[$valueCount]; + } + } + + return $returnValue; + } + + /** + * MODE. + * + * Returns the most frequently occurring, or repetitive, value in an array or range of data + * + * Excel Function: + * MODE(value1[,value2[, ...]]) + * + * @param mixed ...$args Data values + * + * @return float|string The result, or a string containing an error + */ + public static function mode(...$args) + { + $returnValue = Functions::NA(); + + // Loop through arguments + $aArgs = Functions::flattenArray($args); + $aArgs = self::filterArguments($aArgs); + + if (!empty($aArgs)) { + return self::modeCalc($aArgs); + } + + return $returnValue; + } + + protected static function filterArguments($args) + { + return array_filter( + $args, + function ($value) { + // Is it a numeric value? + return (is_numeric($value)) && (!is_string($value)); + } + ); + } + + // + // Special variant of array_count_values that isn't limited to strings and integers, + // but can work with floating point numbers as values + // + private static function modeCalc($data) + { + $frequencyArray = []; + $index = 0; + $maxfreq = 0; + $maxfreqkey = ''; + $maxfreqdatum = ''; + foreach ($data as $datum) { + $found = false; + ++$index; + foreach ($frequencyArray as $key => $value) { + if ((string) $value['value'] == (string) $datum) { + ++$frequencyArray[$key]['frequency']; + $freq = $frequencyArray[$key]['frequency']; + if ($freq > $maxfreq) { + $maxfreq = $freq; + $maxfreqkey = $key; + $maxfreqdatum = $datum; + } elseif ($freq == $maxfreq) { + if ($frequencyArray[$key]['index'] < $frequencyArray[$maxfreqkey]['index']) { + $maxfreqkey = $key; + $maxfreqdatum = $datum; + } + } + $found = true; + + break; + } + } + + if ($found === false) { + $frequencyArray[] = [ + 'value' => $datum, + 'frequency' => 1, + 'index' => $index, + ]; + } + } + + if ($maxfreq <= 1) { + return Functions::NA(); + } + + return $maxfreqdatum; + } } diff --git a/src/PhpSpreadsheet/Calculation/Statistical/Confidence.php b/src/PhpSpreadsheet/Calculation/Statistical/Confidence.php index c4c2a7dd..3147859b 100644 --- a/src/PhpSpreadsheet/Calculation/Statistical/Confidence.php +++ b/src/PhpSpreadsheet/Calculation/Statistical/Confidence.php @@ -12,9 +12,9 @@ class Confidence * * Returns the confidence interval for a population mean * - * @param float $alpha - * @param float $stdDev Standard Deviation - * @param float $size + * @param mixed (float) $alpha + * @param mixed (float) $stdDev Standard Deviation + * @param mixed (float) $size * * @return float|string */ diff --git a/src/PhpSpreadsheet/Calculation/Statistical/Distributions/BaseValidations.php b/src/PhpSpreadsheet/Calculation/Statistical/Distributions/BaseValidations.php new file mode 100644 index 00000000..a8ab3e89 --- /dev/null +++ b/src/PhpSpreadsheet/Calculation/Statistical/Distributions/BaseValidations.php @@ -0,0 +1,36 @@ +getMessage(); + } + + if ($rMin > $rMax) { + $tmp = $rMin; + $rMin = $rMax; + $rMax = $tmp; + } + if (($value < $rMin) || ($value > $rMax) || ($alpha <= 0) || ($beta <= 0) || ($rMin == $rMax)) { + return Functions::NAN(); + } + + $value -= $rMin; + $value /= ($rMax - $rMin); + + return self::incompleteBeta($value, $alpha, $beta); + } + + /** + * BETAINV. + * + * Returns the inverse of the Beta distribution. + * + * @param mixed (float) $probability Probability at which you want to evaluate the distribution + * @param mixed (float) $alpha Parameter to the distribution + * @param mixed (float) $beta Parameter to the distribution + * @param mixed (float) $rMin Minimum value + * @param mixed (float) $rMax Maximum value + * + * @return float|string + */ + public static function inverse($probability, $alpha, $beta, $rMin = 0, $rMax = 1) + { + $probability = Functions::flattenSingleValue($probability); + $alpha = Functions::flattenSingleValue($alpha); + $beta = Functions::flattenSingleValue($beta); + $rMin = Functions::flattenSingleValue($rMin); + $rMax = Functions::flattenSingleValue($rMax); + + try { + $probability = self::validateFloat($probability); + $alpha = self::validateFloat($alpha); + $beta = self::validateFloat($beta); + $rMax = self::validateFloat($rMax); + $rMin = self::validateFloat($rMin); + } catch (Exception $e) { + return $e->getMessage(); + } + + if ($rMin > $rMax) { + $tmp = $rMin; + $rMin = $rMax; + $rMax = $tmp; + } + if (($alpha <= 0) || ($beta <= 0) || ($rMin == $rMax) || ($probability <= 0) || ($probability > 1)) { + return Functions::NAN(); + } + + return self::calculateInverse($probability, $alpha, $beta, $rMin, $rMax); + } + + private static function calculateInverse(float $probability, float $alpha, float $beta, float $rMin, float $rMax) + { + $a = 0; + $b = 2; + + $i = 0; + while ((($b - $a) > Functions::PRECISION) && (++$i <= self::MAX_ITERATIONS)) { + $guess = ($a + $b) / 2; + $result = self::distribution($guess, $alpha, $beta); + if (($result === $probability) || ($result === 0.0)) { + $b = $a; + } elseif ($result > $probability) { + $b = $guess; + } else { + $a = $guess; + } + } + + if ($i === self::MAX_ITERATIONS) { + return Functions::NA(); + } + + return round($rMin + $guess * ($rMax - $rMin), 12); + } + + /** + * Incomplete beta function. + * + * @author Jaco van Kooten + * @author Paul Meagher + * + * The computation is based on formulas from Numerical Recipes, Chapter 6.4 (W.H. Press et al, 1992). + * + * @param mixed $x require 0<=x<=1 + * @param mixed $p require p>0 + * @param mixed $q require q>0 + * + * @return float 0 if x<0, p<=0, q<=0 or p+q>2.55E305 and 1 if x>1 to avoid errors and over/underflow + */ + public static function incompleteBeta(float $x, float $p, float $q): float + { + if ($x <= 0.0) { + return 0.0; + } elseif ($x >= 1.0) { + return 1.0; + } elseif (($p <= 0.0) || ($q <= 0.0) || (($p + $q) > self::LOG_GAMMA_X_MAX_VALUE)) { + return 0.0; + } + + $beta_gam = exp((0 - self::logBeta($p, $q)) + $p * log($x) + $q * log(1.0 - $x)); + if ($x < ($p + 1.0) / ($p + $q + 2.0)) { + return $beta_gam * self::betaFraction($x, $p, $q) / $p; + } + + return 1.0 - ($beta_gam * self::betaFraction(1 - $x, $q, $p) / $q); + } + + // Function cache for logBeta function + private static $logBetaCacheP = 0.0; + + private static $logBetaCacheQ = 0.0; + + private static $logBetaCacheResult = 0.0; + + /** + * The natural logarithm of the beta function. + * + * @param mixed $p require p>0 + * @param mixed $q require q>0 + * + * @return float 0 if p<=0, q<=0 or p+q>2.55E305 to avoid errors and over/underflow + * + * @author Jaco van Kooten + */ + private static function logBeta(float $p, float $q): float + { + if ($p != self::$logBetaCacheP || $q != self::$logBetaCacheQ) { + self::$logBetaCacheP = $p; + self::$logBetaCacheQ = $q; + if (($p <= 0.0) || ($q <= 0.0) || (($p + $q) > self::LOG_GAMMA_X_MAX_VALUE)) { + self::$logBetaCacheResult = 0.0; + } else { + self::$logBetaCacheResult = Gamma::logGamma($p) + Gamma::logGamma($q) - Gamma::logGamma($p + $q); + } + } + + return self::$logBetaCacheResult; + } + + /** + * Evaluates of continued fraction part of incomplete beta function. + * Based on an idea from Numerical Recipes (W.H. Press et al, 1992). + * + * @author Jaco van Kooten + * + * @param mixed $x + * @param mixed $p + * @param mixed $q + */ + private static function betaFraction(float $x, float $p, float $q): float + { + $c = 1.0; + $sum_pq = $p + $q; + $p_plus = $p + 1.0; + $p_minus = $p - 1.0; + $h = 1.0 - $sum_pq * $x / $p_plus; + if (abs($h) < self::XMININ) { + $h = self::XMININ; + } + $h = 1.0 / $h; + $frac = $h; + $m = 1; + $delta = 0.0; + while ($m <= self::MAX_ITERATIONS && abs($delta - 1.0) > Functions::PRECISION) { + $m2 = 2 * $m; + // even index for d + $d = $m * ($q - $m) * $x / (($p_minus + $m2) * ($p + $m2)); + $h = 1.0 + $d * $h; + if (abs($h) < self::XMININ) { + $h = self::XMININ; + } + $h = 1.0 / $h; + $c = 1.0 + $d / $c; + if (abs($c) < self::XMININ) { + $c = self::XMININ; + } + $frac *= $h * $c; + // odd index for d + $d = -($p + $m) * ($sum_pq + $m) * $x / (($p + $m2) * ($p_plus + $m2)); + $h = 1.0 + $d * $h; + if (abs($h) < self::XMININ) { + $h = self::XMININ; + } + $h = 1.0 / $h; + $c = 1.0 + $d / $c; + if (abs($c) < self::XMININ) { + $c = self::XMININ; + } + $delta = $h * $c; + $frac *= $delta; + ++$m; + } + + return $frac; + } + + private static function betaValue(float $a, float $b): float + { + return (Gamma::gammaValue($a) * Gamma::gammaValue($b)) / + Gamma::gammaValue($a + $b); + } + + private static function regularizedIncompleteBeta(float $value, float $a, float $b): float + { + return self::incompleteBeta($value, $a, $b) / self::betaValue($a, $b); + } +} diff --git a/src/PhpSpreadsheet/Calculation/Statistical/Distributions/ChiSquared.php b/src/PhpSpreadsheet/Calculation/Statistical/Distributions/ChiSquared.php new file mode 100644 index 00000000..2d5e4496 --- /dev/null +++ b/src/PhpSpreadsheet/Calculation/Statistical/Distributions/ChiSquared.php @@ -0,0 +1,127 @@ +getMessage(); + } + + if ($degrees < 1) { + return Functions::NAN(); + } + if ($value < 0) { + if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) { + return 1; + } + + return Functions::NAN(); + } + + return 1 - (Gamma::incompleteGamma($degrees / 2, $value / 2) / Gamma::gammaValue($degrees / 2)); + } + + /** + * CHIINV. + * + * Returns the one-tailed probability of the chi-squared distribution. + * + * @param mixed (float) $probability Probability for the function + * @param mixed (int) $degrees degrees of freedom + * + * @return float|string + */ + public static function inverse($probability, $degrees) + { + $probability = Functions::flattenSingleValue($probability); + $degrees = Functions::flattenSingleValue($degrees); + + try { + $probability = self::validateFloat($probability); + $degrees = self::validateInt($degrees); + } catch (Exception $e) { + return $e->getMessage(); + } + + if ($probability < 0.0 || $probability > 1.0 || $degrees < 1) { + return Functions::NAN(); + } + + return self::calculateInverse($degrees, $probability); + } + + /** + * @return float|string + */ + protected static function calculateInverse(int $degrees, float $probability) + { + $xLo = 100; + $xHi = 0; + + $x = $xNew = 1; + $dx = 1; + $i = 0; + + while ((abs($dx) > Functions::PRECISION) && (++$i <= self::MAX_ITERATIONS)) { + // Apply Newton-Raphson step + $result = 1 - (Gamma::incompleteGamma($degrees / 2, $x / 2) + / Gamma::gammaValue($degrees / 2)); + $error = $result - $probability; + + if ($error == 0.0) { + $dx = 0; + } elseif ($error < 0.0) { + $xLo = $x; + } else { + $xHi = $x; + } + + // Avoid division by zero + if ($result != 0.0) { + $dx = $error / $result; + $xNew = $x - $dx; + } + + // If the NR fails to converge (which for example may be the + // case if the initial guess is too rough) we apply a bisection + // step to determine a more narrow interval around the root. + if (($xNew < $xLo) || ($xNew > $xHi) || ($result == 0.0)) { + $xNew = ($xLo + $xHi) / 2; + $dx = $xNew - $x; + } + $x = $xNew; + } + + if ($i === self::MAX_ITERATIONS) { + return Functions::NA(); + } + + return $x; + } +} diff --git a/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Fisher.php b/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Fisher.php new file mode 100644 index 00000000..1d3a7be4 --- /dev/null +++ b/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Fisher.php @@ -0,0 +1,63 @@ +getMessage(); + } + + if (($value <= -1) || ($value >= 1)) { + return Functions::NAN(); + } + + return 0.5 * log((1 + $value) / (1 - $value)); + } + + /** + * FISHERINV. + * + * Returns the inverse of the Fisher transformation. Use this transformation when + * analyzing correlations between ranges or arrays of data. If y = FISHER(x), then + * FISHERINV(y) = x. + * + * @param mixed (float) $value + * + * @return float|string + */ + public static function inverse($value) + { + $value = Functions::flattenSingleValue($value); + + try { + self::validateFloat($value); + } catch (Exception $e) { + return $e->getMessage(); + } + + return (exp(2 * $value) - 1) / (exp(2 * $value) + 1); + } +} diff --git a/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Gamma.php b/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Gamma.php new file mode 100644 index 00000000..aa487329 --- /dev/null +++ b/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Gamma.php @@ -0,0 +1,129 @@ +getMessage(); + } + + if ((((int) $value) == ((float) $value)) && $value <= 0.0) { + return Functions::NAN(); + } + + return self::gammaValue($value); + } + + /** + * GAMMADIST. + * + * Returns the gamma distribution. + * + * @param mixed (float) $value Value at which you want to evaluate the distribution + * @param mixed (float) $a Parameter to the distribution + * @param mixed (float) $b Parameter to the distribution + * @param mixed (bool) $cumulative + * + * @return float|string + */ + public static function distribution($value, $a, $b, $cumulative) + { + $value = Functions::flattenSingleValue($value); + $a = Functions::flattenSingleValue($a); + $b = Functions::flattenSingleValue($b); + + try { + $value = self::validateFloat($value); + $a = self::validateFloat($a); + $b = self::validateFloat($b); + $cumulative = self::validateBool($cumulative); + } catch (Exception $e) { + return $e->getMessage(); + } + + if (($value < 0) || ($a <= 0) || ($b <= 0)) { + return Functions::NAN(); + } + + return self::calculateDistribution($value, $a, $b, $cumulative); + } + + /** + * GAMMAINV. + * + * Returns the inverse of the Gamma distribution. + * + * @param mixed (float) $probability Probability at which you want to evaluate the distribution + * @param mixed (float) $alpha Parameter to the distribution + * @param mixed (float) $beta Parameter to the distribution + * + * @return float|string + */ + public static function inverse($probability, $alpha, $beta) + { + $probability = Functions::flattenSingleValue($probability); + $alpha = Functions::flattenSingleValue($alpha); + $beta = Functions::flattenSingleValue($beta); + + try { + $probability = self::validateFloat($probability); + $alpha = self::validateFloat($alpha); + $beta = self::validateFloat($beta); + } catch (Exception $e) { + return $e->getMessage(); + } + + if (($alpha <= 0.0) || ($beta <= 0.0) || ($probability < 0.0) || ($probability > 1.0)) { + return Functions::NAN(); + } + + return self::calculateInverse($probability, $alpha, $beta); + } + + /** + * GAMMALN. + * + * Returns the natural logarithm of the gamma function. + * + * @param mixed (float) $value + * + * @return float|string + */ + public static function ln($value) + { + $value = Functions::flattenSingleValue($value); + + try { + $value = self::validateFloat($value); + } catch (Exception $e) { + return $e->getMessage(); + } + + if ($value <= 0) { + return Functions::NAN(); + } + + return log(self::gammaValue($value)); + } +} diff --git a/src/PhpSpreadsheet/Calculation/Statistical/Distributions/GammaBase.php b/src/PhpSpreadsheet/Calculation/Statistical/Distributions/GammaBase.php new file mode 100644 index 00000000..ae951af3 --- /dev/null +++ b/src/PhpSpreadsheet/Calculation/Statistical/Distributions/GammaBase.php @@ -0,0 +1,377 @@ + Functions::PRECISION) && (++$i <= self::MAX_ITERATIONS)) { + // Apply Newton-Raphson step + $error = self::calculateDistribution($x, $alpha, $beta, true) - $probability; + if ($error < 0.0) { + $xLo = $x; + } else { + $xHi = $x; + } + + $pdf = self::calculateDistribution($x, $alpha, $beta, false); + // Avoid division by zero + if ($pdf !== 0.0) { + $dx = $error / $pdf; + $xNew = $x - $dx; + } + + // If the NR fails to converge (which for example may be the + // case if the initial guess is too rough) we apply a bisection + // step to determine a more narrow interval around the root. + if (($xNew < $xLo) || ($xNew > $xHi) || ($pdf == 0.0)) { + $xNew = ($xLo + $xHi) / 2; + $dx = $xNew - $x; + } + $x = $xNew; + } + + if ($i === self::MAX_ITERATIONS) { + return Functions::NA(); + } + + return $x; + } + + // + // Implementation of the incomplete Gamma function + // + public static function incompleteGamma(float $a, float $x): float + { + static $max = 32; + $summer = 0; + for ($n = 0; $n <= $max; ++$n) { + $divisor = $a; + for ($i = 1; $i <= $n; ++$i) { + $divisor *= ($a + $i); + } + $summer += ($x ** $n / $divisor); + } + + return $x ** $a * exp(0 - $x) * $summer; + } + + // + // Implementation of the Gamma function + // + public static function gammaValue(float $value): float + { + if ($value == 0.0) { + return 0; + } + + static $p0 = 1.000000000190015; + static $p = [ + 1 => 76.18009172947146, + 2 => -86.50532032941677, + 3 => 24.01409824083091, + 4 => -1.231739572450155, + 5 => 1.208650973866179e-3, + 6 => -5.395239384953e-6, + ]; + + $y = $x = $value; + $tmp = $x + 5.5; + $tmp -= ($x + 0.5) * log($tmp); + + $summer = $p0; + for ($j = 1; $j <= 6; ++$j) { + $summer += ($p[$j] / ++$y); + } + + return exp(0 - $tmp + log(self::SQRT2PI * $summer / $x)); + } + + /** + * logGamma function. + * + * @version 1.1 + * + * @author Jaco van Kooten + * + * Original author was Jaco van Kooten. Ported to PHP by Paul Meagher. + * + * The natural logarithm of the gamma function.
+ * Based on public domain NETLIB (Fortran) code by W. J. Cody and L. Stoltz
+ * Applied Mathematics Division
+ * Argonne National Laboratory
+ * Argonne, IL 60439
+ *

+ * References: + *

    + *
  1. W. J. Cody and K. E. Hillstrom, 'Chebyshev Approximations for the Natural + * Logarithm of the Gamma Function,' Math. Comp. 21, 1967, pp. 198-203.
  2. + *
  3. K. E. Hillstrom, ANL/AMD Program ANLC366S, DGAMMA/DLGAMA, May, 1969.
  4. + *
  5. Hart, Et. Al., Computer Approximations, Wiley and sons, New York, 1968.
  6. + *
+ *

+ *

+ * From the original documentation: + *

+ *

+ * This routine calculates the LOG(GAMMA) function for a positive real argument X. + * Computation is based on an algorithm outlined in references 1 and 2. + * The program uses rational functions that theoretically approximate LOG(GAMMA) + * to at least 18 significant decimal digits. The approximation for X > 12 is from + * reference 3, while approximations for X < 12.0 are similar to those in reference + * 1, but are unpublished. The accuracy achieved depends on the arithmetic system, + * the compiler, the intrinsic functions, and proper selection of the + * machine-dependent constants. + *

+ *

+ * Error returns:
+ * The program returns the value XINF for X .LE. 0.0 or when overflow would occur. + * The computation is believed to be free of underflow and overflow. + *

+ * + * @return float MAX_VALUE for x < 0.0 or when overflow would occur, i.e. x > 2.55E305 + */ + + // Log Gamma related constants + private const LG_D1 = -0.5772156649015328605195174; + + private const LG_D2 = 0.4227843350984671393993777; + + private const LG_D4 = 1.791759469228055000094023; + + private const LG_P1 = [ + 4.945235359296727046734888, + 201.8112620856775083915565, + 2290.838373831346393026739, + 11319.67205903380828685045, + 28557.24635671635335736389, + 38484.96228443793359990269, + 26377.48787624195437963534, + 7225.813979700288197698961, + ]; + + private const LG_P2 = [ + 4.974607845568932035012064, + 542.4138599891070494101986, + 15506.93864978364947665077, + 184793.2904445632425417223, + 1088204.76946882876749847, + 3338152.967987029735917223, + 5106661.678927352456275255, + 3074109.054850539556250927, + ]; + + private const LG_P4 = [ + 14745.02166059939948905062, + 2426813.369486704502836312, + 121475557.4045093227939592, + 2663432449.630976949898078, + 29403789566.34553899906876, + 170266573776.5398868392998, + 492612579337.743088758812, + 560625185622.3951465078242, + ]; + + private const LG_Q1 = [ + 67.48212550303777196073036, + 1113.332393857199323513008, + 7738.757056935398733233834, + 27639.87074403340708898585, + 54993.10206226157329794414, + 61611.22180066002127833352, + 36351.27591501940507276287, + 8785.536302431013170870835, + ]; + + private const LG_Q2 = [ + 183.0328399370592604055942, + 7765.049321445005871323047, + 133190.3827966074194402448, + 1136705.821321969608938755, + 5267964.117437946917577538, + 13467014.54311101692290052, + 17827365.30353274213975932, + 9533095.591844353613395747, + ]; + + private const LG_Q4 = [ + 2690.530175870899333379843, + 639388.5654300092398984238, + 41355999.30241388052042842, + 1120872109.61614794137657, + 14886137286.78813811542398, + 101680358627.2438228077304, + 341747634550.7377132798597, + 446315818741.9713286462081, + ]; + + private const LG_C = [ + -0.001910444077728, + 8.4171387781295e-4, + -5.952379913043012e-4, + 7.93650793500350248e-4, + -0.002777777777777681622553, + 0.08333333333333333331554247, + 0.0057083835261, + ]; + + // Rough estimate of the fourth root of logGamma_xBig + private const LG_FRTBIG = 2.25e76; + + private const PNT68 = 0.6796875; + + // Function cache for logGamma + private static $logGammaCacheResult = 0.0; + + private static $logGammaCacheX = 0.0; + + public static function logGamma(float $x): float + { + if ($x == self::$logGammaCacheX) { + return self::$logGammaCacheResult; + } + + $y = $x; + if ($y > 0.0 && $y <= self::LOG_GAMMA_X_MAX_VALUE) { + if ($y <= self::EPS) { + $res = -log($y); + } elseif ($y <= 1.5) { + $res = self::logGamma1($y); + } elseif ($y <= 4.0) { + $res = self::logGamma2($y); + } elseif ($y <= 12.0) { + $res = self::logGamma3($y); + } else { + $res = self::logGamma4($y); + } + } else { + // -------------------------- + // Return for bad arguments + // -------------------------- + $res = self::MAX_VALUE; + } + + // ------------------------------ + // Final adjustments and return + // ------------------------------ + self::$logGammaCacheX = $x; + self::$logGammaCacheResult = $res; + + return $res; + } + + private static function logGamma1(float $y) + { + // --------------------- + // EPS .LT. X .LE. 1.5 + // --------------------- + if ($y < self::PNT68) { + $corr = -log($y); + $xm1 = $y; + } else { + $corr = 0.0; + $xm1 = $y - 1.0; + } + + $xden = 1.0; + $xnum = 0.0; + if ($y <= 0.5 || $y >= self::PNT68) { + for ($i = 0; $i < 8; ++$i) { + $xnum = $xnum * $xm1 + self::LG_P1[$i]; + $xden = $xden * $xm1 + self::LG_Q1[$i]; + } + + return $corr + $xm1 * (self::LG_D1 + $xm1 * ($xnum / $xden)); + } + + $xm2 = $y - 1.0; + for ($i = 0; $i < 8; ++$i) { + $xnum = $xnum * $xm2 + self::LG_P2[$i]; + $xden = $xden * $xm2 + self::LG_Q2[$i]; + } + + return $corr + $xm2 * (self::LG_D2 + $xm2 * ($xnum / $xden)); + } + + private static function logGamma2(float $y) + { + // --------------------- + // 1.5 .LT. X .LE. 4.0 + // --------------------- + $xm2 = $y - 2.0; + $xden = 1.0; + $xnum = 0.0; + for ($i = 0; $i < 8; ++$i) { + $xnum = $xnum * $xm2 + self::LG_P2[$i]; + $xden = $xden * $xm2 + self::LG_Q2[$i]; + } + + return $xm2 * (self::LG_D2 + $xm2 * ($xnum / $xden)); + } + + protected static function logGamma3(float $y) + { + // ---------------------- + // 4.0 .LT. X .LE. 12.0 + // ---------------------- + $xm4 = $y - 4.0; + $xden = -1.0; + $xnum = 0.0; + for ($i = 0; $i < 8; ++$i) { + $xnum = $xnum * $xm4 + self::LG_P4[$i]; + $xden = $xden * $xm4 + self::LG_Q4[$i]; + } + + return self::LG_D4 + $xm4 * ($xnum / $xden); + } + + protected static function logGamma4(float $y) + { + // --------------------------------- + // Evaluate for argument .GE. 12.0 + // --------------------------------- + $res = 0.0; + if ($y <= self::LG_FRTBIG) { + $res = self::LG_C[6]; + $ysq = $y * $y; + for ($i = 0; $i < 6; ++$i) { + $res = $res / $ysq + self::LG_C[$i]; + } + $res /= $y; + $corr = log($y); + $res = $res + log(self::SQRT2PI) - 0.5 * $corr; + $res += $y * ($corr - 1.0); + } + + return $res; + } +} diff --git a/src/PhpSpreadsheet/Calculation/Statistical/Permutations.php b/src/PhpSpreadsheet/Calculation/Statistical/Permutations.php index 84c10719..5d03e5d5 100644 --- a/src/PhpSpreadsheet/Calculation/Statistical/Permutations.php +++ b/src/PhpSpreadsheet/Calculation/Statistical/Permutations.php @@ -16,8 +16,8 @@ class Permutations * combinations, for which the internal order is not significant. Use this function * for lottery-style probability calculations. * - * @param int $numObjs Number of different objects - * @param int $numInSet Number of objects in each permutation + * @param mixed (int) $numObjs Number of different objects + * @param mixed (int) $numInSet Number of objects in each permutation * * @return int|string Number of permutations, or a string containing an error */ diff --git a/src/PhpSpreadsheet/Calculation/Statistical/StandardDeviations.php b/src/PhpSpreadsheet/Calculation/Statistical/StandardDeviations.php index 28a25a75..4f15615c 100644 --- a/src/PhpSpreadsheet/Calculation/Statistical/StandardDeviations.php +++ b/src/PhpSpreadsheet/Calculation/Statistical/StandardDeviations.php @@ -23,7 +23,7 @@ class StandardDeviations extends VarianceBase { $aArgs = Functions::flattenArrayIndexed($args); - $aMean = Averages::AVERAGE($aArgs); + $aMean = Averages::average($aArgs); if (!is_string($aMean)) { $returnValue = 0.0; @@ -67,7 +67,7 @@ class StandardDeviations extends VarianceBase { $aArgs = Functions::flattenArrayIndexed($args); - $aMean = Averages::AVERAGEA($aArgs); + $aMean = Averages::averageA($aArgs); if (!is_string($aMean)) { $returnValue = 0.0; @@ -109,7 +109,7 @@ class StandardDeviations extends VarianceBase { $aArgs = Functions::flattenArrayIndexed($args); - $aMean = Averages::AVERAGE($aArgs); + $aMean = Averages::average($aArgs); if (!is_string($aMean)) { $returnValue = 0.0; @@ -153,7 +153,7 @@ class StandardDeviations extends VarianceBase { $aArgs = Functions::flattenArrayIndexed($args); - $aMean = Averages::AVERAGEA($aArgs); + $aMean = Averages::averageA($aArgs); if (!is_string($aMean)) { $returnValue = 0.0; diff --git a/src/PhpSpreadsheet/Calculation/Statistical/Trends.php b/src/PhpSpreadsheet/Calculation/Statistical/Trends.php index a1137cef..b1dfbaef 100644 --- a/src/PhpSpreadsheet/Calculation/Statistical/Trends.php +++ b/src/PhpSpreadsheet/Calculation/Statistical/Trends.php @@ -107,7 +107,7 @@ class Trends * Calculates, or predicts, a future value by using existing values. * The predicted value is a y-value for a given x-value. * - * @param float $xValue Value of X for which we want to find Y + * @param mixed (float) $xValue Value of X for which we want to find Y * @param mixed $yValues array of mixed Data Series Y * @param mixed $xValues of mixed Data Series X * @@ -140,7 +140,7 @@ class Trends * @param mixed[] $yValues Data Series Y * @param mixed[] $xValues Data Series X * @param mixed[] $newValues Values of X for which we want to find Y - * @param bool $const a logical value specifying whether to force the intersect to equal 0 + * @param mixed (bool) $const a logical value specifying whether to force the intersect to equal 0 * * @return array of float */ @@ -196,8 +196,8 @@ class Trends * * @param mixed[] $yValues Data Series Y * @param null|mixed[] $xValues Data Series X - * @param bool $const a logical value specifying whether to force the intersect to equal 0 - * @param bool $stats a logical value specifying whether to return additional regression statistics + * @param mixed (bool) $const a logical value specifying whether to force the intersect to equal 0 + * @param mixed (bool) $stats a logical value specifying whether to return additional regression statistics * * @return array|int|string The result, or a string containing an error */ @@ -257,8 +257,8 @@ class Trends * * @param mixed[] $yValues Data Series Y * @param null|mixed[] $xValues Data Series X - * @param bool $const a logical value specifying whether to force the intersect to equal 0 - * @param bool $stats a logical value specifying whether to return additional regression statistics + * @param mixed (bool) $const a logical value specifying whether to force the intersect to equal 0 + * @param mixed (bool) $stats a logical value specifying whether to return additional regression statistics * * @return array|int|string The result, or a string containing an error */ @@ -397,7 +397,7 @@ class Trends * @param mixed[] $yValues Data Series Y * @param mixed[] $xValues Data Series X * @param mixed[] $newValues Values of X for which we want to find Y - * @param bool $const a logical value specifying whether to force the intersect to equal 0 + * @param mixed (bool) $const a logical value specifying whether to force the intersect to equal 0 * * @return array of float */ diff --git a/src/PhpSpreadsheet/Calculation/TextData/CaseConvert.php b/src/PhpSpreadsheet/Calculation/TextData/CaseConvert.php index 2a275133..846a3124 100644 --- a/src/PhpSpreadsheet/Calculation/TextData/CaseConvert.php +++ b/src/PhpSpreadsheet/Calculation/TextData/CaseConvert.php @@ -13,7 +13,7 @@ class CaseConvert * * Converts a string value to upper case. * - * @param string $mixedCaseValue + * @param mixed (string) $mixedCaseValue */ public static function lower($mixedCaseValue): string { @@ -31,7 +31,7 @@ class CaseConvert * * Converts a string value to upper case. * - * @param string $mixedCaseValue + * @param mixed (string) $mixedCaseValue */ public static function upper($mixedCaseValue): string { @@ -49,7 +49,7 @@ class CaseConvert * * Converts a string value to upper case. * - * @param string $mixedCaseValue + * @param mixed (string) $mixedCaseValue */ public static function proper($mixedCaseValue): string { diff --git a/src/PhpSpreadsheet/Calculation/TextData/CharacterConvert.php b/src/PhpSpreadsheet/Calculation/TextData/CharacterConvert.php index 2263b1a7..0003e0cd 100644 --- a/src/PhpSpreadsheet/Calculation/TextData/CharacterConvert.php +++ b/src/PhpSpreadsheet/Calculation/TextData/CharacterConvert.php @@ -10,7 +10,7 @@ class CharacterConvert /** * CHARACTER. * - * @param string $character Value + * @param mixed (int) $character Value */ public static function character($character): string { @@ -31,7 +31,7 @@ class CharacterConvert /** * ASCIICODE. * - * @param string $characters Value + * @param mixed (string) $characters Value * * @return int|string A string if arguments are invalid */ diff --git a/src/PhpSpreadsheet/Calculation/TextData/Extract.php b/src/PhpSpreadsheet/Calculation/TextData/Extract.php index 126d9f49..7ef76546 100644 --- a/src/PhpSpreadsheet/Calculation/TextData/Extract.php +++ b/src/PhpSpreadsheet/Calculation/TextData/Extract.php @@ -10,8 +10,8 @@ class Extract /** * LEFT. * - * @param string $value Value - * @param int $chars Number of characters + * @param mixed (string) $value Value + * @param mixed (int) $chars Number of characters */ public static function left($value = '', $chars = 1): string { @@ -32,9 +32,9 @@ class Extract /** * MID. * - * @param string $value Value - * @param int $start Start character - * @param int $chars Number of characters + * @param mixed (string) $value Value + * @param mixed (int) $start Start character + * @param mixed (int) $chars Number of characters */ public static function mid($value = '', $start = 1, $chars = null): string { @@ -56,8 +56,8 @@ class Extract /** * RIGHT. * - * @param string $value Value - * @param int $chars Number of characters + * @param mixed (string) $value Value + * @param mixed (int) $chars Number of characters */ public static function right($value = '', $chars = 1): string { diff --git a/src/PhpSpreadsheet/Calculation/TextData/Format.php b/src/PhpSpreadsheet/Calculation/TextData/Format.php index 2cea474d..f24ed7ae 100644 --- a/src/PhpSpreadsheet/Calculation/TextData/Format.php +++ b/src/PhpSpreadsheet/Calculation/TextData/Format.php @@ -18,8 +18,8 @@ class Format * This function converts a number to text using currency format, with the decimals rounded to the specified place. * The format used is $#,##0.00_);($#,##0.00).. * - * @param float $value The value to format - * @param int $decimals The number of digits to display to the right of the decimal point. + * @param mixed (float) $value The value to format + * @param mixed (int) $decimals The number of digits to display to the right of the decimal point. * If decimals is negative, number is rounded to the left of the decimal point. * If you omit decimals, it is assumed to be 2 */ @@ -54,7 +54,7 @@ class Format * * @param mixed $value Value to check * @param mixed $decimals - * @param bool $noCommas + * @param mixed (bool) $noCommas */ public static function FIXEDFORMAT($value, $decimals = 2, $noCommas = false): string { @@ -72,7 +72,7 @@ class Format if ($decimals < 0) { $decimals = 0; } - if (!$noCommas) { + if ($noCommas === false) { $valueResult = number_format( $valueResult, $decimals, @@ -88,7 +88,7 @@ class Format * TEXTFORMAT. * * @param mixed $value Value to check - * @param string $format Format mask to use + * @param mixed (string) $format Format mask to use */ public static function TEXTFORMAT($value, $format): string { @@ -152,8 +152,8 @@ class Format * NUMBERVALUE. * * @param mixed $value Value to check - * @param string $decimalSeparator decimal separator, defaults to locale defined value - * @param string $groupSeparator group/thosands separator, defaults to locale defined value + * @param mixed (string) $decimalSeparator decimal separator, defaults to locale defined value + * @param mixed (string) $groupSeparator group/thosands separator, defaults to locale defined value * * @return float|string */ diff --git a/src/PhpSpreadsheet/Calculation/TextData/Replace.php b/src/PhpSpreadsheet/Calculation/TextData/Replace.php index 9a849ba0..a06d4364 100644 --- a/src/PhpSpreadsheet/Calculation/TextData/Replace.php +++ b/src/PhpSpreadsheet/Calculation/TextData/Replace.php @@ -10,10 +10,10 @@ class Replace /** * REPLACE. * - * @param string $oldText String to modify - * @param int $start Start character - * @param int $chars Number of characters - * @param string $newText String to replace in defined position + * @param mixed (string) $oldText String to modify + * @param mixed (int) $start Start character + * @param mixed (int) $chars Number of characters + * @param mixed (string) $newText String to replace in defined position */ public static function replace($oldText, $start, $chars, $newText): string { @@ -31,10 +31,10 @@ class Replace /** * SUBSTITUTE. * - * @param string $text Value - * @param string $fromText From Value - * @param string $toText To Value - * @param int $instance Instance Number + * @param mixed (string) $text Value + * @param mixed (string) $fromText From Value + * @param mixed (string) $toText To Value + * @param mixed (int) $instance Instance Number */ public static function substitute($text = '', $fromText = '', $toText = '', $instance = 0): string { diff --git a/src/PhpSpreadsheet/Calculation/TextData/Search.php b/src/PhpSpreadsheet/Calculation/TextData/Search.php index acbe6a24..cf1bf241 100644 --- a/src/PhpSpreadsheet/Calculation/TextData/Search.php +++ b/src/PhpSpreadsheet/Calculation/TextData/Search.php @@ -11,9 +11,9 @@ class Search /** * SEARCHSENSITIVE. * - * @param string $needle The string to look for - * @param string $haystack The string in which to look - * @param int $offset Offset within $haystack + * @param mixed (string) $needle The string to look for + * @param mixed (string) $haystack The string in which to look + * @param mixed (int) $offset Offset within $haystack * * @return int|string */ @@ -46,9 +46,9 @@ class Search /** * SEARCHINSENSITIVE. * - * @param string $needle The string to look for - * @param string $haystack The string in which to look - * @param int $offset Offset within $haystack + * @param mixed (string) $needle The string to look for + * @param mixed (string) $haystack The string in which to look + * @param mixed (int) $offset Offset within $haystack * * @return int|string */ diff --git a/src/PhpSpreadsheet/Calculation/TextData/Text.php b/src/PhpSpreadsheet/Calculation/TextData/Text.php index a47d373b..338cdd20 100644 --- a/src/PhpSpreadsheet/Calculation/TextData/Text.php +++ b/src/PhpSpreadsheet/Calculation/TextData/Text.php @@ -10,7 +10,7 @@ class Text /** * STRINGLENGTH. * - * @param string $value Value + * @param mixed (string) $value Value */ public static function length($value = ''): int { @@ -28,8 +28,8 @@ class Text * EXACT is case-sensitive but ignores formatting differences. * Use EXACT to test text being entered into a document. * - * @param $value1 - * @param $value2 + * @param mixed (string) $value1 + * @param mixed (string) $value2 */ public static function exact($value1, $value2): bool { diff --git a/src/PhpSpreadsheet/Calculation/TextData/Trim.php b/src/PhpSpreadsheet/Calculation/TextData/Trim.php index 0d5688b0..01fff1a8 100644 --- a/src/PhpSpreadsheet/Calculation/TextData/Trim.php +++ b/src/PhpSpreadsheet/Calculation/TextData/Trim.php @@ -12,7 +12,7 @@ class Trim /** * TRIMNONPRINTABLE. * - * @param mixed $stringValue Value to check + * @param mixed (string) $stringValue Value to check * * @return null|string */ @@ -38,7 +38,7 @@ class Trim /** * TRIMSPACES. * - * @param mixed $stringValue Value to check + * @param mixed (string) $stringValue Value to check * * @return null|string */ diff --git a/tests/data/Calculation/Statistical/BETADIST.php b/tests/data/Calculation/Statistical/BETADIST.php index 9fbecaaa..2046e189 100644 --- a/tests/data/Calculation/Statistical/BETADIST.php +++ b/tests/data/Calculation/Statistical/BETADIST.php @@ -25,12 +25,56 @@ return [ 0.685470581054, 2, 8, 10, 1, 3, ], + [ + 0.4059136, + 0.4, 4, 5, + ], + [ + '#VALUE!', + 'NAN', 8, 10, 1, 3, + ], [ '#VALUE!', 2, 'NAN', 10, 1, 3, ], [ + '#VALUE!', + 2, 8, 'NAN', 1, 3, + ], + [ + '#VALUE!', + 2, 8, 10, 'NAN', 3, + ], + [ + '#VALUE!', + 2, 8, 10, 1, 'NAN', + ], + 'alpha < 0' => [ '#NUM!', 2, -8, 10, 1, 3, ], + 'alpha = 0' => [ + '#NUM!', + 2, 0, 10, 1, 3, + ], + 'beta < 0' => [ + '#NUM!', + 2, 8, -10, 1, 3, + ], + 'beta = 0' => [ + '#NUM!', + 2, 8, 0, 1, 3, + ], + 'value < Min' => [ + '#NUM!', + 0.5, 8, 10, 1, 3, + ], + 'value > Max' => [ + '#NUM!', + 3.5, 8, 10, 1, 3, + ], + 'Min = Max' => [ + '#NUM!', + 2, 8, 10, 2, 2, + ], ]; diff --git a/tests/data/Calculation/Statistical/BETAINV.php b/tests/data/Calculation/Statistical/BETAINV.php index 5afe14cb..4d8cb5bd 100644 --- a/tests/data/Calculation/Statistical/BETAINV.php +++ b/tests/data/Calculation/Statistical/BETAINV.php @@ -25,12 +25,56 @@ return [ 0.303225844664, 0.2, 4, 5, 0, 1, ], + [ + '#VALUE!', + 'NAN', 4, 5, 0, 1, + ], [ '#VALUE!', 0.2, 'NAN', 5, 0, 1, ], [ + '#VALUE!', + 0.2, 4, 'NAN', 0, 1, + ], + [ + '#VALUE!', + 0.2, 4, 5, 'NAN', 1, + ], + [ + '#VALUE!', + 0.2, 4, 5, 0, 'NAN', + ], + 'alpha < 0' => [ '#NUM!', 0.2, -4, 5, 0, 1, ], + 'alpha = 0' => [ + '#NUM!', + 0.2, 0, 5, 0, 1, + ], + 'beta < 0' => [ + '#NUM!', + 0.2, 4, -5, 0, 1, + ], + 'beta = 0' => [ + '#NUM!', + 0.2, 4, 0, 0, 1, + ], + 'Probability < 0' => [ + '#NUM!', + -0.5, 4, 5, 1, 3, + ], + 'Probability = 0' => [ + '#NUM!', + 0.0, 4, 5, 1, 3, + ], + 'Probability > 1' => [ + '#NUM!', + 1.5, 4, 5, 1, 3, + ], + 'Min = Max' => [ + '#NUM!', + 1, 4, 5, 1, 1, + ], ]; diff --git a/tests/data/Calculation/Statistical/CHIDIST.php b/tests/data/Calculation/Statistical/CHIDIST.php index 5cfdc664..24ddab9f 100644 --- a/tests/data/Calculation/Statistical/CHIDIST.php +++ b/tests/data/Calculation/Statistical/CHIDIST.php @@ -35,14 +35,18 @@ return [ ], [ '#VALUE!', - 'NAN', 3, + 'NaN', 3, ], [ - '#NUM!', - 8, 0, + '#VALUE!', + 8, 'NaN', ], - [ + 'Value < 0' => [ '#NUM!', -8, 3, ], + 'Degrees < 1' => [ + '#NUM!', + 8, 0, + ], ]; diff --git a/tests/data/Calculation/Statistical/CHIINV.php b/tests/data/Calculation/Statistical/CHIINV.php index 2384cda6..f931a780 100644 --- a/tests/data/Calculation/Statistical/CHIINV.php +++ b/tests/data/Calculation/Statistical/CHIINV.php @@ -35,6 +35,22 @@ return [ ], [ '#VALUE!', - 0.25, 'NAN', + 'NaN', 3, + ], + [ + '#VALUE!', + 0.25, 'NaN', + ], + 'Probability < 0' => [ + '#NUM!', + -0.1, 3, + ], + 'Probability > 1' => [ + '#NUM!', + 1.1, 3, + ], + 'Freedom > 1' => [ + '#NUM!', + 0.1, 0.5, ], ]; diff --git a/tests/data/Calculation/Statistical/FISHER.php b/tests/data/Calculation/Statistical/FISHER.php index 12909012..faf6442e 100644 --- a/tests/data/Calculation/Statistical/FISHER.php +++ b/tests/data/Calculation/Statistical/FISHER.php @@ -13,12 +13,20 @@ return [ 1.098612288668, 0.8, ], + [ + 0.972955074528, + 0.75, + ], [ '#VALUE!', 'NAN', ], [ '#NUM!', - -2, + -1.5, + ], + [ + '#NUM!', + 1.5, ], ]; diff --git a/tests/data/Calculation/Statistical/FISHERINV.php b/tests/data/Calculation/Statistical/FISHERINV.php index b79fd4f8..d23472b2 100644 --- a/tests/data/Calculation/Statistical/FISHERINV.php +++ b/tests/data/Calculation/Statistical/FISHERINV.php @@ -17,6 +17,10 @@ return [ 0.992631520201, 2.8, ], + [ + 0.7499999990254, + 0.9729550723, + ], [ '#VALUE!', 'NAN', diff --git a/tests/data/Calculation/Statistical/GAMMA.php b/tests/data/Calculation/Statistical/GAMMA.php index 760b429d..24b83f04 100644 --- a/tests/data/Calculation/Statistical/GAMMA.php +++ b/tests/data/Calculation/Statistical/GAMMA.php @@ -6,8 +6,10 @@ return [ [9.513507698669, 0.1], [1.0, 1.0], [0.886226925453, 1.5], + [1.3293403881791, 2.5], [17.837861981813, 4.8], [52.342777784553, 5.5], - ['#NUM!', -1], + 'Zero value' => ['#NUM!', 0.0], + 'Negative integer value' => ['#NUM!', -1], ['#VALUE!', 'NAN'], ]; diff --git a/tests/data/Calculation/Statistical/GAMMADIST.php b/tests/data/Calculation/Statistical/GAMMADIST.php index e79b3869..2a1bfa14 100644 --- a/tests/data/Calculation/Statistical/GAMMADIST.php +++ b/tests/data/Calculation/Statistical/GAMMADIST.php @@ -17,12 +17,44 @@ return [ 0.576809918873, 6, 3, 2, true, ], + 'Boolean as numeric' => [ + 0.576809918873, + 6, 3, 2, 1, + ], + [ + '#VALUE!', + 'NAN', 3, 2, true, + ], [ '#VALUE!', 6, 'NAN', 2, true, ], [ + '#VALUE!', + 6, 3, 'NAN', true, + ], + [ + '#VALUE!', + 6, 3, 2, 'NAN', + ], + 'Value < 0' => [ '#NUM!', -6, 3, 2, true, ], + 'A < 0' => [ + '#NUM!', + 6, -3, 2, true, + ], + 'A = 0' => [ + '#NUM!', + 6, 0, 2, true, + ], + 'B < 0' => [ + '#NUM!', + 6, 3, -2, true, + ], + 'B = 0' => [ + '#NUM!', + 6, 3, 0, true, + ], ]; diff --git a/tests/data/Calculation/Statistical/GAMMAINV.php b/tests/data/Calculation/Statistical/GAMMAINV.php index 3b3604b4..c35c4219 100644 --- a/tests/data/Calculation/Statistical/GAMMAINV.php +++ b/tests/data/Calculation/Statistical/GAMMAINV.php @@ -11,10 +11,38 @@ return [ ], [ '#VALUE!', - 'NAN', 3, 2, + 'NaN', 3, 2, ], [ + '#VALUE!', + 0.5, 'NaN', 2, + ], + [ + '#VALUE!', + 0.5, 3, 'NaN', + ], + 'Probability < 0' => [ '#NUM!', -0.5, 3, 2, ], + 'Probability > 1' => [ + '#NUM!', + 1.5, 3, 2, + ], + 'Alpha < 0' => [ + '#NUM!', + 0.5, -3, 2, + ], + 'Alpha = 0' => [ + '#NUM!', + 0.5, 0, 2, + ], + 'Beta < 0' => [ + '#NUM!', + 0.5, 3, -2, + ], + 'Beta = 0' => [ + '#NUM!', + 0.5, 3, 0, + ], ]; diff --git a/tests/data/Calculation/Statistical/GAMMALN.php b/tests/data/Calculation/Statistical/GAMMALN.php index a415f559..7b43eea8 100644 --- a/tests/data/Calculation/Statistical/GAMMALN.php +++ b/tests/data/Calculation/Statistical/GAMMALN.php @@ -13,8 +13,12 @@ return [ '#VALUE!', 'NAN', ], - [ + 'Value < 0' => [ '#NUM!', -4.5, ], + 'Value = 0' => [ + '#NUM!', + 0.0, + ], ]; diff --git a/tests/data/Calculation/TextData/FIND.php b/tests/data/Calculation/TextData/FIND.php index 0a583456..04d3276d 100644 --- a/tests/data/Calculation/TextData/FIND.php +++ b/tests/data/Calculation/TextData/FIND.php @@ -101,4 +101,9 @@ return [ 'Mark Baker', 8, ], + 'Boolean Needle' => [ + '#VALUE!', + true, + 'Mark Baker', + ], ]; diff --git a/tests/data/Calculation/TextData/SEARCH.php b/tests/data/Calculation/TextData/SEARCH.php index 579830f6..fa970bec 100644 --- a/tests/data/Calculation/TextData/SEARCH.php +++ b/tests/data/Calculation/TextData/SEARCH.php @@ -94,4 +94,9 @@ return [ 'BITE', 'BIT', ], + 'Boolean Needle' => [ + '#VALUE!', + true, + 'Mark Baker', + ], ];