Completed
Push — develop ( 633974...f7b91b )
by Arkadiusz
02:36
created

Matrix   B

Complexity

Total Complexity 43

Size/Duplication

Total Lines 258
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 3
Bugs 0 Features 0
Metric Value
c 3
b 0
f 0
dl 0
loc 258
wmc 43
lcom 1
cbo 2
rs 8.3157

13 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 15 4
A fromFlatArray() 0 9 2
A toArray() 0 4 1
A getRows() 0 4 1
A getColumns() 0 4 1
A getColumnValues() 0 13 3
D getDeterminant() 0 27 9
A isSquare() 0 4 1
A transpose() 0 11 3
B multiply() 0 19 5
A divideByScalar() 0 11 3
B inverse() 0 22 5
B crossOut() 0 19 5

How to fix   Complexity   

Complex Class

Complex classes like Matrix often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Matrix, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare (strict_types = 1);
4
5
namespace Phpml\Math;
6
7
use Phpml\Exception\InvalidArgumentException;
8
use Phpml\Exception\MatrixException;
9
10
class Matrix
11
{
12
    /**
13
     * @var array
14
     */
15
    private $matrix;
16
17
    /**
18
     * @var int
19
     */
20
    private $rows;
21
22
    /**
23
     * @var int
24
     */
25
    private $columns;
26
27
    /**
28
     * @var float
29
     */
30
    private $determinant;
31
32
    /**
33
     * @param array $matrix
34
     * @param bool  $validate
35
     *
36
     * @throws InvalidArgumentException
37
     */
38
    public function __construct(array $matrix, bool $validate = true)
39
    {
40
        $this->rows = count($matrix);
41
        $this->columns = count($matrix[0]);
42
43
        if ($validate) {
44
            for ($i = 0; $i < $this->rows; ++$i) {
45
                if (count($matrix[$i]) !== $this->columns) {
46
                    throw InvalidArgumentException::matrixDimensionsDidNotMatch();
47
                }
48
            }
49
        }
50
51
        $this->matrix = $matrix;
52
    }
53
54
    /**
55
     * @param array $array
56
     * 
57
     * @return Matrix
58
     */
59
    public static function fromFlatArray(array $array)
60
    {
61
        $matrix = [];
62
        foreach ($array as $value) {
63
            $matrix[] = [$value];
64
        }
65
66
        return new self($matrix);
67
    }
68
69
    /**
70
     * @return array
71
     */
72
    public function toArray()
73
    {
74
        return $this->matrix;
75
    }
76
77
    /**
78
     * @return int
79
     */
80
    public function getRows()
81
    {
82
        return $this->rows;
83
    }
84
85
    /**
86
     * @return int
87
     */
88
    public function getColumns()
89
    {
90
        return $this->columns;
91
    }
92
93
    /**
94
     * @param $column
95
     *
96
     * @return array
97
     *
98
     * @throws MatrixException
99
     */
100
    public function getColumnValues($column)
101
    {
102
        if ($column >= $this->columns) {
103
            throw MatrixException::columnOutOfRange();
104
        }
105
106
        $values = [];
107
        for ($i = 0; $i < $this->rows; ++$i) {
108
            $values[] = $this->matrix[$i][$column];
109
        }
110
111
        return $values;
112
    }
113
114
    /**
115
     * @return float|int
116
     * 
117
     * @throws MatrixException
118
     */
119
    public function getDeterminant()
120
    {
121
        if ($this->determinant) {
122
            return $this->determinant;
123
        }
124
125
        if (!$this->isSquare()) {
126
            throw MatrixException::notSquareMatrix();
127
        }
128
129
        $determinant = 0;
130
        if ($this->rows == 1 && $this->columns == 1) {
131
            $determinant = $this->matrix[0][0];
132
        } elseif ($this->rows == 2 && $this->columns == 2) {
133
            $determinant =
134
                $this->matrix[0][0] * $this->matrix[1][1] -
135
                $this->matrix[0][1] * $this->matrix[1][0];
136
        } else {
137
            for ($j = 0; $j < $this->columns; ++$j) {
138
                $subMatrix = $this->crossOut(0, $j);
139
                $minor = $this->matrix[0][$j] * $subMatrix->getDeterminant();
140
                $determinant += fmod($j, 2) == 0 ? $minor : -$minor;
141
            }
142
        }
143
144
        return $this->determinant = $determinant;
145
    }
146
147
    /**
148
     * @return bool
149
     */
150
    public function isSquare()
151
    {
152
        return $this->columns === $this->rows;
153
    }
154
155
    /**
156
     * @return Matrix
157
     */
158
    public function transpose()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
159
    {
160
        $newMatrix = [];
161
        for ($i = 0; $i < $this->rows; ++$i) {
162
            for ($j = 0; $j < $this->columns; ++$j) {
163
                $newMatrix[$j][$i] = $this->matrix[$i][$j];
164
            }
165
        }
166
167
        return new self($newMatrix, false);
168
    }
169
170
    /**
171
     * @param Matrix $matrix
172
     *
173
     * @return Matrix
174
     *
175
     * @throws InvalidArgumentException
176
     */
177
    public function multiply(Matrix $matrix)
178
    {
179
        if ($this->columns != $matrix->getRows()) {
180
            throw InvalidArgumentException::inconsistentMatrixSupplied();
181
        }
182
183
        $product = [];
184
        $multiplier = $matrix->toArray();
185
        for ($i = 0; $i < $this->rows; ++$i) {
186
            for ($j = 0; $j < $matrix->getColumns(); ++$j) {
187
                $product[$i][$j] = 0;
188
                for ($k = 0; $k < $this->columns; ++$k) {
189
                    $product[$i][$j] += $this->matrix[$i][$k] * $multiplier[$k][$j];
190
                }
191
            }
192
        }
193
194
        return new self($product, false);
195
    }
196
197
    /**
198
     * @param $value
199
     *
200
     * @return Matrix
201
     */
202
    public function divideByScalar($value)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
203
    {
204
        $newMatrix = array();
205
        for ($i = 0; $i < $this->rows; ++$i) {
206
            for ($j = 0; $j < $this->columns; ++$j) {
207
                $newMatrix[$i][$j] = $this->matrix[$i][$j] / $value;
208
            }
209
        }
210
211
        return new self($newMatrix, false);
212
    }
213
214
    /**
215
     * @return Matrix
216
     *
217
     * @throws MatrixException
218
     */
219
    public function inverse()
220
    {
221
        if (!$this->isSquare()) {
222
            throw MatrixException::notSquareMatrix();
223
        }
224
225
        $newMatrix = array();
226
        for ($i = 0; $i < $this->rows; ++$i) {
227
            for ($j = 0; $j < $this->columns; ++$j) {
228
                $subMatrix = $this->crossOut($i, $j);
229
                if (fmod($i + $j, 2) == 0) {
230
                    $newMatrix[$i][$j] = ($subMatrix->getDeterminant());
231
                } else {
232
                    $newMatrix[$i][$j] = -($subMatrix->getDeterminant());
233
                }
234
            }
235
        }
236
237
        $cofactorMatrix = new self($newMatrix, false);
238
239
        return $cofactorMatrix->transpose()->divideByScalar($this->getDeterminant());
240
    }
241
242
    /**
243
     * @param int $row
244
     * @param int $column
245
     *
246
     * @return Matrix
247
     */
248
    public function crossOut(int $row, int $column)
249
    {
250
        $newMatrix = [];
251
        $r = 0;
252
        for ($i = 0; $i < $this->rows; ++$i) {
253
            $c = 0;
254
            if ($row != $i) {
255
                for ($j = 0; $j < $this->columns; ++$j) {
256
                    if ($column != $j) {
257
                        $newMatrix[$r][$c] = $this->matrix[$i][$j];
258
                        ++$c;
259
                    }
260
                }
261
                ++$r;
262
            }
263
        }
264
265
        return new self($newMatrix, false);
266
    }
267
}
268