Completed
Push — develop ( f7b91b...01a249 )
by Arkadiusz
02:37
created

Matrix   B

Complexity

Total Complexity 44

Size/Duplication

Total Lines 264
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 5
Bugs 0 Features 0
Metric Value
c 5
b 0
f 0
dl 0
loc 264
wmc 44
lcom 1
cbo 2
rs 8.3396

14 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
A getDeterminant() 0 12 3
B calculateDeterminant() 0 19 7
A isSquare() 0 4 1
A transpose() 0 11 3
B multiply() 0 19 5
A divideByScalar() 0 11 3
B inverse() 0 18 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
        return $this->determinant = $this->calculateDeterminant();
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->calculateDeterminant() can also be of type integer. However, the property $determinant is declared as type double. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
130
    }
131
132
    /**
133
     * @return float|int
134
     *
135
     * @throws MatrixException
136
     */
137
    private function calculateDeterminant()
138
    {
139
        $determinant = 0;
140
        if ($this->rows == 1 && $this->columns == 1) {
141
            $determinant = $this->matrix[0][0];
142
        } elseif ($this->rows == 2 && $this->columns == 2) {
143
            $determinant =
144
                $this->matrix[0][0] * $this->matrix[1][1] -
145
                $this->matrix[0][1] * $this->matrix[1][0];
146
        } else {
147
            for ($j = 0; $j < $this->columns; ++$j) {
148
                $subMatrix = $this->crossOut(0, $j);
149
                $minor = $this->matrix[0][$j] * $subMatrix->getDeterminant();
150
                $determinant += fmod($j, 2) == 0 ? $minor : -$minor;
151
            }
152
        }
153
154
        return $determinant;
155
    }
156
157
    /**
158
     * @return bool
159
     */
160
    public function isSquare()
161
    {
162
        return $this->columns === $this->rows;
163
    }
164
165
    /**
166
     * @return Matrix
167
     */
168
    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...
169
    {
170
        $newMatrix = [];
171
        for ($i = 0; $i < $this->rows; ++$i) {
172
            for ($j = 0; $j < $this->columns; ++$j) {
173
                $newMatrix[$j][$i] = $this->matrix[$i][$j];
174
            }
175
        }
176
177
        return new self($newMatrix, false);
178
    }
179
180
    /**
181
     * @param Matrix $matrix
182
     *
183
     * @return Matrix
184
     *
185
     * @throws InvalidArgumentException
186
     */
187
    public function multiply(Matrix $matrix)
188
    {
189
        if ($this->columns != $matrix->getRows()) {
190
            throw InvalidArgumentException::inconsistentMatrixSupplied();
191
        }
192
193
        $product = [];
194
        $multiplier = $matrix->toArray();
195
        for ($i = 0; $i < $this->rows; ++$i) {
196
            for ($j = 0; $j < $matrix->getColumns(); ++$j) {
197
                $product[$i][$j] = 0;
198
                for ($k = 0; $k < $this->columns; ++$k) {
199
                    $product[$i][$j] += $this->matrix[$i][$k] * $multiplier[$k][$j];
200
                }
201
            }
202
        }
203
204
        return new self($product, false);
205
    }
206
207
    /**
208
     * @param $value
209
     *
210
     * @return Matrix
211
     */
212
    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...
213
    {
214
        $newMatrix = array();
215
        for ($i = 0; $i < $this->rows; ++$i) {
216
            for ($j = 0; $j < $this->columns; ++$j) {
217
                $newMatrix[$i][$j] = $this->matrix[$i][$j] / $value;
218
            }
219
        }
220
221
        return new self($newMatrix, false);
222
    }
223
224
    /**
225
     * @return Matrix
226
     *
227
     * @throws MatrixException
228
     */
229
    public function inverse()
230
    {
231
        if (!$this->isSquare()) {
232
            throw MatrixException::notSquareMatrix();
233
        }
234
235
        $newMatrix = array();
236
        for ($i = 0; $i < $this->rows; ++$i) {
237
            for ($j = 0; $j < $this->columns; ++$j) {
238
                $minor = $this->crossOut($i, $j)->getDeterminant();
239
                $newMatrix[$i][$j] = fmod($i + $j, 2) == 0 ? $minor : -$minor;
240
            }
241
        }
242
243
        $cofactorMatrix = new self($newMatrix, false);
244
245
        return $cofactorMatrix->transpose()->divideByScalar($this->getDeterminant());
246
    }
247
248
    /**
249
     * @param int $row
250
     * @param int $column
251
     *
252
     * @return Matrix
253
     */
254
    public function crossOut(int $row, int $column)
255
    {
256
        $newMatrix = [];
257
        $r = 0;
258
        for ($i = 0; $i < $this->rows; ++$i) {
259
            $c = 0;
260
            if ($row != $i) {
261
                for ($j = 0; $j < $this->columns; ++$j) {
262
                    if ($column != $j) {
263
                        $newMatrix[$r][$c] = $this->matrix[$i][$j];
264
                        ++$c;
265
                    }
266
                }
267
                ++$r;
268
            }
269
        }
270
271
        return new self($newMatrix, false);
272
    }
273
}
274