File Coverage

blib/lib/Math/Recaman.pm
Criterion Covered Total %
statement 53 63 84.1
branch 14 18 77.7
condition 9 13 69.2
subroutine 10 13 76.9
pod 2 2 100.0
total 88 109 80.7


line stmt bran cond sub pod time code
1             package Math::Recaman;
2              
3 5     5   595353 use 5.006;
  5         21  
4 5     5   70 use strict;
  5         34  
  5         166  
5 5     5   26 use warnings;
  5         8  
  5         384  
6              
7 5     5   52 use Exporter qw/import/;
  5         25  
  5         707  
8              
9             our $VERSION = '1.6';
10             our $USING_INTSPAN;
11             our @EXPORT_OK=qw(recaman recaman_a008336);
12              
13             BEGIN {
14 5     5   15 eval { require Set::IntSpan };
  5         853  
15 5         16 $USING_INTSPAN=0;
16 5 50       3472 unless ($@) {
17 0         0 Set::IntSpan->import();
18 0         0 $USING_INTSPAN = 1;
19             }
20             }
21              
22             our @checks = (0, 1, 3, 6, 2, 7, 13, 20, 12, 21,
23             11, 22, 10, 23, 9, 24, 8, 25, 43,
24             62, 42, 63, 41, 18, 42, 17, 43, 16,
25             44, 15, 45, 14, 46, 79, 113, 78, 114,
26             77, 39, 78, 38, 79, 37, 80, 36, 81,
27             35, 82, 34, 83, 33, 84, 32, 85, 31,
28             86, 30, 87, 29, 88, 28, 89, 27, 90,
29             26, 91, 157, 224, 156, 225, 155);
30              
31             our @a008336_checks = (1, 1, 2, 6, 24, 120, 20, 140, 1120, 10080, 1008, 11088, 924,
32             12012, 858, 12870, 205920, 3500640, 194480, 3695120,
33             184756, 3879876, 176358, 4056234, 97349616, 2433740400,
34             93605400, 2527345800, 90262350, 2617608150, 87253605,
35             2704861755, 86555576160, 2856334013280);
36              
37             =encoding utf8
38              
39             =head1 NAME
40              
41             Math::Recaman - Calculate numbers in Recamán's sequence
42              
43             =head1 SYNOPSIS
44              
45             use Math::Recaman qw(recaman);
46              
47             recaman(100); # print the first 100 numbers in Recamán's sequence
48             recaman(1000, sub { push @nums, shift }); # collect the first 1000 numbers into an array
49             my @array = recaman(10); # Returns the first 10 Recaman numbers as an array
50              
51             =head1 DESCRIPTION
52              
53             Recamán's sequence is a well known sequence defined by a recurrence relation.
54              
55             It is named after its inventor, Colombian mathematician Bernado Recamán Santos by Neil Sloane,
56             creator of the On-Line Encyclopedia of Integer Sequences (OEIS). The OEIS entry for this sequence is A005132.
57              
58             The sequence is defined as
59              
60             aₙ = 0 if n = 0
61             aₙ = aₙ₋₁ - n if aₙ₋₁ - n > 0 and is not already in the sequence
62             aₙ = aₙ₋₁ + n otherwise
63              
64             It is known to produce quite aethetically pleasing outputs if plotted as an image or as music.
65              
66             See more:
67              
68             =over 4
69              
70             =item OEIS
71              
72             L
73              
74             =item Wikipedia
75              
76             L
77              
78             =item Numberphile on YouTube
79              
80             L
81              
82             =back
83              
84             =head1 METHODS
85              
86             =head2 recaman [callback]
87              
88             Takes a target number to calculate to. If nothing is given the method returns immediately.
89              
90             By default it prints each number out on a new line.
91              
92             You can optionally pass in a anonymous subroutine which will be called for each new number in the sequence
93             with the arguments C, C and C.
94              
95             If you do not pass an anonymous subroutine and if you call this subroutine expecting an array in return then nothing will be printed.
96              
97             =cut
98              
99             sub recaman {
100 2     2 1 384350 my @seen;
101             my @values;
102              
103 2   50     12 my $target = shift || return;
104 2   66 10   17 my $callback = shift || (wantarray ? sub { push @values, shift } : sub { print $_[0]."\n" });
  10         18  
  0         0  
105              
106 2     81   12 my $increment = sub { $seen[$_[0]]++ };
  81         166  
107 2     54   9 my $present = sub { $seen[$_[0]] };
  54         191  
108 2         5 my $set;
109 2 50       6 if ($USING_INTSPAN) {
110 0         0 $set = Set::IntSpan->new;
111 0     0   0 $increment = sub { $set->insert($_[0]) };
  0         0  
112 0     0   0 $present = sub { $set->member($_[0]) };
  0         0  
113             }
114              
115 2         4 my $num = 1;
116 2         4 my $pointer = 0;
117 2         4 my $max = 0;
118 2         9 while ($num<=$target) {
119 81         175 $increment->($pointer);
120 81 50       250 $callback->($pointer, $num, $max) if defined $callback;
121 81         43773 my $next = $pointer - $num;
122 81 100 100     299 $next = $pointer+$num if $next<0 || $present->($next);
123 81         109 $pointer = $next;
124 81         95 $num++;
125 81 100       230 $max = $pointer if $pointer>$max;
126             }
127 2         35 return @values;
128             }
129              
130             =head2 recaman_a008336 [callback]
131              
132             There is another sequence invented by Recamán, less known, defined as:
133              
134             a₁ = 1
135             aₙ₊₁ = aₙ/n if n divides aₙ
136             aₙ₊₁ = naₙ otherwise
137              
138             Takes a target number to calculate to. If nothing is given the method returns immediately.
139              
140             By default it prints each number out on a new line.
141              
142             You can optionally pass in a anonymous subroutine which will be called for each new number in the sequence
143             with the arguments C, C and C.
144              
145             If you do not pass an anonymous subroutine and if you call this subroutine expecting an array in return then nothing will be printed.
146              
147             =cut
148             sub recaman_a008336 {
149 2     2 1 239298 my @values;
150              
151 2   100     14 my $target = shift || return 0;
152 1   33 0   4 my $callback = shift || (wantarray ? sub { push @values, shift } : sub { print $_[0]."\n" });
  0            
  0            
153              
154 1 50       5 return 0 if $target<1;
155              
156 1         3 my $num = 0;
157 1         2 my $pointer = 0;
158 1         3 my $max = 0;
159 1         4 while ($num<$target) {
160 34 100       121 if ($num < 2) {
    100          
161 2         4 $pointer = 1;
162             } elsif (($pointer%$num)==0) {
163 10         41 $pointer = $pointer/$num;
164             } else {
165 22         38 $pointer = $pointer*$num;
166             }
167             # print "$num) $pointer\n";
168 34         127 $callback->($pointer, $num, $max);
169 34         32393 $num++;
170 34 100       194 $max = $pointer if $pointer>$max;
171             }
172 1         5 return @values;
173             }
174              
175             =head1 Using Set::IntSpan
176              
177             If the module L is installed then that will be used for keeping track of the sequence.
178              
179             This should make it more efficient for very long sequences.
180              
181             You can check to see if the module is using L by checking the variable C<$Math::Recaman::USING_INTSPAN>.
182              
183             You can disable using the module even if it's installed by setting the variable C<$Math::Recaman::USING_INTSPAN> to C<0> before calling the C function.
184              
185             =cut
186              
187             =head1 AUTHOR
188              
189             Simon Wistow, C<< >>
190              
191             =head1 BUGS
192              
193             Please report any bugs or feature requests to C, or through
194             the web interface at L. I will be notified, and then you'll
195             automatically be notified of progress on your bug as I make changes.
196              
197             You can also open issues on GitHub at L.
198              
199             =head1 VERSION
200              
201             Version 0.01
202              
203             =cut
204              
205             =head1 SUPPORT
206              
207             You can find documentation for this module with the perldoc command.
208              
209             perldoc Math::Recaman
210              
211             You can also look for information at:
212              
213             =over 4
214              
215             =item * RT: CPAN's request tracker (report bugs here)
216              
217             L
218              
219             =item * CPAN Ratings
220              
221             L
222              
223             =item * Search CPAN
224              
225             L
226              
227             =item * GitHub
228              
229             L
230              
231             =back
232              
233             =head1 LICENSE AND COPYRIGHT
234              
235             This software is Copyright (c) 2024 by Simon Wistow.
236              
237             This is free software, licensed under:
238              
239             The Artistic License 2.0 (GPL Compatible)
240              
241             =cut
242              
243             1; # End of Math::Recaman