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   243722 use 5.010;
  4         36  
4 4     4   21 use strict;
  4         6  
  4         75  
5 4     4   17 use warnings;
  4         7  
  4         2311  
6              
7             our $VERSION = '0.01';
8              
9             use overload
10             '""' => \&stringify,
11             'neg' => \&neg,
12             'abs' => \&abs,
13 34     34   148 '@{}' => sub { $_[0]->{matrix} },
14              
15             '==' => \&eq,
16             '!=' => \&ne,
17              
18             '<' => \<,
19             '>' => \>,
20             '<=' => \&le,
21             '>=' => \&ge,
22              
23 9 50 0 9   31 '<=>' => sub { $_[2] ? -(&cmp($_[0], $_[1]) // return undef) : &cmp($_[0], $_[1]) },
24              
25 2     2   400 '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   10 '>>' => sub { @_ = ($_[1], $_[0]) if $_[2]; goto &rsft },
  3         8  
36 3 50   3   10 '<<' => sub { @_ = ($_[1], $_[0]) if $_[2]; goto &lsft },
  3         9  
37              
38             '+' => \&add,
39             '*' => \&mul,
40              
41 5 100   5   16 '/' => sub { @_ = ($_[1], $_[0]) if $_[2]; goto &div },
  5         15  
42 7 100   7   19 '-' => sub { @_ = ($_[1], $_[0]) if $_[2]; goto &sub },
  7         22  
43              
44 4 50   4   15 '**' => sub { @_ = $_[2] ? @_[1, 0] : @_[0, 1]; goto &pow },
  4         12  
45 3 100   3   13 '%' => sub { @_ = $_[2] ? @_[1, 0] : @_[0, 1]; goto &mod },
  3         12  
46 4     4   4453 ;
  4         3433  
  4         136  
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 12086 my ($class, $matrix) = @_;
56              
57 138         173 my ($rows, $cols);
58              
59 138 100 66     486 if (ref($matrix) eq 'ARRAY' and !@$matrix) {
60 14         17 $rows = -1;
61 14         14 $cols = -1;
62             }
63             else {
64 124 50 33     380 (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     265 $rows //= $#{$matrix};
  124         254  
69 138   100     263 $cols //= $#{$matrix->[0]};
  124         289  
70              
71 138         689 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 1139 my (undef, $rows, $cols, $callback) = @_;
81              
82 12 100       29 if (!defined($callback)) {
83 11         12 $callback = $cols;
84 11         13 $cols = $rows;
85             }
86              
87 12         13 $rows -= 1;
88 12         13 $cols -= 1;
89              
90             __PACKAGE__->new(
91             [
92             map {
93 12         41 my $i = $_;
  60         219  
94 60         83 [map { $callback->($i, $_) } 0 .. $cols];
  415         1274  
95             } 0 .. $rows
96             ]
97             );
98             }
99              
100             sub identity {
101 5     5 1 11 my $n = $_[-1];
102              
103 5 100       12 if ($n <= 0) {
104 1         4 return __PACKAGE__->new([]);
105             }
106              
107 4         9 __PACKAGE__->new([map { [(0) x ($_ - 1), 1, (0) x ($n - $_)] } 1 .. $n]);
  13         36  
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   4 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         16  
131             }
132              
133             sub column {
134 8     8 1 844 my ($self, $n) = @_;
135              
136 8 100       24 ref($self) || goto &_column_vector;
137              
138 6         13 [map { $_->[$n] } @$self];
  18         35  
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 1278 my ($self, $n) = @_;
152              
153 6 50       17 ref($self) || goto &_row_vector;
154              
155 6         6 [@{$self->[$n]}];
  6         14  
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         2 my $n = scalar(@diag);
171              
172 1         3 __PACKAGE__->new([map { [(0) x ($_ - 1), shift(@diag), (0) x ($n - $_)] } 1 .. $n]);
  3         10  
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         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 916 my (undef, @rows) = @_;
189 1         4 __PACKAGE__->new(\@rows);
190             }
191              
192             sub from_columns {
193 1     1 1 912 my (undef, @columns) = @_;
194 1         3 __PACKAGE__->new(\@columns)->transpose;
195             }
196              
197             sub diagonal {
198 2     2 1 1188 my ($self) = @_;
199              
200 2 100       10 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         4 [map { $A->[$_][$_] } 0 .. $self->{rows}];
  3         21  
206             }
207              
208             sub anti_diagonal {
209 2     2 1 803 my ($self) = @_;
210              
211 2 100       10 ref($self) || goto &_new_anti_diagonal;
212              
213 1 50       3 $self->{is_square} or _croak('anti_diagonal(): not a square matrix');
214              
215 1         3 my $A = $self->{matrix};
216 1         3 my $cols = $self->{cols};
217              
218 1         3 [map { $A->[$_][$cols - $_] } 0 .. $self->{rows}];
  3         9  
219             }
220              
221             sub set_column {
222 4     4 1 19 my ($self, $i, $vector) = @_;
223              
224 4 50       10 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         9 my $A = $clone->{matrix};
232              
233 4         5 foreach my $j (0 .. $#{$vector}) {
  4         9  
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 100 my ($self) = @_;
256 58         334 $self->{matrix};
257             }
258              
259             sub size {
260 2     2 1 10 my ($self) = @_;
261 2         8 ($self->{rows} + 1, $self->{cols} + 1);
262             }
263              
264             sub rows {
265 2     2 1 6 my ($self) = @_;
266 2         3 @{$self->{matrix}};
  2         6  
267             }
268              
269             sub columns {
270 1     1 1 4 my ($self) = @_;
271 1         4 $self->transpose->rows;
272             }
273              
274             sub eq {
275 10     10 1 868 my ($m1, $m2) = @_;
276              
277 10 50       24 ref($m1) eq ref($m2) or return 0;
278              
279 10 50       27 $m1->{rows} == $m2->{rows} or return 0;
280 10 100       24 $m1->{cols} == $m2->{cols} or return 0;
281              
282 8         11 my $A = $m1->{matrix};
283 8         11 my $B = $m2->{matrix};
284              
285 8         12 my $rows = $m1->{rows};
286 8         11 my $cols = $m2->{cols};
287              
288 8         15 foreach my $i (0 .. $rows) {
289              
290 22         25 my $Ai = $A->[$i];
291 22         23 my $Bi = $B->[$i];
292              
293 22         27 foreach my $j (0 .. $cols) {
294 88 100       145 $Ai->[$j] == $Bi->[$j] or return 0;
295             }
296             }
297              
298 4         26 return 1;
299             }
300              
301             sub cmp {
302 28     28 1 38 my ($m1, $m2) = @_;
303              
304 28 50       64 ref($m1) eq ref($m2)
305             or _croak("cmp(): can't compare different objects");
306              
307 28         53 my $a1 = $m1->{matrix};
308 28         31 my $a2 = $m2->{matrix};
309              
310 28         32 my $r1 = $m1->{rows};
311 28         37 my $r2 = $m2->{rows};
312              
313 28         39 my $c1 = $m1->{cols};
314 28         31 my $c2 = $m2->{cols};
315              
316 28 100       80 my $min_rows = $r1 < $r2 ? $r1 : $r2;
317 28 100       50 my $min_cols = $c1 < $c2 ? $c1 : $c2;
318              
319 28         45 foreach my $i (0 .. $min_rows) {
320              
321 51         59 my $Ai = $a1->[$i];
322 51         58 my $Bi = $a2->[$i];
323              
324 51         61 foreach my $j (0 .. $min_cols) {
325 148         157 my $cmp = ($Ai->[$j] <=> $Bi->[$j]);
326 148 100       258 $cmp and return $cmp;
327             }
328              
329 39 100       65 if ($c1 != $c2) {
330 10         50 return ($c1 <=> $c2);
331             }
332             }
333              
334 6         27 $r1 <=> $r2;
335             }
336              
337             sub lt {
338 4     4 1 11 my ($m1, $m2) = @_;
339 4         10 $m1->cmp($m2) < 0;
340             }
341              
342             sub gt {
343 4     4 1 9 my ($m1, $m2) = @_;
344 4         8 $m1->cmp($m2) > 0;
345             }
346              
347             sub le {
348 6     6 1 15 my ($m1, $m2) = @_;
349 6         12 $m1->cmp($m2) <= 0;
350             }
351              
352             sub ge {
353 5     5 1 11 my ($m1, $m2) = @_;
354 5         11 $m1->cmp($m2) >= 0;
355             }
356              
357             sub ne {
358 5     5 1 14 my ($m1, $m2) = @_;
359 5         12 !($m1->eq($m2));
360             }
361              
362             sub _LUP_decomposition {
363 20     20   39 my ($self) = @_;
364              
365 20         22 my @A = map { [@$_] } @{$self->{matrix}};
  81         139  
  20         40  
366 20         58 my $N = $self->{rows};
367 20         40 my @P = (0 .. $N + 1);
368              
369 20         38 foreach my $i (0 .. $N) {
370              
371 81         110 my $maxA = 0;
372 81         96 my $imax = $i;
373              
374 81         98 foreach my $k ($i .. $N) {
375 282   50     412 my $absA = CORE::abs($A[$k][$i] // return [$N, \@A, \@P]);
376              
377 282 100       399 if ($absA > $maxA) {
378 102         116 $maxA = $absA;
379 102         129 $imax = $k;
380             }
381             }
382              
383 81 100       109 if ($imax != $i) {
384              
385 23         46 @P[$i, $imax] = @P[$imax, $i];
386 23         39 @A[$i, $imax] = @A[$imax, $i];
387              
388 23         30 ++$P[$N + 1];
389             }
390              
391 81         107 foreach my $j ($i + 1 .. $N) {
392              
393 201 50       275 if ($A[$i][$i] == 0) {
394 0         0 return [$N, \@A, \@P];
395             }
396              
397 201         245 $A[$j][$i] /= $A[$i][$i];
398              
399 201         238 foreach my $k ($i + 1 .. $N) {
400 905         1136 $A[$j][$k] -= $A[$j][$i] * $A[$i][$k];
401             }
402             }
403             }
404              
405 20         84 [$N, \@A, \@P];
406             }
407              
408             sub decompose {
409 22     22 1 33 my ($self) = @_;
410 22   66     25 @{$self->{_decomposition} //= $self->_LUP_decomposition};
  22         65  
411             }
412              
413             # Reduced row echelon form
414              
415             sub rref {
416 2     2 1 7 my ($self) = @_;
417 2   33     6 $self->{_rref} //= do {
418              
419 2         4 my @m = map { [@$_] } @{$self->{matrix}};
  8         16  
  2         5  
420              
421 2 50       5 @m || return __PACKAGE__->new([]);
422              
423 2         7 my ($j, $rows, $cols) = (0, $self->{rows} + 1, $self->{cols} + 1);
424              
425 2         5 OUTER: foreach my $r (0 .. $rows - 1) {
426              
427 8 50       16 $j < $cols or last;
428              
429 8         10 my $i = $r;
430              
431 8         18 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         16 @m[$i, $r] = @m[$r, $i];
438              
439 8         11 my $mr = $m[$r];
440 8         10 my $mrj = $mr->[$j];
441              
442 8         14 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       53 $i == $r and next;
449              
450 24         30 my $mr = $m[$r];
451 24         28 my $mi = $m[$i];
452 24         37 my $mij = $m[$i][$j];
453              
454 24         39 foreach my $k (0 .. $cols - 1) {
455 120         171 $mi->[$k] -= $mij * $mr->[$k];
456             }
457             }
458              
459 8         10 ++$j;
460             }
461              
462 2         6 __PACKAGE__->new(\@m);
463             };
464             }
465              
466             sub clone {
467 4     4 1 8 my ($self) = @_;
468 4         7 __PACKAGE__->new([map { [@$_] } @{$self->{matrix}}]);
  16         34  
  4         16  
469             }
470              
471             sub transpose {
472 8     8 1 20 my ($self) = @_;
473              
474 8         46 my $A = $self->{matrix};
475              
476 8         14 my $rows = $self->{rows};
477 8         11 my $cols = $self->{cols};
478              
479             __PACKAGE__->new(
480             [
481             map {
482 8         16 my $i = $_;
  30         34  
483 30         39 [map { $A->[$_][$i] } 0 .. $rows]
  137         208  
484             } 0 .. $cols
485             ]
486             );
487             }
488              
489             sub concat {
490 3     3 1 18 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       34 or _croak("concat(): matrices do not have the same row count");
497              
498 3         5 my $A = $m1->{matrix};
499 3         6 my $B = $m2->{matrix};
500              
501 3         24 my @C;
502              
503 3         10 foreach my $i (0 .. $m1->{rows}) {
504 12         18 push @C, [@{$A->[$i]}, @{$B->[$i]}];
  12         19  
  12         23  
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             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   15 my ($matrix, $scalar) = @_;
527              
528 8         10 my @B;
529 8         9 foreach my $row (@{$matrix->{matrix}}) {
  8         15  
530 24         30 push @B, [map { $_ * $scalar } @$row];
  64         99  
531             }
532              
533 8         19 __PACKAGE__->new(\@B);
534             }
535              
536             sub _scalar_add {
537 8     8   12 my ($matrix, $scalar) = @_;
538              
539 8         11 my @B;
540 8         10 foreach my $row (@{$matrix->{matrix}}) {
  8         17  
541 24         29 push @B, [map { $_ + $scalar } @$row];
  60         86  
542             }
543              
544 8         18 __PACKAGE__->new(\@B);
545             }
546              
547             sub _scalar_sub {
548 2     2   5 my ($matrix, $scalar) = @_;
549              
550 2         2 my @B;
551 2         3 foreach my $row (@{$matrix->{matrix}}) {
  2         6  
552 6         10 push @B, [map { $_ - $scalar } @$row];
  14         20  
553             }
554              
555 2         5 __PACKAGE__->new(\@B);
556             }
557              
558             sub _scalar_div {
559 2     2   5 my ($matrix, $scalar) = @_;
560              
561 2         3 my @B;
562 2         2 foreach my $row (@{$matrix->{matrix}}) {
  2         5  
563 6         10 push @B, [map { $_ / $scalar } @$row];
  14         24  
564             }
565              
566 2         7 __PACKAGE__->new(\@B);
567             }
568              
569             sub _scalar_mod {
570 2     2   5 my ($matrix, $scalar) = @_;
571              
572 2         3 my @B;
573 2         3 foreach my $row (@{$matrix->{matrix}}) {
  2         5  
574 6         9 push @B, [map { $_ % $scalar } @$row];
  14         23  
575             }
576              
577 2         6 __PACKAGE__->new(\@B);
578             }
579              
580             sub _scalar_and {
581 2     2   34 my ($matrix, $scalar) = @_;
582              
583 2         27 my @B;
584 2         5 foreach my $row (@{$matrix->{matrix}}) {
  2         6  
585 4         6 push @B, [map { $_ & $scalar } @$row];
  12         20  
586             }
587              
588 2         7 __PACKAGE__->new(\@B);
589             }
590              
591             sub _scalar_or {
592 2     2   4 my ($matrix, $scalar) = @_;
593              
594 2         2 my @B;
595 2         4 foreach my $row (@{$matrix->{matrix}}) {
  2         5  
596 4         6 push @B, [map { $_ | $scalar } @$row];
  12         19  
597             }
598              
599 2         6 __PACKAGE__->new(\@B);
600             }
601              
602             sub _scalar_xor {
603 2     2   4 my ($matrix, $scalar) = @_;
604              
605 2         3 my @B;
606 2         3 foreach my $row (@{$matrix->{matrix}}) {
  2         4  
607 4         7 push @B, [map { $_ ^ $scalar } @$row];
  12         18  
608             }
609              
610 2         6 __PACKAGE__->new(\@B);
611             }
612              
613             sub _scalar_lsft {
614 1     1   2 my ($matrix, $scalar) = @_;
615              
616 1         2 my @B;
617 1         1 foreach my $row (@{$matrix->{matrix}}) {
  1         3  
618 2         4 push @B, [map { $_ << $scalar } @$row];
  6         10  
619             }
620              
621 1         3 __PACKAGE__->new(\@B);
622             }
623              
624             sub _scalar_rsft {
625 1     1   3 my ($matrix, $scalar) = @_;
626              
627 1         1 my @B;
628 1         2 foreach my $row (@{$matrix->{matrix}}) {
  1         3  
629 2         4 push @B, [map { $_ >> $scalar } @$row];
  6         10  
630             }
631              
632 1         4 __PACKAGE__->new(\@B);
633             }
634              
635             sub neg {
636 4     4 1 6 my ($matrix) = @_;
637              
638 4         17 my @B;
639 4         6 foreach my $row (@{$matrix->{matrix}}) {
  4         9  
640 12         17 push @B, [map { -$_ } @$row];
  32         54  
641             }
642              
643 4         10 __PACKAGE__->new(\@B);
644             }
645              
646             sub abs {
647 1     1 1 6 my ($matrix) = @_;
648              
649 1         3 my @B;
650 1         2 foreach my $row (@{$matrix->{matrix}}) {
  1         3  
651 4         5 push @B, [map { CORE::abs($_) } @$row];
  8         13  
652             }
653              
654 1         3 __PACKAGE__->new(\@B);
655             }
656              
657             sub map {
658 3     3 1 7 my ($matrix, $callback) = @_;
659              
660 3         7 my $A = $matrix->{matrix};
661 3         4 my $rows = $matrix->{rows};
662 3         3 my $cols = $matrix->{cols};
663              
664 3         4 my @B;
665 3         8 foreach my $i (0 .. $rows) {
666 6         6 my @map;
667 6         7 my $Ai = $A->[$i];
668 6         8 foreach my $j (0 .. $cols) {
669 18         23 local $_ = $Ai->[$j];
670 18         26 push @map, $callback->($i, $j);
671             }
672 6         11 push @B, \@map;
673             }
674              
675 3         16 __PACKAGE__->new(\@B);
676             }
677              
678             sub add {
679 6     6 1 20 my ($m1, $m2) = @_;
680              
681 6 100       18 if (ref($m2) ne ref($m1)) {
682 4         9 return $m1->_scalar_add($m2);
683             }
684              
685 2         5 my $A = $m1->{matrix};
686 2         3 my $B = $m2->{matrix};
687              
688 2         5 my $rows = $m1->{rows};
689 2         3 my $cols = $m1->{cols};
690              
691             ($rows == $m2->{rows} and $cols == $m2->{cols})
692 2 50 33     10 or _croak("add(): matrices of different sizes");
693              
694 2         4 my @C;
695 2         5 foreach my $i (0 .. $rows) {
696 2         3 my $Ai = $A->[$i];
697 2         2 my $Bi = $B->[$i];
698 2         6 push @C, [map { $Ai->[$_] + $Bi->[$_] } 0 .. $cols];
  6         20  
699             }
700              
701 2         7 __PACKAGE__->new(\@C);
702             }
703              
704             sub sub {
705 8     8 1 16 my ($m1, $m2) = @_;
706              
707 8         13 my $r1 = ref($m1);
708 8         12 my $r2 = ref($m2);
709              
710 8 100       16 if ($r1 ne $r2) {
711 6 100       11 if ($r1 eq __PACKAGE__) {
712 2         6 return $m1->_scalar_sub($m2);
713             }
714              
715             # a - b = -b + a
716 4 50       7 if ($r2 eq __PACKAGE__) {
717 4         12 return $m2->neg->_scalar_add($m1);
718             }
719             }
720              
721 2         4 my $A = $m1->{matrix};
722 2         5 my $B = $m2->{matrix};
723              
724 2         4 my $rows = $m1->{rows};
725 2         3 my $cols = $m2->{cols};
726              
727             ($rows == $m2->{rows} and $cols == $m2->{cols})
728 2 50 33     10 or _croak("sub(): matrices of different sizes");
729              
730 2         3 my @C;
731 2         5 foreach my $i (0 .. $rows) {
732 2         3 my $Ai = $A->[$i];
733 2         3 my $Bi = $B->[$i];
734 2         4 push @C, [map { $Ai->[$_] - $Bi->[$_] } 0 .. $cols];
  6         11  
735             }
736              
737 2         5 __PACKAGE__->new(\@C);
738             }
739              
740             sub and {
741 2     2 1 5 my ($m1, $m2) = @_;
742              
743 2 50       7 if (ref($m2) ne ref($m1)) {
744 2         7 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 4 my ($m1, $m2) = @_;
768              
769 2 50       8 if (ref($m2) ne ref($m1)) {
770 2         6 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 5 my ($m1, $m2) = @_;
794              
795 2 50       7 if (ref($m2) ne ref($m1)) {
796 2         6 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 6 my ($m1, $m2) = @_;
820              
821 3         6 my $r1 = ref($m1);
822 3         4 my $r2 = ref($m2);
823              
824 3 100       9 if ($r1 ne $r2) {
825              
826 1 50       3 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         5 my $A = $m1->{matrix};
834 2         3 my $B = $m2->{matrix};
835              
836 2         3 my $rows = $m1->{rows};
837 2         3 my $cols = $m2->{cols};
838              
839             ($rows == $m2->{rows} and $cols == $m2->{cols})
840 2 50 33     10 or _croak("lsft(): matrices of different sizes");
841              
842 2         3 my @C;
843 2         5 foreach my $i (0 .. $rows) {
844 4         6 my $Ai = $A->[$i];
845 4         12 my $Bi = $B->[$i];
846 4         7 push @C, [map { $Ai->[$_] << $Bi->[$_] } 0 .. $cols];
  12         22  
847             }
848              
849 2         7 __PACKAGE__->new(\@C);
850             }
851              
852             sub rsft {
853 3     3 1 7 my ($m1, $m2) = @_;
854              
855 3         5 my $r1 = ref($m1);
856 3         5 my $r2 = ref($m2);
857              
858 3 100       7 if ($r1 ne $r2) {
859              
860 1 50       4 if ($r1 eq __PACKAGE__) {
861 1         3 return $m1->_scalar_rsft($m2);
862             }
863              
864 0         0 _croak("rsft(): invalid argument");
865             }
866              
867 2         4 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     18 or _croak("rsft(): matrices of different sizes");
875              
876 2         3 my @C;
877 2         5 foreach my $i (0 .. $rows) {
878 4         6 my $Ai = $A->[$i];
879 4         5 my $Bi = $B->[$i];
880 4         6 push @C, [map { $Ai->[$_] >> $Bi->[$_] } 0 .. $cols];
  12         22  
881             }
882              
883 2         6 __PACKAGE__->new(\@C);
884             }
885              
886             sub mul {
887 28     28 1 47 my ($m1, $m2) = @_;
888              
889 28 100       57 if (ref($m2) ne ref($m1)) {
890 5         13 return $m1->_scalar_mul($m2);
891             }
892              
893 23         33 my $A = $m1->{matrix};
894 23         28 my $B = $m2->{matrix};
895              
896 23         23 my @c;
897              
898 23         27 my $a_rows = $m1->{rows};
899 23         27 my $b_rows = $m2->{rows};
900 23         25 my $b_cols = $m2->{cols};
901              
902             $m1->{cols} == $m2->{rows}
903 23 50       42 or _croak("mul(): number of columns in A != number of rows in B");
904              
905 23         33 foreach my $i (0 .. $a_rows) {
906 54         62 foreach my $j (0 .. $b_cols) {
907 162         180 foreach my $k (0 .. $b_rows) {
908              
909 468         537 my $t = $A->[$i][$k] * $B->[$k][$j];
910              
911 468 100       557 if (!defined($c[$i][$j])) {
912 162         203 $c[$i][$j] = $t;
913             }
914             else {
915 306         365 $c[$i][$j] += $t;
916             }
917             }
918             }
919             }
920              
921 23         37 __PACKAGE__->new(\@c);
922             }
923              
924             sub div {
925 5     5 1 10 my ($m1, $m2) = @_;
926              
927 5         8 my $r1 = ref($m1);
928 5         26 my $r2 = ref($m2);
929              
930 5 100       25 if ($r1 ne $r2) {
931              
932 4 100       10 if ($r1 eq __PACKAGE__) {
933 2         7 return $m1->_scalar_div($m2);
934             }
935              
936             # A/B = A * B^(-1)
937 2 50       5 if ($r2 eq __PACKAGE__) {
938 2         7 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 8 my ($self) = @_;
947              
948             __PACKAGE__->new(
949             [
950             map {
951             [
952             map {
953 10         13 my $t = CORE::int($_);
  26         29  
954 26 100 100     62 $t -= 1 if ($_ != $t and $_ < 0);
955 26         45 $t;
956             } @$_
957             ]
958 3         5 } @{$self->{matrix}}
  3         6  
959             ]
960             );
961             }
962              
963             sub ceil {
964 1     1 1 2 my ($self) = @_;
965              
966             __PACKAGE__->new(
967             [
968             map {
969             [
970             map {
971 4         7 my $t = CORE::int($_);
  8         9  
972 8 100 100     22 $t += 1 if ($_ != $t and $_ > 0);
973 8         16 $t;
974             } @$_
975             ]
976 1         3 } @{$self->{matrix}}
  1         2  
977             ]
978             );
979             }
980              
981             sub mod {
982 3     3 1 6 my ($A, $B) = @_;
983              
984 3         5 my $r1 = ref($A);
985 3         5 my $r2 = ref($B);
986              
987 3 50       11 if ($r1 ne $r2) {
988              
989 3 100       7 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 18 my ($A, $pow) = @_;
1005              
1006 6         16 $pow = CORE::int($pow);
1007 6         9 my $neg = ($pow < 0);
1008 6         8 $pow = CORE::int(CORE::abs($pow));
1009              
1010 6 100 66     19 return $A->inv if ($neg and $pow == 1);
1011              
1012 4         12 my $B = Math::MatrixLUP::identity($A->{rows} + 1);
1013              
1014 4 50       9 return $B if ($pow == 0);
1015              
1016 4         5 while (1) {
1017 14 100       36 $B = $B->mul($A) if ($pow & 1);
1018 14 100       25 ($pow >>= 1) or last;
1019 10         17 $A = $A->mul($A);
1020             }
1021              
1022 4 50       18 $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       4 $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         5  
1050              
1051 1         3 my ($N, $A, $P) = $self->decompose;
1052              
1053 1         4 my @x = map { $vector->[$P->[$_]] } 0 .. $N;
  0         0  
1054              
1055 1         3 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         4 \@x;
1069             }
1070              
1071             sub invert {
1072 7     7 1 11 my ($self) = @_;
1073              
1074 7 50       17 $self->{is_square} or _croak('invert(): not a square matrix');
1075              
1076 7   66     18 $self->{_inverse} //= do {
1077 3         7 my ($N, $A, $P) = $self->decompose;
1078              
1079 3         6 my @I;
1080              
1081 3         6 foreach my $j (0 .. $N) {
1082 3         6 foreach my $i (0 .. $N) {
1083              
1084 9 100       13 $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         6 for (my $i = $N ; $i >= 0 ; --$i) {
1092              
1093 9         16 foreach my $k ($i + 1 .. $N) {
1094 9         17 $I[$i][$j] -= $A->[$i][$k] * $I[$k][$j];
1095             }
1096              
1097 9   50     29 $I[$i][$j] /= $A->[$i][$i] // return __PACKAGE__->new([]);
1098             }
1099             }
1100              
1101 3         9 __PACKAGE__->new(\@I);
1102             };
1103             }
1104              
1105             *inv = \&invert;
1106              
1107             sub determinant {
1108 18     18 1 499 my ($self) = @_;
1109              
1110 18 50       69 $self->{is_square} or _croak('determinant(): not a square matrix');
1111              
1112 18   66     42 $self->{_determinant} //= do {
1113 18         32 my ($N, $A, $P) = $self->decompose;
1114              
1115 18   100     50 my $det = $A->[0][0] // return 1;
1116              
1117 16         26 foreach my $i (1 .. $N) {
1118 62         77 $det *= $A->[$i][$i];
1119             }
1120              
1121 16 100       37 if (($P->[$N + 1] - $N) % 2 == 0) {
1122 12         25 $det *= -1;
1123             }
1124              
1125 16         100 $det;
1126             };
1127             }
1128              
1129             *det = \&determinant;
1130              
1131             sub stringify {
1132 13     13 1 24 my ($self) = @_;
1133             $self->{_stringification} //=
1134 13   66     62 "[\n " . join(",\n ", map { "[" . join(", ", @$_) . "]" } @{$self->{matrix}}) . "\n]";
  23         136  
  8         26  
1135             }
1136              
1137             1; # End of Math::MatrixLUP