File Coverage

blib/lib/Math/Modular/SquareRoot.pm
Criterion Covered Total %
statement 125 126 99.2
branch 51 66 77.2
condition 13 15 86.6
subroutine 15 15 100.0
pod 6 9 66.6
total 210 231 90.9


line stmt bran cond sub pod time code
1             =head1 Name
2              
3             Math::Modular::SquareRoot - Modular square roots
4              
5             =head1 Synopsis
6              
7             =cut
8              
9              
10             package Math::Modular::SquareRoot;
11              
12 3     3   68004 use Carp;
  3         6  
  3         288  
13 3     3   15 use strict;
  3         6  
  3         100  
14 3     3   16 use Scalar::Util qw(looks_like_number);
  3         9  
  3         5186  
15              
16              
17             # Check a parameter is a positive integer
18              
19 88     88 0 157 sub posInteger($$$)
20             {my ($s, $p, $n) = @_;
21 88         107 ++$p;
22 88 0       188 if (ref($n) eq "Math::BigInt")
  0 50       0  
23 88 50       277 {$n > 0 or croak "$s(parameter $p) must be a positive integer not $n";
24             }
25             else
26             {looks_like_number($n) or croak "$s(parameter $p): $n not a number";
27 88 50       188 int($n) == $n or croak "$s(parameter $p): $n not an integer";
28 88 50       285 $n > 1 or croak "$s(parameter $p): $n not allowed as argument";
29             }
30             }
31              
32              
33             # Check a parameter is any integer
34              
35 81     81 0 150 sub anyInteger($$$)
36             {my ($s, $p, $n) = @_;
37 81         108 ++$p;
38 81 50       177 if (ref($n) eq "Math::BigInt")
39 81 100       681 {}
40             else
41             {looks_like_number($n) or croak "$s(parameter $p): $n not a number";
42 79 100       463 int($n) == $n or croak "$s(parameter $p): $n not an integer";
43             }
44             }
45              
46              
47             =pod
48              
49             Find the integer square roots of $S modulo $a, where $S,$a are integers:
50              
51             use Math::Modular::SquareRoot qw(:msqrt);
52              
53             msqrt1(3,11);
54              
55             # 5 6
56              
57             =cut
58              
59 19     19 1 39 sub msqrt1($$)
60             {my ($S, $a) = @_;
61 19         46 anyInteger('msqrt1',0,$S);
62 19         52 posInteger('msqrt1',1,$a);
63              
64 19         30 $S %= $a;
65              
66 19         23 my @r;
67 19 100       43 push @r, 0 if $S == 0;
68 19         23 my $l = 0;
69 19         54 for($_ = 1; $_ < $a; ++$_)
  4090378         4128808  
70             {$l += 2*$_-1;
71 4090378         3801158 $l %= $a;
72 4090378 100       9762123 push @r, $_ if $l == $S;
73             }
74             @r
75 19         212 }
76              
77              
78             =pod
79              
80             Find the integer square roots of $S modulo $a*$b when $S,$a,$b are
81             integers:
82              
83             use Math::Modular::SquareRoot qw(:msqrt);
84              
85             msqrt2((243243 **2, 1_000_037, 1_000_039);
86              
87             # 243243 243252243227 756823758219 1000075758200
88              
89             =cut
90              
91 2     2 1 31 sub msqrt2($$$)
92             {my ($S, $a, $b) = @_;
93              
94 2         10 anyInteger('msqrt2',0,$S);
95 2         8 posInteger('msqrt2',1,$a);
96 2         6 posInteger('msqrt2',2,$b);
97              
98 2         8 my @A = msqrt1($S, $a);
99 2         106 my @B = msqrt1($S, $b);
100 2         24 my ($m, $n) = dgcd($a, $b);
101            
102 2         5 my @r;
103 2         6 for my $A(@A)
  8         24  
104 4         7 {for my $B(@B)
105             {push @r, (($B-$A)*$a*$m+$A) % ($a*$b);
106             }
107             }
108             @r
109 2         40 }
110              
111              
112             =pod
113              
114             Find the greatest common divisor of a list of numbers:
115              
116             use Math::Modular::SquareRoot qw(gcd);
117              
118             gcd 10,12,6;
119              
120             # 2
121              
122             =cut
123              
124              
125 24         49 sub gcd(@)
126 8     8 1 539 {my (@n) = grep {$_} @_;
127              
128             # Validate
129              
130 8         33 anyInteger('gcd',$_,$_[$_]) for 0..$#_;
131              
132 5 50       17 @n > 0 or croak "gcd(@_) requires at least one non zero numeric argument";
133              
134 5         21 $_ = abs($_) for @n;
135              
136              
137             # Find gcd
138              
139 11     11   19 my $g = sub
140             {my ($a, $b) = @_;
141              
142 11         28 for(;my $r = $a % $b;)
  18         18  
143 18         45 {$a = $b; $b = $r;
144             }
145 11         29 $b
146 5         21 };
147              
148             # Find gcd of list
149              
150              
151 5         11 my $n = shift @n;
152 5         13 $n = &$g($n, $_) for @n;
153            
154 5         37 $n
155             }
156              
157              
158             =pod
159              
160             Find the greatest common divisor of two numbers, optimized for speed
161             with no parameter checking:
162              
163             use Math::Modular::SquareRoot qw(gcd2);
164              
165             gcd2 9,24;
166              
167             # 3
168              
169             =cut
170              
171 3410314     3410314 1 4725659 sub gcd2($$)
172             {my ($a, $b) = @_;
173              
174 3410314         7434927 for(;my $r = $a % $b;)
  51611905         55130573  
175 51611905         108050192 {$a = $b; $b = $r;
176             }
177              
178 3410311         14122267 abs $b
179             }
180              
181              
182             my $comment = << 'end';
183              
184             given: a,b, gcd(a,b) == 1, N % a = A, N % b = B find N
185             => N = ai + A = bj + B
186             => ai - bj = B - A = C
187              
188             We can find am-bn = 1
189             => Cam-Cbn = C
190             => Cam = ai, Cbn = bj
191             => N = Cam+A = Cbn+B
192             => N = (B-A)am+A = (B-A)bn+B
193              
194             To find m,n for 41m-12n=1
195             a*m - b*n = c
196             41 - 12*3 = 5 12/5 = 2, 2+1 = 3
197             41*3 - 12*10 = 3
198             5 - 3 = 2
199             5*2 - 3*3 = 1
200             =>
201             41*2 - 12*6 = 10
202             41*9 - 12*30 = 9
203             =>
204             41*-7 - 12*-24 = 1
205             =>
206             12*24 - 41*7 = 1
207              
208             end
209              
210              
211             =pod
212              
213             Solve $a*$m+$b*$n == 1 for integers $m,$n, given integers $a,$b where
214             gcd($a,$b) == 1
215              
216             use Math::Modular::SquareRoot qw(dgcd);
217              
218             dgcd(12, 41);
219              
220             # 24 -7
221             # 24*12-7*41 == 1
222              
223             =cut
224              
225             sub dgcd($$)
226 20     20 0 537 {anyInteger('dgcd',$_,$_[$_]) for 0..$#_;
227 20         38 {my $d = gcd2($_[0], $_[1]);
  20         50  
228 20 50       84 $d == 1 or croak "dgcd(@_) == $d: arguments are not coprime to each other";
229             }
230              
231 20     33   54 my $d; $d = sub
  33         48  
232             {my ($a, $b) = @_;
233 33 50       70 return ($a,$b) if $b == 1;
234 33         80 my ($m, $n) = (1, ($a - $a % $b) / $b);
235 33         66 my $c = ($a*$m - $b*$n);
236              
237 33 100       104 return($m, $n) if $c == 1;
238              
239 20         46 my $c1 = ($b - $b % $c) / $c + 1;
240 20         39 my ($M, $N) = ($c1*$m, $c1*$n+1);
241 20         31 my $C = $a*$M - $b*$N;
242              
243 20 100       54 return($M, $N) if $C == 1;
244            
245 13         14 my ($mM, $nN);
246 13 50       111 ($mM, $nN) = &$d($c, $C) if $c > $C;
247 13 50       31 ($nN, $mM) = &$d($C, $c) if $C > $c;
248              
249 13         40 ($m*$mM-$M*$nN, $n*$mM-$N*$nN)
250 20         149 };
251              
252 20         74 my ($a, $b) = @_;
253 20         30 my ($A, $B) = (0, 0);
254 20         28 my ($m, $n);
255 20 100       72 ($A = 1, $a = -$a) if $a < 0;
256 20 100       48 ($B = 1, $b = -$b) if $b < 0;
257 20 100       47 if ($a > $b)
  12         28  
258 12         24 {($m, $n) = &$d($a, $b); $n = -$n;
  8         21  
259             }
260             else
261 8         15 {($n, $m) = &$d($b, $a); $m = -$m;
262             }
263 20 100       45 $m = -$m if $A;
264 20 100       40 $n = -$n if $B;
265 20 100       32 {$a = -$a if $A;
  20         42  
266 20 100       51 $b = -$b if $B;
267 20         48 my $r = $m*$a+$b*$n;
268 20 50       50 $r == 1 or croak "dgcd(@_): m=$m*a=$a+b=$b*n=$n == $r != 1";
269             }
270              
271 20         706 ($m, $n);
272             }
273              
274              
275             =pod
276              
277             Factorial of a number:
278              
279             use Math::Modular::SquareRoot qw(factorial);
280              
281             factorial(6);
282              
283             # 720
284              
285             =cut
286              
287 5     5 1 9 sub factorial($)
288             {my ($n) = @_;
289 5         9 posInteger('factorial',0,$n);
290              
291 5 50       9 return 1 if $n == 1;
292              
293 5         7 my $p = 1; $p *= $_ for 2..$n;
  5         19  
294              
295 5         16 $p
296             }
297              
298              
299              
300             =pod
301              
302             Check whether an integer is a prime:
303              
304             use Math::Modular::SquareRoot qw(prime);
305              
306             prime(9);
307              
308             # 0
309              
310             or possibly prime by trying to factor a specified number of times:
311              
312             use Math::Modular::SquareRoot qw(prime);
313              
314             prime(2**31-1, 7);
315              
316             # 1
317              
318             =cut
319              
320 46     46 1 898 sub prime($;$)
321             {my ($p, $n) = @_;
322 46         187 posInteger('prime',$_,$_[$_]) for 0..$#_;
323              
324 46 100 66     558 return 1 if $p == 1 or $p == 2 or$p == 3 or $p == 5 or $p == 7;
      100        
      100        
      100        
325 39 100       89 return 0 if $p < 11;
326              
327 34         75 my $s = int(sqrt($p))+1;
328 34 100       84 return 0 if $p % $s == 0;
329              
330 33 100       68 unless ($n)
  551449 100       984723  
331 21         30 {for(2..$s)
332             {return 0 unless $p % $_;
333             }
334 15         88 return 1;
335             }
336              
337 12         23 my $N = 10**$n;
338 12 50       31 $N = $s if $s < $N;
339 12         19 my $D = $s - $N;
340              
341 12 100 66     24 for(2..$N)
  3410286         11511921  
342             {return 0 if $p % $_ == 0 or gcd2($N+int(rand($D)), $p) > 1;
343             }
344              
345             1
346 11         101 }
347              
348              
349             # Export details
350            
351             require 5;
352             require Exporter;
353              
354 3     3   20 use vars qw(@ISA @EXPORT_OK %EXPORT_TAGS $VERSION);
  3         7  
  3         616  
355              
356             @ISA = qw(Exporter);
357             @EXPORT_OK = qw(dgcd gcd gcd2 factorial msqrt1 msqrt2 prime);
358             %EXPORT_TAGS = (all=>[@EXPORT_OK], msqrt=>[qw(msqrt1 msqrt2)]);
359             $VERSION = '1.001'; # Monday 23 March 2009
360              
361             =head1 Description
362              
363             The routines
364              
365             msqrt1 ($S,$a*$b)>
366             msqrt2 ($S,$a,$b)>
367              
368             demonstrate the difference in time required to find the modular square
369             root of a number $S modulo $p when the factorization of $p is
370             respectively unknown and known. To see this difference, compare the time
371             required to process test: C with line 11 uncommented with that of
372             C. The time required to find the modular square root of $S
373             modulo $p grows exponentially with the length $l in characters of the
374             number $p. For well chosen:
375              
376             $p=$a*$b
377              
378             the difference in times required to recover the square root can be made
379             very large for small $l. The difference can be made so large that the
380             unfactored version takes more than a year's effort by all the computers
381             on planet Earth to solve, whilst the factored version can be solved in a
382             few seconds on one personal computer.
383              
384             Ideally $a,$b and should be prime. This prevents alternate
385             factorizarizations of $p being present which would lower the difference
386             in time to find the modular square root.
387              
388              
389             =head2 msqrt1() msqrt2()
390              
391             C finds the square roots of $S modulo $a where $S,$a are
392             integers. There are normally either zero or two roots for a given pair
393             of numbers if gcd($S,$a) == 1 although in the case that $S==0 and $a is
394             prime, zero will have just one square root: zero. If gcd($S,$a) != 1
395             there will be more pairs of square roots. The square roots are returned
396             as a list. C will croak if its arguments are not
397             integers, or if $a is zero.
398              
399             C finds the square roots of $S modulo $a*$b where
400             $S,$a,$b are integers. There are normally either zero or four roots for
401             a given triple of numbers if gcd($S,$a) == 1 and gcd($S,$b) == 1. If
402             this is not so there will be more pairs of square roots. The square
403             roots are returned as a list. C will croak if its
404             arguments are not integers, or if $a or $b are zero.
405              
406              
407             =head2 gcd() gcd2()
408              
409             C finds the greatest common divisor of a list of numbers @_,
410             with error checks to validate the parameter list. C will croak
411             unless all of its arguments are integers. At least one of these integers
412             must be non zero.
413              
414             C finds the greatest common divisor of two integers $a,$b
415             as quickly as possible with no error checks to validate the parameter
416             list. C can always be used as a plug in replacement for
417             C but not vice versa.
418              
419             C solves the equation:
420              
421             $a*$m+$b*$n == 1
422              
423             for $m,$n given $a,$b where $a,$b,$m,$n are integers and
424              
425             gcd($a,$b) == 1
426              
427             The returned value is the list:
428              
429             ($m, $n)
430              
431             A check is made that the solution does solve the above equation, a croak
432             is issued if this test fails. C will also croak unless
433             supplied with two non zero integers as parameters.
434              
435              
436             =head2 prime()
437              
438             C checks that $p is prime, returning 1 if it is, 0 if it is
439             not. C will croak unless it is supplied with one integer
440             parameter greater than zero.
441              
442             C checks that $p is prime by trying the first $N =
443             10**$n integers as divisors, while at the same time, finding the
444             greatest common divisor of $p and a number at chosen at random between
445             $N and the square root of $p $N times. If neither of these techniques
446             finds a divisor, it is possible that $p is prime and the
447             function retuerns 1, else 0.
448              
449              
450             =head2 factorial()
451              
452             C finds the product of the integers from 1 to $n.
453             C will croak unless $n is a positive integer.
454              
455              
456             =head1 Export
457              
458             C are
459             exported upon request. Alternatively the tag B<:all> exports all these
460             functions, while the tag B<:sqrt> exports just C.
461              
462              
463             =head1 Installation
464              
465             Standard Module::Build process for building and installing modules:
466              
467             perl Build.PL
468             ./Build
469             ./Build test
470             ./Build install
471              
472             Or, if you're on a platform (like DOS or Windows) that doesn't require
473             the "./" notation, you can do this:
474              
475             perl Build.PL
476             Build
477             Build test
478             Build install
479              
480             =head1 Author
481              
482             PhilipRBrenan@handybackup.com
483              
484             http://www.handybackup.com
485              
486             =head1 See Also
487              
488             =over
489              
490             =back
491              
492             =head1 Copyright
493              
494             Copyright (c) 2009 Philip R Brenan.
495              
496             This module is free software. It may be used, redistributed and/or
497             modified under the same terms as Perl itself.
498              
499             =cut