SEDOLs: Difference between revisions
No edit summary |
|||
Line 23: | Line 23: | ||
=={{header|Ada}}== |
=={{header|Ada}}== |
||
<ada> |
<lang ada> |
||
with Ada.Text_IO; use Ada.Text_IO; |
with Ada.Text_IO; use Ada.Text_IO; |
||
Line 64: | Line 64: | ||
end loop; |
end loop; |
||
end Test_SEDOL; |
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. |
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. |
||
Line 82: | Line 82: | ||
=={{header|BASIC}}== |
=={{header|BASIC}}== |
||
{{works with|QuickBasic|4.5}} |
{{works with|QuickBasic|4.5}} |
||
<qbasic>DECLARE FUNCTION getSedolCheckDigit! (str AS STRING) |
<lang qbasic>DECLARE FUNCTION getSedolCheckDigit! (str AS STRING) |
||
DO |
DO |
||
INPUT a$ |
INPUT a$ |
||
Line 112: | Line 112: | ||
NEXT i |
NEXT i |
||
getSedolCheckDigit = (10 - (total MOD 10)) MOD 10 |
getSedolCheckDigit = (10 - (total MOD 10)) MOD 10 |
||
END FUNCTION</ |
END FUNCTION</lang> |
||
=={{header|Forth}}== |
=={{header|Forth}}== |
||
Line 249: | Line 249: | ||
=={{header|Java}}== |
=={{header|Java}}== |
||
<java>import java.util.Scanner; |
<lang java>import java.util.Scanner; |
||
public class SEDOL{ |
public class SEDOL{ |
||
Line 279: | Line 279: | ||
return (10 - (total % 10)) % 10; |
return (10 - (total % 10)) % 10; |
||
} |
} |
||
}</ |
}</lang> |
||
=={{header|OCaml}}== |
=={{header|OCaml}}== |
||
<ocaml>let char2value c = |
<lang ocaml>let char2value c = |
||
assert (not (String.contains "AEIOU" c)); |
assert (not (String.contains "AEIOU" c)); |
||
match c with |
match c with |
||
Line 310: | Line 310: | ||
"B0YBKR"; |
"B0YBKR"; |
||
"585284"; |
"585284"; |
||
"B0YBKT" ]</ |
"B0YBKT" ]</lang> |
||
=={{header|Perl}}== |
=={{header|Perl}}== |
||
This program reads from standard input. |
This program reads from standard input. |
||
<perl>sub sum |
<lang perl>sub sum |
||
{my $n = 0; |
{my $n = 0; |
||
$n += $_ foreach @_; |
$n += $_ foreach @_; |
||
Line 343: | Line 343: | ||
while (<>) |
while (<>) |
||
{chomp; |
{chomp; |
||
print sedol($_), "\n";}</ |
print sedol($_), "\n";}</lang> |
||
=={{header|PHP}}== |
=={{header|PHP}}== |
||
<php>function char2value($c) { |
<lang php>function char2value($c) { |
||
assert(stripos('AEIOU', $c) === FALSE); |
assert(stripos('AEIOU', $c) === FALSE); |
||
return intval($c, 36); |
return intval($c, 36); |
||
Line 371: | Line 371: | ||
'585284', |
'585284', |
||
'B0YBKT') as $sedol) |
'B0YBKT') as $sedol) |
||
echo $sedol, checksum($sedol), "\n";</ |
echo $sedol, checksum($sedol), "\n";</lang> |
||
=={{header|Python}}== |
=={{header|Python}}== |
||
<python>def char2value(c): |
<lang python>def char2value(c): |
||
assert c not in 'AEIOU', "No vowels" |
assert c not in 'AEIOU', "No vowels" |
||
return int(c, 36) |
return int(c, 36) |
||
Line 398: | Line 398: | ||
B0YBKT |
B0YBKT |
||
'''.split(): |
'''.split(): |
||
print sedol + checksum(sedol)</ |
print sedol + checksum(sedol)</lang> |
||
=={{header|Ruby}}== |
=={{header|Ruby}}== |
||
<ruby>def char2value(c) |
<lang ruby>def char2value(c) |
||
raise "No vowels" if 'AEIOU'.include?(c) |
raise "No vowels" if 'AEIOU'.include?(c) |
||
c.to_i(36) |
c.to_i(36) |
||
Line 428: | Line 428: | ||
} |
} |
||
puts sedol + checksum(sedol) |
puts sedol + checksum(sedol) |
||
end</ |
end</lang> |
Revision as of 15:50, 3 February 2009
You are encouraged to solve this task according to the task description, using any language you may know.
For each number list of 6-digit SEDOLs, calculate and append the checksum digit. That is, given this input:
710889 B0YBKJ 406566 B0YBLH 228276 B0YBKL 557910 B0YBKR 585284 B0YBKT
Produce this output:
7108899 B0YBKJ7 4065663 B0YBLH2 2282765 B0YBKL9 5579107 B0YBKR5 5852842 B0YBKT7
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
BASIC
<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>
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
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
Output
710889 7108899 B0YBKJ B0YBKJ7 406566 4065663 B0YBLH B0YBLH2 228276 2282765 B0YBKL B0YBKL9 557910 5579107 B0YBKR B0YBKR5 585284 5852842 B0YBKT B0YBKT7
Haskell
import Data.Char char2value c | c `elem` "AEIOU" = error "No vowels." | c >= '0' && c <= '9' = ord c - ord '0' | c >= 'A' && c <= 'Z' = ord c - ord 'A' + 10 sedolweight = [1,3,1,7,3,9] checksum sedol = show ((10 - (tmp `mod` 10)) `mod` 10) where tmp = sum $ zipWith (*) sedolweight $ map char2value sedol main = mapM_ (\sedol -> putStrLn $ sedol ++ checksum sedol) [ "710889", "B0YBKJ", "406566", "B0YBLH", "228276", "B0YBKL", "557910", "B0YBKR", "585284", "B0YBKT" ]
J
There are several ways to perform this in J. This most closely follows the algorithmic description at Wikipedia:
sn =. '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' ac0 =: (, 10 | 1 3 1 7 3 9 +/@:* -)&.(sn i. |:)
However, so J is so concise, having written the above, it becomes clear that the negation (-) is unneccsary.
The fundamental operation is the linear combination (+/@:*) and neither argument is "special". In particular, the coefficients are just another array participating in the calculation, and there's no reason we can't modify them as easily as the input array. Having this insight, it is obvious that manipulating the coefficients, rather than the input array, will be more efficient (because the coefficients are fixed at small size, while the input array can be arbitrarily large).
Which leads us to this more efficient formulation:
ac1 =: (, 10 | (10 - 1 3 1 7 3 9) +/@:* ])&.(sn i. |:)
which reduces to:
ac1 =: (, 10 | 9 7 9 3 7 1 +/@:* ])&.(sn i. |:)
Which is just as concise as ac0, but faster.
Following this train of thought, our array thinking leads us to realize that even the modulous isn't neccesary. The number of SEDOL numbers is finite, as is the number of coefficients; therefore the number of possible linear combinations of these is finite. In fact, there are only 841 possible outcomes. This is a small number, and can be efficiently stored as a lookup table (even better, since the outcomes will be mod 10, they are restricted to the digits 0-9, and they repeat).
Which leads us to:
ac2 =. (,"1 0 (841 $ '0987654321') {~ 1 3 1 7 3 9 +/ .*~ sn i. ])
Which is more than twice as fast as even the optimized formulation (ac1), though it is slightly longer.
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(str.length() != 6){ System.err.println("Six chars only please"); return -1; } str = str.toUpperCase(); int total = 0; for(int i = 0;i < 6; i++){ char s = str.charAt(i); if(s == 'A' || s == 'E' || s == 'I' || s == 'O' ||s == 'U'){ System.err.println("No vowels"); return -1; } total += Character.digit(s, 36) * mult[i]; } return (10 - (total % 10)) % 10; } }</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
let sedolweight = [1;3;1;7;3;9]
let explode s =
let result = ref [] in String.iter (fun c -> result := c :: !result) s; List.rev !result
let checksum sedol =
let tmp = List.fold_left2 (fun sum ch weight -> sum + char2value ch * weight) 0 (explode sedol) sedolweight in string_of_int ((10 - (tmp mod 10)) mod 10)
List.iter (fun sedol -> print_endline (sedol ^ checksum sedol))
[ "710889"; "B0YBKJ"; "406566"; "B0YBLH"; "228276"; "B0YBKL"; "557910"; "B0YBKR"; "585284"; "B0YBKT" ]</lang>
Perl
This program reads from standard input. <lang perl>sub sum
{my $n = 0; $n += $_ foreach @_; return $n;}
sub zip
{my $f = shift; my @a = @{shift()}; my @b = @{shift()}; my @result = (); push(@result, $f->(shift @a, shift @b)) while @a and @b; return @result;}
sub char_to_v
{my $c = shift; $c =~ /[AEIOU]/ and die "No vowels"; $c =~ /[A-Z]/ and $c = ord($c) - ord('A') + 10; return $c;}
my @weights = (1, 3, 1, 7, 3, 9); sub sedol
{my $s = shift; my @vs = map {char_to_v $_} split //, $s; my $checksum = sum (zip sub {$_[0] * $_[1]}, \@vs, \@weights); my $check_digit = (10 - $checksum % 10) % 10; return $s . $check_digit;}
while (<>)
{chomp; print sedol($_), "\n";}</lang>
PHP
<lang php>function char2value($c) {
assert(stripos('AEIOU', $c) === FALSE); return intval($c, 36);
}
$sedolweight = array(1,3,1,7,3,9);
function checksum($sedol) {
global $sedolweight; $tmp = array_sum(array_map(create_function('$ch, $weight', 'return char2value($ch) * $weight;'), str_split($sedol), $sedolweight) ); return strval((10 - ($tmp % 10)) % 10);
}
foreach (array('710889',
'B0YBKJ', '406566', 'B0YBLH', '228276', 'B0YBKL', '557910', 'B0YBKR', '585284', 'B0YBKT') as $sedol) echo $sedol, checksum($sedol), "\n";</lang>
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>
Ruby
<lang ruby>def char2value(c)
raise "No vowels" if 'AEIOU'.include?(c) c.to_i(36)
end
Sedolweight = [1,3,1,7,3,9]
def checksum(sedol)
tmp = sedol.split().zip(Sedolweight).map { |ch, weight| char2value(ch) * weight }.inject(0) { |sum, x| sum + x } ((10 - (tmp % 10)) % 10).to_s
end
for sedol in %w{
710889 B0YBKJ 406566 B0YBLH 228276 B0YBKL 557910 B0YBKR 585284 B0YBKT } puts sedol + checksum(sedol)
end</lang>