SEDOLs

Revision as of 14:02, 12 September 2019 by Langurmonkey (talk | contribs) (→‎{{header|Langur}}: implicit range with series())

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

Task
SEDOLs
You are encouraged to solve this task according to the task description, using any language you may know.
Task


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
Extra credit

Check each input is correctly formed, especially with respect to valid characters allowed in a SEDOL string.


Related tasks



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

ALGOL W

<lang algolw>begin

   % returns the check digit for the specified SEDOL %
   string(1) procedure sedolCheckDigit ( string(6) value sedol ) ;
   begin
       integer       checkSum, checkDigit;
       checkSum := 0;
       for cPos := 0 until 5 do begin
           string(1) c;
           integer   digit;
           c := sedol( cPos // 1 );
           if c >= "0" and c <= "9"
           then digit :=        decode( c ) - decode( "0" )
           else digit := 10 + ( decode( c ) - decode( "A" ) );
           checkSum := checkSum + ( ( case cPos + 1 of ( 1, 3, 1, 7, 3, 9 ) ) * digit )
       end for_cPos ;
       checkDigit := ( 10 - ( checkSum rem 10 ) ) rem 10;
       if checkDigit < 10
       then code( decode( "0" ) +   checkDigit        )
       else code( decode( "A" ) + ( checkDigit - 10 ) )
   end sedolCheckDigit ;
   % task test cases %
   procedure testCheckDigit ( string(6) value sedol; string(1) value expectedCheckDigit ) ;
   begin
       string(1) checkDigit;
       checkDigit := sedolCheckDigit( sedol );
       write( s_w := 0, sedol, checkDigit );
       if checkDigit not = expectedCheckDigit then writeon( " ?? expected: ", expectedCheckDigit )
   end testCheckDigit ;
   testCheckDigit( "710889", "9" );
   testCheckDigit( "B0YBKJ", "7" );
   testCheckDigit( "406566", "3" );
   testCheckDigit( "B0YBLH", "2" );
   testCheckDigit( "228276", "5" );
   testCheckDigit( "B0YBKL", "9" );
   testCheckDigit( "557910", "7" );
   testCheckDigit( "B0YBKR", "5" );
   testCheckDigit( "585284", "2" );
   testCheckDigit( "B0YBKT", "7" );
   testCheckDigit( "B00030", "0" )

end.</lang>

Output:
7108899
B0YBKJ7
4065663
B0YBLH2
2282765
B0YBKL9
5579107
B0YBKR5
5852842
B0YBKT7
B000300

AutoHotkey

ahk forum: discussion

Full

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

}</lang>

Short

Works with: AutoHotkey 1.1

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

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

BBC BASIC

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

Output:

7108899
B0YBKJ7
4065663
B0YBLH2
2282765
B0YBKL9
5579107
B0YBKR5
5852842
B0YBKT7
B000300

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 <cctype>
  3. include <iostream>
  4. include <string>


template<typename result_sink_t> auto sedol_checksum(std::string const& sedol, result_sink_t result_sink) {

   if(sedol.size() != 6) 
       return result_sink(0, "length of sedol string != 6");
       
   const char * valid_chars = "BCDFGHJKLMNPQRSTVWXYZ0123456789";
   if(sedol.find_first_not_of(valid_chars) != std::string::npos)
       return result_sink(0, "sedol string contains disallowed characters");
  
   const int weights[] = {1,3,1,7,3,9};    
   auto weighted_sum = std::inner_product(sedol.begin(), sedol.end(), weights, 0
                                          , [](int acc, int prod){ return acc + prod; }
                                          , [](char c, int weight){ return (std::isalpha(c) ? c -'A' + 10 : c - '0') * weight; }
                                            );
   return result_sink((10 - (weighted_sum % 10)) % 10, nullptr);

}

int main() {

   using namespace std; 
   string inputs[] = {
      "710889", "B0YBKJ", "406566", "B0YBLH", "228276", "B0YBKL", 
      "557910", "B0YBKR", "585284", "B0YBKT", "B00030"
  }; 
  for(auto const & sedol : inputs)
  {
       sedol_checksum(sedol, [&](auto sum, char const * errorMessage)
       {
           if(errorMessage)
               cout << "error for sedol: " << sedol << " message: " <<  errorMessage << "\n";
           else
               cout << sedol << sum << "\n"; 
       });
  }
  return 0;

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

Caché ObjectScript

<lang cos>Class Utils.Check [ Abstract ] {

ClassMethod SEDOL(x As %String) As %Boolean { // https://en.wikipedia.org/wiki/SEDOL IF x'?1(7N,1U5UN1N) QUIT 0 IF x'=$TRANSLATE(x,"AEIOU") QUIT 0 SET cd=$EXTRACT(x,*), x=$EXTRACT(x,1,*-1) SET wgt="1317391", t=0 FOR i=1:1:$LENGTH(x) { SET n=$EXTRACT(x,i) IF n'=+n SET n=$ASCII(n)-55 SET t=t+(n*$EXTRACT(wgt,i)) } QUIT cd=((10-(t#10))#10) }

}</lang>

Examples:
USER>For  { Read s Quit:s=""  Write ": "_##class(Utils.Check).SEDOL(s), ! }
7108899: 1
B0YBKJ7: 1
4065663: 1
B0YBLH2: 1
2282765: 1
B0YBKL9: 1
5579107: 1
B0YBKR5: 1
5852842: 1
B0YBKT7: 1
B000300: 1

USER>

Clojure

<lang Clojure>(defn sedols [xs]

 (letfn [(sedol [ys] (let [weights [1 3 1 7 3 9]
                           convtonum (map #(Character/getNumericValue %) ys)
                           check (-> (reduce + (map * weights convtonum)) (rem 10) (->> (- 10)) (rem 10))]
                       (str ys check)))]
   (map #(sedol %) xs)))</lang>

COBOL

Works with: GNU Cobol version 2.0

<lang cobol> >>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.</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))
 (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))))))</lang>

D

Functional Version

<lang d>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 digits[10 - (d % 10)];

}

void main() {

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

}</lang>

Output:
7108899
B0YBKJ7
4065663
B0YBLH2
2282765
B0YBKL9
5579107
B0YBKR5
5852842
B0YBKT7

Imperative Version

Longer, faster lower-level version, same output. <lang d>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);

}</lang>

Short Version

Same output. <lang d>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);

}</lang>

Delphi

<lang Delphi>program Sedol;

{$APPTYPE CONSOLE}

uses

 SysUtils;


const

 SEDOL_CHR_COUNT = 6;
 DIGITS = ['0'..'9'];
 LETTERS = ['A'..'Z'];
 VOWELS = ['A', 'E', 'I', 'O', 'U'];
 ACCEPTABLE_CHRS = DIGITS + LETTERS - VOWELS;
 WEIGHTS : ARRAY [1..SEDOL_CHR_COUNT] of integer = (1, 3, 1, 7, 3, 9);
 LETTER_OFFSET = 9;


function AddSedolCheckDigit(Sedol : string) : string; var

 iChr : integer;
 Checksum : integer;
 CheckDigit : char;

begin

 if Sedol <> uppercase(Sedol) then
   raise ERangeError.CreateFmt('%s contains lower case characters',[Sedol]);
 if length(Sedol) <> SEDOL_CHR_COUNT then
   raise ERangeError.CreateFmt('"%s" length is invalid. Should be 6 characters',[Sedol]);
 Checksum := 0;
 for iChr := 1 to SEDOL_CHR_COUNT do
 begin
   if Sedol[iChr] in Vowels then
     raise ERangeError.CreateFmt('%s contains a vowel (%s) at chr %d',[Sedol, Sedol[iChr], iChr]);
   if not (Sedol[iChr] in ACCEPTABLE_CHRS) then
     raise ERangeError.CreateFmt('%s contains an invalid chr (%s) at position %d',[Sedol, Sedol[iChr], iChr]);
   if Sedol[iChr] in DIGITS then
     Checksum := Checksum + (ord(Sedol[iChr]) - ord('0')) * WEIGHTS[iChr]
   else
     Checksum := Checksum + (ord(Sedol[iChr]) - ord('A') + 1 + LETTER_OFFSET) * WEIGHTS[iChr];
 end;
 Checksum := (Checksum mod 10);
 if Checksum <> 0 then
   Checksum := 10 - Checksum;
 CheckDigit := chr(CheckSum + ord('0'));
 Result := Sedol + CheckDigit;

end;


procedure Test(First6 : string); begin

 writeln(First6, ' becomes ', AddSedolCheckDigit(First6));

end;


begin

 try
   Test('710889');
   Test('B0YBKJ');
   Test('406566');
   Test('B0YBLH');
   Test('228276');
   Test('B0YBKL');
   Test('557910');
   Test('B0YBKR');
   Test('585284');
   Test('B0YBKT');
   Test('B00030');
 except
   on E : Exception do
     writeln(E.Message);
 end;
 readln;

end. </lang>

Output:

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

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>

Elixir

Translation of: Ruby

<lang elixir>defmodule SEDOL do

 @sedol_char  "0123456789BCDFGHJKLMNPQRSTVWXYZ" |> String.codepoints
 @sedolweight  [1,3,1,7,3,9]
 
 defp char2value(c) do
   unless c in @sedol_char, do: raise ArgumentError, "No vowels"
   String.to_integer(c,36)
 end
 
 def checksum(sedol) do
   if String.length(sedol) != length(@sedolweight), do: raise ArgumentError, "Invalid length"
   sum = Enum.zip(String.codepoints(sedol), @sedolweight)
         |> Enum.map(fn {ch, weight} -> char2value(ch) * weight end)
         |> Enum.sum
   to_string(rem(10 - rem(sum, 10), 10))
 end

end

data = ~w{

         710889
         B0YBKJ
         406566
         B0YBLH
         228276
         B0YBKL
         557910
         B0YBKR
         585284
         B0YBKT
         B00030
         C0000
         1234567
         00000A
        }

Enum.each(data, fn sedol ->

 :io.fwrite "~-8s ", [sedol]
 try do
   IO.puts sedol <> SEDOL.checksum(sedol)
 rescue
   e in ArgumentError -> IO.inspect e
 end

end)</lang>

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{message: "Invalid length"}
1234567  %ArgumentError{message: "Invalid length"}
00000A   %ArgumentError{message: "No vowels"}

Excel VBA

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

F#

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

Factor

<lang factor>USING: combinators combinators.short-circuit formatting io kernel math math.parser regexp sequences unicode ; IN: rosetta-code.sedols

<PRIVATE

CONSTANT: input {

   "710889" "B0YBKJ" "406566" "B0YBLH" "228276" "B0YBKL"
   "557910" "B0YBKR" "585284" "B0YBKT" "B00030" "AEIOUA"
   "123"    ""       "B_7K90"

}

CONSTANT: weights B{ 1 3 1 7 3 9 1 }

sedol-error ( seq -- err-str )
   {
       { [ dup empty? ] [ drop "no data" ] }
       { [ dup length 6 = not ] [ drop "invalid length" ] }
       [ drop "invalid char(s)" ]
   } cond "*error* " prepend ;
sedol-valid? ( seq -- ? )
   { [ length 6 = ] [ R/ [0-9BCDFGHJ-NP-TV-Z]+/ matches? ] } 1&& ;
sedol-value ( m -- n ) dup digit? [ digit> ] [ 55 - ] if ;
sedol-checksum ( seq -- n )
   [ sedol-value ] { } map-as weights [ * ] 2map sum ;
(sedol-check-digit) ( seq -- str )
   sedol-checksum 10 mod 10 swap - 10 mod number>string ;

PRIVATE>

sedol-check-digit ( seq -- str )
   dup sedol-valid? [ (sedol-check-digit) ] [ sedol-error ] if ;
sedol-demo ( -- )
   "SEDOL   Check digit\n======  ===========" print
   input [ dup sedol-check-digit "%-6s  %s\n" printf ] each ;

MAIN: sedol-demo</lang>

Output:
SEDOL   Check digit
======  ===========
710889  9
B0YBKJ  7
406566  3
B0YBLH  2
228276  5
B0YBKL  9
557910  7
B0YBKR  5
585284  2
B0YBKT  7
B00030  0
AEIOUA  *error* invalid char(s)
123     *error* invalid length
        *error* no data
B_7K90  *error* invalid char(s)

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

FreeBASIC

<lang FreeBASIC>' version 05-07-2015 ' compile with: fbc -s console

Function check_sedol(input_nr As String) As Integer

   input_nr = Trim(input_nr)
   Dim As Integer i, j, x, nr_begin, sum
   Dim As String ch, legal = "AEIOU0123456789BCDFGHJKLMNPQRSTVWXYZ"
   Dim As Integer weight(0 To ...) = { 1, 3, 1, 7, 3, 9, 1}
   x = Len(input_nr)
   If x < 6 Or x > 7 Then
       Return -99 ' to long or to short
   End If
   For i = 0 To 5
       ch = Chr(input_nr[i])
       j = InStr(legal,ch)
       If j < 6 Then
           Return -90+j ' not a legal char. or a vowel
       End If
       j = ch[0] - Asc("0")
       If j > 9 Then j = j + (Asc("0") + 10- Asc("A"))
       If i = 0 AndAlso j < 10 Then nr_begin = 1
       If nr_begin = 1 AndAlso i > 0 Then
           If j > 9 Then Return -97 ' first is number then all be numbers
       End If
       sum = sum + j * weight(i)
   Next
   sum= ((10 - (sum Mod 10)) Mod 10)
   If x = 7 Then
       j=input_nr[6] - Asc("0") ' checksum digit is only number
       If j = sum Then
           Return 100+sum ' correct
       Else
           Return -98   ' wrong
       End If
   End If
   Return sum ' checksum digit

End Function

Sub sedol(in As String)

   Dim As Integer checksum = check_sedol(in)
   Print(in);
   Select Case checksum
       Case -99
           Print " Illegal SEDOL: wrong length"
       Case -98
           Print " Illegal SEDOL: checksum digits do not match"
       Case -97
           Print " Illegal SEDOL: starts with number, may only contain numbers"
       Case -90
           Print " Illegal SEDOL: illegal character"
       Case -89 To -85
           Print " Illegal SEDOL: No vowels allowed"
       Case Is > 99
           Print " Valid SEDOL: checksums match"
       Case Else
           Print " checksum calculated : ";in;Str(checksum)
   End Select

End Sub ' ------=< MAIN >=------

Dim As Integer k,checksum Dim As String in(1 To ...) = {"710889", "B0YBKJ", "406566", "B0YBLH",_

                             "228276", "B0YBKL", "557910", "B0YBKR",_
                                       "585284", "B0YBKT", "B00030"}

Print "Calculated checksum" For k = 1 To UBound(in) : sedol(in(k)) : Next

Print : Print "Check checksum" Dim As String in1(1 To ...) = {"7108899", "B0YBKJ7", "4065663", "B0YBLH2",_

                               "2282765", "B0YBKL9","5579107", "B0YBKR5",_
                                         "5852842", "B0YBKT7", "B000300"}

For k = 1 To UBound(in1) : sedol(in1(k)) : Next

Print : Print "Error test" Dim As String errors(1 To ...) = {"12", "1234567890", "1B0000", "123 45",_

                                                    "A00000", "B000301"}

For k = 1 To UBound(errors) : sedol(errors(k)) : Next

' empty keyboard buffer While Inkey <> "" : Wend Print : Print "hit any key to end program" Sleep End</lang>

Output:
Calculated checksum
710889 checksum calculated : 7108899
B0YBKJ checksum calculated : B0YBKJ7
406566 checksum calculated : 4065663
B0YBLH checksum calculated : B0YBLH2
228276 checksum calculated : 2282765
B0YBKL checksum calculated : B0YBKL9
557910 checksum calculated : 5579107
B0YBKR checksum calculated : B0YBKR5
585284 checksum calculated : 5852842
B0YBKT checksum calculated : B0YBKT7
B00030 checksum calculated : B000300

Check checksum
7108899 Valid SEDOL: checksums match
B0YBKJ7 Valid SEDOL: checksums match
4065663 Valid SEDOL: checksums match
B0YBLH2 Valid SEDOL: checksums match
2282765 Valid SEDOL: checksums match
B0YBKL9 Valid SEDOL: checksums match
5579107 Valid SEDOL: checksums match
B0YBKR5 Valid SEDOL: checksums match
5852842 Valid SEDOL: checksums match
B0YBKT7 Valid SEDOL: checksums match
B000300 Valid SEDOL: checksums match

Error test
12 Illegal SEDOL: wrong length
1234567890 Illegal SEDOL: wrong length
1B0000 Illegal SEDOL: starts with number, may only contain numbers
123 45 Illegal SEDOL: illegal character
A00000 Illegal SEDOL: No vowels allowed
B000301 Illegal SEDOL: checksum digits do not match

Gambas

Click this link to run this code <lang gambas>Public Sub Main() Dim byWeight As Byte[] = [1, 3, 1, 7, 3, 9, 1] Dim byCount, byCompute As Byte Dim siTotal As Short Dim sWork As New String[] Dim sToProcess As String[] = ["710889", "B0YBKJ", "406566", "B0YBLH", "228276", "B0YBKL",

                             "557910", "B0YBKR", "585284", "B0YBKT", "B00030"]

For byCompute = 0 To sToProcess.Max

 For byCount = 1 To 6 
   If IsLetter(Mid(sToProcess[byCompute], byCount, 1)) Then 
     sWork.Add(Str(Asc(Mid(sToProcess[byCompute], byCount, 1)) - 55) * byWeight[byCount - 1])
   Else
     sWork.Add(Val(Mid(sToProcess[byCompute], byCount, 1)) * byWeight[byCount - 1])
   End If
 Next
 For byCount = 0 To 5
   siTotal += Val(sWork[byCount])
 Next
 siTotal = (10 - (siTotal Mod 10)) Mod 10
 Print sToProcess[byCompute] & " = " & sToProcess[byCompute] & siTotal
 sWork.Clear()
 siTotal = 0

Next

End</lang> Output:

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

Go

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

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

Groovy

<lang 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) }</lang> Test Code: <lang groovy>[ '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()

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

Haskell

<lang haskell>import Data.Char (isDigit, isAsciiUpper, ord)

checkSum :: String -> String checkSum =

 show .
 (`rem` 10) .
 (-) 10 . (`rem` 10) . sum . zipWith (*) [1, 3, 1, 7, 3, 9] . fmap charValue

charValue :: Char -> Int charValue c

 | c `elem` "AEIOU" = error "No vowels."
 | isDigit c = ord c - ord '0'
 | isAsciiUpper c = ord c - ord 'A' + 10

-- TEST ---------------------------------------------------------------------- main :: IO () main =

 mapM_
   (putStrLn . ((++) <*> checkSum))
   [ "710889"
   , "B0YBKJ"
   , "406566"
   , "B0YBLH"
   , "228276"
   , "B0YBKL"
   , "557910"
   , "B0YBKR"
   , "585284"
   , "B0YBKT"
   , "B00030"
   ]</lang>
Output:
7108899
B0YBKJ7
4065663
B0YBLH2
2282765
B0YBKL9
5579107
B0YBKR5
5852842
B0YBKT7
B000300

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

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>ac2 =: (, 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 modulus isn't necessary. 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>ac3 =: (,"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 (ac2), 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

Imperative

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

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

Functional

<lang javascript>(() => {

   'use strict';
   const main = () => {
       // checkSumLR :: String -> Either String String
       const checkSumLR = s => {
           const
               tpl = partitionEithers(map(charValueLR, s));
           return 0 < tpl[0].length ? (
               Left(s + ' -> ' + unwords(tpl[0]))
           ) : Right(rem(10 - rem(
               sum(zipWith(
                   (a, b) => a * b,
                   [1, 3, 1, 7, 3, 9],
                   tpl[1]
               )), 10
           ), 10).toString());
       };
       // charValue :: Char -> Either String Int
       const charValueLR = c =>
           isAlpha(c) ? (
               isUpper(c) ? (
                   elem(c, 'AEIOU') ? Left(
                       'Unexpected vowel: ' + c
                   ) : Right(ord(c) - ord('A') + 10)
               ) : Left('Unexpected lower case character: ' + c)
           ) : isDigit(c) ? Right(
               parseInt(c, 10)
           ) : Left('Unexpected character: ' + c);
       // TESTS ------------------------------------------
       const [problems, checks] = Array.from(
           partitionEithers(map(s => bindLR(
                   checkSumLR(s),
                   c => Right(s + c)
               ),
               [
                   "710889", "B0YBKJ", "406566",
                   "B0YBLH", "228276", "B0YBKL",
                   "557910", "B0YBKR", "585284",
                   "B0YBKT", "B00030"
               ]
           ))
       );
       return unlines(
           0 < problems.length ? (
               problems
           ) : checks
       );
   };
   // GENERIC FUNCTIONS ----------------------------
   // Left :: a -> Either a b
   const Left = x => ({
       type: 'Either',
       Left: x
   });
   // Right :: b -> Either a b
   const Right = x => ({
       type: 'Either',
       Right: x
   });
   // Tuple (,) :: a -> b -> (a, b)
   const Tuple = (a, b) => ({
       type: 'Tuple',
       '0': a,
       '1': b,
       length: 2
   });
   // bindLR (>>=) :: Either a -> (a -> Either b) -> Either b
   const bindLR = (m, mf) =>
       undefined !== m.Left ? (
           m
       ) : mf(m.Right);
   // elem :: Eq a => a -> [a] -> Bool
   const elem = (x, xs) => xs.includes(x);
   // isAlpha :: Char -> Bool
   const isAlpha = c =>
       /[A-Za-z\u00C0-\u00FF]/.test(c);
   // isDigit :: Char -> Bool
   const isDigit = c => {
       const n = ord(c);
       return 48 <= n && 57 >= n;
   };
   // isUpper :: Char -> Bool
   const isUpper = c =>
       /[A-Z]/.test(c);
   // Returns Infinity over objects without finite length.
   // This enables zip and zipWith to choose the shorter
   // argument when one is non-finite, like cycle, repeat etc
   // length :: [a] -> Int
   const length = xs =>
       (Array.isArray(xs) || 'string' === typeof xs) ? (
           xs.length
       ) : Infinity;
   // map :: (a -> b) -> [a] -> [b]
   const map = (f, xs) =>
       (Array.isArray(xs) ? (
           xs
       ) : xs.split()).map(f);
   // ord :: Char -> Int
   const ord = c => c.codePointAt(0);
   // partitionEithers :: [Either a b] -> ([a],[b])
   const partitionEithers = xs =>
       xs.reduce(
           (a, x) => undefined !== x.Left ? (
               Tuple(a[0].concat(x.Left), a[1])
           ) : Tuple(a[0], a[1].concat(x.Right)),
           Tuple([], [])
       );
   // rem :: Int -> Int -> Int
   const rem = (n, m) => n % m;
   // sum :: [Num] -> Num
   const sum = xs => xs.reduce((a, x) => a + x, 0);
   // take :: Int -> [a] -> [a]
   // take :: Int -> String -> String
   const take = (n, xs) =>
       'GeneratorFunction' !== xs.constructor.constructor.name ? (
           xs.slice(0, n)
       ) : [].concat.apply([], Array.from({
           length: n
       }, () => {
           const x = xs.next();
           return x.done ? [] : [x.value];
       }));
   // unlines :: [String] -> String
   const unlines = xs => xs.join('\n');
   // unwords :: [String] -> String
   const unwords = xs => xs.join(' ');
   // zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
   const zipWith = (f, xs, ys) => {
       const
           lng = Math.min(length(xs), length(ys)),
           as = take(lng, xs),
           bs = take(lng, ys);
       return Array.from({
           length: lng
       }, (_, i) => f(as[i], bs[i], i));
   };
   // MAIN ---
   return main();

})();</lang>

Output:
7108899
B0YBKJ7
4065663
B0YBLH2
2282765
B0YBKL9
5579107
B0YBKR5
5852842
B0YBKT7
B000300

jq

Works with: jq version 1.4

This implementation accepts strings with lowercase letters, but converts them to uppercase. <lang jq>def ascii_upcase:

 explode | map( if 97 <= . and . <= 122 then . - 32  else . end) | implode;

def sedol_checksum:

 def encode(a): 10 + (a|explode[0]) - ("A"|explode[0]);
 . as $sed
 | [1,3,1,7,3,9] as $sw
 | reduce range(0;6) as $i
     (0;
      $sed[$i:$i+1] as $c
      | if ( "0123456789" | index($c) )
        then . + ($c|tonumber) * $sw[$i]
        else . + encode($c) * $sw[$i]
        end )
 | (10 - (. % 10)) % 10 ;
  1. error on error, else pass input to output

def check_valid_sedol:

 def has_vowel: 
   ("AEIOU"|explode) as $vowels
   | reduce explode[] as $c
       (false; if . then . else $vowels|index($c) end);
 if has_vowel then error( "\(.) is not a valid SEDOL code" )
 else .
 end
 | if length > 7 or length < 6 then
     error( "\(.) is too long or too short to be valid SEDOL")
   else .
   end;

def sedolize:

 ascii_upcase as $in
 | $in
 | check_valid_sedol
 | .[0:6] as $sedol
 | ($sedol | sedol_checksum | tostring) as $sedolcheck 
 | ($sedol + $sedolcheck) as $ans
 | if length == 7 and $ans != $in then
        $ans + " (original \($in) has wrong checksum digit"
   else $ans
   end ;

sedolize</lang>

Output:
# Assuming sedol.txt contains the input in the task description
$ jq -R -r -M -f sedol.jq sedol.txt
... (output is exactly as shown in the task description)

Julia

Works with: Julia version 0.6

<lang julia>using Base.Test

function appendchecksum(chars::AbstractString)

   if !all(isalnum, chars) throw(ArgumentError("invalid SEDOL number '$chars'")) end
   weights = [1, 3, 1, 7, 3, 9, 1]
   s = 0
   for (w, c) in zip(weights, chars)
       s += w * parse(Int, c, 36)
   end
   return string(chars, (10 - s % 10) % 10)

end

tests = ["710889", "B0YBKJ", "406566", "B0YBLH", "228276", "B0YBKL", "557910", "B0YBKR", "585284", "B0YBKT", "B00030"] csums = ["7108899", "B0YBKJ7", "4065663", "B0YBLH2", "2282765", "B0YBKL9", "5579107", "B0YBKR5", "5852842", "B0YBKT7", "B000300"]

@testset "Checksums" begin

   for (t, c) in zip(tests, csums)
       @test appendchecksum(t) == c
   end

end</lang>

Output:
Test Summary: | Pass  Total
Checksums     |   11     11

Kotlin

<lang scala>// version 1.1.0

val weights = listOf(1, 3, 1, 7, 3, 9, 1)

fun sedol7(sedol6: String): String {

   if (sedol6.length != 6) throw IllegalArgumentException("Length of argument string must be 6")
   var sum = 0
   for (i in 0..5) {
       val c = sedol6[i] 
       val v = when (c) {
           in '0'..'9' -> c.toInt() - 48  
           in 'A'..'Z' -> c.toInt() - 55
           else        -> throw IllegalArgumentException("Argument string contains an invalid character")
       }
       sum += v * weights[i]
   }
   val check = (10 - (sum % 10)) % 10 
   return sedol6 + (check + 48).toChar()

}

fun main(args: Array<String>) {

   val sedol6s = listOf("710889", "B0YBKJ", "406566", "B0YBLH", "228276", "B0YBKL",
                        "557910", "B0YBKR", "585284", "B0YBKT", "B00030")
   for (sedol6 in sedol6s) println("$sedol6 -> ${sedol7(sedol6)}")

}</lang>

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

Langur

Translation of: Go

<lang Langur>val .weight = [1,3,1,7,3,9]

val .csd = f(.code) {

   given len(.code) {
       case 0:
           return "nada, zip, zilch"
       case != 6:
           return "invalid length"
   }
   if matching(re/[^B-DF-HJ-NP-TV-Z0-9]/, .code) {
       return "invalid character(s)"
   }
   val .sum = foldfrom(
       f(.sum, .i, .c) .sum + toNumber(.c, 36) x .weight[.i],
       0,
       series len .code,
       split ZLS, .code,
   )
   toString 9 - (.sum - 1) rem 10

}

val .h = h{

   # invalid...
   "": 0,
   "123": 0,
   "A00030": 0,
   "E00030": 0,
   "I00030": 0,
   "O00030": 0,
   "U00030": 0,
   "β00030": 0,
   # valid...
   "710889": 9,
   "B0YBKJ": 7,
   "406566": 3,
   "B0YBLH": 2,
   "228276": 5,
   "B0YBKL": 9,
   "557910": 7,
   "B0YBKR": 5,
   "585284": 2,
   "B0YBKT": 7,
   "B00030": 0,

}

for .input in sort(keys .h) {

   val .d = .csd(.input)
   if len(.d) > 1 {
       writeln .input, ": ", .d
   } else {
       val .expect = toString .h[.input]
       write .input, .d
       writeln if .expect == .d {""} else {
           $" (SEDOL test failed; expected check digit \.expect;)"}
   }

}</lang>

Output:
: nada, zip, zilch
123: invalid length
2282765
4065663
5579107
5852842
7108899
A00030: invalid character(s)
B000300
B0YBKJ7
B0YBKL9
B0YBKR5
B0YBKT7
B0YBLH2
E00030: invalid character(s)
I00030: invalid character(s)
O00030: invalid character(s)
U00030: invalid character(s)
β00030: invalid character(s)

Liberty BASIC

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

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>

Mathematica

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

Mercury

<lang>:- 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').</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

Nim

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

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
 | _ -> assert false

let sedolweight = [1;3;1;7;3;9]

let explode s =

 s |> String.to_seq |> List.of_seq

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>

Oforth

<lang Oforth>func: sedol(s)

  [ 1, 3, 1, 7, 3, 9 ] s 
  zipWith(#[ dup isDigit ifTrue: [ '0' - ] else: [ 'A' - 10 + ] * ]) sum
  10 mod 10 swap - 10 mod
  StringBuffer new s << swap '0' + <<c ;</lang>
Output:
[ "710889", "B0YBKJ", "406566", "B0YBLH", "228276", "B0YBKL", "557910", "B0YBKR", "585284", "B0YBKT", "B00030" ]
apply(#[ sedol println ])
7108899
B0YBKJ7
4065663
B0YBLH2
2282765
B0YBKL9
5579107
B0YBKR5
5852842
B0YBKT7
B000300

Pascal

Works with: Free_Pascal

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

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

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
Works with: rakudo version 2015-12-17

<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 = (flat 0..9, 'A'..'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>

Phix

<lang Phix>type string6(object s)

   return string(s) and length(s)=6

end type

type sedolch(integer ch)

   return ch>='0' and ch<='Z' and (ch<='9' or ch>='A') and not find(ch,"AEIOU")

end type

function sedol(string6 t) sedolch c integer s = 0

   for i=1 to 6 do
       c = t[i]
       s += iff(c>='A'?c-'A'+10:c-'0')*{1,3,1,7,3,9}[i]
   end for
   return t & mod(10-mod(s,10),10)+'0'

end function

constant tests = {"710889",

                 "B0YBKJ",
                 "406566",
                 "B0YBLH",
                 "228276",
                 "B0YBKL",
                 "557910",
                 "B0YBKR",
                 "585284",
                 "B0YBKT",
                 "B00030"}

for i=1 to length(tests) do

   ?sedol(tests[i])

end for</lang>

Output:
"7108899"
"B0YBKJ7"
"4065663"
"B0YBLH2"
"2282765"
"B0YBKL9"
"5579107"
"B0YBKR5"
"5852842"
"B0YBKT7"
"B000300"

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

PL/I

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

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

Potion

No extra credit. <lang potion>sedolnum = (c) :

  if ("0" ord <= c ord and c ord <= "9" ord): c number integer.
  else: 10 + c ord - "A" ord.

.

sedol = (str) :

  weight = (1, 3, 1, 7, 3, 9)
  sum = 0
  6 times (i) :
     sum = sum + sedolnum(str(i)) * weight(i)
  .
  (str, (10 - (sum % 10)) % 10) join

.</lang>

PowerShell

<lang powershell>function Add-SEDOLCheckDigit

   {
   Param ( #  Validate input as six-digit SEDOL number
           [ValidatePattern( "^[0123456789bcdfghjklmnpqrstvwxyz]{6}$" )]
           [parameter ( Mandatory = $True ) ]
           [string]
           $SixDigitSEDOL )

   #  Convert to array of single character strings, using type char as an intermediary
   $SEDOL = [string[]][char[]]$SixDigitSEDOL

   #  Define place weights
   $Weight = @( 1, 3, 1, 7, 3, 9 )

   #  Define character values (implicit in 0-based location within string)
   $Characters = "0123456789abcdefghijklmnopqrstuvwxyz"

   $CheckSum = 0
  
   #  For each digit, multiply the character value by the weight and add to check sum
   0..5 | ForEach { $CheckSum += $Characters.IndexOf( $SEDOL[$_].ToLower() ) * $Weight[$_] }

   #  Derive the check digit from the partial check sum
   $CheckDigit = ( 10 - $CheckSum % 10 ) % 10

   #  Return concatenated result
   return ( $SixDigitSEDOL + $CheckDigit )
   }

  1. Test

$List = @(

   "710889"
   "B0YBKJ"
   "406566"
   "B0YBLH"
   "228276"
   "B0YBKL"
   "557910"
   "B0YBKR"
   "585284"
   "B0YBKT"
   "B00030"
   )

ForEach ( $PartialSEDOL in $List )

   {
   Add-SEDOLCheckDigit -SixDigitSEDOL $PartialSEDOL
   }</lang>
Output:
7108899
B0YBKJ7
4065663
B0YBLH2
2282765
B0YBKL9
5579107
B0YBKR5
5852842
B0YBKT7
B000300

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>


Or, combining reduce with an option type – handling disallowed characters without assertion errors:

Works with: Python version 3.7

<lang python>SEDOL checksum digits

from functools import reduce


  1. sedolCheckSumDigitLR :: String -> Either String Char

def sedolCheckSumDigitLR(s):

   Either an explanatory message, or a
      checksum digit character to append
      to a given six-character SEDOL string.
   
   def goLR(lr, cn):
       c, n = cn
       return bindLR(lr)(
           lambda a: bindLR(sedolValLR(c))(
               lambda x: Right(a + x * n)
           )
       )
   return bindLR(
       reduce(
           goLR,
           zip(s, [1, 3, 1, 7, 3, 9]),
           Right(0)
       )
   )(lambda d: Right(str((10 - (d % 10)) % 10)))


  1. sedolValLR :: Char -> Either String Char

def sedolValLR(c):

   Either an explanatory message, or the
      SEDOL value of a given character.
   
   return Right(int(c, 36)) if (
       c not in 'AEIOU'
   ) else Left('Unexpected vowel in SEDOL string: ' + c)


  1. TEST -------------------------------------------------

def main():

   Append checksums where valid.
   print(
       fTable(__doc__ + ':\n')(str)(
           either(str)(str)
       )(sedolCheckSumDigitLR)(
           710889
              B0YBKJ
              406566
              B0YBLH
              228276
              B0YBKL
              BOYBKL
              557910
              B0YBKR
              585284
              B0YBKT
              B00030
           .split()
       )
   )


  1. GENERIC -------------------------------------------------


  1. Left :: a -> Either a b

def Left(x):

   Constructor for an empty Either (option type) value
      with an associated string.
   return {'type': 'Either', 'Right': None, 'Left': x}


  1. Right :: b -> Either a b

def Right(x):

   Constructor for a populated Either (option type) value
   return {'type': 'Either', 'Left': None, 'Right': x}


  1. bindLR (>>=) :: Either a -> (a -> Either b) -> Either b

def bindLR(m):

   Either monad injection operator.
      Two computations sequentially composed,
      with any value produced by the first
      passed as an argument to the second.
   return lambda mf: (
       mf(m.get('Right')) if None is m.get('Left') else m
   )


  1. compose (<<<) :: (b -> c) -> (a -> b) -> a -> c

def compose(g):

   Right to left function composition.
   return lambda f: lambda x: g(f(x))


  1. either :: (a -> c) -> (b -> c) -> Either a b -> c

def either(fl):

   The application of fl to e if e is a Left value,
      or the application of fr to e if e is a Right value.
   return lambda fr: lambda e: fl(e['Left']) if (
       None is e['Right']
   ) else fr(e['Right'])


  1. fTable :: String -> (a -> String) ->
  2. (b -> String) ->
  3. (a -> b) -> [a] -> String

def fTable(s):

   Heading -> x display function -> fx display function ->
         f -> value list -> tabular string.
   def go(xShow, fxShow, f, xs):
       w = max(map(compose(len)(xShow), xs))
       return s + '\n' + '\n'.join([
           xShow(x).rjust(w, ' ') + ' -> ' + fxShow(f(x)) for x in xs
       ])
   return lambda xShow: lambda fxShow: (
       lambda f: lambda xs: go(
           xShow, fxShow, f, xs
       )
   )


  1. MAIN ---

if __name__ == '__main__':

   main()</lang>
Output:
SEDOL checksum digits:

710889 -> 9
B0YBKJ -> 7
406566 -> 3
B0YBLH -> 2
228276 -> 5
B0YBKL -> 9
BOYBKL -> Unexpected vowel in SEDOL string: O
557910 -> 7
B0YBKR -> 5
585284 -> 2
B0YBKT -> 7
B00030 -> 0

Q

<lang q>scd:{

   v:{("i"$x) - ?[("0"<=x) & x<="9"; "i"$"0"; -10+"i"$"A"]} each x; / Turn characters of SEDOL into their values
   w:sum v*1 3 1 7 3 9;        / Weighted sum of values
   d:(10 - w mod 10) mod 10;   / Check digit value
   x,"c"$(("i"$"0")+d)         / Append to SEDOL

}

scd each ("710889";"B0YBKJ";"406566";"B0YBLH";"228276";"B0YBKL";"557910";"B0YBKR";"585284";"B0YBKT";"B00030")</lang>

R

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

Racket

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

Output:

7108899
B0YBKJ7
4065663
B0YBLH2
2282765
B0YBKL9
5579107
B0YBKR5
5852842
B0YBKT7
B000300

Extra Credit!
#f
invalid checksum

REXX

         ╔════════════════════════════════════════════════════════════════════╗
         ║  If the SEDOL is 6 characters, a check digit is added.             ║
         ║                                                                    ║
         ║  If the SEDOL is 7 characters, a check digit is created and it's   ║
         ║  verified that it's equal to the check digit already on the SEDOL. ║
         ╚════════════════════════════════════════════════════════════════════╝

<lang rexx>/*REXX program computes the check digit (last digit) for six or seven character SEDOLs.*/ @abcU = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' /*the uppercase Latin alphabet. */ alphaDigs= '0123456789'@abcU /*legal characters, and then some. */ allowable=space(translate(alphaDigs,,'AEIOU'),0) /*remove the vowels from the alphabet. */ weights = 1317391 /*various weights for SEDOL characters.*/ @.= /* [↓] the ARG statement capitalizes. */ arg @.1 . /*allow a user─specified SEDOL from CL*/ if @.1== then do /*if none, then assume eleven defaults.*/

                @.1  =  710889                  /*if all numeric, we don't need quotes.*/
                @.2  = 'B0YBKJ'
                @.3  =  406566
                @.4  = 'B0YBLH'
                @.5  =  228276
                @.6  = 'B0YBKL'
                @.7  =  557910
                @.8  = 'B0YBKR'
                @.9  =  585284
                @.10 = 'B0YBKT'
                @.11 = 'B00030'
                end
     do j=1  while  @.j\==;      sedol=@.j    /*process each of the specified SEDOLs.*/
     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                                      /*the  checkDigit  sum  (so far).      */
             do k=1  for 6                      /*process each character in the 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,15)      'SEDOL + check digit ───► '   left(sedol,6)chkDig
     end       /*j*/

exit /*stick a fork in it, we're all done. */ /*──────────────────────────────────────────────────────────────────────────────────────*/ sed: say; say 'SEDOL:' sedol; say; return ser: say; say '***error***' 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

Ring

<lang ring> see sedol("710889") + nl see sedol("B0YBKJ") + nl see sedol("406566") + nl see sedol("B0YBLH") + nl see sedol("228276") + nl see sedol("B0YBKL") + nl see sedol("557910") + nl see sedol("B0YBKR") + nl see sedol("585284") + nl see sedol("B0YBKT") + nl see sedol("B00030") + nl

func sedol d

    d = upper(d)
    s = 0 
    weights  = [1, 3, 1, 7, 3, 9]
    for i = 1 to 6
        a = substr(d,i,1)
        if ascii(a) >= 48 and ascii(a) <= 57
           s += number(a) * weights[i]
        else
           s += (ascii(a) - 55) * weights[i] ok
    next
    return d + (10 - (s % 10)) % 10

</lang> Output:

7108899
B0YBKJ7
4065663
B0YBLH2
2282765
B0YBKL9
5579107
B0YBKR5
5852842
B0YBKT7
B000300

Ruby

<lang ruby>Sedol_char = "0123456789BCDFGHJKLMNPQRSTVWXYZ" Sedolweight = [1,3,1,7,3,9]

def char2value(c)

 raise ArgumentError, "Invalid char #{c}" unless Sedol_char.include?(c)
 c.to_i(36)

end

def checksum(sedol)

 raise ArgumentError, "Invalid length" unless sedol.size == Sedolweight.size
 sum = sedol.chars.zip(Sedolweight).sum{|ch, weight| char2value(ch) * weight }
 ((10 - (sum % 10)) % 10).to_s

end

data = %w(710889

         B0YBKJ
         406566
         B0YBLH
         228276
         B0YBKL
         557910
         B0YBKR
         585284
         B0YBKT
         B00030
         C0000
         1234567
         00000A)

data.each do |sedol|

 print "%-8s " % sedol
 begin
   puts sedol + checksum(sedol)
 rescue => e
   p e
 end

end</lang>

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>

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

Seed7

<lang 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;</lang>
Output:
7108899
B0YBKJ7
4065663
B0YBLH2
2282765
B0YBKL9
5579107
B0YBKR5
5852842
B0YBKT7
B000300

Sidef

Translation of: Perl 6

<lang ruby>func sedol(s) {

   die 'No vowels allowed' if (s ~~ /[AEIOU]/);
   die 'Invalid format'    if (s !~ /^[0-9B-DF-HJ-NP-TV-Z]{6}$/);
   const base36 = ((@(0..9) + @('A'..'Z')) ~Z @(0..35) -> flatten.to_h);
   const weights = [1, 3, 1, 7, 3, 9];
   var vs = [base36{ s.chars... }];
   var checksum = (vs ~Z* weights -> sum);
   var check_digit = ((10 - checksum%10) % 10);
   return (s + check_digit);

}

%w(

   710889
   B0YBKJ
   406566
   B0YBLH
   228276
   B0YBKL
   557910
   B0YBKR
   585284
   B0YBKT
   B00030

).each { |s|

   say sedol(s);

}</lang>

Output:
7108899
B0YBKJ7
4065663
B0YBLH2
2282765
B0YBKL9
5579107
B0YBKR5
5852842
B0YBKT7
B000300

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>

SQL PL

Works with: Db2 LUW

version 9.7 or higher.

With SQL PL: <lang sql pl> --#SET TERMINATOR @

SET SERVEROUTPUT ON@

CREATE OR REPLACE FUNCTION CHECK_SEDOL (

 IN TEXT VARCHAR(6)
  ) RETURNS VARCHAR(7)
BEGIN
 DECLARE TYPE SEDOL AS CHAR(1) ARRAY [6];

--declare text varchar(6) default 'B12345';

 DECLARE WEIGHT SEDOL;
 DECLARE I SMALLINT;
 DECLARE SENTENCE VARCHAR(256);
 DECLARE CHAR_AT CHAR(1);
 DECLARE OUTPUT CHAR(1);
 DECLARE SUM SMALLINT;
 DECLARE CHECK SMALLINT;
 DECLARE INVALID_CHAR CONDITION FOR SQLSTATE '22004' ;
 DECLARE STMT STATEMENT;
 -- Converts all to upper.
 SET TEXT = UPPER (TEXT);
 -- CALL DBMS_OUTPUT.PUT_LINE(TEXT);
 -- Checks the characters.
 SET I = 1;
 WHILE (I <= 6) DO
  SET CHAR_AT = SUBSTR(TEXT, I, 1);
  -- CALL DBMS_OUTPUT.PUT_LINE('Char ' || CHAR_AT);
  SET SENTENCE = 'SET ? = (SELECT SEDOL FROM (SELECT  || CHAR_AT
    ||  SEDOL FROM SYSIBM.SYSDUMMY1) WHERE SEDOL IN (B,C,D,F,G,H,J,'
    || 'K,L,M,N,P,Q,R,S,T,V,W,X,Y,Z,0,'
    || '1,2,3,4,5,6,7,8,9))';
  PREPARE STMT FROM SENTENCE;
  EXECUTE STMT INTO OUTPUT;
  IF (OUTPUT IS NULL) THEN
   SIGNAL INVALID_CHAR;
  END IF;
  SET I = I + 1;
 END WHILE;

 -- Assigns weight
 SET WEIGHT[1] = '1';
 SET WEIGHT[2] = '3';
 SET WEIGHT[3] = '1';
 SET WEIGHT[4] = '7';
 SET WEIGHT[5] = '3';
 SET WEIGHT[6] = '9';
 -- Process the SEDOL.
 SET SUM = 0;
 SET I = 1;
 WHILE (I <= 6) DO
  SET CHAR_AT = SUBSTR(TEXT, I, 1);
  IF (ASCII(CHAR_AT) > 65) THEN
   SET SUM = SUM + WEIGHT[I] * (ASCII(CHAR_AT) - 64 + 9);
  ELSE
   SET SUM = SUM + WEIGHT[I] * CHAR_AT;
  END IF;
  SET I = I + 1;
 END WHILE;
 SET CHECK = MOD((10 - MOD(SUM, 10)), 10);
 CALL DBMS_OUTPUT.PUT_LINE(CHECK);
 RETURN TEXT || CHECK;
END @

</lang> Output:

db2 -td@
db2 => SET SERVEROUTPUT ON@
DB20000I  The SET SERVEROUTPUT command completed successfully.
db2 => CREATE OR REPLACE FUNCTION CHECK_SEDOL (
...
db2 (cont.) => END @
DB20000I  The SQL command completed successfully.

db2 -x
db2 => values CHECK_SEDOL('710889')
7108899
9
db2 => values CHECK_SEDOL('B0YBKJ')
B0YBKJ7
7
db2 => values CHECK_SEDOL('406566')
4065663
3
db2 => values CHECK_SEDOL('B0YBLH')
B0YBLH2
2
db2 => values CHECK_SEDOL('228276')
2282765
5
db2 => values CHECK_SEDOL('B0YBKL')
B0YBKL9
9
db2 => values CHECK_SEDOL('557910')
5579107
7
db2 => values CHECK_SEDOL('B0YBKR')
B0YBKR5
5
db2 => values CHECK_SEDOL('585284')
5852842
2
db2 => values CHECK_SEDOL('B0YBKT')
B0YBKT7
7
db2 => values CHECK_SEDOL('B00030')
B000300
0

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>

Transact-SQL

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

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

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

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

VBScript

Derived from the BASIC version. <lang VBScript> arr = Array("710889",_

           "B0YBKJ",_

"406566",_ "B0YBLH",_ "228276",_ "B0YBKL",_ "557910",_

           "B0YBKR",_

"585284",_ "B0YBKT",_ "12345",_ "A12345",_ "B00030")

For j = 0 To UBound(arr) WScript.StdOut.Write arr(j) & getSEDOLCheckDigit(arr(j)) WScript.StdOut.WriteLine Next

Function getSEDOLCheckDigit(str) If Len(str) <> 6 Then getSEDOLCheckDigit = " is invalid. Only 6 character strings are allowed." Exit Function End If Set mult = CreateObject("Scripting.Dictionary") With mult .Add "1","1" : .Add "2", "3" : .Add "3", "1" .Add "4","7" : .Add "5", "3" : .Add "6", "9" End With 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 getSEDOLCheckDigit = " is invalid. Vowels are not allowed." Exit Function End If If Asc(s) >= 48 And Asc(s) <=57 Then total = total + CInt(s) * CInt(mult.Item(CStr(i))) Else total = total + (Asc(s) - 55) * CInt(mult.Item(CStr(i))) End If Next getSEDOLCheckDigit = (10 - total Mod 10) Mod 10 End Function</lang>

Output:
7108899
B0YBKJ7
4065663
B0YBLH2
2282765
B0YBKL9
5579107
B0YBKR5
5852842
B0YBKT7
12345 is invalid. Only 6 character strings are allowed.
A12345 is invalid. Vowels are not allowed.
B000300

Visual FoxPro

<lang vfp>

  1. DEFINE ALPHABET "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  2. DEFINE VOWELS "AEIOU"
  3. DEFINE VALIDCHARS "0123456789" + ALPHABET

LOCAL cMsg As String, cCode As String LOCAL ARRAY codes[12] codes[1] = "710889" codes[2] = "B0YBKJ" codes[3] = "406566" codes[4] = "B0YBLH" codes[5] = "228276" codes[6] = "B0YBKL" codes[7] = "557910" codes[8] = "B0YBKR" codes[9] = "585284" codes[10] = "B0YBKT" codes[11] = "B00030" codes[12] = "B0030A" DIMENSION w[6] w[1] = 1 w[2] = 3 w[3] = 1 w[4] = 7 w[5] = 3 w[6] = 9 CLEAR FOR EACH cCode IN codes

   cMsg = ""
   IF IsValidCode(@cCode, @cMsg)	&& Parameters passed by reference

cCode = cCode + GetCheckDigit(cCode) ? cCode

   ELSE

? cCode, cMsg

   ENDIF			

ENDFOR

FUNCTION GetCheckDigit(tcCode As String) As String LOCAL i As Integer, c As String, s As Integer, k As Integer s = 0 FOR i = 1 TO 6

   c = SUBSTR(tcCode, i, 1)
   IF ISDIGIT(c)

k = VAL(c)

   ELSE

k = 9 + AT(c, ALPHABET)

   ENDIF
   s = s + k*w[i]

ENDFOR RETURN TRANSFORM((10 - s%10)%10) ENDFUNC

FUNCTION IsValidCode(tcCode As String, tcMsg As String) As Boolean LOCAL n As Integer, c As String, i As Integer

  • !* Get rid of any spaces and convert to upper case

tcCode = UPPER(STRTRAN(tcCode, " ")) n = LEN(tcCode) IF LEN(tcCode) # 6

   tcMsg = "Code must be 6 characters."

ELSE

   FOR i = 1 TO n

c = SUBSTR(tcCode, i, 1) IF NOT c $ VALIDCHAR tcMsg = c + " is not a valid character." EXIT ELSE IF c $ VOWELS tcMsg = "Vowels are not allowed." EXIT ENDIF ENDIF

   ENDFOR

ENDIF RETURN EMPTY(tcMsg) ENDFUNC </lang>

Output:
Output as in task description.
The last code is printed as BOO30A Vowels are not allowed.

Yabasic

<lang Yabasic>data "710889", "B0YBKJ", "406566", "B0YBLH", "228276", "B0YBKL", "557910", "B0YBKR", "585284", "B0YBKT", "B00030", "AB", "B00A03", ""

do

   read d$
   if d$ = "" break
   print sedol$(d$)

loop

sub sedol$(d$)

   LOCAL a, i, s, weights$(1)
   
   a = len(d$)
   if a < 6 or a > 6 return d$ + ": Error in length"
   
   for i = 1 to 6
       if not instr("BCDFGHJKLMNPQRSTVWXYZ0123456789", mid$(d$, i, 1)) return d$ + ": Error in symbol " + mid$(d$, i, 1)
   next
   
   a = token("1 3 1 7 3 9", weights$())
   
   FOR i = 1 TO 6
     a = ASC(MID$(d$, i, 1)) - 48
     s = s + (a + 3 * (a > 9)) * val(weights$(i))
   NEXT
   return d$ + CHR$(48 + mod(10 - mod(s, 10), 10))

end sub</lang>

zkl

<lang 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; }</lang> It sure does look like that trailing %10 is extraneous. It also seems like lower case is implicitly invalid. <lang zkl>T("710889","B0YBKJ","406566","B0YBLH","228276",

 "B0YBKL","557910","B0YBKR","585284","B0YBKT","B00030")

.apply(checksum).println();</lang>

Output:
L("7108899","B0YBKJ7","4065663","B0YBLH2","2282765","B0YBKL9",
  "5579107","B0YBKR5","5852842","B0YBKT7","B000300")