File Coverage

blib/lib/Math/ModInt/Perl.pm
Criterion Covered Total %
statement 118 118 100.0
branch 42 42 100.0
condition 11 11 100.0
subroutine 25 25 100.0
pod 5 5 100.0
total 201 201 100.0


line stmt bran cond sub pod time code
1             package Math::ModInt::Perl;
2              
3 7     7   890 use 5.006;
  7         26  
4 7     7   47 use strict;
  7         14  
  7         178  
5 7     7   42 use warnings;
  7         12  
  7         235  
6 7     7   53 use Carp qw(croak);
  7         72  
  7         486  
7              
8             # ----- object definition -----
9              
10             # Math::ModInt::Perl=ARRAY(...)
11              
12             # .......... index .......... # .......... value ..........
13 7     7   58 use constant F_RESIDUE => 0; # residue r, 0 <= r < m
  7         15  
  7         518  
14 7     7   47 use constant F_MODULUS => 1; # modulus m
  7         18  
  7         402  
15 7     7   49 use constant NFIELDS => 2;
  7         31  
  7         421  
16              
17             # ----- class data -----
18              
19 7     7   56 use constant _OPT_THRESHOLD => 256;
  7         15  
  7         358  
20 7     7   39 use constant _OPT_LIMIT => 32768;
  7         21  
  7         643  
21              
22             BEGIN {
23 7     7   52 require Math::ModInt;
24 7         154 our @ISA = qw(Math::ModInt);
25 7         8017 our $VERSION = '0.013';
26             }
27              
28             my %inverses = ();
29              
30             # ----- private methods -----
31              
32             # special case of _NEW, not using modulo, no class method
33             sub _make {
34 171     171   286 my ($this, $r) = @_;
35 171         574 return bless [$r, $this->[F_MODULUS]], ref $this;
36             }
37              
38             sub _mod_inv {
39 86     86   154 my ($r, $mod) = @_;
40 86         137 my $inv = $inverses{$mod};
41 86 100 100     197 if ($inv) {
    100          
42 70         99 my $i = $inv->[$r];
43 70 100       157 return $i if defined $i;
44             }
45             elsif (!defined($inv) && $mod <= _OPT_THRESHOLD) {
46 4         11 $inv = $inverses{$mod} = [0];
47             }
48 24         49 my ($d, $dd, $i, $ii) = ($mod, $r, 0, 1);
49 24         59 while ($dd) {
50 51         792 my $f = int($d / $dd);
51 51         760 ($d, $dd) = ($dd, $d - $f * $dd);
52 51         823 ($i, $ii) = ($ii, $i - $f * $ii);
53             }
54 24 100       252 if (1 != $d) {
    100          
55 4         6 $i = 0;
56             }
57             elsif ($i < 0) {
58 5         7 $i += $mod;
59             }
60 24 100       315 if ($inv) {
61 12         25 $inv->[$r] = $i;
62 12 100       55 if ($i) {
63 9         46 $inv->[$i] = $r;
64             }
65             }
66 24         65 return $i;
67             }
68              
69             sub _NEG {
70 9     9   49 my ($this) = @_;
71 9         12 my ($r, $mod) = @{$this};
  9         14  
72 9 100       21 return $this if !$r;
73 7         15 return $this->_make($mod-$r);
74             }
75              
76             sub _ADD {
77 53     53   79 my ($this, $that) = @_;
78 53         84 my $r = $this->[F_RESIDUE] + $that->[F_RESIDUE];
79 53         68 my $mod = $this->[F_MODULUS];
80 53 100       98 if ($mod <= $r) {
81 20         26 $r -= $mod;
82             }
83 53         84 return $this->_make($r);
84             }
85              
86             sub _SUB {
87 42     42   57 my ($this, $that) = @_;
88 42         67 my $r = $this->[F_RESIDUE] - $that->[F_RESIDUE];
89 42         51 my $mod = $this->[F_MODULUS];
90 42 100       74 if ($r < 0) {
91 17         25 $r += $mod;
92             }
93 42         78 return $this->_make($r);
94             }
95              
96             sub _MUL {
97 65     65   111 my ($this, $that) = @_;
98 65         118 return $this->_NEW($this->[F_RESIDUE]*$that->[F_RESIDUE]);
99             }
100              
101             sub _DIV {
102 43     43   69 my ($this, $that) = @_;
103 43         57 my $mod = $this->[F_MODULUS];
104 43         66 my $i = _mod_inv($that->[F_RESIDUE], $mod);
105 43 100       116 return $this->undefined if !$i;
106 30         79 return $this->_NEW($this->[F_RESIDUE]*$i);
107             }
108              
109             sub _POW {
110 81     81   124 my ($this, $exp) = @_;
111 81         113 my ($r, $mod) = @{$this};
  81         127  
112 81 100       156 return $this->_make(1) if !$exp;
113 71 100       154 if ($exp < 0) {
    100          
114 27         54 $r = _mod_inv($r, $mod);
115 27 100       58 return $this->undefined if !$r;
116 21         33 $exp = -$exp;
117             }
118             elsif (!$r) {
119 6         13 return $this;
120             }
121 59         149 my $p = 1;
122 59         123 while ($exp) {
123 201 100       1625 if (1 & $exp) {
124 136         188 $p = $p*$r % $mod;
125             }
126 201 100       1974 $exp >>= 1 and $r = $r*$r % $mod;
127             }
128 59         111 return $this->_make($p);
129             }
130              
131             sub _INV {
132 16     16   30 my ($this) = @_;
133 16         21 my ($r, $mod) = @{$this};
  16         67  
134 16         41 my $i = _mod_inv($r, $mod);
135 16 100       45 return $this->undefined if !$i;
136 11         22 return $this->_NEW($i);
137             }
138              
139             sub _NEW {
140 293     293   693 my ($this, $residue, $modulus) = @_;
141 293         434 my $class = ref $this;
142 293 100       451 if ($class) {
143 214         332 $modulus = $this->[F_MODULUS];
144             }
145             else {
146 79         131 $class = $this;
147             }
148 293         871 return bless [$residue % $modulus, $modulus], $class;
149             }
150              
151             # ----- public methods -----
152              
153             sub residue {
154 824     824 1 3818 my ($this) = @_;
155 824         1841 return $this->[F_RESIDUE];
156             }
157              
158             sub modulus {
159 1257     1257 1 3097 my ($this) = @_;
160 1257         2974 return $this->[F_MODULUS];
161             }
162              
163             sub optimize_time {
164 4     4 1 9 my ($this) = @_;
165 4         7 my $mod = $this->modulus;
166 4 100       9 if ($mod <= _OPT_LIMIT) {
167 3   100     13 $inverses{$mod} ||= [0];
168             }
169 4         9 return $this;
170             }
171              
172             sub optimize_space {
173 2     2 1 4 my ($this) = @_;
174 2         5 $inverses{$this->modulus} = 0;
175 2         6 return $this;
176             }
177              
178             sub optimize_default {
179 5     5 1 17 my ($this) = @_;
180 5         12 my $mod = $this->modulus;
181 5 100 100     25 if (exists $inverses{$mod} and $mod > _OPT_THRESHOLD || !$inverses{$mod}) {
      100        
182 2         4 delete $inverses{$mod};
183             }
184 5         11 return $this;
185             }
186              
187             1;
188              
189             __END__