aboutsummaryrefslogtreecommitdiff
path: root/challenge-162/james-smith/README.md
blob: 3ecb201fd9df179fb495b3ed3677b8912b28ad90 (plain)
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;
}
```