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             package Math::ModInt;
2              
3 11     11   436216 use 5.006;
  11         112  
4 11     11   64 use strict;
  11         23  
  11         295  
5 11     11   56 use warnings;
  11         23  
  11         446  
6             use Math::ModInt::Event
7 11     11   5116 qw(UsageError Nonexistent LoadingFailure UndefinedResult DifferentModuli);
  11         33  
  11         2354  
8              
9             sub _no_op {
10 22     22   1988 my ($op) = @_;
11 22     2   2269 return $op => sub { UsageError->raise('undefined operation', $op) };
  2         237  
12             }
13              
14             use overload (
15 11         156 'neg' => '_NEG',
16             '+' => \&_oadd,
17             '-' => \&_osub,
18             '*' => \&_omul,
19             '/' => \&_odiv,
20             '**' => \&_opow,
21             '==' => \&_oeq,
22             '!=' => \&_oneq,
23             '!' => 'is_zero',
24             'bool' => 'is_not_zero',
25             '0+' => 'residue',
26             '""' => 'as_string',
27             'fallback' => undef,
28             _no_op('abs'),
29             _no_op('int'),
30 11     11   8578 );
  11         13338  
31              
32             # ----- class data -----
33              
34             BEGIN {
35 11     11   5690 require Exporter;
36 11         236 our @ISA = qw(Exporter);
37 11         44 our @EXPORT_OK = qw(mod divmod qmod);
38 11         21 our @CARP_NOT = qw(Math::ModInt::ChineseRemainder);
39 11         1240 our $VERSION = '0.013';
40             }
41              
42             sub _max_modulus_perl {
43 11     11   27 my $limit = 32767;
44 11         51 foreach my $bits (16..96) {
45 429         558 my $probe = $limit + $limit + 1;
46 429 100       893 last if 0.5 != $probe / 2 - ($probe ^ 1) / 2;
47 418         681 $limit = $probe;
48             }
49 11         1333 return int sqrt $limit;
50             }
51              
52 11     11   82 use constant _MAX_MODULUS_PERL => _max_modulus_perl();
  11         22  
  11         29  
53              
54             my $undefined = bless []; # singleton
55             my %loaded = (); # collects loaded modules
56              
57             # ----- private subroutines -----
58              
59             sub _is_integer {
60 324     324   503 my ($arg) = @_;
61 324         482 local $@;
62             return
63 324         594 eval {
64 11     11   92 use warnings FATAL => 'all';
  11         30  
  11         18289  
65 324 100 100     1927 ref $arg?
66             $arg->isa('Math::BigInt')
67             :
68             $arg == int($arg) && abs($arg) <= ~0
69             };
70             }
71              
72             sub _incompatible {
73 25     25   49 my ($this, $that) = @_;
74 25 100 100     52 if ($this->is_defined && $that->is_defined) {
75 17         60 DifferentModuli->raise($this, $that);
76             }
77 21         65 return $undefined;
78             }
79              
80             sub _best_class {
81 127     127   220 my ($modulus) = @_;
82 127 100       254 if (_is_integer($modulus)) {
83 122 100       307 return 'Math::ModInt::BigInt' if $modulus > _MAX_MODULUS_PERL;
84 111 100       630 return 'Math::ModInt::Perl' if $modulus > 3;
85 32 100       89 return 'Math::ModInt::GF3' if $modulus == 3;
86 25 100       64 return 'Math::ModInt::GF2' if $modulus == 2;
87 15 100       43 return 'Math::ModInt::Trivial' if $modulus == 1;
88             }
89 14         55 UsageError->raise('positive integer modulus expected');
90             }
91              
92             sub _load {
93 114     114   1369 my ($class) = @_;
94 114 100       156 do {
95 114         160 local $@;
96 114   100     1772 $loaded{$class} ||= eval "require $class"
97             }
98             or LoadingFailure->raise($class);
99 113         234 return $class;
100             }
101              
102             sub _oadd {
103 120     120   4480 my ($this, $that) = @_;
104 120 100 100     629 if (!ref $that || !$that->isa(__PACKAGE__)) {
    100          
105 9         25 $that = $this->_NEW($that);
106             }
107             elsif ($this->modulus != $that->modulus) {
108 13         38 return _incompatible($this, $that);
109             }
110 107         1250 return $this->_ADD($that);
111             }
112              
113             sub _osub {
114 92     92   1803 my ($this, $that, $reversed) = @_;
115 92 100 100     456 if (!ref $that || !$that->isa(__PACKAGE__)) {
    100          
116 6         23 $that = $this->_NEW($that);
117             }
118             elsif ($this->modulus != $that->modulus) {
119 4         7 return _incompatible($this, $that);
120             }
121 88 100       973 return $reversed? $that->_SUB($this): $this->_SUB($that);
122             }
123              
124             sub _omul {
125 131     131   3862 my ($this, $that) = @_;
126 131 100 100     643 if (!ref $that || !$that->isa(__PACKAGE__)) {
    100          
127 6         13 $that = $this->_NEW($that);
128             }
129             elsif ($this->modulus != $that->modulus) {
130 4         7 return _incompatible($this, $that);
131             }
132 127         1618 return $this->_MUL($that);
133             }
134              
135             sub _odiv {
136 93     93   1559 my ($this, $that, $reversed) = @_;
137 93 100 100     464 if (!ref $that || !$that->isa(__PACKAGE__)) {
    100          
138 6         12 $that = $this->_NEW($that);
139             }
140             elsif ($this->modulus != $that->modulus) {
141 4         7 return _incompatible($this, $that);
142             }
143 89 100       1015 return $reversed? $that->_DIV($this): $this->_DIV($that);
144             }
145              
146             sub _opow {
147 199     199   6311 my ($this, $exp, $reversed) = @_;
148             # exponent should be in perl integer range or be a big int
149 199 100 100     556 if ($reversed || !_is_integer($exp)) {
150 13         69 UsageError->raise('integer exponent expected');
151             }
152 186         508 return $this->_POW($exp);
153             }
154              
155             sub _oeq {
156 675     675   3585 my ($this, $that) = @_;
157             # note that comparing with $undefined is illegal
158 675 100 100     2768 if (!ref $that || !$that->isa(__PACKAGE__)) {
159 17         46 return $this->residue == $that % $this->modulus;
160             }
161             return
162 658   100     1406 $this->residue == $that->residue &&
163             $this->modulus == $that->modulus;
164             }
165              
166             sub _oneq {
167 20     20   2068 my ($this, $that) = @_;
168             # note that comparing with $undefined is illegal
169 20 100 100     112 if (!ref $that || !$that->isa(__PACKAGE__)) {
170 8         25 return $this->residue != $that % $this->modulus;
171             }
172             return
173 12   100     32 $this->residue != $that->residue ||
174             $this->modulus != $that->modulus;
175             }
176              
177             # ----- fallback for implementation interface method -----
178              
179             sub _NEW2 {
180 3     3   7 my ($this, $int, $modulus) = @_;
181 3         5 my $that;
182 3 100       7 if (ref $this) {
183 1         3 $that = $this->_NEW($int);
184 1         3 $modulus = $this->modulus;
185             }
186             else {
187 2         8 $that = $this->_NEW($int, $modulus);
188             }
189 3         8 my $quot = ($int - $that->residue) / $modulus;
190 3         12 return ($quot, $that);
191             }
192              
193             # ----- public methods -----
194              
195             # constructors
196              
197             sub mod {
198 121     121 1 136269 my ($int, $modulus) = @_;
199 121         268 my $class = _load(_best_class($modulus));
200 106         337 return $class->_NEW($int, $modulus);
201             }
202              
203             sub divmod {
204 6     6 1 810 my ($int, $modulus) = @_;
205 6         23 my $class = _load(_best_class($modulus));
206 6         28 return $class->_NEW2($int, $modulus);
207             }
208              
209             sub qmod {
210 1     1 1 2365 my ($rat, $modulus) = @_;
211 1         4 my $class = _load(_best_class($modulus));
212 1         6 my $num = $class->_NEW($rat->numerator, $modulus);
213 1         212 my $den = $num->_NEW($rat->denominator);
214 1         185 return $num / $den;
215             }
216              
217             sub new {
218 255     255 1 11162 my ($this, $int, $modulus) = @_;
219 255 100       725 return $this->_NEW($int) if ref $this;
220 19         41 return mod($int, $modulus);
221             }
222              
223             sub new2 {
224 4     4 1 1643 my ($this, $int, $modulus) = @_;
225 4 100       23 return $this->_NEW2($int) if ref $this;
226 1         5 return divmod($int, $modulus);
227             }
228              
229             sub undefined {
230 61     61 1 601 UndefinedResult->raise;
231 61         169 return $undefined;
232             }
233              
234             # accessors
235              
236             sub residue {
237 3     3 1 110 Nonexistent->raise('undefined residue');
238             }
239              
240             sub modulus {
241 17 100   17 1 177 return 0 if __PACKAGE__ eq (caller)[0]; # special case for _oadd etc.
242 1         6 Nonexistent->raise('undefined modulus');
243             }
244              
245             sub signed_residue {
246 25     25 1 2002 my ($this) = @_;
247 25         65 my $r = $this->residue;
248 24         51 my $m = $this->modulus;
249 24         54 my $n = $m - $r;
250 24 100       881 return $n <= $r? -$n: $r;
251             }
252              
253             sub centered_residue {
254 21     21 1 1856 my ($this) = @_;
255 21         61 my $r = $this->residue;
256 20         42 my $m = $this->modulus;
257 20         79 my $n = $m - $r;
258 20 100       994 return $n < $r? -$n: $r;
259             }
260              
261             sub is_defined {
262 701     701 1 29004 my ($this) = @_;
263 701         1722 return ref $undefined ne ref $this;
264             }
265              
266             sub is_undefined {
267 150     150 1 906 my ($this) = @_;
268 150         535 return ref $undefined eq ref $this;
269             }
270              
271             sub is_zero {
272 76     76 1 825 my ($this) = @_;
273 76         158 return 0 == $this->residue;
274             }
275              
276             sub is_not_zero {
277 6     6 1 439 my ($this) = @_;
278 6         28 return 0 != $this->residue;
279             }
280              
281             sub as_string {
282 22     22 1 1864 my ($this) = @_;
283 22 100       65 my ($r, $mod) =
284             $this->is_defined? ($this->residue, $this->modulus): qw(? ?);
285 22         769 return "mod($r, $mod)";
286             }
287              
288             # operators
289              
290 29     29 1 962 sub inverse { $_[0]->_INV }
291              
292             BEGIN {
293 11     11   46 foreach my $method (qw(
294             _NEW _NEG _INV _ADD _SUB _MUL _DIV _POW
295             )) {
296 11     11   123 no strict 'refs';
  11         23  
  11         1111  
297 88     36   315 *{$method} = sub { $undefined };
  88         1475  
  36         64  
298             }
299             }
300              
301             # miscellaneous
302              
303 1     1 1 4 sub optimize_time { $_[0] }
304 1     1 1 3 sub optimize_space { $_[0] }
305 1     1 1 4 sub optimize_default { $_[0] }
306              
307             1;
308              
309             __END__