File Coverage

blib/lib/Math/Big/Factors.pm
Criterion Covered Total %
statement 98 101 97.0
branch 15 20 75.0
condition 5 10 50.0
subroutine 9 10 90.0
pod 2 2 100.0
total 129 143 90.2


line stmt bran cond sub pod time code
1             #############################################################################
2             # Math/Big/Factors.pm -- factor big numbers into prime factors
3              
4             package Math::Big::Factors;
5              
6             require 5.006002; # requires this Perl version or later
7              
8 1     1   54048 use strict;
  1         3  
  1         44  
9 1     1   8 use warnings;
  1         3  
  1         52  
10              
11 1     1   8 use Math::BigInt;
  1         2  
  1         9  
12 1     1   2120 use Math::BigFloat;
  1         24762  
  1         5  
13 1     1   1399 use Math::Big;
  1         4  
  1         74  
14 1     1   7 use Exporter;
  1         1  
  1         898  
15              
16             our $VERSION = '1.14';
17             our @ISA = qw( Exporter );
18             our @EXPORT_OK = qw( wheel factors_wheel
19             );
20              
21             sub wheel
22             {
23             # calculate a prime-wheel of order $o
24 12   50 12 1 1968 my $o = abs(shift || 1); # >= 1
25              
26             # some primitive wheels as shortcut:
27 12 100       45 return [ Math::BigInt->new(2), Math::BigInt->new(1) ] if $o == 1;
28              
29 9         42 my @primes = Math::Big::primes($o*5); # initial primes, get some more
30              
31 9         231 my $mul = Math::BigInt->new(1); my @wheel;
  9         213  
32 9         31 for (my $i = 0; $i < $o; $i++)
33             {
34             #print "$primes[$i]\n";
35 27         62 $mul *= $primes[$i]; push @wheel,$primes[$i];
  27         1125  
36             }
37             #print "Mul $mul\n";
38 9         13 my $last = $wheel[-1]; # get biggest initial
39             #print "last is $last\n";
40             # now sieve any number that is a multiply of one of the inital ones
41 9         29 @primes = (); # undef => leftover
42 9         17 foreach (@wheel)
43             {
44 27 100       1194 next if $_ == 2; # dont mark these, we skip 'em
45 18         1221 my $i = $_; my $add = $i;
  18         19  
46 18         47 while ($i < $mul)
47             {
48 462         25785 $primes[$i] = 1; $i += $add;
  462         4940  
49             }
50             }
51 9         647 push @wheel, Math::BigInt->new(1);
52 9         237 my $i = $last;
53 9         22 while ($i < $mul)
54             {
55 351 100       38328 push @wheel,$i if !defined $primes[$i]; $i += 2; # skip even ones
  351         4434  
56             }
57 9         1132 \@wheel;
58             }
59              
60             sub _transform_wheel
61             {
62             # from a given prime-wheel, calculate a increment table that can be used
63             # to step trough numbers
64             # input: ref to array with prime wheel
65             # output: ($restart,$ref_to_add_table);
66              
67 8     8   9 my (@wheel,$we);
68 8         13 my $add = shift; shift @$add; # remove the first 2 from wheel
  8         14  
69              
70 8 100       34 if (@$add == 1) # order 1
71             {
72 2         5 my $two = Math::BigInt->new(2);
73             # (2,2) or (2,2,2,2,2,2) etc would do, too
74 2         40 @wheel = ($two->copy(),$two->copy(),$two->copy(),$two->copy());
75 2         81 return (1,\@wheel);
76             }
77             # from the list of divisors above create a add-table which we can take to
78             # increment from 3 onwards. The tabe consists of two parts, the second part
79             # will be repeatedly used
80 6         12 my $last = -1; my $mod = 2; my $i = 0;
  6         11  
  6         9  
81             # create the increment part for the initial primes (3,5, or 3,5,7 etc)
82 6         15 while ($add->[$i] != 1)
83             {
84 12         785 $mod *= $add->[$i];
85 12 100       847 push @wheel, $add->[$i] - $last if $last != -1; # skip the first
86             #print $wheel[-1],"\n" if $last != -1;
87 12         1001 $last = $add->[$i]; $i++;
  12         28  
88             }
89             #print "mod $mod\n";
90 6         360 my $border = $i-1; # account for ++
91 6         11 my $length = scalar @$add-$i;
92 6         12 my $ws = $border+$length; # remember this
93             #print "border: $border length $length $mod\n";
94              
95             # now we add two arrays in a row, both are equal except the first element
96             # which is in case A a step from the last inital prime to the second in list
97             # and in case B a step from '1' to the second in list
98              
99             #print "add[border+1]: ",$add->[$border+1]," add[border] $add->[$border]\n";
100 6         18 $wheel[$border] = $add->[$border+2]-$add->[$border];
101 6         548 $wheel[$border+$length] = $add->[$border+2]-1;
102             # and last add a wrap-around around $mod
103             #print "last: ",$add->[-1],"\n";
104 6         831 $wheel[$border+$length-1] = 1+$mod-$add->[-1];
105 6         1055 $wheel[$border+$length*2-1] = $wheel[$border+$length-1];
106              
107 6         10 $i = $border + 1;
108             # now fill in the rest
109 6         23 while ($i < $length+$border-1)
110             {
111 104         205 $wheel[$i] = $add->[$i+2]-$add->[$i+1];
112 104         7746 $wheel[$i+$length] = $wheel[$i];
113 104         251 $i++;
114             }
115 6         27 ($ws,\@wheel);
116             }
117              
118             sub factors_wheel
119             {
120 24     24 1 9879 my $n = shift;
121 24   50     107 my $o = abs(shift || 1);
122              
123 24 50       136 $n = Math::BigInt->new($n) unless ref $n;
124 24         954 my $two = Math::BigInt->new(2);
125 24         476 my $three = Math::BigInt->new(3);
126              
127 24         445 my @factors = ();
128 24         59 my $x = $n->copy();
129              
130 24 100       315 return ($x) if $x < 4;
131 8         562 my ($i,$y,$w,$div,$rem);
132              
133             #print "Using a wheel of order $o, length ";
134 8         22 my $wheel = wheel($o);
135             #print scalar @$wheel,":\n";
136 8         109 my ($ws,$add) = _transform_wheel($wheel); undef $wheel;
  8         62  
137 8         14 my $we = scalar @$add - 1;
138              
139             # reduce to odd number (after that, no odd left-over divisior will ocur)
140 8   66     32 while (($x->is_even) && (!$x->is_zero))
141             {
142 4         98 push @factors, $two->copy();
143             #print "factoring $x (",$x->length(),")\n";
144             #print "2\n";
145 4         52 $x /= $two;
146             }
147             # 8 => 6 => 3, 7, 6 => 3, 5, 4 => 2 => 1, 3, 2 => 1, are all prime
148             # so the first number interesting for us is 9
149 8         349 my $op = 0;
150             OUTER:
151 8         21 while ($x > 8)
152             {
153             #print "factoring $x (",$x->length(),")\n";
154 8         602 $i = $three; $w = 0;
  8         16  
155 8         17 while ($i < $x) # should be sqrt()
156             {
157             # $steps++;
158             # $op = 0, print "$i\r" if $op++ == 1024;
159 8         158 $y = $x->copy();
160 8         95 ($div,$rem) = $y->bdiv($i);
161 8 50       913 if ($rem == 0)
162             {
163             #print "$i\n";
164 8         1022 push @factors,$i;
165 8         13 $x = $div; next OUTER;
  8         39  
166             }
167             #print "$i + ",$add->[$w]," ($w)\n";
168             #$i += 2; # trial div by odd numbers
169 0         0 $i += $add->[$w];
170             #print "restart $w $ws\n" if $w == $we; # wheel of 2,3,5,7...
171 0 0       0 $w = $ws if $w++ == $we; # wheel of 2,3,5,7...
172             #exit if $i > 100000;
173             }
174 0         0 last;
175             }
176 8 50 33     533 push @factors,$x if $x != 1 || $n == 1;
177 8         678 @factors;
178             }
179              
180             sub _factor
181       0     {
182             # later: factor ( n => $n, algorithmn => 'wheel', order => 3 );
183             }
184              
185             1;
186              
187             __END__