by Arne Sommer

# Cyclops Be Dammed with Raku

[196] Published 13. August 2022.

This is my response to The Weekly Challenge #177.

## Challenge #177.1: Damm Algorithm

You are given a positive number, `\$n`.

Write a script to validate the given number against the included check digit.

Example 1: ```Input: \$n = 5724 Output: 1 as it is valid number ``` Example 2: ```Input: \$n = 5727 Output: 0 as it is invalid number ```

File: damm ```#! /usr/bin/env raku unit sub MAIN (Int \$value where \$value > 1, :v(:\$verbose)); # [1] my @ot = ( (0,3,1,7,5,9,8,6,4,2), # [2] (7,0,9,2,1,5,4,8,6,3), (4,2,0,6,8,7,1,3,5,9), (1,7,5,0,9,8,3,4,2,6), (6,1,2,3,0,4,5,9,7,8), (3,6,7,4,2,0,9,5,8,1), (5,8,6,9,7,2,0,1,3,4), (8,9,4,5,3,6,2,0,1,7), (9,4,3,8,6,1,7,2,0,5), (2,5,8,1,4,3,6,7,9,0), ); my \$interim-digit = 0; # [3] for \$value.comb -> \$digit # [4] { print ": D:\$digit I:\$interim-digit" if \$verbose; \$interim-digit = @ot[\$interim-digit][\$digit]; # [5] say " -> \$interim-digit" if \$verbose; } say \$interim-digit == 0 ?? "valid" !! "invalid"; # [6] ```

[1] A positive integer, not number as incorrectly stated in the challenge.

[2] We need the values from the matrix in the wikipedia article, so here they are. As a matrix, as that is indeed a handy format.

[3] The initial interim digit value is zero.

[4] Iterate over each digit in the number (i.e. integer).

[5] Get the new interim digit.

[6] The final interim digit (after we have run out of digits in [4]) has to be zero for the number to be valid. I have chosen to print the result as a text.

Running it:

```\$ ./damm 5724 valid \$ ./damm 5727 invalid ```

Verbose mode gives us the intermediary values as shown in the wikipedia article, albeit oriented in a different fashion:

```\$ ./damm -v 5724 : D:5 I:0 -> 9 : D:7 I:9 -> 7 : D:2 I:7 -> 4 : D:4 I:4 -> 0 valid \$ ./damm -v 5727 : D:5 I:0 -> 9 : D:7 I:9 -> 7 : D:2 I:7 -> 4 : D:7 I:4 -> 9 invalid ```

Ok. Let us try a Damm Sequence:

File: damm-seq ```#! /usr/bin/env raku unit sub MAIN (Int \$count where \$count > 0 = 20); my \$ds := (10 .. Inf).grep: *.&is-damm; say \$ds[^\$count].join(", "); sub is-damm (Int \$value) # [1] { my @ot = ( (0,3,1,7,5,9,8,6,4,2), (7,0,9,2,1,5,4,8,6,3), (4,2,0,6,8,7,1,3,5,9), (1,7,5,0,9,8,3,4,2,6), (6,1,2,3,0,4,5,9,7,8), (3,6,7,4,2,0,9,5,8,1), (5,8,6,9,7,2,0,1,3,4), (8,9,4,5,3,6,2,0,1,7), (9,4,3,8,6,1,7,2,0,5), (2,5,8,1,4,3,6,7,9,0), ); my \$interim-digit = 0; for \$value.comb -> \$digit { \$interim-digit = @ot[\$interim-digit][\$digit]; } return \$interim-digit == 0; } ```

[1] Nothing much to see here. I have wrapped the Damm detection code in a procedure, so this looks like the sequences shown in several of my responses to recent challenges.

Running it:

```\$ ./damm-seq 13, 21, 37, 45, 59, 68, 76, 84, 92, 101, 117, 125, 130, 149, 158, 163, 174, \ 182, 196, 207 \$ ./damm-seq 200 13, 21, 37, 45, 59, 68, 76, 84, 92, 101, 117, 125, 130, 149, 158, 163, 174, \ 182, 196, 207, 210, 229, 232, 241, 255, 264, 278, 286, 293, 308, 319, 324, \ 335, 343, 356, 362, 370, 381, 397, 403, 416, 427, 434, 442, 450, 469, 475, \ 488, 491, 502, 515, 528, 531, 544, 553, 566, 577, 589, 590, 609, 614, 623, \ 638, 646, 651, 667, 672, 680, 695, 705, 718, 726, 739, 747, 752, 760, 771, \ 783, 794, 806, 811, 822, 833, 840, 854, 865, 879, 887, 898, 904, 912, 920, \ 936, 948, 957, 961, 973, 985, 999, 1007, 1010, 1029, 1032, 1041, 1055, \ 1064, 1078, 1086, 1093, 1108, 1119, 1124, 1135, 1143, 1156, 1162, 1170, \ 1181, 1197, 1203, 1216, 1227, 1234, 1242, 1250, 1269, 1275, 1288, 1291, \ 1300, 1313, 1321, 1337, 1345, 1359, 1368, 1376, 1384, 1392, 1402, 1415, \ 1428, 1431, 1444, 1453, 1466, 1477, 1489, 1490, 1509, 1514, 1523, 1538, \ 1546, 1551, 1567, 1572, 1580, 1595, 1601, 1617, 1625, 1630, 1649, 1658, \ 1663, 1674, 1682, 1696, 1706, 1711, 1722, 1733, 1740, 1754, 1765, 1779, \ 1787, 1798, 1804, 1812, 1820, 1836, 1848, 1857, 1861, 1873, 1885, 1899, \ 1905, 1918, 1926, 1939, 1947, 1952, 1960, 1971, 1983, 1994, 2008 ```

The last one shows that we get approximately one tenth of the numbers. (The approximation comes from the fact that we started at 10 (instead of 1), and may have stoppet just before - or after - a Damm number.) This is logical, as we get exactly one control digit (out of ten possibilities).

We have actually generated all (up to a point that is) the positive integers, postfixed with the Damm check digit. It would have been faster to implement it this way, as we then would only generate the Damm check digit for the values we show instead of verifying them for the 9 out of 10 that do not verify.

File: damm-seq-faster ```#! /usr/bin/env raku unit sub MAIN (Int \$count where \$count > 0 = 20); my \$ds := (1 .. Inf).map({ \$_ ~ is-damm(:get-check-digit, \$_) }); # [1] say \$ds[^\$count].join(", "); sub is-damm (Int \$value, :\$get-check-digit) # [2] { my @ot = ( (0,3,1,7,5,9,8,6,4,2), (7,0,9,2,1,5,4,8,6,3), (4,2,0,6,8,7,1,3,5,9), (1,7,5,0,9,8,3,4,2,6), (6,1,2,3,0,4,5,9,7,8), (3,6,7,4,2,0,9,5,8,1), (5,8,6,9,7,2,0,1,3,4), (8,9,4,5,3,6,2,0,1,7), (9,4,3,8,6,1,7,2,0,5), (2,5,8,1,4,3,6,7,9,0), ); my \$interim-digit = 0; for \$value.comb -> \$digit { \$interim-digit = @ot[\$interim-digit][\$digit]; } return \$get-check-digit # [3] ?? \$interim-digit !! \$interim-digit == 0; } ```

[1] `map` instead of `grep` this time.

[3] New or old behaviour? Behave accordingly.

Note that this is based on «damm-seq» (and not «damm-seq-faster»). The reason is that I presume that «is-prime» is faster than «is-damm». I may well be wrong...

File: damm-prime-seq ```#! /usr/bin/env raku unit sub MAIN (Int \$count where \$count > 0 = 20); my \$ds := (10 .. Inf).grep({ \$_.is-prime && \$_.&is-damm }); # [1] say \$ds[^\$count].join(", "); sub is-damm (Int \$value, :\$get-check-digit) # [2] { my @ot = ( (0,3,1,7,5,9,8,6,4,2), (7,0,9,2,1,5,4,8,6,3), (4,2,0,6,8,7,1,3,5,9), (1,7,5,0,9,8,3,4,2,6), (6,1,2,3,0,4,5,9,7,8), (3,6,7,4,2,0,9,5,8,1), (5,8,6,9,7,2,0,1,3,4), (8,9,4,5,3,6,2,0,1,7), (9,4,3,8,6,1,7,2,0,5), (2,5,8,1,4,3,6,7,9,0), ); my \$interim-digit = 0; for \$value.comb -> \$digit { \$interim-digit = @ot[\$interim-digit][\$digit]; } return \$get-check-digit ?? \$interim-digit !! \$interim-digit == 0; } ```

[1] I had to use the `({` ... `})` syntax, as I refer to the current value twice (now as `\$_`, instead of `*` if used only once, and without the braces).

[2] The same procedure as before, but used to verify (and not generate) the check digit.

Running it:

```\$ ./damm-prime-seq 13, 37, 59, 101, 149, 163, 229, 241, 293, 397, 491, 577, 739, 811, 887, \ 1093, 1181, 1291, 1321, 1453 ```

## Challenge #177.2: Palindromic Prime Cyclops

Write a script to generate `first 20 Palindromic Prime Cyclops Numbers`.

A cyclops number is a number with an odd number of digits that has a zero in the center only.

Output: ```101, 16061, 31013, 35053, 38083, 73037, 74047, 91019, 94049, 1120211, 1150511, 1160611, 1180811, 1190911, 1250521, 1280821, 1360631, 1390931, 1490941, 1520251 ```

Premature optimisation be damned, here we go again...

(That was a reference to last week.)

File: ppc ```#! /usr/bin/env raku unit sub MAIN (Int \$count where \$count > 0 = 20); my \$ppc := (1..Inf).map({ next if /0/; \$_ ~ "0" ~ \$_.flip }).grep: *.is-prime; # 1 ############# # 2 ############# # 3 ################## # 4 ############ say \$ppc[^\$count].join(", "); ```

[1] This should be familiar by now; a lazy sequence of integers from 1 to infinity.

[2] `map` is a loop in disguise, so using `next` inside it works (even though using `grep` is a more obvious choice. Normally. Here we use the `next` part to get rid of numbers containing at least one instance of the digit zero.

See docs.raku.org/syntax/next for more information about `next`.

[3] This part maps (yes, that is why the method is called `map`) the value into a new one - the original value, followed by a zero and the opriginal value reversed.

[4] We want primes only. Note the usage of `:` instead of parens. This works as this is the last part of the chained command. Also note the usage of `*` to refer to the current value. This works when we skip the curly braces. Use them, and go for `\$_` instead, if you want to.

Running it:

```\$ ./ppc 101, 16061, 31013, 35053, 38083, 73037, 74047, 91019, 94049, 1120211, 1150511, 1160611, 1180811, 1190911, 1250521, \ 1280821, 1360631, 1390931, 1490941, 1520251 ```

Spot on, except the line breaks. But that is surely just a presentation detail...

And that's it.