This is my response to the Perl Weekly Challenge #137.
1900
and 2100
which is a Long Year
.
[UPDATED][2021-11-01 16:20:00]: For more information about Long Year
,
please refer to wikipedia.
1903, 1908, 1914, 1920, 1925,
1931, 1936, 1942, 1948, 1953,
1959, 1964, 1970, 1976, 1981,
1987, 1992, 1998, 2004, 2009,
2015, 2020, 2026, 2032, 2037,
2043, 2048, 2054, 2060, 2065,
2071, 2076, 2082, 2088, 2093,
2099
#! /usr/bin/env raku
unit sub MAIN (Int :$lower = 1900, Int :$upper = 2100);
my @long-years;
for $lower .. $upper -> $year
{
@long-years.push: $year if Date.new("$year-12-31").week-number == 53; # [1]
}
say @long-years.join(", ");
[1] Add the year to the list if it has 53 weeks. The
Date
class does the heavy lifting for us.
See
docs.raku.org/type/Date
for information about the Date
class.
Running it:
$ ./long-year
1903, 1908, 1914, 1920, 1925, 1931, 1936, 1942, 1948, 1953, 1959, 1964, 1970,\
1976, 1981, 1987, 1992, 1998, 2004, 2009, 2015, 2020, 2026, 2032, 2037, 2043,\
2048, 2054, 2060, 2065, 2071, 2076, 2082, 2088, 2093, 2099
That's ok-ish. Except for the missing newlines after every five entries in the challenge. Let us remedy that:
File: long-year-tabulated
#! /usr/bin/env raku
unit sub MAIN (Int :$lower = 1900, Int :$upper = 2100, :t(:$tabulated));
my @long-years;
for $lower .. $upper -> $year
{
@long-years.push: $year if Date.new("$year-12-31").week-number == 53;
}
my $i = 1;
say $tabulated
?? @long-years.map({ ($i++ %% 5) ?? "$_,\n" !! "$_, " }).join
!! @long-years.join(", ");
Running it:
$ ./long-year-tabulated -t
1903, 1908, 1914, 1920, 1925,
1931, 1936, 1942, 1948, 1953,
1959, 1964, 1970, 1976, 1981,
1987, 1992, 1998, 2004, 2009,
2015, 2020, 2026, 2032, 2037,
2043, 2048, 2054, 2060, 2065,
2071, 2076, 2082, 2088, 2093,
2099,
That is better. Except the trailing comma on the last line.
We can chop it off:
File: long-year-tabulated2
#! /usr/bin/env raku
unit sub MAIN (Int :$lower = 1900, Int :$upper = 2100, :t(:$tabulated));
my @long-years;
for $lower .. $upper -> $year
{
@long-years.push: $year if Date.new("$year-12-31").week-number == 53;
}
my $i = 1;
say $tabulated
?? @long-years.map({ ($i++ %% 5) ?? "$_,\n" !! "$_, " }).join.chop(2) # [1]
!! @long-years.join(", ");
[1] The very last character is a space. The offending comma is
the last but one. Thus we chop
off two characters.
See
docs.raku.org/routine/chop
for information about the chop
function.
A one-liner version:
File: long-year-oneliner
#! /usr/bin/env raku
say (1900..2100).grep({ Date.new("$_-12-31").week-number == 53 }).join(", ");
The output is also a one-liner, in a bout of poetic justice.
#! /usr/bin/env perl
use strict;
use warnings;
use feature 'say';
use DateTime; # [1]
my $lower = 1900;
my $upper = 2100;
my @long_years;
for my $year ($lower .. $upper)
{
push(@long_years, $year)
if DateTime->new(year => $year, month => 12, day => 31)->week_number() == 53;
}
say join(", ", @long_years);
[1] There are quite a lot of time and/or date handling modules. I have chosen «DateTime».
Running it gives the same result as the Raku version:
$ ./long-year-perl
1903, 1908, 1914, 1920, 1925, 1931, 1936, 1942, 1948, 1953, 1959, 1964, 1970,\
1976, 1981, 1987, 1992, 1998, 2004, 2009, 2015, 2020, 2026, 2032, 2037, 2043,\
2048, 2054, 2060, 2065, 2071, 2076, 2082, 2088, 2093, 2099
$n
<= 1000.
a. Stop if the number of iterations reached 500.
b. Stop if you end up with number >= 10_000_000.
[UPDATED][2021-11-01 16:20:00]: If you stop because of any of the above two rules
then we expect 1
as an output.
According to wikipedia:
A Lychrel number is a natural number that cannot form a palindrome through the iterative process of repeatedly reversing its digits and adding the resulting numbers.
Input: $n = 56
Output: 0
After 1 iteration, we found palindrome number.
56 + 65 = 121
Example 2:
Input: $n = 57
Output: 0
After 2 iterations, we found palindrome number.
57 + 75 = 132
132 + 231 = 363
Example 3:
Input: $n = 59
Output: 0
After 3 iterations, we found palindrome number.
59 + 95 = 154
154 + 451 = 605
605 + 506 = 1111
File: lychrel-number
#! /usr/bin/env raku
unit sub MAIN (Int $n where 10 <= $n <= 1000); # [1]
say + is-lychrel($n); # [2]
sub is-lychrel ($current is copy) # [3]
{
my $i = 0; # [4]
loop
{
$current = $current + $current.flip; # [5]
return False if $current == $current.flip; # [6]
return True if $i++ == 500 || $current >= 10_000_000; # [7]
}
}
[1] Ensure that we get an integer withing the specified limits.
[2] Coerce the result to 0
or 1
, as I have chosen to let
the procedure return False
or True
.
[3] Note the use of is copy
so that we can change the variable in the
procedure.
[4] Number of iterations.
[5] Add the reversed version of the value, which we get with
flip
.
See
docs.raku.org/routine/flip for more information about flip
.
[6] Do we have a palindrome? If so, it is not a Lychrel number.
[7] Give in eventually, and assume that it is a Lychrel number.
Running it:
$ ./lychrel-number 56
0
$ ./lychrel-number 57
0
$ ./lychrel-number 59
0
That was not excactly exciting.
Let us have a go at the Lychrel numbers, as a sequence:
File: lychrel-sequence
#! /usr/bin/env raku
unit sub MAIN (Int $n where $n > 0);
my $lychrel := (10 .. *).grep({ is-lychrel($_) }); # [1]
say $lychrel[^$n].join(", ");
sub is-lychrel ($current is copy)
{
my $i = 0;
loop
{
$current = $current + $current.flip;
return False if $current == $current.flip;
return True if $i++ == 500 || $current >= 10_000_000; # [3]
}
}
[1] The sequence.
[2] Print (after computing) the requested number of values.
Running it:
$ ./lychrel-sequence 10
89, 98, 167, 177, 187, 196, 266, 276, 286, 295
$ ./lychrel-sequence 100
89, 98, 167, 177, 187, 196, 266, 276, 286, 295, 365, 375, 385, 394, 464, \
474, 484, 493, 563, 573, 583, 592, 662, 672, 682, 689, 691, 739, 761, 771, \
781, 788, 790, 829, 838, 849, 860, 869, 870, 879, 880, 887, 899, 928, 937, \
948, 968, 978, 986, 989, 998, 999, 1297, 1387, 1397, 1477, 1487, 1495, \
1496, 1497, 1567, 1577, 1585, 1586, 1587, 1657, 1667, 1675, 1676, 1677, \
1747, 1757, 1765, 1766, 1767, 1792, 1797, 1798, 1837, 1847, 1855, 1856, \
1857, 1882, 1887, 1888, 1894, 1897, 1927, 1937, 1945, 1946, 1947, 1972, \
1977, 1978, 1984, 1987, 1991, 1995
Note that the termination in [3] will give us false positives, as 89 isn't a Lychrel number (according to the wikipedia article). But we have done as requested by the challenge.
We can extend the original program, with a «sort of like the sequence» mode. Use the «-u» sommand line option to get all the values upto the specified value. I have added verbose mode at the same time.
File: lychrel-number-upto
#! /usr/bin/env raku
unit sub MAIN (Int $n where 10 <= $n <= 1000, :u(:$upto), :v(:$verbose));
$upto
?? (10 .. $n).map({ say "$_ -> { + is-lychrel($_) }" })
!! say + is-lychrel($n);
sub is-lychrel ($current is copy)
{
my $i = 0;
loop
{
my $flipped = $current.flip;
say ": $current + $flipped = { $current + $flipped }" if $verbose;
$current = $current + $current.flip;
return False if $current == $current.flip;
return True if $i++ == 500 || $current >= 10_000_000;
}
}
Running it (with abridged output):
$ ./lychrel-number-upto -u 100
10 -> 0
11 -> 0
12 -> 0
13 -> 0
…
97 -> 0
98 -> 1
99 -> 0
100 -> 0
With verbose mode, on a single value:
$ ./lychrel-number-upto -v 89
: 89 + 98 = 187
: 187 + 781 = 968
: 968 + 869 = 1837
: 1837 + 7381 = 9218
: 9218 + 8129 = 17347
: 17347 + 74371 = 91718
: 91718 + 81719 = 173437
: 173437 + 734371 = 907808
: 907808 + 808709 = 1716517
: 1716517 + 7156171 = 8872688
: 8872688 + 8862788 = 17735476
1
#! /usr/bin/env perl
use strict;
use warnings;
use feature 'say';
use feature 'signatures';
no warnings qw(experimental::signatures);
my $n = $ARGV[0] // "";
die "Please specify an integer in the range 10 .. 1000"
unless $n =~ /^[1-9]\d*$/;
die "Please specify an integer in the range 10 .. 1000"
if $n < 10 || $n > 1000;
say is_lychrel($n);
sub is_lychrel ($current)
{
my $i = 0;
while (1)
{
$current = $current + reverse $current;
return 0 if $current == reverse $current;
return 1 if $i++ == 500 || $current >= 10_000_000;
}
}
Running it gives the same result as the Raku version:
$ ./lychrel-number-perl 56
0
$ ./lychrel-number-perl 57
0
$ ./lychrel-number-perl 59
0
And that's it.