# RogerBW's Blog

Perl Weekly Challenge 135: Valid Middle 21 October 2021

I’ve been doing the Weekly Challenges. The latest involved digit substrings and securities validation. (Note that this is open until 24 October 2021.)

You are given an integer.

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

So the steps here are something like:

• take positive value of input
• convert to string
• make sure it's long enough and of an odd length
• find the middle character, and extract it and the characters to each side
• return them (I choose to do so as a string, since the first one might legitimately be 0)

Thus:

``````sub m3d {
my \$n=shift;
my \$m=abs(\$n);
\$m="\$m";
my \$l=length(\$m);
if (\$l < 3) {
return 'too short';
}
if (\$l % 2 == 0) {
return 'even number of digits';
}
return substr(\$m,int(\$l/2)-1,3);
}
``````

(This does mean that the test input `10` will return "too short" rather than "even number of digits" as given in the examples, but I find it hard to take that seriously as a problem. I'd rather do a straight comparison than a modulus as my first step.)

Given that I'm doing this cross-language and want to test for error messages I just stuff the error into the output string.

The other languages are similar. Some let me chain conversions, such as Rust's `let m=n.abs().to_string();` while some insist on a string slice being given as start-and-end rather than start-and-length (Python and Rust as far as I can tell). PostScript doesn't have early return so I end up using nested conditionals; I also need to take the log of the number to find out how long a string I need to put it in.

``````/m3d {
abs
dup log cvi 1 add string /sform exch def
sform cvs pop
sform length
dup 3 lt {
pop
(too short)
} {
dup 2 mod 0 eq {
pop
(even number of digits)
} {
2 idiv 1 sub
sform exch 3 getinterval
} ifelse
} ifelse
} def
``````

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.

The Wikipedia page gives a starting point, in that a valid one:

• is 7 characters long
• each character lies in [0-9A-Z] and isn't a vowel
• each character has a value (0-9 = 0-9, B=11, C=12, etc., not skipping vowels in the count)
• the sum of character values mulitiplied by position weights, mod 10, is zero.

The fiddly bit is validating the letters and getting their values. Rust is cleanest:

``````fn sedolv(sedol: &str) -> u8 {
if sedol.len() != 7 {
return 0;
}
let w=vec![1,3,1,7,3,9,1].into_iter();
let mut s=0;
let j=w.zip(sedol.chars().into_iter());
``````

So here I have two joined iterators, the character from the string and the weight assigned to that position. (I did this in Ruby too. Python apparently can manage it, but it works differently. Perl/Raku/Python got a simple index.)

Validating and decoding become relatively easy because I can just treat each character as a base-36 number.

``````    for x in j {
let n;
if x.1.is_digit(10) ||
(x.1.is_ascii_alphabetic() && x.1.is_ascii_uppercase() &&
x.1 != 'A' && x.1 != 'E' && x.1 != 'I' &&
x.1 != 'O' && x.1 != 'U') {
n=x.1.to_digit(36).unwrap();
} else {
return 0;
}
s += n*x.0;
}
if s % 10 == 0 {
return 1;
}
0
}
``````

In Perl the relevant work is done with regular expressions of course:

``````    if (\$c =~ /[0-9]/) {
\$n=\$c+0;
} elsif (\$c =~ /[B-Z]/ && \$c !~ /[EIOU]/) {
\$n=ord(\$c)-55;
} else {
return 0;
}
\$s+=\$n*\$w[\$i];
``````

Python probably has the base36 decoder somewhere, but:

``````    if sedol[i].isdigit():
n=int(sedol[i])
elif sedol[i].isalpha() and sedol[i].isupper() and not sedol[i] in "AEIOU":
n=ord(sedol[i])-55
else:
return 0
``````

And in PostScript, again lacking early return, I just invalidate and continue…

``````/sedolv {
/sedol exch def
/weight [ 1 3 1 7 3 9 1 ] def
/valid 1 def
/sum 0 def
sedol length 7 ne { 0 exit } if
0 1 6 {
dup
sedol exch get
valid 0 eq { exit } if
dup 48 ge exch dup 57 le 3 -1 roll and {
48 sub
} {
dup 66 ge exch dup 90 le 3 -1 roll and {
dup 69 eq { /valid 0 def } if
dup 73 eq { /valid 0 def } if
dup 79 eq { /valid 0 def } if
dup 85 eq { /valid 0 def } if
55 sub
} {
/valid 0 def
} ifelse
} ifelse
exch weight exch get mul sum add /sum exch def
} for
sum 10 mod 0 ne {
/valid 0 def
} if
valid
} def
``````

Full code on github.