File Coverage

blib/lib/Math/NumSeq/PythagoreanHypots.pm
Criterion Covered Total %
statement 84 86 97.6
branch 20 22 90.9
condition 8 9 88.8
subroutine 17 17 100.0
pod 4 4 100.0
total 133 138 96.3


line stmt bran cond sub pod time code
1             # Copyright 2010, 2011, 2012, 2013, 2014 Kevin Ryde
2              
3             # This file is part of Math-NumSeq.
4             #
5             # Math-NumSeq is free software; you can redistribute it and/or modify
6             # it under the terms of the GNU General Public License as published by the
7             # Free Software Foundation; either version 3, or (at your option) any later
8             # version.
9             #
10             # Math-NumSeq is distributed in the hope that it will be useful, but
11             # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12             # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13             # for more details.
14             #
15             # You should have received a copy of the GNU General Public License along
16             # with Math-NumSeq. If not, see .
17              
18             package Math::NumSeq::PythagoreanHypots;
19 1     1   1109 use 5.004;
  1         2  
20 1     1   3 use strict;
  1         1  
  1         28  
21              
22 1     1   3 use vars '$VERSION', '@ISA';
  1         1  
  1         66  
23             $VERSION = 72;
24              
25 1     1   4 use Math::NumSeq 7; # v.7 for _is_infinite()
  1         9  
  1         38  
26             @ISA = ('Math::NumSeq');
27             *_is_infinite = \&Math::NumSeq::_is_infinite;
28              
29 1     1   3 use Math::NumSeq::Primes;
  1         1  
  1         22  
30 1     1   3 use Math::Prime::XS 0.23 'is_prime'; # version 0.23 fix for 1928099
  1         14  
  1         58  
31              
32             # uncomment this to run the ### lines
33             #use Smart::Comments;
34              
35              
36             # use constant name => Math::NumSeq::__('Pyathagorean Hypotenuses');
37 1     1   5 use constant description => Math::NumSeq::__('The hypotenuses of Pythagorean triples, ie. integers C for which there\'s some A>=1,B>=1 satisfying A^2+B^2=C^2. Primitive hypotenuses are where A,B have no common factor.');
  1         1  
  1         3  
38 1     1   3 use constant characteristic_increasing => 1;
  1         1  
  1         40  
39 1     1   3 use constant characteristic_integer => 1;
  1         1  
  1         33  
40 1     1   3 use constant values_min => 5;
  1         1  
  1         33  
41 1     1   3 use constant i_start => 1;
  1         2  
  1         54  
42              
43 1         3 use constant parameter_info_array =>
44             [ {
45             name => 'pythagorean_type',
46             type => 'enum',
47             default => 'all',
48             choices => ['all','primitive'],
49             choices_display => [Math::NumSeq::__('All'),
50             Math::NumSeq::__('Primitive'),
51             ],
52             },
53 1     1   4 ];
  1         1  
54              
55             #------------------------------------------------------------------------------
56              
57             # cf A002144 - primes 4n+1, the primitive elements of hypots x!=y
58             # -1 is a quadratic residue ...
59             # A002365 - the "y" of prime "c" ??
60             # A002366 - the "x" of prime "c" ??
61             # A046083 - the "a" smaller number, ordered by "c"
62             # A046084 - the "b" second number, ordered by "c"
63             #
64             # A008846 - primitives, x,y no common factor
65             # A004613 - all prime factors are 4n+1, is 1 then primitive hypots
66             #
67             # A009000 - hypots with repetitions
68             # A009012 - "b" second number, ordered by "b", with repetitions
69             #
70             my %oeis_anum = (all => 'A009003', # distinct a!=b and a,b>0
71             primitive => 'A008846',
72             );
73             # OEIS-Catalogue: A009003
74             # OEIS-Catalogue: A008846 pythagorean_type=primitive
75             sub oeis_anum {
76 2     2 1 5 my ($self) = @_;
77 2         4 return $oeis_anum{$self->{'pythagorean_type'}};
78             }
79              
80             #------------------------------------------------------------------------------
81              
82             sub rewind {
83 6     6 1 1142 my ($self) = @_;
84 6         14 $self->{'array'} = [];
85 6         81 $self->{'i'} = $self->i_start;
86 6         16 $self->{'hi'} = 1;
87             }
88              
89             sub next {
90 24     24 1 618 my ($self) = @_;
91 24         21 my $array = $self->{'array'};
92 24         19 for (;;) {
93 28 100       48 if (defined (my $value = shift @$array)) {
94 24         46 return ($self->{'i'}++,
95             $value);
96             }
97 4         8 my $lo = $self->{'hi'} + 1;
98 4         5 $self->{'hi'} = my $hi = $lo + 1000;
99 4         7 @$array = _hypots_block ($self, $lo, $hi);
100             }
101             }
102              
103             sub _hypots_block {
104 4     4   5 my ($self, $lo, $hi) = @_;
105              
106 4 100       8 if ($self->{'pythagorean_type'} eq 'primitive') {
107 2         22 return grep {$self->pred($_)} $lo .. $hi;
  2002         2023  
108              
109             } else {
110 2         4 my %hypots;
111 2         7 foreach my $p (grep {($_ & 3)==1}
  336         361  
112             Math::NumSeq::Primes::_primes_list(2, $hi)) {
113 160         193 @hypots{map {$_*$p}
  1296         1451  
114             (int(($lo+$p-1)/$p) .. int($hi/$p))
115             } = ();
116             }
117 2         99 return sort {$a<=>$b} keys %hypots;
  9013         5174  
118             }
119             }
120              
121             sub pred {
122 2110     2110 1 1680 my ($self, $value) = @_;
123             ### pred: $value
124              
125 2110 100 66     3816 if ($value < 5
      100        
126             || _is_infinite($value)
127             || $value != int($value)) {
128 53         51 return 0;
129             }
130              
131 2057         1798 my $pythagorean_type = $self->{'pythagorean_type'};
132             ### $pythagorean_type
133              
134 2057 100       2311 unless ($value % 2) {
135             ### even ...
136 1024 100       1147 if ($pythagorean_type ne 'all') {
137             ### primitive and prime never even ...
138 1014         1101 return 0;
139             }
140 10         10 do {
141 18         27 $value /= 2;
142             } until ($value % 2);
143             }
144 1043 50       1090 unless ($value <= 0xFFFF_FFFF) {
145 0         0 return undef;
146             }
147 1043         709 $value = "$value"; # numize Math::BigInt for speed
148              
149 1043         1090 my $limit = int(sqrt($value));
150              
151 1043         1286 for (my $p = 3; $p <= $limit; $p += 2) {
152 4940 100       7715 if (($value % $p) == 0) {
153 698 100       747 if (($p & 3) == 1) {
154             ### found 4k+1 prime: $p
155 193 50       225 if ($pythagorean_type eq 'all') {
156 0         0 return 1;
157             }
158             } else {
159             ### found 4k+3 prime: $p
160 505 100       536 if ($pythagorean_type eq 'primitive') {
161 501         608 return 0;
162             }
163             }
164              
165 197         130 do {
166 243         324 $value /= $p;
167             } while (($value % $p) == 0);
168              
169 197         265 $limit = int(sqrt($value)); # new smaller limit
170             ### divide out prime: "$p new limit $limit"
171             }
172             }
173              
174             # $value now 1 or prime
175 542 100       545 if ($pythagorean_type eq 'primitive') {
176             # all, check this isn't a 4k+3 prime
177 520         790 return ($value % 4) != 3;
178             } else {
179             # all, last chance to see a 4k+1 prime
180 22   100     64 return ($value > 1
181             && ($value % 4) == 1);
182             }
183             }
184              
185             1;
186             __END__