From b7411931842c7236d2c282b9b350a5f1af6d5068 Mon Sep 17 00:00:00 2001 From: MarkBaker Date: Thu, 10 Feb 2022 19:13:30 +0100 Subject: [PATCH] Refactoring of the processor --- .../Calculation/ArrayEnabled.php | 161 +--------------- .../Engine/ArrayArgumentProcessor.php | 174 ++++++++++++++++++ 2 files changed, 177 insertions(+), 158 deletions(-) create mode 100644 src/PhpSpreadsheet/Calculation/Engine/ArrayArgumentProcessor.php diff --git a/src/PhpSpreadsheet/Calculation/ArrayEnabled.php b/src/PhpSpreadsheet/Calculation/ArrayEnabled.php index d2e6f3cf..fa90017d 100644 --- a/src/PhpSpreadsheet/Calculation/ArrayEnabled.php +++ b/src/PhpSpreadsheet/Calculation/ArrayEnabled.php @@ -3,6 +3,7 @@ namespace PhpOffice\PhpSpreadsheet\Calculation; use PhpOffice\PhpSpreadsheet\Calculation\Engine\ArrayArgumentHelper; +use PhpOffice\PhpSpreadsheet\Calculation\Engine\ArrayArgumentProcessor; trait ArrayEnabled { @@ -37,7 +38,7 @@ trait ArrayEnabled self::initialiseHelper($arguments); $arguments = self::$arrayArgumentHelper->arguments(); - return self::processArguments($method, ...$arguments); + return ArrayArgumentProcessor::processArguments(self::$arrayArgumentHelper, $method, ...$arguments); } /** @@ -50,162 +51,6 @@ trait ArrayEnabled $arguments = self::$arrayArgumentHelper->arguments(); $arguments = array_merge($arguments, $trailingArguments); - return self::processArguments($method, ...$arguments); - } - - /** - * @param mixed ...$arguments - */ - private static function processArguments(callable $method, ...$arguments): array - { - if (self::$arrayArgumentHelper->hasArrayArgument() === false) { - return [$method(...$arguments)]; - } - - if (self::$arrayArgumentHelper->arrayArguments() === 1) { - $nthArgument = self::$arrayArgumentHelper->getFirstArrayArgumentNumber(); - - return self::evaluateNthArgumentAsArray($method, $nthArgument, ...$arguments); - } - - $singleRowVectorIndex = self::$arrayArgumentHelper->getSingleRowVector(); - $singleColumnVectorIndex = self::$arrayArgumentHelper->getSingleColumnVector(); - if ($singleRowVectorIndex !== null && $singleColumnVectorIndex !== null) { - // Basic logic for a single row vector and a single column vector - return self::evaluateVectorPair($method, $singleRowVectorIndex, $singleColumnVectorIndex, ...$arguments); - } - - $matrixPair = self::$arrayArgumentHelper->getMatrixPair(); - if ($matrixPair !== []) { - if ( - (self::$arrayArgumentHelper->isVector($matrixPair[0]) === true && - self::$arrayArgumentHelper->isVector($matrixPair[1]) === false) || - (self::$arrayArgumentHelper->isVector($matrixPair[0]) === false && - self::$arrayArgumentHelper->isVector($matrixPair[1]) === true) - ) { - // Logic for a matrix and a vector (row or column) - return self::evaluateVectorMatrixPair($method, $matrixPair, ...$arguments); - } - // Logic for matrix/matrix, column vector/column vector or row vector/row vector - return self::evaluateMatrixPair($method, $matrixPair, ...$arguments); - } - - // Still need to work out the logic for more than two array arguments, - // For the moment, we're throwing an Exception when we initialise the ArrayArgumentHelper - return ['#VALUE!']; - } - - /** - * @param mixed ...$arguments - */ - private static function evaluateVectorMatrixPair(callable $method, array $matrixIndexes, ...$arguments): array - { - $matrix2 = array_pop($matrixIndexes); - /** @var array $matrixValues2 */ - $matrixValues2 = $arguments[$matrix2]; - $matrix1 = array_pop($matrixIndexes); - /** @var array $matrixValues1 */ - $matrixValues1 = $arguments[$matrix1]; - - $rows = min(array_map([self::$arrayArgumentHelper, 'rowCount'], [$matrix1, $matrix2])); - $columns = min(array_map([self::$arrayArgumentHelper, 'columnCount'], [$matrix1, $matrix2])); - - if ($rows === 1) { - $rows = max(array_map([self::$arrayArgumentHelper, 'rowCount'], [$matrix1, $matrix2])); - } - if ($columns === 1) { - $columns = max(array_map([self::$arrayArgumentHelper, 'columnCount'], [$matrix1, $matrix2])); - } - - $result = []; - for ($rowIndex = 0; $rowIndex < $rows; ++$rowIndex) { - for ($columnIndex = 0; $columnIndex < $columns; ++$columnIndex) { - $rowIndex1 = self::$arrayArgumentHelper->isRowVector($matrix1) ? 0 : $rowIndex; - $columnIndex1 = self::$arrayArgumentHelper->isColumnVector($matrix1) ? 0 : $columnIndex; - $value1 = $matrixValues1[$rowIndex1][$columnIndex1]; - $rowIndex2 = self::$arrayArgumentHelper->isRowVector($matrix2) ? 0 : $rowIndex; - $columnIndex2 = self::$arrayArgumentHelper->isColumnVector($matrix2) ? 0 : $columnIndex; - $value2 = $matrixValues2[$rowIndex2][$columnIndex2]; - $arguments[$matrix1] = $value1; - $arguments[$matrix2] = $value2; - - $result[$rowIndex][$columnIndex] = $method(...$arguments); - } - } - - return $result; - } - - /** - * @param mixed ...$arguments - */ - private static function evaluateMatrixPair(callable $method, array $matrixIndexes, ...$arguments): array - { - $matrix2 = array_pop($matrixIndexes); - /** @var array $matrixValues2 */ - $matrixValues2 = $arguments[$matrix2]; - $matrix1 = array_pop($matrixIndexes); - /** @var array $matrixValues1 */ - $matrixValues1 = $arguments[$matrix1]; - - $result = []; - foreach ($matrixValues1 as $rowIndex => $row) { - foreach ($row as $columnIndex => $value1) { - if (isset($matrixValues2[$rowIndex][$columnIndex]) === false) { - continue; - } - - $value2 = $matrixValues2[$rowIndex][$columnIndex]; - $arguments[$matrix1] = $value1; - $arguments[$matrix2] = $value2; - - $result[$rowIndex][$columnIndex] = $method(...$arguments); - } - } - - return $result; - } - - /** - * @param mixed ...$arguments - */ - private static function evaluateVectorPair(callable $method, int $rowIndex, int $columnIndex, ...$arguments): array - { - $rowVector = Functions::flattenArray($arguments[$rowIndex]); - $columnVector = Functions::flattenArray($arguments[$columnIndex]); - - $result = []; - foreach ($columnVector as $column) { - $rowResults = []; - foreach ($rowVector as $row) { - $arguments[$rowIndex] = $row; - $arguments[$columnIndex] = $column; - - $rowResults[] = $method(...$arguments); - } - $result[] = $rowResults; - } - - return $result; - } - - /** - * Note, offset is from 1 (for the first argument) rather than from 0. - * - * @param mixed ...$arguments - */ - private static function evaluateNthArgumentAsArray(callable $method, int $nthArgument, ...$arguments): array - { - $values = array_slice($arguments, $nthArgument - 1, 1); - /** @var array $values */ - $values = array_pop($values); - - $result = []; - foreach ($values as $value) { - $arguments[$nthArgument - 1] = $value; - $result[] = $method(...$arguments); - } - - return $result; + return ArrayArgumentProcessor::processArguments(self::$arrayArgumentHelper, $method, ...$arguments); } } diff --git a/src/PhpSpreadsheet/Calculation/Engine/ArrayArgumentProcessor.php b/src/PhpSpreadsheet/Calculation/Engine/ArrayArgumentProcessor.php new file mode 100644 index 00000000..4e377fe5 --- /dev/null +++ b/src/PhpSpreadsheet/Calculation/Engine/ArrayArgumentProcessor.php @@ -0,0 +1,174 @@ +hasArrayArgument() === false) { + return [$method(...$arguments)]; + } + + if (self::$arrayArgumentHelper->arrayArguments() === 1) { + $nthArgument = self::$arrayArgumentHelper->getFirstArrayArgumentNumber(); + + return self::evaluateNthArgumentAsArray($method, $nthArgument, ...$arguments); + } + + $singleRowVectorIndex = self::$arrayArgumentHelper->getSingleRowVector(); + $singleColumnVectorIndex = self::$arrayArgumentHelper->getSingleColumnVector(); + if ($singleRowVectorIndex !== null && $singleColumnVectorIndex !== null) { + // Basic logic for a single row vector and a single column vector + return self::evaluateVectorPair($method, $singleRowVectorIndex, $singleColumnVectorIndex, ...$arguments); + } + + $matrixPair = self::$arrayArgumentHelper->getMatrixPair(); + if ($matrixPair !== []) { + if ( + (self::$arrayArgumentHelper->isVector($matrixPair[0]) === true && + self::$arrayArgumentHelper->isVector($matrixPair[1]) === false) || + (self::$arrayArgumentHelper->isVector($matrixPair[0]) === false && + self::$arrayArgumentHelper->isVector($matrixPair[1]) === true) + ) { + // Logic for a matrix and a vector (row or column) + return self::evaluateVectorMatrixPair($method, $matrixPair, ...$arguments); + } + // Logic for matrix/matrix, column vector/column vector or row vector/row vector + return self::evaluateMatrixPair($method, $matrixPair, ...$arguments); + } + + // Still need to work out the logic for more than two array arguments, + // For the moment, we're throwing an Exception when we initialise the ArrayArgumentHelper + return ['#VALUE!']; + } + + /** + * @param mixed ...$arguments + */ + private static function evaluateVectorMatrixPair(callable $method, array $matrixIndexes, ...$arguments): array + { + $matrix2 = array_pop($matrixIndexes); + /** @var array $matrixValues2 */ + $matrixValues2 = $arguments[$matrix2]; + $matrix1 = array_pop($matrixIndexes); + /** @var array $matrixValues1 */ + $matrixValues1 = $arguments[$matrix1]; + + $rows = min(array_map([self::$arrayArgumentHelper, 'rowCount'], [$matrix1, $matrix2])); + $columns = min(array_map([self::$arrayArgumentHelper, 'columnCount'], [$matrix1, $matrix2])); + + if ($rows === 1) { + $rows = max(array_map([self::$arrayArgumentHelper, 'rowCount'], [$matrix1, $matrix2])); + } + if ($columns === 1) { + $columns = max(array_map([self::$arrayArgumentHelper, 'columnCount'], [$matrix1, $matrix2])); + } + + $result = []; + for ($rowIndex = 0; $rowIndex < $rows; ++$rowIndex) { + for ($columnIndex = 0; $columnIndex < $columns; ++$columnIndex) { + $rowIndex1 = self::$arrayArgumentHelper->isRowVector($matrix1) ? 0 : $rowIndex; + $columnIndex1 = self::$arrayArgumentHelper->isColumnVector($matrix1) ? 0 : $columnIndex; + $value1 = $matrixValues1[$rowIndex1][$columnIndex1]; + $rowIndex2 = self::$arrayArgumentHelper->isRowVector($matrix2) ? 0 : $rowIndex; + $columnIndex2 = self::$arrayArgumentHelper->isColumnVector($matrix2) ? 0 : $columnIndex; + $value2 = $matrixValues2[$rowIndex2][$columnIndex2]; + $arguments[$matrix1] = $value1; + $arguments[$matrix2] = $value2; + + $result[$rowIndex][$columnIndex] = $method(...$arguments); + } + } + + return $result; + } + + /** + * @param mixed ...$arguments + */ + private static function evaluateMatrixPair(callable $method, array $matrixIndexes, ...$arguments): array + { + $matrix2 = array_pop($matrixIndexes); + /** @var array $matrixValues2 */ + $matrixValues2 = $arguments[$matrix2]; + $matrix1 = array_pop($matrixIndexes); + /** @var array $matrixValues1 */ + $matrixValues1 = $arguments[$matrix1]; + + $result = []; + foreach ($matrixValues1 as $rowIndex => $row) { + foreach ($row as $columnIndex => $value1) { + if (isset($matrixValues2[$rowIndex][$columnIndex]) === false) { + continue; + } + + $value2 = $matrixValues2[$rowIndex][$columnIndex]; + $arguments[$matrix1] = $value1; + $arguments[$matrix2] = $value2; + + $result[$rowIndex][$columnIndex] = $method(...$arguments); + } + } + + return $result; + } + + /** + * @param mixed ...$arguments + */ + private static function evaluateVectorPair(callable $method, int $rowIndex, int $columnIndex, ...$arguments): array + { + $rowVector = Functions::flattenArray($arguments[$rowIndex]); + $columnVector = Functions::flattenArray($arguments[$columnIndex]); + + $result = []; + foreach ($columnVector as $column) { + $rowResults = []; + foreach ($rowVector as $row) { + $arguments[$rowIndex] = $row; + $arguments[$columnIndex] = $column; + + $rowResults[] = $method(...$arguments); + } + $result[] = $rowResults; + } + + return $result; + } + + /** + * Note, offset is from 1 (for the first argument) rather than from 0. + * + * @param mixed ...$arguments + */ + private static function evaluateNthArgumentAsArray(callable $method, int $nthArgument, ...$arguments): array + { + $values = array_slice($arguments, $nthArgument - 1, 1); + /** @var array $values */ + $values = array_pop($values); + + $result = []; + foreach ($values as $value) { + $arguments[$nthArgument - 1] = $value; + $result[] = $method(...$arguments); + } + + return $result; + } +}