Perl Weekly Challenge 135.

My solutions (task 1, and task 2 ) to the The Weekly Challenge - 135.

Task 1: Middle 3-digits

Submitted by: Mohammad S Anwar
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

This seems simple enough for a oneliner: Take input from the command line, remove optional sign, get length, compute initial position, test length and output.

perl -E '($s=$i=$ARGV[0])=~s/^[+-]//; $l=length $s; $b=($l-3)/2;\
         say "$i->", $l%2==0?"Even":$l<3?"Short":substr $s, $b,3;' 1234567
perl -E '($s=$i=$ARGV[0])=~s/^[+-]//; $l=length $s; $b=($l-3)/2;\
         say "$i->", $l%2==0?"Even":$l<3?"Short":substr $s, $b,3;' -- -123
perl -E '($s=$i=$ARGV[0])=~s/^[+-]//; $l=length $s; $b=($l-3)/2;
         say "$i->", $l%2==0?"Even":$l<3?"Short":substr $s, $b,3;' 1
perl -E '($s=$i=$ARGV[0])=~s/^[+-]//; $l=length $s; $b=($l-3)/2;\
         say "$i->", $l%2==0?"Even":$l<3?"Short":substr $s, $b,3;' 10

Results:

1234567->345
-123->123
1->Short
10->Even

(Notice the -- in the -123 case, so that Perl doesn’t interpret the minus sign as flagging a Perl option.)

A full program might take the desired number of digits as an additional parameter.

# Perl weekly challenge 135
# Task 1: Middle 3-digits
#
# See https://wlmb.github.io/2021/10/20/PWC135/#task-1-middle-3-digits
use v5.12;
use warnings;
my @even_odd=qw(even odd);
say "Usage: ./ch-1.pl howmany number [number] ..." and exit unless @ARGV>1;
my $how_many= shift @ARGV;
my $parity=$how_many%2; # Desire an even or an odd number of digits?
foreach my $input(@ARGV){
    (my $digits=$input)=~s/^[+-]//; # Remove leading sign
    my $length=length $digits;
    my $begin=($length-$how_many)/2; # initial position
    my $output=
	$digits!~/^\d+$/        ? "Expected only digits"
	:$length%2!=$parity? "Expected an $even_odd[$parity] number of digits"
	:$length<$how_many ? "Expected more than $how_many digits"
	:substr($digits, $begin, $how_many). " (middle $how_many digits)";
    say "Input: $input\nOutput: $output";
}

Examples:

./ch-1.pl 3 1234567 -123 1 10

Results:

Input: 1234567
Output: 345 (middle 3 digits)
Input: -123
Output: 123 (middle 3 digits)
Input: 1
Output: Expected more than 3 digits
Input: 10
Output: Expected an odd number of digits

Other examples:

./ch-1.pl 3 1234567 12345678
./ch-1.pl 4 1234567 12345678

Results:

Input: 1234567
Output: 345 (middle 3 digits)
Input: 12345678
Output: Expected an odd number of digits
Input: 1234567
Output: Expected an even number of digits
Input: 12345678
Output: 3456 (middle 4 digits)

Task 2: Validate SEDOL

Submitted by: Mohammad S Anwar
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

According to the wikipedia article SEDOLS are words consisting of 7 characters. The first six are digits or consonants. The last one is a check digit. The weighted sum of the values of all the characters is congruent with 0 modulo 10 using the weights 1,3,1,7,3,9,1 and where the values are the positions occupied in the list 0..9,A..Z. I assume that lower case letters are valid and may be converted to uppercase before testing. I understand that old style SEDOLS had only digits while new style SEDOLS start with B or higher letter. Also, words starting in 9 are reserved for end user allocation; I guess they can contain consonants besides digits. If the first character is a digit other than 9, the word would correspond to an old style SEDOL, which had no letters, only digits. The first digit then indicates the origin. I use below all the information from the wikipedia page (as I understood it).

# Perl weekly challenge 135
# Task 2: Validate SEDOL
#
# See https://wlmb.github.io/2021/10/20/PWC135/#task-2-validate-sedol
use v5.12;
use warnings;
use List::Util qw(all sum0);
use List::MoreUtils qw(pairwise);
my @weights=(1,3,1,7,3,9,1);
my $i=0;
my %values=map {$_=> $i++} 0..9,"A".."Z"; # compute weights
map {$values{$_}=undef} split '', "AEIOU"; # remove vowels
my %origin;
$origin{6}="Asia or Africa";
@origin{0,3}=("UK or Ireland")x2;
@origin{4,5,7}=("Europe")x3;
$origin{2}="America";

foreach(@ARGV){
    say "Input: $_, Output: ", is_sedol($_);
}
sub is_sedol {
    my @s=split '', uc shift; # Assume lc is valid
    return "0, Need 7 chars" unless @s==7;
    return "0, Last char should be digit" unless $s[6]=~m/\d/;
    return "0, Invalid char" unless all {defined $values{$_}} @s; # valid chars
    my @v=@values{@s};
    return "0, Wrong check digit"
           unless (sum0 pairwise {$a*$b} @weights, @v)%10==0;
    return "1, End user SEDOL" if $v[0]==9; # Assume no other restriction
    return "1, New SEDOL" if $s[0] ge 'B';
    return "0, Only digits for old SEDOLs" unless  all {$_<10} @v;
    return "1, Old SEDOL, probably from $origin{$v[0]}" if defined $origin{$v[0]};
    return "1, Old SEDOL, unknown origin";
}

Examples:

./ch-2.pl 2936921 1234567 B0YBKL9

Results:

Input: 2936921, Output: 1, Old SEDOL, probably from America
Input: 1234567, Output: 0, Wrong check digit
Input: B0YBKL9, Output: 1, New SEDOL

Other examples:

./ch-2.pl 123 123456C ACDFGH5 1234564 9234565 92B4567 \
       BCDFGH4 bcdfgh4 1C34563 0234564 1234563 2234562 \
       3234561 4234560 5234569 6234568 7234567 8234566

Results:

Input: 123, Output: 0, Need 7 chars
Input: 123456C, Output: 0, Last char should be digit
Input: ACDFGH5, Output: 0, Invalid char
Input: 1234564, Output: 0, Wrong check digit
Input: 9234565, Output: 1, End user SEDOL
Input: 92B4567, Output: 1, End user SEDOL
Input: BCDFGH4, Output: 1, New SEDOL
Input: bcdfgh4, Output: 1, New SEDOL
Input: 1C34563, Output: 0, Only digits for old SEDOLs
Input: 0234564, Output: 1, Old SEDOL, probably from UK or Ireland
Input: 1234563, Output: 1, Old SEDOL, unknown origin
Input: 2234562, Output: 1, Old SEDOL, probably from America
Input: 3234561, Output: 1, Old SEDOL, probably from UK or Ireland
Input: 4234560, Output: 1, Old SEDOL, probably from Europe
Input: 5234569, Output: 1, Old SEDOL, probably from Europe
Input: 6234568, Output: 1, Old SEDOL, probably from Asia or Africa
Input: 7234567, Output: 1, Old SEDOL, probably from Europe
Input: 8234566, Output: 1, Old SEDOL, unknown origin
Written on October 20, 2021