Passed
Pull Request — master (#4447)
by Owen
12:54
created

ArrayArgumentHelper::getMatrixPair()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 4.25

Importance

Changes 0
Metric Value
eloc 5
dl 0
loc 11
ccs 3
cts 4
cp 0.75
rs 10
c 0
b 0
f 0
cc 4
nc 4
nop 0
crap 4.25
1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet\Calculation\Engine;
4
5
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
6
7
class ArrayArgumentHelper
8
{
9
    protected int $indexStart = 0;
10
11
    /** @var mixed[] */
12
    protected array $arguments;
13
14
    protected int $argumentCount;
15
16
    /** @var int[] */
17
    protected array $rows;
18
19 3787
    /** @var int[] */
20
    protected array $columns;
21 3787
22 3787
    /** @param mixed[] $arguments */
23 3787
    public function initialise(array $arguments): void
24 3787
    {
25
        $keys = array_keys($arguments);
26 3787
        $this->indexStart = (int) array_shift($keys);
27 3787
        $this->rows = $this->rows($arguments);
28
        $this->columns = $this->columns($arguments);
29 3787
30 3787
        $this->argumentCount = count($arguments);
31
        $this->arguments = $this->flattenSingleCellArrays($arguments, $this->rows, $this->columns);
32 3787
33 2
        $this->rows = $this->rows($arguments);
34
        $this->columns = $this->columns($arguments);
35
36
        if ($this->arrayArguments() > 2) {
37 3785
            throw new Exception('Formulae with more than two array arguments are not supported');
38
        }
39 3785
    }
40
41
    /** @return mixed[] */
42 3785
    public function arguments(): array
43
    {
44 3785
        return $this->arguments;
45
    }
46
47 218
    public function hasArrayArgument(): bool
48
    {
49 218
        return $this->arrayArguments() > 0;
50 218
    }
51
52 218
    public function getFirstArrayArgumentNumber(): int
53 218
    {
54 218
        $rowArrays = $this->filterArray($this->rows);
55
        $columnArrays = $this->filterArray($this->columns);
56
57
        for ($index = $this->indexStart; $index < $this->argumentCount; ++$index) {
58
            if (isset($rowArrays[$index]) || isset($columnArrays[$index])) {
59
                return ++$index;
60
            }
61 99
        }
62
63 99
        return 0;
64
    }
65 99
66
    public function getSingleRowVector(): ?int
67
    {
68 99
        $rowVectors = $this->getRowVectors();
69
70 99
        return count($rowVectors) === 1 ? array_pop($rowVectors) : null;
71 99
    }
72 99
73 81
    /** @return int[] */
74
    private function getRowVectors(): array
75
    {
76
        $rowVectors = [];
77 99
        for ($index = $this->indexStart; $index < ($this->indexStart + $this->argumentCount); ++$index) {
78
            if ($this->rows[$index] === 1 && $this->columns[$index] > 1) {
79
                $rowVectors[] = $index;
80 99
            }
81
        }
82 99
83
        return $rowVectors;
84 99
    }
85
86
    public function getSingleColumnVector(): ?int
87 99
    {
88
        $columnVectors = $this->getColumnVectors();
89 99
90 99
        return count($columnVectors) === 1 ? array_pop($columnVectors) : null;
91 99
    }
92 78
93
    /** @return int[] */
94
    private function getColumnVectors(): array
95
    {
96 99
        $columnVectors = [];
97
        for ($index = $this->indexStart; $index < ($this->indexStart + $this->argumentCount); ++$index) {
98
            if ($this->rows[$index] > 1 && $this->columns[$index] === 1) {
99 33
                $columnVectors[] = $index;
100
            }
101 33
        }
102 33
103 33
        return $columnVectors;
104 33
    }
105
106
    /** @return int[] */
107
    public function getMatrixPair(): array
108
    {
109
        for ($i = $this->indexStart; $i < ($this->indexStart + $this->argumentCount - 1); ++$i) {
110
            for ($j = $i + 1; $j < $this->argumentCount; ++$j) {
111
                if (isset($this->rows[$i], $this->rows[$j])) {
112 33
                    return [$i, $j];
113
                }
114 33
            }
115
        }
116
117 7
        return [];
118
    }
119 7
120
    public function isVector(int $argument): bool
121
    {
122 7
        return $this->rows[$argument] === 1 || $this->columns[$argument] === 1;
123
    }
124 7
125
    public function isRowVector(int $argument): bool
126
    {
127 7
        return $this->rows[$argument] === 1;
128
    }
129 7
130
    public function isColumnVector(int $argument): bool
131
    {
132 7
        return $this->columns[$argument] === 1;
133
    }
134 7
135
    public function rowCountMixed(mixed $argument): int
136
    {
137 3787
        return is_int($argument) ? $this->rowCount($argument) : throw new Exception('arg should be int');
138
    }
139 3787
140 3787
    public function rowCount(int $argument): int
141 3787
    {
142 3787
        return $this->rows[$argument];
143
    }
144
145 3787
    public function columnCountMixed(mixed $argument): int
146
    {
147 3787
        return is_int($argument) ? $this->columnCount($argument) : throw new Exception('arg should be int');
148 3787
    }
149 3785
150 3787
    public function columnCount(int $argument): int
151 3787
    {
152 3787
        return $this->columns[$argument];
153
    }
154
155 3787
    /**
156
     * @param mixed[] $arguments
157 3787
     *
158 3787
     * @return int[]
159 3787
     */
160 319
    private function rows(array $arguments): array
161
    {
162
        return array_map(
163
            fn ($argument): int => is_countable($argument) ? count($argument) : 1,
164 3787
            $arguments
165
        );
166
    }
167 3787
168
    /**
169 3787
     * @param mixed[] $arguments
170 3787
     *
171 3714
     * @return int[]
172 3536
     */
173
    private function columns(array $arguments): array
174 3714
    {
175
        return array_map(
176
            fn (mixed $argument): int => is_array($argument) && is_array($argument[array_keys($argument)[0]])
177
                    ? count($argument[array_keys($argument)[0]])
178 3787
                    : 1,
179
            $arguments
180
        );
181 218
    }
182
183 218
    public function arrayArguments(): int
184 218
    {
185 218
        $count = 0;
186 218
        foreach (array_keys($this->arguments) as $argument) {
187
            if ($this->rows[$argument] > 1 || $this->columns[$argument] > 1) {
188
                ++$count;
189
            }
190
        }
191
192
        return $count;
193
    }
194
195
    /**
196
     * @param mixed[] $arguments
197
     * @param int[] $rows
198
     * @param int[] $columns
199
     *
200
     * @return mixed[]
201
     */
202
    private function flattenSingleCellArrays(array $arguments, array $rows, array $columns): array
203
    {
204
        foreach ($arguments as $index => $argument) {
205
            if ($rows[$index] === 1 && $columns[$index] === 1) {
206
                while (is_array($argument)) {
207
                    $argument = array_pop($argument);
208
                }
209
                $arguments[$index] = $argument;
210
            }
211
        }
212
213
        return $arguments;
214
    }
215
216
    /**
217
     * @param mixed[] $array
218
     *
219
     * @return mixed[]
220
     */
221
    private function filterArray(array $array): array
222
    {
223
        return array_filter(
224
            $array,
225
            fn ($value): bool => $value > 1
226
        );
227
    }
228
}
229