1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
|
[< Previous 161](https://github.com/drbaggy/perlweeklychallenge-club/tree/master/challenge-161/james-smith) |
[Next 163 >](https://github.com/drbaggy/perlweeklychallenge-club/tree/master/challenge-163/james-smith)
# The Weekly Challenge 162
You can find more information about this weeks, and previous weeks challenges at:
https://theweeklychallenge.org/
If you are not already doing the challenge - it is a good place to practise your
**perl** or **raku**. If it is not **perl** or **raku** you develop in - you can
submit solutions in whichever language you feel comfortable with.
You can find the solutions here on github at:
https://github.com/drbaggy/perlweeklychallenge-club/tree/master/challenge-162/james-smith
# Challenge 1 - Validate ISBN-13
***Write a script to generate the check digit of given ISBN-13 code. Checksum is generated by summing the numbers in the odd positions with 3 times the sum of the numbers in the even positions. The checksum digit is the number you would add to get a total of 0***
## The solution
Rather than computing the checksum - as we have ISBN numbers with the checksum we will validate the numbers {checking the checksum calculated is equal to the last digit in the ISNB number].
```perl
sub validate_isbn13 {
my @p = ( my $s = 0, grep {/\d/} split //, $_[0] );
$s += 3*shift(@p) + shift @p while @p;
!($s%10);
}
sub checksum_isbn13 {
my($s,@p) = ( 0, @{[grep {/\d/} split //, $_[0]]}[0..11] ); ## Remove checksum if present..
$s -= shift(@p) + 3*shift @p while @p;
$s%10;
}
```
# Challenge 2 - Wheatstone-Playfair
***Implement encryption and decryption using the Wheatstone-Playfair cipher.***
I'm not going to try and explain this here - but refer you to wikipedia https://en.wikipedia.org/wiki/Playfair_cipher
First we note the only difference between encryption and decryption is the direction we move left->right up->down of *vv*, so we can implement these with a single "function" and two wrappers "emcrypt" and "decrypt". Often two way ciphers have this feature.
We first create a hash and array which map the letter to their position and their position to the letter. This makes the future calculations easier
```perl
sub encrypt { return _crypt( 1,@_); }
sub decrypt { return _crypt(-1,@_); }
sub _crypt {
my($off,$key,$p,$out,@r,%l) = (shift,shift,0,''); ## Initialise variables and get mapping...
($_ eq 'j' && ($_='i')), exists $l{$_} || ($l{$_}=[int $p/5,($p++)%5]) for grep { /[a-z]/ } split(//,$key),'a'..'i','k'..'z';
$r[$l{$_}[0]][$l{$_}[1]]=$_ for keys %l;
my @seq = grep {/[a-z]/} split //, shift =~ s{j}{i}gr; ## Prep sequence
while(my($m,$n)=splice @seq,0,2) { ## Loop through letter pairs
unshift(@seq,$n), $n='x' if $n && $n eq $m and $n ne 'x'; ## Deal with case when both letters the same
$n ||= 'x'; ## Pad if required...
$out.= $l{$m}[0] eq $l{$n}[0] ? $r[ $l{$m}[0] ][($l{$m}[1]+$off)%5] . $r[ $l{$n}[0] ][($l{$n}[1]+$off)%5]
: $l{$m}[1] eq $l{$n}[1] ? $r[($l{$m}[0]+$off)%5][ $l{$m}[1] ] . $r[($l{$n}[0]+$off)%5][ $l{$n}[1] ]
: $r[ $l{$m}[0] ][ $l{$n}[1] ] . $r[ $l{$n}[0] ][ $l{$m}[1] ]
;
}
$out;
}
```
|