Completed
Push — develop ( 3e4dc3...633974 )
by Arkadiusz
02:40
created

Matrix   B

Complexity

Total Complexity 41

Size/Duplication

Total Lines 245
Duplicated Lines 8.98 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 41
c 2
b 0
f 0
lcom 1
cbo 2
dl 22
loc 245
rs 8.2769

12 Methods

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

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

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
     * @return array
56
     */
57
    public function toArray()
58
    {
59
        return $this->matrix;
60
    }
61
62
    /**
63
     * @return int
64
     */
65
    public function getRows()
66
    {
67
        return $this->rows;
68
    }
69
70
    /**
71
     * @return int
72
     */
73
    public function getColumns()
74
    {
75
        return $this->columns;
76
    }
77
78
    /**
79
     * @param $column
80
     *
81
     * @return array
82
     *
83
     * @throws MatrixException
84
     */
85
    public function getColumnValues($column)
86
    {
87
        if ($column >= $this->columns) {
88
            throw MatrixException::columnOutOfRange();
89
        }
90
91
        $values = [];
92
        for ($i = 0; $i < $this->rows; ++$i) {
93
            $values[] = $this->matrix[$i][$column];
94
        }
95
96
        return $values;
97
    }
98
99
    /**
100
     * @return float|int
101
     * 
102
     * @throws MatrixException
103
     */
104
    public function getDeterminant()
105
    {
106
        if ($this->determinant) {
107
            return $this->determinant;
108
        }
109
110
        if (!$this->isSquare()) {
111
            throw MatrixException::notSquareMatrix();
112
        }
113
114
        $determinant = 0;
115
        if ($this->rows == 1 && $this->columns == 1) {
116
            $determinant = $this->matrix[0][0];
117
        } elseif ($this->rows == 2 && $this->columns == 2) {
118
            $determinant = $this->matrix[0][0] * $this->matrix[1][1] -
119
                $this->matrix[0][1] * $this->matrix[1][0];
120
        } else {
121
            for ($j = 0; $j < $this->columns; ++$j) {
122
                $subMatrix = $this->crossOut(0, $j);
123
                if (fmod($j, 2) == 0) {
124
                    $determinant += $this->matrix[0][$j] * $subMatrix->getDeterminant();
125
                } else {
126
                    $determinant -= $this->matrix[0][$j] * $subMatrix->getDeterminant();
127
                }
128
            }
129
        }
130
131
        return $this->determinant = $determinant;
132
    }
133
134
    /**
135
     * @return bool
136
     */
137
    public function isSquare()
138
    {
139
        return $this->columns === $this->rows;
140
    }
141
142
    /**
143
     * @return Matrix
144
     */
145 View Code Duplication
    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...
146
    {
147
        $newMatrix = [];
148
        for ($i = 0; $i < $this->rows; ++$i) {
149
            for ($j = 0; $j < $this->columns; ++$j) {
150
                $newMatrix[$j][$i] = $this->matrix[$i][$j];
151
            }
152
        }
153
154
        return new self($newMatrix, false);
155
    }
156
157
    /**
158
     * @param Matrix $matrix
159
     *
160
     * @return Matrix
161
     *
162
     * @throws InvalidArgumentException
163
     */
164
    public function multiply(Matrix $matrix)
165
    {
166
        if ($this->columns != $matrix->getRows()) {
167
            throw InvalidArgumentException::inconsistentMatrixSupplied();
168
        }
169
170
        $product = [];
171
        $multiplier = $matrix->toArray();
172
        for ($i = 0; $i < $this->rows; ++$i) {
173
            for ($j = 0; $j < $matrix->getColumns(); ++$j) {
174
                $product[$i][$j] = 0;
175
                for ($k = 0; $k < $this->columns; ++$k) {
176
                    $product[$i][$j] += $this->matrix[$i][$k] * $multiplier[$k][$j];
177
                }
178
            }
179
        }
180
181
        return new self($product, false);
182
    }
183
184
    /**
185
     * @param $value
186
     *
187
     * @return Matrix
188
     */
189 View Code Duplication
    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...
190
    {
191
        $newMatrix = array();
192
        for ($i = 0; $i < $this->rows; ++$i) {
193
            for ($j = 0; $j < $this->columns; ++$j) {
194
                $newMatrix[$i][$j] = $this->matrix[$i][$j] / $value;
195
            }
196
        }
197
198
        return new self($newMatrix, false);
199
    }
200
201
    /**
202
     * @return Matrix
203
     *
204
     * @throws MatrixException
205
     */
206
    public function inverse()
207
    {
208
        if (!$this->isSquare()) {
209
            throw MatrixException::notSquareMatrix();
210
        }
211
212
        $newMatrix = array();
213
        for ($i = 0; $i < $this->rows; ++$i) {
214
            for ($j = 0; $j < $this->columns; ++$j) {
215
                $subMatrix = $this->crossOut($i, $j);
216
                if (fmod($i + $j, 2) == 0) {
217
                    $newMatrix[$i][$j] = ($subMatrix->getDeterminant());
218
                } else {
219
                    $newMatrix[$i][$j] = -($subMatrix->getDeterminant());
220
                }
221
            }
222
        }
223
224
        $cofactorMatrix = new self($newMatrix, false);
225
226
        return $cofactorMatrix->transpose()->divideByScalar($this->getDeterminant());
227
    }
228
229
    /**
230
     * @param int $row
231
     * @param int $column
232
     *
233
     * @return Matrix
234
     */
235
    public function crossOut(int $row, int $column)
236
    {
237
        $newMatrix = [];
238
        $r = 0;
239
        for ($i = 0; $i < $this->rows; ++$i) {
240
            $c = 0;
241
            if ($row != $i) {
242
                for ($j = 0; $j < $this->columns; ++$j) {
243
                    if ($column != $j) {
244
                        $newMatrix[$r][$c] = $this->matrix[$i][$j];
245
                        ++$c;
246
                    }
247
                }
248
                ++$r;
249
            }
250
        }
251
252
        return new self($newMatrix, false);
253
    }
254
}
255