From ebeed87db556a28abbafcbcd29d5b377248c1071 Mon Sep 17 00:00:00 2001 From: Mark Baker Date: Mon, 31 Jan 2022 22:29:03 +0100 Subject: [PATCH] Initial implementation of the MS Excel RANDARRAY() MathTrig function (#2540) * Initial implementation of the MS Excel RANDARRAY() MathTrig function Update Change Log * Unit Tests for RANDARRAY() --- CHANGELOG.md | 3 +- .../Calculation/Calculation.php | 2 +- .../Calculation/MathTrig/Random.php | 49 ++++++++++++++ .../Functions/MathTrig/RandArrayTest.php | 66 +++++++++++++++++++ 4 files changed, 118 insertions(+), 2 deletions(-) create mode 100644 tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/RandArrayTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 433dfb6c..e20ed410 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org). ### Added -- Support for the Excel365 Math/Trig SEQUENCE() function [PR #2536](https://github.com/PHPOffice/PhpSpreadsheet/pull/2536) +- Support for the Excel365 Math/Trig SEQUENCE() function [PR #2536](https://github.com/PHPOffice/PhpSpreadsheet/pull/2536) +- Support for the Excel365 Math/Trig RANDARRAY() function [PR #2540](https://github.com/PHPOffice/PhpSpreadsheet/pull/2540) Note that the Spill Operator is not yet supported in the Calculation Engine; but this can still be useful for defining array constants. - Improved support for Conditional Formatting Rules [PR #2491](https://github.com/PHPOffice/PhpSpreadsheet/pull/2491) diff --git a/src/PhpSpreadsheet/Calculation/Calculation.php b/src/PhpSpreadsheet/Calculation/Calculation.php index 41320a7c..c5ce0afa 100644 --- a/src/PhpSpreadsheet/Calculation/Calculation.php +++ b/src/PhpSpreadsheet/Calculation/Calculation.php @@ -2078,7 +2078,7 @@ class Calculation ], 'RANDARRAY' => [ 'category' => Category::CATEGORY_MATH_AND_TRIG, - 'functionCall' => [Functions::class, 'DUMMY'], + 'functionCall' => [MathTrig\Random::class, 'randArray'], 'argumentCount' => '0-5', ], 'RANDBETWEEN' => [ diff --git a/src/PhpSpreadsheet/Calculation/MathTrig/Random.php b/src/PhpSpreadsheet/Calculation/MathTrig/Random.php index 963a789a..505b7d34 100644 --- a/src/PhpSpreadsheet/Calculation/MathTrig/Random.php +++ b/src/PhpSpreadsheet/Calculation/MathTrig/Random.php @@ -3,6 +3,7 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\MathTrig; use PhpOffice\PhpSpreadsheet\Calculation\Exception; +use PhpOffice\PhpSpreadsheet\Calculation\Functions; class Random { @@ -36,4 +37,52 @@ class Random return mt_rand($min, $max); } + + /** + * RANDARRAY. + * + * Generates a list of sequential numbers in an array. + * + * Excel Function: + * RANDARRAY([rows],[columns],[start],[step]) + * + * @param mixed $rows the number of rows to return, defaults to 1 + * @param mixed $columns the number of columns to return, defaults to 1 + * @param mixed $min the minimum number to be returned, defaults to 0 + * @param mixed $max the maximum number to be returned, defaults to 1 + * @param bool $wholeNumber the type of numbers to return: + * False - Decimal numbers to 15 decimal places. (default) + * True - Whole (integer) numbers + * + * @return array|string The resulting array, or a string containing an error + */ + public static function randArray($rows = 1, $columns = 1, $min = 0, $max = 1, $wholeNumber = false) + { + try { + $rows = (int) Helpers::validateNumericNullSubstitution($rows, 1); + Helpers::validatePositive($rows); + $columns = (int) Helpers::validateNumericNullSubstitution($columns, 1); + Helpers::validatePositive($columns); + $min = Helpers::validateNumericNullSubstitution($min, 1); + $max = Helpers::validateNumericNullSubstitution($max, 1); + + if ($max <= $min) { + return Functions::VALUE(); + } + } catch (Exception $e) { + return $e->getMessage(); + } + + return array_chunk( + array_map( + function () use ($min, $max, $wholeNumber) { + return $wholeNumber + ? mt_rand((int) $min, (int) $max) + : (mt_rand() / mt_getrandmax()) * ($max - $min) + $min; + }, + array_fill(0, $rows * $columns, $min) + ), + $columns + ); + } } diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/RandArrayTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/RandArrayTest.php new file mode 100644 index 00000000..25a53b60 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/RandArrayTest.php @@ -0,0 +1,66 @@ += $min && $value <= $max); + } + ); + } + + public function testRANDARRAYFloat(): void + { + $rows = 3; + $cols = 2; + $min = -2; + $max = 2; + + $result = MathTrig\Random::randArray($rows, $cols, $min, $max, false); + self::assertIsArray($result); + self::assertCount($rows, $result); + self::assertIsArray($result[0]); + self::assertCount($cols, $result[0]); + + $values = Functions::flattenArray($result); + array_walk( + $values, + function ($value) use ($min, $max): void { + self::assertIsFloat($value); + self::assertTrue($value >= $min && $value <= $max); + } + ); + } + + public function testRANDARRAYException(): void + { + $rows = 3; + $cols = 2; + $min = 2; + $max = -2; + + $result = MathTrig\Random::randArray($rows, $cols, $min, $max, false); + self::assertSame(Functions::VALUE(), $result); + } +}