Passed
Push — master ( 3c7afe...11f0b5 )
by
unknown
14:12 queued 14s
created

ArrayArgumentProcessor   A

Complexity

Total Complexity 28

Size/Duplication

Total Lines 167
Duplicated Lines 0 %

Test Coverage

Coverage 98.75%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 28
eloc 78
dl 0
loc 167
ccs 79
cts 80
cp 0.9875
rs 10
c 1
b 0
f 0

5 Methods

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