File Coverage

blib/lib/Math/ModInt.pm
Criterion Covered Total %
statement 150 150 100.0
branch 60 60 100.0
condition 34 36 94.4
subroutine 45 45 100.0
pod 19 19 100.0
total 308 310 99.3


line stmt bran cond sub pod time code
1             # Copyright (c) 2009-2015 Martin Becker. All rights reserved.
2             # This package is free software; you can redistribute it and/or modify it
3             # under the same terms as Perl itself.
4             #
5             # $Id: ModInt.pm 60 2015-05-18 08:47:12Z demetri $
6              
7             package Math::ModInt;
8              
9 11     11   261716 use 5.006;
  11         31  
  11         393  
10 11     11   58 use strict;
  11         15  
  11         381  
11 11     11   43 use warnings;
  11         18  
  11         391  
12             use Math::ModInt::Event
13 11     11   4572 qw(UsageError Nonexistent LoadingFailure UndefinedResult DifferentModuli);
  11         24  
  11         2363  
14              
15             sub _no_op {
16 22     22   32 my ($op) = @_;
17 22     2   132 return $op => sub { UsageError->raise('undefined operation', $op) };
  2         495  
18             }
19              
20             use overload (
21 11         107 'neg' => '_NEG',
22             '+' => \&_oadd,
23             '-' => \&_osub,
24             '*' => \&_omul,
25             '/' => \&_odiv,
26             '**' => \&_opow,
27             '==' => \&_oeq,
28             '!=' => \&_oneq,
29             '!' => 'is_zero',
30             'bool' => 'is_not_zero',
31             '0+' => 'residue',
32             '""' => 'as_string',
33             'fallback' => undef,
34             _no_op('abs'),
35             _no_op('int'),
36 11     11   6753 );
  11         5800  
37              
38             # ----- class data -----
39              
40             BEGIN {
41 11     11   2472 require Exporter;
42 11         112 our @ISA = qw(Exporter);
43 11         22 our @EXPORT_OK = qw(mod divmod qmod);
44 11         20 our @CARP_NOT = qw(Math::ModInt::ChineseRemainder);
45 11         1030 our $VERSION = '0.011';
46             }
47              
48             sub _max_modulus_perl {
49 11     11   15 my $limit = 32767;
50 11         30 foreach my $bits (16..96) {
51 429         341 my $probe = $limit + $limit + 1;
52 429 100       730 last if 0.5 != $probe / 2 - ($probe ^ 1) / 2;
53 418         353 $limit = $probe;
54             }
55 11         1121 return int sqrt $limit;
56             }
57              
58 11     11   49 use constant _MAX_MODULUS_PERL => _max_modulus_perl();
  11         30  
  11         22  
59              
60             my $undefined = bless []; # singleton
61             my %loaded = (); # collects loaded modules
62              
63             # ----- private subroutines -----
64              
65             sub _is_integer {
66 324     324   311 my ($arg) = @_;
67 324         312 local $@;
68             return
69 324         456 eval {
70 11     11   58 use warnings FATAL => 'all';
  11         13  
  11         14998  
71 324 100 100     2280 ref $arg?
72             $arg->isa('Math::BigInt')
73             :
74             $arg == int($arg) && abs($arg) <= ~0
75             };
76             }
77              
78             sub _incompatible {
79 25     25   34 my ($this, $that) = @_;
80 25 100 100     51 if ($this->is_defined && $that->is_defined) {
81 17         66 DifferentModuli->raise($this, $that);
82             }
83 21         76 return $undefined;
84             }
85              
86             sub _best_class {
87 127     127   143 my ($modulus) = @_;
88 127 100       232 if (_is_integer($modulus)) {
89 122 100       269 return 'Math::ModInt::BigInt' if $modulus > _MAX_MODULUS_PERL;
90 111 100       595 return 'Math::ModInt::Perl' if $modulus > 3;
91 32 100       88 return 'Math::ModInt::GF3' if $modulus == 3;
92 25 100       78 return 'Math::ModInt::GF2' if $modulus == 2;
93 15 100       260 return 'Math::ModInt::Trivial' if $modulus == 1;
94             }
95 14         63 UsageError->raise('positive integer modulus expected');
96             }
97              
98             sub _load {
99 114     114   949 my ($class) = @_;
100 114 100       119 do {
101 114         120 local $@;
102 114   100     2170 $loaded{$class} ||= eval "require $class"
103             }
104             or LoadingFailure->raise($class);
105 113         249 return $class;
106             }
107              
108             sub _oadd {
109 120     120   3684 my ($this, $that) = @_;
110 120 100 100     738 if (!ref $that || !$that->isa(__PACKAGE__)) {
    100          
111 9         21 $that = $this->_NEW($that);
112             }
113             elsif ($this->modulus != $that->modulus) {
114 13         29 return _incompatible($this, $that);
115             }
116 107         907 return $this->_ADD($that);
117             }
118              
119             sub _osub {
120 92     92   2333 my ($this, $that, $reversed) = @_;
121 92 100 100     512 if (!ref $that || !$that->isa(__PACKAGE__)) {
    100          
122 6         17 $that = $this->_NEW($that);
123             }
124             elsif ($this->modulus != $that->modulus) {
125 4         8 return _incompatible($this, $that);
126             }
127 88 100       693 return $reversed? $that->_SUB($this): $this->_SUB($that);
128             }
129              
130             sub _omul {
131 131     131   2978 my ($this, $that) = @_;
132 131 100 100     733 if (!ref $that || !$that->isa(__PACKAGE__)) {
    100          
133 6         10 $that = $this->_NEW($that);
134             }
135             elsif ($this->modulus != $that->modulus) {
136 4         9 return _incompatible($this, $that);
137             }
138 127         1105 return $this->_MUL($that);
139             }
140              
141             sub _odiv {
142 93     93   1612 my ($this, $that, $reversed) = @_;
143 93 100 100     510 if (!ref $that || !$that->isa(__PACKAGE__)) {
    100          
144 6         23 $that = $this->_NEW($that);
145             }
146             elsif ($this->modulus != $that->modulus) {
147 4         8 return _incompatible($this, $that);
148             }
149 89 100       718 return $reversed? $that->_DIV($this): $this->_DIV($that);
150             }
151              
152             sub _opow {
153 199     199   9668 my ($this, $exp, $reversed) = @_;
154             # exponent should be in perl integer range or be a big int
155 199 100 100     551 if ($reversed || !_is_integer($exp)) {
156 13         87 UsageError->raise('integer exponent expected');
157             }
158 186         428 return $this->_POW($exp);
159             }
160              
161             sub _oeq {
162 674     674   3145 my ($this, $that) = @_;
163             # note that comparing with $undefined is illegal
164 674 100 66     2534 if (!ref $that || !$that->isa(__PACKAGE__)) {
165 16         39 return $this->residue == $that % $this->modulus;
166             }
167             return
168 658   100     1207 $this->residue == $that->residue &&
169             $this->modulus == $that->modulus;
170             }
171              
172             sub _oneq {
173 21     21   1622 my ($this, $that) = @_;
174             # note that comparing with $undefined is illegal
175 21 100 66     115 if (!ref $that || !$that->isa(__PACKAGE__)) {
176 9         21 return $this->residue != $that % $this->modulus;
177             }
178             return
179 12   100     24 $this->residue != $that->residue ||
180             $this->modulus != $that->modulus;
181             }
182              
183             # ----- fallback for implementation interface method -----
184              
185             sub _NEW2 {
186 3     3   4 my ($this, $int, $modulus) = @_;
187 3         4 my $that;
188 3 100       5 if (ref $this) {
189 1         3 $that = $this->_NEW($int);
190 1         3 $modulus = $this->modulus;
191             }
192             else {
193 2         7 $that = $this->_NEW($int, $modulus);
194             }
195 3         13 my $quot = ($int - $that->residue) / $modulus;
196 3         10 return ($quot, $that);
197             }
198              
199             # ----- public methods -----
200              
201             # constructors
202              
203             sub mod {
204 121     121 1 113373 my ($int, $modulus) = @_;
205 121         260 my $class = _load(_best_class($modulus));
206 106         435 return $class->_NEW($int, $modulus);
207             }
208              
209             sub divmod {
210 6     6 1 455 my ($int, $modulus) = @_;
211 6         18 my $class = _load(_best_class($modulus));
212 6         22 return $class->_NEW2($int, $modulus);
213             }
214              
215             sub qmod {
216 1     1 1 1712 my ($rat, $modulus) = @_;
217 1         4 my $class = _load(_best_class($modulus));
218 1         5 my $num = $class->_NEW($rat->numerator, $modulus);
219 1         150 my $den = $num->_NEW($rat->denominator);
220 1         148 return $num / $den;
221             }
222              
223             sub new {
224 255     255 1 10632 my ($this, $int, $modulus) = @_;
225 255 100       824 return $this->_NEW($int) if ref $this;
226 19         42 return mod($int, $modulus);
227             }
228              
229             sub new2 {
230 3     3 1 1370 my ($this, $int, $modulus) = @_;
231 3 100       20 return $this->_NEW2($int) if ref $this;
232 1         3 return divmod($int, $modulus);
233             }
234              
235             sub undefined {
236 61     61 1 754 UndefinedResult->raise;
237 61         154 return $undefined;
238             }
239              
240             # accessors
241              
242             sub residue {
243 3     3 1 223 Nonexistent->raise('undefined residue');
244             }
245              
246             sub modulus {
247 17 100   17 1 278 return 0 if __PACKAGE__ eq (caller)[0]; # special case for _oadd etc.
248 1         7 Nonexistent->raise('undefined modulus');
249             }
250              
251             sub signed_residue {
252 27     27 1 2154 my ($this) = @_;
253 27         69 my $r = $this->residue;
254 26         62 my $m = $this->modulus;
255 26         45 my $n = $m - $r;
256 26 100       756 return $n <= $r? -$n: $r;
257             }
258              
259             sub centered_residue {
260 25     25 1 2367 my ($this) = @_;
261 25         62 my $r = $this->residue;
262 24         94 my $m = $this->modulus;
263 24         50 my $n = $m - $r;
264 24 100       681 return $n < $r? -$n: $r;
265             }
266              
267             sub is_defined {
268 692     692 1 19706 my ($this) = @_;
269 692         1569 return ref $undefined ne ref $this;
270             }
271              
272             sub is_undefined {
273 150     150 1 754 my ($this) = @_;
274 150         528 return ref $undefined eq ref $this;
275             }
276              
277             sub is_zero {
278 76     76 1 677 my ($this) = @_;
279 76         166 return 0 == $this->residue;
280             }
281              
282             sub is_not_zero {
283 6     6 1 764 my ($this) = @_;
284 6         18 return 0 != $this->residue;
285             }
286              
287             sub as_string {
288 13     13 1 896 my ($this) = @_;
289 13 100       43 my ($r, $mod) =
290             $this->is_defined? ($this->residue, $this->modulus): qw(? ?);
291 13         819 return "mod($r, $mod)";
292             }
293              
294             # operators
295              
296 29     29 1 1201 sub inverse { $_[0]->_INV }
297              
298             BEGIN {
299 11     11   28 foreach my $method (qw(
300             _NEW _NEG _INV _ADD _SUB _MUL _DIV _POW
301             )) {
302 11     11   79 no strict 'refs';
  11         17  
  11         592  
303 88     36   207 *{$method} = sub { $undefined };
  88         1078  
  36         48  
304             }
305             }
306              
307             # miscellaneous
308              
309 1     1 1 3 sub optimize_time { $_[0] }
310 1     1 1 3 sub optimize_space { $_[0] }
311 1     1 1 3 sub optimize_default { $_[0] }
312              
313             1;
314              
315             __END__