File Coverage

blib/lib/Math/MatrixLUP.pm
Criterion Covered Total %
statement 507 602 84.2
branch 112 182 61.5
condition 34 68 50.0
subroutine 73 84 86.9
pod 53 53 100.0
total 779 989 78.7


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