SEDOLs

From Rosetta Code
(Redirected from SEDOL)
Jump to: navigation, search
Task
SEDOLs
You are encouraged to solve this task according to the task description, using any language you may know.

For each number list of 6-digit SEDOLs, calculate and append the checksum digit.

That is, given this input:
710889
B0YBKJ
406566
B0YBLH
228276
B0YBKL
557910
B0YBKR
585284
B0YBKT
B00030
Produce this output:
7108899
B0YBKJ7
4065663
B0YBLH2
2282765
B0YBKL9
5579107
B0YBKR5
5852842
B0YBKT7
B000300

For extra credit, check each input is correctly formed, especially with respect to valid characters allowed in a SEDOL string.

C.f. Luhn test

Contents

[edit] ActionScript

//Return the code corresponding to a given character.
//ActionScript does not have a character type, so 1-digit strings
//are used instead
function toSEDOLCode(char:String):uint {
//Make sure only a single character was sent.
if(char.length != 1)
throw new Error("toSEDOL expected string length of 1, got " + char.length);
//Character is uppercase
if (char >= "A" && char <= "Z") {
return SEDOL.charCodeAt() + 10 - "A".charCodeAt();
}
//Character is numeric
else if (char >= "0" && char <= "9"){
return uint(char);
}
//Error: character is neither numeric nor uppercase
else{
throw new Error("toSEDOLCode expected numeric or uppercase character, recieved " + char);
}
}
//Calculate the weighted sum for the SEDOL.
function toSum(str:String):uint {
if(str.length != 6)
throw new Error("toSum expected length 6, recieved " + str.length);
var sum:uint=0;
for (var i:uint = 0; i < str.length; i++)
sum+=toSEDOLCode(str.charAt(i))*[1,3,1,7,3,9][i];
return sum;
}
//Calculate the check digit from the weighted sum.
function toCheck(num:int):uint
{
return (10 -(num % 10)) % 10;
}
//Print the SEDOL with the check digit added.
function printWithCheck(SEDOL:String):void
{
trace(SEDOL + toCheck(toSum(SEDOL)));
}
printWithCheck("710889");
printWithCheck("B0YBKJ");
printWithCheck("406566");
printWithCheck("B0YBLH");
printWithCheck("228276");
printWithCheck("B0YBKL");
printWithCheck("557910");
printWithCheck("B0YBKR");
printWithCheck("585284");
printWithCheck("B0YBKT");
printWithCheck("B00030");

[edit] Ada

with Ada.Text_IO;  use Ada.Text_IO;
 
procedure Test_SEDOL is
 
subtype SEDOL_String is String (1..6);
type SEDOL_Sum is range 0..9;
 
function Check (SEDOL : SEDOL_String) return SEDOL_Sum is
Weight : constant array (SEDOL_String'Range) of Integer := (1,3,1,7,3,9);
Sum  : Integer := 0;
Item  : Integer;
begin
for Index in SEDOL'Range loop
Item := Character'Pos (SEDOL (Index));
case Item is
when Character'Pos ('0')..Character'Pos ('9') =>
Item := Item - Character'Pos ('0');
when Character'Pos ('B')..Character'Pos ('D') |
Character'Pos ('F')..Character'Pos ('H') |
Character'Pos ('J')..Character'Pos ('N') |
Character'Pos ('P')..Character'Pos ('T') |
Character'Pos ('V')..Character'Pos ('Z') =>
Item := Item - Character'Pos ('A') + 10;
when others =>
raise Constraint_Error;
end case;
Sum := Sum + Item * Weight (Index);
end loop;
return SEDOL_Sum ((-Sum) mod 10);
end Check;
 
Test : constant array (1..10) of SEDOL_String :=
( "710889", "B0YBKJ", "406566", "B0YBLH", "228276",
"B0YBKL", "557910", "B0YBKR", "585284", "B0YBKT"
);
begin
for Index in Test'Range loop
Put_Line (Test (Index) & Character'Val (Character'Pos ('0') + Check (Test (Index))));
end loop;
end Test_SEDOL;

The function Check raises Constraint_Error upon an invalid input. The calculated sum is trimmed using (-sum) mod 10, which is mathematically equivalent to (10 - (sum mod 10)) mod 10.

Sample output:

7108899
B0YBKJ7
4065663
B0YBLH2
2282765
B0YBKL9
5579107
B0YBKR5
5852842
B0YBKT7

[edit] ALGOL 68

Translation of: C
Works with: ALGOL 68G version Any - tested with release mk15-0.8b.fc9.i386 - char in string, is alpha, is digit and to upper are not in the standard's prelude
[]INT sedol weights = (1, 3, 1, 7, 3, 9);
STRING reject = "AEIOUaeiou";
 
PROC strcspn = (STRING s,reject)INT: (
INT out:=0;
FOR i TO UPB s DO
IF char in string(s[i], LOC INT, reject) THEN
return out
FI;
out:=i
OD;
return out: out
);
 
PROC sedol checksum = (REF STRING sedol6)INT:
(
INT out;
 
INT len := UPB sedol6;
INT sum := 0;
 
IF sedol6[len-1] = REPR 10 THEN len-:=1; sedol6[len]:=null char FI;
IF len = 7 THEN
putf(stand error, ($"SEDOL code already checksummed? ("g")"l$, sedol6));
out := ABS ( BIN ABS sedol6[6] AND 16r7f); return out
FI;
IF len > 7 OR len < 6 OR strcspn(sedol6, reject) /= 6 THEN
putf(stand error, ($"not a SEDOL code? ("g")"l$, sedol6));
out := -1; return out
FI;
FOR i TO UPB sedol6 DO
sum+:=sedol weights[i]*
IF is digit(sedol6[i]) THEN
ABS sedol6[i]- ABS "0"
ELIF is alpha(sedol6[i]) THEN
(ABS to upper(sedol6[i])-ABS "A") + 10
ELSE
putf(stand error, $"SEDOL with not alphanumeric digit"l$);
out:=-1; return out
FI
OD;
out := (10 - (sum MOD 10)) MOD 10 + ABS "0";
return out: out
);
 
main:
(
STRING line;
 
on logical file end(stand in, (REF FILE f)BOOL: done);
DO getf(stand in, ($gl$,line));
INT sr := sedol checksum(line);
IF sr > 0 THEN
printf(($ggl$, line, REPR sedol checksum(line)))
FI
OD;
done: SKIP
)

Output:

7108899
B0YBKJ7
4065663
B0YBLH2
2282765
B0YBKL9
5579107
B0YBKR5
5852842
B0YBKT7

[edit] AutoHotkey

ahk forum: discussion

[edit] Full

codes = 710889,B0YBKJ,406566,B0YBLH,228276,B0YBKL,557910,B0YBKR,585284,B0YBKT,B00030,ABCDEF,BBBBBBB
Loop, Parse, codes, `,
output .= A_LoopField "`t-> " SEDOL(A_LoopField) "`n"
Msgbox %output%
 
SEDOL(code) {
Static weight1:=1, weight2:=3, weight3:=1, weight4:=7, weight5:=3, weight6:=9
If (StrLen(code) != 6)
Return "Invalid length."
StringCaseSense On
Loop, Parse, code
If A_LoopField is Number
check_digit += A_LoopField * weight%A_Index%
Else If A_LoopField in B,C,D,F,G,H,J,K,L,M,N,P,Q,R,S,T,V,W,X,Y,Z
check_digit += (Asc(A_LoopField)-Asc("A") + 10) * weight%A_Index%
Else
Return "Invalid character."
Return code . Mod(10-Mod(check_digit,10),10)
}

[edit] Short

Works with: AutoHotkey 1.1
MsgBox % SEDOL("710889")  ;7108899
MsgBox % SEDOL("B0YBKJ") ;B0YBKJ7
MsgBox % SEDOL("406566") ;4065663
MsgBox % SEDOL("B0YBLH") ;B0YBLH2
MsgBox % SEDOL("228276") ;2282765
MsgBox % SEDOL("B0YBKL") ;B0YBKL9
MsgBox % SEDOL("557910") ;5579107
MsgBox % SEDOL("B0YBKR") ;B0YBKR5
MsgBox % SEDOL("585284") ;5852842
MsgBox % SEDOL("B0YBKT") ;B0YBKT7
 
SEDOL(w) {
static weights := [1,3,1,7,3,9]
loop parse, w
s += ((c := Asc(A_LoopField)) >= 65 ? c - 65 + 10 : c - 48) * weights[A_Index]
return w Mod(10 - Mod(s, 10), 10)
}

[edit] AWK

Validate or calculate checksum of SEDOL codes read from standard input (one per line)

function ord(a)
{
return amap[a]
}
 
function sedol_checksum(sed)
{
sw[1] = 1; sw[2] = 3; sw[3] = 1
sw[4] = 7; sw[5] = 3; sw[6] = 9
sum = 0
for(i=1; i <= 6; i++) {
c = substr(toupper(sed), i, 1)
if ( c ~ /[[:digit:]]/ ) {
sum += c*sw[i]
} else {
sum += (ord(c)-ord("A")+10)*sw[i]
}
}
return (10 - (sum % 10)) % 10
}
 
BEGIN { # prepare amap for ord
for(_i=0;_i<256;_i++) {
astr = sprintf("%c", _i)
amap[astr] = _i
}
}
 
/[AEIOUaeiou]/ {
print "'" $0 "' not a valid SEDOL code"
next
}
{
if ( (length($0) > 7) || (length($0) < 6) ) {
print "'" $0 "' is too long or too short to be valid SEDOL"
next
}
sedol = substr($0, 1, 6)
sedolcheck = sedol_checksum(sedol)
if ( length($0) == 7 ) {
if ( (sedol sedolcheck) != $0 ) {
print sedol sedolcheck " (original " $0 " has wrong check digit"
} else {
print sedol sedolcheck
}
} else {
print sedol sedolcheck
}
}

[edit] BASIC

Works with: QuickBasic version 4.5
DECLARE FUNCTION getSedolCheckDigit! (str AS STRING)
DO
INPUT a$
PRINT a$ + STR$(getSedolCheckDigit(a$))
LOOP WHILE a$ <> ""
 
FUNCTION getSedolCheckDigit (str AS STRING)
IF LEN(str) <> 6 THEN
PRINT "Six chars only please"
EXIT FUNCTION
END IF
str = UCASE$(str)
DIM mult(6) AS INTEGER
mult(1) = 1: mult(2) = 3: mult(3) = 1
mult(4) = 7: mult(5) = 3: mult(6) = 9
total = 0
FOR i = 1 TO 6
s$ = MID$(str, i, 1)
IF s$ = "A" OR s$ = "E" OR s$ = "I" OR s$ = "O" OR s$ = "U" THEN
PRINT "No vowels"
EXIT FUNCTION
END IF
IF ASC(s$) >= 48 AND ASC(s$) <= 57 THEN
total = total + VAL(s$) * mult(i)
ELSE
total = total + (ASC(s$) - 55) * mult(i)
END IF
 
NEXT i
getSedolCheckDigit = (10 - (total MOD 10)) MOD 10
END FUNCTION

[edit] BBC BASIC

      PRINT FNsedol("710889")
PRINT FNsedol("B0YBKJ")
PRINT FNsedol("406566")
PRINT FNsedol("B0YBLH")
PRINT FNsedol("228276")
PRINT FNsedol("B0YBKL")
PRINT FNsedol("557910")
PRINT FNsedol("B0YBKR")
PRINT FNsedol("585284")
PRINT FNsedol("B0YBKT")
PRINT FNsedol("B00030")
END
 
DEF FNsedol(d$)
LOCAL a%, i%, s%, weights%()
DIM weights%(6) : weights%() = 0, 1, 3, 1, 7, 3, 9
FOR i% = 1 TO 6
a% = ASCMID$(d$,i%) - &30
s% += (a% + 7 * (a% > 9)) * weights%(i%)
NEXT
= d$ + CHR$(&30 + (10 - s% MOD 10) MOD 10)

Output:

7108899
B0YBKJ7
4065663
B0YBLH2
2282765
B0YBKL9
5579107
B0YBKR5
5852842
B0YBKT7
B000300

[edit] C

Notes: it reads the codes from standard input, one per line (linefeed terminated); the input encoding must meet the following specifications: single byte encoding, digits (0-9) must have codes that follow the same order of the digits (0, 1, 2, ...) and similar for letters, the encoding must match the one used with the compiled source (likely, ASCII based encodings). This should happen 99% of the time (for ASCII, ISO-8859 family and UTF-8 have the same byte encoding for alphanumeric characters).

#include <stdio.h>
#include <ctype.h>
#include <string.h>
 
int sedol_weights[] = {1, 3, 1, 7, 3, 9};
const char *reject = "AEIOUaeiou";
 
int sedol_checksum(const char *sedol6)
{
int len = strlen(sedol6);
int sum = 0, i;
 
if ( len == 7 ) {
fprintf(stderr, "SEDOL code already checksummed? (%s)\n", sedol6);
return sedol6[6] & 0x7f;
}
if ( (len > 7) || (len < 6) || ( strcspn(sedol6, reject) != 6 )) {
fprintf(stderr, "not a SEDOL code? (%s)\n", sedol6);
return -1;
}
for(i=0; i < 6; i++) {
if ( isdigit(sedol6[i]) ) {
sum += (sedol6[i]-'0')*sedol_weights[i];
} else if ( isalpha(sedol6[i]) ) {
sum += ((toupper(sedol6[i])-'A') + 10)*sedol_weights[i];
} else {
fprintf(stderr, "SEDOL with not alphanumeric digit\n");
return -1;
}
}
return (10 - (sum%10))%10 + '0';
}
 
 
#define MAXLINELEN 10
int main()
{
char line[MAXLINELEN];
int sr, len;
while( fgets(line, MAXLINELEN, stdin) != NULL ) {
len = strlen(line);
if ( line[len-1] == '\n' ) line[len-1]='\0';
sr = sedol_checksum(line);
if ( sr > 0 )
printf("%s%c\n", line, sr);
}
return 0;
}

Fed the input list from the task description, the output is:

7108899
B0YBKJ7
4065663
B0YBLH2
2282765
B0YBKL9
5579107
B0YBKR5
5852842
B0YBKT7

[edit] C++

 
#include <numeric>
#include <algorithm>
#include <cctype>
#include <iostream>
#include <stdexcept>
#include <iterator>
#include <vector>
 
using namespace std;
 
const int weights[] = {1,3,1,7,3,9};
const string valid_chars = "BCDFGHJKLMNPQRSTVWXYZ0123456789";
 
int char_value(char c){ return isalpha(c) ? c -'A' + 10 : c - '0'; }
 
int sedol_checksum(string const& sedol)
{
//Check contents
if(sedol.size() != 6)
throw length_error("length of sedol string != 6");
if(sedol.find_first_not_of(valid_chars) != std::string::npos)
throw invalid_argument("sedol string contains disallowed characters");
 
vector<int> char_values;
transform(sedol.begin(), sedol.end(), back_inserter(char_values), char_value);
const int weighted_sum = inner_product(char_values.begin(), char_values.end(), weights, 0);
return (10 - (weighted_sum % 10)) % 10;
}
 
int main()
{
string inputs[] = {
"710889",
"B0YBKJ",
"406566",
"B0YBLH",
"228276",
"B0YBKL",
"557910",
"B0YBKR",
"585284",
"B0YBKT",
"B00030"
};
const size_t n_inputs = sizeof(inputs) / sizeof(*inputs);
 
for(size_t i = 0; i != n_inputs; ++i)
{
cout << inputs[i] << sedol_checksum(inputs[i]) << "\n";
}
return 0;
}
 

[edit] C#

static int[] sedol_weights = { 1, 3, 1, 7, 3, 9 };
static int sedolChecksum(string sedol)
{
int len = sedol.Length;
int sum = 0;
 
if (len == 7) //SEDOL code already checksummed?
return (int)sedol[6];
 
if ((len > 7) || (len < 6) || System.Text.RegularExpressions.Regex.IsMatch(sedol, "[AEIOUaeiou]+")) //invalid SEDOL
return -1;
 
for (int i = 0; i < 6; i++)
{
if (Char.IsDigit(sedol[i]))
sum += (((int)sedol[i] - 48) * sedol_weights[i]);
 
else if (Char.IsLetter(sedol[i]))
sum += (((int)Char.ToUpper(sedol[i]) - 55) * sedol_weights[i]);
 
else
return -1;
 
}
 
return (10 - (sum % 10)) % 10;
}

[edit] COBOL

Works with: GNU Cobol version 2.0
       >>SOURCE FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. sedol.
 
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT sedol-file ASSIGN "sedol.txt"
ORGANIZATION LINE SEQUENTIAL
FILE STATUS sedol-file-status.
 
DATA DIVISION.
FILE SECTION.
FD sedol-file.
01 sedol PIC X(6).
 
WORKING-STORAGE SECTION.
01 sedol-file-status PIC XX.
88 sedol-file-ok VALUE "00".
 
01 digit-num PIC 9 COMP.
 
01 digit-weights-area VALUE "1317391".
03 digit-weights PIC 9 OCCURS 7 TIMES.
 
01 weighted-sum-parts-area.
03 weighted-sum-parts PIC 9(3) COMP OCCURS 6 TIMES.
 
01 weighted-sum PIC 9(3) COMP.
 
01 check-digit PIC 9.
 
PROCEDURE DIVISION.
OPEN INPUT sedol-file
PERFORM UNTIL NOT sedol-file-ok
READ sedol-file
AT END
EXIT PERFORM
END-READ
 
MOVE FUNCTION UPPER-CASE(sedol) TO sedol
 
PERFORM VARYING digit-num FROM 1 BY 1 UNTIL digit-num > 6
EVALUATE TRUE
WHEN sedol (digit-num:1) IS ALPHABETIC-UPPER
IF sedol (digit-num:1) = "A" OR "E" OR "I" OR "O" OR "U"
DISPLAY "Invalid SEDOL: " sedol
EXIT PERFORM CYCLE
END-IF
 
COMPUTE weighted-sum-parts (digit-num) =
(FUNCTION ORD(sedol (digit-num:1)) - FUNCTION ORD("A")
+ 10) * digit-weights (digit-num)
 
WHEN sedol (digit-num:1) IS NUMERIC
MULTIPLY FUNCTION NUMVAL(sedol (digit-num:1))
BY digit-weights (digit-num)
GIVING weighted-sum-parts (digit-num)
 
WHEN OTHER
DISPLAY "Invalid SEDOL: " sedol
EXIT PERFORM CYCLE
END-EVALUATE
END-PERFORM
 
INITIALIZE weighted-sum
PERFORM VARYING digit-num FROM 1 BY 1 UNTIL digit-num > 6
ADD weighted-sum-parts (digit-num) TO weighted-sum
END-PERFORM
 
COMPUTE check-digit =
FUNCTION MOD(10 - FUNCTION MOD(weighted-sum, 10), 10)
 
DISPLAY sedol check-digit
END-PERFORM
 
CLOSE sedol-file
.
END PROGRAM sedol.

[edit] Common Lisp

Implemented from scratch using the description on Wikipedia as specification.

Works with: ClozureCL
(defun append-sedol-check-digit (sedol &key (start 0) (end (+ start 6)))
(assert (<= 0 start end (length sedol)))
(assert (= (- end start) 6))
(loop
:with checksum = 0
:for weight :in '(1 3 1 7 3 9)
:for index :upfrom start
:do (incf checksum (* weight (digit-char-p (char sedol index) 36)))
:finally (let* ((posn (- 10 (mod checksum 10)))
(head (subseq sedol start end))
(tail (digit-char posn)))
(return (concatenate 'string head (list tail))))))

[edit] D

[edit] Functional Version

import std.stdio, std.algorithm, std.string, std.numeric, std.ascii;
 
char checksum(in char[] sedol) pure @safe /*@nogc*/
in {
assert(sedol.length == 6);
foreach (immutable c; sedol)
assert(c.isDigit || (c.isUpper && !"AEIOU".canFind(c)));
} out (result) {
assert(result.isDigit);
} body {
static immutable c2v = (in dchar c) => c.isDigit ? c - '0' : (c - 'A' + 10);
immutable int d = sedol.map!c2v.dotProduct([1, 3, 1, 7, 3, 9]);
return '0' + 10 - (d % 10);
}
 
void main() {
foreach (const sedol; "710889 B0YBKJ 406566 B0YBLH 228276
B0YBKL 557910 B0YBKR 585284 B0YBKT"
.split)
writeln(sedol, sedol.checksum);
}
Output:
7108899
B0YBKJ7
4065663
B0YBLH2
2282765
B0YBKL9
5579107
B0YBKR5
5852842
B0YBKT7

[edit] Imperative Version

Longer, faster lower-level version, same output.

import std.stdio, std.algorithm, std.string, std.numeric, std.ascii;
 
char sedolChecksum(in char[] sedol) pure nothrow @safe /*@nogc*/
in {
assert(sedol.length == 6, "SEDOL must be 6 chars long.");
enum uint mask = 0b11_1110_1111_1011_1110_1110_1110;
 
foreach (immutable c; sedol)
assert(c.isDigit ||
(c > 'A' && c <= 'Z' && ((1U << (c - 'A')) & mask)),
"SEDOL with wrong char.");
} out(result) {
assert(result.isDigit);
static int c2v(in dchar c) pure nothrow @safe @nogc {
return c.isDigit ? c - '0' : c - 'A' + 10;
}
immutable int d = sedol.map!c2v.dotProduct([1, 3, 1, 7, 3, 9]);
assert((d + result - '0') % 10 == 0);
} body {
static immutable int[] weights = [1, 3, 1, 7, 3, 9];
 
int sum = 0;
foreach (immutable i, immutable c; sedol) {
if (c.isDigit)
sum += (c - '0') * weights[i];
else
sum += (c - 'A' + 10) * weights[i];
}
 
return '0' + 10 - (sum % 10);
}
 
void main() {
foreach (immutable s; ["710889", "B0YBKJ", "406566", "B0YBLH",
"228276", "B0YBKL", "557910", "B0YBKR",
"585284", "B0YBKT"])
writeln(s, s.sedolChecksum);
}

[edit] Short Version

Same output.

void main() {
import std.stdio, std.algorithm, std.string, std.numeric,std.ascii;
 
foreach (const s; "710889 B0YBKJ 406566 B0YBLH 228276
B0YBKL 557910 B0YBKR 585284 B0YBKT"
.split)
writeln(s, '0' + 10 - s
.map!(c => c.isDigit ? c - '0' : c - 'A' + 10)
.dotProduct([1, 3, 1, 7, 3, 9]) % 10);
}

[edit] E

def weights := [1,3,1,7,3,9]
def Digit := ('0'..'9')
def Letter := ('B'..'D'|'F'..'H'|'J'..'N'|'P'..'T'|'V'..'Z')
def sedolCharValue(c) {
switch (c) {
match digit :Digit { return digit - '0' }
match letter :Letter {
return letter - 'A'
}
}
}
 
def checksum(sedol :String) {
require(sedol.size() == 6)
var sum := 0
for i => c in sedol {
sum += weights[i] * sedolCharValue(c)
}
return E.toString((10 - sum %% 10) %% 10)
}
 
def addChecksum(sedol :String) {
return sedol + checksum(sedol)
}
 
for sedol in "710889
B0YBKJ
406566
B0YBLH
228276
B0YBKL
557910
B0YBKR
585284
B0YBKT"
.trim().split("\n") {
println(addChecksum(sedol.trim()))
}

[edit] Excel VBA

 
Function getSedolCheckDigit(Input1)
Dim mult(6) As Integer
mult(1) = 1: mult(2) = 3: mult(3) = 1
mult(4) = 7: mult(5) = 3: mult(6) = 9
If Len(Input1) <> 6 Then
getSedolCheckDigit = "Six chars only please"
Exit Function
End If
Input1 = UCase(Input1)
Total = 0
For i = 1 To 6
s1 = Mid(Input1, i, 1)
If (s1 = "A") Or (s1 = "E") Or (s1 = "I") Or (s1 = "O") Or (s1 = "U") Then
getSedolCheckDigit = "No vowels"
Exit Function
End If
If (Asc(s1) >= 48) And (Asc(s1) <= 57) Then
Total = Total + Val(s1) * mult(i)
Else
Total = Total + (Asc(s1) - 55) * mult(i)
End If
 
Next i
getSedolCheckDigit = Input1 + CStr((10 - (Total Mod 10)) Mod 10)
 
End Function
 

[edit] Forth

create weight 1 , 3 , 1 , 7 , 3 , 9 ,
 
: char>num ( '0-9A-Z' -- 0..35 )
dup [char] 9 > 7 and - [char] 0 - ;
 
: check+ ( sedol -- sedol' )
6 <> abort" wrong SEDOL length"
0 ( sum )
6 0 do
over I + c@ char>num
weight I cells + @ *
+
loop
10 mod 10 swap - 10 mod [char] 0 +
over 6 + c! 7 ;
 
: sedol" [char] " parse check+ type ;
 
sedol" 710889" 7108899 ok
sedol" B0YBKJ" B0YBKJ7 ok
sedol" 406566" 4065663 ok
sedol" B0YBLH" B0YBLH2 ok
sedol" 228276" 2282765 ok
sedol" B0YBKL" B0YBKL9 ok
sedol" 557910" 5579107 ok
sedol" B0YBKR" B0YBKR5 ok
sedol" 585284" 5852842 ok
sedol" B0YBKT" B0YBKT7 ok

[edit] Fortran

Works with: Fortran version 90 and later
MODULE SEDOL_CHECK
IMPLICIT NONE
CONTAINS
 
FUNCTION Checkdigit(c)
CHARACTER :: Checkdigit
CHARACTER(6), INTENT(IN) :: c
CHARACTER(36) :: alpha = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
INTEGER, DIMENSION(6) :: weights = (/ 1, 3, 1, 7, 3, 9 /), temp
INTEGER :: i, n
 
DO i = 1, 6
temp(i) = INDEX(alpha, c(i:i)) - 1
END DO
temp = temp * weights
n = MOD(10 - (MOD(SUM(temp), 10)), 10)
Checkdigit = ACHAR(n + 48)
END FUNCTION Checkdigit
 
END MODULE SEDOL_CHECK
 
PROGRAM SEDOLTEST
USE SEDOL_CHECK
IMPLICIT NONE
 
CHARACTER(31) :: valid = "0123456789BCDFGHJKLMNPQRSTVWXYZ"
CHARACTER(6) :: codes(10) = (/ "710889", "B0YBKJ", "406566", "B0YBLH", "228276" , &
"B0YBKL", "557910", "B0YBKR", "585284", "B0YBKT" /)
CHARACTER(7) :: sedol
INTEGER :: i, invalid
 
DO i = 1, 10
invalid = VERIFY(codes(i), valid)
IF (invalid == 0) THEN
sedol = codes(i)
sedol(7:7) = Checkdigit(codes(i))
ELSE
sedol = "INVALID"
END IF
WRITE(*, "(2A9)") codes(i), sedol
END DO
 
END PROGRAM SEDOLTEST

Output

  710889  7108899
  B0YBKJ  B0YBKJ7
  406566  4065663
  B0YBLH  B0YBLH2
  228276  2282765
  B0YBKL  B0YBKL9
  557910  5579107
  B0YBKR  B0YBKR5
  585284  5852842
  B0YBKT  B0YBKT7

[edit] F#

open System
let Inputs = ["710889"; "B0YBKJ"; "406566"; "B0YBLH"; "228276"; "B0YBKL"
"557910"; "B0YBKR"; "585284"; "B0YBKT"; "B00030"]
 
let Vowels = set ['A'; 'E'; 'I'; 'O'; 'U']
let Weights = [1; 3; 1; 7; 3; 9; 1]
 
let inline isVowel c = Vowels.Contains (Char.ToUpper c)
 
let char2value c =
if Char.IsDigit c then int c - 0x30
else (['A'..'Z'] |> List.findIndex ((=) (Char.ToUpper c))) + 10
 
let sedolCheckDigit (input: string) =
if input.Length <> 6 || input |> Seq.exists isVowel then
failwithf "Input must be six characters long and not contain vowels: %s" input
 
let sum = Seq.map2 (fun ch weight -> (char2value ch) * weight) input Weights |> Seq.sum
(10 - sum%10)%10
 
let addCheckDigit inputs =
inputs |> List.map (fun s -> s + (sedolCheckDigit s).ToString())
 
let processDigits() =
try
addCheckDigit Inputs |> List.iter (printfn "%s")
with
ex -> printfn "ERROR: %s" ex.Message

[edit] Go

 
package main
 
import (
"fmt"
"strings"
"strconv"
)
 
const input = `710889
B0YBKJ
406566
B0YBLH
228276
B0YBKL
557910
B0YBKR
585284
B0YBKT
B00030
 
B
B0003
B000300
A00030
E00030
I00030
O00030
U00030
β00030
β0003`

 
var weight = [...]int{1,3,1,7,3,9}
 
func csd(code string) string {
switch len(code) {
case 6:
case 0:
return "No data"
default:
return "Invalid length"
}
sum := 0
for i, c := range code {
n, err := strconv.ParseInt(string(c), 36, 0)
if err != nil || c == 'A' || c == 'E' || c == 'I' || c == 'O' || c == 'U' {
return "Invalid character"
}
sum += int(n)*weight[i]
}
return strconv.Itoa(9-(sum-1)%10)
}
 
func main() {
for _, s := range strings.Split(input, "\n") {
d := csd(s)
if len(d) > 1 {
fmt.Printf(":%s: %s\n", s, d)
} else {
fmt.Println(s + d)
}
}
}
 

Output:

7108899
B0YBKJ7
4065663
B0YBLH2
2282765
B0YBKL9
5579107
B0YBKR5
5852842
B0YBKT7
B000300
:: No data
:B: Invalid length
:B0003: Invalid length
:B000300: Invalid length
:A00030: Invalid character
:E00030: Invalid character
:I00030: Invalid character
:O00030: Invalid character
:U00030: Invalid character
:β00030: Invalid length
:β0003: Invalid character

[edit] Groovy

def checksum(text) {
assert text.size() == 6 && !text.toUpperCase().find(/[AEIOU]+/) : "Invalid SEDOL text: $text"
 
def sum = 0
(0..5).each { index ->
sum += Character.digit(text.charAt(index), 36) * [1, 3, 1, 7, 3, 9][index]
}
text + (10 - (sum % 10)) % 10
}
String.metaClass.sedol = { this.&checksum(delegate) }

Test Code:

[ '710889': '7108899', 'B0YBKJ': 'B0YBKJ7', '406566': '4065663', 'B0YBLH': 'B0YBLH2',
'228276': '2282765', 'B0YBKL': 'B0YBKL9', '557910': '5579107', 'B0YBKR': 'B0YBKR5',
'585284': '5852842', 'B0YBKT': 'B0YBKT7', 'B00030': 'B000300'].each { text, expected ->
println "Checking $text -> $expected"
assert expected == text.sedol()
}

Output:

Checking 710889 -> 7108899
Checking B0YBKJ -> B0YBKJ7
Checking 406566 -> 4065663
Checking B0YBLH -> B0YBLH2
Checking 228276 -> 2282765
Checking B0YBKL -> B0YBKL9
Checking 557910 -> 5579107
Checking B0YBKR -> B0YBKR5
Checking 585284 -> 5852842
Checking B0YBKT -> B0YBKT7
Checking B00030 -> B000300

[edit] Haskell

import Data.Char
 
char2value c | c `elem` "AEIOU" = error "No vowels."
| c >= '0' && c <= '9' = ord c - ord '0'
| c >= 'A' && c <= 'Z' = ord c - ord 'A' + 10
 
sedolweight = [1,3,1,7,3,9]
 
checksum sedol = show ((10 - (tmp `mod` 10)) `mod` 10)
where tmp = sum $ zipWith (*) sedolweight $ map char2value sedol
 
main = mapM_ (\sedol -> putStrLn $ sedol ++ checksum sedol)
[ "710889",
"B0YBKJ",
"406566",
"B0YBLH",
"228276",
"B0YBKL",
"557910",
"B0YBKR",
"585284",
"B0YBKT" ]

[edit] Icon and Unicon

procedure main()
every write(sedol("710889"|"B0YBKJ"|"406566"|"B0YBLH"|"228276"|
"B0YBKL"|"557910"|"B0YBKR"|"585284"|"B0YBKT"|"B00030"))
end
 
procedure sedol(x) #: return the completed sedol with check digit
static w,c
initial {
every (i := -1, c := table())[!(&digits||&ucase)] := i +:= 1 # map chars
every c[!"AEIOU"] := &null # delete vowels
w := [1,3,1,7,3,9] # weights
}
 
if *(x := map(x,&lcase,&ucase)) = *w then { # match lengths
every (t :=0, i := 1 to *x) do
t +:= \c[x[i]]*w[i] | fail # accumulate weighted chars
return x || (10 - (t%10)) % 10 # complete
}
end

[edit] J

There are several ways to perform this in J. This most closely follows the algorithmic description at Wikipedia:

sn   =.  '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'  
ac0 =: (, 10 | 1 3 1 7 3 9 +/@:* -)&.(sn i. |:)

However, because J is so concise, having written the above, it becomes clear that the negation (-) is unneccsary.

The fundamental operation is the linear combination (+/@:*) and neither argument is "special". In particular, the coefficients are just another array participating in the calculation, and there's no reason we can't modify them as easily as the input array. Having this insight, it is obvious that manipulating the coefficients, rather than the input array, will be more efficient (because the coefficients are fixed at small size, while the input array can be arbitrarily large).

Which leads us to this more efficient formulation:

ac1  =:  (, 10 | (10 - 1 3 1 7 3 9) +/@:* ])&.(sn i. |:)

which reduces to:

ac1  =:  (, 10 | 9 7 9 3 7 1 +/@:* ])&.(sn i. |:)

Which is just as concise as ac0, but faster.

Following this train of thought, our array thinking leads us to realize that even the modulous isn't neccesary. The number of SEDOL numbers is finite, as is the number of coefficients; therefore the number of possible linear combinations of these is finite. In fact, there are only 841 possible outcomes. This is a small number, and can be efficiently stored as a lookup table (even better, since the outcomes will be mod 10, they are restricted to the digits 0-9, and they repeat).

Which leads us to:

ac2  =.  (,"1 0 (841 $ '0987654321') {~ 1 3 1 7 3 9 +/ .*~ sn i. ])

Which is more than twice as fast as even the optimized formulation (ac1), though it is slightly longer.

[edit] Java

import java.util.Scanner;
 
public class SEDOL{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
while(sc.hasNext()){
String sedol = sc.next();
System.out.println(sedol + getSedolCheckDigit(sedol));
}
}
 
private static final int[] mult = {1, 3, 1, 7, 3, 9};
 
public static int getSedolCheckDigit(String str){
if(!validateSedol(str)){
System.err.println("SEDOL strings must contain six characters with no vowels.");
return -1;
}
str = str.toUpperCase();
int total = 0;
for(int i = 0;i < 6; i++){
char s = str.charAt(i);
total += Character.digit(s, 36) * mult[i];
}
return (10 - (total % 10)) % 10;
}
 
public static boolean validateSedol(String str){
return (str.length() == 6) && !str.toUpperCase().matches(".*?[AEIOU].*?");
}
}

[edit] JavaScript

function sedol(input) {
return input + sedol_check_digit(input);
}
 
var weight = [1, 3, 1, 7, 3, 9, 1];
function sedol_check_digit(char6) {
if (char6.search(/^[0-9BCDFGHJKLMNPQRSTVWXYZ]{6}$/) == -1)
throw "Invalid SEDOL number '" + char6 + "'";
var sum = 0;
for (var i = 0; i < char6.length; i++)
sum += weight[i] * parseInt(char6.charAt(i), 36);
var check = (10 - sum%10) % 10;
return check.toString();
}
 
var input = [
'710889', 'B0YBKJ', '406566', 'B0YBLH', '228276',
'B0YBKL', '557910', 'B0YBKR', '585284', 'B0YBKT',
"BOATER" , "12345", "123456", "1234567"
];
 
var expected = [
'7108899', 'B0YBKJ7', '4065663', 'B0YBLH2', '2282765',
'B0YBKL9', '5579107', 'B0YBKR5', '5852842', 'B0YBKT7',
null, null, '1234563', null
];
 
for (var i in input) {
try {
var sedolized = sedol(input[i]);
if (sedolized == expected[i])
print(sedolized);
else
print("error: calculated sedol for input " + input[i] +
" is " + sedolized + ", but it should be " + expected[i]
);
}
catch (e) {
print("error: " + e);
}
}

output

7108899
B0YBKJ7
4065663
B0YBLH2
2282765
B0YBKL9
5579107
B0YBKR5
5852842
B0YBKT7
error: Invalid SEDOL number 'BOATER'
error: Invalid SEDOL number '12345'
1234563
error: Invalid SEDOL number '1234567'

[edit] Liberty BASIC

 
'adapted from BASIC solution
mult(1) = 1: mult(2) = 3: mult(3) = 1
mult(4) = 7: mult(5) = 3: mult(6) = 9
 
DO
INPUT a$
PRINT a$ + STR$(getSedolCheckDigit(a$))
LOOP WHILE a$ <> ""
 
FUNCTION getSedolCheckDigit (str$)
IF LEN(str$) <> 6 THEN
PRINT "Six chars only please"
EXIT FUNCTION
END IF
str$ = upper$(str$)
total = 0
FOR i = 1 TO 6
s$ = MID$(str$, i, 1)
IF (s$ = "A") OR (s$ = "E") OR (s$ = "I") OR (s$ = "O") OR (s$ = "U") THEN
PRINT "No vowels"
EXIT FUNCTION
END IF
IF (ASC(s$) >= 48) AND (ASC(s$) <= 57) THEN
total = total + VAL(s$) * mult(i)
ELSE
total = total + (ASC(s$) - 55) * mult(i)
END IF
 
NEXT i
getSedolCheckDigit = (10 - (total MOD 10)) MOD 10
END FUNCTION
 

[edit] M4

divert(-1)
changequote(`[',`]')
define([_bar],include(sedol.inp))
define([eachlineA],
[ifelse(eval($2>0),1,
[$3(substr([$1],0,$2))[]eachline(substr([$1],incr($2)),[$3])])])
define([eachline],[eachlineA([$1],index($1,[
]),[$2])])
define([_idx],
[index([0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ],substr($1,$2,1))])
define([_wsum],
[eval(_idx($1,0)+_idx($1,1)*3+_idx($1,2)+_idx($1,3)*7+_idx($1,4)*3+_idx($1,5)*9)])
define([checksum],
[$1[]eval((10-_wsum($1)%10)%10)
])
divert
eachline(_bar,[checksum])

[edit] Mathematica

SEDOL[Code_?(Function[v,StringFreeQ[v,{"A","E","I","O","U"}]])]:=
Code<>ToString[10-Mod[ToExpression[Quiet[Flatten[Characters[Code]
/.x_?LetterQ->(ToCharacterCode[x]-55)]]].{1,3,1,7,3,9},10]]
 
Scan[Print[SEDOL[#]] &, {"710889","B0YBKJ","406566","B0YBLH","228276","B0YBKL","557910","B0YBKR","585284","B0YBKT","B00030","DUMMY"}]
 
->Output:
7108899
B0YBKJ7
4065663
B0YBLH2
2282765
B0YBKL9
5579107
B0YBKR5
5852842
B0YBKT7
B0003010
SEDOL[DUMMY] -> rejected

[edit] Mercury

:- module sedol.
:- interface.
 
:- import_module io.
:- pred main(io::di, io::uo) is det.
 
:- implementation.
:- import_module char, int, list, require, string.
 
main(!IO) :-
Input = [
"710889",
"B0YBKJ",
"406566",
"B0YBLH",
"228276",
"B0YBKL",
"557910",
"B0YBKR",
"585284",
"B0YBKT",
"B00030"
],
list.foldl(print_with_checksum, Input, !IO).
 
:- pred print_with_checksum(string::in, io::di, io::uo) is det.
 
print_with_checksum(S, !IO) :-
io.format("%s%d\n", [s(S), i(sedol_checksum(S))], !IO).
 
:- func sedol_checksum(string) = int.
 
sedol_checksum(Sedol) = CheckSum :-
Digits = string.foldr((func(C, A) = [to_sedol_code(C) | A]), Sedol, []),
WeightedDigits = list.map_corresponding(int.times, Digits, [1, 3, 1, 7, 3, 9]),
WeightedSum = list.foldl(int.plus, WeightedDigits, 0),
CheckSum = (10 - (WeightedSum mod 10)) mod 10.
 
:- func to_sedol_code(char) = int.
 
to_sedol_code(Char) =
( if char.digit_to_int(Char, Code), not is_vowel(to_upper(Char))
then Code
else func_error("invalid SEDOL")
).
 
:- pred is_vowel(char::in) is semidet.
 
is_vowel('A').
is_vowel('E').
is_vowel('I').
is_vowel('O').
is_vowel('U').

[edit] Modula-3

MODULE SEDOL EXPORTS Main;
 
IMPORT IO, Fmt, Text, Stdio;
 
EXCEPTION BadSedol(TEXT);
 
VAR test := ARRAY [1..10] OF TEXT {"710889", "B0YBKJ", "406566", "B0YBLH",
"228276", "B0YBKL", "557910", "B0YBKR",
"585284", "B0YBKT" };
 
PROCEDURE Check(sed: TEXT): INTEGER RAISES {BadSedol}=
VAR
weights := ARRAY [0..5] OF INTEGER {1, 3, 1, 7, 3, 9};
result, d: INTEGER;
char: CHAR;
BEGIN
IF Text.Length(sed) # 6 THEN
RAISE BadSedol("ERROR: Must be 6 digits.");
END;
result := 0;
FOR i := 0 TO 5 DO
char := Text.GetChar(sed, i);
CASE char OF
| '0'..'9' => d := ORD(char) - ORD('0');
| 'B'..'D', 'F'..'H', 'J'..'N', 'P'..'T', 'V'..'Z'
=> d := ORD(char) - ORD('A') + 10;
ELSE
RAISE BadSedol("ERROR: Must be numbers or (non-vowel) letters.");
END;
INC(result, d * weights[i]);
END;
result := (10 - (result MOD 10)) MOD 10;
RETURN result;
END Check;
 
BEGIN
TRY
FOR i := FIRST(test) TO LAST(test) DO
IO.Put(test[i] & Fmt.Char(VAL(ORD('0') + Check(test[i]), CHAR)) & "\n");
END;
EXCEPT
| BadSedol(text) => IO.Put(text & "\n", Stdio.stderr);
END;
END SEDOL.

Output:

7108899
B0YBKJ7
4065663
B0YBLH2
2282765
B0YBKL9
5579107
B0YBKR5
5852842
B0YBKT7

[edit] MUMPS

SEDOL
NEW A,B
SEDOL1
READ !,"Enter the first 6 digits of a SEDOL: ",A
SET B=$$SEDOLCHK(A)
WRITE !,$SELECT($LENGTH(B)=1:"Full SEDOL is "_A_B,1:B)
GOTO SEDOL1
QUIT
SEDOLCHK(STOCK)
NEW WT,VAL,I,CHK,C,FLAG
SET WT="1317391",VAL=0,FLAG=0
FOR I=1:1:6 SET C=$$TOUPPER($EXTRACT(STOCK,I)),VAL=VAL+($$SEDVAL(C)*$EXTRACT(WT,I)) SET:"AEIOUaeiou"[C FLAG=1
SET:$LENGTH(STOCK)'=6 FLAG=1
KILL WT,I,CHK,C
QUIT $SELECT(FLAG:"INVALID",'FLAG:(10-(VAL#10))#10)
SEDVAL(X)
QUIT $SELECT($ISVALIDNUM(X):X,1:$ASCII(X)-$ASCII("@")+9)
TOUPPER(X)
NEW UP,LO
SET UP="ABCDEFGHIJKLMNOPQRSTUVWXYZ",LO="abcdefghijklmnopqrstuvwxyz"
QUIT $TRANSLATE(X,LO,UP)

Examples:

USER>D SEDOL^ROSETTA
 
Enter the first 6 digits of a SEDOL: 710889
Full SEDOL is 7108899
Enter the first 6 digits of a SEDOL: B0YBKJ
Full SEDOL is B0YBKJ7
Enter the first 6 digits of a SEDOL: 406566
Full SEDOL is 4065663
Enter the first 6 digits of a SEDOL: B0YBLH
Full SEDOL is B0YBLH2
Enter the first 6 digits of a SEDOL: 228276
Full SEDOL is 2282765
Enter the first 6 digits of a SEDOL: B0YBKL
Full SEDOL is B0YBKL9
Enter the first 6 digits of a SEDOL: 557910
Full SEDOL is 5579107
Enter the first 6 digits of a SEDOL: B0YBKR
Full SEDOL is B0YBKR5
Enter the first 6 digits of a SEDOL: 585284
Full SEDOL is 5852842
Enter the first 6 digits of a SEDOL: B0YBKT
Full SEDOL is B0YBKT7
Enter the first 6 digits of a SEDOL: B00030
Full SEDOL is B000300
Enter the first 6 digits of a SEDOL: Booo3o
INVALID
Enter the first 6 digits of a SEDOL: B123456
INVALID

[edit] Nimrod

import strutils
 
proc c2v(c): int =
assert c notin "AEIOU"
let a = ord(c)
if a < 65: a - 48
else: a - 55
 
const weight = [1,3,1,7,3,9]
 
proc checksum(sedol): string =
var tmp = 0
for i,s in sedol:
tmp += c2v(s) * weight[i]
result = $((10 - (tmp mod 10)) mod 10)
 
for sedol in """710889
B0YBKJ
406566
B0YBLH
228276
B0YBKL
557910
B0YBKR
585284
B0YBKT
B00030""".splitLines():
echo sedol, checksum(sedol)

[edit] OCaml

let char2value c =
assert (not (String.contains "AEIOU" c));
match c with
'0'..'9' -> int_of_char c - int_of_char '0'
| 'A'..'Z' -> int_of_char c - int_of_char 'A' + 10
 
let sedolweight = [1;3;1;7;3;9]
 
let explode s =
let result = ref [] in
String.iter (fun c -> result := c :: !result) s;
List.rev !result
 
let checksum sedol =
let tmp = List.fold_left2 (fun sum ch weight -> sum + char2value ch * weight)
0 (explode sedol) sedolweight in
string_of_int ((10 - (tmp mod 10)) mod 10)
 
List.iter (fun sedol -> print_endline (sedol ^ checksum sedol))
[ "710889";
"B0YBKJ";
"406566";
"B0YBLH";
"228276";
"B0YBKL";
"557910";
"B0YBKR";
"585284";
"B0YBKT" ]

[edit] Pascal

Works with: Free_Pascal
program Sedols(output);
 
function index(c: char): integer;
const
alpha = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
var
i: integer;
begin
index := 0;
for i := low(alpha) to high(alpha) do
if c = alpha[i] then
index := i;
end;
 
function checkdigit(c: string): char;
const
weight: array [1..6] of integer = (1, 3, 1, 7, 3, 9);
var
i, sum: integer;
begin
sum := 0;
for i := 1 to 6 do
sum := sum + (index(c[i]) - 1) * weight[i];
checkdigit := char((10 - (sum mod 10)) mod 10 + 48);
end;
 
const
codes: array [1..11] of string =
('710889', 'B0YBKJ', '406566', 'B0YBLH',
'228276', 'B0YBKL', '557910', 'B0YBKR',
'585284', 'B0YBKT', 'B00030');
 
var
seforl: string;
i: integer;
 
begin
for i := low(codes) to high(codes) do
begin
seforl := codes[i];
setlength(seforl, 7);
seforl[7] := checkdigit(codes[i]);
writeln(codes[i], ' -> ', seforl);
end;
end.

Output:

% ./Sedols 
710889 -> 7108899
B0YBKJ -> B0YBKJ7
406566 -> 4065663
B0YBLH -> B0YBLH2
228276 -> 2282765
B0YBKL -> B0YBKL9
557910 -> 5579107
B0YBKR -> B0YBKR5
585284 -> 5852842
B0YBKT -> B0YBKT7
B00030 -> B000300

[edit] Perl

This program reads from standard input.

use List::Util qw(sum);
use POSIX qw(strtol);
 
sub zip(&\@\@) {
my $f = shift;
my @a = @{shift()};
my @b = @{shift()};
my @result;
push(@result, $f->(shift @a, shift @b)) while @a && @b;
return @result;
}
 
my @weights = (1, 3, 1, 7, 3, 9);
sub sedol($) {
my $s = shift;
$s =~ /[AEIOU]/ and die "No vowels";
my @vs = map {(strtol $_, 36)[0]} split //, $s;
my $checksum = sum (zip {$_[0] * $_[1]} @vs, @weights);
my $check_digit = (10 - $checksum % 10) % 10;
return $s . $check_digit;
}
 
while (<>) {
chomp;
print sedol($_), "\n";
}

[edit] Perl 6

Translation of: Perl

Note: Change %base36 and @weights from `my` to `constant` when Rakudo implements them.

sub sedol( Str $s ) {
die 'No vowels allowed' if $s ~~ /<[AEIOU]>/;
die 'Invalid format' if $s !~~ /^ <[0..9B..DF..HJ..NP..TV..Z]>**6 $ /;
 
my %base36 = ( 0..9, 'A'..'Z' ) Z ( ^36 );
my @weights = 1, 3, 1, 7, 3, 9;
 
my @vs = %base36{ $s.comb };
my $checksum = [+] @vs Z* @weights;
my $check_digit = (10 - $checksum % 10) % 10;
return $s ~ $check_digit;
}
 
say sedol($_) for <
710889
B0YBKJ
406566
B0YBLH
228276
B0YBKL
557910
B0YBKR
585284
B0YBKT
B00030
>;

[edit] PHP

function char2value($c) {
assert(stripos('AEIOU', $c) === FALSE);
return intval($c, 36);
}
 
$sedolweight = array(1,3,1,7,3,9);
 
function checksum($sedol) {
global $sedolweight;
$tmp = array_sum(array_map(create_function('$ch, $weight', 'return char2value($ch) * $weight;'),
str_split($sedol), $sedolweight)
);
return strval((10 - ($tmp % 10)) % 10);
}
 
foreach (array('710889',
'B0YBKJ',
'406566',
'B0YBLH',
'228276',
'B0YBKL',
'557910',
'B0YBKR',
'585284',
'B0YBKT') as $sedol)
echo $sedol, checksum($sedol), "\n";

[edit] PicoLisp

(de sedol (Str)
(pack Str
(char
(+ `(char "0")
(%
(- 10
(%
(sum
'((W C)
(cond
((>= "9" C "0")
(* W (format C)) )
((>= "Z" (setq C (uppc C)) "A")
(* W (+ 10 (- (char C) `(char "A")))) ) ) )
(1 3 1 7 3 9)
(chop Str) )
10 ) )
10 ) ) ) ) )
 
(for S '("710889" "B0YBKJ" "406566" "B0YBLH" "228276" "B0YBKL" "557910" "B0YBKR" "585284" "B0YBKT" "B00030")
(prinl (sedol S)) )

[edit] PL/I

/* Compute SEDOLs; includes check for invalid characters. */
sedol: procedure options (main); /* 3 March 2012 */
declare alphabet character (36) static initial
('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ');
declare weight (6) fixed static initial (1, 3, 1, 7, 3, 9);
declare s character (6);
declare (i, v, k) fixed;
 
do while ('1'b);
get edit (s) (a(6));
put skip edit (s) (a);
/* Check for invalid characters: */
if verify(s, '0123456789BCDFGHJKLMNPQRSTVWXYZ') > 0 then stop;
v = 0;
do i = 1 to 6;
k = index(alphabet, substr(s, i, 1)) - 1;
v = v + weight(i) * k;
end;
k = mod(v, 10);
v = mod(10 - k, 10);
put edit (s, v) (x(2), a, f(1)); put edit (' ') (a);
end;
end sedol;
Output:
710889  7108899
B0YBKJ  B0YBKJ7
406566  4065663
B0YBLH  B0YBLH2
228276  2282765
B0YBKL  B0YBKL9
557910  5579107
B0YBKR  B0YBKR5
585284  5852842
B0YBKT  B0YBKT7
B00030  B000300

[edit] PureBasic

Procedure.s SEDOLs(rawstring$)
Protected i, j, sum, c, m
For i=1 To Len(rawstring$)
c=Asc(Mid(rawstring$,i,1))
Select c
Case Asc("0") To Asc("9")
j=Val(Mid(rawstring$,i,1))
Default
j=c-Asc("A")
EndSelect
Select i
Case 1, 3, 7: m=1
Case 2, 5: m=3
Case 4: m=7
Default: m=9
EndSelect
sum+j*m
Next
sum=(10-(sum%10))%10
ProcedureReturn rawstring$+Str(sum)
EndProcedure
 
Define result$, i
Restore Tests
For i=0 To 10
Read.s SEDOL$
result$+SEDOLs(SEDOL$)
If i%2
result$+#CRLF$
ElseIf i<10
result$+", "
EndIf
Next
MessageRequester("SEDOLs","Result"+#CRLF$+result$)
 
DataSection
Tests:
Data.s "710889","B0YBKJ","406566","B0YBLH","228276"
Data.s "B0YBKL","557910","B0YBKR","585284","B0YBKT","B00030"
EndDataSection

PB SEDOL.png

[edit] Python

def char2value(c):
assert c not in 'AEIOU', "No vowels"
return int(c, 36)
 
sedolweight = [1,3,1,7,3,9]
 
def checksum(sedol):
tmp = sum(map(lambda ch, weight: char2value(ch) * weight,
sedol, sedolweight)
)
return str((10 - (tmp % 10)) % 10)
 
for sedol in '''
710889
B0YBKJ
406566
B0YBLH
228276
B0YBKL
557910
B0YBKR
585284
B0YBKT
'''
.split():
print sedol + checksum(sedol)

[edit] R

# Read in data from text connection
datalines <- readLines(tc <- textConnection("710889
B0YBKJ
406566
B0YBLH
228276
B0YBKL
557910
B0YBKR
585284
B0YBKT"
)); close(tc)
 
# Check data valid
checkSedol <- function(datalines)
{
ok <- grep("^[[:digit:][:upper:]]{6}$", datalines)
if(length(ok) < length(datalines))
{
stop("there are invalid lines")
}
}
checkSedol(datalines)
 
# Append check digit
appendCheckDigit <- function(x)
{
if(length(x) > 1) return(sapply(x, appendCheckDigit))
ascii <- as.integer(charToRaw(x))
scores <- ifelse(ascii < 65, ascii - 48, ascii - 55)
weights <- c(1, 3, 1, 7, 3, 9)
chkdig <- (10 - sum(scores * weights) %% 10) %% 10
paste(x, as.character(chkdig), sep="")
}
withchkdig <- appendCheckDigit(datalines)
 
#Print in format requested
writeLines(withchkdig)

[edit] Racket

#lang racket
;;; Since the Task gives us unchecksummed and checksummed SEDOLs, and
;;; we'll just take a list of the output SEDOLs and remove their last
;;; characters for the input
(define output-SEDOLS
(list "7108899" "B0YBKJ7" "4065663"
"B0YBLH2" "2282765" "B0YBKL9"
"5579107" "B0YBKR5" "5852842"
"B0YBKT7" "B000300"))
(define (output->input-SEDOL S) (substring S 0 6))
(define input-SEDOLS (map output->input-SEDOL output-SEDOLS))
 
;;; checksum calculation
(define (SEDOL-character-value c)
(if (char-numeric? c)
(- (char->integer c) (char->integer #\0))
(+ 10 (- (char->integer c) (char->integer #\A)))))
(define (SEDOL-character-sum S)
(for/sum ((c S)  ; if we run out of c's before the final 1 in weight, we'll have the unchecksummed weighted sum
(weight (in-list '(1 3 1 7 3 9 1))))
(* weight (SEDOL-character-value c))))
(define (SEDOL-checksum S) (number->string (modulo (- 10 (SEDOL-character-sum S)) 10)))
 
;;; build output from input
(define (SEDOL-append-checksum S) (string-append S (SEDOL-checksum S)))
 
;;; Extra credit -- according to wikipedia:
;;; "SEDOLs are seven characters in length"
;;; "vowels are never used"
;;; there seems to be no statement as to case, but we'll assert that too!
;;;
;;; valid-SEDOL? is a predicate... it doesn't report a reason
(define (invalid-SEDOL? S)
(define (invalid-SEDOL-character? c)
(if
(and (not (char-upper-case? c)) (not (char-numeric? c)))
(format "contains non upper case/non numeric ~a" c)
(case c [(#\A #\E #\I #\O #\U) (format "contains vowel ~a" c)] [else #f])))
(cond
[(< (string-length S) 7) "too few characters"]
[(> (string-length S) 7) "too many characters"]
[(not (zero? (modulo (SEDOL-character-sum S) 10))) "invalid checksum"]
[(for/first ((c S) #:when (invalid-SEDOL-character? c)) c) => identity]
[else #f])) ; a.k.a. valid!
 
(module+ main
(for* ((S input-SEDOLS))
(displayln (SEDOL-append-checksum S)))
(newline)
(displayln "Extra Credit!")
(displayln (invalid-SEDOL? "B0YBKT7")) ; expect #f output
(displayln (invalid-SEDOL? "B000301")) ; expect "invalid checksum" output
)
 
(module+ test
(require rackunit)
(check-= (SEDOL-character-value #\3) 3 0)
(check-= (SEDOL-character-value #\B) 11 0)
(check-equal? (invalid-SEDOL? "B000301") "invalid checksum")
(for ((S output-SEDOLS))
(check-false (invalid-SEDOL? S))
(check-equal? (SEDOL-append-checksum (substring S 0 6))
S (format "test SEDOL for ~a" S))))

Output:

7108899
B0YBKJ7
4065663
B0YBLH2
2282765
B0YBKL9
5579107
B0YBKR5
5852842
B0YBKT7
B000300

Extra Credit!
#f
invalid checksum

[edit] REXX

/*REXX program computes the check (last) digit for  6 or 7  char SEDOLs.*/
/*if the SEDOL is 6 characters, */
/*a check digit is added. */
 
/*if the SEDOL is 7 characters, a */
/*check digit is created and it is*/
/*verified that it's equal to the */
/*check digit already on the SEDOL*/
@.=
arg @.1 . /*allow a user-specified SEDOL. */
if @.1=='' then do /*if none, then assume 11 defaults*/
@.1 = 710889
@.2 ='B0YBKJ'
@.3 = 406566
@.4 ='B0YBLH'
@.5 = 228276
@.6 ='B0YBKL'
@.7 = 557910
@.8 ='B0YBKR'
@.9 = 585284
@.10='B0YBKT'
@.11='B00030'
end
 
@abcU='ABCDEFGHIJKLMNOPQRSTUVWXYZ' /*the uppercase Latin alphabet. */
alphaDigs='0123456789'@abcU /*legal chars, and then some. */
allowable=space(translate(alphaDigs,,'AEIOU'),0) /*remove the vowels.*/
weights=1317391 /*various weights for SEDOL chars*/
/*an alternative would be to use:*/
/* weights='1 3 1 7 3 9 1' if */
/*any weights were greater than 9*/
do j=1 while @.j\==''; sedol=@.j /*process each specified SEDOL. */
L=length(sedol)
if L<6 | L>7 then call ser "SEDOL isn't a valid length"
if left(sedol,1)==9 then call swa 'SEDOL is reserved for end user allocation'
_=verify(sedol,allowable)
if _\==0 then call ser 'illegal character in SEDOL:' substr(sedol,_,1)
sum=0 /*checkDigit sum (so far). */
do k=1 for 6 /*process each character in SEDOL*/
sum=sum+(pos(substr(sedol,k,1),alphaDigs)-1)*substr(weights,k,1)
end /*k*/
 
chkDig= (10-sum//10) // 10
r=right(sedol,1)
if L==7 & chkDig\==r then call ser sedol,'invalid check digit:' r
say 'SEDOL:' left(sedol,9) 'SEDOL + check digit:' left(sedol,6)chkDig
end /*j*/
 
exit /*stick a fork in it, we're done.*/
/*─────────────────────────────────────subroutines──────────────────────*/
sed: say; say 'SEDOL:' sedol; say; return
ser: say; say '*** error! ***'; say; say arg(1); call sed; exit 13
swa: say; say '*** warning! ***' arg(1); say; return

output when using the defaults:

SEDOL: 710889    SEDOL + check digit: 7108899
SEDOL: B0YBKJ    SEDOL + check digit: B0YBKJ7
SEDOL: 406566    SEDOL + check digit: 4065663
SEDOL: B0YBLH    SEDOL + check digit: B0YBLH2
SEDOL: 228276    SEDOL + check digit: 2282765
SEDOL: B0YBKL    SEDOL + check digit: B0YBKL9
SEDOL: 557910    SEDOL + check digit: 5579107
SEDOL: B0YBKR    SEDOL + check digit: B0YBKR5
SEDOL: 585284    SEDOL + check digit: 5852842
SEDOL: B0YBKT    SEDOL + check digit: B0YBKT7
SEDOL: B00030    SEDOL + check digit: B000300

[edit] Ruby

Sedol_char = "0123456789BCDFGHJKLMNPQRSTVWXYZ"
 
def char2value(c)
raise ArgumentError, "No vowels" unless Sedol_char.include?(c)
c.to_i(36)
end
 
Sedolweight = [1,3,1,7,3,9]
 
def checksum(sedol)
raise ArgumentError, "Invalid length" unless sedol.size == Sedolweight.size
sum = sedol.split('').zip(Sedolweight).map { |ch, weight|
char2value(ch) * weight }.inject(:+)
((10 - (sum % 10)) % 10).to_s
end
 
data = %w{
710889
B0YBKJ
406566
B0YBLH
228276
B0YBKL
557910
B0YBKR
585284
B0YBKT
B00030
C0000
1234567
00000A
}
 
for sedol in data
print "%-8s " % sedol
begin
puts sedol + checksum(sedol)
rescue => e
p e
end
end
Output:
710889   7108899
B0YBKJ   B0YBKJ7
406566   4065663
B0YBLH   B0YBLH2
228276   2282765
B0YBKL   B0YBKL9
557910   5579107
B0YBKR   B0YBKR5
585284   5852842
B0YBKT   B0YBKT7
B00030   B000300
C0000    #<ArgumentError: Invalid length>
1234567  #<ArgumentError: Invalid length>
00000A   #<ArgumentError: No vowels>

[edit] Scala

class SEDOL(s: String) {
require(s.size == 6 || s.size == 7, "SEDOL length must be 6 or 7 characters")
require(s.size == 6 || s(6).asDigit == chksum, "Incorrect SEDOL checksum")
require(s forall (c => !("aeiou" contains c.toLower)), "Vowels not allowed in SEDOL")
def chksum = 10 - ((s zip List(1, 3, 1, 7, 3, 9) map { case (c, w) => c.asDigit * w } sum) % 10)
override def toString = s.take(6) + chksum
}

Test cases:

scala> """710889
     | B0YBKJ
     | 406566
     | B0YBLH
     | 228276
     | B0YBKL
     | 557910
     | B0YBKR
     | 585284
     | B0YBKT""".lines.map(_.trim).foreach(s => println(new SEDOL(s)))
7108899
B0YBKJ7
4065663
B0YBLH2
2282765
B0YBKL9
5579107
B0YBKR5
5852842
B0YBKT7

Validations:

scala> new SEDOL("12")
java.lang.IllegalArgumentException: requirement failed: SEDOL length must be 6 or 7 characters

scala> new SEDOL("7108890")
java.lang.IllegalArgumentException: requirement failed: Incorrect SEDOL checksum

scala> new SEDOL("71088A")
java.lang.IllegalArgumentException: requirement failed: Vowels not allowed in SEDOL

[edit] Seed7

$ include "seed7_05.s7i";
 
const func char: sedolCheckDigit (in string: sedol) is func
result
var char: checkDigit is ' ';
local
const array integer: weight is [] (1, 3, 1, 7, 3, 9);
var char: ch is ' ';
var integer: index is 0;
var integer: item is 0;
var integer: sum is 0;
begin
for ch key index range sedol do
case ch of
when {'0' .. '9'}:
item := ord(ch) - ord('0');
when {'A' .. 'Z'} - {'A', 'E', 'I', 'O', 'U'}:
item := ord(ch) - ord('A') + 10;
otherwise:
raise RANGE_ERROR;
end case;
sum +:= item * weight[index];
end for;
checkDigit := chr(-sum mod 10 + ord('0'));
end func;
 
const proc: main is func
local
var string: sedol is "";
begin
for sedol range [] ("710889", "B0YBKJ", "406566", "B0YBLH", "228276", "B0YBKL",
"557910", "B0YBKR", "585284", "B0YBKT", "B00030") do
writeln(sedol <& sedolCheckDigit(sedol));
end for;
end func;

Output:

7108899
B0YBKJ7
4065663
B0YBLH2
2282765
B0YBKL9
5579107
B0YBKR5
5852842
B0YBKT7
B000300

[edit] Smalltalk

Works with: GNU Smalltalk
String extend [
includesAnyOf: aSet [
aSet do: [ :e | (self includes: e) ifTrue: [ ^true ] ].
^false
]
].
Object subclass: SEDOL [
|weight charList|
 
initialize [
weight := Array from: { 1. 3. 1. 7. 3. 9 }.
charList :=
('ABCDEFGHIJKLMNOPQRSTUVWXYZ' asOrderedCollection)
collect: [ :c | ('AEIOU' includes: c) ifTrue: [ nil ] ifFalse: [ c ] ].
]
 
SEDOL class >> new [
^ (self basicNew) initialize
]
 
"to be considered private"
blindCheckDigit: aSEDOL [ |sum|
sum := 0.
aSEDOL keysAndValuesDo: [ :i :c |
('0123456789' includes: c)
ifTrue: [ sum := sum +
((weight at: i) *
(Number readFrom: (c asString readStream))).
]
ifFalse: [ sum := sum + (((charList indexOf: c) + 9) *
(weight at: i))
]
].
^ ((10 - (sum rem: 10)) rem: 10) displayString
]
 
checked: aSEDOL [
(aSEDOL size < 6) |
(aSEDOL size > 7) |
(aSEDOL asUppercase includesAnyOf: 'AEIOU' asSet )
ifTrue: [ SystemExceptions.InvalidArgument
signalOn: aSEDOL
reason: 'Not a valid SEDOL'
]
ifFalse: [ |t| t := aSEDOL copyFrom: 1 to: 6.
^ t , (self blindCheckDigit: t)
]
]
].
|sedol|
sedol := SEDOL new.
{ '710889'.
'B0YBKJ'.
'406566'.
'B0YBLH'.
'228276'.
'B0YBKL'.
'557910'.
'B0YBKR'.
'585284'.
'B0YBKT' } do: [ :c | (sedol checked: c) displayNl ]

[edit] Standard ML

fun char2value c =
if List.exists (fn x => x = c) (explode "AEIOU") then raise Fail "no vowels"
else if Char.isDigit c then ord c - ord #"0"
else if Char.isUpper c then ord c - ord #"A" + 10
else raise Match
 
val sedolweight = [1,3,1,7,3,9]
 
fun checksum sedol = let
val tmp = ListPair.foldlEq (fn (ch, weight, sum) => sum + char2value ch * weight)
0 (explode sedol, sedolweight)
in
Int.toString ((10 - (tmp mod 10)) mod 10)
end
 
app (fn sedol => print (sedol ^ checksum sedol ^ "\n"))
[ "710889",
"B0YBKJ",
"406566",
"B0YBLH",
"228276",
"B0YBKL",
"557910",
"B0YBKR",
"585284",
"B0YBKT" ];

[edit] Tcl

namespace eval sedol {
variable chars {0 1 2 3 4 5 6 7 8 9 "" B C D "" F G H "" J K L M N "" P Q R S T "" V W X Y Z}
variable weight {1 3 1 7 3 9 1}
 
proc checksum {alnum6} {
variable chars
variable weight
set sum 0
set col 0
foreach char [split [string toupper [string range $alnum6 0 5]] ""] {
if {[set idx [lsearch -exact $chars $char]] == -1} {
error "invalid character: $char"
}
incr sum [expr {$idx * [lindex $weight $col]}]
incr col
}
return [expr {(10 - ($sum % 10)) % 10}]
}
 
proc valid {alnum7} {
expr {[checksum [string range $alnum7 0 5]] == [string index $alnum7 6]}
}
}
 
proc assert {condition {message "Assertion failed!"}} {
if { ! [uplevel 1 [list expr $condition]]} {
return -code error $message
}
}
 
set codes {710889 B0YBKJ 406566 B0YBLH 228276 B0YBKL 557910 B0YBKR 585284 B0YBKT}
set answers {7108899 B0YBKJ7 4065663 B0YBLH2 2282765 B0YBKL9 5579107 B0YBKR5 5852842 B0YBKT7}
 
foreach code $codes answer $answers {
set sedol "${code}[sedol::checksum $code]"
assert {$sedol eq $answer} "assertion failed: $sedol ne $answer"
puts $sedol
}

[edit] Transact-SQL

SQL Server transact-SQL implementation. Compatible with all versions from 6.5 to 2008. Returns empty string if invalid.

CREATE FUNCTION [dbo].[fn_CheckSEDOL]
( @SEDOL varchar(50) )
RETURNS varchar(7)
AS
BEGIN
declare @true bit = 1,
@false bit = 0,
@isSEDOL bit,
@sedol_weights varchar(6) ='131739',
@sedol_len int = LEN(@SEDOL),
@sum int = 0
 
 
if ((@sedol_len = 6))
begin
select @SEDOL = UPPER(@SEDOL)
Declare @vowels varchar(5) = 'AEIOU',
@letters varchar(21) = 'BCDFGHJKLMNPQRSTVWXYZ',
@i int=1,
@isStillGood bit = @true,
@char char = '',
@weighting int =0
 
select @isSEDOL = @false
 
while ((@i < 7) and (@isStillGood = @true))
begin
select @char = SUBSTRING(@SEDOL,@i,1),
@weighting = CONVERT (INT,SUBSTRING(@sedol_weights, @i, 1))
if (CHARINDEX(@char, @vowels) > 0) -- no vowels please
begin
select @isStillGood=@false
end
else
begin
if (ISNUMERIC(@char) = @true) -- is a number
begin
select @sum = @sum + (ASCII(@char) - 48) * @weighting
end
else if (CHARINDEX(@char, @letters) = 0) -- test for the rest of the alphabet
begin
select @isStillGood=@false
end
else
begin
select @sum = @sum + (ASCII(@char) - 55) * @weighting
end
end
select @i = @i +1
end -- of while loop
if (@isStillGood = @true)
begin
declare @checksum int = (10 - (@sum%10))%10
select @SEDOL = @SEDOL + CONVERT(CHAR,@checksum)
end
end
else
begin
select @SEDOL = ''
end
-- Return the result of the function
RETURN @SEDOL
END

Examples:

print dbo.fn_CheckSEDOL('710889')
print dbo.fn_CheckSEDOL('B0YBKJ')
print dbo.fn_CheckSEDOL('406566')
print dbo.fn_CheckSEDOL('B0YBLH')
print dbo.fn_CheckSEDOL('228276')
print dbo.fn_CheckSEDOL('B0YBKL')
print dbo.fn_CheckSEDOL('557910')
print dbo.fn_CheckSEDOL('B0YBKR')
print dbo.fn_CheckSEDOL('585284')
print dbo.fn_CheckSEDOL('B0YBKT')
print dbo.fn_CheckSEDOL('B00030')

7108899
B0YBKJ7
4065663
B0YBLH2
2282765
B0YBKL9
5579107
B0YBKR5
5852842
B0YBKT7
B000300

[edit] TUSCRIPT

 
$$ MODE TUSCRIPT
check="1'3'1'7'3'9"
values="123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
value=STRINGS (values,":<%:")
BUILD r_TABLE/or illegal=":A:E:I:O:U:"
LOOP input="710889'B0YBKJ'406566'B0YBLH'228276'B0YBKL'557910'B0YBKR'585284'B0YBKT'BOYAKT'B00030",sum=""
IF (input.ma.illegal) THEN
PRINT/ERROR input, " illegal"
CYCLE
ENDIF
strings=STRINGS (input,":<%:")
LOOP d,nr=strings
c=SELECT (check,#d)
IF (nr!='digits') nr=FILTER_INDEX (value,":{nr}:",-)
x=nr*c, sum=APPEND(sum,x)
ENDLOOP
endsum=SUM(sum), checksum=10-(endsum%10)
IF (checksum==10) checksum=0
PRINT input, " checkdigit: ", checksum
ENDLOOP
 

Output:

710889 checkdigit: 9
B0YBKJ checkdigit: 7
406566 checkdigit: 3
B0YBLH checkdigit: 2
228276 checkdigit: 5
B0YBKL checkdigit: 9
557910 checkdigit: 7
B0YBKR checkdigit: 5
585284 checkdigit: 2
B0YBKT checkdigit: 7
@@@@@@@@  BOYAKT illegal
B00030 checkdigit: 0 

[edit] Ursala

The straightforward approach closely follows the published specification, using a table-driven finite map (charval) from characters to numbers, and calculating the inner product as a cumulative sum of the weight vector zipped with the product function to the list of character values.

#import std
#import nat
 
alphabet = digits-- ~=`A-~r letters
weights = <1,3,1,7,3,9>
charval = -:@rlXS num alphabet
iprod = sum:-0+ product*p/weights+ charval*
checksum = difference/10+ remainder\10+ iprod

An optimization following the J solution avoids a run-time subtraction by complementing the coefficients at compile time using these definitions in place of those above.

weights  = difference/*10 <1,3,1,7,3,9>
checksum = remainder\10+ iprod

A further performance improvement subsumes the character value lookup and multiplcation table within the same finite map in the version shown below.

lookup   = -: (^/~& product^|/~& charval)*lsPrK0/weights alphabet
iprod = sum:-0+ lookup*p/weights

To optimize further, we can build a separate smaller multiplication table for each coefficient, letting the coefficient be hard coded and allowing faster lookups. The zipwith operation (*p) is also avoided by having each map index directly into the input list.

lookups  = (-:+ * ^/~&l product^|/charval ~&)* *-* -*weights alphabet
iprod = sum:-0+ gang +^|(~&,~)*lNrXXK9 ^(~&,&h!)* lookups

Here is a test program.

#show+
 
examples = ^T(~&,~&h+ %nP+ checksum)*t
 
-[
710889
B0YBKJ
406566
B0YBLH
228276
B0YBKL
557910
B0YBKR
585284
B0YBKT]-

output:

7108899
B0YBKJ7
4065663
B0YBLH2
2282765
B0YBKL9
5579107
B0YBKR5
5852842
B0YBKT7

[edit] zkl

fcn checksum(text){
( text.len()!=6 or (text..matches("*[AEIOUaeioua-z]*")) ) and
throw(Exception.ValueError("Invalid SEDOL text: "+text));
 
text + (10 - text.pump(List,'wrap(c){
if("0"<=c<="9") c.toAsc()-0x30;
else c.toAsc()-55;
}).zipWith('*,T(1,3,1,7,3,9)).sum() % 10) % 10;
}

It sure does look like that trailing %10 is extraneous. It also seems like lower case is implicitly invalid.

T("710889","B0YBKJ","406566","B0YBLH","228276",
"B0YBKL","557910","B0YBKR","585284","B0YBKT","B00030")
.apply(checksum).println();
Output:
L("7108899","B0YBKJ7","4065663","B0YBLH2","2282765","B0YBKL9",
  "5579107","B0YBKR5","5852842","B0YBKT7","B000300")
Personal tools
Namespaces

Variants
Actions
Community
Explore
Misc
Toolbox