File Coverage

blib/lib/Math/FastGF2/Matrix.pm
Criterion Covered Total %
statement 275 423 65.0
branch 106 186 56.9
condition 61 137 44.5
subroutine 27 29 93.1
pod 10 23 43.4
total 479 798 60.0


line stmt bran cond sub pod time code
1              
2             package Math::FastGF2::Matrix;
3              
4 1     1   32258 use 5.006000;
  1         4  
  1         47  
5 1     1   5 use strict;
  1         3  
  1         43  
6 1     1   5 use warnings;
  1         2  
  1         34  
7 1     1   5 use Carp;
  1         2  
  1         104  
8              
9 1     1   673 use Math::FastGF2 ":ops";
  1         3  
  1         192  
10              
11 1     1   6 use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $VERSION);
  1         2  
  1         5996  
12              
13             require Exporter;
14              
15             @ISA = qw(Exporter Math::FastGF2);
16             %EXPORT_TAGS = ( 'all' => [ qw( ) ],
17             );
18             @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
19             @EXPORT = ( );
20             $VERSION = '0.04';
21              
22             require XSLoader;
23             XSLoader::load('Math::FastGF2', $VERSION);
24              
25             our @orgs=("undefined", "rowwise", "colwise");
26              
27             sub new {
28 57     57 1 15638 my $proto = shift;
29 57   33     234 my $class = ref($proto) || $proto;
30 57   33     146 my $parent = ref($proto) && $proto;
31 57         296 my %o=
32             (
33             rows => undef,
34             cols => undef,
35             width => undef,
36             org => "rowwise",
37             @_,
38             );
39 57         64 my $org; # numeric value 1==ROWWISE, 2==COLWISE
40 57         64 my $errors=0;
41              
42 57         102 foreach (qw(rows cols width)) {
43 171 50       428 unless (defined($o{$_})) {
44 0         0 carp "required parameter '$_' not supplied";
45 0         0 ++$errors;
46             }
47             }
48              
49 57 50       137 if (defined($o{"org"})) {
50 57 100       128 if ($o{"org"} eq "rowwise") {
    50          
51             #carp "setting org to 1 as requested";
52 50         71 $org=1;
53             } elsif ($o{"org"} eq "colwise") {
54             #carp "setting org to 2 as requested";
55 7         10 $org=2;
56             } else {
57 0         0 carp "value of 'org' parameter should be 'rowwise' or 'colwise'";
58 0         0 ++$errors;
59             }
60             } else {
61             #carp "defaulting org to 1";
62 0         0 $org=1; # default to ROWWISE
63             }
64              
65 57 50 100     270 if ($o{width} != 1 and $o{width} != 2 and $o{width} != 4) {
      66        
66 0         0 carp "Invalid width $o{width} (must be 1, 2 or 4)";
67 0         0 ++$errors;
68             }
69              
70 57 50       103 return undef if $errors;
71              
72             #carp "Calling C Matrix allocator with rows=$o{rows}, ".
73             # "cols=$o{cols}, width=$o{width}, org=$org";
74 57         409 return alloc_c($class,$o{rows},$o{cols},$o{width},$org);
75              
76             }
77              
78             sub new_identity {
79 27     27 1 1619 my $proto = shift;
80 27   66     69 my $class = ref($proto) || $proto;
81 27   66     92 my $parent = ref($proto) && $proto;
82 27         113 my %o = (
83             size => undef,
84             org => "rowwise", # default to rowwise
85             width => undef,
86             @_
87             );
88 27 50 33     119 unless (defined($o{size}) and $o{size} > 0) {
89 0         0 carp "new_identity needs a size argument";
90 0         0 return undef;
91             }
92 27 50 66     187 unless (defined($o{width}) and ($o{width}==1 or $o{width}==2 or
      33        
93             $o{width}==4)) {
94 0         0 carp "new_identity needs width parameter of 1, 2 or 4";
95 0         0 return undef;
96             }
97 27 50 33     123 unless (defined($o{org}) and ($o{org} eq "rowwise"
      33        
98             or $o{org}== "colwise")) {
99 0         0 carp "new_identity org parameter must be 'rowwise' or 'colwise'";
100 0         0 return undef;
101             }
102 27 50       55 my $org = ($o{org} eq "rowwise" ? 1 : 2);
103              
104 27         117 my $id=alloc_c($class,$o{size},$o{size},$o{width},$org);
105 27 50       54 return undef unless $id;
106 27         63 for my $i (0 .. $o{size} - 1 ) {
107 128         254 $id->setval($i,$i,1);
108             }
109 27         100 return $id;
110             }
111              
112             sub ORG {
113 166     166 0 1851 my $self=shift;
114             #carp "Numeric organisation value is " . $self->ORGNUM;
115 166         1057 return $orgs[$self->ORGNUM];
116             }
117              
118             sub multiply {
119 14     14 0 12857 my $self = shift;
120 14         24 my $class = ref($self);
121 14         15 my $other = shift;
122 14         20 my $result = shift;
123              
124 14 50 33     96 unless (defined($other) and ref($other) eq $class) {
125 0         0 carp "need another matrix to multiply by";
126 0         0 return undef;
127             }
128 14 50       74 unless ($self->COLS == $other->ROWS) {
129 0         0 carp "this matrix's COLS must equal other's ROWS";
130 0         0 return undef;
131             }
132 14 50       50 unless ($self->WIDTH == $other->WIDTH) {
133 0         0 carp "can only multiply two matrices with the same WIDTH";
134 0         0 return undef;
135             }
136              
137 14 100       28 if (defined($result)) {
138 1 50       5 unless (ref($result) eq $class) {
139 0         0 carp "result object is not a matrix";
140 0         0 return undef;
141             }
142 1 50       7 unless ($self->ROWS == $result->ROWS) {
143 0         0 carp "this matrix's ROWS must equal result's ROWS";
144 0         0 return undef;
145             }
146 1 50       10 unless ($self->WIDTH == $result->WIDTH) {
147 0         0 carp "result matrix's WIDTH does not match this ones.";
148 0         0 return undef;
149             }
150             } else {
151 13         61 $result=new($class, rows=>$self->ROWS, cols =>$other->COLS,
152             width=> $self->WIDTH, org=>$self->ORG);
153 13 50 33     71 unless (defined ($result) and ref($result) eq $class) {
154 0         0 carp "Problem allocating new RESULT matrix";
155 0         0 return undef;
156             }
157             }
158              
159 14         278 multiply_submatrix_c($self, $other, $result,
160             0,0,$self->ROWS,
161             0,0,$other->COLS);
162 14         47 return $result;
163             }
164              
165             sub eq {
166 50     50 0 3352 my $self = shift;
167 50         86 my $class = ref($self);
168 50         56 my $other = shift;
169              
170 50 50 33     352 unless (defined($other) and ref($other) eq $class) {
171 0         0 carp "eq needs another matrix to compare against";
172 0         0 return undef;
173             }
174 50 50       174 unless ($self->COLS == $other->COLS) {
175 0         0 return 0;
176             }
177 50 50       142 unless ($self->COLS == $other->COLS) {
178 0         0 return 0;
179             }
180 50 50       168 unless ($self->WIDTH == $other->WIDTH) {
181 0         0 return 0;
182             }
183 50         285 return values_eq_c($self,$other);
184             }
185              
186              
187             sub ne {
188 3     3 0 5 my $self = shift;
189 3         7 my $class = ref($self);
190 3         4 my $other = shift;
191              
192 3 50 33     19 unless (defined($other) and ref($other) eq $class) {
193 0         0 carp "eq needs another matrix to compare against";
194 0         0 return undef;
195             }
196 3 100       15 if ($self->COLS != $other->COLS) {
197 1         6 return 1;
198             }
199 2 50       28 if ($self->COLS != $other->COLS) {
200 0         0 return 1;
201             }
202 2 50       11 if ($self->WIDTH != $other->WIDTH) {
203 0         0 return 1;
204             }
205 2         12 return !values_eq_c($self,$other);
206             }
207              
208             sub offset_to_rowcol {
209 12     12 0 3981 my $self=shift;
210 12         23 my $offset=shift;
211              
212 12 50       93 if ($offset % $self->WIDTH) {
213 0         0 carp "offset must be a multiple of WIDTH in offset_to_rowcol";
214 0         0 return undef;
215             }
216 12         29 $offset /= $self->WIDTH;
217 12 50 33     91 if ($offset < 0 or $offset >= $self->ROWS * $self->COLS) {
218 0         0 carp "Offset out of range in offset_to_rowcol";
219 0         0 return undef;
220             }
221 12 100       19 if ($self->ORG eq "rowwise") {
222 4         22 return ((int ($offset / $self->COLS)),
223             ($offset % $self->COLS) );
224             } else {
225 8         49 return (($offset % $self->ROWS),
226             (int ($offset / $self->ROWS)));
227             }
228             }
229              
230             sub rowcol_to_offset {
231 12     12 0 1100 my $self=shift;
232 12         14 my $row=shift;
233 12         12 my $col=shift;
234              
235 12 50 33     80 if ($row < 0 or $row >= $self->ROWS) {
236 0         0 carp "ROW out of range in rowcol_to_offset";
237 0         0 return undef;
238             }
239 12 50 33     53 if ($col < 0 or $col >= $self->COLS) {
240 0         0 carp "COL out of range in rowcol_to_offset";
241 0         0 return undef;
242             }
243 12 100       25 if ($self->ORG eq "rowwise") {
244 4         25 return ($row * $self->COLS + $col) * $self->WIDTH;# / $self->WIDTH;
245             } else {
246 8         71 return ($col * $self->ROWS + $row) * $self->WIDTH; # / $self->WIDTH
247             }
248             }
249              
250             sub getvals {
251 69     69 0 16312 my $self = shift;
252 69         97 my $class = ref($self);
253 69         63 my $row = shift;
254 69         67 my $col = shift;
255 69         55 my $words = shift;
256 69   100     213 my $order = shift || 0;
257 69         83 my $want_list = wantarray;
258              
259             #carp "Asked to read ROW=$row, COL=$col, len=$bytes (words)";
260              
261 69 50       120 unless ($class) {
262 0         0 carp "getvals only operates on an object instance";
263 0         0 return undef;
264             }
265             #if ($bytes % $self->WIDTH) {
266             # carp "bytes to get must be a multiple of WIDTH";
267             # return undef;
268             #}
269 69 50 33     366 unless (defined($row) and defined($col) and defined($words)) {
      33        
270 0         0 carp "getvals requires row, col, words parameters";
271 0         0 return undef;
272             }
273 69 50 33     286 if ($order < 0 or $order > 2) {
274 0         0 carp "order ($order) != 0 (native), 1 (little-endian) or 2 (big-endian)";
275 0         0 return undef;
276             }
277 69         145 my $width=$self->WIDTH;
278 69         171 my $msize=$self->ROWS * $self->COLS;
279 69 50 33     303 if ($row < 0 or $row >= $self->ROWS) {
280 0         0 carp "starting row out of range";
281 0         0 return undef;
282             }
283 69 50 33     292 if ($col < 0 or $row >= $self->ROWS) {
284 0         0 carp "starting row out of range";
285 0         0 return undef;
286             }
287              
288 69         193 my $s=get_raw_values_c($self, $row, $col, $words, $order);
289              
290 69 100       228 return $s unless $want_list;
291              
292             # Since the get_raw_values_c call swaps byte order, we don't do it here
293 9 100       38 if ($self->WIDTH == 1) {
    100          
294 1         8 return unpack "C*", $s;
295             } elsif ($self->WIDTH == 2) {
296 4         17 return unpack "S*", $s
297             } else {
298 4         20 return unpack "L*", $s;
299             }
300              
301             # return unpack ($self->WIDTH == 2 ? "v*" : "V*"), $s;
302             # return unpack ($self->WIDTH == 2 ? "n*" : "N*"), $s;
303             }
304              
305             sub setvals {
306 71     71 0 5761 my $self = shift;
307 71         90 my $class = ref($self);
308 71         101 my ($row, $col, $vals, $order) = @_;
309 71         59 my ($str,$words);
310 71 100       133 $order=0 unless defined($order);
311              
312             #carp "Asked to write ROW=$row, COL=$col";
313              
314 71 50       124 unless ($class) {
315 0         0 carp "setvals only operates on an object instance";
316 0         0 return undef;
317             }
318 71 50 33     274 unless (defined($row) and defined($col)) {
319 0         0 carp "setvals requires row, col, order parameters";
320 0         0 return undef;
321             }
322 71 50 33     237 if ($order < 0 or $order > 2) {
323 0         0 carp "order != 0 (native), 1 (little-endian) or 2 (big-endian)";
324 0         0 return undef;
325             }
326 71 50 33     328 if ($row < 0 or $row >= $self->ROWS) {
327 0         0 carp "starting row out of range";
328 0         0 return undef;
329             }
330 71 50 33     283 if ($col < 0 or $row >= $self->ROWS) {
331 0         0 carp "starting row out of range";
332 0         0 return undef;
333             }
334              
335 71 100       116 if(ref($vals)) {
336             # treat $vals as a list(ref) of numbers
337 18 50       35 unless ($words=scalar(@$vals)) {
338 0         0 carp "setvals: values must be either a string or reference to a list";
339 0         0 return undef;
340             }
341 18 100       74 if ($self->WIDTH == 1) {
    100          
342 4         15 $str=pack "C*", @$vals;
343             } elsif ($self->WIDTH == 2) {
344 11         39 $str=pack "S*", @$vals;
345             } else {
346 3         13 $str=pack "L*", @$vals;
347             }
348             } else {
349             # treat vals as a string
350 53         58 $str="$vals";
351 53         122 $words=(length $str) / $self->WIDTH;
352             }
353              
354 71         211 my $msize=$self->ROWS * $self->COLS;
355 71 50 66     128 if ( (($self->ORG eq "rowwise") and
      33        
356             ($words + $self->COLS * $row + $col > $msize)) or
357             ($words + $self->ROWS * $col + $row > $msize)) {
358 0         0 carp "string length exceeds matrix size";
359 0         0 return undef;
360             }
361              
362             #carp "Writing $words word(s) to ($row,$col) (string '$str')";
363 71         156 set_raw_values_c($self, $row, $col, $words, $order, $str);
364 71         121 return $str;
365             }
366              
367             # return new matrix with self on left, other on right
368             sub concat {
369 22     22 0 28 my $self = shift;
370 22         28 my $class = ref($self);
371 22         24 my $other = shift;
372              
373 22 50 33     108 unless (defined($other) and ref($other) eq $class) {
374 0         0 carp "concat needs a second matrix to operate on";
375 0         0 return undef;
376             }
377 22 50       75 unless ($self->WIDTH == $other->WIDTH) {
378 0         0 carp "concat: incompatible matrix widths";
379 0         0 return undef;
380             }
381 22 50       70 unless ($self->ROWS == $other->ROWS) {
382 0         0 carp "can't concat: the matrices have different number of rows";
383 0         0 return undef;
384             }
385              
386 22         142 my $cat=alloc_c($class, $self->ROWS, $self->COLS + $other->COLS,
387             $self->WIDTH, $self->ORGNUM);
388 22 50       49 return undef unless defined $cat;
389 22 50       36 if ($self->ORG eq "rowwise") {
390 22         21 my $s;
391 22         53 for my $row (0.. $other->ROWS - 1) {
392 96         284 $s=get_raw_values_c($self, $row, 0, $self->COLS, 0);
393 96         280 set_raw_values_c ($cat, $row, 0, $self->COLS, 0, $s);
394 96         187 for my $col (0.. $other->COLS - 1) {
395 448         1443 $cat->setval($row, $self->COLS + $col,
396             $other->getval($row,$col));
397             }
398             }
399             } else {
400 0         0 my $s;
401 0         0 $s=get_raw_values_c($self, 0, 0, $self->COLS * $self->ROWS, 0);
402 0         0 set_raw_values_c ($cat, 0, 0, $self->COLS * $self->ROWS, 0, $s);
403 0         0 for my $row (0.. $other->ROWS - 1) {
404 0         0 for my $col (0.. $other->COLS - 1) {
405 0         0 $cat->setval($row, $self->COLS + $col,
406             $other->getval($row,$col));
407             }
408             }
409             }
410              
411 22         42 return $cat;
412             }
413              
414             # Swapping rows and columns in a matrix is done in-place
415             sub swap_rows {
416 0     0 0 0 my ($self, $row1, $row2, $start_col) = @_;
417 0 0       0 return if $row1==$row2;
418 0 0       0 $start_col=0 unless defined $start_col;
419              
420 0         0 my $cols=$self->COLS;
421 0         0 my ($s,$t);
422              
423 0 0       0 if ($self->ORG eq "rowwise") {
424 0         0 $s=get_raw_values_c($self, $row1, $start_col,
425             $cols - $start_col, 0);
426 0         0 $t=get_raw_values_c($self, $row2, $start_col,
427             $cols - $start_col, 0);
428 0         0 set_raw_values_c ($self, $row1, $start_col,
429             $cols - $start_col, 0, $t);
430 0         0 set_raw_values_c ($self, $row2, $start_col,
431             $cols - $start_col, 0, $s);
432             } else {
433 0         0 for my $col ($start_col .. $cols -1) {
434 0         0 $s=$self->getval($row1,$col);
435 0         0 $t=$self->getval($row2,$col);
436 0         0 $self->setval($row1, $col, $t);
437 0         0 $self->setval($row2, $col, $s);
438             }
439             }
440             }
441              
442             sub swap_cols {
443 0     0 0 0 my ($self, $col1, $col2, $start_row) = @_;
444 0 0       0 return if $col1==$col2;
445 0 0       0 $start_row=0 unless defined $start_row;
446              
447 0         0 my $rows=$self->ROWS;
448 0         0 my ($s,$t);
449              
450 0 0       0 if ($self->ORG eq "colwise") {
451 0         0 $s=get_raw_values_c($self, $start_row, $col1,
452             $rows - $start_row, 0);
453 0         0 $t=get_raw_values_c($self, $start_row, $col2,
454             $rows - $start_row, 0);
455 0         0 set_raw_values_c ($self, $start_row, $col1,
456             $rows - $start_row, 0, $t);
457 0         0 set_raw_values_c ($self, $start_row, $col2,
458             $rows - $start_row, 0, $s);
459             } else {
460 0         0 for my $row ($start_row .. $rows -1) {
461 0         0 $s=$self->getval($row,$col1);
462 0         0 $t=$self->getval($row,$col2);
463 0         0 $self->setval($row, $col1, $t);
464 0         0 $self->setval($row, $col2, $s);
465             }
466             }
467             }
468              
469              
470             # I'll replace this with some C code later
471             sub solve {
472              
473 22     22 0 25 my $self = shift;
474 22         27 my $class = ref($self);
475              
476 22         41 my $rows=$self->ROWS;
477 22         35 my $cols=$self->COLS;
478 22         43 my $bits=$self->WIDTH * 8;
479              
480 22 50       43 unless ($cols > $rows) {
481 0         0 carp "solve only works on matrices with COLS > ROWS";
482 0         0 return undef;
483             }
484              
485             # work down the diagonal one row at a time ...
486 22         34 for my $row (0 .. $rows - 1) {
487              
488             # We have to check whether the matrix is non-singular; all k x k
489             # sub-matrices generated by the split part of the IDA are
490             # guaranteed to be invertible, but user-supplied matrices may not
491             # be, so we have to test for this.
492              
493 96 50       300 if ($self->getval($row,$row) == 0) {
494 0         0 print "had to swap zeros\n";
495 0         0 my $found=undef;
496 0         0 for my $other_row ($row + 1 .. $rows - 1) {
497 0 0       0 next if $row == $other_row;
498 0 0       0 if ($self->getval($other_row,$row) != 0) {
499 0         0 $found=$other_row;
500 0         0 last;
501             }
502             }
503 0 0       0 return undef unless defined $found;
504 0         0 $self->swap_rows($row,$found,$row);
505             }
506              
507             # normalise the current row first
508 96         320 my $diag_inverse = gf2_inv($bits,$self->getval($row,$row));
509              
510 96         176 $self->setval($row,$row,1);
511 96         151 for my $col ($row + 1 .. $cols - 1) {
512 624         1979 $self->setval($row,$col,
513             gf2_mul($bits, $self->getval($row,$col), $diag_inverse));
514             }
515              
516             # zero all elements above and below ...
517 96         192 for my $other_row (0 .. $rows - 1) {
518 448 100       812 next if $row == $other_row;
519              
520 352         600 my $other=$self->getval($other_row,$row);
521 352 100       610 next if $other == 0;
522 240         393 $self->setval($other_row,$row,0);
523 240         504 for my $col ($row + 1 .. $cols - 1) {
524 1320         4973 $self->setval($other_row,$col,
525             gf2_mul($bits, $self->getval($row,$col), $other) ^
526             $self->getval($other_row,$col));
527             }
528             }
529             }
530              
531 22         123 my $result=alloc_c($class, $rows, $cols - $rows,
532             $self->WIDTH, $self->ORGNUM);
533 22         42 for my $row (0 .. $rows - 1) {
534 96         134 for my $col (0 .. $cols - $rows - 1) {
535 448         1167 $result->setval($row,$col,
536             $self->getval($row, $col + $rows));
537             }
538             }
539              
540 22         117 return $result;
541             }
542              
543             sub invert {
544              
545 22     22 0 385 my $self = shift;
546 22         33 my $class = ref($self);
547              
548             #carp "Asked to invert matrix!";
549              
550 22 50       91 unless ($self->COLS == $self->ROWS) {
551 0         0 carp "invert only works on square matrices";
552 0         0 return undef;
553             }
554              
555 22         84 my $cat=
556             $self->concat($self->new_identity(size => $self->COLS,
557             width => $self->WIDTH));
558 22 50       82 return undef unless defined ($cat);
559 22         57 return $cat->solve;
560             }
561              
562             sub zero {
563 1     1 1 6 my $self = shift;
564 1         2 my $class = ref($self);
565              
566 1         10 $self->setvals(0,0,"\0" x ($self->ROWS * $self->COLS * $self->WIDTH));
567              
568             }
569              
570             # Generic routine for copying some matrix elements into a new matrix
571             #
572             # rows => [ $row1, $row2, ... ]
573             # cols => [ $col1, $col2, ... ]
574             # submatrix => [ $first_row, $first_col, $last_row, $last_col ]
575             #
576             # In order to keep this routine fairly simple, the newly-created
577             # matrix will have the same organisation as the original, and we won't
578             # allow for transposition in the same step.
579             sub copy {
580 15     15 1 25 my $self = shift;
581 15         28 my $class = ref($self);
582 15         55 my %o=(
583             rows => undef,
584             cols => undef,
585             submatrix => undef,
586             @_,
587             );
588              
589 15         18 my $rows = $o{rows};
590 15         16 my $cols = $o{cols};
591 15         15 my $submatrix = $o{submatrix};
592              
593 15 100 100     64 if (defined($submatrix)) {
    100          
594 3 50 33     16 if (defined($rows) or defined($cols)) {
595 0         0 carp "Can't specify both submatrix and rows/cols";
596 0         0 return undef;
597             }
598 3         5 my ($row1,$col1,$row2,$col2)=@$submatrix;
599 3 50 33     31 unless (defined($row1) and defined($col1) and
      33        
      33        
600             defined($row2) and defined($col2)) {
601 0         0 carp 'Need submatrx => [$row1,$col1,$row2,$col2]';
602 0         0 return undef;
603             }
604              
605 3 50 33     48 unless ($row1 >=0 and $row1 <= $row2 and $row2 < $self->ROWS and
      33        
      33        
      33        
      33        
606             $col1 >=0 and $col1 <= $col2 and $col2 < $self->COLS) {
607 0         0 carp "submatrix corners out of range";
608 0         0 return undef;
609             }
610 3         21 my $mat=alloc_c($class, $row2 - $row1 + 1, $col2 - $col1 + 1,
611             $self->WIDTH, $self->ORGNUM);
612 3         4 my ($s,$dest)=("",0);
613 3 50       7 if ($self->ORG eq "rowwise") {
614 3         15 for my $r ($row1 .. $row2) {
615 19         59 $s=$self->getvals($r,$col1,$col2 - $col1 + 1);
616 19         43 $mat->setvals($dest,0,$s);
617 19         26 ++$dest;
618             }
619             } else {
620 0         0 for my $c ($col1 .. $col2) {
621 0         0 $s=$self->getvals($row1,$c,$row2 - $row1 + 1);
622 0         0 $mat->setvals(0,$dest,$s);
623 0         0 ++$dest;
624             }
625             }
626 3         13 return $mat;
627              
628             } elsif (defined($rows) or defined($cols)) {
629              
630 8 50 66     31 if (defined($rows) and !ref($rows)) {
631 0         0 carp "rows must be a reference to a list of rows";
632 0         0 return undef;
633             }
634 8 50 66     36 if (defined($cols) and !ref($cols)) {
635 0         0 carp "cols must be a reference to a list of columns";
636 0         0 return undef;
637             }
638              
639 8 100 100     66 if (defined($rows) and defined($cols)) {
    100 66        
    50 33        
640 2         17 my $mat=alloc_c($class, scalar(@$rows), scalar(@$cols),
641             $self->WIDTH, $self->ORGNUM);
642 2         4 my $dest_row=0;
643 2         3 my $dest_col;
644 2         4 for my $r (@$rows) {
645 11         18 $dest_col=0;
646 11         15 for my $c (@$cols) {
647 70         169 $mat->setval($dest_row,$dest_col++,
648             $self->getval($r,$c));
649             }
650 11         15 ++$dest_row;
651             }
652 2         9 return $mat;
653              
654             } elsif (defined($rows) and $self->ORG eq "rowwise") {
655 3         22 my $mat=alloc_c($class, scalar(@$rows), $self->COLS,
656             $self->WIDTH, $self->ORGNUM);
657 3         6 my ($s,$dest)=("",0);
658 3         7 for my $r (@$rows) {
659 19         64 $s=$self->getvals($r,0,$self->COLS);
660 19         36 $mat->setvals($dest,0,$s);
661 19         25 ++$dest;
662             }
663 3         12 return $mat;
664              
665             } elsif (defined($cols) and $self->ORG eq "colwise") {
666 0         0 my $mat=alloc_c($class, $self->ROWS, scalar(@$cols),
667             $self->WIDTH, $self->ORGNUM);
668 0         0 my ($s,$dest)=("",0);
669 0         0 for my $c (@$cols) {
670 0         0 $s=$self->getvals(0,$c,$self->ROWS);
671 0         0 $mat->setvals(0,$dest,$s);
672 0         0 ++$dest;
673             }
674 0         0 return $mat;
675              
676             } else {
677             # we've been told to copy some rows or some columns, but the
678             # organisation of the matrix doesn't allow for using quick
679             # getvals. Iterate as we would have done if both rows and cols
680             # were specified, but set whichever of rows/cols wasn't set to
681             # the input matrix's rows/cols.
682 3 50       17 $rows=[ 0 .. $self->ROWS - 1] unless defined($rows);
683 3 50       8 $cols=[ 0 .. $self->COLS - 1] unless defined($cols);
684 3         19 my $mat=alloc_c($class, scalar(@$rows), scalar(@$cols),
685             $self->WIDTH, $self->ORGNUM);
686 3         4 my $dest_row=0;
687 3         4 my $dest_col;
688 3         5 for my $r (@$rows) {
689 21         19 $dest_col=0;
690 21         22 for my $c (@$cols) {
691 138         312 $mat->setval($dest_row,$dest_col++,
692             $self->getval($r,$c));
693             }
694 21         28 ++$dest_row;
695             }
696 3         14 return $mat;
697              
698             }
699              
700             } else {
701             # No submatrix/rows/cols option given, so do a full copy. This is
702             # made easy by not allowing transpose or re-organistaion options
703 4         31 my $mat=alloc_c($class, $self->ROWS, $self->COLS,
704             $self->WIDTH, $self->ORGNUM);
705 4 50       9 return undef unless defined $mat;
706 4         15 my $s=$self->getvals(0,0,$self->ROWS * $self->COLS);
707 4         8 $mat->setvals(0,0,$s);
708 4         15 return $mat;
709             }
710              
711 0         0 die "Unreachable? ORLY?\n";
712             }
713              
714             # provide aliases for all forms of copy except copy rows /and/ cols
715              
716             sub copy_rows {
717 1     1 1 4 return shift -> copy(rows => [ @_ ]);
718             }
719              
720             sub copy_cols {
721 1     1 1 5 return shift -> copy(cols => [ @_ ]);
722             }
723              
724             sub submatrix {
725 1     1 1 4 return shift -> copy(submatrix => [ @_ ]);
726             }
727              
728             # Roll the transpose and reorganise code into one "flip" routine.
729             # This can save the user one step in some cases.
730              
731             sub flip {
732 6     6 1 8 my $self=shift;
733 6         15 my %o=( transpose => 0, org => $self->ORG, @_ );
734              
735 6         12 my $transpose=$o{"transpose"};
736 6         6 my $mat;
737 6         5 my ($fliporg,$neworg);
738 0         0 my ($r,$c,$s);
739              
740 6 100       11 if (($o{"org"} ne $self->ORG)) {
741 3         4 $neworg=$o{"org"};
742 3         3 $fliporg=1;
743             } else {
744 3         6 $neworg=$self->ORG;
745 3         5 $fliporg=0;
746             }
747              
748 6 100       27 if ($transpose) {
    100          
749 3         20 $mat=Math::FastGF2::Matrix->
750             new(rows => $self->COLS, cols=>$self->ROWS,
751             width => $self->WIDTH, org => $neworg);
752 3 50       7 return undef unless defined ($mat);
753 3 100       8 if ($fliporg) {
754 1         7 $s=$self->getvals(0,0,$self->COLS * $self->ROWS);
755 1         11 $mat->setvals(0,0,$s);
756             } else {
757 2         13 for $r (0..$self->ROWS - 1) {
758 10         19 for $c (0..$self->COLS - 1) {
759 40         106 $mat->setval($c,$r,$self->getval($r,$c));
760             }
761             }
762             }
763 3         10 return $mat;
764              
765             } elsif ($fliporg) {
766 2         20 $mat=Math::FastGF2::Matrix->
767             new(rows => $self->ROWS, cols=> $self->COLS,
768             width => $self->WIDTH, org => $neworg);
769 2 50       6 return undef unless defined ($mat);
770 2         12 for $r (0..$self->ROWS - 1) {
771 10         18 for $c (0..$self->COLS - 1) {
772 40         92 $mat->setval($r,$c,$self->getval($r,$c));
773             }
774             }
775 2         7 return $mat;
776              
777             } else {
778             # no change, but return a new copy of self to be in line with all
779             # other input cases.
780 1         4 return $self->copy;
781             }
782 0         0 die "Unreachable? ORLY?\n";
783             }
784              
785              
786             sub transpose {
787 1     1 1 48 return shift -> flip(transpose => 1);
788             }
789              
790             sub reorganise {
791 1     1 1 2 my $self=shift;
792              
793 1 50       2 if ($self->ORG eq "rowwise") {
794 1         2 return $self->flip(org => "colwise");
795             } else {
796 0           return $self->flip(org => "rowwise");
797             }
798             }
799              
800              
801             1;
802              
803             __END__