File Coverage

blib/lib/Math/Factoring.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package Math::Factoring;
2             {
3             $Math::Factoring::VERSION = '0.02';
4             }
5              
6 2     2   47650 use warnings;
  2         6  
  2         65  
7 2     2   10 use strict;
  2         5  
  2         70  
8 2     2   2034 use Math::GMPz qw/:mpz/;
  0            
  0            
9             use Math::Primality qw/is_prime/;
10             use base 'Exporter';
11             use constant GMP => 'Math::GMPz';
12             our @EXPORT_OK = qw/factor factor_trial/;
13             our @EXPORT = qw//;
14             use Data::Dumper;
15              
16             # this gets rid of prototype warnings
17             sub factor_trial($);
18              
19             my @small_primes = qw/
20             5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79
21             83 89 97 101 103 107 109 113 127 131 137 139
22             149 151 157 163 167 173 179 181 191 193 197
23             1p99 211 223 227 229 233 239 241 251 257
24             /;
25             my %small_primes = map { $_ => 1 } @small_primes;
26              
27             # ABSTRACT: Math::Factoring - Advanced Factoring Algorithms
28              
29              
30             sub _random()
31             {
32             my $n = GMP->new(int rand(1e15) );
33             my $state = rand_init($n);
34             my $rand = GMP->new;
35             Rmpz_urandomm($rand, $state, $n,1);
36             #warn "return rand=$rand";
37             return $rand;
38             }
39              
40             # this is fast but only works for certain numbers
41             # which satisfy smoothness constraints that are currently not being
42             # checked for
43             sub _factor_pollard_rho($$$)
44             {
45             my ($n,$a,$x0) = @_;
46             warn "_factor_pollard($n,$a,$x0)\n";
47             my ($x,$y,$q,$d) = map { GMP->new } ( 1 .. 4 );
48             my ($i,$j) = (1,1);
49             $q = 1; $x = $x0; $y = $x0;
50              
51             do {
52             $x = ($x*$x + $a ) % $n;
53             $y = ($y*$y + $a ) % $n;
54             $y = ($y*$y + $a ) % $n;
55             $q *= ($x - $y);
56             $q %= $n;
57              
58             $i++;
59             $j = 1 if !$j;
60             if( ($i % $j ) == 0 ) {
61             $j++;
62             Rmpz_gcd($d, $q, $n);
63             if ($d != 1) {
64             if (!is_prime($d)) {
65             no warnings 'prototype';
66             return _factor_pollard_rho( $d,
67             (_random() & 32) - 16,
68             _random() & 31 );
69             } else {
70             return $d;
71             }
72             }
73             }
74             };
75             return 0;
76              
77             }
78              
79              
80             sub factor($)
81             {
82             my ($n) = @_;
83             if ($n <= 257 && $small_primes{$n} ){
84             return ("$n");
85             } else {
86             return factor_trial($n);
87             }
88             }
89              
90             sub factor_trial($)
91             {
92             my $n = shift;
93             if ($n >= 0 and $n <= 3) {
94             return ("$n");
95             }
96             $n = GMP->new($n);
97             my $sqrt = GMP->new;
98             Rmpz_sqrt($sqrt, $n);
99              
100             # speed up factors of perfect squares
101              
102             if( Rmpz_perfect_square_p($n) ){
103             my @root_factors = factor_trial($sqrt);
104             return map { ("$_","$_") } @root_factors;
105             }
106              
107             my @factors;
108             my $cur = GMP->new(2);
109             my ($mod,$square) = (GMP->new,GMP->new);
110             Rmpz_mul($square,$cur,$cur);
111              
112             while( $square <= $n ) {
113             Rmpz_mod($mod,$n,$cur);
114             if( Rmpz_cmp_ui($mod,0) == 0 ) {
115             push @factors,"$cur";
116             Rmpz_tdiv_q($n,$n,$cur); # $n = $n / $cur;
117             } else {
118             Rmpz_add_ui($cur,$cur,1); # $cur++
119             }
120             Rmpz_mul($square,$cur,$cur);
121             }
122             if (@factors == 0) {
123             return ("$n"); # it was prime
124             }
125             if ( Rmpz_cmp_ui($n,1) ) {
126             push @factors,"$n"; # add the last factor
127             }
128             return sort { $a <=> $b } @factors;
129             }
130              
131              
132             sub factor_pollard_rho($)
133             {
134             my $n = GMP->new($_[0]);
135             my @factors;
136             if ($n >= 0 and $n <= 3) {
137             return "$n";
138             } else {
139             my ($a,$x0) = (1,3);
140             my $t;
141             while( !is_prime($n) ) {
142             $t = _factor_pollard_rho($n,$a,$x0);
143             warn "found t=$t,n=$n";
144             last if $t == 0;
145             push @factors, "$t";
146             $n /= $t;
147             }
148             push @factors, "$n";
149             }
150              
151             return sort { $a <=> $b } @factors;
152             }
153              
154             1; # End of Math::Factoring
155              
156             __END__