File Coverage

blib/lib/Math/ModInt.pm
Criterion Covered Total %
statement 149 149 100.0
branch 60 60 100.0
condition 36 36 100.0
subroutine 45 45 100.0
pod 19 19 100.0
total 309 309 100.0


line stmt bran cond sub pod time code
1             # Copyright (c) 2009-2019 Martin Becker, Blaubeuren.
2             # This package is free software; you can distribute it and/or modify it
3             # under the terms of the Artistic License 2.0 (see LICENSE file).
4              
5             package Math::ModInt;
6              
7 11     11   369859 use 5.006;
  11         84  
8 11     11   53 use strict;
  11         17  
  11         228  
9 11     11   47 use warnings;
  11         19  
  11         313  
10             use Math::ModInt::Event
11 11     11   4719 qw(UsageError Nonexistent LoadingFailure UndefinedResult DifferentModuli);
  11         25  
  11         1814  
12              
13             sub _no_op {
14 22     22   50 my ($op) = @_;
15 22     2   3380 return $op => sub { UsageError->raise('undefined operation', $op) };
  2         187  
16             }
17              
18             use overload (
19 11         97 'neg' => '_NEG',
20             '+' => \&_oadd,
21             '-' => \&_osub,
22             '*' => \&_omul,
23             '/' => \&_odiv,
24             '**' => \&_opow,
25             '==' => \&_oeq,
26             '!=' => \&_oneq,
27             '!' => 'is_zero',
28             'bool' => 'is_not_zero',
29             '0+' => 'residue',
30             '""' => 'as_string',
31             'fallback' => undef,
32             _no_op('abs'),
33             _no_op('int'),
34 11     11   6517 );
  11         10533  
35              
36             # ----- class data -----
37              
38             BEGIN {
39 11     11   4340 require Exporter;
40 11         165 our @ISA = qw(Exporter);
41 11         42 our @EXPORT_OK = qw(mod divmod qmod);
42 11         17 our @CARP_NOT = qw(Math::ModInt::ChineseRemainder);
43 11         1011 our $VERSION = '0.012';
44             }
45              
46             sub _max_modulus_perl {
47 11     11   24 my $limit = 32767;
48 11         46 foreach my $bits (16..96) {
49 429         466 my $probe = $limit + $limit + 1;
50 429 100       835 last if 0.5 != $probe / 2 - ($probe ^ 1) / 2;
51 418         498 $limit = $probe;
52             }
53 11         988 return int sqrt $limit;
54             }
55              
56 11     11   99 use constant _MAX_MODULUS_PERL => _max_modulus_perl();
  11         16  
  11         27  
57              
58             my $undefined = bless []; # singleton
59             my %loaded = (); # collects loaded modules
60              
61             # ----- private subroutines -----
62              
63             sub _is_integer {
64 324     324   421 my ($arg) = @_;
65 324         388 local $@;
66             return
67 324         527 eval {
68 11     11   64 use warnings FATAL => 'all';
  11         25  
  11         15515  
69 324 100 100     1557 ref $arg?
70             $arg->isa('Math::BigInt')
71             :
72             $arg == int($arg) && abs($arg) <= ~0
73             };
74             }
75              
76             sub _incompatible {
77 25     25   33 my ($this, $that) = @_;
78 25 100 100     43 if ($this->is_defined && $that->is_defined) {
79 17         52 DifferentModuli->raise($this, $that);
80             }
81 21         52 return $undefined;
82             }
83              
84             sub _best_class {
85 127     127   188 my ($modulus) = @_;
86 127 100       207 if (_is_integer($modulus)) {
87 122 100       263 return 'Math::ModInt::BigInt' if $modulus > _MAX_MODULUS_PERL;
88 111 100       535 return 'Math::ModInt::Perl' if $modulus > 3;
89 32 100       79 return 'Math::ModInt::GF3' if $modulus == 3;
90 25 100       51 return 'Math::ModInt::GF2' if $modulus == 2;
91 15 100       35 return 'Math::ModInt::Trivial' if $modulus == 1;
92             }
93 14         42 UsageError->raise('positive integer modulus expected');
94             }
95              
96             sub _load {
97 114     114   1149 my ($class) = @_;
98 114 100       169 do {
99 114         140 local $@;
100 114   100     1443 $loaded{$class} ||= eval "require $class"
101             }
102             or LoadingFailure->raise($class);
103 113         215 return $class;
104             }
105              
106             sub _oadd {
107 120     120   3773 my ($this, $that) = @_;
108 120 100 100     530 if (!ref $that || !$that->isa(__PACKAGE__)) {
    100          
109 9         21 $that = $this->_NEW($that);
110             }
111             elsif ($this->modulus != $that->modulus) {
112 13         28 return _incompatible($this, $that);
113             }
114 107         1144 return $this->_ADD($that);
115             }
116              
117             sub _osub {
118 92     92   1344 my ($this, $that, $reversed) = @_;
119 92 100 100     384 if (!ref $that || !$that->isa(__PACKAGE__)) {
    100          
120 6         11 $that = $this->_NEW($that);
121             }
122             elsif ($this->modulus != $that->modulus) {
123 4         7 return _incompatible($this, $that);
124             }
125 88 100       731 return $reversed? $that->_SUB($this): $this->_SUB($that);
126             }
127              
128             sub _omul {
129 131     131   3331 my ($this, $that) = @_;
130 131 100 100     544 if (!ref $that || !$that->isa(__PACKAGE__)) {
    100          
131 6         18 $that = $this->_NEW($that);
132             }
133             elsif ($this->modulus != $that->modulus) {
134 4         9 return _incompatible($this, $that);
135             }
136 127         1528 return $this->_MUL($that);
137             }
138              
139             sub _odiv {
140 93     93   1140 my ($this, $that, $reversed) = @_;
141 93 100 100     387 if (!ref $that || !$that->isa(__PACKAGE__)) {
    100          
142 6         12 $that = $this->_NEW($that);
143             }
144             elsif ($this->modulus != $that->modulus) {
145 4         8 return _incompatible($this, $that);
146             }
147 89 100       774 return $reversed? $that->_DIV($this): $this->_DIV($that);
148             }
149              
150             sub _opow {
151 199     199   4942 my ($this, $exp, $reversed) = @_;
152             # exponent should be in perl integer range or be a big int
153 199 100 100     456 if ($reversed || !_is_integer($exp)) {
154 13         55 UsageError->raise('integer exponent expected');
155             }
156 186         402 return $this->_POW($exp);
157             }
158              
159             sub _oeq {
160 675     675   2659 my ($this, $that) = @_;
161             # note that comparing with $undefined is illegal
162 675 100 100     2197 if (!ref $that || !$that->isa(__PACKAGE__)) {
163 17         44 return $this->residue == $that % $this->modulus;
164             }
165             return
166 658   100     1112 $this->residue == $that->residue &&
167             $this->modulus == $that->modulus;
168             }
169              
170             sub _oneq {
171 20     20   1877 my ($this, $that) = @_;
172             # note that comparing with $undefined is illegal
173 20 100 100     101 if (!ref $that || !$that->isa(__PACKAGE__)) {
174 8         28 return $this->residue != $that % $this->modulus;
175             }
176             return
177 12   100     28 $this->residue != $that->residue ||
178             $this->modulus != $that->modulus;
179             }
180              
181             # ----- fallback for implementation interface method -----
182              
183             sub _NEW2 {
184 3     3   6 my ($this, $int, $modulus) = @_;
185 3         4 my $that;
186 3 100       7 if (ref $this) {
187 1         3 $that = $this->_NEW($int);
188 1         2 $modulus = $this->modulus;
189             }
190             else {
191 2         5 $that = $this->_NEW($int, $modulus);
192             }
193 3         7 my $quot = ($int - $that->residue) / $modulus;
194 3         14 return ($quot, $that);
195             }
196              
197             # ----- public methods -----
198              
199             # constructors
200              
201             sub mod {
202 121     121 1 103146 my ($int, $modulus) = @_;
203 121         237 my $class = _load(_best_class($modulus));
204 106         284 return $class->_NEW($int, $modulus);
205             }
206              
207             sub divmod {
208 6     6 1 610 my ($int, $modulus) = @_;
209 6         20 my $class = _load(_best_class($modulus));
210 6         22 return $class->_NEW2($int, $modulus);
211             }
212              
213             sub qmod {
214 1     1 1 2346 my ($rat, $modulus) = @_;
215 1         3 my $class = _load(_best_class($modulus));
216 1         5 my $num = $class->_NEW($rat->numerator, $modulus);
217 1         212 my $den = $num->_NEW($rat->denominator);
218 1         190 return $num / $den;
219             }
220              
221             sub new {
222 255     255 1 8945 my ($this, $int, $modulus) = @_;
223 255 100       621 return $this->_NEW($int) if ref $this;
224 19         33 return mod($int, $modulus);
225             }
226              
227             sub new2 {
228 4     4 1 1292 my ($this, $int, $modulus) = @_;
229 4 100       19 return $this->_NEW2($int) if ref $this;
230 1         4 return divmod($int, $modulus);
231             }
232              
233             sub undefined {
234 61     61 1 442 UndefinedResult->raise;
235 61         164 return $undefined;
236             }
237              
238             # accessors
239              
240             sub residue {
241 3     3 1 112 Nonexistent->raise('undefined residue');
242             }
243              
244             sub modulus {
245 17 100   17 1 131 return 0 if __PACKAGE__ eq (caller)[0]; # special case for _oadd etc.
246 1         5 Nonexistent->raise('undefined modulus');
247             }
248              
249             sub signed_residue {
250 25     25 1 1652 my ($this) = @_;
251 25         54 my $r = $this->residue;
252 24         44 my $m = $this->modulus;
253 24         42 my $n = $m - $r;
254 24 100       672 return $n <= $r? -$n: $r;
255             }
256              
257             sub centered_residue {
258 21     21 1 1477 my ($this) = @_;
259 21         44 my $r = $this->residue;
260 20         38 my $m = $this->modulus;
261 20         53 my $n = $m - $r;
262 20 100       767 return $n < $r? -$n: $r;
263             }
264              
265             sub is_defined {
266 701     701 1 22816 my ($this) = @_;
267 701         1373 return ref $undefined ne ref $this;
268             }
269              
270             sub is_undefined {
271 150     150 1 547 my ($this) = @_;
272 150         380 return ref $undefined eq ref $this;
273             }
274              
275             sub is_zero {
276 76     76 1 667 my ($this) = @_;
277 76         124 return 0 == $this->residue;
278             }
279              
280             sub is_not_zero {
281 6     6 1 369 my ($this) = @_;
282 6         20 return 0 != $this->residue;
283             }
284              
285             sub as_string {
286 22     22 1 1515 my ($this) = @_;
287 22 100       48 my ($r, $mod) =
288             $this->is_defined? ($this->residue, $this->modulus): qw(? ?);
289 22         613 return "mod($r, $mod)";
290             }
291              
292             # operators
293              
294 29     29 1 751 sub inverse { $_[0]->_INV }
295              
296             BEGIN {
297 11     11   39 foreach my $method (qw(
298             _NEW _NEG _INV _ADD _SUB _MUL _DIV _POW
299             )) {
300 11     11   93 no strict 'refs';
  11         18  
  11         585  
301 88     36   257 *{$method} = sub { $undefined };
  88         1176  
  36         77  
302             }
303             }
304              
305             # miscellaneous
306              
307 1     1 1 9 sub optimize_time { $_[0] }
308 1     1 1 2 sub optimize_space { $_[0] }
309 1     1 1 3 sub optimize_default { $_[0] }
310              
311             1;
312              
313             __END__