File Coverage

lib/Math/FastGF2/Matrix.pm
Criterion Covered Total %
statement 314 562 55.8
branch 112 228 49.1
condition 64 181 35.3
subroutine 30 35 85.7
pod 13 26 50.0
total 533 1032 51.6


line stmt bran cond sub pod time code
1              
2             package Math::FastGF2::Matrix;
3              
4 3     3   225975 use 5.006000;
  3         28  
5 3     3   22 use strict;
  3         3  
  3         70  
6 3     3   14 use warnings;
  3         5  
  3         95  
7 3     3   16 no warnings qw(redefine);
  3         4  
  3         113  
8              
9 3     3   17 use Carp;
  3         6  
  3         198  
10              
11 3     3   1348 use Math::FastGF2 ":ops";
  3         8  
  3         527  
12              
13 3     3   23 use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $VERSION);
  3         5  
  3         418  
14              
15             require Exporter;
16              
17             @ISA = qw(Exporter Math::FastGF2);
18             %EXPORT_TAGS = ( 'all' => [ qw( ) ],
19             );
20             @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
21             @EXPORT = ( );
22             $VERSION = '0.07';
23              
24             require XSLoader;
25             XSLoader::load('Math::FastGF2', $VERSION);
26              
27             use constant {
28 3         18412 ROWWISE => 1,
29             COLWISE => 2,
30 3     3   21 };
  3         7  
31              
32             our @orgs=("undefined", "rowwise", "colwise");
33              
34             sub new {
35 58     58 1 11050 my $proto = shift;
36 58   33     208 my $class = ref($proto) || $proto;
37 58   33     133 my $parent = ref($proto) && $proto;
38 58         256 my %o=
39             (
40             rows => undef,
41             cols => undef,
42             width => undef,
43             org => "rowwise",
44             @_,
45             );
46 58         88 my $org; # numeric value 1==ROWWISE, 2==COLWISE
47 58         85 my $errors=0;
48              
49 58         107 foreach (qw(rows cols width)) {
50 174 50       350 unless (defined($o{$_})) {
51 0         0 carp "required parameter '$_' not supplied";
52 0         0 ++$errors;
53             }
54             }
55              
56 58 50       115 if (defined($o{"org"})) {
57 58 100       122 if ($o{"org"} eq "rowwise") {
    50          
58             #carp "setting org to 1 as requested";
59 51         84 $org=1;
60             } elsif ($o{"org"} eq "colwise") {
61             #carp "setting org to 2 as requested";
62 7         12 $org=2;
63             } else {
64 0         0 carp "value of 'org' parameter should be 'rowwise' or 'colwise'";
65 0         0 ++$errors;
66             }
67             } else {
68             #carp "defaulting org to 1";
69 0         0 $org=1; # default to ROWWISE
70             }
71              
72 58 50 100     219 if ($o{width} != 1 and $o{width} != 2 and $o{width} != 4) {
      66        
73 0         0 carp "Invalid width $o{width} (must be 1, 2 or 4)";
74 0         0 ++$errors;
75             }
76              
77 58 50       110 return undef if $errors;
78              
79 58         305 return alloc_c($class,$o{rows},$o{cols},$o{width},$org);
80             }
81              
82              
83             sub new_identity {
84 445     445 1 1806 my $proto = shift;
85 445   66     894 my $class = ref($proto) || $proto;
86 445   66     1376 my $parent = ref($proto) && $proto;
87 445         1653 my %o = (
88             size => undef,
89             org => "rowwise", # default to rowwise
90             width => undef,
91             @_
92             );
93 445 50 33     1635 unless (defined($o{size}) and $o{size} > 0) {
94 0         0 carp "new_identity needs a size argument";
95 0         0 return undef;
96             }
97 445 50 66     1424 unless (defined($o{width}) and ($o{width}==1 or $o{width}==2 or
      66        
98             $o{width}==4)) {
99 0         0 carp "new_identity needs width parameter of 1, 2 or 4";
100 0         0 return undef;
101             }
102 445 50 33     1311 unless (defined($o{org}) and ($o{org} eq "rowwise"
      33        
103             or $o{org}== "colwise")) {
104 0         0 carp "new_identity org parameter must be 'rowwise' or 'colwise'";
105 0         0 return undef;
106             }
107 445 50       855 my $org = ($o{org} eq "rowwise" ? 1 : 2);
108              
109 445         1432 my $id=alloc_c($class,$o{size},$o{size},$o{width},$org);
110 445 50       904 return undef unless $id;
111 445         1039 for my $i (0 .. $o{size} - 1 ) {
112 1382         2767 $id->setval($i,$i,1);
113             }
114 445         1358 return $id;
115             }
116              
117             sub ORG {
118 800     800 0 2282 my $self=shift;
119             #carp "Numeric organisation value is " . $self->ORGNUM;
120 800         2722 return $orgs[$self->ORGNUM];
121             }
122              
123             sub multiply {
124 14     14 0 5034 my $self = shift;
125 14         29 my $class = ref($self);
126 14         17 my $other = shift;
127 14         23 my $result = shift;
128              
129 14 50 33     72 unless (defined($other) and ref($other) eq $class) {
130 0         0 carp "need another matrix to multiply by";
131 0         0 return undef;
132             }
133 14 50       59 unless ($self->COLS == $other->ROWS) {
134 0         0 carp "this matrix's COLS must equal other's ROWS";
135 0         0 return undef;
136             }
137 14 50       42 unless ($self->WIDTH == $other->WIDTH) {
138 0         0 carp "can only multiply two matrices with the same WIDTH";
139 0         0 return undef;
140             }
141              
142 14 100       33 if (defined($result)) {
143 1 50       4 unless (ref($result) eq $class) {
144 0         0 carp "result object is not a matrix";
145 0         0 return undef;
146             }
147 1 50       6 unless ($self->ROWS == $result->ROWS) {
148 0         0 carp "this matrix's ROWS must equal result's ROWS";
149 0         0 return undef;
150             }
151 1 50       6 unless ($self->WIDTH == $result->WIDTH) {
152 0         0 carp "result matrix's WIDTH does not match this ones.";
153 0         0 return undef;
154             }
155             } else {
156 13         50 $result=new($class, rows=>$self->ROWS, cols =>$other->COLS,
157             width=> $self->WIDTH, org=>$self->ORG);
158 13 50 33     56 unless (defined ($result) and ref($result) eq $class) {
159 0         0 carp "Problem allocating new RESULT matrix";
160 0         0 return undef;
161             }
162             }
163              
164 14         154 multiply_submatrix_c($self, $other, $result,
165             0,0,$self->ROWS,
166             0,0,$other->COLS);
167 14         38 return $result;
168             }
169              
170             sub eq {
171 259     259 0 2859 my $self = shift;
172 259         459 my $class = ref($self);
173 259         335 my $other = shift;
174              
175 259 50 33     972 unless (defined($other) and ref($other) eq $class) {
176 0         0 carp "eq needs another matrix to compare against";
177 0         0 return undef;
178             }
179 259 50       688 unless ($self->COLS == $other->COLS) {
180 0         0 return 0;
181             }
182 259 50       644 unless ($self->COLS == $other->COLS) {
183 0         0 return 0;
184             }
185 259 50       652 unless ($self->WIDTH == $other->WIDTH) {
186 0         0 return 0;
187             }
188 259         1074 return values_eq_c($self,$other);
189             }
190              
191              
192             sub ne {
193 212     212 0 120520 my $self = shift;
194 212         423 my $class = ref($self);
195 212         315 my $other = shift;
196              
197 212 50 33     938 unless (defined($other) and ref($other) eq $class) {
198 0         0 carp "eq needs another matrix to compare against";
199 0         0 return undef;
200             }
201 212 100       759 if ($self->COLS != $other->COLS) {
202 1         5 return 1;
203             }
204 211 50       563 if ($self->COLS != $other->COLS) {
205 0         0 return 1;
206             }
207 211 50       516 if ($self->WIDTH != $other->WIDTH) {
208 0         0 return 1;
209             }
210 211         993 return !values_eq_c($self,$other);
211             }
212              
213              
214             sub _offset_to_rowcol_disabled {
215 0     0   0 my $self=shift;
216 0         0 my $offset=shift;
217              
218             # XS calls are expensive, so do them only once
219             my ($WIDTH, $ROWS, $COLS) =
220 0         0 map { $self->$_ } qw{WIDTH ROWS COLS};
  0         0  
221              
222 0         0 $offset /= $WIDTH;
223 0 0       0 if (int($offset) != $offset) {
224 0         0 carp "offset must be a multiple of WIDTH in offset_to_rowcol";
225 0         0 return undef;
226             }
227 0 0 0     0 if ($offset < 0 or $offset >= $ROWS * $COLS) {
228 0         0 carp "Offset out of range in offset_to_rowcol";
229 0         0 return undef;
230             }
231 0 0       0 if (ROWWISE == $self->ORGNUM) {
232 0         0 my $row = int ($offset / $COLS);
233 0         0 return ( ($row),
234             ($offset - $row * $COLS) ); # = $offset % $cols
235             } else {
236 0         0 return (($offset % $ROWS),
237             (int ($offset / $ROWS)));
238             }
239             }
240              
241             sub rowcol_to_offset {
242 12     12 0 26 my $self=shift;
243 12         18 my $row=shift;
244 12         15 my $col=shift;
245              
246 12 50 33     63 if ($row < 0 or $row >= $self->ROWS) {
247 0         0 carp "ROW out of range in rowcol_to_offset";
248 0         0 return undef;
249             }
250 12 50 33     45 if ($col < 0 or $col >= $self->COLS) {
251 0         0 carp "COL out of range in rowcol_to_offset";
252 0         0 return undef;
253             }
254 12 100       25 if ($self->ORG eq "rowwise") {
255 4         25 return ($row * $self->COLS + $col) * $self->WIDTH;# / $self->WIDTH;
256             } else {
257 8         47 return ($col * $self->ROWS + $row) * $self->WIDTH; # / $self->WIDTH
258             }
259             }
260              
261             sub getvals {
262 696     696 0 6040 my $self = shift;
263 696         1021 my $class = ref($self);
264 696         901 my $row = shift;
265 696         850 my $col = shift;
266 696         891 my $words = shift;
267 696   100     1659 my $order = shift || 0;
268 696         1011 my $want_list = wantarray;
269              
270             # XS calls are expensive, so do them only once
271             my ($WIDTH, $ROWS, $COLS) =
272 696         1115 map { $self->$_ } qw{WIDTH ROWS COLS};
  2088         4716  
273              
274 696 50       1433 unless ($class) {
275 0         0 carp "getvals only operates on an object instance";
276 0         0 return undef;
277             }
278             #if ($bytes % $self->WIDTH) {
279             # carp "bytes to get must be a multiple of WIDTH";
280             # return undef;
281             #}
282 696 50 33     2605 unless (defined($row) and defined($col) and defined($words)) {
      33        
283 0         0 carp "getvals requires row, col, words parameters";
284 0         0 return undef;
285             }
286 696 50 33     1925 if ($order < 0 or $order > 2) {
287 0         0 carp "order ($order) != 0 (native), 1 (little-endian) or 2 (big-endian)";
288 0         0 return undef;
289             }
290 696         918 my $width=$WIDTH;
291 696         911 my $msize=$ROWS * $COLS;
292 696 50 33     1915 if ($row < 0 or $row >= $ROWS) {
293 0         0 carp "starting row out of range";
294 0         0 return undef;
295             }
296 696 50 33     1798 if ($col < 0 or $row >= $ROWS) {
297 0         0 carp "starting row out of range";
298 0         0 return undef;
299             }
300              
301 696         2050 my $s=get_raw_values_c($self, $row, $col, $words, $order);
302              
303 696 100       2188 return $s unless $want_list;
304              
305             # Since the get_raw_values_c call swaps byte order, we don't do it here
306 9 100       28 if ($WIDTH == 1) {
    100          
307 1         8 return unpack "C*", $s;
308             } elsif ($WIDTH == 2) {
309 4         20 return unpack "S*", $s
310             } else {
311 4         16 return unpack "L*", $s;
312             }
313              
314             # return unpack ($self->WIDTH == 2 ? "v*" : "V*"), $s;
315             # return unpack ($self->WIDTH == 2 ? "n*" : "N*"), $s;
316             }
317              
318             sub setvals {
319 698     698 0 5012 my $self = shift;
320 698         1007 my $class = ref($self);
321 698         1239 my ($row, $col, $vals, $order) = @_;
322 698         912 my ($str,$words);
323 698 100       1646 $order=0 unless defined($order);
324              
325             # XS calls are expensive, so do them only once
326             my ($WIDTH, $ROWS, $COLS) =
327 698         1107 map { $self->$_ } qw{WIDTH ROWS COLS};
  2094         4574  
328              
329 698 50       1383 unless ($class) {
330 0         0 carp "setvals only operates on an object instance";
331 0         0 return undef;
332             }
333 698 50 33     2155 unless (defined($row) and defined($col)) {
334 0         0 carp "setvals requires row, col, order parameters";
335 0         0 return undef;
336             }
337 698 50 33     1908 if ($order < 0 or $order > 2) {
338 0         0 carp "order != 0 (native), 1 (little-endian) or 2 (big-endian)";
339 0         0 return undef;
340             }
341 698 50 33     1835 if ($row < 0 or $row >= $ROWS) {
342 0         0 carp "starting row out of range";
343 0         0 return undef;
344             }
345 698 50 33     1984 if ($col < 0 or $row >= $ROWS) {
346 0         0 carp "starting row out of range";
347 0         0 return undef;
348             }
349              
350 698 100       1166 if(ref($vals)) {
351             # treat $vals as a list(ref) of numbers
352 18 50       48 unless ($words=scalar(@$vals)) {
353 0         0 carp "setvals: values must be either a string or reference to a list";
354 0         0 return undef;
355             }
356 18 100       49 if ($WIDTH == 1) {
    100          
357 4         14 $str=pack "C*", @$vals;
358             } elsif ($WIDTH == 2) {
359 11         40 $str=pack "S*", @$vals;
360             } else {
361 3         13 $str=pack "L*", @$vals;
362             }
363             } else {
364             # treat vals as a string
365 680         988 $str="$vals";
366 680         1063 $words=(length $str) / $WIDTH;
367             }
368              
369 698         980 my $msize=$ROWS * $COLS;
370 698 50 66     3479 if ( ((ROWWISE == $self->ORGNUM) and
      33        
371             ($words + $COLS * $row + $col > $msize)) or
372             ($words + $ROWS * $col + $row > $msize)) {
373 0         0 carp "string length exceeds matrix size";
374 0         0 return undef;
375             }
376              
377             #carp "Writing $words word(s) to ($row,$col) (string '$str')";
378 698         1950 set_raw_values_c($self, $row, $col, $words, $order, $str);
379 698         1221 return $str;
380             }
381              
382             # return new matrix with self on left, other on right
383             sub concat {
384 440     440 0 633 my $self = shift;
385 440         1018 my $class = ref($self);
386 440         614 my $other = shift;
387              
388 440 50 33     1574 unless (defined($other) and ref($other) eq $class) {
389 0         0 carp "concat needs a second matrix to operate on";
390 0         0 return undef;
391             }
392 440 50       1176 unless ($self->WIDTH == $other->WIDTH) {
393 0         0 carp "concat: incompatible matrix widths";
394 0         0 return undef;
395             }
396 440 50       1165 unless ($self->ROWS == $other->ROWS) {
397 0         0 carp "can't concat: the matrices have different number of rows";
398 0         0 return undef;
399             }
400              
401 440         2023 my $cat=alloc_c($class, $self->ROWS, $self->COLS + $other->COLS,
402             $self->WIDTH, $self->ORGNUM);
403 440 50       923 return undef unless defined $cat;
404 440 50       784 if ($self->ORG eq "rowwise") {
405 440         630 my $s;
406 440         1011 for my $row (0.. $other->ROWS - 1) {
407 1350         3685 $s=get_raw_values_c($self, $row, 0, $self->COLS, 0);
408 1350         3928 set_raw_values_c ($cat, $row, 0, $self->COLS, 0, $s);
409 1350         2658 for my $col (0.. $other->COLS - 1) {
410 4210         11118 $cat->setval($row, $self->COLS + $col,
411             $other->getval($row,$col));
412             }
413             }
414             } else {
415 0         0 my $s;
416 0         0 $s=get_raw_values_c($self, 0, 0, $self->COLS * $self->ROWS, 0);
417 0         0 set_raw_values_c ($cat, 0, 0, $self->COLS * $self->ROWS, 0, $s);
418 0         0 for my $row (0.. $other->ROWS - 1) {
419 0         0 for my $col (0.. $other->COLS - 1) {
420 0         0 $cat->setval($row, $self->COLS + $col,
421             $other->getval($row,$col));
422             }
423             }
424             }
425              
426 440         854 return $cat;
427             }
428              
429             # Swapping rows and columns in a matrix is done in-place
430             sub swap_rows {
431 90     90 0 194 my ($self, $row1, $row2, $start_col) = @_;
432 90 50       159 return if $row1==$row2;
433 90 50       155 $start_col=0 unless defined $start_col;
434              
435 90         175 my $cols=$self->COLS;
436 90         135 my ($s,$t);
437              
438 90 50       158 if ($self->ORG eq "rowwise") {
439 90         327 $s=get_raw_values_c($self, $row1, $start_col,
440             $cols - $start_col, 0);
441 90         211 $t=get_raw_values_c($self, $row2, $start_col,
442             $cols - $start_col, 0);
443 90         264 set_raw_values_c ($self, $row1, $start_col,
444             $cols - $start_col, 0, $t);
445 90         217 set_raw_values_c ($self, $row2, $start_col,
446             $cols - $start_col, 0, $s);
447             } else {
448 0         0 for my $col ($start_col .. $cols -1) {
449 0         0 $s=$self->getval($row1,$col);
450 0         0 $t=$self->getval($row2,$col);
451 0         0 $self->setval($row1, $col, $t);
452 0         0 $self->setval($row2, $col, $s);
453             }
454             }
455             }
456              
457             sub swap_cols {
458 0     0 0 0 my ($self, $col1, $col2, $start_row) = @_;
459 0 0       0 return if $col1==$col2;
460 0 0       0 $start_row=0 unless defined $start_row;
461              
462 0         0 my $rows=$self->ROWS;
463 0         0 my ($s,$t);
464              
465 0 0       0 if ($self->ORG eq "colwise") {
466 0         0 $s=get_raw_values_c($self, $start_row, $col1,
467             $rows - $start_row, 0);
468 0         0 $t=get_raw_values_c($self, $start_row, $col2,
469             $rows - $start_row, 0);
470 0         0 set_raw_values_c ($self, $start_row, $col1,
471             $rows - $start_row, 0, $t);
472 0         0 set_raw_values_c ($self, $start_row, $col2,
473             $rows - $start_row, 0, $s);
474             } else {
475 0         0 for my $row ($start_row .. $rows -1) {
476 0         0 $s=$self->getval($row,$col1);
477 0         0 $t=$self->getval($row,$col2);
478 0         0 $self->setval($row, $col1, $t);
479 0         0 $self->setval($row, $col2, $s);
480             }
481             }
482             }
483              
484              
485             # I'll replace this with some C code later
486             sub solve {
487              
488 440     440 0 602 my $self = shift;
489 440         716 my $class = ref($self);
490              
491 440         771 my $rows=$self->ROWS;
492 440         816 my $cols=$self->COLS;
493 440         813 my $bits=$self->WIDTH * 8;
494              
495 440 50       842 unless ($cols > $rows) {
496 0         0 carp "solve only works on matrices with COLS > ROWS";
497 0         0 return undef;
498             }
499              
500             # work down the diagonal one row at a time ...
501 440         823 for my $row (0 .. $rows - 1) {
502              
503             # We have to check whether the matrix is non-singular; all k x k
504             # sub-matrices generated by the split part of the IDA are
505             # guaranteed to be invertible, but user-supplied matrices may not
506             # be, so we have to test for this.
507              
508 1350 100       3082 if ($self->getval($row,$row) == 0) {
509 90         1968 print "had to swap zeros\n";
510 90         280 my $found=undef;
511 90         219 for my $other_row ($row + 1 .. $rows - 1) {
512 90 50       195 next if $row == $other_row;
513 90 50       326 if ($self->getval($other_row,$row) != 0) {
514 90         146 $found=$other_row;
515 90         168 last;
516             }
517             }
518 90 50       169 return undef unless defined $found;
519 90         188 $self->swap_rows($row,$found,$row);
520             }
521              
522             # normalise the current row first
523 1350         3152 my $diag_inverse = gf2_inv($bits,$self->getval($row,$row));
524              
525 1350         3113 $self->setval($row,$row,1);
526 1350         2386 for my $col ($row + 1 .. $cols - 1) {
527 5640         14277 $self->setval($row,$col,
528             gf2_mul($bits, $self->getval($row,$col), $diag_inverse));
529             }
530              
531             # zero all elements above and below ...
532 1350         2195 for my $other_row (0 .. $rows - 1) {
533 4210 100       7374 next if $row == $other_row;
534              
535 2860         4754 my $other=$self->getval($other_row,$row);
536 2860 100       4788 next if $other == 0;
537 2478         4960 $self->setval($other_row,$row,0);
538 2478         3939 for my $col ($row + 1 .. $cols - 1) {
539 10332         30421 $self->setval($other_row,$col,
540             gf2_mul($bits, $self->getval($row,$col), $other) ^
541             $self->getval($other_row,$col));
542             }
543             }
544             }
545              
546 440         1565 my $result=alloc_c($class, $rows, $cols - $rows,
547             $self->WIDTH, $self->ORGNUM);
548 440         849 for my $row (0 .. $rows - 1) {
549 1350         2087 for my $col (0 .. $cols - $rows - 1) {
550 4210         9316 $result->setval($row,$col,
551             $self->getval($row, $col + $rows));
552             }
553             }
554              
555 440         1723 return $result;
556             }
557              
558             sub invert {
559              
560 440     440 0 1582 my $self = shift;
561 440         742 my $class = ref($self);
562              
563             #carp "Asked to invert matrix!";
564              
565 440 50       1550 unless ($self->COLS == $self->ROWS) {
566 0         0 carp "invert only works on square matrices";
567 0         0 return undef;
568             }
569              
570 440         1341 my $cat=
571             $self->concat($self->new_identity(size => $self->COLS,
572             width => $self->WIDTH));
573 440 50       1552 return undef unless defined ($cat);
574 440         972 return $cat->solve;
575             }
576              
577             sub zero {
578 1     1 1 6 my $self = shift;
579 1         3 my $class = ref($self);
580              
581 1         10 $self->setvals(0,0,"\0" x ($self->ROWS * $self->COLS * $self->WIDTH));
582              
583             }
584              
585              
586             # Create a new Cauchy matrix
587             # (was being done in Crypt::IDA before, but it's more relevant here)
588             sub new_cauchy {
589 0     0 1 0 my $proto = shift;
590 0   0     0 my $class = ref($proto) || $proto;
591 0   0     0 my $parent = ref($proto) && $proto;
592 0         0 my %o = (
593             org => "rowwise",
594             width => undef,
595             xvals => undef, # was "sharelist"
596             yvals => undef,
597             xyvals => undef, # was "key"
598             rows => undef,
599             cols => undef,
600             @_
601             );
602              
603 0   0     0 my $w = $o{width} || die "width parameter required\n";
604              
605             # any of these could be undefined
606 0         0 my ($rows, $cols, $xvals, $yvals);
607 0         0 $rows = $o{rows};
608 0         0 $cols = $o{cols};
609 0         0 $xvals = $o{xvals};
610 0         0 $yvals = $o{yvals};
611              
612 0 0       0 if (defined $o{xyvals}) {
613 0         0 my $xyvals = $o{xyvals};
614 0 0       0 die "Can't pass both xyvals and xvals\n" if defined $xvals;
615 0 0       0 die "Can't pass both xyvals and yvals\n" if defined $yvals;
616              
617 0 0 0     0 if (defined $rows and !defined $cols) {
    0 0        
618 0         0 $cols = @$xyvals - $rows;
619             # warn "Rows was $rows. Set 'cols' to $cols\n";
620             } elsif (defined $cols and !defined $rows) {
621 0         0 $rows = @$xyvals - $cols;
622             # warn "Cols was $cols. Set 'rows' to $rows\n";
623             } else {
624 0         0 die "Passing xyvals requires rows and/or cols\n";
625             }
626              
627             # case where both are defined is handled later
628              
629             # break up into two lists
630 0         0 $xvals = [@$xyvals[0.. $rows - 1]];
631 0         0 $yvals = [@$xyvals[$rows .. $rows + $cols - 1]];
632             # warn "Set up xvals with " . (@$xvals+0) . " elements\n";
633             # warn "Set up yvals with " . (@$yvals+0) . " elements\n";
634             } else {
635             # if user sets cols/rows explicitly, don't overwrite
636 0 0 0     0 $rows = scalar(@$xvals) if defined $xvals and !defined $rows;
637 0 0 0     0 $cols = scalar(@$yvals) if defined $yvals and !defined $cols;
638             }
639              
640 0 0 0     0 die "Must have either xyvals + rows/cols or xvals + yvals\n"
641             unless defined $xvals and defined $yvals;
642 0 0       0 die "Rows/xvals are inconsistent\n" unless @$xvals == $rows;
643 0 0       0 die "Cols/yvals are inconsistent\n" unless @$yvals == $cols;
644 0 0       0 die "Must have rows >= cols\n" unless $rows >= $cols;
645              
646             # Note: no check done to ensure that x,y values are unique
647 0         0 my @x = @$xvals;
648 0         0 my @y = @$yvals;
649              
650             my $self = $class->new(rows => $rows, cols => $cols, width => $w,
651 0         0 org => $o{org});
652 0 0       0 die unless ref $self;
653              
654             # ported from IDA::ida_key_to_matrix
655 0         0 $w <<= 3; # bits <- bytes
656 0         0 for my $row (0..$rows - 1 ) {
657 0         0 for my $col (0 .. $cols - 1) {
658 0         0 my $xi = $x[$row];
659 0         0 my $yj = $y[$col];
660 0         0 $self->setval($row, $col, gf2_inv($w, $xi ^ $yj));
661             }
662             }
663 0         0 return $self;
664             }
665              
666              
667             #
668             # New method to calculate inverse Cauchy matrix given list:
669             #
670             # xyvals = [ x_1, ... x_n, y_1, ... y_k ]
671             #
672             # See Wikipedia page on Cauchy matrices:
673             #
674             # https://en.wikipedia.org/wiki/Cauchy_matrix
675             #
676             # A better page:
677             #
678             # https://proofwiki.org/wiki/Inverse_of_Cauchy_Matrix
679              
680             sub new_inverse_cauchy {
681              
682             # parameter names were based on Crypt::IDA::ida_key_to_matrix
683 0     0 1 0 my $proto = shift;
684 0   0     0 my $class = ref($proto) || $proto;
685 0   0     0 my $parent = ref($proto) && $proto;
686 0         0 my %o = (
687             org => "rowwise",
688             size => undef, # was "quorum"
689             width => undef,
690             #shares => undef, # was "shares"
691             xvals => undef, # was "sharelist"
692             width => undef,
693             xylist => undef, # was "key"
694            
695             @_
696             );
697              
698             # Our "key" is in x's, y's format. The last "quorum" values are y's
699             # To create a square matrix, we only take the x values specified
700             # in the xvals listref
701 0   0     0 my $k = $o{size} || die "size parameter required\n";
702 0   0     0 my $w = $o{width} || die "width parameter required\n";
703 0   0     0 my $key = $o{xylist} || die "xylist parameter required\n";
704 0 0       0 die "xvals parameter required\n" unless defined $o{xvals};
705              
706             # Note: no check done below to ensure that xvals are unique
707 0         0 my @x = map {$key->[$_]} @{$o{xvals}};
  0         0  
  0         0  
708 0         0 my @y = splice @$key, -$k;
709 0         0 push @$key, @y; # splice is destructive; undo damage
710              
711             # warn "xlist: [" . (join ", ", @x) . "]\n";
712             # warn "ylist: [" . (join ", ", @y) . "]\n";
713 0 0       0 die if @x - @y; # is it square?
714 0 0       0 die if @$key < 2 * $k; # is n >= k?
715 0 0       0 die if @x != $k; # did user supply k xvals?
716              
717             my $self = $class->new(rows => $k, cols => $k, width => $w,
718 0         0 org => $o{org});
719 0 0       0 die unless ref $self;
720              
721             # Using the proof wiki page as a guide...
722             #
723             # rename k to n so we can use it as a loop counter (and go -1
724             # because we're using zero-based indexes)
725 0         0 my ($i, $j, $n, $bits) = (0,0, $k-1, $w * 8);
726              
727 0 0       0 if ($n < 3) {
728             # unoptimised version
729 0         0 for $i (0 .. $n) {
730 0         0 for $j (0 .. $n) {
731            
732 0         0 my $top = 1; # multiplicative identity
733             map {
734 0         0 $top = gf2_mul($bits, $top, $x[$j] ^ $y[$_]);
  0         0  
735 0         0 $top = gf2_mul($bits, $top, $x[$_] ^ $y[$i]);
736             } (0 .. $n);
737            
738 0         0 my $bot = $x[$j] ^ $y[$i];
739            
740 0         0 map { $bot = gf2_mul($bits, $bot, $x[$j] ^ $x[$_]) }
741 0         0 grep { $_ != $j } (0 .. $n); # $_ is our $k
  0         0  
742            
743 0         0 map { $bot = gf2_mul($bits, $bot, $y[$i] ^ $y[$_]) }
744 0         0 grep { $_ != $i } (0 .. $n); # $_ is our $k
  0         0  
745            
746 0         0 $top = gf2_mul($bits, $top, gf2_inv($bits, $bot));
747            
748 0         0 $self->setval($i,$j,$top);
749             }
750             }
751             } else {
752             # The above two big product loops are ripe for optimisation
753             # Instead of calculating:
754             # * product of row except for ... (n-1 multiplications)
755             # * product of col except for ... (n-1 multiplications)
756             # You can memoise full row/col products
757             #
758             # This should be hugely noticeable for large n, but should
759             # probably also even have a positive effect for n>3 assuming
760             # a word size of 1 byte and gf2_inv that is as cheap (or cheaper)
761             # than a multiplication.
762              
763 0         0 my @jmemo = ();
764 0         0 for $j (0..$n) {
765 0         0 my ($sum,$xj) = (1, $x[$j]);
766 0         0 map { $sum = gf2_mul($bits, $sum, $xj ^ $x[$_]) }
767 0         0 grep { $_ != $j} (0..$n);
  0         0  
768 0         0 push @jmemo, $sum;
769             }
770 0         0 my @imemo = ();
771 0         0 for $i (0..$n) {
772 0         0 my ($sum,$yi) = (1, $y[$i]);
773 0         0 map { $sum = gf2_mul($bits, $sum, $yi ^ $y[$_]) }
774 0         0 grep {$_ != $i} (0..$n);
  0         0  
775 0         0 push @imemo, $sum;
776             }
777 0         0 for $i (0 .. $n) {
778 0         0 for $j (0 .. $n) {
779              
780             # We can save one multiplication by 1 below:
781 0         0 my $top = gf2_mul($bits, $x[$j] ^ $y[0], $x[0] ^ $y[$i]);
782             map {
783 0         0 $top = gf2_mul($bits, $top, $x[$j] ^ $y[$_]);
  0         0  
784 0         0 $top = gf2_mul($bits, $top, $x[$_] ^ $y[$i]);
785             } (1 .. $n);
786            
787 0         0 my $bot = $x[$j] ^ $y[$i];
788 0         0 $bot = gf2_mul($bits, $bot, $jmemo[$j]);
789 0         0 $bot = gf2_mul($bits, $bot, $imemo[$i]);
790            
791 0         0 $top = gf2_mul($bits, $top, gf2_inv($bits, $bot));
792            
793 0         0 $self->setval($i,$j,$top);
794             }
795             }
796             }
797 0         0 return $self;
798             }
799              
800             sub new_vandermonde {
801 1     1 1 572 my $proto = shift;
802 1   33     7 my $class = ref($proto) || $proto;
803 1   33     4 my $parent = ref($proto) && $proto;
804 1         7 my %o = (
805             org => "rowwise",
806             cols => undef,
807             width => undef,
808             xvals => undef,
809             @_
810             );
811              
812             # pull out variables
813             my ($org,$cols,$width,$xvals) =
814 1         4 @o{qw(org cols width xvals)}; # hash slice
815              
816 1 50       5 die "xvals must be a listref\n" unless ref($xvals) eq "ARRAY";
817 1 50 33     5 die "need cols > 0" unless defined $cols and $cols >0;
818              
819 1         2 my $rows = @$xvals;
820              
821 1         50 my $self = $class->new(
822             org => $org, width => $width, rows => $rows, cols => $cols);
823 1 50       4 die unless ref($self);
824              
825             # populate all the rows
826 1         3 my $w = $width << 3;
827 1         5 for my $row (0 .. $rows -1) {
828 7         20 $self->setval($row,0,1);
829 7         10 my $x = 1;
830 7         11 for my $col (1 .. $cols -1) {
831 14         38 $x = gf2_mul($w,$x,$xvals->[$row]);
832 14         27 $self->setval($row,$col,$x);
833             }
834             }
835            
836 1         5 $self;
837             }
838              
839             sub print {
840 0     0 0 0 my $self = shift;
841             #die ref $self;
842 0         0 my $rows = $self->ROWS;
843 0         0 my $cols = $self->COLS;
844 0         0 my $w = $self->WIDTH;
845 0         0 $w <<= 1;
846              
847 0         0 for my $row (0.. $rows -1) {
848 0         0 print "| ";
849 0         0 for my $col (0.. $cols -1) {
850 0         0 my $f =" {%0${w}x} ";
851 0         0 printf $f, $self->getval($row,$col);
852             }
853 0         0 print "|\n";
854             }
855             }
856              
857              
858             # Generic routine for copying some matrix elements into a new matrix
859             #
860             # rows => [ $row1, $row2, ... ]
861             # cols => [ $col1, $col2, ... ]
862             # submatrix => [ $first_row, $first_col, $last_row, $last_col ]
863             #
864             # In order to keep this routine fairly simple, the newly-created
865             # matrix will have the same organisation as the original, and we won't
866             # allow for transposition in the same step.
867             sub copy {
868 224     224 1 417 my $self = shift;
869 224         406 my $class = ref($self);
870 224         709 my %o=(
871             rows => undef,
872             cols => undef,
873             submatrix => undef,
874             @_,
875             );
876              
877 224         390 my $rows = $o{rows};
878 224         301 my $cols = $o{cols};
879 224         334 my $submatrix = $o{submatrix};
880              
881 224 100 100     792 if (defined($submatrix)) {
    100          
882 3 50 33     16 if (defined($rows) or defined($cols)) {
883 0         0 carp "Can't specify both submatrix and rows/cols";
884 0         0 return undef;
885             }
886 3         9 my ($row1,$col1,$row2,$col2)=@$submatrix;
887 3 50 33     31 unless (defined($row1) and defined($col1) and
      33        
      33        
888             defined($row2) and defined($col2)) {
889 0         0 carp 'Need submatrx => [$row1,$col1,$row2,$col2]';
890 0         0 return undef;
891             }
892              
893 3 50 33     39 unless ($row1 >=0 and $row1 <= $row2 and $row2 < $self->ROWS and
      33        
      33        
      33        
      33        
894             $col1 >=0 and $col1 <= $col2 and $col2 < $self->COLS) {
895 0         0 carp "submatrix corners out of range";
896 0         0 return undef;
897             }
898 3         24 my $mat=alloc_c($class, $row2 - $row1 + 1, $col2 - $col1 + 1,
899             $self->WIDTH, $self->ORGNUM);
900 3         10 my ($s,$dest)=("",0);
901 3 50       9 if ($self->ORG eq "rowwise") {
902 3         12 for my $r ($row1 .. $row2) {
903 19         39 $s=$self->getvals($r,$col1,$col2 - $col1 + 1);
904 19         49 $mat->setvals($dest,0,$s);
905 19         28 ++$dest;
906             }
907             } else {
908 0         0 for my $c ($col1 .. $col2) {
909 0         0 $s=$self->getvals($row1,$c,$row2 - $row1 + 1);
910 0         0 $mat->setvals(0,$dest,$s);
911 0         0 ++$dest;
912             }
913             }
914 3         13 return $mat;
915              
916             } elsif (defined($rows) or defined($cols)) {
917              
918 217 50 66     933 if (defined($rows) and !ref($rows)) {
919 0         0 carp "rows must be a reference to a list of rows";
920 0         0 return undef;
921             }
922 217 50 66     488 if (defined($cols) and !ref($cols)) {
923 0         0 carp "cols must be a reference to a list of columns";
924 0         0 return undef;
925             }
926              
927 217 100 100     1015 if (defined($rows) and defined($cols)) {
    100 66        
    50 33        
928 2         13 my $mat=alloc_c($class, scalar(@$rows), scalar(@$cols),
929             $self->WIDTH, $self->ORGNUM);
930 2         4 my $dest_row=0;
931 2         3 my $dest_col;
932 2         5 for my $r (@$rows) {
933 11         14 $dest_col=0;
934 11         15 for my $c (@$cols) {
935 70         149 $mat->setval($dest_row,$dest_col++,
936             $self->getval($r,$c));
937             }
938 11         17 ++$dest_row;
939             }
940 2         7 return $mat;
941              
942             } elsif (defined($rows) and $self->ORG eq "rowwise") {
943 212         1000 my $mat=alloc_c($class, scalar(@$rows), $self->COLS,
944             $self->WIDTH, $self->ORGNUM);
945 212         505 my ($s,$dest)=("",0);
946 212         393 for my $r (@$rows) {
947 646         1487 $s=$self->getvals($r,0,$self->COLS);
948 646         1604 $mat->setvals($dest,0,$s);
949 646         1010 ++$dest;
950             }
951 212         867 return $mat;
952              
953             } elsif (defined($cols) and $self->ORG eq "colwise") {
954 0         0 my $mat=alloc_c($class, $self->ROWS, scalar(@$cols),
955             $self->WIDTH, $self->ORGNUM);
956 0         0 my ($s,$dest)=("",0);
957 0         0 for my $c (@$cols) {
958 0         0 $s=$self->getvals(0,$c,$self->ROWS);
959 0         0 $mat->setvals(0,$dest,$s);
960 0         0 ++$dest;
961             }
962 0         0 return $mat;
963              
964             } else {
965             # we've been told to copy some rows or some columns, but the
966             # organisation of the matrix doesn't allow for using quick
967             # getvals. Iterate as we would have done if both rows and cols
968             # were specified, but set whichever of rows/cols wasn't set to
969             # the input matrix's rows/cols.
970 3 50       16 $rows=[ 0 .. $self->ROWS - 1] unless defined($rows);
971 3 50       7 $cols=[ 0 .. $self->COLS - 1] unless defined($cols);
972 3         17 my $mat=alloc_c($class, scalar(@$rows), scalar(@$cols),
973             $self->WIDTH, $self->ORGNUM);
974 3         6 my $dest_row=0;
975 3         12 my $dest_col;
976 3         8 for my $r (@$rows) {
977 21         28 $dest_col=0;
978 21         30 for my $c (@$cols) {
979 138         287 $mat->setval($dest_row,$dest_col++,
980             $self->getval($r,$c));
981             }
982 21         30 ++$dest_row;
983             }
984 3         13 return $mat;
985              
986             }
987              
988             } else {
989             # No submatrix/rows/cols option given, so do a full copy. This is
990             # made easy by not allowing transpose or re-organistaion options
991 4         48 my $mat=alloc_c($class, $self->ROWS, $self->COLS,
992             $self->WIDTH, $self->ORGNUM);
993 4 50       12 return undef unless defined $mat;
994 4         31 my $s=$self->getvals(0,0,$self->ROWS * $self->COLS);
995 4         14 $mat->setvals(0,0,$s);
996 4         20 return $mat;
997             }
998              
999 0         0 die "Unreachable? ORLY?\n";
1000             }
1001              
1002             # provide aliases for all forms of copy except copy rows /and/ cols
1003              
1004             sub copy_rows {
1005 210     210 1 70363 return shift -> copy(rows => [ @_ ]);
1006             }
1007              
1008             sub copy_cols {
1009 1     1 1 5 return shift -> copy(cols => [ @_ ]);
1010             }
1011              
1012             sub submatrix {
1013 1     1 1 15 return shift -> copy(submatrix => [ @_ ]);
1014             }
1015              
1016             # Roll the transpose and reorganise code into one "flip" routine.
1017             # This can save the user one step in some cases.
1018              
1019             sub flip {
1020 6     6 1 11 my $self=shift;
1021 6         16 my %o=( transpose => 0, org => $self->ORG, @_ );
1022              
1023 6         12 my $transpose=$o{"transpose"};
1024 6         10 my $mat;
1025 6         15 my ($fliporg,$neworg);
1026 6         0 my ($r,$c,$s);
1027              
1028 6 100       12 if (($o{"org"} ne $self->ORG)) {
1029 3         5 $neworg=$o{"org"};
1030 3         6 $fliporg=1;
1031             } else {
1032 3         15 $neworg=$self->ORG;
1033 3         7 $fliporg=0;
1034             }
1035              
1036 6 100       15 if ($transpose) {
    100          
1037 3         16 $mat=Math::FastGF2::Matrix->
1038             new(rows => $self->COLS, cols=>$self->ROWS,
1039             width => $self->WIDTH, org => $neworg);
1040 3 50       10 return undef unless defined ($mat);
1041 3 100       7 if ($fliporg) {
1042 1         6 $s=$self->getvals(0,0,$self->COLS * $self->ROWS);
1043 1         3 $mat->setvals(0,0,$s);
1044             } else {
1045 2         16 for $r (0..$self->ROWS - 1) {
1046 10         22 for $c (0..$self->COLS - 1) {
1047 40         89 $mat->setval($c,$r,$self->getval($r,$c));
1048             }
1049             }
1050             }
1051 3         15 return $mat;
1052              
1053             } elsif ($fliporg) {
1054 2         12 $mat=Math::FastGF2::Matrix->
1055             new(rows => $self->ROWS, cols=> $self->COLS,
1056             width => $self->WIDTH, org => $neworg);
1057 2 50       14 return undef unless defined ($mat);
1058 2         9 for $r (0..$self->ROWS - 1) {
1059 10         22 for $c (0..$self->COLS - 1) {
1060 40         89 $mat->setval($r,$c,$self->getval($r,$c));
1061             }
1062             }
1063 2         12 return $mat;
1064              
1065             } else {
1066             # no change, but return a new copy of self to be in line with all
1067             # other input cases.
1068 1         3 return $self->copy;
1069             }
1070 0         0 die "Unreachable? ORLY?\n";
1071             }
1072              
1073              
1074             sub transpose {
1075 1     1 1 49 return shift -> flip(transpose => 1);
1076             }
1077              
1078             sub reorganise {
1079 1     1 1 34 my $self=shift;
1080              
1081 1 50       5 if ($self->ORG eq "rowwise") {
1082 1         4 return $self->flip(org => "colwise");
1083             } else {
1084 0           return $self->flip(org => "rowwise");
1085             }
1086             }
1087              
1088              
1089             1;
1090              
1091             __END__