File Coverage

blib/lib/Math/MatrixLUP.pm
Criterion Covered Total %
statement 507 611 82.9
branch 112 182 61.5
condition 34 68 50.0
subroutine 73 85 85.8
pod 54 54 100.0
total 780 1000 78.0


line stmt bran cond sub pod time code
1             package Math::MatrixLUP;
2              
3 4     4   249496 use 5.010;
  4         40  
4 4     4   21 use strict;
  4         6  
  4         77  
5 4     4   15 use warnings;
  4         8  
  4         2189  
6              
7             our $VERSION = '0.03';
8              
9             use overload
10             '""' => \&stringify,
11             'neg' => \&neg,
12             'abs' => \&abs,
13 34     34   153 '@{}' => sub { $_[0]->{matrix} },
14              
15             '==' => \&eq,
16             '!=' => \&ne,
17              
18             '<' => \<,
19             '>' => \>,
20             '<=' => \&le,
21             '>=' => \&ge,
22              
23 9 50 0 9   43 '<=>' => sub { $_[2] ? -(&cmp($_[0], $_[1]) // return undef) : &cmp($_[0], $_[1]) },
24              
25 2     2   561 'eq' => sub { "$_[0]" eq "$_[1]" },
26 0     0   0 'ne' => sub { "$_[0]" ne "$_[1]" },
27              
28 0 0   0   0 'cmp' => sub { $_[2] ? ("$_[1]" cmp $_[0]->stringify) : ($_[0]->stringify cmp "$_[1]") },
29              
30             '&' => \&and,
31             '|' => \&or,
32             '^' => \&xor,
33             '~' => \&transpose,
34              
35 3 50   3   11 '>>' => sub { @_ = ($_[1], $_[0]) if $_[2]; goto &rsft },
  3         14  
36 3 50   3   11 '<<' => sub { @_ = ($_[1], $_[0]) if $_[2]; goto &lsft },
  3         15  
37              
38             '+' => \&add,
39             '*' => \&mul,
40              
41 5 100   5   26 '/' => sub { @_ = ($_[1], $_[0]) if $_[2]; goto &div },
  5         25  
42 7 100   7   32 '-' => sub { @_ = ($_[1], $_[0]) if $_[2]; goto &sub },
  7         37  
43              
44 4 50   4   21 '**' => sub { @_ = $_[2] ? @_[1, 0] : @_[0, 1]; goto &pow },
  4         18  
45 3 100   3   20 '%' => sub { @_ = $_[2] ? @_[1, 0] : @_[0, 1]; goto &mod },
  3         14  
46 4     4   4441 ;
  4         3498  
  4         138  
47              
48             sub _croak {
49 0     0   0 my ($msg) = @_;
50 0         0 require Carp;
51 0         0 Carp::croak($msg);
52             }
53              
54             sub new {
55 138     138 1 17745 my ($class, $matrix) = @_;
56              
57 138         202 my ($rows, $cols);
58              
59 138 100 66     527 if (ref($matrix) eq 'ARRAY' and !@$matrix) {
60 14         21 $rows = -1;
61 14         16 $cols = -1;
62             }
63             else {
64 124 50 33     418 (ref($matrix) eq 'ARRAY' and ref($matrix->[0]) eq 'ARRAY')
65             or _croak("Math::MatrixLUP->new(): invalid argument (expected a 2D array)");
66             }
67              
68 138   100     322 $rows //= $#{$matrix};
  124         262  
69 138   100     266 $cols //= $#{$matrix->[0]};
  124         271  
70              
71 138         687 bless {
72             matrix => $matrix,
73             rows => $rows,
74             cols => $cols,
75             is_square => ($rows == $cols),
76             }, $class;
77             }
78              
79             sub build {
80 12     12 1 1262 my (undef, $rows, $cols, $callback) = @_;
81              
82 12 100       26 if (!defined($callback)) {
83 11         14 $callback = $cols;
84 11         14 $cols = $rows;
85             }
86              
87 12         15 $rows -= 1;
88 12         14 $cols -= 1;
89              
90             __PACKAGE__->new(
91             [
92             map {
93 12         25 my $i = $_;
  60         215  
94 60         74 [map { $callback->($i, $_) } 0 .. $cols];
  415         1312  
95             } 0 .. $rows
96             ]
97             );
98             }
99              
100             sub identity {
101 5     5 1 18 my $n = $_[-1];
102              
103 5 100       14 if ($n <= 0) {
104 1         5 return __PACKAGE__->new([]);
105             }
106              
107 4         13 __PACKAGE__->new([map { [(0) x ($_ - 1), 1, (0) x ($n - $_)] } 1 .. $n]);
  13         44  
108             }
109              
110             *I = \&identity;
111              
112             sub zero {
113 0     0 1 0 my (undef, $row_count, $col_count) = @_;
114              
115 0   0     0 $col_count //= $row_count;
116              
117 0 0       0 if ($row_count <= 0) {
118 0         0 return __PACKAGE__->new([]);
119             }
120              
121 0         0 __PACKAGE__->new([map { [(0) x $col_count] } 1 .. $row_count]);
  0         0  
122             }
123              
124             sub _column_vector {
125 2     2   5 my (undef, $vector) = @_;
126              
127 2 50       7 ref($vector) eq 'ARRAY'
128             or _croak("column(): the vector must be an ARRAY ref");
129              
130 2         5 __PACKAGE__->new([map { [$_] } @$vector]);
  8         17  
131             }
132              
133             sub column {
134 8     8 1 889 my ($self, $n) = @_;
135              
136 8 100       25 ref($self) || goto &_column_vector;
137              
138 6         14 [map { $_->[$n] } @$self];
  18         50  
139             }
140              
141             sub _row_vector {
142 0     0   0 my (undef, $vector) = @_;
143              
144 0 0       0 ref($vector) eq 'ARRAY'
145             or _croak("row(): the vector must be an ARRAY ref");
146              
147 0         0 __PACKAGE__->new([$vector]);
148             }
149              
150             sub row {
151 6     6 1 1303 my ($self, $n) = @_;
152              
153 6 50       24 ref($self) || goto &_row_vector;
154              
155 6         10 [@{$self->[$n]}];
  6         15  
156             }
157              
158             sub scalar {
159 0     0 1 0 my (undef, $n, $value) = @_;
160 0         0 __PACKAGE__->new([map { [(0) x ($_ - 1), $value, (0) x ($n - $_)] } 1 .. $n]);
  0         0  
161             }
162              
163             sub _new_diagonal {
164 1     1   3 my (undef, $vector) = @_;
165              
166 1 50       5 ref($vector) eq 'ARRAY'
167             or _croak("diagonal(): the vector must be an ARRAY ref");
168              
169 1         4 my @diag = @$vector;
170 1         2 my $n = scalar(@diag);
171              
172 1         4 __PACKAGE__->new([map { [(0) x ($_ - 1), shift(@diag), (0) x ($n - $_)] } 1 .. $n]);
  3         13  
173             }
174              
175             sub _new_anti_diagonal {
176 1     1   5 my (undef, $vector) = @_;
177              
178 1 50       5 ref($vector) eq 'ARRAY'
179             or _croak("anti_diagonal(): the vector must be an ARRAY ref");
180              
181 1         3 my @diag = @$vector;
182 1         2 my $n = scalar(@diag);
183              
184 1         3 __PACKAGE__->new([map { [(0) x ($n - $_), shift(@diag), (0) x ($_ - 1)] } 1 .. $n]);
  3         10  
185             }
186              
187             sub from_rows {
188 1     1 1 965 my (undef, @rows) = @_;
189 1         5 __PACKAGE__->new(\@rows);
190             }
191              
192             sub from_columns {
193 1     1 1 937 my (undef, @columns) = @_;
194 1         4 __PACKAGE__->new(\@columns)->transpose;
195             }
196              
197             sub diagonal {
198 2     2 1 1277 my ($self) = @_;
199              
200 2 100       12 ref($self) || goto &_new_diagonal;
201              
202 1 50       5 $self->{is_square} or _croak('diagonal(): not a square matrix');
203              
204 1         2 my $A = $self->{matrix};
205 1         3 [map { $A->[$_][$_] } 0 .. $self->{rows}];
  3         14  
206             }
207              
208             sub anti_diagonal {
209 2     2 1 815 my ($self) = @_;
210              
211 2 100       12 ref($self) || goto &_new_anti_diagonal;
212              
213 1 50       5 $self->{is_square} or _croak('anti_diagonal(): not a square matrix');
214              
215 1         2 my $A = $self->{matrix};
216 1         2 my $cols = $self->{cols};
217              
218 1         2 [map { $A->[$_][$cols - $_] } 0 .. $self->{rows}];
  3         8  
219             }
220              
221             sub set_column {
222 4     4 1 14 my ($self, $i, $vector) = @_;
223              
224 4 50       9 ref($vector) eq 'ARRAY'
225             or _croak("set_column(): the vector must be an ARRAY ref");
226              
227 4         10 $#{$vector} == $self->{rows}
228 4 50       6 or _croak("set_column(): length(vector) != length(matrix)");
229              
230 4         9 my $clone = $self->clone;
231 4         7 my $A = $clone->{matrix};
232              
233 4         5 foreach my $j (0 .. $#{$vector}) {
  4         8  
234 16         20 $A->[$j][$i] = $vector->[$j];
235             }
236              
237 4         7 $clone;
238             }
239              
240             sub set_row {
241 0     0 1 0 my ($self, $i, $vector) = @_;
242              
243 0 0       0 ref($vector) eq 'ARRAY'
244             or _croak("set_row(): the vector must be an ARRAY ref");
245              
246 0         0 $#{$vector} == $self->{cols}
247 0 0       0 or _croak("set_row(): length(vector) != length(matrix)");
248              
249 0         0 my $clone = $self->clone;
250 0         0 $clone->{matrix}[$i] = [@{$vector}];
  0         0  
251 0         0 $clone;
252             }
253              
254             sub as_array {
255 58     58 1 115 my ($self) = @_;
256 58         370 $self->{matrix};
257             }
258              
259             sub size {
260 2     2 1 14 my ($self) = @_;
261 2         9 ($self->{rows} + 1, $self->{cols} + 1);
262             }
263              
264             sub rows {
265 2     2 1 7 my ($self) = @_;
266 2         3 @{$self->{matrix}};
  2         6  
267             }
268              
269             sub columns {
270 1     1 1 3 my ($self) = @_;
271 1         6 $self->transpose->rows;
272             }
273              
274             sub eq {
275 10     10 1 954 my ($m1, $m2) = @_;
276              
277 10 50       31 ref($m1) eq ref($m2) or return 0;
278              
279 10 50       30 $m1->{rows} == $m2->{rows} or return 0;
280 10 100       34 $m1->{cols} == $m2->{cols} or return 0;
281              
282 8         15 my $A = $m1->{matrix};
283 8         16 my $B = $m2->{matrix};
284              
285 8         16 my $rows = $m1->{rows};
286 8         10 my $cols = $m2->{cols};
287              
288 8         19 foreach my $i (0 .. $rows) {
289              
290 22         28 my $Ai = $A->[$i];
291 22         26 my $Bi = $B->[$i];
292              
293 22         29 foreach my $j (0 .. $cols) {
294 88 100       151 $Ai->[$j] == $Bi->[$j] or return 0;
295             }
296             }
297              
298 4         32 return 1;
299             }
300              
301             sub cmp {
302 28     28 1 43 my ($m1, $m2) = @_;
303              
304 28 50       68 ref($m1) eq ref($m2)
305             or _croak("cmp(): can't compare different objects");
306              
307 28         55 my $a1 = $m1->{matrix};
308 28         35 my $a2 = $m2->{matrix};
309              
310 28         36 my $r1 = $m1->{rows};
311 28         39 my $r2 = $m2->{rows};
312              
313 28         44 my $c1 = $m1->{cols};
314 28         38 my $c2 = $m2->{cols};
315              
316 28 100       47 my $min_rows = $r1 < $r2 ? $r1 : $r2;
317 28 100       46 my $min_cols = $c1 < $c2 ? $c1 : $c2;
318              
319 28         52 foreach my $i (0 .. $min_rows) {
320              
321 51         67 my $Ai = $a1->[$i];
322 51         56 my $Bi = $a2->[$i];
323              
324 51         63 foreach my $j (0 .. $min_cols) {
325 148         170 my $cmp = ($Ai->[$j] <=> $Bi->[$j]);
326 148 100       256 $cmp and return $cmp;
327             }
328              
329 39 100       72 if ($c1 != $c2) {
330 10         44 return ($c1 <=> $c2);
331             }
332             }
333              
334 6         25 $r1 <=> $r2;
335             }
336              
337             sub lt {
338 4     4 1 14 my ($m1, $m2) = @_;
339 4         12 $m1->cmp($m2) < 0;
340             }
341              
342             sub gt {
343 4     4 1 12 my ($m1, $m2) = @_;
344 4         11 $m1->cmp($m2) > 0;
345             }
346              
347             sub le {
348 6     6 1 14 my ($m1, $m2) = @_;
349 6         13 $m1->cmp($m2) <= 0;
350             }
351              
352             sub ge {
353 5     5 1 12 my ($m1, $m2) = @_;
354 5         12 $m1->cmp($m2) >= 0;
355             }
356              
357             sub ne {
358 5     5 1 26 my ($m1, $m2) = @_;
359 5         19 !($m1->eq($m2));
360             }
361              
362             sub _LUP_decomposition {
363 20     20   29 my ($self) = @_;
364              
365 20         29 my @A = map { [@$_] } @{$self->{matrix}};
  81         135  
  20         59  
366 20         33 my $N = $self->{rows};
367 20         47 my @P = (0 .. $N + 1);
368              
369 20         46 foreach my $i (0 .. $N) {
370              
371 81         88 my $maxA = 0;
372 81         89 my $imax = $i;
373              
374 81         93 foreach my $k ($i .. $N) {
375 282   50     392 my $absA = CORE::abs($A[$k][$i] // return [$N, \@A, \@P]);
376              
377 282 100       420 if ($absA > $maxA) {
378 102         106 $maxA = $absA;
379 102         117 $imax = $k;
380             }
381             }
382              
383 81 100       108 if ($imax != $i) {
384              
385 23         41 @P[$i, $imax] = @P[$imax, $i];
386 23         32 @A[$i, $imax] = @A[$imax, $i];
387              
388 23         38 ++$P[$N + 1];
389             }
390              
391 81         109 foreach my $j ($i + 1 .. $N) {
392              
393 201 50       283 if ($A[$i][$i] == 0) {
394 0         0 return [$N, \@A, \@P];
395             }
396              
397 201         226 $A[$j][$i] /= $A[$i][$i];
398              
399 201         230 foreach my $k ($i + 1 .. $N) {
400 905         1111 $A[$j][$k] -= $A[$j][$i] * $A[$i][$k];
401             }
402             }
403             }
404              
405 20         89 [$N, \@A, \@P];
406             }
407              
408             sub decompose {
409 22     22 1 32 my ($self) = @_;
410 22   66     25 @{$self->{_decomposition} //= $self->_LUP_decomposition};
  22         76  
411             }
412              
413             # Reduced row echelon form
414              
415             sub rref {
416 2     2 1 8 my ($self) = @_;
417 2   33     8 $self->{_rref} //= do {
418              
419 2         2 my @m = map { [@$_] } @{$self->{matrix}};
  8         18  
  2         6  
420              
421 2 50       6 @m || return __PACKAGE__->new([]);
422              
423 2         6 my ($j, $rows, $cols) = (0, $self->{rows} + 1, $self->{cols} + 1);
424              
425 2         4 OUTER: foreach my $r (0 .. $rows - 1) {
426              
427 8 50       16 $j < $cols or last;
428              
429 8         19 my $i = $r;
430              
431 8         20 while ($m[$i][$j] == 0) {
432 0 0       0 ++$i == $rows or next;
433 0         0 $i = $r;
434 0 0       0 ++$j == $cols and last OUTER;
435             }
436              
437 8         12 @m[$i, $r] = @m[$r, $i];
438              
439 8         12 my $mr = $m[$r];
440 8         13 my $mrj = $mr->[$j];
441              
442 8         13 foreach my $k (0 .. $cols - 1) {
443 40         57 $mr->[$k] /= $mrj;
444             }
445              
446 8         14 foreach my $i (0 .. $rows - 1) {
447              
448 32 100       56 $i == $r and next;
449              
450 24         29 my $mr = $m[$r];
451 24         30 my $mi = $m[$i];
452 24         27 my $mij = $m[$i][$j];
453              
454 24         39 foreach my $k (0 .. $cols - 1) {
455 120         172 $mi->[$k] -= $mij * $mr->[$k];
456             }
457             }
458              
459 8         12 ++$j;
460             }
461              
462 2         6 __PACKAGE__->new(\@m);
463             };
464             }
465              
466             sub clone {
467 4     4 1 5 my ($self) = @_;
468 4         5 __PACKAGE__->new([map { [@$_] } @{$self->{matrix}}]);
  16         27  
  4         6  
469             }
470              
471             sub transpose {
472 8     8 1 27 my ($self) = @_;
473              
474 8         60 my $A = $self->{matrix};
475              
476 8         15 my $rows = $self->{rows};
477 8         16 my $cols = $self->{cols};
478              
479             __PACKAGE__->new(
480             [
481             map {
482 8         25 my $i = $_;
  30         38  
483 30         43 [map { $A->[$_][$i] } 0 .. $rows]
  137         239  
484             } 0 .. $cols
485             ]
486             );
487             }
488              
489             sub concat {
490 3     3 1 10 my ($m1, $m2) = @_;
491              
492 3 50       8 ref($m1) eq ref($m2)
493             or _croak("concat(): expected a Matrix::LUP argument");
494              
495             $m1->{rows} == $m2->{rows}
496 3 50       42 or _croak("concat(): matrices do not have the same row count");
497              
498 3         28 my $A = $m1->{matrix};
499 3         7 my $B = $m2->{matrix};
500              
501 3         4 my @C;
502              
503 3         10 foreach my $i (0 .. $m1->{rows}) {
504 12         15 push @C, [@{$A->[$i]}, @{$B->[$i]}];
  12         19  
  12         34  
505             }
506              
507 3         8 __PACKAGE__->new(\@C);
508             }
509              
510             sub horizontal_flip {
511 0     0 1 0 my ($self) = @_;
512 0         0 __PACKAGE__->new([map { [reverse(@$_)] } @{$self->{matrix}}]);
  0         0  
  0         0  
513             }
514              
515             *hflip = \&horizontal_flip;
516              
517             sub vertical_flip {
518 0     0 1 0 my ($self) = @_;
519 0         0 __PACKAGE__->new([reverse @{$self->{matrix}}]);
  0         0  
520             }
521              
522             *vflip = \&vertical_flip;
523              
524             sub flip {
525 0     0 1 0 my ($self) = @_;
526 0         0 __PACKAGE__->new([reverse map { [reverse(@$_)] } @{$self->{matrix}}]);
  0         0  
  0         0  
527             }
528              
529             sub _scalar_mul {
530 8     8   17 my ($matrix, $scalar) = @_;
531              
532 8         40 my @B;
533 8         12 foreach my $row (@{$matrix->{matrix}}) {
  8         18  
534 24         33 push @B, [map { $_ * $scalar } @$row];
  64         103  
535             }
536              
537 8         25 __PACKAGE__->new(\@B);
538             }
539              
540             sub _scalar_add {
541 8     8   20 my ($matrix, $scalar) = @_;
542              
543 8         11 my @B;
544 8         14 foreach my $row (@{$matrix->{matrix}}) {
  8         16  
545 24         50 push @B, [map { $_ + $scalar } @$row];
  60         92  
546             }
547              
548 8         18 __PACKAGE__->new(\@B);
549             }
550              
551             sub _scalar_sub {
552 2     2   5 my ($matrix, $scalar) = @_;
553              
554 2         4 my @B;
555 2         4 foreach my $row (@{$matrix->{matrix}}) {
  2         6  
556 6         9 push @B, [map { $_ - $scalar } @$row];
  14         24  
557             }
558              
559 2         8 __PACKAGE__->new(\@B);
560             }
561              
562             sub _scalar_div {
563 2     2   5 my ($matrix, $scalar) = @_;
564              
565 2         3 my @B;
566 2         4 foreach my $row (@{$matrix->{matrix}}) {
  2         7  
567 6         11 push @B, [map { $_ / $scalar } @$row];
  14         26  
568             }
569              
570 2         7 __PACKAGE__->new(\@B);
571             }
572              
573             sub _scalar_mod {
574 2     2   5 my ($matrix, $scalar) = @_;
575              
576 2         4 my @B;
577 2         3 foreach my $row (@{$matrix->{matrix}}) {
  2         9  
578 6         9 push @B, [map { $_ % $scalar } @$row];
  14         25  
579             }
580              
581 2         7 __PACKAGE__->new(\@B);
582             }
583              
584             sub _scalar_and {
585 2     2   8 my ($matrix, $scalar) = @_;
586              
587 2         5 my @B;
588 2         3 foreach my $row (@{$matrix->{matrix}}) {
  2         19  
589 4         9 push @B, [map { $_ & $scalar } @$row];
  12         31  
590             }
591              
592 2         7 __PACKAGE__->new(\@B);
593             }
594              
595             sub _scalar_or {
596 2     2   6 my ($matrix, $scalar) = @_;
597              
598 2         3 my @B;
599 2         5 foreach my $row (@{$matrix->{matrix}}) {
  2         7  
600 4         8 push @B, [map { $_ | $scalar } @$row];
  12         22  
601             }
602              
603 2         7 __PACKAGE__->new(\@B);
604             }
605              
606             sub _scalar_xor {
607 2     2   5 my ($matrix, $scalar) = @_;
608              
609 2         4 my @B;
610 2         2 foreach my $row (@{$matrix->{matrix}}) {
  2         8  
611 4         7 push @B, [map { $_ ^ $scalar } @$row];
  12         21  
612             }
613              
614 2         7 __PACKAGE__->new(\@B);
615             }
616              
617             sub _scalar_lsft {
618 1     1   3 my ($matrix, $scalar) = @_;
619              
620 1         2 my @B;
621 1         2 foreach my $row (@{$matrix->{matrix}}) {
  1         3  
622 2         4 push @B, [map { $_ << $scalar } @$row];
  6         10  
623             }
624              
625 1         4 __PACKAGE__->new(\@B);
626             }
627              
628             sub _scalar_rsft {
629 1     1   4 my ($matrix, $scalar) = @_;
630              
631 1         1 my @B;
632 1         3 foreach my $row (@{$matrix->{matrix}}) {
  1         3  
633 2         4 push @B, [map { $_ >> $scalar } @$row];
  6         10  
634             }
635              
636 1         5 __PACKAGE__->new(\@B);
637             }
638              
639             sub neg {
640 4     4 1 8 my ($matrix) = @_;
641              
642 4         6 my @B;
643 4         7 foreach my $row (@{$matrix->{matrix}}) {
  4         10  
644 12         18 push @B, [map { -$_ } @$row];
  32         48  
645             }
646              
647 4         12 __PACKAGE__->new(\@B);
648             }
649              
650             sub abs {
651 1     1 1 7 my ($matrix) = @_;
652              
653 1         2 my @B;
654 1         2 foreach my $row (@{$matrix->{matrix}}) {
  1         2  
655 4         8 push @B, [map { CORE::abs($_) } @$row];
  8         13  
656             }
657              
658 1         3 __PACKAGE__->new(\@B);
659             }
660              
661             sub map {
662 3     3 1 19 my ($matrix, $callback) = @_;
663              
664 3         8 my $A = $matrix->{matrix};
665 3         6 my $rows = $matrix->{rows};
666 3         4 my $cols = $matrix->{cols};
667              
668 3         5 my @B;
669 3         8 foreach my $i (0 .. $rows) {
670 6         6 my @map;
671 6         7 my $Ai = $A->[$i];
672 6         9 foreach my $j (0 .. $cols) {
673 18         26 local $_ = $Ai->[$j];
674 18         29 push @map, $callback->($i, $j);
675             }
676 6         13 push @B, \@map;
677             }
678              
679 3         8 __PACKAGE__->new(\@B);
680             }
681              
682             sub add {
683 6     6 1 29 my ($m1, $m2) = @_;
684              
685 6 100       24 if (ref($m2) ne ref($m1)) {
686 4         18 return $m1->_scalar_add($m2);
687             }
688              
689 2         6 my $A = $m1->{matrix};
690 2         4 my $B = $m2->{matrix};
691              
692 2         4 my $rows = $m1->{rows};
693 2         5 my $cols = $m1->{cols};
694              
695             ($rows == $m2->{rows} and $cols == $m2->{cols})
696 2 50 33     13 or _croak("add(): matrices of different sizes");
697              
698 2         4 my @C;
699 2         7 foreach my $i (0 .. $rows) {
700 2         3 my $Ai = $A->[$i];
701 2         3 my $Bi = $B->[$i];
702 2         6 push @C, [map { $Ai->[$_] + $Bi->[$_] } 0 .. $cols];
  6         13  
703             }
704              
705 2         7 __PACKAGE__->new(\@C);
706             }
707              
708             sub sub {
709 8     8 1 23 my ($m1, $m2) = @_;
710              
711 8         22 my $r1 = ref($m1);
712 8         39 my $r2 = ref($m2);
713              
714 8 100       27 if ($r1 ne $r2) {
715 6 100       18 if ($r1 eq __PACKAGE__) {
716 2         8 return $m1->_scalar_sub($m2);
717             }
718              
719             # a - b = -b + a
720 4 50       11 if ($r2 eq __PACKAGE__) {
721 4         12 return $m2->neg->_scalar_add($m1);
722             }
723             }
724              
725 2         6 my $A = $m1->{matrix};
726 2         5 my $B = $m2->{matrix};
727              
728 2         5 my $rows = $m1->{rows};
729 2         4 my $cols = $m2->{cols};
730              
731             ($rows == $m2->{rows} and $cols == $m2->{cols})
732 2 50 33     13 or _croak("sub(): matrices of different sizes");
733              
734 2         4 my @C;
735 2         6 foreach my $i (0 .. $rows) {
736 2         4 my $Ai = $A->[$i];
737 2         3 my $Bi = $B->[$i];
738 2         4 push @C, [map { $Ai->[$_] - $Bi->[$_] } 0 .. $cols];
  6         13  
739             }
740              
741 2         7 __PACKAGE__->new(\@C);
742             }
743              
744             sub and {
745 2     2 1 8 my ($m1, $m2) = @_;
746              
747 2 50       64 if (ref($m2) ne ref($m1)) {
748 2         47 return $m1->_scalar_and($m2);
749             }
750              
751 0         0 my $A = $m1->{matrix};
752 0         0 my $B = $m2->{matrix};
753              
754 0         0 my $rows = $m1->{rows};
755 0         0 my $cols = $m2->{cols};
756              
757             ($rows == $m2->{rows} and $cols == $m2->{cols})
758 0 0 0     0 or _croak("and(): matrices of different sizes");
759              
760 0         0 my @C;
761 0         0 foreach my $i (0 .. $rows) {
762 0         0 my $Ai = $A->[$i];
763 0         0 my $Bi = $B->[$i];
764 0         0 push @C, [map { $Ai->[$_] & $Bi->[$_] } 0 .. $cols];
  0         0  
765             }
766              
767 0         0 __PACKAGE__->new(\@C);
768             }
769              
770             sub or {
771 2     2 1 6 my ($m1, $m2) = @_;
772              
773 2 50       22 if (ref($m2) ne ref($m1)) {
774 2         9 return $m1->_scalar_or($m2);
775             }
776              
777 0         0 my $A = $m1->{matrix};
778 0         0 my $B = $m2->{matrix};
779              
780 0         0 my $rows = $m1->{rows};
781 0         0 my $cols = $m1->{cols};
782              
783             ($rows == $m2->{rows} and $cols == $m2->{cols})
784 0 0 0     0 or _croak("or(): matrices of different sizes");
785              
786 0         0 my @C;
787 0         0 foreach my $i (0 .. $rows) {
788 0         0 my $Ai = $A->[$i];
789 0         0 my $Bi = $B->[$i];
790 0         0 push @C, [map { $Ai->[$_] | $Bi->[$_] } 0 .. $cols];
  0         0  
791             }
792              
793 0         0 __PACKAGE__->new(\@C);
794             }
795              
796             sub xor {
797 2     2 1 8 my ($m1, $m2) = @_;
798              
799 2 50       7 if (ref($m2) ne ref($m1)) {
800 2         11 return $m1->_scalar_xor($m2);
801             }
802              
803 0         0 my $A = $m1->{matrix};
804 0         0 my $B = $m2->{matrix};
805              
806 0         0 my $rows = $m1->{rows};
807 0         0 my $cols = $m1->{cols};
808              
809             ($rows == $m2->{rows} and $cols == $m2->{cols})
810 0 0 0     0 or _croak("xor(): matrices of different sizes");
811              
812 0         0 my @C;
813 0         0 foreach my $i (0 .. $rows) {
814 0         0 my $Ai = $A->[$i];
815 0         0 my $Bi = $B->[$i];
816 0         0 push @C, [map { $Ai->[$_] ^ $Bi->[$_] } 0 .. $cols];
  0         0  
817             }
818              
819 0         0 __PACKAGE__->new(\@C);
820             }
821              
822             sub lsft {
823 3     3 1 8 my ($m1, $m2) = @_;
824              
825 3         6 my $r1 = ref($m1);
826 3         6 my $r2 = ref($m2);
827              
828 3 100       8 if ($r1 ne $r2) {
829              
830 1 50       4 if ($r1 eq __PACKAGE__) {
831 1         5 return $m1->_scalar_lsft($m2);
832             }
833              
834 0         0 _croak("lsft(): invalid argument");
835             }
836              
837 2         13 my $A = $m1->{matrix};
838 2         4 my $B = $m2->{matrix};
839              
840 2         4 my $rows = $m1->{rows};
841 2         4 my $cols = $m2->{cols};
842              
843             ($rows == $m2->{rows} and $cols == $m2->{cols})
844 2 50 33     15 or _croak("lsft(): matrices of different sizes");
845              
846 2         2 my @C;
847 2         7 foreach my $i (0 .. $rows) {
848 4         5 my $Ai = $A->[$i];
849 4         5 my $Bi = $B->[$i];
850 4         9 push @C, [map { $Ai->[$_] << $Bi->[$_] } 0 .. $cols];
  12         24  
851             }
852              
853 2         5 __PACKAGE__->new(\@C);
854             }
855              
856             sub rsft {
857 3     3 1 7 my ($m1, $m2) = @_;
858              
859 3         7 my $r1 = ref($m1);
860 3         5 my $r2 = ref($m2);
861              
862 3 100       8 if ($r1 ne $r2) {
863              
864 1 50       5 if ($r1 eq __PACKAGE__) {
865 1         5 return $m1->_scalar_rsft($m2);
866             }
867              
868 0         0 _croak("rsft(): invalid argument");
869             }
870              
871 2         4 my $A = $m1->{matrix};
872 2         3 my $B = $m2->{matrix};
873              
874 2         5 my $rows = $m1->{rows};
875 2         3 my $cols = $m1->{cols};
876              
877             ($rows == $m2->{rows} and $cols == $m2->{cols})
878 2 50 33     11 or _croak("rsft(): matrices of different sizes");
879              
880 2         3 my @C;
881 2         5 foreach my $i (0 .. $rows) {
882 4         6 my $Ai = $A->[$i];
883 4         6 my $Bi = $B->[$i];
884 4         8 push @C, [map { $Ai->[$_] >> $Bi->[$_] } 0 .. $cols];
  12         23  
885             }
886              
887 2         6 __PACKAGE__->new(\@C);
888             }
889              
890             sub mul {
891 28     28 1 64 my ($m1, $m2) = @_;
892              
893 28 100       68 if (ref($m2) ne ref($m1)) {
894 5         20 return $m1->_scalar_mul($m2);
895             }
896              
897 23         39 my $A = $m1->{matrix};
898 23         35 my $B = $m2->{matrix};
899              
900 23         27 my @c;
901              
902 23         31 my $a_rows = $m1->{rows};
903 23         31 my $b_rows = $m2->{rows};
904 23         30 my $b_cols = $m2->{cols};
905              
906             $m1->{cols} == $m2->{rows}
907 23 50       45 or _croak("mul(): number of columns in A != number of rows in B");
908              
909 23         42 foreach my $i (0 .. $a_rows) {
910 54         64 foreach my $j (0 .. $b_cols) {
911 162         181 foreach my $k (0 .. $b_rows) {
912              
913 468         528 my $t = $A->[$i][$k] * $B->[$k][$j];
914              
915 468 100       604 if (!defined($c[$i][$j])) {
916 162         200 $c[$i][$j] = $t;
917             }
918             else {
919 306         373 $c[$i][$j] += $t;
920             }
921             }
922             }
923             }
924              
925 23         47 __PACKAGE__->new(\@c);
926             }
927              
928             sub div {
929 5     5 1 47 my ($m1, $m2) = @_;
930              
931 5         27 my $r1 = ref($m1);
932 5         13 my $r2 = ref($m2);
933              
934 5 100       16 if ($r1 ne $r2) {
935              
936 4 100       12 if ($r1 eq __PACKAGE__) {
937 2         8 return $m1->_scalar_div($m2);
938             }
939              
940             # A/B = A * B^(-1)
941 2 50       7 if ($r2 eq __PACKAGE__) {
942 2         8 return $m2->inv->_scalar_mul($m1);
943             }
944             }
945              
946 1         5 $m1->mul($m2->inv);
947             }
948              
949             sub floor {
950 3     3 1 11 my ($self) = @_;
951              
952             __PACKAGE__->new(
953             [
954             map {
955             [
956             map {
957 10         15 my $t = CORE::int($_);
  26         31  
958 26 100 100     71 $t -= 1 if ($_ != $t and $_ < 0);
959 26         47 $t;
960             } @$_
961             ]
962 3         6 } @{$self->{matrix}}
  3         8  
963             ]
964             );
965             }
966              
967             sub ceil {
968 1     1 1 3 my ($self) = @_;
969              
970             __PACKAGE__->new(
971             [
972             map {
973             [
974             map {
975 4         7 my $t = CORE::int($_);
  8         11  
976 8 100 100     24 $t += 1 if ($_ != $t and $_ > 0);
977 8         15 $t;
978             } @$_
979             ]
980 1         3 } @{$self->{matrix}}
  1         5  
981             ]
982             );
983             }
984              
985             sub mod {
986 3     3 1 10 my ($A, $B) = @_;
987              
988 3         7 my $r1 = ref($A);
989 3         6 my $r2 = ref($B);
990              
991 3 50       15 if ($r1 ne $r2) {
992              
993 3 100       8 if ($r1 eq __PACKAGE__) {
994 2         10 return $A->_scalar_mod($B);
995             }
996              
997             # A - B*floor(A/B) = A - B*floor(A * B^(-1))
998 1 50       4 if ($r2 eq __PACKAGE__) {
999 1         3 return Math::MatrixLUP::sub($A, $B->mul($B->inv->_scalar_mul($A)->floor));
1000             }
1001             }
1002              
1003             # A - B*floor(A/B)
1004 0         0 $A->sub($B->mul($A->div($B)->floor));
1005             }
1006              
1007             sub pow {
1008 6     6 1 26 my ($A, $pow) = @_;
1009              
1010 6         14 $pow = CORE::int($pow);
1011 6         11 my $neg = ($pow < 0);
1012 6         9 $pow = CORE::int(CORE::abs($pow));
1013              
1014 6 100 66     22 return $A->inv if ($neg and $pow == 1);
1015              
1016 4         20 my $B = Math::MatrixLUP::identity($A->{rows} + 1);
1017              
1018 4 50       12 return $B if ($pow == 0);
1019              
1020 4         6 while (1) {
1021 14 100       38 $B = $B->mul($A) if ($pow & 1);
1022 14 100       31 ($pow >>= 1) or last;
1023 10         20 $A = $A->mul($A);
1024             }
1025              
1026 4 50       22 $neg ? $B->inv : $B;
1027             }
1028              
1029             sub powmod {
1030 0     0 1 0 my ($A, $pow, $mod) = @_;
1031              
1032 0         0 $pow = CORE::int($pow);
1033              
1034 0 0       0 if ($pow < 0) {
1035 0         0 $A = $A->invmod($mod);
1036 0         0 $pow = CORE::abs($pow);
1037             }
1038              
1039 0         0 my $B = Math::MatrixLUP::identity($A->{rows} + 1);
1040              
1041 0 0       0 return $B->mod($mod) if ($pow == 0);
1042              
1043 0         0 while (1) {
1044 0 0       0 $B = $B->mul($A)->mod($mod) if ($pow & 1);
1045 0 0       0 ($pow >>= 1) or last;
1046 0         0 $A = $A->mul($A)->mod($mod);
1047             }
1048              
1049 0         0 $B->mod($mod);
1050             }
1051              
1052             sub solve {
1053 1     1 1 4 my ($self, $vector) = @_;
1054              
1055 1 50       6 $self->{is_square} or _croak('solve(): not a square matrix');
1056 1 50       4 ref($vector) eq 'ARRAY' or _croak('solve(): the vector must be an ARRAY ref');
1057 1 50       2 $#{$vector} == $self->{rows} or _croak('solve(): length(vector) != length(matrix)');
  1         5  
1058              
1059 1         4 my ($N, $A, $P) = $self->decompose;
1060              
1061 1         4 my @x = map { $vector->[$P->[$_]] } 0 .. $N;
  0         0  
1062              
1063 1         4 foreach my $i (1 .. $N) {
1064 0         0 foreach my $k (0 .. $i - 1) {
1065 0         0 $x[$i] -= $A->[$i][$k] * $x[$k];
1066             }
1067             }
1068              
1069 1         4 for (my $i = $N ; $i >= 0 ; --$i) {
1070 0         0 foreach my $k ($i + 1 .. $N) {
1071 0         0 $x[$i] -= $A->[$i][$k] * $x[$k];
1072             }
1073 0         0 $x[$i] /= $A->[$i][$i];
1074             }
1075              
1076 1         5 \@x;
1077             }
1078              
1079             sub invert {
1080 7     7 1 24 my ($self) = @_;
1081              
1082 7 50       22 $self->{is_square} or _croak('invert(): not a square matrix');
1083              
1084 7   66     20 $self->{_inverse} //= do {
1085 3         11 my ($N, $A, $P) = $self->decompose;
1086              
1087 3         6 my @I;
1088              
1089 3         7 foreach my $j (0 .. $N) {
1090 3         6 foreach my $i (0 .. $N) {
1091              
1092 9 100       15 $I[$i][$j] = ($P->[$i] == $j) ? 1 : 0;
1093              
1094 9         14 foreach my $k (0 .. $i - 1) {
1095 9         25 $I[$i][$j] -= $A->[$i][$k] * $I[$k][$j];
1096             }
1097             }
1098              
1099 3         6 for (my $i = $N ; $i >= 0 ; --$i) {
1100              
1101 9         18 foreach my $k ($i + 1 .. $N) {
1102 9         10 $I[$i][$j] -= $A->[$i][$k] * $I[$k][$j];
1103             }
1104              
1105 9   50     19 $I[$i][$j] /= $A->[$i][$i] // return __PACKAGE__->new([]);
1106             }
1107             }
1108              
1109 3         9 __PACKAGE__->new(\@I);
1110             };
1111             }
1112              
1113             *inv = \&invert;
1114              
1115             sub invmod {
1116 0     0 1 0 my ($self, $mod) = @_;
1117              
1118 0         0 my $A = $self->invert->clone;
1119              
1120 0         0 foreach my $row (@$A) {
1121 0         0 foreach my $i (0 .. $#{$row}) {
  0         0  
1122 0         0 $row->[$i] %= $mod;
1123             }
1124             }
1125              
1126 0         0 return $A;
1127             }
1128              
1129             sub determinant {
1130 18     18 1 553 my ($self) = @_;
1131              
1132 18 50       89 $self->{is_square} or _croak('determinant(): not a square matrix');
1133              
1134 18   66     48 $self->{_determinant} //= do {
1135 18         41 my ($N, $A, $P) = $self->decompose;
1136              
1137 18   100     48 my $det = $A->[0][0] // return 1;
1138              
1139 16         27 foreach my $i (1 .. $N) {
1140 62         74 $det *= $A->[$i][$i];
1141             }
1142              
1143 16 100       46 if (($P->[$N + 1] - $N) % 2 == 0) {
1144 12         18 $det *= -1;
1145             }
1146              
1147 16         93 $det;
1148             };
1149             }
1150              
1151             *det = \&determinant;
1152              
1153             sub stringify {
1154 13     13 1 32 my ($self) = @_;
1155             $self->{_stringification} //=
1156 13   66     122 "[\n " . join(",\n ", map { "[" . join(", ", @$_) . "]" } @{$self->{matrix}}) . "\n]";
  23         183  
  8         36  
1157             }
1158              
1159             1; # End of Math::MatrixLUP