Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.
   1  <?php
   2  
   3  namespace PhpOffice\PhpSpreadsheet\Calculation\Engine;
   4  
   5  use PhpOffice\PhpSpreadsheet\Calculation\Functions;
   6  
   7  class ArrayArgumentProcessor
   8  {
   9      /**
  10       * @var ArrayArgumentHelper
  11       */
  12      private static $arrayArgumentHelper;
  13  
  14      /**
  15       * @param mixed ...$arguments
  16       */
  17      public static function processArguments(
  18          ArrayArgumentHelper $arrayArgumentHelper,
  19          callable $method,
  20          ...$arguments
  21      ): array {
  22          self::$arrayArgumentHelper = $arrayArgumentHelper;
  23  
  24          if (self::$arrayArgumentHelper->hasArrayArgument() === false) {
  25              return [$method(...$arguments)];
  26          }
  27  
  28          if (self::$arrayArgumentHelper->arrayArguments() === 1) {
  29              $nthArgument = self::$arrayArgumentHelper->getFirstArrayArgumentNumber();
  30  
  31              return self::evaluateNthArgumentAsArray($method, $nthArgument, ...$arguments);
  32          }
  33  
  34          $singleRowVectorIndex = self::$arrayArgumentHelper->getSingleRowVector();
  35          $singleColumnVectorIndex = self::$arrayArgumentHelper->getSingleColumnVector();
  36  
  37          if ($singleRowVectorIndex !== null && $singleColumnVectorIndex !== null) {
  38              // Basic logic for a single row vector and a single column vector
  39              return self::evaluateVectorPair($method, $singleRowVectorIndex, $singleColumnVectorIndex, ...$arguments);
  40          }
  41  
  42          $matrixPair = self::$arrayArgumentHelper->getMatrixPair();
  43          if ($matrixPair !== []) {
  44              if (
  45                  (self::$arrayArgumentHelper->isVector($matrixPair[0]) === true &&
  46                      self::$arrayArgumentHelper->isVector($matrixPair[1]) === false) ||
  47                  (self::$arrayArgumentHelper->isVector($matrixPair[0]) === false &&
  48                      self::$arrayArgumentHelper->isVector($matrixPair[1]) === true)
  49              ) {
  50                  // Logic for a matrix and a vector (row or column)
  51                  return self::evaluateVectorMatrixPair($method, $matrixPair, ...$arguments);
  52              }
  53              // Logic for matrix/matrix, column vector/column vector or row vector/row vector
  54              return self::evaluateMatrixPair($method, $matrixPair, ...$arguments);
  55          }
  56  
  57          // Still need to work out the logic for more than two array arguments,
  58          // For the moment, we're throwing an Exception when we initialise the ArrayArgumentHelper
  59          return ['#VALUE!'];
  60      }
  61  
  62      /**
  63       * @param mixed ...$arguments
  64       */
  65      private static function evaluateVectorMatrixPair(callable $method, array $matrixIndexes, ...$arguments): array
  66      {
  67          $matrix2 = array_pop($matrixIndexes);
  68          /** @var array $matrixValues2 */
  69          $matrixValues2 = $arguments[$matrix2];
  70          $matrix1 = array_pop($matrixIndexes);
  71          /** @var array $matrixValues1 */
  72          $matrixValues1 = $arguments[$matrix1];
  73  
  74          $rows = min(array_map([self::$arrayArgumentHelper, 'rowCount'], [$matrix1, $matrix2]));
  75          $columns = min(array_map([self::$arrayArgumentHelper, 'columnCount'], [$matrix1, $matrix2]));
  76  
  77          if ($rows === 1) {
  78              $rows = max(array_map([self::$arrayArgumentHelper, 'rowCount'], [$matrix1, $matrix2]));
  79          }
  80          if ($columns === 1) {
  81              $columns = max(array_map([self::$arrayArgumentHelper, 'columnCount'], [$matrix1, $matrix2]));
  82          }
  83  
  84          $result = [];
  85          for ($rowIndex = 0; $rowIndex < $rows; ++$rowIndex) {
  86              for ($columnIndex = 0; $columnIndex < $columns; ++$columnIndex) {
  87                  $rowIndex1 = self::$arrayArgumentHelper->isRowVector($matrix1) ? 0 : $rowIndex;
  88                  $columnIndex1 = self::$arrayArgumentHelper->isColumnVector($matrix1) ? 0 : $columnIndex;
  89                  $value1 = $matrixValues1[$rowIndex1][$columnIndex1];
  90                  $rowIndex2 = self::$arrayArgumentHelper->isRowVector($matrix2) ? 0 : $rowIndex;
  91                  $columnIndex2 = self::$arrayArgumentHelper->isColumnVector($matrix2) ? 0 : $columnIndex;
  92                  $value2 = $matrixValues2[$rowIndex2][$columnIndex2];
  93                  $arguments[$matrix1] = $value1;
  94                  $arguments[$matrix2] = $value2;
  95  
  96                  $result[$rowIndex][$columnIndex] = $method(...$arguments);
  97              }
  98          }
  99  
 100          return $result;
 101      }
 102  
 103      /**
 104       * @param mixed ...$arguments
 105       */
 106      private static function evaluateMatrixPair(callable $method, array $matrixIndexes, ...$arguments): array
 107      {
 108          $matrix2 = array_pop($matrixIndexes);
 109          /** @var array $matrixValues2 */
 110          $matrixValues2 = $arguments[$matrix2];
 111          $matrix1 = array_pop($matrixIndexes);
 112          /** @var array $matrixValues1 */
 113          $matrixValues1 = $arguments[$matrix1];
 114  
 115          $result = [];
 116          foreach ($matrixValues1 as $rowIndex => $row) {
 117              foreach ($row as $columnIndex => $value1) {
 118                  if (isset($matrixValues2[$rowIndex][$columnIndex]) === false) {
 119                      continue;
 120                  }
 121  
 122                  $value2 = $matrixValues2[$rowIndex][$columnIndex];
 123                  $arguments[$matrix1] = $value1;
 124                  $arguments[$matrix2] = $value2;
 125  
 126                  $result[$rowIndex][$columnIndex] = $method(...$arguments);
 127              }
 128          }
 129  
 130          return $result;
 131      }
 132  
 133      /**
 134       * @param mixed ...$arguments
 135       */
 136      private static function evaluateVectorPair(callable $method, int $rowIndex, int $columnIndex, ...$arguments): array
 137      {
 138          $rowVector = Functions::flattenArray($arguments[$rowIndex]);
 139          $columnVector = Functions::flattenArray($arguments[$columnIndex]);
 140  
 141          $result = [];
 142          foreach ($columnVector as $column) {
 143              $rowResults = [];
 144              foreach ($rowVector as $row) {
 145                  $arguments[$rowIndex] = $row;
 146                  $arguments[$columnIndex] = $column;
 147  
 148                  $rowResults[] = $method(...$arguments);
 149              }
 150              $result[] = $rowResults;
 151          }
 152  
 153          return $result;
 154      }
 155  
 156      /**
 157       * Note, offset is from 1 (for the first argument) rather than from 0.
 158       *
 159       * @param mixed ...$arguments
 160       */
 161      private static function evaluateNthArgumentAsArray(callable $method, int $nthArgument, ...$arguments): array
 162      {
 163          $values = array_slice($arguments, $nthArgument - 1, 1);
 164          /** @var array $values */
 165          $values = array_pop($values);
 166  
 167          $result = [];
 168          foreach ($values as $value) {
 169              $arguments[$nthArgument - 1] = $value;
 170              $result[] = $method(...$arguments);
 171          }
 172  
 173          return $result;
 174      }
 175  }