File Coverage

lib/Math/FastGF2/Matrix.pm
Criterion Covered Total %
statement 325 562 57.8
branch 116 228 50.8
condition 65 181 35.9
subroutine 31 35 88.5
pod 13 27 48.1
total 550 1033 53.2


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