From c380b25d3cf7843765462cdac3ae163b34a935a8 Mon Sep 17 00:00:00 2001 From: Mark Baker Date: Fri, 26 Mar 2021 09:08:23 +0100 Subject: [PATCH] Extract Poisson distribution into its own class (#1953) --- .../Calculation/Calculation.php | 4 +- .../Calculation/DateTimeExcel/Days360.php | 2 +- .../Calculation/Engineering/Complex.php | 6 +- src/PhpSpreadsheet/Calculation/Financial.php | 42 +++++----- .../Calculation/Financial/Depreciation.php | 14 ++-- src/PhpSpreadsheet/Calculation/Functions.php | 4 +- src/PhpSpreadsheet/Calculation/LookupRef.php | 4 +- .../Calculation/LookupRef/Matrix.php | 2 +- .../Calculation/Statistical.php | 77 ++++++++----------- .../Statistical/Distributions/Poisson.php | 55 +++++++++++++ .../data/Calculation/Statistical/POISSON.php | 16 +++- 11 files changed, 138 insertions(+), 88 deletions(-) create mode 100644 src/PhpSpreadsheet/Calculation/Statistical/Distributions/Poisson.php diff --git a/src/PhpSpreadsheet/Calculation/Calculation.php b/src/PhpSpreadsheet/Calculation/Calculation.php index 80a6fbcc..792402ce 100644 --- a/src/PhpSpreadsheet/Calculation/Calculation.php +++ b/src/PhpSpreadsheet/Calculation/Calculation.php @@ -1963,12 +1963,12 @@ class Calculation ], 'POISSON' => [ 'category' => Category::CATEGORY_STATISTICAL, - 'functionCall' => [Statistical::class, 'POISSON'], + 'functionCall' => [Statistical\Distributions\Poisson::class, 'distribution'], 'argumentCount' => '3', ], 'POISSON.DIST' => [ 'category' => Category::CATEGORY_STATISTICAL, - 'functionCall' => [Statistical::class, 'POISSON'], + 'functionCall' => [Statistical\Distributions\Poisson::class, 'distribution'], 'argumentCount' => '3', ], 'POWER' => [ diff --git a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days360.php b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days360.php index 068ea2bc..b90bc367 100644 --- a/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days360.php +++ b/src/PhpSpreadsheet/Calculation/DateTimeExcel/Days360.php @@ -22,7 +22,7 @@ class Days360 * PHP DateTime object, or a standard date string * @param mixed $endDate Excel date serial value (float), PHP date timestamp (integer), * PHP DateTime object, or a standard date string - * @param bool $method US or European Method + * @param mixed (bool) $method US or European Method * FALSE or omitted: U.S. (NASD) method. If the starting date is * the last day of a month, it becomes equal to the 30th of the * same month. If the ending date is the last day of a month and diff --git a/src/PhpSpreadsheet/Calculation/Engineering/Complex.php b/src/PhpSpreadsheet/Calculation/Engineering/Complex.php index f6429cbd..7dd5ff95 100644 --- a/src/PhpSpreadsheet/Calculation/Engineering/Complex.php +++ b/src/PhpSpreadsheet/Calculation/Engineering/Complex.php @@ -16,9 +16,9 @@ class Complex * Excel Function: * COMPLEX(realNumber,imaginary[,suffix]) * - * @param float $realNumber the real coefficient of the complex number - * @param float $imaginary the imaginary coefficient of the complex number - * @param string $suffix The suffix for the imaginary component of the complex number. + * @param mixed (float) $realNumber the real coefficient of the complex number + * @param mixed (float) $imaginary the imaginary coefficient of the complex number + * @param mixed (string) $suffix The suffix for the imaginary component of the complex number. * If omitted, the suffix is assumed to be "i". * * @return string diff --git a/src/PhpSpreadsheet/Calculation/Financial.php b/src/PhpSpreadsheet/Calculation/Financial.php index 2b54e1cd..084562f8 100644 --- a/src/PhpSpreadsheet/Calculation/Financial.php +++ b/src/PhpSpreadsheet/Calculation/Financial.php @@ -42,15 +42,15 @@ class Financial * @param mixed $settlement The security's settlement date. * The security settlement date is the date after the issue date * when the security is traded to the buyer. - * @param float $rate the security's annual coupon rate - * @param float $par The security's par value. + * @param mixed (float) $rate the security's annual coupon rate + * @param mixed (float) $par The security's par value. * If you omit par, ACCRINT uses $1,000. - * @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 @@ -98,10 +98,10 @@ class Financial * * @param mixed $issue The security's issue date * @param mixed $settlement The security's settlement (or maturity) date - * @param float $rate The security's annual coupon rate - * @param float $par The security's par value. + * @param mixed (float) $rate The security's annual coupon rate + * @param mixed (float) $par The security's par value. * If you omit par, ACCRINT uses $1,000. - * @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 @@ -890,11 +890,11 @@ class Financial * Excel Function: * IRR(values[,guess]) * - * @param float[] $values An array or a reference to cells that contain numbers for which you want + * @param mixed (float[]) $values An array or a reference to cells that contain numbers for which you want * to calculate the internal rate of return. * Values must contain at least one positive value and one negative value to * calculate the internal rate of return. - * @param float $guess A number that you guess is close to the result of IRR + * @param mixed (float) $guess A number that you guess is close to the result of IRR * * @return float|string */ @@ -1000,11 +1000,11 @@ class Financial * Excel Function: * MIRR(values,finance_rate, reinvestment_rate) * - * @param float[] $values An array or a reference to cells that contain a series of payments and + * @param mixed (float[]) $values An array or a reference to cells that contain a series of payments and * income occurring at regular intervals. * Payments are negative value, income is positive values. - * @param float $finance_rate The interest rate you pay on the money used in the cash flows - * @param float $reinvestment_rate The interest rate you receive on the cash flows as you reinvest them + * @param mixed (float) $finance_rate The interest rate you pay on the money used in the cash flows + * @param mixed (float) $reinvestment_rate The interest rate you receive on the cash flows as you reinvest them * * @return float|string Result, or a string containing an error */ @@ -1371,20 +1371,20 @@ class Financial * Excel Function: * RATE(nper,pmt,pv[,fv[,type[,guess]]]) * - * @param float $nper The total number of payment periods in an annuity - * @param float $pmt The payment made each period and cannot change over the life + * @param mixed (float) $nper The total number of payment periods in an annuity + * @param mixed (float) $pmt The payment made each period and cannot change over the life * of the annuity. * Typically, pmt includes principal and interest but no other * fees or taxes. - * @param float $pv The present value - the total amount that a series of future + * @param mixed (float) $pv The present value - the total amount that a series of future * payments is worth now - * @param float $fv The future value, or a cash balance you want to attain after + * @param mixed (float) $fv The future value, or a cash balance you want to attain after * the last payment is made. If fv is omitted, it is assumed * to be 0 (the future value of a loan, for example, is 0). - * @param int $type A number 0 or 1 and indicates when payments are due: + * @param mixed (int) $type A number 0 or 1 and indicates when payments are due: * 0 or omitted At the end of the period. * 1 At the beginning of the period. - * @param float $guess Your guess for what the rate will be. + * @param mixed (float) $guess Your guess for what the rate will be. * If you omit guess, it is assumed to be 10 percent. * * @return float|string @@ -1443,9 +1443,9 @@ class Financial * The security settlement date is the date after the issue 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 $investment The amount invested in the security - * @param int $discount The security's discount rate - * @param int $basis The type of day count to use. + * @param mixed (int) $investment The amount invested in the security + * @param mixed (int) $discount The security's discount rate + * @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 173e29bb..8770242f 100644 --- a/src/PhpSpreadsheet/Calculation/Financial/Depreciation.php +++ b/src/PhpSpreadsheet/Calculation/Financial/Depreciation.php @@ -137,9 +137,9 @@ class Depreciation * * Returns the straight-line depreciation of an asset for one period * - * @param mixed $cost Initial cost of the asset - * @param mixed $salvage Value at the end of the depreciation - * @param mixed $life Number of periods over which the asset is depreciated + * @param mixed (float) $cost Initial cost of the asset + * @param mixed (float) $salvage Value at the end of the depreciation + * @param mixed (float) $life Number of periods over which the asset is depreciated * * @return float|string Result, or a string containing an error */ @@ -169,10 +169,10 @@ class Depreciation * * Returns the sum-of-years' digits depreciation of an asset for a specified period. * - * @param mixed $cost Initial cost of the asset - * @param mixed $salvage Value at the end of the depreciation - * @param mixed $life Number of periods over which the asset is depreciated - * @param mixed $period Period + * @param mixed (float) $cost Initial cost of the asset + * @param mixed (float) $salvage Value at the end of the depreciation + * @param mixed (float) $life Number of periods over which the asset is depreciated + * @param mixed (float) $period Period * * @return float|string Result, or a string containing an error */ diff --git a/src/PhpSpreadsheet/Calculation/Functions.php b/src/PhpSpreadsheet/Calculation/Functions.php index 022e6be5..6ad387e8 100644 --- a/src/PhpSpreadsheet/Calculation/Functions.php +++ b/src/PhpSpreadsheet/Calculation/Functions.php @@ -576,7 +576,7 @@ class Functions /** * Convert a multi-dimensional array to a simple 1-dimensional array. * - * @param array $array Array to be flattened + * @param mixed (array) $array Array to be flattened * * @return array Flattened array */ @@ -609,7 +609,7 @@ class Functions /** * Convert a multi-dimensional array to a simple 1-dimensional array, but retain an element of indexing. * - * @param array $array Array to be flattened + * @param mixed (array) $array Array to be flattened * * @return array Flattened array */ diff --git a/src/PhpSpreadsheet/Calculation/LookupRef.php b/src/PhpSpreadsheet/Calculation/LookupRef.php index 17115a06..4a1bcb06 100644 --- a/src/PhpSpreadsheet/Calculation/LookupRef.php +++ b/src/PhpSpreadsheet/Calculation/LookupRef.php @@ -148,8 +148,8 @@ class LookupRef * Excel Function: * =HYPERLINK(linkURL,displayName) * - * @param string $linkURL Value to check, is also the value returned when no error - * @param string $displayName Value to return when testValue is an error condition + * @param mixed (string) $linkURL Value to check, is also the value returned when no error + * @param mixed (string) $displayName Value to return when testValue is an error condition * @param Cell $pCell The cell to set the hyperlink in * * @return mixed The value of $displayName (or $linkURL if $displayName was blank) diff --git a/src/PhpSpreadsheet/Calculation/LookupRef/Matrix.php b/src/PhpSpreadsheet/Calculation/LookupRef/Matrix.php index 8859a287..59af4258 100644 --- a/src/PhpSpreadsheet/Calculation/LookupRef/Matrix.php +++ b/src/PhpSpreadsheet/Calculation/LookupRef/Matrix.php @@ -9,7 +9,7 @@ class Matrix /** * TRANSPOSE. * - * @param array $matrixData A matrix of values + * @param mixed (array) $matrixData A matrix of values * * @return array */ diff --git a/src/PhpSpreadsheet/Calculation/Statistical.php b/src/PhpSpreadsheet/Calculation/Statistical.php index c8e084b5..ca160c36 100644 --- a/src/PhpSpreadsheet/Calculation/Statistical.php +++ b/src/PhpSpreadsheet/Calculation/Statistical.php @@ -251,10 +251,10 @@ class Statistical * experiment. For example, BINOMDIST can calculate the probability that two of the next three * babies born are male. * - * @param float $value Number of successes in trials - * @param float $trials Number of trials - * @param float $probability Probability of success on each trial - * @param bool $cumulative + * @param mixed (float) $value Number of successes in trials + * @param mixed (float) $trials Number of trials + * @param mixed (float) $probability Probability of success on each trial + * @param mixed (bool) $cumulative * * @return float|string */ @@ -1502,9 +1502,9 @@ class Statistical * distribution, except that the number of successes is fixed, and the number of trials is * variable. Like the binomial, trials are assumed to be independent. * - * @param float $failures Number of Failures - * @param float $successes Threshold number of Successes - * @param float $probability Probability of success on each trial + * @param mixed (float) $failures Number of Failures + * @param mixed (float) $successes Threshold number of Successes + * @param mixed (float) $probability Probability of success on each trial * * @return float|string The result, or a string containing an error */ @@ -1539,10 +1539,10 @@ class Statistical * function has a very wide range of applications in statistics, including hypothesis * testing. * - * @param float $value - * @param float $mean Mean Value - * @param float $stdDev Standard Deviation - * @param bool $cumulative + * @param mixed (float) $value + * @param mixed (float) $mean Mean Value + * @param mixed (float) $stdDev Standard Deviation + * @param mixed (bool) $cumulative * * @return float|string The result, or a string containing an error */ @@ -1573,9 +1573,9 @@ class Statistical * * Returns the inverse of the normal cumulative distribution for the specified mean and standard deviation. * - * @param float $probability - * @param float $mean Mean Value - * @param float $stdDev Standard Deviation + * @param mixed (float) $probability + * @param mixed (float) $mean Mean Value + * @param mixed (float) $stdDev Standard Deviation * * @return float|string The result, or a string containing an error */ @@ -1606,7 +1606,7 @@ class Statistical * a mean of 0 (zero) and a standard deviation of one. Use this function in place of a * table of standard normal curve areas. * - * @param float $value + * @param mixed (float) $value * * @return float|string The result, or a string containing an error */ @@ -1627,8 +1627,8 @@ class Statistical * a mean of 0 (zero) and a standard deviation of one. Use this function in place of a * table of standard normal curve areas. * - * @param float $value - * @param bool $cumulative + * @param mixed (float) $value + * @param mixed (bool) $cumulative * * @return float|string The result, or a string containing an error */ @@ -1648,7 +1648,7 @@ class Statistical * * Returns the inverse of the standard normal cumulative distribution * - * @param float $value + * @param mixed (float) $value * * @return float|string The result, or a string containing an error */ @@ -1714,9 +1714,9 @@ class Statistical * rather than floored (as MS Excel), so value 3 for a value set of 1, 2, 3, 4 will return * 0.667 rather than 0.666 * - * @param float[] $valueSet An array of, or a reference to, a list of numbers - * @param int $value the number whose rank you want to find - * @param int $significance the number of significant digits for the returned percentage value + * @param mixed (float[]) $valueSet An array of, or a reference to, a list of numbers + * @param mixed (int) $value the number whose rank you want to find + * @param mixed (int) $significance the number of significant digits for the returned percentage value * * @return float|string (string if result is an error) */ @@ -1787,37 +1787,20 @@ class Statistical * is predicting the number of events over a specific time, such as the number of * cars arriving at a toll plaza in 1 minute. * - * @param float $value - * @param float $mean Mean Value - * @param bool $cumulative + * @Deprecated 1.18.0 + * + * @see Statistical\Distributions\Poisson::distribution() + * Use the distribution() method in the Statistical\Distributions\Poisson class instead + * + * @param mixed (float) $value + * @param mixed (float) $mean Mean Value + * @param mixed (bool) $cumulative * * @return float|string The result, or a string containing an error */ public static function POISSON($value, $mean, $cumulative) { - $value = Functions::flattenSingleValue($value); - $mean = Functions::flattenSingleValue($mean); - - if ((is_numeric($value)) && (is_numeric($mean))) { - if (($value < 0) || ($mean <= 0)) { - return Functions::NAN(); - } - if ((is_numeric($cumulative)) || (is_bool($cumulative))) { - if ($cumulative) { - $summer = 0; - $floor = floor($value); - for ($i = 0; $i <= $floor; ++$i) { - $summer += $mean ** $i / MathTrig::FACT($i); - } - - return exp(0 - $mean) * $summer; - } - - return (exp(0 - $mean) * $mean ** $value) / MathTrig::FACT($value); - } - } - - return Functions::VALUE(); + return Statistical\Distributions\Poisson::distribution($value, $mean, $cumulative); } /** diff --git a/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Poisson.php b/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Poisson.php new file mode 100644 index 00000000..1ba7adca --- /dev/null +++ b/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Poisson.php @@ -0,0 +1,55 @@ +getMessage(); + } + + if (($value < 0) || ($mean < 0)) { + return Functions::NAN(); + } + + if ($cumulative) { + $summer = 0; + $floor = floor($value); + for ($i = 0; $i <= $floor; ++$i) { + $summer += $mean ** $i / MathTrig::FACT($i); + } + + return exp(0 - $mean) * $summer; + } + + return (exp(0 - $mean) * $mean ** $value) / MathTrig::FACT($value); + } +} diff --git a/tests/data/Calculation/Statistical/POISSON.php b/tests/data/Calculation/Statistical/POISSON.php index 11a82cab..7c839ac7 100644 --- a/tests/data/Calculation/Statistical/POISSON.php +++ b/tests/data/Calculation/Statistical/POISSON.php @@ -18,11 +18,23 @@ return [ 35, 40, true, ], [ - '#NUM!', - 35, -40, true, + '#VALUE!', + 'Nan', 40, true, ], [ '#VALUE!', 35, 'Nan', true, ], + [ + '#VALUE!', + 35, 40, 'Nan', + ], + 'Value < 0' => [ + '#NUM!', + -35, 40, true, + ], + 'Mean < 0' => [ + '#NUM!', + 35, -40, true, + ], ];