CUSIP
This page uses content from Wikipedia. The original article was at CUSIP. The list of authors can be seen in the page history. As with Rosetta Code, the text of Wikipedia is available under the GNU FDL. (See links for details on variance) |
A CUSIP is a nine-character alphanumeric code that identifies a North American financial security for the purposes of facilitating clearing and settlement of trades. The CUSIP was adopted as an American National Standard under Accredited Standards X9.6.
- Task
Ensure the last digit (i.e. check digit) of the CUSIP code is correct, against the following codes.
- Apple Inc.: 037833100
- Cisco Systems: 17275R102
- Google Inc.: 38259P508
- Microsoft Corporation: 594918104
- Oracle Corporation (Incorrect): 68389X106
- Oracle Corporation: 68389X105
- Example pseudo-code below.
<lang>algorithm Cusip-Check-Digit(cusip) is
Input: an 8-character CUSIP
sum := 0 for 1 ≤ i ≤ 8 do c := the ith character of cusip if c is a digit then v := numeric value of the digit c else if c is a letter then p := ordinal position of c in the alphabet (A=1, B=2...) v := p + 9 else if c = "*" then v := 36 else if c = "@" then v := 37 else if' c = "#" then v := 38 end if if i is even then v := v × 2 end if
sum := sum + int ( v div 10 ) + v mod 10 repeat return (10 - (sum mod 10)) mod 10
end function</lang>
- See related tasks
ALGOL 68
<lang algol68>BEGIN
# returns TRUE if cusip is a valid CUSIP code # OP ISCUSIP = ( STRING cusip )BOOL: IF ( UPB cusip - LWB cusip ) /= 8 THEN # code is wrong length # FALSE ELSE # string is 9 characters long - check it is valid # STRING cusip digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ*@#"[ AT 0 ]; INT check digit := 0; IF NOT char in string( cusip[ UPB cusip ], check digit, cusip digits ) THEN # invalid check digit # FALSE ELSE # OK so far compare the calculated check sum to the supplied one # INT sum := 0; INT c pos := LWB cusip - 1; FOR i TO 8 DO INT digit := 0; IF NOT char in string( cusip[ i + c pos ], digit, cusip digits ) THEN # invalid digit # digit := -999 FI; IF NOT ODD i THEN # even digit # digit *:= 2 FI; sum +:= ( digit OVER 10 ) + ( digit MOD 10 ) OD; ( 10 - ( sum MOD 10 ) ) MOD 10 = check digit FI FI ; # ISCUSIP #
# task test cases #
PROC test cusip = ( STRING cusip )VOID: print( ( cusip, IF ISCUSIP cusip THEN " valid" ELSE " invalid" FI, newline ) );
test cusip( "037833100" ); test cusip( "17275R102" ); test cusip( "38259P508" ); test cusip( "594918104" ); test cusip( "68389X106" ); test cusip( "68389X105" )
END</lang>
- Output:
037833100 valid 17275R102 valid 38259P508 valid 594918104 valid 68389X106 invalid 68389X105 valid
ALGOL W
Based on Algol 68 <lang algolw>begin % returns true if cusip is a valid CUSIP code %
logical procedure isCusip ( string(9) value cusip ) ; begin % returns the base 39 digit corresponding to a character of a CUSIP code % integer procedure cusipDigit( string(1) value cChar ) ; if cChar >= "0" and cChar <= "9" then ( decode( cChar ) - decode( "0" ) ) else if cChar >= "A" and cChar <= "Z" then ( decode( cChar ) - decode( "A" ) ) + 10 else if cChar = "*" then 36 else if cChar = "@" then 37 else if cChar = "#" then 38 else % invalid digit % -999 ;
integer checkDigit, sum; checkDigit := cusipDigit( cusip( 8 // 1 ) ); for cPos := 1 until 8 do begin integer digit; digit := cusipDigit( cusip( ( cPos - 1 ) // 1 ) ); if not odd( cPos ) then digit := digit * 2; sum := sum + ( digit div 10 ) + ( digit rem 10 ) end for_cPos ; ( ( 10 - ( sum rem 10 ) ) rem 10 ) = checkDigit end isCusip ;
begin % task test cases % procedure testCusip ( string(9) value cusip ) ; write( s_w := 0, cusip, if isCusip( cusip ) then " valid" else " invalid" );
testCusip( "037833100" ); testCusip( "17275R102" ); testCusip( "38259P508" ); testCusip( "594918104" ); testCusip( "68389X106" ); testCusip( "68389X105" ) end testCases
end.</lang>
- Output:
037833100 valid 17275R102 valid 38259P508 valid 594918104 valid 68389X106 invalid 68389X105 valid
AWK
<lang AWK>
- syntax: GAWK -f CUSIP.AWK
BEGIN {
n = split("037833100,17275R102,38259P508,594918104,68389X106,68389X105",arr,",") for (i=1; i<=n; i++) { printf("%9s %s\n",arr[i],cusip(arr[i])) } exit(0)
} function cusip(n, c,i,sum,v,x) {
- returns: 1=OK, 0=NG, -1=bad data
if (length(n) != 9) { return(-1) } for (i=1; i<=8; i++) { c = substr(n,i,1) if (c ~ /[0-9]/) { v = c } else if (c ~ /[A-Z]/) { v = index("ABCDEFGHIJKLMNOPQRSTUVWXYZ",c) + 9 } else if (c == "*") { v = 36 } else if (c == "@") { v = 37 } else if (c == "#") { v = 38 } else { return(-1) } if (i ~ /[02468]/) { v *= 2 } sum += int(v / 10) + (v % 10) } x = (10 - (sum % 10)) % 10 return(substr(n,9,1) == x ? 1 : 0)
} </lang>
- Output:
037833100 1 17275R102 1 38259P508 1 594918104 1 68389X106 0 68389X105 1
Caché ObjectScript
<lang cos>Class Utils.Check [ Abstract ] {
ClassMethod CUSIP(x As %String) As %Boolean { SET x=$TRANSLATE(x," ") // https://leiq.bus.umich.edu/res_codes_cusip.htm IF x'?8UNP1N QUIT 0 SET cd=$EXTRACT(x,*), x=$EXTRACT(x,1,*-1), t=0 FOR i=1:1:$LENGTH(x) { SET n=$EXTRACT(x,i) IF n'=+n SET n=$CASE(n,"*":36,"@":37,"#":38,:$ASCII(n)-55) IF i#2=0 SET n=n*2 SET t=t+(n\10)+(n#10) } QUIT cd=((10-(t#10))#10) }
}</lang>
- Examples:
USER>For { Read s Quit:s="" Write ": "_##class(Utils.Check).CUSIP(s), ! } 037833100: 1 17275R102: 1 38259P508: 1 594918104: 1 68389X106: 0 68389X105: 1 USER>
FreeBASIC
<lang freebasic>' version 04-04-2017 ' compile with: fbc -s console
sub cusip(input_str As String)
Print input_str; If Len(input_str) <> 9 Then Print " length is incorrect, invalid cusip" Return End If
Dim As Long i, v , sum Dim As UByte x
For i = 1 To 8 x = input_str[i-1] Select Case x Case Asc("0") To Asc("9") v = x - Asc("0") Case Asc("A") To Asc("Z") v = x - Asc("A") + 1 + 9 Case Asc("*") v= 36 Case Asc("@") v = 37 Case Asc("#") v = 38 Case Else Print " found a invalid character, invalid cusip" return End Select
If (i And 1) = 0 Then v = v * 2 sum = sum + v \ 10 + v Mod 10 Next
sum = (10 - (sum Mod 10)) Mod 10 If sum = (input_str[8] - Asc("0")) Then Print " is valid" Else Print " is invalid" End If
End Sub
' ------=< MAIN >=------
Data "037833100", "17275R102", "38259P508" Data "594918104", "68389X106", "68389X105"
Dim As String input_str
Print For i As Integer = 1 To 6
Read input_str cusip(input_str)
Next
' empty keyboard buffer While InKey <> "" : Wend Print : Print "hit any key to end program" Sleep End</lang>
- Output:
037833100 is valid 17275R102 is valid 38259P508 is valid 594918104 is valid 68389X106 is invalid 68389X105 is valid
Kotlin
<lang scala>// version 1.1.0
fun isCusip(s: String): Boolean {
if (s.length != 9) return false var sum = 0 for (i in 0..7) { val c = s[i] var v = when (c) { in '0'..'9' -> c.toInt() - 48 in 'A'..'Z' -> c.toInt() - 64 // lower case letters apparently invalid '*' -> 36 '@' -> 37 '#' -> 38 else -> return false } if (i % 2 == 1) v *= 2 // check if odd as using 0-based indexing sum += v / 10 + v % 10 } return s[8].toInt() - 48 == (10 - (sum % 10)) % 10
}
fun main(args: Array<String>) {
val candidates = listOf( "037833100", "17275R102", "38259P508", "594918104", "68389X106", "68389X105" ) for (candidate in candidates) println("$candidate -> ${if(isCusip(candidate)) "correct" else "incorrect"}")
}</lang>
- Output:
037833100 -> correct 17275R102 -> correct 38259P508 -> correct 594918104 -> correct 68389X106 -> incorrect 68389X105 -> correct
Lua
The checkDigit function is a line-for-line translation of the pseudo-code algorithm. <lang Lua>function checkDigit (cusip)
if #cusip ~= 8 then return false end local sum, c, v, p = 0 for i = 1, 8 do c = cusip:sub(i, i) if c:match("%d") then v = tonumber(c) elseif c:match("%a") then p = string.byte(c) - 64 v = p + 9 elseif c == "*" then v = 36 elseif c == "@" then v = 37 elseif c == "#" then v = 38 end if i % 2 == 0 then v = v * 2 end sum = sum + math.floor(v / 10) + v % 10 end return tostring((10 - (sum % 10)) % 10)
end
local testCases = {
"037833100", "17275R102", "38259P508", "594918104", "68389X106", "68389X105"
} for _, CUSIP in pairs(testCases) do
io.write(CUSIP .. ": ") if checkDigit(CUSIP:sub(1, 8)) == CUSIP:sub(9, 9) then print("VALID") else print("INVALID") end
end</lang>
- Output:
037833100: VALID 17275R102: VALID 38259P508: VALID 594918104: VALID 68389X106: INVALID 68389X105: VALID
Perl 6
<lang perl6>sub divmod ($v, $r) { $v div $r, $v mod $r } my %chr = (flat 0..9, 'A'..'Z', <* @ #>) Z=> 0..*;
sub cuisp-check ($cuisp where *.chars == 9) {
my ($code, $chk) = $cuisp.comb(8); my $sum = [+] $code.comb.kv.map: { [+] (($^k % 2 + 1) * %chr{$^v}).&divmod(10) }; so (10 - $sum mod 10) mod 10 eq $chk;
}
- TESTING
say "$_: ", $_.&cuisp-check for < 037833100 17275R102 38259P508 594918104 68389X106 68389X105 ></lang>
- Output:
037833100: True 17275R102: True 38259P508: True 594918104: True 68389X106: False 68389X105: True
Racket
<lang racket>#lang racket (require srfi/14)
(define 0-char (char->integer #\0)) (define A-char (char->integer #\A))
(define (cusip-value c)
(cond [(char-set-contains? char-set:digit c) (- (char->integer c) 0-char)] [(char-set-contains? char-set:upper-case c) (+ 10 (- (char->integer c) A-char))] [(char=? c #\*) 36] [(char=? c #\@) 37] [(char=? c #\#) 38]))
(define (cusip-check-digit cusip)
(modulo (- 10 (modulo (for/sum ((i (sequence-map add1 (in-range 8))) (c (in-string cusip))) (let* ((v (cusip-value c)) (v′ (if (even? i) (* v 2) v))) (+ (quotient v′ 10) (modulo v′ 10)))) 10)) 10))
(define (CUSIP? s)
(char=? (string-ref s (sub1 (string-length s))) (integer->char (+ 0-char (cusip-check-digit s)))))
(module+ test
(require rackunit) (check-true (CUSIP? "037833100")) (check-true (CUSIP? "17275R102")) (check-true (CUSIP? "38259P508")) (check-true (CUSIP? "594918104")) (check-false (CUSIP? "68389X106")) (check-true (CUSIP? "68389X105")))</lang>
no output indicates all tests passed.
REXX
idiomatic
<lang rexx>/*REXX program validates that the last digit (the check digit) of a CUSIP is valid. */ @.= parse arg @.1 . if @.1== | @.1=="," then do; @.1= 037833100 /* Apple Incorporated */
@.2= 17275R102 /* Cisco Systems */ @.3= 38259P508 /* Google Incorporated */ @.4= 594918104 /* Microsoft Corporation */ @.5= 68389X106 /* Oracle Corporation (incorrect)*/ @.6= 68389X105 /* Oracle Corporation */ end
do j=1 while @.j\=; chkDig=CUSIPchk(@.j) /*calculate check digit from func*/ OK=word("isn't is", 1 + (chkDig==right(@.j,1) ) ) /*validate check digit with func*/ say 'CUSIP ' @.j right(OK, 6) "valid." /*display the CUSIP and validity.*/ end /*j*/
exit /*stick a fork in it, we're all done. */ /*──────────────────────────────────────────────────────────────────────────────────────*/ CUSIPchk: procedure; arg x 9; $=0; abc= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
do k=1 for 8 y=substr(x, k, 1) select when datatype(y,'W') then #=y when datatype(y,'U') then #=pos(y, abc) + 9 when y=='*' then #=36 when y=='@' then #=37 when y=='#' then #=38 otherwise return 0 /*invalid character.*/ end /*select*/ if k//2==0 then #=#+# /*K even? Double it*/ $=$ + #%10 + #//10 end /*k*/ return (10- $//10) // 10</lang>
output when using the default input:
CUSPID 037833100 is valid. CUSPID 17275R102 is valid. CUSPID 38259P508 is valid. CUSPID 594918104 is valid. CUSPID 68389X106 isn't valid. CUSPID 68389X105 is valid.
conciser function
<lang rexx>/*REXX program validates that the last digit (the check digit) of a CUSIP is valid. */ @.= parse arg @.1 . if @.1== | @.1=="," then do; @.1= 037833100 /* Apple Incorporated */
@.2= 17275R102 /* Cisco Systems */ @.3= 38259P508 /* Google Incorporated */ @.4= 594918104 /* Microsoft Corporation */ @.5= 68389X106 /* Oracle Corporation (incorrect)*/ @.6= 68389X105 /* Oracle Corporation */ end
do j=1 while @.j\=; chkDig=CUSIPchk(@.j) /*calculate check digit from func*/ OK=word("isn't is", 1 + (chkDig==right(@.j,1) ) ) /*validate check digit with func*/ say 'CUSIP ' @.j right(OK, 6) "valid." /*display the CUSIP and validity.*/ end /*j*/
exit /*stick a fork in it, we're all done. */ /*──────────────────────────────────────────────────────────────────────────────────────*/ CUSIPchk: procedure; arg x 9; $=0; abc= '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ*@#'
/* [↓] if Y isn' found, then POS returns zero.*/ do k=1 for 8; y=substr(x,k,1) /*get a character. */ #=pos(y, abc) - 1 /*get its position.*/ if # == -1 then return 0 /*invalid character*/ if k//2== 0 then #=#+# /*K even? double it*/ $=$ + #%10 + #//10 end /*k*/ return (10-$//10) // 10</lang>
output is the same as the idiomatic REXX version.
zkl
<lang zkl>fcn cusipCheckDigit(cusip){
var [const] vs=[0..9].chain(["A".."Z"],T("*","@","#")).pump(String); try{ sum:=Walker.cycle(1,2).zipWith(fcn(n,c){ v:=vs.index(c)*n; v/10 + v%10 }, cusip[0,8]).reduce('+); ((10 - sum%10)%10 == cusip[8].toInt()) and cusip.len()==9 }catch{ False }
}</lang> <lang zkl>foreach cusip in (T("037833100", "17275R102", "38259P508", "594918104", "68389X106", "68389X105")){
println(cusip,": ",cusipCheckDigit(cusip));
}</lang>
- Output:
037833100: True 17275R102: True 38259P508: True 594918104: True 68389X106: False 68389X105: True