More unit tests for statistical functions, including a bugfix to LARGE() (#1601)

* More unit tests for statistical functions, including a bugfix to LARGE() that was identified in testing
This commit is contained in:
Mark Baker 2020-07-29 23:56:37 +02:00 committed by GitHub
parent a203c3a7ea
commit 9683e5be18
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 478 additions and 28 deletions

View File

@ -552,7 +552,7 @@ class Database
* the column label in which you specify a condition for the * the column label in which you specify a condition for the
* column. * column.
* *
* @return float * @return float|string (string if result is an error)
*/ */
public static function DVAR($database, $field, $criteria) public static function DVAR($database, $field, $criteria)
{ {
@ -591,7 +591,7 @@ class Database
* the column label in which you specify a condition for the * the column label in which you specify a condition for the
* column. * column.
* *
* @return float * @return float|string (string if result is an error)
*/ */
public static function DVARP($database, $field, $criteria) public static function DVARP($database, $field, $criteria)
{ {

View File

@ -126,7 +126,7 @@ class LookupRef
* *
* @param null|array|string $cellAddress An array or array formula, or a reference to a range of cells for which you want the number of columns * @param null|array|string $cellAddress An array or array formula, or a reference to a range of cells for which you want the number of columns
* *
* @return int The number of columns in cellAddress * @return int|string The number of columns in cellAddress, or a string if arguments are invalid
*/ */
public static function COLUMNS($cellAddress = null) public static function COLUMNS($cellAddress = null)
{ {
@ -160,7 +160,7 @@ class LookupRef
* *
* @param null|array|string $cellAddress A reference to a range of cells for which you want the row numbers * @param null|array|string $cellAddress A reference to a range of cells for which you want the row numbers
* *
* @return int or array of integer * @return int|mixed[]|string
*/ */
public static function ROW($cellAddress = null) public static function ROW($cellAddress = null)
{ {
@ -203,7 +203,7 @@ class LookupRef
* *
* @param null|array|string $cellAddress An array or array formula, or a reference to a range of cells for which you want the number of rows * @param null|array|string $cellAddress An array or array formula, or a reference to a range of cells for which you want the number of rows
* *
* @return int The number of rows in cellAddress * @return int|string The number of rows in cellAddress, or a string if arguments are invalid
*/ */
public static function ROWS($cellAddress = null) public static function ROWS($cellAddress = null)
{ {

View File

@ -871,7 +871,7 @@ class MathTrig
* *
* Returns the ratio of the factorial of a sum of values to the product of factorials. * Returns the ratio of the factorial of a sum of values to the product of factorials.
* *
* @param array of mixed Data Series * @param mixed[] $args An array of mixed values for the Data Series
* *
* @return float|string The result, or a string containing an error * @return float|string The result, or a string containing an error
*/ */
@ -1149,7 +1149,7 @@ class MathTrig
* *
* Returns the sum of a power series * Returns the sum of a power series
* *
* @param array of mixed Data Series * @param mixed[] $args An array of mixed values for the Data Series
* *
* @return float|string The result, or a string containing an error * @return float|string The result, or a string containing an error
*/ */
@ -1273,21 +1273,22 @@ class MathTrig
* *
* Returns a subtotal in a list or database. * Returns a subtotal in a list or database.
* *
* @param int the number 1 to 11 that specifies which function to * @param int $functionType
* A number 1 to 11 that specifies which function to
* use in calculating subtotals within a range * use in calculating subtotals within a range
* list * list
* Numbers 101 to 111 shadow the functions of 1 to 11 * Numbers 101 to 111 shadow the functions of 1 to 11
* but ignore any values in the range that are * but ignore any values in the range that are
* in hidden rows or columns * in hidden rows or columns
* @param array of mixed Data Series * @param mixed[] $args A mixed data series of values
* *
* @return float|string * @return float|string
*/ */
public static function SUBTOTAL(...$args) public static function SUBTOTAL($functionType, ...$args)
{ {
$cellReference = array_pop($args); $cellReference = array_pop($args);
$aArgs = Functions::flattenArrayIndexed($args); $aArgs = Functions::flattenArrayIndexed($args);
$subtotal = array_shift($aArgs); $subtotal = Functions::flattenSingleValue($functionType);
// Calculate // Calculate
if ((is_numeric($subtotal)) && (!is_string($subtotal))) { if ((is_numeric($subtotal)) && (!is_string($subtotal))) {

View File

@ -1445,7 +1445,7 @@ class Statistical
return $returnValue; return $returnValue;
} }
return self::NA(); return Functions::NA();
} }
/** /**
@ -1701,7 +1701,6 @@ class Statistical
$xHi = $alpha * $beta * 5; $xHi = $alpha * $beta * 5;
$x = $xNew = 1; $x = $xNew = 1;
$error = $pdf = 0;
$dx = 1024; $dx = 1024;
$i = 0; $i = 0;
@ -2019,11 +2018,12 @@ class Statistical
public static function LARGE(...$args) public static function LARGE(...$args)
{ {
$aArgs = Functions::flattenArray($args); $aArgs = Functions::flattenArray($args);
$entry = array_pop($aArgs);
// Calculate
$entry = floor(array_pop($aArgs));
if ((is_numeric($entry)) && (!is_string($entry))) { if ((is_numeric($entry)) && (!is_string($entry))) {
$entry = (int) floor($entry);
// Calculate
$mArgs = []; $mArgs = [];
foreach ($aArgs as $arg) { foreach ($aArgs as $arg) {
// Is it a numeric value? // Is it a numeric value?
@ -2032,7 +2032,7 @@ class Statistical
} }
} }
$count = self::COUNT($mArgs); $count = self::COUNT($mArgs);
$entry = floor(--$entry); --$entry;
if (($entry < 0) || ($entry >= $count) || ($count == 0)) { if (($entry < 0) || ($entry >= $count) || ($count == 0)) {
return Functions::NAN(); return Functions::NAN();
} }
@ -2873,7 +2873,7 @@ class Statistical
* @param int $value the number whose rank you want to find * @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 int $significance the number of significant digits for the returned percentage value
* *
* @return float * @return float|string (string if result is an error)
*/ */
public static function PERCENTRANK($valueSet, $value, $significance = 3) public static function PERCENTRANK($valueSet, $value, $significance = 3)
{ {
@ -3169,6 +3169,8 @@ class Statistical
$entry = array_pop($aArgs); $entry = array_pop($aArgs);
if ((is_numeric($entry)) && (!is_string($entry))) { if ((is_numeric($entry)) && (!is_string($entry))) {
$entry = (int) floor($entry);
$mArgs = []; $mArgs = [];
foreach ($aArgs as $arg) { foreach ($aArgs as $arg) {
// Is it a numeric value? // Is it a numeric value?
@ -3177,7 +3179,7 @@ class Statistical
} }
} }
$count = self::COUNT($mArgs); $count = self::COUNT($mArgs);
$entry = floor(--$entry); --$entry;
if (($entry < 0) || ($entry >= $count) || ($count == 0)) { if (($entry < 0) || ($entry >= $count) || ($count == 0)) {
return Functions::NAN(); return Functions::NAN();
} }
@ -3481,7 +3483,6 @@ class Statistical
$ttheta = atan2($value, sqrt($tterm)); $ttheta = atan2($value, sqrt($tterm));
$tc = cos($ttheta); $tc = cos($ttheta);
$ts = sin($ttheta); $ts = sin($ttheta);
$tsum = 0;
if (($degrees % 2) == 1) { if (($degrees % 2) == 1) {
$ti = 3; $ti = 3;
@ -3657,7 +3658,7 @@ class Statistical
* *
* @param mixed ...$args Data values * @param mixed ...$args Data values
* *
* @return float * @return float|string (string if result is an error)
*/ */
public static function VARFunc(...$args) public static function VARFunc(...$args)
{ {
@ -3699,7 +3700,7 @@ class Statistical
* *
* @param mixed ...$args Data values * @param mixed ...$args Data values
* *
* @return float * @return float|string (string if result is an error)
*/ */
public static function VARA(...$args) public static function VARA(...$args)
{ {
@ -3754,7 +3755,7 @@ class Statistical
* *
* @param mixed ...$args Data values * @param mixed ...$args Data values
* *
* @return float * @return float|string (string if result is an error)
*/ */
public static function VARP(...$args) public static function VARP(...$args)
{ {
@ -3797,7 +3798,7 @@ class Statistical
* *
* @param mixed ...$args Data values * @param mixed ...$args Data values
* *
* @return float * @return float|string (string if result is an error)
*/ */
public static function VARPA(...$args) public static function VARPA(...$args)
{ {
@ -3853,7 +3854,7 @@ class Statistical
* @param float $beta Beta Parameter * @param float $beta Beta Parameter
* @param bool $cumulative * @param bool $cumulative
* *
* @return float * @return float|string (string if result is an error)
*/ */
public static function WEIBULL($value, $alpha, $beta, $cumulative) public static function WEIBULL($value, $alpha, $beta, $cumulative)
{ {
@ -3887,7 +3888,7 @@ class Statistical
* @param float $m0 Alpha Parameter * @param float $m0 Alpha Parameter
* @param float $sigma Beta Parameter * @param float $sigma Beta Parameter
* *
* @return float|string * @return float|string (string if result is an error)
*/ */
public static function ZTEST($dataSet, $m0, $sigma = null) public static function ZTEST($dataSet, $m0, $sigma = null)
{ {

View File

@ -2,6 +2,7 @@
namespace PhpOffice\PhpSpreadsheet\Calculation; namespace PhpOffice\PhpSpreadsheet\Calculation;
use DateTimeInterface;
use PhpOffice\PhpSpreadsheet\Shared\Date; use PhpOffice\PhpSpreadsheet\Shared\Date;
use PhpOffice\PhpSpreadsheet\Shared\StringHelper; use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
use PhpOffice\PhpSpreadsheet\Style\NumberFormat; use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
@ -98,7 +99,7 @@ class TextData
* *
* @param string $characters Value * @param string $characters Value
* *
* @return int * @return int|string A string if arguments are invalid
*/ */
public static function ASCIICODE($characters) public static function ASCIICODE($characters)
{ {
@ -543,7 +544,7 @@ class TextData
* *
* @param mixed $value Value to check * @param mixed $value Value to check
* *
* @return bool * @return DateTimeInterface|float|int|string A string if arguments are invalid
*/ */
public static function VALUE($value = '') public static function VALUE($value = '')
{ {

View File

@ -0,0 +1,27 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Statistical;
use PhpOffice\PhpSpreadsheet\Calculation\Statistical;
use PHPUnit\Framework\TestCase;
class LargeTest extends TestCase
{
/**
* @dataProvider providerLARGE
*
* @param mixed $expectedResult
* @param mixed $values
* @param mixed $position
*/
public function testLARGE($expectedResult, $values, $position): void
{
$result = Statistical::LARGE($values, $position);
self::assertEqualsWithDelta($expectedResult, $result, 1E-12);
}
public function providerLARGE()
{
return require 'tests/data/Calculation/Statistical/LARGE.php';
}
}

View File

@ -0,0 +1,33 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Statistical;
use PhpOffice\PhpSpreadsheet\Calculation\Statistical;
use PHPUnit\Framework\TestCase;
class LinEstTest extends TestCase
{
/**
* @dataProvider providerLINEST
*
* @param mixed $expectedResult
* @param mixed $xValues
* @param mixed $yValues
* @param mixed $const
* @param mixed $stats
*/
public function testLINEST($expectedResult, $yValues, $xValues, $const, $stats): void
{
$result = Statistical::LINEST($yValues, $xValues, $const, $stats);
$elements = count($expectedResult);
for ($element = 0; $element < $elements; ++$element) {
self::assertEqualsWithDelta($expectedResult[$element], $result[$element], 1E-12);
}
}
public function providerLINEST()
{
return require 'tests/data/Calculation/Statistical/LINEST.php';
}
}

View File

@ -0,0 +1,33 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Statistical;
use PhpOffice\PhpSpreadsheet\Calculation\Statistical;
use PHPUnit\Framework\TestCase;
class LogEstTest extends TestCase
{
/**
* @dataProvider providerLOGEST
*
* @param mixed $expectedResult
* @param mixed $xValues
* @param mixed $yValues
* @param mixed $const
* @param mixed $stats
*/
public function testLOGEST($expectedResult, $yValues, $xValues, $const, $stats): void
{
$result = Statistical::LOGEST($yValues, $xValues, $const, $stats);
$elements = count($expectedResult);
for ($element = 0; $element < $elements; ++$element) {
self::assertEqualsWithDelta($expectedResult[$element], $result[$element], 1E-12);
}
}
public function providerLOGEST()
{
return require 'tests/data/Calculation/Statistical/LOGEST.php';
}
}

View File

@ -0,0 +1,25 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Statistical;
use PhpOffice\PhpSpreadsheet\Calculation\Statistical;
use PHPUnit\Framework\TestCase;
class LogInvTest extends TestCase
{
/**
* @dataProvider providerLOGINV
*
* @param mixed $expectedResult
*/
public function testLOGINV($expectedResult, ...$args): void
{
$result = Statistical::LOGINV(...$args);
self::assertEqualsWithDelta($expectedResult, $result, 1E-12);
}
public function providerLOGINV(): array
{
return require 'tests/data/Calculation/Statistical/LOGINV.php';
}
}

View File

@ -0,0 +1,25 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Statistical;
use PhpOffice\PhpSpreadsheet\Calculation\Statistical;
use PHPUnit\Framework\TestCase;
class MaxATest extends TestCase
{
/**
* @dataProvider providerMAXA
*
* @param mixed $expectedResult
*/
public function testMAXA($expectedResult, ...$args): void
{
$result = Statistical::MAXA(...$args);
self::assertEqualsWithDelta($expectedResult, $result, 1E-12);
}
public function providerMAXA(): array
{
return require 'tests/data/Calculation/Statistical/MAXA.php';
}
}

View File

@ -0,0 +1,25 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Statistical;
use PhpOffice\PhpSpreadsheet\Calculation\Statistical;
use PHPUnit\Framework\TestCase;
class MaxTest extends TestCase
{
/**
* @dataProvider providerMAX
*
* @param mixed $expectedResult
*/
public function testMAX($expectedResult, ...$args): void
{
$result = Statistical::MAX(...$args);
self::assertEqualsWithDelta($expectedResult, $result, 1E-12);
}
public function providerMAX(): array
{
return require 'tests/data/Calculation/Statistical/MAX.php';
}
}

View File

@ -0,0 +1,25 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Statistical;
use PhpOffice\PhpSpreadsheet\Calculation\Statistical;
use PHPUnit\Framework\TestCase;
class MinATest extends TestCase
{
/**
* @dataProvider providerMINA
*
* @param mixed $expectedResult
*/
public function testMINA($expectedResult, ...$args): void
{
$result = Statistical::MINA(...$args);
self::assertEqualsWithDelta($expectedResult, $result, 1E-12);
}
public function providerMINA(): array
{
return require 'tests/data/Calculation/Statistical/MINA.php';
}
}

View File

@ -0,0 +1,25 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Statistical;
use PhpOffice\PhpSpreadsheet\Calculation\Statistical;
use PHPUnit\Framework\TestCase;
class MinTest extends TestCase
{
/**
* @dataProvider providerMIN
*
* @param mixed $expectedResult
*/
public function testMIN($expectedResult, ...$args): void
{
$result = Statistical::MIN(...$args);
self::assertEqualsWithDelta($expectedResult, $result, 1E-12);
}
public function providerMIN(): array
{
return require 'tests/data/Calculation/Statistical/MIN.php';
}
}

View File

@ -0,0 +1,27 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Statistical;
use PhpOffice\PhpSpreadsheet\Calculation\Statistical;
use PHPUnit\Framework\TestCase;
class SmallTest extends TestCase
{
/**
* @dataProvider providerSMALL
*
* @param mixed $expectedResult
* @param mixed $values
* @param mixed $position
*/
public function testSMALL($expectedResult, $values, $position): void
{
$result = Statistical::SMALL($values, $position);
self::assertEqualsWithDelta($expectedResult, $result, 1E-12);
}
public function providerSMALL()
{
return require 'tests/data/Calculation/Statistical/SMALL.php';
}
}

View File

@ -0,0 +1,34 @@
<?php
return [
[
5,
[3, 4, 5, 2, 3, 4, 5, 6, 4, 7],
3,
],
[
4,
[3, 4, 5, 2, 3, 4, 5, 6, 4, 7],
7,
],
[
2,
[3, 4, '5', 2, 3, 4, '5', '6', 4, '7'],
6,
],
[
'#VALUE!',
[3, 4, 5, 2, 3, 4, 5, 6, 4, 7],
'NAN',
],
[
'#NUM!',
[3, 4, 5, 2, 3, 4, 5, 6, 4, 7],
-1,
],
[
'#NUM!',
[],
1,
],
];

View File

@ -0,0 +1,11 @@
<?php
return [
[
[2.0, 1.0],
[1, 9, 5, 7],
[0, 4, 2, 3],
true,
false,
],
];

View File

@ -0,0 +1,11 @@
<?php
return [
[
[1.463275628116, 495.304770158727],
[33100, 47300, 69000, 102000, 150000, 220000],
[11, 12, 13, 14, 15, 16],
true,
false,
],
];

View File

@ -0,0 +1,24 @@
<?php
return [
[
4.000025209777,
0.039084, 3.5, 1.2,
],
[
6.653346075337,
0.3, 2, 0.2,
],
[
7.135708009256,
0.3, 2.5, 1.02,
],
[
'#VALUE!',
1.1, 2.2, 'NAN',
],
[
'#NUM!',
1.1, 2.2, 3.3,
],
];

View File

@ -0,0 +1,16 @@
<?php
return [
[
27,
10, 7, 9, 27, 2,
],
[
10,
10, 7, 9, '27', 2,
],
[
0,
null, 'STRING', true, '', '27',
],
];

View File

@ -0,0 +1,28 @@
<?php
return [
[
27,
10, 7, 9, 27, 2,
],
[
10,
10, 7, 9, '17', 2,
],
[
0,
-10, -7, -9, '17', -2,
],
[
1,
-10, true, -9, '17', -2,
],
[
1,
null, 'STRING', true, '', -2, 0, false, '27',
],
[
0,
null, 'STRING', '', 'xl95',
],
];

View File

@ -0,0 +1,16 @@
<?php
return [
[
2,
10, 7, 2, 9, 27,
],
[
-9,
10, 7, -9, '-27', 2,
],
[
0,
null, 'STRING', true, '', '27',
],
];

View File

@ -0,0 +1,28 @@
<?php
return [
[
2,
10, 7, 9, 27, 2,
],
[
-7,
10, '-9', -7, '17', 2,
],
[
0,
10, 7, 9, '17', 2,
],
[
1,
10, true, 9, 2,
],
[
0,
null, true, 2, false,
],
[
0,
null, 'STRING', '', 'xl95',
],
];

View File

@ -0,0 +1,34 @@
<?php
return [
[
3,
[1, 4, 8, 3, 7, 12, 54, 8, 23],
2,
],
[
4,
[3, 4, 5, 2, 3, 4, 6, 4, 7],
4,
],
[
6,
['3', 4, 5, '2', '3', 4, 5, 6, 4, 7],
6,
],
[
'#VALUE!',
[3, 4, 5, 2, 3, 4, 5, 6, 4, 7],
'NAN',
],
[
'#NUM!',
[3, 4, 5, 2, 3, 4, 5, 6, 4, 7],
-1,
],
[
'#NUM!',
[],
1,
],
];