by Arne Sommer

# Lychrel Longing with Raku and Perl

 Published 7. November 2021.

This is my response to the Perl Weekly Challenge #137.

## Challenge #137.1: Long Year

Write a script to find all the years between `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.

Expected Output: ```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 ```
File: long-year ```#! /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; #  } say @long-years.join(", "); ```

 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) #  !! @long-years.join(", "); ```

 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.

### A Perl Version

This is straight forward translation of the first Raku version.

File: long-year-perl ```#! /usr/bin/env perl use strict; use warnings; use feature 'say'; use DateTime; #  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); ```

 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 ```

## Challenge #137.2: Lychrel Number

You are given a number, 10 <= `\$n` <= 1000.

Write a script to find out if the given number is Lychrel number. To keep the task simple, we impose the following rules:

```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.

Example 1: ```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); #  say + is-lychrel(\$n); #  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; #  } } ```

 Ensure that we get an integer withing the specified limits.

 Coerce the result to `0` or `1`, as I have chosen to let the procedure return `False` or `True`.

 Note the use of `is copy` so that we can change the variable in the procedure.

 Number of iterations.

 Add the reversed version of the value, which we get with `flip`.

See docs.raku.org/routine/flip for more information about `flip`.

 Do we have a palindrome? If so, it is not a Lychrel number.

 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(\$_) }); #  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; #  } } ```

 The sequence.

 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  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 ```

### Perl

This is a straight forward translation of the Raku version.

File: lychrel-number-perl ```#! /usr/bin/env perl use strict; use warnings; use feature 'say'; use feature 'signatures'; no warnings qw(experimental::signatures); my \$n = \$ARGV // ""; 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.