This is my response to The Weekly Challenge #177.
$n
.
Please checkout the wikipedia page for information.
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.
See docs.raku.org/language/variables#The_:_twigil for more information about named parameters.
[3] New or old behaviour? Behave accordingly.
What about a Damm Prime Sequence, you may ask?
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
first 20 Palindromic Prime Cyclops Numbers
.
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.