### Perl Weekly Challenge: Week 135

#### Challenge 1:

**Middle 3-digits**

You are given an integer.

Write a script find out the middle 3-digits of the given integer, if possible otherwise throw sensible error.

##### Example 1

```
Input: $n = 1234567
Output: 345
```

##### Example 2

```
Input: $n = -123
Output: 123
```

##### Example 3

```
Input: $n = 1
Output: too short
```

##### Example 4

```
Input: $n = 10
Output: even number of digits
```

A nice and simple problem. Here's how I solved it in Perl.

First I got the input as a command line argument.

```
my $n = shift // die "Need an integer.\n";
```

Whether the number is positive or negative is irrelevant so I removed the initial `-`

if there was one.

```
$n =~ s/^\-//;
```

What's left should be all digits. If there is some other character like a `.`

for example, it is not an integer.

```
if ($n !~ /^\d+$/) {
die "Not an integer.\n";
}
```

For the next two tests, we need to know how many digits the integer has.

```
my $len = length $n;
```

If the integer has an even number of digits, we reject it.

```
if ($len % 2 == 0) {
die "Even number of digits\n";
}
```

If there are less than three digits in the integer, it is too short.

```
if ($len < 3) {
die "Too short.\n";
}
```

Now we can take out the middle three digits and print them.

```
say substr $n, ($len - 3) / 2, 3;
```

For the Raku version, the only substantial change I had to make is that function parameters are immutable so when I removed the initial hyphen, I had to assign the result to a new variable.

```
sub MAIN(
Int $N
) {
my $n = $N.subst(/^\-/, q{});
if $n !~~ /^ \d+ $/ {
die "$n Not an integer.\n";
}
my $len = $n.chars;
if $len % 2 == 0 {
die "Even number of digits\n";
}
if $len < 3 {
die "Too short.\n";
}
say $n.substr(($len - 3) / 2, 3);
}
```

#### Challenge 2:

**Validate SEDOL**

You are given 7-characters alphanumeric SEDOL.

Write a script to validate the given SEDOL. Print 1 if it is a valid SEDOL otherwise 0.

For more information about SEDOL, please checkout the wikipedia page.

##### Example 1

```
Input: $SEDOL = '2936921'
Output: 1
```

##### Example 2

```
Input: $SEDOL = '1234567'
Output: 0
```

##### Example 3

```
Input: $SEDOL = 'B0YBKL9'
Output: 1
```

I have used Perl for so many tasks like this over the years. It's the kind of thing the language is ideally suited for. I consolidated all the validation into a function which returns true or false values. (not true or false literals as Perl does nothave them.)

```
sub check {
my ($sedol) = @_;
```

An easy check is to make sure the prospective SEDOL is seven characters long:

```
if (length $sedol != 7) {
return undef;
}
```

...then we check if it is made up of allowed characters. The first six characters should either be digits or upper case letters except vowels. The last character must be a digit. This can be expressed as a regex. The character class of digits and allowed letters is kind of ungainly but the alternative would be to list them all out and I don't think that would have been any more readable.

```
if ($sedol !~ /^ [0-9B-DF-HJ-NP-TV-Z]{6} [0-9] $/x) {
return undef;
}
```

These are the weights assigned to each character in the SEDOL. The last one is superfluous but it doesn't hurt to leave it there.

```
my @weights = (1, 3, 1, 7, 3, 9, 1);
```

The SEDOL has to be split into an array of its constituent characters.

```
my @chars = split //, $sedol;
```

The first six characters are ordinalized (using the `ord()`

function natuarally) and multiplied by their respective weights and added to a running total. The sample javascript code had a simpler way of doing this by using the characters as base 36 numbers. I had
developed some code for base 35 way back in PWC 2 which I could have adapted but the `ord()`

method seemed easier. Because digits and upper-case letters are disjoint sets, they had to be treated separately.

```
my $sum = 0;
for my $i (0 .. 5) {
if (ord($chars[$i]) >= ord('0') && ord($chars[$i]) <= ord('9')) {
$sum += $chars[$i] * $weights[$i];
} else {
$sum += (ord($chars[$i]) - ord('A')) * $weights[$i];
}
}
```

The final sum is taken modulo 10. As this could still be greater than 10, modulo 10 is taken again. This results in a single digit which is compared to the last digit of the SEDOL. If it is the same, a true value is returned or a false value if it is not the same.

```
return ((10 - $sum % 10) % 10) == $chars[6];
}
```

This is the Raku version:

```
sub check(Str $sedol) {
if ($sedol.chars != 7) {
return False;
}
```

The main thing I wish to illustrate is the nice way the allowed character class is constructed below. It is so much more readable than the Perl version.

```
if ($sedol !~~ /^ <[0..9] + [A..Z] - [AEIOU]> ** 6 <[0..9]> $ /) {
return False;
}
my @weights = (1, 3, 1, 7, 3, 9, 1);
my @chars = $sedol.comb;
my $sum = 0;
for 0 .. 5 -> $i {
if (@chars[$i].ord >= '0'.ord && @chars[$i].ord <= '9'.ord) {
$sum += @chars[$i] * @weights[$i];
} else {
$sum += (@chars[$i].ord - 'A'.ord) * @weights[$i];
}
}
return ((10 - $sum % 10) % 10) == @chars[6];
}
```