Validate International Securities Identification Number: Difference between revisions

m
(74 intermediate revisions by 33 users not shown)
Line 3:
An [[wp:International_Securities_Identification_Number|International Securities Identification Number]] (ISIN) is a unique international identifier for a financial security such as a stock or bond.
 
{{task heading}}
 
;Task:
Write a function or program that takes a string as input, and checks whether it is a valid ISIN.<br>
Write a function or program that takes a string as input, and checks whether it is a valid ISIN.
It is only valid if it has the correct format, ''and'' the embedded checksum is correct.
 
It is only valid if it has the correct format, &nbsp; ''and'' &nbsp; the embedded checksum is correct.
 
Demonstrate that your code passes the test-cases listed below.
 
{{task heading|Details}}
 
;Details:
The format of an ISIN is as follows:
 
<!-- BEGIN DIAGRAM -->
<div style="margin:0.5em3em; white-space:nowrap; line-height:20px">
<div><span style="font-size:20px; font-family:'Lucida Console',Monaco,monospace"><span style="color:green; margin:0 0 0 10px">┌───────────── </span></span><span style="color:green">a 2-character ISO country code (A-Z)</span></div>
<div><span style="font-size:20px; font-family:'Lucida Console',Monaco,monospace"><span style="color:green; margin:0 -10px 0 10px">│</span>&nbsp;<span style="color:blue; margin:0 0 0 10px">┌─────────── </span></span><span style="color:blue">a 9-character security code (A-Z, 0-9)</span></div>
Line 22 ⟶ 23:
</div>
<!-- END DIAGRAM -->
 
 
For this task, you may assume that any 2-character alphabetic sequence is a valid country code.
 
The checksum can be validated as follows:
# '''Replace letters with digits''', by converting each character from base 36 to base 10, e.g. <code>AU0000XVGZA3</code> &rarr;<code>1030000033311635103</code>.
# '''Perform the Luhn test on this base-10 number.'''<br>There is a separate task for this test: ''[[Luhn test of credit card numbers]]''.<br>You don't have to replicate the implementation of this test here &ndashnbsp; ─── &nbsp; you can just call the existing function from that task. &nbsp; (Add a comment stating if you did this.)
 
{{task heading|Test-cases}}
 
;Test cases:
{| class="wikitable"
:::: {| class="wikitable"
! ISIN
! Validity
Line 50 ⟶ 53:
|}
 
(The comments are just informational. &nbsp; Your function should simply return a Boolean result. &nbsp; See [[#Perl_6Raku]] for a reference solution.)
 
{{task heading|See also}}
 
Related task:
Useful resources:
* [[Luhn test of credit card numbers]]
* [http://www.isincodes.net/validate-isin.php Interactive online ISIN validator]
 
 
;Also see:
* [https://www.isincodes.net/validate-isin/ Interactive online ISIN validator]
* Wikipedia article: [[wp:International_Securities_Identification_Number|International Securities Identification Number]]
<br><br>
 
=={{header|11l}}==
Related tasks:
{{trans|Python}}
* [[Luhn test of credit card numbers]]
 
<br>
<syntaxhighlight lang="11l">F check_isin(a)
<hr>
I a.len != 12
R 0B
[Int] s
L(c) a
I c.is_digit()
I L.index < 2
R 0B
s.append(c.code - 48)
E I c.is_uppercase()
I L.index == 11
R 0B
V (d, m) = divmod(c.code - 55, 10)
s [+]= [d, m]
E
R 0B
V v = sum(s[((len)-1..).step(-2)])
L(=k) s[((len)-2..).step(-2)]
k = 2 * k
v += I k > 9 {k - 9} E k
R v % 10 == 0
 
print([‘US0378331005’, ‘US0373831005’, ‘U50378331005’, ‘US03378331005’,
‘AU0000XVGZA3’, ‘AU0000VXGZA3’, ‘FR0000988040’].map(s -> check_isin(s)))</syntaxhighlight>
 
{{out}}
<pre>
[1B, 0B, 0B, 0B, 1B, 1B, 1B]
</pre>
 
=={{header|360 Assembly}}==
<syntaxhighlight lang="360asm">* Validate ISIN 08/03/2019
VALISIN CSECT
USING VALISIN,R13 base register
B 72(R15) skip savearea
DC 17F'0' savearea
SAVE (14,12) save previous context
ST R13,4(R15) link backward
ST R15,8(R13) link forward
LR R13,R15 set addressability
LA R7,1 j=1
DO WHILE=(C,R7,LE,=A(NN)) do j=1 to hbound(tt)
LR R1,R7 j
SLA R1,4 ~
LA R4,TT-16(R1) @tt(j)
MVC CC,0(R4) cc=tt(j)
MVC C,=CL28' ' c=' '
MVC R,=CL28' ' r=' '
MVI ERR,X'00' err=false
MVC LCC,=F'0' lcc=0
LA R1,L'CC i=length(cc)
LENTRIA LA R5,CC-1 @cc
AR R5,R1 +i
CLI 0(R5),C' ' if cc[i]=' '
BE LENTRIB then iterate loop
ST R1,LCC lcc=lentrim(cc)
B LENTRIC leave loop
LENTRIB BCT R1,LENTRIA i--; if i<>0 then loop
LENTRIC L R4,LCC lcc
IF CH,R4,EQ,=H'12' THEN if lcc=12 then
MVC LC,=F'0' lc=0
MVC WW,=CL28' ' ww=''
LA R10,WW @ww
LA R6,1 i=1
DO WHILE=(C,R6,LE,LCC) do i=1 to lcc
LA R4,CC-1 @cc
AR R4,R6 +i
MVC CI(1),0(R4) ci=substr(cc,i,1)
LA R2,BASE36 @base36
LA R3,L'BASE36 length(base36)
BAL R14,INDEX r0=index(base36,ci)
IF LTR,R0,NZ,R0 THEN if p<>0 then
LR R1,R0 ip
BCTR R1,0 -1
XDECO R1,XDEC str(ip-1)
MVC 0(2,R10),XDEC+10 ww=ww||str(p-1)
ELSE , else
MVI ERR,X'FF' err=true
ENDIF , endif
LA R10,2(R10) @ww+=2
LA R6,1(R6) i++
ENDDO , enddo i
MVC C,=CL28' ' c=''
LA R8,WW @ww
LA R9,C @c
LA R10,0 length(c)
LA R6,1 i=1
DO WHILE=(C,R6,LE,=A(L'WW)) do i=1 to length(ww)
IF CLI,0(R8),NE,C' ' THEN if ww[i]<>' ' then
MVC 0(1,R9),0(R8) c=ww[i]
LA R9,1(R9) @c++
LA R10,1(R10) length(c)++
ENDIF , endif
LA R8,1(R8) @ww++
LA R6,1(R6) i++
ENDDO , enddo i
ST R10,LC lc=length(c)
LA R6,1 i=1
DO WHILE=(CH,R6,LE,=H'2') do i=1 to 2
LA R4,CC-1 @cc
AR R4,R6 +i
MVC CI(1),0(R4) ci=substr(cc,i,1)
LA R2,ALPHA @alpha
LA R3,L'ALPHA length(alpha)
BAL R14,INDEX r0=index(alpha,ci)
IF LTR,R0,Z,R0 THEN if index(alpha,ci)=0 then
MVI ERR,X'FF' err=true
ENDIF , endif
LA R6,1(R6) i++
ENDDO , enddo i
SR R8,R8 i1=0
SR R9,R9 i2=0
IF CLI,ERR,EQ,X'00' THEN if not err then
SR R0,R0 0
L R6,LC i=lc
MVC R,=CL28' ' r=''
LA R10,C @c
LA R11,R-1 @r
A R11,LC @r=@r+length(strip((c))
DO WHILE=(CH,R6,GE,=H'1') do i=lc to 1 step -1
MVC 0(1,R11),0(R10) r[k]=c[i]
BCTR R11,0 @r--
LA R10,1(R10) @c++
BCTR R6,0 i--
ENDDO , enddo i
LA R6,1 i=1
DO WHILE=(C,R6,LE,LC) do i=1 to lc step 2
LA R4,R-1 @r
AR R4,R6 +i
MVC CI(1),0(R4) ci=substr(r,i,1)
MVC XDEC,=CL12' ' ~
MVC XDEC(L'CI),CI ci
XDECI R2,XDEC int(ci)
AR R8,R2 i1=i1+int(ci)
LA R6,2(R6) i+=2
ENDDO , enddo i
LA R6,2 i=2
DO WHILE=(C,R6,LE,LC) do i=2 to lc step 2
LA R4,R-1 @r
AR R4,R6 +i
MVC CI(1),0(R4) ci=substr(r,i,1)
MVC XDEC,=CL12' ' ~
MVC XDEC(L'CI),CI ci
XDECI R10,XDEC int(ci)
SLA R10,1 ii=int(ci)*2
IF CH,R10,GE,=H'10' THEN if ii>=10 then
SH R10,=H'9' ii=ii-9
ENDIF , endif
AR R9,R10 i2=i2+ii
LA R6,2(R6) i++
ENDDO , enddo i
LR R2,R8 i1
AR R2,R9 +i2
XDECO R2,XDEC s=str(i1+i2)
IF CLI,XDEC+11,EQ,C'0' THEN if substr(s,length(s),1)='0' then
MVC MSG,=CL6'OK' msg='ok'
ELSE , else
MVC MSG,=CL6'?err1' msg='?1'
ENDIF , endif
ELSE , else
MVC MSG,=CL6'?err2' msg='?2'
ENDIF , endif
ELSE , else
MVC MSG,=CL6'?err3' msg='?3'
ENDIF , endif
XDECO R7,XDEC edit j
MVC PG(2),XDEC+10 j
MVC PG+3(16),CC cc
MVC PG+20(6),MSG msg
XPRNT PG,L'PG print buffer
LA R7,1(R7) j++
ENDDO , enddo j
L R13,4(0,R13) restore previous savearea pointer
RETURN (14,12),RC=0 restore registers from calling sav
MVCX MVC 0(0,R4),0(R5) pattern svc
INDEX SR R0,R0 index(r2,ci) r3=len
LA R1,1 k=1
SINDEXA CR R1,R3 do k=1 to length(ca)
BH SINDEXC ~
CLC 0(1,R2),CI if ca[k]=ci
BNE SINDEXB then iterate loop
LR R0,R1 ii=k
B SINDEXC exit loop
SINDEXB LA R2,1(R2) @ca++
LA R1,1(R1) k++
B SINDEXA enddo
SINDEXC BR R14 end index
NN EQU (BASE36-TT)/16 number of items
TT DC CL16'US0378331005',CL16'US0373831005'
DC CL16'U50378331005',CL16'US03378331005'
DC CL16'AU0000XVGZA3',CL16'AU0000VXGZA3'
DC CL16'FR0000988040'
BASE36 DC CL36'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
ALPHA DC CL26'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
ERR DS X error
LCC DS F length of cc
LC DS F length of c
CI DS CL1
CC DS CL16 current element of tt
C DS CL28
R DS CL28
WW DS CL28
MSG DS CL6 message
PG DC CL80' ' buffer
XDEC DS CL12 temp for xdeco and xdeci
REGEQU
END VALISIN</syntaxhighlight>
{{out}}
<pre>
1 US0378331005 OK
2 US0373831005 ?err1
3 U50378331005 ?err2
4 US03378331005 ?err3
5 AU0000XVGZA3 OK
6 AU0000VXGZA3 OK
7 FR0000988040 OK
</pre>
 
=={{header|Ada}}==
Calling the existing Luhn algorithm implementation from the ''[[Luhn test of credit card numbers]]'' task.
 
<langsyntaxhighlight Adalang="ada">procedure ISIN is
-- Luhn_Test copied from other Task
function Luhn_Test (Number: String) return Boolean is
Line 140 ⟶ 362:
when others =>
Ada.Text_IO.Put_Line("Exception occured");
end ISIN;</langsyntaxhighlight>
 
Output:
Line 153 ⟶ 375:
=={{header|ALGOL W}}==
Uses the LuhnTest procedure from the [[Luhn test of credit card numbers]] task.
<langsyntaxhighlight lang="algolw">begin
% external procedure that returns true if ccNumber passes the Luhn test, false otherwise %
logical procedure LuhnTest ( string(32) value ccNumber
Line 227 ⟶ 449:
testIsIsin( "AU0000VXGZA3", true );
testIsIsin( "FR0000988040", true );
end.</langsyntaxhighlight>
{{out}}
<pre>
Line 238 ⟶ 460:
FR0000988040 is valid
</pre>
 
=={{header|AppleScript}}==
 
This script calls a handler posted for the [https://www.rosettacode.org/wiki/Luhn_test_of_credit_card_numbers#Straightforward Luhn test of credit card numbers] task.
 
<syntaxhighlight lang="applescript">use AppleScript version "2.4" -- OS X 10.10 (Yosemite) or later
use framework "Foundation"
 
on ISINTest(ISIN)
-- Check that the input is both text and 12 characters long …
if not ((ISIN's class is text) and ((count ISIN) is 12)) then return false
-- … and that it has the required format.
set ISIN to current application's class "NSMutableString"'s stringWithString:(ISIN)
if ((ISIN's rangeOfString:("^[A-Z]{2}[0-9A-Z]{9}[0-9]$") options:(current application's NSRegularExpressionSearch) range:({0, ISIN's |length|()}))'s |length|() is 0) then return false
-- Replace all letters with text representations of equivalent decimal numbers in the range 10 to 35.
set letterCharacters to characters of "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
repeat with i from 1 to 26
tell ISIN to replaceOccurrencesOfString:(item i of letterCharacters) withString:((i + 9) as text) options:(0) range:({0, its |length|()})
end repeat
-- Apply the Luhn test handler from the "Luhn test of credit card numbers" task.
-- <https://www.rosettacode.org/wiki/Luhn_test_of_credit_card_numbers#Straightforward>
return luhnTest(ISIN as text)
end ISINTest
 
-- Test code:
set testResults to {}
repeat with ISIN in {"US0378331005", "US0373831005", "U50378331005", "US03378331005", "AU0000XVGZA3", "AU0000VXGZA3", "FR0000988040"}
set end of testResults to {testNumber:ISIN's contents, valid:ISINTest(ISIN)}
end repeat
return testResults</syntaxhighlight>
 
{{output}}
<syntaxhighlight lang="applescript">{{testNumber:"US0378331005", valid:true}, {testNumber:"US0373831005", valid:false}, {testNumber:"U50378331005", valid:false}, {testNumber:"US03378331005", valid:false}, {testNumber:"AU0000XVGZA3", valid:true}, {testNumber:"AU0000VXGZA3", valid:true}, {testNumber:"FR0000988040", valid:true}}</syntaxhighlight>
=={{header|AWK}}==
<syntaxhighlight lang="awk">
# syntax: GAWK -f VALIDATE_INTERNATIONAL_SECURITIES_IDENTIFICATION_NUMBER.AWK
# converted from Fortran
BEGIN {
for (i=0; i<=255; i++) { ord_arr[sprintf("%c",i)] = i } # build array[character]=ordinal_value
n = split("US0378331005,US0373831005,U50378331005,US03378331005,AU0000XVGZA3,AU0000VXGZA3,FR0000988040",arr,",")
for (i=1; i<=n; i++) {
printf("%s %s\n",is_isin(arr[i]),arr[i])
}
exit(0)
}
function is_isin(arg, i,j,k,s,v) {
for (i=1; i<=12; i++) { # convert to an array of digits
k = ord_arr[substr(arg,i,1)]
if (k >= 48 && k <= 57) {
if (i < 3) { return(0) }
k -= 48
s[++j] = k
} else if (k >= 65 && k <= 90) {
if (i == 12) { return(0) }
k = k - 65 + 10
s[++j] = int(k / 10)
s[++j] = k % 10
} else {
return(0)
}
}
for (i=j-1; i>=1; i-=2) { # compute checksum
k = 2 * s[i]
if (k > 9) { k -= 9 }
v += k
}
for (i=j; i>=1; i-=2) {
v += s[i]
}
return(v % 10 == 0)
}
</syntaxhighlight>
{{out}}
<pre>
1 US0378331005
0 US0373831005
0 U50378331005
0 US03378331005
1 AU0000XVGZA3
1 AU0000VXGZA3
1 FR0000988040
</pre>
 
=={{header|BASIC256}}==
<syntaxhighlight lang="vbnet">array base 1
 
dim test_set$(7)
test_set$ = {"US0378331005", "US0373831005", "U50378331005", "US03378331005", "AU0000XVGZA3", "AU0000VXGZA3", "FR0000988040"}
 
for i = 1 to test_set$[?]
test_str$ = ""
l = length(test_set$[i])
if l <> 12 then
print test_set$[i]; " Invalid, length <> 12 char."
continue for
end if
if asc(mid(test_set$[i], 1, 1)) < asc("A") or asc(mid(test_set$[i], 2, 1)) < asc("A") then
print test_set$[i]; " Invalid, number needs to start with 2 characters"
continue for
end if
for n = 1 to l
x = asc(mid(test_set$[i], n, 1)) - asc("0")
if x > 9 then x -= 7
if x < 10 then
test_str$ += string(x)
else # two digest number
test_str$ += string(x \ 10) + string(x mod 10)
end if
next
print test_set$[i]; " ";
if luhntest(test_str$) = 1 then
print "Invalid, checksum error"
else
print "Valid"
end if
next
end
 
function luhntest(cardnr$)
cardnr$ = trim(cardnr$) # remove spaces
l = length(cardnr$)
s1 = 0
s2 = 0
 
# sum odd numbers
for i = 1 to l step 2
s1 += fromradix(asc(mid(cardnr$, i, 1)), 10)
next
# sum even numbers
for i = 2 to l step 2
j = fromradix(asc(mid(cardnr$, i, 1)), 10)
j *= 2
if j > 9 then j = (j mod 10) + 1
s2 += j
next
 
return (s1 + s2) mod 10 = 0
end function</syntaxhighlight>
{{out}}
<pre>Similar to FreeBASIC entry.</pre>
 
=={{header|Bruijn}}==
Using bruijn's <code>luhn</code> solution from [[Luhn test of credit card numbers]]:
<syntaxhighlight lang="bruijn">
:import luhn_test_of_credit_card_numbers .
 
:import std/Number/Conversion .
:import std/Combinator .
:import std/String .
:import std/Char .
:import std/Logic .
:import std/Number .
 
# verifies ISIN format
format? [len ⋀? country ⋀? security ⋀? checksum]
len (length 0) =? (+12)
country all? uppercase? (take (+2) 0)
security all? (φ or? uppercase? numeric?) (take (+9) (drop (+2) 0))
checksum numeric? _0
 
# performs luhn test
checksum? (map (from-base36 → number→string)) → concat → string→number → luhn
from-base36 binary→ternary → [(0 - (0 ≥? (+65) ((+65) - (+10)) (+48)))]
 
# performs format and checksum test
validate φ and? format? checksum?
 
:test (validate "US0378331005") (true)
:test (validate "US0373831005") (false)
:test (validate "U50378331005") (false)
:test (validate "US03378331005") (false)
:test (validate "AU0000XVGZA3") (true)
:test (validate "AU0000VXGZA3") (true)
:test (validate "FR0000988040") (true)
</syntaxhighlight>
 
=={{header|C}}==
<langsyntaxhighlight lang="c">#include <stdio.h>
 
int check_isin(char *a) {
Line 285 ⟶ 683:
}
 
/* will print: T F F F T T T */</langsyntaxhighlight>
 
=={{header|C sharp|C#}}==
{
<syntaxhighlight lang="csharp">using System;
using System.Linq;
using System.Text.RegularExpressions;
 
namespace ValidateIsin
{
public static class IsinValidator
{
public static bool IsValidIsin(string isin) =>
IsinRegex.IsMatch(isin) && LuhnTest(Digitize(isin));
 
private static readonly Regex IsinRegex =
new Regex("^[A-Z]{2}[A-Z0-9]{9}\\d$", RegexOptions.Compiled);
 
private static string Digitize(string isin) =>
string.Join("", isin.Select(c => $"{DigitValue(c)}"));
 
private static bool LuhnTest(string number) =>
number.Reverse().Select(DigitValue).Select(Summand).Sum() % 10 == 0;
 
private static int Summand(int digit, int i) =>
digit + (i % 2) * (digit - digit / 5 * 9);
 
private static int DigitValue(char c) =>
c >= '0' && c <= '9'
? c - '0'
: c - 'A' + 10;
}
public class Program
{
public static void Main()
{
string[] isins =
{
"US0378331005",
"US0373831005",
"U50378331005",
"US03378331005",
"AU0000XVGZA3",
"AU0000VXGZA3",
"FR0000988040"
};
 
foreach (string isin in isins) {
string validOrNot = IsinValidator.IsValidIsin(isin) ? "valid" : "not valid";
Console.WriteLine($"{isin} is {validOrNot}");
}
}
}
}</syntaxhighlight>
{{out}}
<pre>US0378331005 is valid
US0373831005 is not valid
U50378331005 is not valid
US03378331005 is not valid
AU0000XVGZA3 is valid
AU0000VXGZA3 is valid
FR0000988040 is valid</pre>
 
=={{header|C++}}==
<langsyntaxhighlight lang="cpp">
 
#include <string>
Line 349 ⟶ 809:
return 0;
}
</syntaxhighlight>
</lang>
 
=={{header|Caché ObjectScript}}==
 
<langsyntaxhighlight lang="cos">Class Utils.Check [ Abstract ]
{
 
Line 381 ⟶ 842:
}
 
}</langsyntaxhighlight>
{{out|Examples}}
<pre>USER>For { Read isin Quit:isin="" Write ": "_##class(Utils.Check).ISIN(isin), ! }
Line 393 ⟶ 854:
 
USER></pre>
 
=={{header|Clojure}}==
<syntaxhighlight lang="clojure">(defn luhn? [cc]
(let [sum (->> cc
(map #(Character/digit ^char % 10))
reverse
(map * (cycle [1 2]))
(map #(+ (quot % 10) (mod % 10)))
(reduce +))]
(zero? (mod sum 10))))
 
(defn is-valid-isin? [isin]
(and (re-matches #"^[A-Z]{2}[A-Z0-9]{9}[0-9]$" isin)
(->> isin
(map #(Character/digit ^char % 36))
(apply str)
luhn?)))
 
(use 'clojure.pprint)
(doseq [isin ["US0378331005" "US0373831005" "U50378331005" "US03378331005"
"AU0000XVGZA3" "AU0000VXGZA3" "FR0000988040"]]
(cl-format *out* "~A: ~:[invalid~;valid~]~%" isin (is-valid-isin? isin)))
</syntaxhighlight>
<tt>luhn?</tt> is based on ''[[Luhn test of credit card numbers#Clojure]]''.
{{out}}
<pre>US0378331005: valid
US0373831005: invalid
U50378331005: invalid
US03378331005: invalid
AU0000XVGZA3: valid
AU0000VXGZA3: valid
FR0000988040: valid</pre>
 
=={{header|COBOL}}==
By Steve Williams {{works with|GnuCOBOL}}
<langsyntaxhighlight lang="cobol"> >>SOURCE FORMAT FREE
*> this is gnucobol 2.0
identification division.
Line 568 ⟶ 1,061:
goback
.
end program luhntest.</langsyntaxhighlight>
 
{{out}}
Line 581 ⟶ 1,074:
 
=={{header|Common Lisp}}==
<langsyntaxhighlight lang="lisp">(defun alphap (char)
(char<= #\A char #\Z))
 
Line 618 ⟶ 1,111:
(dolist (isin '("US0378331005" "US0373831005" "U50378331005" "US03378331005"
"AU0000XVGZA3" "AU0000VXGZA3" "FR0000988040"))
(format t "~A: ~:[invalid~;valid~]~%" isin (valid-isin-p isin))))</langsyntaxhighlight>
{{out}}
<pre>US0378331005: valid
Line 631 ⟶ 1,124:
{{trans|Java}}
Code for the luhn test was taken from [[https://rosettacode.org/wiki/Luhn_test_of_credit_card_numbers#D]]
<langsyntaxhighlight Dlang="d">import std.stdio;
 
void main() {
Line 670 ⟶ 1,163:
import luhn;
return luhnTest(sb.data);
}</langsyntaxhighlight>
 
{{out}}
Line 680 ⟶ 1,173:
AU0000VXGZA3 is valid
FR0000988040 is valid</pre>
 
=={{header|Dart}}==
<syntaxhighlight lang="dart">bool checkISIN(String isin) {
int j = 0, v = 0;
List<int> s = List.filled(24, 0);
 
for (int i = 0; i < 12; i++) {
int k = isin.codeUnitAt(i);
if (k >= '0'.codeUnitAt(0) && k <= '9'.codeUnitAt(0)) {
if (i < 2) return false;
s[j++] = k - '0'.codeUnitAt(0);
} else if (k >= 'A'.codeUnitAt(0) && k <= 'Z'.codeUnitAt(0)) {
if (i == 11) return false;
k -= 'A'.codeUnitAt(0) - 10;
s[j++] = k ~/ 10;
s[j++] = k % 10;
} else {
return false;
}
}
 
if (isin.length > 12) return false;
 
for (int i = j - 2; i >= 0; i -= 2) {
int k = 2 * s[i];
v += k > 9 ? k - 9 : k;
}
 
for (int i = j - 1; i >= 0; i -= 2) {
v += s[i];
}
 
return v % 10 == 0;
}
 
void main() {
List<String> test = [
"US0378331005",
"US0373831005",
"U50378331005",
"US03378331005",
"AU0000XVGZA3",
"AU0000VXGZA3",
"FR0000988040"
];
 
for (String isin in test) {
print('$isin - ${checkISIN(isin)}');
}
}</syntaxhighlight>
{{out}}
<pre>US0378331005 - true
US0373831005 - false
U50378331005 - false
US03378331005 - false
AU0000XVGZA3 - true
AU0000VXGZA3 - true
FR0000988040 - true</pre>
 
=={{header|Delphi}}==
{{works with|Delphi|6.0}}
{{libheader|SysUtils,StdCtrls}}
 
 
<syntaxhighlight lang="Delphi">
 
 
function StrToBase10(S: string): TByteDynArray;
{Convert ASCII string to Base-10}
{ASCII Digits converted to integer 0..9 }
{ASCII Chars convert to bytes "A"=10, "B"=11, etc }
var I: Integer;
var B: byte;
 
procedure StoreByte(B: byte);
begin
SetLength(Result,Length(Result)+1);
Result[High(Result)]:=B;
end;
 
begin
SetLength(Result,0);
for I:=1 to Length(S) do
begin
if S[I] in ['0'..'9'] then StoreByte(Byte(S[I])-$30)
else
begin
B:=(Byte(S[I])-$41)+10;
StoreByte(B div 10);
StoreByte(B mod 10);
end;
end;
end;
 
{Simplifies cases where we have to sum a two digit number}
 
const DigitSum: array [0..18] of byte = (0,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9);
 
function LuhnTest(Nums: array of byte): boolean;
{Perform Luhn Test of byte array}
var I,J,Len,Sum,Sum1,Sum2: integer;
var Rev: array of byte;
begin
Sum1:=0; Sum2:=0;
Len:=High(Nums);
for I:=Len downto 0 do
if ((I-Len) and 1)=0 then Sum1:=Sum1 + Nums[I]
else Sum2:=Sum2 + DigitSum[Nums[I]*2];
Sum:=Sum1+Sum2;
Result:=(Sum mod 10)=0;
end;
 
{String error types}
 
type TStringErrors = (seNone,seLength,seCountry);
 
function ValidateStr(IDStr: string): TStringErrors;
{Validate string checking for incorrectly length}
{And invalid country code}
begin
if Length(IDStr)<>12 then Result:=seLength
else if not (IDStr[1] in ['a'..'z','A'..'Z']) or
not (IDStr[2] in ['a'..'z','A'..'Z']) then Result:=seCountry
else Result:=seNone;
end;
 
 
 
procedure ValidateID(Memo: TMemo; IDStr: string);
{Validate and display status of string}
var BA: TByteDynArray;
var LT: boolean;
var SE: TStringErrors;
var S: string;
begin
SE:=ValidateStr(IDStr);
BA:=StrToBase10(IDStr);
LT:=LuhnTest(BA);
if LT and (SE=seNone) then Memo.Lines.Add(IDStr+': Valid')
else
begin
S:=IDStr+': Invalid';
if not LT then S:=S+', Luhn Error';
case SE of
seLength: S:=S+', Length Error';
seCountry: S:=S+', Country Code Error';
end;
Memo.Lines.Add(S);
end;
end;
 
 
 
procedure ValidateSecuritiesID(Memo: TMemo);
var BA: TByteDynArray;
var I: integer;
var S: string;
begin
ValidateID(Memo,'US0378331005');
ValidateID(Memo,'US0373831005');
ValidateID(Memo,'U50378331005');
ValidateID(Memo,'US03378331005');
ValidateID(Memo,'AU0000XVGZA3');
ValidateID(Memo,'AU0000VXGZA3');
ValidateID(Memo,'FR0000988040');
end;
 
 
 
</syntaxhighlight>
{{out}}
<pre>
US0378331005: Valid
US0373831005: Invalid, Luhn Error
U50378331005: Invalid, Country Code Error
US03378331005: Invalid, Length Error
AU0000XVGZA3: Valid
AU0000VXGZA3: Valid
FR0000988040: Valid
 
Elapsed Time: 6.863 ms.
 
</pre>
 
 
=={{header|EasyLang}}==
{{trans|AWK}}
<syntaxhighlight>
func isin t$ .
if len t$ <> 12
return 0
.
for i to 12
k = strcode substr t$ i 1
if k >= 48 and k <= 57
if i <= 2
return 0
.
s[] &= k - 48
elif k >= 65 and k <= 91
if (i = 12)
return 0
.
k -= 55
s[] &= k div 10
s[] &= k mod 10
else
return 0
.
.
i = len s[] - 1
while i >= 1
k = 2 * s[i]
if k > 9
k -= 9
.
v += k
i -= 2
.
i = len s[]
while i >= 1
v += s[i]
i -= 2
.
if v mod 10 = 0
return 1
.
.
test$[] = [ "US0378331005" "US0373831005" "U50378331005" "US03378331005" "AU0000XVGZA3" "AU0000VXGZA3" "FR0000988040" ]
for t$ in test$[]
if isin t$ = 1
print t$ & " is valid"
else
print t$ & " is invalid"
.
.
</syntaxhighlight>
{{out}}
<pre>
US0378331005 is valid
US0373831005 is invalid
U50378331005 is invalid
US03378331005 is invalid
AU0000XVGZA3 is valid
AU0000VXGZA3 is valid
FR0000988040 is valid
</pre>
 
=={{header|Elixir}}==
used Luhn module from [[Luhn_test_of_credit_card_numbers#Elixir | here]]
<langsyntaxhighlight lang="elixir">isin? = fn str ->
if str =~ ~r/\A[A-Z]{2}[A-Z0-9]{9}\d\z/ do
String.codepoints(str)
Line 701 ⟶ 1,441:
AU0000VXGZA3
FR0000988040)
|> Enum.each(&IO.puts "#{&1}\t#{isin?.(&1)}")</langsyntaxhighlight>
 
{{out}}
Line 713 ⟶ 1,453:
AU0000VXGZA3 true
FR0000988040 true
</pre>
 
=={{header|Factor}}==
We re-use the <code>luhn?</code> word from ''[[Luhn test of credit card numbers#Factor]]''.
<syntaxhighlight lang="factor">USING: combinators.short-circuit.smart formatting kernel luhn
math math.parser qw sequences strings unicode ;
IN: rosetta-code.isin
 
CONSTANT: test-cases qw{
US0378331005 US0373831005 U50378331005 US03378331005
AU0000XVGZA3 AU0000VXGZA3 FR0000988040
}
 
: valid-length? ( str -- ? ) length 12 = ;
 
: valid-country-code? ( str -- ? ) first2 [ Letter? ] both? ;
 
: valid-security-code? ( str -- ? )
[ 2 11 ] dip subseq [ alpha? ] all? ;
: valid-checksum-digit? ( str -- ? ) last digit? ;
: valid-format? ( str -- ? ) {
[ valid-length? ]
[ valid-country-code? ]
[ valid-security-code? ]
[ valid-checksum-digit? ]
} && ;
: base36>base10 ( str -- n )
>upper [ dup LETTER? [ 55 - number>string ] [ 1string ] if ]
{ } map-as concat string>number ;
: isin? ( str -- ? )
{ [ valid-format? ] [ base36>base10 luhn? ] } && ;
: main ( -- )
test-cases [
dup isin? "" " not" ? "%s is%s valid\n" printf
] each ;
MAIN: main</syntaxhighlight>
{{out}}
<pre>
US0378331005 is valid
US0373831005 is not valid
U50378331005 is not valid
US03378331005 is not valid
AU0000XVGZA3 is valid
AU0000VXGZA3 is valid
FR0000988040 is valid
</pre>
 
=={{header|Fortran}}==
 
<langsyntaxhighlight lang="fortran">program isin
use ctype
implicit none
Line 773 ⟶ 1,564:
check_isin = 0 == mod(v, 10)
end function
end program</langsyntaxhighlight>
 
=={{header|FreeBASIC}}==
<langsyntaxhighlight lang="freebasic">' version 27-10-2016
' compile with: fbc -s console
 
Line 853 ⟶ 1,644:
Print : Print "hit any key to end program"
Sleep
End</langsyntaxhighlight>
{{out}}
<pre>US0378331005 Valid
Line 865 ⟶ 1,656:
=={{header|Go}}==
 
<langsyntaxhighlight lang="go">package main
 
import "regexp"
Line 894 ⟶ 1,685:
sum += int(n[11] - '0')
return sum%10 == 0
}</langsyntaxhighlight>
 
<langsyntaxhighlight lang="go">package main
 
import "testing"
Line 921 ⟶ 1,712:
}
}
}</langsyntaxhighlight>
 
=={{header|Groovy}}==
Line 927 ⟶ 1,718:
{{update|Groovy|Use the new test-cases, and consider calling the existing Luhn algorithm implementation from the ''[[Luhn test of credit card numbers]]'' task instead of duplicating it.}}
 
<langsyntaxhighlight lang="groovy">CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
 
int checksum(String prefix) {
Line 939 ⟶ 1,730:
assert checksum('GB000263494') == 6
assert checksum('US037833100') == 5
assert checksum('US037833107') == 0</langsyntaxhighlight>
 
=={{header|Haskell}}==
<langsyntaxhighlight Haskelllang="haskell">module ISINVerification2 where
 
import Data.Char (isUpper, isDigit, digitToInt)
Line 1,023 ⟶ 1,814:
, "FR0000988040"
]
mapM_ printSolution isinnumbers</langsyntaxhighlight>
 
{{out}}
Line 1,034 ⟶ 1,825:
FR0000988040 is valid</pre>
 
Or, making alternative choices from the standard libraries:
<syntaxhighlight lang ="haskell">import qualified DataControl.Map asMonad M((<=<))
import ControlData.MonadBifunctor (sequence, liftM2first)
import Data.List (foldl') -- '
import qualified Data.Map as M
import Data.Maybe (fromMaybe)
 
-------------------- VALID ISIN STRING -------------------
validISIN, isinPattern, luhn :: String -> Bool
validISIN = liftM2 (&&) isinPattern (luhn . (show =<<) . stringInts)
 
validISIN :: String -> Bool
validISIN =
(&&) . isinPattern
<*> luhn . (show <=< stringInts)
 
isinPattern :: String -> Bool
isinPattern s =
12 == length s &&
&& all (`elem` capitals) l
let [l, m, r] = bites [2, 9, 1] s
in && all (`elem` (capitals) l<> &&digits)) m
all (`elem` (capitals ++ digits)) m && head r `elem` digits
where
[l, m, r] = bites s [2, 9, 1]
 
luhn x:: =String -> Bool
luhn x let= odd0 == [rem (:s1 []),+ consts2) []]10
where
even = reverse odd
odds = [(: []), const []]
stream f = concat $ zipWith ($) (cycle f) (stringInts $ reverse x)
s1evens = sum (streamreverse odd)odds
stream f =
s2 = sum $ sum . stringInts . show . (2 *) <$> stream even
in rem (s1 + s2)concat 10 == 0$
zipWith ($) (cycle f) (stringInts $ reverse x)
s1 = sum (stream odds)
s2 =
sum $
sum . stringInts . show . (2 *) <$> stream evens
 
charMap :: M.Map Char Int
charMap = M.fromList $ zip (digits ++<> capitals) [0 ..]
 
stringInts :: String -> [Int]
stringInts = fromMaybe [] . sequence . fmaptraverse (`M.lookup` charMap)
 
bites :: [Inta] -> [aInt] -> [[a]]
bites ns xs =
(reverse . fst $)
. foldl' -- '
foldr
(\x (a, r) x -> first (: a) (splitAt x r))
let (b[], r_xs) = splitAt x r
in (b : a, r_))
([], xs)
(reverse ns)
 
capitals, digits :: String
capitals = ['A' .. 'Z']
 
digits = ['0' .. '9']
 
--------------------------- TEST -------------------------
main :: IO ()
main =
mapM_
(print . ((,) <*> validISIN))
[ "US0378331005",
, "US0373831005",
, "U50378331005",
, "US03378331005",
, "AU0000XVGZA3",
, "AU0000VXGZA3",
, "FR0000988040"
]</langsyntaxhighlight>
{{Out}}
<pre>("US0378331005",True)
Line 1,101 ⟶ 1,903:
 
'''Solution:'''
<langsyntaxhighlight lang="j">require'regex'
validFmt=: 0 -: '^[A-Z]{2}[A-Z0-9]{9}[0-9]{1}$'&rxindex
 
Line 1,107 ⟶ 1,909:
luhn=: 0 = 10 (| +/@,) 10 #.inv 1 2 *&|: _2 "."0\ |. NB. as per task Luhn_test_of_credit_card_numbers#J
 
validISIN=: validFmt *. luhn@df36</langsyntaxhighlight>
 
'''Required Examples:'''
<langsyntaxhighlight lang="j"> Tests=: 'US0378331005';'US0373831005';'U50378331005';'US03378331005';'AU0000XVGZA3';'AU0000VXGZA3';'FR0000988040'
validISIN&> Tests
1 0 0 0 1 1 1</langsyntaxhighlight>
 
=={{header|Java}}==
As the Luhn test method from the ''[[Luhn test of credit card numbers]]'' task is only a few lines, it has been embedded in the ISIN class for convenience.
{{incorrect|Java|Output isn't according to the input.}}
This now assumes that the existing Luhn algorithm implementation from the ''[[Luhn test of credit card numbers]]'' task is available in the same (default) package.
 
<lang java>public class ISIN {
 
<syntaxhighlight lang="java">public class ISIN {
public static void main(String[] args) {
String[] isins = {
"US0378331005",
"US0373831005",
"U50378331005",
"US03378331005",
"AU0000XVGZA3",
"AU0000VXGZA3",
"FR0000988040",
};
for (String isin : isins)
System.out.printf("%s is %s%n\n", isin, ISINtest(isin) ? "valid" : "not valid");
}
 
static boolean ISINtest(String isin) {
isin = isin.trim().toUpperCase();
 
if (!isin.matches("^[A-Z]{2}[A-Z0-9]{9}\\d$"))
return false;
 
StringBuilder sb = new StringBuilder();
for (char c : isin.substring(0, 12).toCharArray())
sb.append(Character.digit(c, 36));
return luhnTest(sb.toString());
}
 
static boolean return Luhn.luhnTest(sb.toString(String number)); {
int s1 = 0, s2 = 0;
String reverse = new StringBuffer(number).reverse().toString();
for (int i = 0; i < reverse.length(); i++){
int digit = Character.digit(reverse.charAt(i), 10);
//This is for odd digits, they are 1-indexed in the algorithm.
if (i % 2 == 0){
s1 += digit;
} else { // Add 2 * digit for 0-4, add 2 * digit - 9 for 5-9.
s2 += 2 * digit;
if(digit >= 5){
s2 -= 9;
}
}
}
return (s1 + s2) % 10 == 0;
}
}</langsyntaxhighlight>
 
<pre>US0378331005 is valid
US0373831009US0373831005 is not valid
D56000543287U50378331005 is not valid
US03378331005 is not valid
AU0000XVGZA3 is valid
AU0000VXGZA3 is valid
GB0002634946FR0000988040 is valid</pre>
 
US0373831005 is not valid</pre>
=={{header|jq}}==
{{works with|jq}}
'''Works with gojq, the Go implementation of jq'''
<syntaxhighlight lang="jq"># This filter may be applied to integers or integer-valued strings
def luhntest:
def digits: tostring | explode | map([.]|implode|tonumber);
(digits | reverse)
| ( [.[range(0;length;2)]] | add ) as $sum1
| [.[range(1;length;2)]]
| (map( (2 * .) | if . > 9 then (digits|add) else . end) | add) as $sum2
| ($sum1 + $sum2) % 10 == 0;
 
def decodeBase36:
# decode a single character
def d1:
explode[0]
# "0" is 48; "A" is 65
| if . < 65 then . - 48
else . - 55
end;
def chars: explode | map([.]|implode);
chars | map(d1) | join("");
 
def is_ISIN:
type == "string"
and test("^(?<cc>[A-Z][A-Z])(?<sc>[0-9A-Z]{9})(?<cs>[0-9])$")
and (decodeBase36 | luhntest);</syntaxhighlight>
'''The Task'''
<syntaxhighlight lang="jq">def task:
"US0378331005",
"US0373831005",
"U50378331005",
"US03378331005",
"AU0000XVGZA3",
"AU0000VXGZA3",
"FR0000988040"
| . + " => " + (if is_ISIN then "valid" else "invalid" end);
 
task</syntaxhighlight>
{{out}}
<pre>
US0378331005 => valid
US0373831005 => invalid
U50378331005 => invalid
US03378331005 => invalid
AU0000XVGZA3 => valid
AU0000VXGZA3 => valid
FR0000988040 => valid
</pre>
 
 
=={{header|Julia}}==
<syntaxhighlight lang="julia">using Printf
{{works with|Julia|0.6}}
 
<lang julia>luhntest(x) = luhntest(parse(Int, x))
 
function checkISIN(inum::AbstractString)
Line 1,168 ⟶ 2,039:
"US03378331005", "AU0000XVGZA3", "AU0000VXGZA3", "FR0000988040"]
@printf("%-15s %5s\n", inum, ifelse(checkISIN(inum), "pass", "fail"))
end</langsyntaxhighlight>
 
{{out}}
Line 1,181 ⟶ 2,052:
=={{header|Kotlin}}==
As the Luhn test method is only a few lines, it's reproduced here for convenience:
<langsyntaxhighlight lang="scala">// version 1.1
 
object Isin {
Line 1,217 ⟶ 2,088:
println("$isin\t -> ${if (Isin.isValid(isin)) "valid" else "not valid"}")
}
}</langsyntaxhighlight>
 
{{out}}
Line 1,229 ⟶ 2,100:
FR0000988040 -> valid
</pre>
 
=={{header|langur}}==
The luhn test is repeated here for simplicity.
 
<syntaxhighlight lang="langur">val .luhntest = fn(.s) {
val .t = [0, 2, 4, 6, 8, 1, 3, 5, 7, 9]
val .numbers = s2n .s
val .oddeven = len(.numbers) rem 2
 
for[=0] .i of .numbers {
_for += if .i rem 2 == .oddeven {
.numbers[.i]
} else {
.t[.numbers[.i]+1]
}
} div 10
}
 
val .isintest = fn(.s) {
.s -> re/^[A-Z][A-Z][0-9A-Z]{9}[0-9]$/ and
.luhntest(join s2n .s)
}
 
val .tests = {
"US0378331005": true,
"US0373831005": false,
"U50378331005": false,
"AU0000XVGZA3": true,
"AU0000VXGZA3": true,
"FR0000988040": true,
"US03378331005": false,
}
 
for .key in sort(keys .tests) {
val .pass = .isintest(.key)
write .key, ": ", .pass
writeln if(.pass == .tests[.key]: ""; " (ISIN TEST FAILED)")
}</syntaxhighlight>
 
{{out}}
<pre>AU0000VXGZA3: true
AU0000XVGZA3: true
FR0000988040: true
U50378331005: false
US03378331005: false
US0373831005: false
US0378331005: true</pre>
 
=={{header|Lua}}==
<langsyntaxhighlight Lualang="lua">function luhn (n)
local revStr, s1, s2, digit, mod = n:reverse(), 0, 0
for pos = 1, #revStr do
Line 1,267 ⟶ 2,185:
"FR0000988040"
}
for _, ISIN in pairs(testCases) do print(ISIN, checkISIN(ISIN)) end</langsyntaxhighlight>
{{out}}
<pre>US0378331005 true
Line 1,276 ⟶ 2,194:
AU0000VXGZA3 true
FR0000988040 true</pre>
 
=={{header|Mathematica}} / {{header|Wolfram Language}}==
<syntaxhighlight lang="mathematica">ClearAll[LuhnQ, VakudISINQ]
LuhnQ[n_Integer] := Block[{digits = Reverse@IntegerDigits@n}, Mod[Total[{digits[[;; ;; 2]], IntegerDigits[2 #] & /@ digits[[2 ;; ;; 2]]}, -1], 10] == 0]
VakudISINQ[sin_String] := Module[{s = ToUpperCase[sin]},
If[StringMatchQ[s,
LetterCharacter ~~ LetterCharacter ~~
Repeated[DigitCharacter | LetterCharacter, {9}] ~~
DigitCharacter],
s = StringJoin[
Characters[s] /.
Thread[CharacterRange["A", "Z"] -> ToString /@ Range[10, 35]]];
LuhnQ[ToExpression[s]]
,
False
]
]
VakudISINQ /@ {"US0378331005", "US0373831005", "U50378331005", "US03378331005", "AU0000XVGZA3", "AU0000VXGZA3", "FR0000988040"}</syntaxhighlight>
{{out}}
<pre>{True, False, False, False, True, True, True}</pre>
 
=={{header|Nim}}==
<syntaxhighlight lang="nim">import strformat
 
const
DigitRange = '0'..'9'
UpperCaseRange = 'A'..'Z'
 
type ISINError = object of ValueError
 
 
proc luhn(s: string): bool =
const m = [0, 2, 4, 6, 8, 1, 3, 5, 7, 9]
var sum = 0
var odd = true
for i in countdown(s.high, 0):
let digit = ord(s[i]) - ord('0')
sum += (if odd: digit else: m[digit])
odd = not odd
result = sum mod 10 == 0
 
 
proc validateISIN(s: string) =
if s.len != 12:
raise newException(ISINError, "wrong length")
if s[0] notin UpperCaseRange or s[1] notin UpperCaseRange:
raise newException(ISINError, "wrong country code")
if s[11] notin DigitRange:
raise newException(ISINError, "wrong checksum character")
var t: string
for ch in s:
case ch
of '0'..'9': t.add ch
of 'A'..'Z': t.addInt ord(ch) - ord('A') + 10
else: raise newException(ISINError, "invalid characters in code")
if not t.luhn():
raise newException(ISINError, "checksum error")
 
 
when isMainModule:
 
for isin in ["US0378331005", "US0373831005", "U50378331005",
"US03378331005", "AU0000XVGZA3", "AU0000VXGZA3", "FR0000988040"]:
try:
isin.validateISIN()
echo &"{isin} is valid."
except ISINError:
echo &"{isin} is not valid: {getCurrentExceptionMsg()}."</syntaxhighlight>
 
{{out}}
<pre>US0378331005 is valid.
US0373831005 is not valid: checksum error.
U50378331005 is not valid: wrong country code.
US03378331005 is not valid: wrong length.
AU0000XVGZA3 is valid.
AU0000VXGZA3 is valid.
FR0000988040 is valid.</pre>
 
=={{header|Perl}}==
We reuse the <tt>luhn_test()</tt> function from ''[[Luhn test of credit card numbers#Perl]]''.
<langsyntaxhighlight lang="perl">use strict;
use English;
use POSIX;
Line 1,299 ⟶ 2,294:
split(//s, $isin));
return luhn_test($base10);
}</langsyntaxhighlight>
{{out}}
<pre>1..7
Line 1,309 ⟶ 2,304:
ok 6 - Test 6
ok 7 - Test 7</pre>
 
=={{header|Perl 6}}==
{{works with|Rakudo|2018.03}}
 
Using the <tt>luhn-test</tt> function from the ''[[Luhn test of credit card numbers#Perl 6|Luhn test of credit card numbers]]'' task.
 
<lang perl6>my $ISIN = /
^ <[A..Z]>**2 <[A..Z0..9]>**9 <[0..9]> $
<?{ luhn-test $/.comb.map({ :36($_) }).join }>
/;
 
sub luhn-test ($number --> Bool) {
my @digits = $number.comb.reverse;
my $sum = @digits[0,2...*].sum
+ @digits[1,3...*].map({ |($_ * 2).comb }).sum;
return $sum %% 10;
}
 
# Testing:
 
say "$_ is {$ISIN ?? "valid" !! "not valid"}" for <
US0378331005
US0373831005
U50378331005
US03378331005
AU0000XVGZA3
AU0000VXGZA3
FR0000988040
>;</lang>
 
{{out}}
<pre>
US0378331005 is valid
US0373831005 is not valid
U50378331005 is not valid
US03378331005 is not valid
AU0000XVGZA3 is valid
AU0000VXGZA3 is valid
FR0000988040 is valid
</pre>
 
=={{header|Phix}}==
Note this (slightly better) version of Luhn() has the reverse() inside it, whereas the original did not.
<!--<syntaxhighlight lang="phix">(phixonline)-->
<lang Phix>function Luhn(string st)
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span>
integer s=0, d
<span style="color: #008080;">function</span> <span style="color: #000000;">Luhn</span><span style="color: #0000FF;">(</span><span style="color: #004080;">string</span> <span style="color: #000000;">st</span><span style="color: #0000FF;">)</span>
st = reverse(st)
<span style="color: #004080;">integer</span> <span style="color: #000000;">s</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">d</span>
for i=1 to length(st) do
<span style="color: #000000;">st</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">reverse</span><span style="color: #0000FF;">(</span><span style="color: #000000;">st</span><span style="color: #0000FF;">)</span>
d = st[i]-'0'
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">st</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
s += iff(mod(i,2)?d,d*2-(d>4)*9)
<span style="color: #000000;">d</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">st</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]-</span><span style="color: #008000;">'0'</span>
end for
<span style="color: #000000;">s</span> <span style="color: #0000FF;">+=</span> <span style="color: #008080;">iff</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">mod</span><span style="color: #0000FF;">(</span><span style="color: #000000;">i</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2</span><span style="color: #0000FF;">)?</span><span style="color: #000000;">d</span><span style="color: #0000FF;">,</span><span style="color: #000000;">d</span><span style="color: #0000FF;">*</span><span style="color: #000000;">2</span><span style="color: #0000FF;">-(</span><span style="color: #000000;">d</span><span style="color: #0000FF;">></span><span style="color: #000000;">4</span><span style="color: #0000FF;">)*</span><span style="color: #000000;">9</span><span style="color: #0000FF;">)</span>
return remainder(s,10)=0
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
end function
<span style="color: #008080;">return</span> <span style="color: #7060A8;">remainder</span><span style="color: #0000FF;">(</span><span style="color: #000000;">s</span><span style="color: #0000FF;">,</span><span style="color: #000000;">10</span><span style="color: #0000FF;">)=</span><span style="color: #000000;">0</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
function valid_ISIN(string st)
-- returns 1 if valid, else 0/2/3/4.
<span style="color: #008080;">function</span> <span style="color: #000000;">valid_ISIN</span><span style="color: #0000FF;">(</span><span style="color: #004080;">string</span> <span style="color: #000000;">st</span><span style="color: #0000FF;">)</span>
-- (feel free to return 0 instead of 2/3/4)
<span style="color: #000080;font-style:italic;">-- returns 1 if valid, else 0/2/3/4.
if length(st)!=12 then return 2 end if
-- (feel free to return 0 instead of 2/3/4)</span>
for i=length(st) to 1 by -1 do
<span style="color: #008080;">if</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">st</span><span style="color: #0000FF;">)!=</span><span style="color: #000000;">12</span> <span style="color: #008080;">then</span> <span style="color: #008080;">return</span> <span style="color: #000000;">2</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
integer ch = st[i]
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">st</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">to</span> <span style="color: #000000;">1</span> <span style="color: #008080;">by</span> <span style="color: #0000FF;">-</span><span style="color: #000000;">1</span> <span style="color: #008080;">do</span>
if ch>='A' then
<span style="color: #004080;">integer</span> <span style="color: #000000;">ch</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">st</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span>
if ch>'Z' then return 3 end if
<span style="color: #008080;">if</span> <span style="color: #000000;">ch</span><span style="color: #0000FF;">>=</span><span style="color: #008000;">'A'</span> <span style="color: #008080;">then</span>
st[i..i] = sprintf("%d",ch-55)
<span style="color: #008080;">if</span> <span style="color: #000000;">ch</span><span style="color: #0000FF;">></span><span style="color: #008000;">'Z'</span> <span style="color: #008080;">then</span> <span style="color: #008080;">return</span> <span style="color: #000000;">3</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
elsif i<=2 then
<span style="color: #000000;">st</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">..</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">sprintf</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"%d"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">ch</span><span style="color: #0000FF;">-</span><span style="color: #000000;">55</span><span style="color: #0000FF;">)</span>
return 4
<span style="color: #008080;">elsif</span> <span style="color: #000000;">i</span><span style="color: #0000FF;"><=</span><span style="color: #000000;">2</span> <span style="color: #008080;">then</span>
elsif ch<'0' or ch>'9' then
<span style="color: #008080;">return</span> <span style="color: #000000;">4</span>
return 3
<span style="color: #008080;">elsif</span> <span style="color: #000000;">ch</span><span style="color: #0000FF;"><</span><span style="color: #008000;">'0'</span> <span style="color: #008080;">or</span> <span style="color: #000000;">ch</span><span style="color: #0000FF;">></span><span style="color: #008000;">'9'</span> <span style="color: #008080;">then</span>
end if
<span style="color: #008080;">return</span> <span style="color: #000000;">3</span>
end for
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
return Luhn(st)
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
end function
<span style="color: #008080;">return</span> <span style="color: #000000;">Luhn</span><span style="color: #0000FF;">(</span><span style="color: #000000;">st</span><span style="color: #0000FF;">)</span>
 
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
sequence tests = {"US0378331005", -- valid
"US0373831005", -- not valid The transposition typo is caught by the checksum constraint.
<span style="color: #008080;">constant</span> <span style="color: #000000;">tests</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #008000;">"US0378331005"</span><span style="color: #0000FF;">,</span> <span style="color: #000080;font-style:italic;">-- valid </span>
"U50378331005", -- not valid The substitution typo is caught by the format constraint.
<span style="US03378331005color: #008000;">"US0373831005"</span><span style="color: #0000FF;">,</span> <span style="color: #000080;font-style:italic;">-- not valid The duplicationtransposition typo is caught by the formatchecksum constraint.</span>
<span style="color: #008000;">"U50378331005"</span><span style="color: #0000FF;">,</span> <span style="color: #000080;font-style:italic;">-- not valid The substitution typo is caught by the format constraint.</span>
"AU0000XVGZA3", -- valid
<span style="AU0000VXGZA3color: #008000;">"US03378331005"</span><span style="color: #0000FF;">,</span> <span style="color: #000080;font-style:italic;">-- not valid Unfortunately, not all transposition typosThe duplication typo areis caught by the checksumformat constraint.</span>
<span style="color: #008000;">"AU0000XVGZA3"</span><span style="color: #0000FF;">,</span> <span style="color: #000080;font-style:italic;">-- valid </span>
"FR0000988040"} -- valid
<span style="color: #008000;">"AU0000VXGZA3"</span><span style="color: #0000FF;">,</span> <span style="color: #000080;font-style:italic;">-- valid Unfortunately, not all transposition typos are caught by the checksum constraint.</span>
 
<span style="color: #008000;">"FR0000988040"</span><span style="color: #0000FF;">},</span> <span style="color: #000080;font-style:italic;">-- valid</span>
constant reasons = {"wrong checksum","valid","wrong length","bad char","wrong country"}
 
<span style="color: #000000;">reasons</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #008000;">"wrong checksum"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"valid"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"wrong length"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"bad char"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"wrong country"</span><span style="color: #0000FF;">}</span>
for i=1 to length(tests) do
string ti = tests[i]
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">tests</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
printf(1,"%s : %s\n",{ti,reasons[valid_ISIN(ti)+1]})
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"%s : %s\n"</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">tests</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">],</span><span style="color: #000000;">reasons</span><span style="color: #0000FF;">[</span><span style="color: #000000;">valid_ISIN</span><span style="color: #0000FF;">(</span><span style="color: #000000;">tests</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">])+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]})</span>
end for</lang>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<!--</syntaxhighlight>-->
{{out}}
<pre>
Line 1,407 ⟶ 2,364:
=={{header|PicoLisp}}==
Using the <tt>luhn</tt> function defined at ''[[Luhn test of credit card numbers#PicoLisp]]'':
<langsyntaxhighlight PicoLisplang="picolisp">(de isin (Str)
(let Str (mapcar char (chop Str))
(and
Line 1,429 ⟶ 2,386:
"AU0000XVGZA3"
"AU0000VXGZA3"
"FR0000988040" ) ) )</langsyntaxhighlight>
{{out}}
<pre>(0 NIL NIL NIL 0 0 0)</pre>
 
=={{header|PowerShell}}==
<syntaxhighlight lang="powershell">
<lang PowerShell>
function Test-ISIN
{
Line 1,498 ⟶ 2,455:
(10 - ($sum % 10)) % 10 -match $checkDigit
}
</syntaxhighlight>
</lang>
<syntaxhighlight lang="powershell">
<lang PowerShell>
"US0378331005","US0373831005","US0337833103","AU0000XVGZA3","AU0000VXGZA3","FR0000988040" | ForEach-Object {
[PSCustomObject]@{
Line 1,506 ⟶ 2,463:
}
}
</syntaxhighlight>
</lang>
{{Out}}
<pre>
Line 1,520 ⟶ 2,477:
 
=={{header|PureBasic}}==
<langsyntaxhighlight PureBasiclang="purebasic">EnableExplicit
 
Procedure.b Check_ISIN(*c.Character)
Line 1,568 ⟶ 2,525:
CloseFile(0)
EndIf
Input()</langsyntaxhighlight>
{{Out}}
<pre>US0378331005 TRUE
Line 1,580 ⟶ 2,537:
=={{header|Python}}==
 
<langsyntaxhighlight lang="python">def check_isin(a):
if len(a) != 12 or not all(c.isalpha() for c in a[:2]) or not all(c.isalnum() for c in a[2:]):
return False
Line 1,612 ⟶ 2,569:
"AU0000XVGZA3", "AU0000VXGZA3", "FR0000988040"]]
 
# [True, False, False, False, True, True, True]</langsyntaxhighlight>
 
=={{header|Quackery}}==
 
<code>luhn</code> is defined at [[Luhn test of credit card numbers#Quackery]].
 
<syntaxhighlight lang="quackery"> [ 2 split drop do
char A char z 1+ within
swap
char A char z 1+ within
and ] is 2chars ( $ --> b )
[ dup size 12 != iff
[ drop false ] done
dup 2chars not iff
[ drop false ] done
[] swap
witheach
[ 36 base put
char->n
base release
number$ join ]
$->n drop luhn ] is isin ( n --> b )
 
[ dup echo$
say " is "
isin not if
[ say "not " ]
say "valid." cr ] is task ( n --> )
 
$ "US0378331005" task
$ "US0373831005" task
$ "U50378331005" task
$ "US03378331005" task
$ "AU0000XVGZA3" task
$ "AU0000VXGZA3" task
$ "FR0000988040" task
</syntaxhighlight>
 
{{out}}
 
<pre>US0378331005 is valid.
US0373831005 is not valid.
U50378331005 is not valid.
US03378331005 is not valid.
AU0000XVGZA3 is valid.
AU0000VXGZA3 is valid.
FR0000988040 is valid.</pre>
 
=={{header|Racket}}==
 
<langsyntaxhighlight lang="racket">
#lang racket
 
Line 1,650 ⟶ 2,654:
(map isin-test? test-cases)
;; -> '(#t #f #f #f #t #t #t)
</syntaxhighlight>
</lang>
 
{{out}}
 
'(#t #f #f #f #t #t #t)
 
=={{header|Raku}}==
(formerly Perl 6)
{{works with|Rakudo|2018.12}}
 
Using the <tt>luhn-test</tt> function from the ''[[Luhn test of credit card numbers#Raku|Luhn test of credit card numbers]]'' task.
 
<syntaxhighlight lang="raku" line>my $ISIN = /
^ <[A..Z]>**2 <[A..Z0..9]>**9 <[0..9]> $
<?{ luhn-test $/.comb.map({ :36($_) }).join }>
/;
 
sub luhn-test ($number --> Bool) {
my @digits = $number.comb.reverse;
my $sum = @digits[0,2...*].sum
+ @digits[1,3...*].map({ |($_ * 2).comb }).sum;
return $sum %% 10;
}
 
# Testing:
 
say "$_ is { m/$ISIN/ ?? "valid" !! "not valid"}" for <
US0378331005
US0373831005
U50378331005
US03378331005
AU0000XVGZA3
AU0000VXGZA3
FR0000988040
>;</syntaxhighlight>
 
{{out}}
<pre>
US0378331005 is valid
US0373831005 is not valid
U50378331005 is not valid
US03378331005 is not valid
AU0000XVGZA3 is valid
AU0000VXGZA3 is valid
FR0000988040 is valid
</pre>
 
=={{header|REXX}}==
<langsyntaxhighlight lang="rexx">/*REXX program validates the checksum digit for an International Securities ID number.*/
parse arg z /*obtain optional ISINs from the C.L.*/
if z='' then z= "US0378331005 US0373831005 U50378331005 US03378331005 AU0000XVGZA3" ,
'AU0000VXGZA3 FR0000988040' /* [↑] use the default list of ISINs.*/
/* [↓] process all specified ISINs.*/
do n=1 for words(z); x=word(z, n); y= x /*obtain an ISIN from the Z list. */
$= /* [↓] construct list of ISIN digits. */
do k=1 for length(x); _= substr(x,k,1) /*the ISIN may contain alphabetic chars*/
p= pos(_, '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ') /*X must contain A──►Z, 0──►9.*/
if p==0 then y= /*trigger "not" valid below.*/
else $= $ || p-1 /*convert X string (base 36 ──► dec).*/
end /*k*/ /* [↑] convert alphabetic ──► digits.*/
@= /*placeholder for the "not" in message.*/
if length(y)\==12 then @= "not" /*checksee if the ISIN is exactly 12 chars. */
if \datatype( left(x,2),'U') then @= "not" /* " " " " 1st 2 chars cap. let.*/
if \datatype(right(x,1),'W') then @= "not" /* " " " " last char not a digit*/
if @=='' then if \luhn($) then @= "not" /* " " " " passed the Luhn test. */
say right(x, 30) right(@, 5) "valid" /*display the yea or nay message.*/
end /*n*/ /* [↑] 1st 3 IFs could've been combined*/
exit /*stick a fork in it, we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
Luhn: procedure; parse arg x; $=0 $= 0 /*get credit card number; zero $ sum. */
y= reverse( left(0, length(x) // 2)x) /*add leading zero if needed, & reverse*/
do j=1 to length(y)-1 by 2; _= 2 * substr(y, j+1, 1)
$= $ + substr(y, j, 1) + left(_, 1) + substr(_, 2 , 1, 0)
end /*j*/ /* [↑] sum the odd and even digits.*/
return right($, 1)==0 /*return "1" if number passed Luhn test*/</langsyntaxhighlight>
'''{{out|output''' |text=&nbsp; when using the defaults fordefault inputinputs:}}
<pre>
US0378331005 valid
Line 1,696 ⟶ 2,741:
 
=={{header|Ring}}==
<langsyntaxhighlight lang="ring">
# Project : Validate International Securities Identification Number
# Date : 2018/02/15
# Author : Gal Zsolt (~ CalmoSoft ~)
# Email : <calmosoft@gmail.com>
 
decimals(0)
Line 1,780 ⟶ 2,822:
next
return sumarr
</syntaxhighlight>
</lang>
Output:
<pre>
Line 1,790 ⟶ 2,832:
AU0000VXGZA3 -> Valid
FR0000988040 -> Valid
</pre>
 
=={{header|RPL}}==
<code>LUHN?</code> is defined at [[Luhn test of credit card numbers#RPL|Luhn test of credit card numbers]]
{{works with|RPL|HP48-R}}
« '''IF''' DUP SIZE 12 ≠ '''THEN''' DROP 0
'''ELSE'''
""
1 3 PICK SIZE '''FOR''' j
OVER j DUP SUB
'''IF''' "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" SWAP POS '''THEN''' LASTARG 1 - + '''END'''
'''NEXT'''
<span style="color:blue">LUHN?</span>
{ "AD" "AT" "AU" "BE" "CA" "DE" "ES" "FR" "GB" "HK" "IT" "US" "ZW" } <span style="color:grey">@ country codes sample </span>
ROT 1 2 SUB POS AND
'''END'''
» '<span style="color:blue">ISIN?</span>' STO
 
{"US0378331005" "US0373831005" "U50378331005" "US03378331005" "AU0000XVGZA3" "AU0000VXGZA3" "FR0000988040"}
1 « <span style="color:blue">ISIN?</span> » DOLIST
{{out}}
<pre>
1: { 1 0 0 0 1 1 1 }
</pre>
 
=={{header|Ruby}}==
Using a pre-existing luhn method:
<langsyntaxhighlight lang="ruby">RE = /\A[A-Z]{2}[A-Z0-9]{9}[0-9]{1}\z/
 
def valid_isin?(str)
Line 1,809 ⟶ 2,874:
FR0000988040).map{|tc| valid_isin?(tc) }
# => [true, false, false, false, true, true, true]</langsyntaxhighlight>
 
=={{header|Rust}}==
 
<syntaxhighlight lang="rust">extern crate luhn_cc;
 
use luhn_cc::compute_luhn;
 
fn main() {
assert_eq!(validate_isin("US0378331005"), true);
assert_eq!(validate_isin("US0373831005"), false);
assert_eq!(validate_isin("U50378331005"), false);
assert_eq!(validate_isin("US03378331005"), false);
assert_eq!(validate_isin("AU0000XVGZA3"), true);
assert_eq!(validate_isin("AU0000VXGZA3"), true);
assert_eq!(validate_isin("FR0000988040"), true);
}
 
fn validate_isin(isin: &str) -> bool {
// Preliminary checks to avoid working on non-ASCII stuff
if !isin.chars().all(|x| x.is_alphanumeric()) || isin.len() != 12 {
return false;
}
if !isin[..2].chars().all(|x| x.is_alphabetic())
|| !isin[2..12].chars().all(|x| x.is_alphanumeric())
|| !isin.chars().last().unwrap().is_numeric()
{
return false;
}
 
// Converts the alphanumeric string in a numeric-only string
let bytes = isin.as_bytes();
 
let s2 = bytes.iter()
.flat_map(|&c| {
if c.is_ascii_digit() {
vec![c]
}
else {
(c + 10 - ('A' as u8)).to_string().into_bytes()
}
}).collect::<Vec<u8>>();
 
let string = std::str::from_utf8(&s2).unwrap();
let number = string.parse::<usize>().unwrap();
 
return compute_luhn(number);
}
</syntaxhighlight>
 
=={{header|SAS}}==
<langsyntaxhighlight lang="sas">data test;
length isin $20 ok $1;
input isin;
Line 1,859 ⟶ 2,972:
FR0000988040
;
run;</langsyntaxhighlight>
=={{header|Tcl}}==
 
=={{header|Scala}}==
<lang Tcl>package require Tcl 8.6 ;# mostly needed for [assert]. Substitute a simpler one or a NOP if required.</lang>
{{Out}}Best seen running in your browser either by [https://scalafiddle.io/sf/D9ax4Js/0 ScalaFiddle (ES aka JavaScript, non JVM)] or [https://scastie.scala-lang.org/yOymYqoPSEeA7K7rjgn65g Scastie (remote JVM)].
<syntaxhighlight lang="scala">object Isin extends App {
val isins = Seq("US0378331005", "US0373831005", "U50378331005",
"US03378331005", "AU0000XVGZA3","AU0000VXGZA3", "FR0000988040")
 
private def ISINtest(isin: String): Boolean = {
A proc like assert is always good to have around. This one tries to report values used in its expression using subst:
val isin0 = isin.trim.toUpperCase
 
def luhnTestS(number: String): Boolean = {
<lang Tcl>proc assert {expr} { ;# for "static" assertions that throw nice errors
 
def luhnTestN(digits: Seq[Int]): Boolean = {
 
def checksum(digits: Seq[Int]): Int = {
digits.reverse.zipWithIndex
.foldLeft(0) {
case (sum, (digit, i)) =>
if (i % 2 == 0) sum + digit
else sum + (digit * 2) / 10 + (digit * 2) % 10
} % 10
}
 
checksum(digits) == 0
}
 
luhnTestN(number.map { c =>
assert(c.isDigit, s"$number has a non-digit error")
c.asDigit
})
}
 
if (!isin0.matches("^[A-Z]{2}[A-Z0-9]{9}\\d$")) false
else {
val sb = new StringBuilder
for (c <- isin0.substring(0, 12)) sb.append(Character.digit(c, 36))
luhnTestS(sb.toString)
}
}
 
isins.foreach(isin => println(f"$isin is ${if (ISINtest(isin)) "" else "not"}%s valid"))
 
}</syntaxhighlight>
 
=={{header|SQL PL}}==
{{works with|Db2 LUW}} version 9.7 or higher.
With SQL PL:
<syntaxhighlight lang="sql pl">
--#SET TERMINATOR @
 
SET SERVEROUTPUT ON @
 
CREATE OR REPLACE FUNCTION VALIDATE_ISIN (
IN IDENTIFIER VARCHAR(12)
) RETURNS SMALLINT
-- ) RETURNS BOOLEAN
BEGIN
DECLARE CHECKSUM_FUNC CHAR(1);
DECLARE CONVERTED VARCHAR(24);
DECLARE I SMALLINT;
DECLARE LENGTH SMALLINT;
DECLARE RET SMALLINT DEFAULT 1;
--DECLARE RET BOOLEAN DEFAULT FALSE;
DECLARE CHAR_AT CHAR(1);
DECLARE INVALID_CHAR CONDITION FOR SQLSTATE 'ISIN1';
 
SET CHAR_AT = SUBSTR(IDENTIFIER, 1, 1);
IF (ASCII(CHAR_AT) < 65 OR 90 < ASCII(CHAR_AT)) THEN
SIGNAL INVALID_CHAR SET MESSAGE_TEXT = 'Country code with invalid characters';
END IF;
SET CHAR_AT = SUBSTR(IDENTIFIER, 2, 1);
IF (ASCII(CHAR_AT) < 65 OR 90 < ASCII(CHAR_AT)) THEN
SIGNAL INVALID_CHAR SET MESSAGE_TEXT = 'Country code with invalid characters';
END IF;
 
-- Convert letters to numbers.
SET I = 1;
SET CONVERTED = '';
SET LENGTH = LENGTH(IDENTIFIER);
WHILE (I <= LENGTH) DO
SET CHAR_AT = SUBSTR(IDENTIFIER, I, 1);
IF (48 <= ASCII(CHAR_AT) AND ASCII(CHAR_AT) <= 57) THEN
SET CONVERTED = CONVERTED || CHAR_AT;
ELSE
SET CONVERTED = CONVERTED || (ASCII(CHAR_AT) - 55);
END IF;
SET I = I + 1;
END WHILE;
 
CALL DBMS_OUTPUT.PUT_LINE(CONVERTED);
-- This function is implemented in Rosetta code.
SET CHECKSUM_FUNC = LUHN_TEST(CONVERTED);
IF (CHECKSUM_FUNC = 0) THEN
SET RET = 0;
--SET RET = TRUE;
END IF;
 
RETURN RET;
END @
</syntaxhighlight>
Output:
<pre>
db2 -td@
db2 => BEGIN
...
db2 (cont.) => END @
DB20000I The SQL command completed successfully.
db2 => VALUES VALIDATE_ISIN('US0378331005')@
1
------
0
 
1 record(s) selected.
 
30280378331005
It is a valid number 27+23=50
db2 => VALUES VALIDATE_ISIN('US0373831005')@
1
------
1
 
1 record(s) selected.
 
30280373831005
It is NOT a valid number 22+24=46
db2 => VALUES VALIDATE_ISIN('U50378331005')@
1
------
SQL0438N Application raised error or warning with diagnostic text: "Country
code with invalid characters". SQLSTATE=ISIN1
db2 => VALUES VALIDATE_ISIN('U503378331005')@
1
------
SQL0433N Value "U503378331005" is too long. SQLSTATE=22001
db2 => VALUES VALIDATE_ISIN('AU0000XVGZA3')@
1
------
0
 
1 record(s) selected.
 
1030000033311635103
It is a valid number 18+12=30
db2 => VALUES VALIDATE_ISIN('AU0000VXGZA3')@
1
------
0
 
1 record(s) selected.
 
1030000031331635103
It is a valid number 18+12=30
db2 => VALUES VALIDATE_ISIN('FR0000988040')@
 
1
------
0
 
1 record(s) selected.
 
15270000988040
It is a valid number 20+30=50
</pre>
 
=={{header|Tcl}}==
<syntaxhighlight lang="tcl">package require Tcl 8.6 ;# mostly needed for [assert]. Substitute a simpler one or a NOP if required.</syntaxhighlight>
A proc like assert is always good to have around. This one tries to report values used in its expression using subst:
<syntaxhighlight lang="tcl">proc assert {expr} { ;# for "static" assertions that throw nice errors
if {![uplevel 1 [list expr $expr]]} {
set msg "{$expr}"
Line 1,872 ⟶ 3,147:
tailcall throw {ASSERT ERROR} $msg
}
}</langsyntaxhighlight>
 
isin itself is a simple package. We compute the alphabet when the package is loaded in _init, because that's more fun than typing out the table:
<syntaxhighlight lang="tcl">namespace eval isin {
 
<lang Tcl>namespace eval isin {
proc _init {} { ;# sets up the map used on every call
variable map
Line 1,912 ⟶ 3,185:
}
 
}</langsyntaxhighlight>
 
To run the test suite, we use the tcltest framework included with Tcl:
<syntaxhighlight lang="tcl">package require tcltest
 
<lang Tcl>package require tcltest
 
tcltest::test isin-1 "Test isin validation" -body {
Line 1,935 ⟶ 3,206:
}
return ok
} -result ok</langsyntaxhighlight>
 
=={{header|Transact-SQL}}==
 
<syntaxhighlight lang="transact-sql">
<lang Transact-SQL>
CREATE FUNCTION dbo._ISINCheck( @strISIN VarChar(40) )
RETURNS bit
Line 1,981 ⟶ 3,252:
RETURN @bValid
END
</syntaxhighlight>
</lang>
Testing
<syntaxhighlight lang="transact-sql">
<lang Transact-SQL>
-- Testing. The following tests all pass.
;WITH ISIN_Tests AS
Line 1,997 ⟶ 3,268:
)
SELECT ISIN, Expected, dbo._ISINCheck(ISIN) AS TestResult FROM ISIN_Tests ORDER BY ISIN
</syntaxhighlight>
</lang>
 
=={{header|Visual BasicVBScript}}==
<syntaxhighlight lang="vb">' Validate International Securities Identification Number - 03/03/2019
 
buf=buf&test("US0378331005")&vbCrLf
{{update|Visual Basic|Use the new test-cases, and consider calling the existing Luhn algorithm implementation from the ''[[Luhn test of credit card numbers]]'' task instead of duplicating it.}}
buf=buf&test("US0373831005")&vbCrLf
buf=buf&test("U50378331005")&vbCrLf
buf=buf&test("US03378331005")&vbCrLf
buf=buf&test("AU0000XVGZA3")&vbCrLf
buf=buf&test("AU0000VXGZA3")&vbCrLf
buf=buf&test("FR0000988040")&vbCrLf
msgbox buf,,"Validate International Securities Identification Number"
 
function test(cc)
{{works with|VB6}}
dim err,c,r,s,i1,i2
<lang vb>
if len(cc)=12 then
Option Explicit
for i=1 to len(cc)
p=instr("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ",mid(cc,i,1))
if p<>0 then c=c&(p-1) else err=1
next 'i
for i=1 to 2
if instr("ABCDEFGHIJKLMNOPQRSTUVWXYZ",mid(cc,i,1))=0 then err=1
next 'i
if err=0 then
for i=len(c) to 1 step -1
r=r&mid(c,i,1)
next 'i
for i=1 to len(r) step 2
i1=i1+cint(mid(r,i,1))
next 'i
for i=2 to len(r) step 2
ii=cint(mid(r,i,1))*2
if ii>=10 then ii=ii-9
i2=i2+ii
next 'i
s=cstr(i1+i2)
if mid(s,len(s),1)="0" then
msg="valid"
else
msg="invalid ??1"
end if
else
msg="invalid ??2"
end if
else
msg="invalid ??3"
end if
test=cc&" "&msg
end function 'test </syntaxhighlight>
{{out}}
<pre>
US0378331005 valid
US0373831005 invalid ??1
U50378331005 invalid ??2
US03378331005 invalid ??3
AU0000XVGZA3 valid
AU0000VXGZA3 valid
FR0000988040 valid
</pre>
 
=={{header|Visual Basic}}==
Function MakeIsinCode(Exchange As String, security As String)
{{works with|Visual Basic|VB6 Standard}}
Dim numLeadingZeroes As Integer
Calls LuhnCheckPassed() function described at [[Luhn_test_of_credit_card_numbers#Visual_Basic]]
<syntaxhighlight lang="vb">Function IsValidISIN(ByVal ISIN As String) As Boolean
numLeadingZeroes = 9 - Len(security)
Dim s As String, c As String
Dim leaderi As StringLong
If Len(ISIN) = 12 Then
For i = 1 To Len(ISIN)
leader = Exchange & String(numLeadingZeroes, "0") & security
c = UCase$(Mid(ISIN, i, 1))
Select Case c
MakeIsinCode = leader & CStr(IsinCheckDigit(leader))
Case "A" To "Z"
End Function
If i = 12 Then Exit Function
s = s & CStr(Asc(c) - 55)
Case "0" To "9"
If i < 3 Then Exit Function
s = s & c
Case Else
Exit Function
End Select
Next i
IsValidISIN = LuhnCheckPassed(s)
End If
End Function</syntaxhighlight>
Test:
<syntaxhighlight lang="vb">Sub Main()
Debug.Assert IsValidISIN("US0378331005")
Debug.Assert Not IsValidISIN("US0373831005")
Debug.Assert Not IsValidISIN("U50378331005")
Debug.Assert Not IsValidISIN("US03378331005")
Debug.Assert IsValidISIN("AU0000XVGZA3")
Debug.Assert IsValidISIN("AU0000VXGZA3")
Debug.Assert IsValidISIN("FR0000988040")
Debug.Assert Not IsValidISIN("FR000098804O")
End Sub</syntaxhighlight>
 
=={{header|Visual Basic .NET}}==
Function IsinCheckDigit(ByVal security As String) As Integer
{{trans|C#}}
Dim digits As String
<syntaxhighlight lang="vbnet">Option Strict On
Imports System.Text.RegularExpressions
Dim i As Integer
 
Module Module1
For i = 1 To Len(security)
ReadOnly IsinRegex As New Regex("^[A-Z]{2}[A-Z0-9]{9}\d$", RegexOptions.Compiled)
Dim ch As String
 
Function DigitValue(c As Char) As Integer
ch = UCase(Mid(security, i, 1))
Dim temp As Integer
If chAsc(c) >= Asc("A0"c) AndAndAlso chAsc(c) <= Asc("Z9"c) Then
'temp A= toAsc(c) Z translated to "10", "11", ..- Asc("350"c)
digits = digits & CStr(Asc(ch) - 55)
ElseIf ch >= "0" And ch <= "9" Then
digits = digits & ch
Else
Err.Raisetemp 50001,= ,Asc(c) - Asc("SecurityA"c) must+ contain only letters and digits"10
End If
Next Return temp
End Function
 
Dim total As Integer
Function LuhnTest(number As String) As Boolean
Dim tmp As Integer
Return number.Select(Function(c, i) (AscW(c) - 48) << ((number.Length - i - 1) And 1)).Sum(Function(n) If(n > 9, n - 9, n)) Mod 10 = 0
totalEnd = 0Function
 
Function Digitize(isin As String) As String
'If rightmost even, "other" digits for doubling are 2,4,6. If rightmost odd, they're 1,3,5.
Return String.Join("", isin.Select(Function(c) $"{DigitValue(c)}"))
'rightmost digit is always doubled, so start with it and work backwards
DimEnd other As BooleanFunction
 
other = True
Function IsValidIsin(isin As String) As Boolean
Return IsinRegex.IsMatch(isin) AndAlso LuhnTest(Digitize(isin))
For i = Len(digits) To 1 Step -1
End Function
tmp = CInt(Mid(digits, i, 1))
 
Sub If other ThenMain()
Dim isins() = If tmp < 5 Then{
' 0 to 4 map to 0"US0378331005",2,4,6,8
total = total + (tmp * 2)"US0373831005",
"U50378331005",
"US03378331005",
"AU0000XVGZA3",
"AU0000VXGZA3",
"FR0000988040"
}
 
For Each isin In isins
If IsValidIsin(isin) Then
Console.WriteLine("{0} is valid", isin)
Else
'Console.WriteLine("{0} 5is tonot 9valid", map to 1,3,5,7,9isin)
total = total + ((tmp * 2) - 9)
End If
ElseNext
End Sub
total = total + tmp
 
End If
End Module</syntaxhighlight>
{{out}}
'Toggle doubling flag
<pre>US0378331005 is valid
other = Not other
US0373831005 is not valid
U50378331005 is not valid
US03378331005 is not valid
AU0000XVGZA3 is valid
AU0000VXGZA3 is valid
FR0000988040 is valid</pre>
 
=={{header|V (Vlang)}}==
{{trans|go}}
<syntaxhighlight lang="v (vlang)">import regex
 
const (
inc = [
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[0, 2, 4, 6, 8, 1, 3, 5, 7, 9],
]
)
fn valid_isin(n string) bool {
mut r,_,_ := regex.regex_base('^[A-Z]{2}[A-Z0-9]{9}\d$')
if !r.matches_string(n) {
return false
}
mut sum := 0
mut p := 0
for i := 10; i >= 0; i-- {
p = 1 - p
mut d := n[i..i+1].int()
if d < 'A'.int() {
sum += inc[p][d-'0'.int()]
} else {
d -= 'A'.int()
sum += inc[p][d%10]
p = 1 - p
sum += inc[p][d/10+1]
}
}
sum += n[11..12].int() - '0'.int()
return sum%10 == 0
}
 
struct Testcases {
isin string
valid bool
}
 
fn main(){
testcases := [
Testcases{"US0378331005", true},
Testcases{"US0373831005", false},
Testcases{"U50378331005", false},
Testcases{"US03378331005", false},
Testcases{"AU0000XVGZA3", true},
Testcases{"AU0000VXGZA3", true},
Testcases{"FR0000988040", true},
]
for testcase in testcases {
actual := valid_isin(testcase.isin)
if actual != testcase.valid {
println("expected ${testcase.valid} for ${testcase.isin}, got $actual")
}
}
}</syntaxhighlight>
{{out}}
<pre>expected true for US0378331005, got false
expected true for AU0000XVGZA3, got false
expected true for AU0000VXGZA3, got false
expected true for FR0000988040, got false
</pre>
 
=={{header|Wren}}==
{{libheader|Wren-str}}
{{libheader|Wren-iterate}}
{{libheader|Wren-fmt}}
The Luhn test method is reproduced here for convenience.
<syntaxhighlight lang="wren">import "./str" for Char
import "./iterate" for Stepped
import "./fmt" for Conv, Fmt
 
var luhn = Fn.new { |s|
s = s[-1..0]
var s1 = Stepped.new(s, 2).reduce(0) { |sum, d| sum + d.bytes[0] - 48 }
var s2 = Stepped.new(s[1..-1], 2).reduce(0) { |sum, d|
var d2 = (d.bytes[0] - 48) * 2
return sum + ((d2 > 9) ? d2%10 + 1 : d2)
}
return (s1 + s2)%10 == 0
}
 
var isin = Fn.new { |s|
if (!(s is String && s.count == 12)) return false
for (i in 0..11) {
var c = s[i]
if (i <= 1) {
if (!Char.isUpper(c)) return false
} else if (i >= 2 && i <= 10) {
if (!Char.isUpper(c) && !Char.isDigit(c)) return false
} else {
if (!Char.isDigit(c)) return false
}
}
var dec = ""
for (i in 0...s.count) dec = dec + "%(Conv.atoi(s[i], 36))"
return luhn.call(dec)
}
 
var tests = [
"US0378331005", "US0373831005", "U50378331005", "US03378331005",
"AU0000XVGZA3", "AU0000VXGZA3", "FR0000988040"
]
 
for (test in tests) {
var ans = (isin.call(test)) ? "valid" : "not valid"
System.print("%(Fmt.s(-13, test)) -> %(ans)")
}</syntaxhighlight>
 
{{out}}
<pre>
US0378331005 -> valid
US0373831005 -> not valid
U50378331005 -> not valid
US03378331005 -> not valid
AU0000XVGZA3 -> valid
AU0000VXGZA3 -> valid
FR0000988040 -> valid
</pre>
 
=={{header|XPL0}}==
<syntaxhighlight lang="xpl0">string 0; \use zero-terminated strings
 
func Luhn(Str); \Return 'true' if digits in Str pass Luhn test
char Str;
int Len, Sum, I, Dig;
[Len:= 0; \find length of Str
while Str(Len) do Len:= Len+1;
Sum:= 0; \sum even and odd digits
for I:= 0 to Len-1 do \(no need to reverse)
[if (I xor Len) & 1 then
Sum:= Sum + Str(I) - ^0
else [Dig:= Str(I) - ^0;
Dig:= Dig*2;
Sum:= Sum + Dig/10 + rem(0);
];
];
return rem(Sum/10) = 0;
]; \Luhn
 
func Valid(Str); \Return 'true' if valid ISIN code
char Str, Str2(100);
int Sum, I, J, C, V;
[J:= 0;
for I:= 0 to 12-1 do \convert letters in Str to digits in Str2
[C:= Str(I);
case of
C>=^0 & C<=^9: [Str2(J):= C; J:= J+1];
C>=^A & C<=^Z: [Str2(J):= (C-^A+10)/10 + ^0; J:= J+1;
Str2(J):= rem(0) + ^0; J:= J+1]
other return false;
if I=1 & J#4 then return false; \first two chars not letters
];
if Str(I) # 0 then return false; \too long
Str2(J):= 0; \terminate string
return Luhn(Str2);
]; \Valid
 
int ISIN, N;
[ISIN:= ["US0378331005",
"US0373831005",
"U50378331005",
"US03378331005",
"AU0000XVGZA3",
"AU0000VXGZA3",
"FR0000988040"];
for N:= 0 to 7-1 do
[Text(0, ISIN(N));
Text(0, if Valid(ISIN(N))
then " is valid"
else " is not valid");
CrLf(0);
];
]</syntaxhighlight>
 
{{out}}
<pre>
US0378331005 is valid
US0373831005 is not valid
U50378331005 is not valid
US03378331005 is not valid
AU0000XVGZA3 is valid
AU0000VXGZA3 is valid
FR0000988040 is valid
</pre>
 
=={{header|Yabasic}}==
{{trans|FreeBASIC}}
<syntaxhighlight lang="yabasic">sub luhntest(cardnr$)
local i, j, s1, s2, l
cardnr$ = Trim$(cardnr$) // remove spaces
l = Len(cardnr$)
// sum odd numbers
For i = l To 1 Step -2
s1 = s1 + (asc(mid$(cardnr$, i, 1)) - Asc("0"))
Next
// sum even numbers
For i = l-1 To 1 Step -2
j = asc(mid$(cardnr$, i, 1)) - Asc("0")
j = j * 2
If j > 9 j = mod(j, 10) + 1
s2 = s2 + j
Next
return mod(s1 + s2, 10) = 0
End sub
// ------=< MAIN >=-----
data "US0378331005", "US0373831005", "U50378331005", "US03378331005", "AU0000XVGZA3", "AU0000VXGZA3", "FR0000988040", ""
do
read test_item$
if test_item$ = "" break
l = Len(test_item$)
'Last Mod 10 is to wrap 10 to zero
If l <> 12 Then
IsinCheckDigit = (10 - (total Mod 10)) Mod 10
Print test_item$, " Invalid, length <> 12 char."
End Function
Continue
</lang>
End If
c1$ = mid$(test_item$, 1, 1) : c2$ = mid$(test_item$, 2, 1)
If c1$ < "A" Or c1$ > "Z" or c2$ < "A" or c2$ > "Z" Then
Print test_item$, " Invalid, number needs to start with 2 characters"
Continue
End If
test_str$ = ""
For n = 1 To l
x = asc(mid$(test_item$, n, 1)) - Asc("0")
// if is a letter we to correct for that
If x > 9 x = x - 7
If x < 10 Then
test_str$ = test_str$ + Str$(x)
Else // two digest number
test_str$ = test_str$ + Str$(int(x / 10)) + Str$(mod(x, 10))
End If
Next
Print test_item$;
if luhntest(test_str$) then print " Valid" else print " Invalid, checksum error" end if
loop
</syntaxhighlight>
 
=={{header|zkl}}==
Uses the luhn test from [[Luhn_test_of_credit_card_numbers#zkl]] (copied here as it is short).
<langsyntaxhighlight lang="zkl">fcn validateISIN(isin){
RegExp(String("^","[A-Z]"*2,"[A-Z0-9]"*9,"[0-9]$")).matches(isin) and
luhnTest(isin.split("").apply("toInt",36).concat().toInt())
Line 2,082 ⟶ 3,678:
0 == (n.split().reverse().reduce(fcn(s,n,clk){
s + if(clk.inc()%2) n else 2*n%10 + n/5 },0,Ref(1)) %10)
}</langsyntaxhighlight>
<langsyntaxhighlight lang="zkl">println(" ISIN Valid?");
foreach isin in (T("US0378331005","US0373831005","U50378331005",
"US03378331005","AU0000XVGZA3","AU0000VXGZA3","FR0000988040")){
println(isin," --> ",validateISIN(isin));
}</langsyntaxhighlight>
{{out}}
<pre>
973

edits