SEDOLs: Difference between revisions

From Rosetta Code
Content added Content deleted
(→‎{{header|T-SQL}}: correct ordering of parameters of CHARINDEX statements)
(Added short D version)
Line 557: Line 557:
"B0YBKL", "557910", "B0YBKR", "585284", "B0YBKT"])
"B0YBKL", "557910", "B0YBKR", "585284", "B0YBKT"])
writeln(s, sedolChecksum(s));
writeln(s, sedolChecksum(s));
}</lang>
Short version (same output):
<lang d>import std.stdio, std.algorithm, std.string, std.numeric;

auto checksum(string s) {
auto m = map!q{ a>='0' && a<='9' ? a-'0' : a-'A'+10 }(s);
return '0' + 10 - dotProduct(m, [1,3,1,7,3,9]) % 10;
}

void main() {
foreach (sedol; "710889 B0YBKJ 406566 B0YBLH 228276
B0YBKL 557910 B0YBKR 585284 B0YBKT".split())
writeln(sedol, checksum(sedol));
}</lang>
}</lang>



Revision as of 18:50, 19 August 2011

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

ActionScript

<lang 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");</lang>

Ada

<lang 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;</lang> 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

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

<lang algol68>[]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

)</lang> Output:

7108899
B0YBKJ7
4065663
B0YBLH2
2282765
B0YBKL9
5579107
B0YBKR5
5852842
B0YBKT7

AutoHotkey

ahk forum: discussion <lang AutoHotkey>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 w1:=1,w2:=3,w3:=1,w4:=7,w5:=3,w6:=9
  Loop Parse, w
     s += ((c:=Asc(A_LoopField))>=Asc("A") ? c-Asc("A")+10 : c-Asc("0")) * w%A_Index%
  Return w mod(10-mod(s,10),10)

}</lang>

AWK

Validate or calculate checksum of SEDOL codes read from standard input (one per line) <lang awk>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
 }

}</lang>

BASIC

Works with: QuickBasic version 4.5

<lang qbasic>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</lang>

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).

<lang c>#include <stdio.h>

  1. include <ctype.h>
  2. 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'; 

}


  1. 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;

}</lang>

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

7108899
B0YBKJ7
4065663
B0YBLH2
2282765
B0YBKL9
5579107
B0YBKR5
5852842
B0YBKT7

C++

<lang cpp>

  1. include <numeric>
  2. include <algorithm>
  3. include <cctype>
  4. include <iostream>
  5. include <stdexcept>
  6. include <iterator>
  7. 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() {

   char * 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";
  }

} </lang>

C#

<lang csharp>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;

}</lang>

Common Lisp

Implemented from scratch using the description on Wikipedia as specification.

Works with: ClozureCL

<lang lisp>(defun append-sedol-check-digit (sedol &key (start 0) (end (+ start 6)))

 (assert (<= 0 start end (length sedol)))
 (assert (= (- end start) 6))
 (labels ((char-value (char)

(let ((code (char-code char))) (cond ((<= #.(char-code #\A) code #.(char-code #\Z)) (+ 10 (- code #.(char-code #\A)))) ((<= #.(char-code #\a) code #.(char-code #\z)) (+ 10 (- code #.(char-code #\a)))) ((<= #.(char-code #\0) code #.(char-code #\9)) (- code #.(char-code #\0))) (t (error "unsupported character ~S" char))))))

   (loop 
      :with checksum = 0
      :for weight :in '(1 3 1 7 3 9)
      :for index :upfrom start
      :do (incf checksum (* weight (char-value (char sedol index))))
      :finally (let* ((posn (- 10 (mod checksum 10)))

(head (subseq sedol start end)) (tail (char "0123456789" posn))) (return (concatenate 'string head (list tail)))))))</lang>

D

Functional style: <lang d>import std.stdio, std.algorithm, std.string, std.numeric, std.ctype;

char checksum(in char[] sedol) in {

   assert(sedol.length == 6);
   foreach (c; sedol)
       assert(std.ctype.isdigit(c) ||
              (c > 'A' && c <= 'Z' && !canFind("AEIOU", c)));

} body {

   int c2v(in dchar c){ return isdigit(c) ? c-'0' : c-'A'+10; }
   int d = dotProduct(map!c2v(sedol), [1,3,1,7,3,9]);
   return cast(char)('0' + 10 - (d % 10));

}

void main() {

   foreach (sedol; "710889 B0YBKJ 406566 B0YBLH 228276
                    B0YBKL 557910 B0YBKR 585284 B0YBKT".split())
       writeln(sedol, checksum(sedol));

}</lang> Output:

7108899
B0YBKJ7
4065663
B0YBLH2
2282765
B0YBKL9
5579107
B0YBKR5
5852842
B0YBKT7

Longer faster lower-level version, same output: <lang d>import std.stdio, std.algorithm, std.string, std.numeric, std.ctype;

/*nothrow pure*/ char sedolChecksum(in char[] sedol) in {

   assert(sedol.length == 6, "SEDOL must be 6 chars long.");
   enum uint mask = 0b11_1110_1111_1011_1110_1110_1110;
   foreach (c; sedol)
       assert(std.ctype.isdigit(c) ||
              (c > 'A' && c <= 'Z' && ((1U << (c - 'A')) & mask)),
              "SEDOL with wrong char.");

} out(result) {

   assert(isdigit(result));
   int c2v(in dchar c){ return isdigit(c) ? c-'0' : c-'A'+10; }
   int d = dotProduct(map!c2v(sedol), [1,3,1,7,3,9]);
   assert((d + result - '0') % 10 == 0);

} body {

   enum int[] weights = [1, 3, 1, 7, 3, 9];
   int sum = 0;
   foreach (i, c; sedol) {
       if (std.ctype.isdigit(c))
           sum += (c - '0') * weights[i];
       else
           sum += (c - 'A' + 10) * weights[i];
   }
   return cast(char)('0' + 10 - (sum % 10));

}

void main() {

   foreach (s; ["710889", "B0YBKJ", "406566", "B0YBLH", "228276",
                "B0YBKL", "557910", "B0YBKR", "585284", "B0YBKT"])
       writeln(s, sedolChecksum(s));

}</lang> Short version (same output): <lang d>import std.stdio, std.algorithm, std.string, std.numeric;

auto checksum(string s) {

   auto m = map!q{ a>='0' && a<='9' ? a-'0' : a-'A'+10 }(s);
   return '0' + 10 - dotProduct(m, [1,3,1,7,3,9]) % 10;

}

void main() {

   foreach (sedol; "710889 B0YBKJ 406566 B0YBLH 228276
                    B0YBKL 557910 B0YBKR 585284 B0YBKT".split())
       writeln(sedol, checksum(sedol));

}</lang>

E

<lang 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()))

}</lang>

Forth

<lang 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</lang>

Fortran

Works with: Fortran version 90 and later

<lang fortran>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</lang> Output

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

Go

<lang go> package main

import (

   "fmt"
   "strings"

)

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"
   }
   var n, sum int
   for i, c := range code {
       switch {
       case c >= '0' && c <= '9':
           n = c - '0'
       case c >= 'B' && c <= 'Z' &&
               c != 'E' && c != 'I' && c != 'O' && c != 'U':
           n = c - 'B' + 11
       default:
           return "Invalid character"
       }
       sum += n*weight[i]
   }
   return string('9'-(sum-1)%10)

}

func main() {

   for _, s := range strings.Split(input, "\n", -1) {
       d := csd(s)
       if len(d) > 1 {
           fmt.Printf(":%s: %s\n", s, d)
       } else {
           fmt.Println(s + d)
       }
   }

} </lang> 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

Haskell

<lang 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" ]</lang>

Icon and Unicon

<lang Icon>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</lang>

J

There are several ways to perform this in J. This most closely follows the algorithmic description at Wikipedia: <lang j>sn =. '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' ac0 =: (, 10 | 1 3 1 7 3 9 +/@:* -)&.(sn i. |:)</lang> 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: <lang j>ac1 =: (, 10 | (10 - 1 3 1 7 3 9) +/@:* ])&.(sn i. |:)</lang> which reduces to: <lang j>ac1 =: (, 10 | 9 7 9 3 7 1 +/@:* ])&.(sn i. |:)</lang> 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: <lang j>ac2 =. (,"1 0 (841 $ '0987654321') {~ 1 3 1 7 3 9 +/ .*~ sn i. ])</lang> Which is more than twice as fast as even the optimized formulation (ac1), though it is slightly longer.

Java

<lang 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].*?"); } }</lang>

JavaScript

<lang 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 chars = char6.split();
   var sum = 0;
   for (var i in chars)
       sum += weight[i] * char_to_value(chars[i]);
   var check = (10 - sum%10) % 10;
   return check.toString();

}

function char_to_value(ch) {

   var val = ch.charCodeAt(0);
   if (48 <= val && val <= 57)
       return val - 48;
   else
       return val - 65 + 10;

}

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);
   }

}</lang> 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'

M4

<lang 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])</lang>

Modula-3

<lang modula3>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.</lang> Output:

7108899
B0YBKJ7
4065663
B0YBLH2
2282765
B0YBKL9
5579107
B0YBKR5
5852842
B0YBKT7

MUMPS

<lang 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)</lang>

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

OCaml

<lang 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" ]</lang>

Perl

This program reads from standard input. <lang perl>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";

}</lang>

Perl 6

Translation of: Perl

Note: Change %base36 and @weights from `my` to `constant` when Rakudo implements them. <lang perl6>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

>;</lang>

PHP

<lang 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";</lang>

PicoLisp

<lang PicoLisp>(de sedol (Str)

  (pack Str
     (char
        (+ `(char "0")
           (%
              (- 10
                 (%
                    (sum
                       '((W C)
                          (cond
                             ((>= "9" C "0")
                                (* W (- (char C) `(char "0"))) )
                             ((>= "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)) )</lang>

PureBasic

<lang 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</lang>

Python

<lang 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)</lang>

R

<lang r># Read in data from text connection datalines <- readLines(tc <- textConnection("710889 B0YBKJ 406566 B0YBLH 228276 B0YBKL 557910 B0YBKR 585284 B0YBKT")); close(tc)

  1. Check data valid

checkSedol <- function(datalines) {

  ok <- grep("^[[:digit:][:upper:]]{6}$", datalines)
  if(length(ok) < length(datalines))
  {
     stop("there are invalid lines") 
  }

} checkSedol(datalines)

  1. 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)

  1. Print in format requested

writeLines(withchkdig)</lang>

REXX

<lang 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*/

@.=

parse arg @.1 . /*allow 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

@abc='abcdefghijklmnopqrstuvwxyz' /*the alphabet, lower case. */ @abcU=@abc; upper @abcU /* " " upper case. */ alphaDigs='0123456789'@abcU /*legal chars, and then some. */ allowable=translate(alphaDigs,,'AEIOU') /*now, remove the vowels. */ allowable=space(allowable,0) /*now, remove the blanks. */ 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\==                /*process each specified SEDOL.  */
 sedol=@.j;  upper sedol              /*uppercase it, just in case.    */
 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
 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

/*─────────────────────────────────────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 </lang> 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

Ruby

<lang ruby>def char2value(c)

 raise "No vowels" if 'AEIOU'.include?(c)
 c.to_i(36)

end

Sedolweight = [1,3,1,7,3,9]

def checksum(sedol)

   tmp = sedol.split().zip(Sedolweight).map { |ch, weight|
             char2value(ch) * weight }.inject(0) { |sum, x|
             sum + x }
   ((10 - (tmp % 10)) % 10).to_s

end

for sedol in %w{

   710889
   B0YBKJ
   406566
   B0YBLH
   228276
   B0YBKL
   557910
   B0YBKR
   585284
   B0YBKT
   }
   puts sedol + checksum(sedol)

end</lang>

Scala

<lang 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

}</lang>

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

Smalltalk

Works with: GNU Smalltalk

<lang smalltalk>String extend [

 includesAnyOf: aSet [
     aSet do: [ :e | (self includes: e) ifTrue: [ ^true ] ].
     ^false
 ]

].</lang>

<lang smalltalk>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)
             ]
 ]

].</lang>

<lang smalltalk>|sedol| sedol := SEDOL new. { '710889'.

  'B0YBKJ'.
  '406566'.
  'B0YBLH'.
  '228276'.
  'B0YBKL'.
  '557910'.
  'B0YBKR'.
  '585284'.
  'B0YBKT' } do: [ :c | (sedol checked: c) displayNl ]</lang>

Standard ML

<lang sml>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" ];</lang>

Tcl

<lang 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

}</lang>

T-SQL

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

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

<lang tsql>

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

</lang>

TUSCRIPT

<lang 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 </lang> 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 

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.

<lang Ursala>#import std

  1. 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</lang>

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. <lang Ursala>weights = difference/*10 <1,3,1,7,3,9> checksum = remainder\10+ iprod</lang>

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

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

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.

<lang Ursala>lookups = (-:+ * ^/~&l product^|/charval ~&)* *-* -*weights alphabet iprod = sum:-0+ gang +^|(~&,~)*lNrXXK9 ^(~&,&h!)* lookups</lang> Here is a test program. <lang Ursala>#show+

examples = ^T(~&,~&h+ %nP+ checksum)*t

-[ 710889 B0YBKJ 406566 B0YBLH 228276 B0YBKL 557910 B0YBKR 585284 B0YBKT]-</lang> output:

7108899
B0YBKJ7
4065663
B0YBLH2
2282765
B0YBKL9
5579107
B0YBKR5
5852842
B0YBKT7