File Coverage

blib/lib/Math/NumSeq/PythagoreanHypots.pm
Criterion Covered Total %
statement 85 87 97.7
branch 20 22 90.9
condition 8 9 88.8
subroutine 17 17 100.0
pod 4 4 100.0
total 134 139 96.4


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   3321 use 5.004;
  1         4  
  1         53  
20 1     1   8 use strict;
  1         2  
  1         65  
21              
22 1     1   5 use vars '$VERSION', '@ISA';
  1         4  
  1         92  
23             $VERSION = 71;
24              
25 1     1   5 use Math::NumSeq 7; # v.7 for _is_infinite()
  1         15  
  1         59  
26             @ISA = ('Math::NumSeq');
27             *_is_infinite = \&Math::NumSeq::_is_infinite;
28              
29 1     1   7 use Math::NumSeq::Primes;
  1         1  
  1         29  
30 1     1   6 use Math::Prime::XS 0.23 'is_prime'; # version 0.23 fix for 1928099
  1         23  
  1         83  
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   4 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         2  
  1         6  
38 1     1   7 use constant characteristic_increasing => 1;
  1         11  
  1         51  
39 1     1   6 use constant characteristic_integer => 1;
  1         2  
  1         53  
40 1     1   6 use constant values_min => 5;
  1         2  
  1         50  
41 1     1   5 use constant i_start => 1;
  1         2  
  1         137  
42              
43 1         6 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   6 ];
  1         3  
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 9 my ($self) = @_;
77 2         9 return $oeis_anum{$self->{'pythagorean_type'}};
78             }
79              
80             #------------------------------------------------------------------------------
81              
82             sub rewind {
83 6     6 1 2070 my ($self) = @_;
84 6         17 $self->{'array'} = [];
85 6         193 $self->{'i'} = $self->i_start;
86 6         28 $self->{'hi'} = 1;
87             }
88              
89             sub next {
90 24     24 1 1405 my ($self) = @_;
91 24         48 my $array = $self->{'array'};
92 24         28 for (;;) {
93 28 100       84 if (defined (my $value = shift @$array)) {
94 24         90 return ($self->{'i'}++,
95             $value);
96             }
97 4         9 my $lo = $self->{'hi'} + 1;
98 4         11 $self->{'hi'} = my $hi = $lo + 1000;
99 4         15 @$array = _hypots_block ($self, $lo, $hi);
100             }
101             }
102              
103             sub _hypots_block {
104 4     4   10 my ($self, $lo, $hi) = @_;
105              
106 4 100       17 if ($self->{'pythagorean_type'} eq 'primitive') {
107 2         74 return grep {$self->pred($_)} $lo .. $hi;
  2002         3779  
108              
109             } else {
110 2         3 my %hypots;
111 2         10 foreach my $p (grep {($_ & 3)==1}
  336         664  
112             Math::NumSeq::Primes::_primes_list(2, $hi)) {
113 160         376 @hypots{map {$_*$p}
  1296         2939  
114             (int(($lo+$p-1)/$p) .. int($hi/$p))
115             } = ();
116             }
117 2         182 return sort {$a<=>$b} keys %hypots;
  8991         8596  
118             }
119             }
120              
121             sub pred {
122 2110     2110 1 3310 my ($self, $value) = @_;
123             ### pred: $value
124              
125 2110 100 66     6724 if ($value < 5
      100        
126             || _is_infinite($value)
127             || $value != int($value)) {
128 53         127 return 0;
129             }
130              
131 2057         3845 my $pythagorean_type = $self->{'pythagorean_type'};
132             ### $pythagorean_type
133              
134 2057 100       4136 unless ($value % 2) {
135             ### even ...
136 1024 100       1898 if ($pythagorean_type ne 'all') {
137             ### primitive and prime never even ...
138 1014         2468 return 0;
139             }
140 10         9 do {
141 18         36 $value /= 2;
142             } until ($value % 2);
143             }
144 1043 50       1855 unless ($value <= 0xFFFF_FFFF) {
145 0         0 return undef;
146             }
147 1043         1193 $value = "$value"; # numize Math::BigInt for speed
148              
149 1043         1857 my $limit = int(sqrt($value));
150              
151 1043         2150 for (my $p = 3; $p <= $limit; $p += 2) {
152 4940 100       11921 if (($value % $p) == 0) {
153 698 100       1176 if (($p & 3) == 1) {
154             ### found 4k+1 prime: $p
155 193 50       358 if ($pythagorean_type eq 'all') {
156 0         0 return 1;
157             }
158             } else {
159             ### found 4k+3 prime: $p
160 505 100       974 if ($pythagorean_type eq 'primitive') {
161 501         1206 return 0;
162             }
163             }
164              
165 197         217 do {
166 243         573 $value /= $p;
167             } while (($value % $p) == 0);
168              
169 197         464 $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       961 if ($pythagorean_type eq 'primitive') {
176             # all, check this isn't a 4k+3 prime
177 520         1671 return ($value % 4) != 3;
178             } else {
179             # all, last chance to see a 4k+1 prime
180 22   100     91 return ($value > 1
181             && ($value % 4) == 1);
182             }
183             }
184              
185             1;
186             __END__