File Coverage

blib/lib/Math/GF/Extension.pm
Criterion Covered Total %
statement 68 74 91.8
branch 16 26 61.5
condition 1 3 33.3
subroutine 20 20 100.0
pod 12 12 100.0
total 117 135 86.6


line stmt bran cond sub pod time code
1             package Math::GF::Extension;
2 3     3   1521 use strict;
  3         6  
  3         80  
3 3     3   12 use warnings;
  3         4  
  3         127  
4             { our $VERSION = '0.004'; }
5              
6 3     3   12 use Scalar::Util qw< blessed >;
  3         5  
  3         238  
7             use overload
8 3         19 '/' => 'divided_by',
9             '==' => 'equal_to',
10             'eq' => 'equal_to',
11             '-' => 'minus',
12             '!=' => 'not_equal_to',
13             '+' => 'plus',
14             '""' => 'stringify',
15             '*' => 'times',
16 3     3   16 '**' => 'to_power';
  3         5  
17              
18 3     3   492 use Ouch;
  3         6  
  3         17  
19 3     3   215 use Moo;
  3         6  
  3         17  
20              
21             has field => (
22             is => 'ro',
23             required => 1,
24             isa => sub {
25             my $F = shift;
26             (blessed($F) && $F->isa('Math::GF'))
27             or ouch 500, 'field is not a valid Math::GF instance';
28             return 1;
29             },
30             );
31              
32             has p => (
33             is => 'ro',
34             init_arg => undef,
35             lazy => 1,
36             default => sub { shift->field->p },
37             );
38              
39             has n => (
40             is => 'ro',
41             init_arg => undef,
42             lazy => 1,
43             default => sub { shift->field->n },
44             );
45              
46             has sum_table => (
47             is => 'ro',
48             init_arg => undef,
49             lazy => 1,
50             default => sub { shift->field->sum_table },
51             );
52              
53             has prod_table => (
54             is => 'ro',
55             init_arg => undef,
56             lazy => 1,
57             default => sub { shift->field->prod_table },
58             );
59              
60             # the identifier of this object
61             has v => (
62             is => 'ro',
63             default => 0,
64             );
65              
66             # the multiplicative inverse, if exists, undef otherwise
67             has i => (
68             is => 'ro',
69             lazy => 1,
70             builder => 'BUILD_multiplicative_inverse',
71             );
72              
73             has o => (
74             is => 'ro',
75             lazy => 1,
76             builder => 'BUILD_additive_inverse',
77             );
78              
79             sub assert_compatibility {
80 39     39 1 66 my ($self, $other) = @_;
81 39 50 33     229 (blessed($other) && $other->isa('Math::GF::Extension'))
82             || ouch 500, 'one of the ops is not a Math::GF::Extension object';
83 39         109 my $order = $self->field->order;
84 39 50       85 $order == $other->field->order
85             || ouch 500, 'the two operands are not in the same field';
86 39         52 return $order;
87             }
88              
89             sub BUILD_multiplicative_inverse {
90 2     2 1 15 my $self = shift;
91 2 50       6 my $v = $self->v or ouch 500, 'no inverse for 0';
92 2         29 my $pt = $self->prod_table;
93 2         9 for my $i (0 .. $v) {
94 5 100       14 return $i if $pt->[$v][$i] == 1;
95             }
96 1         3 for my $j (($v + 1) .. $#$pt) {
97 1 50       20 return $j if $pt->[$j][$v] == 1;
98             }
99 0         0 my ($p, $n) = ($self->p, $self->n);
100 0         0 ouch 500, "no inverse for $v in GF_${p}_$n"; # never happens
101             } ## end sub BUILD_multiplicative_inverse
102              
103             sub BUILD_additive_inverse {
104 3     3 1 26 my $self = shift;
105 3 50       10 my $v = $self->v or return 0;
106 3         44 my $pt = $self->sum_table;
107 3         12 for my $i (0 .. $v) {
108 9 100       29 return $i if $pt->[$v][$i] == 0;
109             }
110 0         0 for my $j (($v + 1) .. $#$pt) {
111 0 0       0 return $j if $pt->[$j][$v] == 0;
112             }
113 0         0 my ($p, $n) = ($self->p, $self->n);
114 0         0 ouch 500, "no opposite for $v in GF_${p}_$n"; # never happens
115             }
116              
117             sub _prod {
118 9     9   41 my ($self, $x, $y) = @_;
119 9 100       159 return $x > $y
120             ? $self->prod_table->[$x][$y]
121             : $self->prod_table->[$y][$x];
122             }
123              
124             sub _sum {
125 4     4   8 my ($self, $x, $y) = @_;
126 4 100       66 return $x > $y
127             ? $self->sum_table->[$x][$y]
128             : $self->sum_table->[$y][$x];
129             }
130              
131             sub divided_by {
132 4     4 1 1146 my ($self, $other, $swap) = @_;
133 4         11 $self->assert_compatibility($other);
134 4 50       8 ($self, $other) = ($other, $self) if $swap; # never happens...
135 4         92 return $self->new(
136             field => $self->field,
137             v => $self->_prod($self->v, $other->i),
138             );
139             } ## end sub divided_by
140              
141             sub equal_to {
142 26     26 1 5111 my ($self, $other, $swap) = @_;
143 26         62 $self->assert_compatibility($other);
144 26         272 return $self->v == $other->v;
145             }
146              
147             sub inv {
148 2     2 1 584 my $self = shift;
149 2         43 return $self->new(
150             field => $self->field,
151             v => $self->i,
152             i => $self->v,
153             );
154             }
155              
156             sub minus { # FIXME
157 2     2 1 600 my ($self, $other, $swap) = @_;
158 2         6 $self->assert_compatibility($other);
159 2         55 return $self->new(
160             field => $self->field,
161             v => $self->_sum($self->v, $other->o),
162             );
163             } ## end sub minus
164              
165             sub not_equal_to {
166 1     1 1 329 return !shift->equal_to(@_);
167             }
168              
169             sub plus {
170 2     2 1 298 my ($self, $other, $swap) = @_;
171 2         5 my $n = $self->assert_compatibility($other);
172 2         6 return $self->new(
173             field => $self->field,
174             v => $self->_sum($self->v, $other->v),
175             );
176             } ## end sub plus
177              
178             sub stringify {
179 2     2 1 15 return shift->v;
180             }
181              
182             sub times {
183 5     5 1 586 my ($self, $other, $swap) = @_;
184 5         11 $self->assert_compatibility($other);
185 5         15 return $self->new(
186             field => $self->field,
187             v => $self->_prod($self->v, $other->v),
188             );
189             } ## end sub times
190              
191             sub to_power {
192 1     1 1 2 my ($self, $exp, $swap) = @_;
193 1 50       4 ouch 500, 'cannot elevate' if $swap;
194 1         6 my $x = $self->field->multiplicative_neutral;
195 1         11 my $zero = $self->field->additive_neutral;
196 1         9 while ($exp > 0) {
197 3         5 $x = $x * $self;
198 3 50       23 last if $x == $zero;
199 3         7 $exp--;
200             }
201 1         5 return $x;
202             } ## end sub to_power
203              
204             1;