Data commit
This commit is contained in:
parent
7387c8f97b
commit
cb5bb5e222
199093 changed files with 3378972 additions and 0 deletions
|
|
@ -0,0 +1,2 @@
|
|||
---
|
||||
from: http://rosettacode.org/wiki/Validate_International_Securities_Identification_Number
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
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:
|
||||
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.
|
||||
|
||||
Demonstrate that your code passes the test-cases listed below.
|
||||
|
||||
|
||||
;Details:
|
||||
The format of an ISIN is as follows:
|
||||
|
||||
<!-- BEGIN DIAGRAM -->
|
||||
<div style="margin:3em; 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> <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>
|
||||
<div><span style="font-size:20px; font-family:'Lucida Console',Monaco,monospace"><span style="color:green; margin:0 -10px 0 10px">│</span> <span style="color:blue; margin:0 -5px 0 10px">│</span> <span style="color:red; margin:0 0 0 5px">┌── </span></span><span style="color:red">a checksum digit (0-9)</span></div>
|
||||
<div style="font-size:20px; font-family:'Lucida Console',Monaco,monospace"><span style="background: #d9ebd9; color:green; border:solid 1px green; margin:0 1px">AU</span><span style="background:#e0e0ff; color:blue; border:solid 1px blue; margin:0 1px">0000XVGZA</span><span style="background:#feefef; color:red; border:solid 1px red; margin:0 1px">3</span></div>
|
||||
</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> →<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 ─── you can just call the existing function from that task. (Add a comment stating if you did this.)
|
||||
|
||||
|
||||
;Test cases:
|
||||
:::: {| class="wikitable"
|
||||
! ISIN
|
||||
! Validity
|
||||
! Comment
|
||||
|-
|
||||
| <tt>US0378331005</tt> || valid ||
|
||||
|-
|
||||
| <tt>US0373831005</tt> || not valid || The transposition typo is caught by the checksum constraint.
|
||||
|-
|
||||
| <tt>U50378331005</tt> || not valid || The substitution typo is caught by the format constraint.
|
||||
|-
|
||||
| <tt>US03378331005</tt> || not valid || The duplication typo is caught by the format constraint.
|
||||
|-
|
||||
| <tt>AU0000XVGZA3</tt> || valid ||
|
||||
|-
|
||||
| <tt>AU0000VXGZA3</tt> || valid || Unfortunately, not ''all'' transposition typos are caught by the checksum constraint.
|
||||
|-
|
||||
| <tt>FR0000988040</tt> || valid ||
|
||||
|}
|
||||
|
||||
(The comments are just informational. Your function should simply return a Boolean result. See [[#Raku]] for a reference solution.)
|
||||
|
||||
|
||||
Related task:
|
||||
* [[Luhn test of credit card numbers]]
|
||||
|
||||
|
||||
;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>
|
||||
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
F check_isin(a)
|
||||
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)))
|
||||
|
|
@ -0,0 +1,176 @@
|
|||
* 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
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
begin
|
||||
% external procedure that returns true if ccNumber passes the Luhn test, false otherwise %
|
||||
logical procedure LuhnTest ( string(32) value ccNumber
|
||||
; integer value ccLength
|
||||
) ; algol "LUHN" ;
|
||||
|
||||
% returns true if isin is a valid ISIN, false otherwise %
|
||||
logical procedure isIsin ( string(32) value isin ) ;
|
||||
if isin( 12 // 20 ) not = "" then false % code is too long %
|
||||
else begin
|
||||
% the first two characters must be upper-case letters %
|
||||
|
||||
% returns the digit corresponding to a character of an ISIN %
|
||||
integer procedure isinDigit ( string(1) value iChar ) ;
|
||||
if iChar >= "0" and iChar <= "9" then ( decode( iChar ) - decode( "0" ) )
|
||||
else if iChar >= "A" and iChar <= "Z" then ( decode( iChar ) - decode( "A" ) ) + 10
|
||||
else begin % invalid digit %
|
||||
isValid := false;
|
||||
-1
|
||||
end isinDigit ;
|
||||
|
||||
integer d1, d2;
|
||||
logical isValid;
|
||||
isValid := true;
|
||||
d1 := isinDigit( isin( 0 // 1 ) );
|
||||
d2 := isinDigit( isin( 1 // 1 ) );
|
||||
if d1 < 10 or d1 > 35 or d2 < 10 or d2 > 35 then false % invalid first two characters %
|
||||
else begin
|
||||
% ok so far - conveet from base 36 to base 10 %
|
||||
string(24) base10Isin;
|
||||
integer b10Pos;
|
||||
base10Isin := "";
|
||||
b10Pos := 0;
|
||||
for cPos := 0 until 10 do begin
|
||||
integer digit;
|
||||
digit := isinDigit( isin( cPos // 1 ) );
|
||||
if isValid then begin
|
||||
% valid digit %
|
||||
if digit > 9 then begin
|
||||
base10Isin( b10Pos // 1 ) := code( ( digit div 10 ) + decode( "0" ) );
|
||||
b10Pos := b10Pos + 1;
|
||||
end if_digit_gt_9 ;
|
||||
base10Isin( b10Pos // 1 ) := code( ( digit rem 10 ) + decode( "0" ) );
|
||||
b10Pos := b10Pos + 1
|
||||
end if_isValid
|
||||
end for_cPos ;
|
||||
% add the check digit as is %
|
||||
base10Isin( b10Pos // 1 ) := isin( 11 // 1 );
|
||||
isValid and LuhnTest( base10Isin, b10Pos + 1 )
|
||||
end
|
||||
end isIsin ;
|
||||
|
||||
% task test cases %
|
||||
|
||||
procedure testIsIsin ( string(32) value isin
|
||||
; logical value expected
|
||||
) ;
|
||||
begin
|
||||
logical isValid;
|
||||
isValid := isIsin( isin );
|
||||
write( s_w := 0
|
||||
, isin
|
||||
, if isValid then " is valid" else " is invalid"
|
||||
, if isValid = expected then "" else " NOT as expected ??"
|
||||
)
|
||||
end testIsin ;
|
||||
|
||||
testIsIsin( "US0378331005", true );
|
||||
testIsIsin( "US0373831005", false );
|
||||
testIsIsin( "U50378331005", false );
|
||||
testIsIsin( "US03378331005", false );
|
||||
testIsIsin( "AU0000XVGZA3", true );
|
||||
testIsIsin( "AU0000VXGZA3", true );
|
||||
testIsIsin( "FR0000988040", true );
|
||||
end.
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
# 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)
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
procedure ISIN is
|
||||
-- Luhn_Test copied from other Task
|
||||
function Luhn_Test (Number: String) return Boolean is
|
||||
Sum : Natural := 0;
|
||||
Odd : Boolean := True;
|
||||
Digit: Natural range 0 .. 9;
|
||||
begin
|
||||
for p in reverse Number'Range loop
|
||||
Digit := Integer'Value (Number (p..p));
|
||||
if Odd then
|
||||
Sum := Sum + Digit;
|
||||
else
|
||||
Sum := Sum + (Digit*2 mod 10) + (Digit / 5);
|
||||
end if;
|
||||
Odd := not Odd;
|
||||
end loop;
|
||||
return (Sum mod 10) = 0;
|
||||
end Luhn_Test;
|
||||
|
||||
subtype Decimal is Character range '0' .. '9';
|
||||
subtype Letter is Character range 'A' .. 'Z';
|
||||
subtype ISIN_Type is String(1..12);
|
||||
|
||||
-- converts a string of decimals and letters into a string of decimals
|
||||
function To_Digits(S: String) return String is
|
||||
-- Character'Pos('A')-Offset=10, Character'Pos('B')-Offset=11, ...
|
||||
Offset: constant Integer := Character'Pos('A')-10;
|
||||
|
||||
Invalid_Character: exception;
|
||||
begin
|
||||
if S = "" then
|
||||
return "";
|
||||
elsif S(S'First) = ' ' then -- skip blanks
|
||||
return To_Digits(S(S'First+1 .. S'Last));
|
||||
elsif S(S'First) in Decimal then
|
||||
return S(S'First) & To_Digits(S(S'First+1 .. S'Last));
|
||||
elsif S(S'First) in Letter then
|
||||
return To_Digits(Integer'Image(Character'Pos(S(S'First))-Offset))
|
||||
& To_Digits(S(S'First+1 .. S'Last));
|
||||
else
|
||||
raise Invalid_Character;
|
||||
end if;
|
||||
end To_Digits;
|
||||
|
||||
function Is_Valid_ISIN(S: ISIN_Type) return Boolean is
|
||||
Number : String := To_Digits(S);
|
||||
begin
|
||||
return S(S'First) in Letter and
|
||||
S(S'First+1) in Letter and
|
||||
S(S'Last) in Decimal and
|
||||
Luhn_Test(Number);
|
||||
end Is_Valid_ISIN;
|
||||
|
||||
Test_Cases : constant Array(1..6) of ISIN_Type :=
|
||||
("US0378331005",
|
||||
"US0373831005",
|
||||
"U50378331005",
|
||||
-- excluded by type with fixed length
|
||||
-- "US03378331005",
|
||||
"AU0000XVGZA3",
|
||||
"AU0000VXGZA3",
|
||||
"FR0000988040");
|
||||
begin
|
||||
for I in Test_Cases'Range loop
|
||||
Ada.Text_IO.Put_Line(Test_Cases(I) & ":" &
|
||||
Boolean'Image(Is_Valid_ISIN(Test_Cases(I))));
|
||||
end loop;
|
||||
-- using wrong length will result in an exception:
|
||||
Ada.Text_IO.Put("US03378331005:");
|
||||
Ada.Text_IO.Put_Line(Boolean'Image(Is_Valid_Isin("US03378331005")));
|
||||
exception
|
||||
when others =>
|
||||
Ada.Text_IO.Put_Line("Exception occured");
|
||||
end ISIN;
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
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
|
||||
|
|
@ -0,0 +1 @@
|
|||
{{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}}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
#include <string>
|
||||
#include <regex>
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
#include <sstream>
|
||||
|
||||
bool CheckFormat(const std::string& isin)
|
||||
{
|
||||
std::regex isinRegEpx(R"([A-Z]{2}[A-Z0-9]{9}[0-9])");
|
||||
std::smatch match;
|
||||
return std::regex_match(isin, match, isinRegEpx);
|
||||
}
|
||||
|
||||
std::string CodeISIN(const std::string& isin)
|
||||
{
|
||||
std::string coded;
|
||||
int offset = 'A' - 10;
|
||||
for (auto ch : isin)
|
||||
{
|
||||
if (ch >= 'A' && ch <= 'Z')
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << static_cast<int>(ch) - offset;
|
||||
coded += ss.str();
|
||||
}
|
||||
else
|
||||
{
|
||||
coded.push_back(ch);
|
||||
}
|
||||
}
|
||||
|
||||
return std::move(coded);
|
||||
}
|
||||
|
||||
bool CkeckISIN(const std::string& isin)
|
||||
{
|
||||
if (!CheckFormat(isin))
|
||||
return false;
|
||||
|
||||
std::string coded = CodeISIN(isin);
|
||||
// from http://rosettacode.org/wiki/Luhn_test_of_credit_card_numbers#C.2B.2B11
|
||||
return luhn(coded);
|
||||
}
|
||||
|
||||
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
|
||||
int main()
|
||||
{
|
||||
std::string isins[] = { "US0378331005", "US0373831005", "U50378331005",
|
||||
"US03378331005", "AU0000XVGZA3", "AU0000VXGZA3",
|
||||
"FR0000988040" };
|
||||
for (const auto& isin : isins)
|
||||
{
|
||||
std::cout << isin << std::boolalpha << " - " << CkeckISIN(isin) <<std::endl;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
#include <stdio.h>
|
||||
|
||||
int check_isin(char *a) {
|
||||
int i, j, k, v, s[24];
|
||||
|
||||
j = 0;
|
||||
for(i = 0; i < 12; i++) {
|
||||
k = a[i];
|
||||
if(k >= '0' && k <= '9') {
|
||||
if(i < 2) return 0;
|
||||
s[j++] = k - '0';
|
||||
} else if(k >= 'A' && k <= 'Z') {
|
||||
if(i == 11) return 0;
|
||||
k -= 'A' - 10;
|
||||
s[j++] = k / 10;
|
||||
s[j++] = k % 10;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(a[i]) return 0;
|
||||
|
||||
v = 0;
|
||||
for(i = j - 2; i >= 0; i -= 2) {
|
||||
k = 2 * s[i];
|
||||
v += k > 9 ? k - 9 : k;
|
||||
}
|
||||
|
||||
for(i = j - 1; i >= 0; i -= 2) {
|
||||
v += s[i];
|
||||
}
|
||||
|
||||
return v % 10 == 0;
|
||||
}
|
||||
|
||||
int main() {
|
||||
char *test[7] = {"US0378331005", "US0373831005", "U50378331005",
|
||||
"US03378331005", "AU0000XVGZA3", "AU0000VXGZA3",
|
||||
"FR0000988040"};
|
||||
int i;
|
||||
for(i = 0; i < 7; i++) printf("%c%c", check_isin(test[i]) ? 'T' : 'F', i == 6 ? '\n' : ' ');
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* will print: T F F F T T T */
|
||||
|
|
@ -0,0 +1,173 @@
|
|||
>>SOURCE FORMAT FREE
|
||||
*> this is gnucobol 2.0
|
||||
identification division.
|
||||
program-id. callISINtest.
|
||||
data division.
|
||||
working-storage section.
|
||||
01 ISINtest-result binary-int.
|
||||
procedure division.
|
||||
start-callISINtest.
|
||||
display 'should be valid ' with no advancing
|
||||
call 'ISINtest' using 'US0378331005' ISINtest-result
|
||||
perform display-ISINtest-result
|
||||
display 'should not be valid ' with no advancing
|
||||
call 'ISINtest' using 'US0373831005' ISINtest-result
|
||||
perform display-ISINtest-result
|
||||
display 'should not be valid ' with no advancing
|
||||
call 'ISINtest' using 'U50378331005' ISINtest-result
|
||||
perform display-ISINtest-result
|
||||
display 'should not be valid ' with no advancing
|
||||
call 'ISINtest' using 'US03378331005' ISINtest-result
|
||||
perform display-ISINtest-result
|
||||
display 'should be valid ' with no advancing
|
||||
call 'ISINtest' using 'AU0000XVGZA3' ISINtest-result
|
||||
perform display-ISINtest-result
|
||||
display 'should be valid ' with no advancing
|
||||
call 'ISINtest' using 'AU0000VXGZA3' ISINtest-result
|
||||
perform display-ISINtest-result
|
||||
display 'should be valid ' with no advancing
|
||||
call 'ISINtest' using 'FR0000988040' ISINtest-result
|
||||
perform display-ISINtest-result
|
||||
stop run
|
||||
.
|
||||
display-ISINtest-result.
|
||||
evaluate ISINtest-result
|
||||
when 0
|
||||
display ' is valid'
|
||||
when -1
|
||||
display ' invalid length '
|
||||
when -2
|
||||
display ' invalid countrycode '
|
||||
when -3
|
||||
display ' invalid base36 digit '
|
||||
when -4
|
||||
display ' luhn test failed'
|
||||
when other
|
||||
display ' invalid return code ' ISINtest-result
|
||||
end-evaluate
|
||||
.
|
||||
end program callISINtest.
|
||||
|
||||
identification division.
|
||||
program-id. ISINtest.
|
||||
data division.
|
||||
working-storage section.
|
||||
01 country-code-values value
|
||||
'ADAEAFAGAIALAMAOAQARASATAUAWAXAZBABBBDBEBFBGBHBIBJBLBMBNBOBQBRBS'
|
||||
& 'BTBVBWBYBZCACCCDCFCGCHCICKCLCMCNCOCRCUCVCWCXCYCZDEDJDKDMDODZECEE'
|
||||
& 'EGEHERESETFIFJFKFMFOFRGAGBGDGEGFGGGHGIGLGMGNGPGQGRGSGTGUGWGYHKHM'
|
||||
& 'HNHRHTHUIDIEILIMINIOIQIRISITJEJMJOJPKEKGKHKIKMKNKPKRKWKYKZLALBLC'
|
||||
& 'LILKLRLSLTLULVLYMAMCMDMEMFMGMHMKMLMMMNMOMPMQMRMSMTMUMVMWMXMYMZNA'
|
||||
& 'NCNENFNGNINLNONPNRNUNZOMPAPEPFPGPHPKPLPMPNPRPSPTPWPYQARERORSRURW'
|
||||
& 'SASBSCSDSESGSHSISJSKSLSMSNSOSRSSSTSVSXSYSZTCTDTFTGTHTJTKTLTMTNTO'
|
||||
& 'TRTTTVTWTZUAUGUMUSUYUZVAVCVEVGVIVNVUWFWSYEYTZAZMZW'.
|
||||
03 country-codes occurs 249
|
||||
ascending key country-code
|
||||
indexed by cc-idx.
|
||||
05 country-code pic xx.
|
||||
|
||||
01 b pic 99.
|
||||
01 base36-digits pic x(36) value
|
||||
'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'.
|
||||
|
||||
01 i pic 99.
|
||||
01 p pic 99.
|
||||
01 luhn-number pic x(20).
|
||||
01 luhntest-result binary-int.
|
||||
|
||||
linkage section.
|
||||
01 test-number any length.
|
||||
01 ISINtest-result binary-int.
|
||||
|
||||
procedure division using test-number ISINtest-result.
|
||||
start-ISINtest.
|
||||
display space test-number with no advancing
|
||||
|
||||
*> format test
|
||||
if function length(test-number) <> 12
|
||||
move -1 to ISINtest-result
|
||||
goback
|
||||
end-if
|
||||
|
||||
*> countrycode test
|
||||
search all country-codes
|
||||
at end
|
||||
move -2 to ISINtest-result
|
||||
goback
|
||||
when test-number(1:2) = country-code(cc-idx)
|
||||
continue
|
||||
end-search
|
||||
|
||||
*> convert each character from base 36 to base 10
|
||||
*> and add to the luhn-number
|
||||
move 0 to p
|
||||
perform varying i from 1 by 1 until i > 12
|
||||
if test-number(i:1) >= '0' and <= '9'
|
||||
move test-number(i:1) to luhn-number(p + 1:1)
|
||||
add 1 to p
|
||||
else
|
||||
perform varying b from 9 by 1 until b > 35
|
||||
or base36-digits(b + 1:1) = test-number(i:1)
|
||||
continue
|
||||
end-perform
|
||||
if b > 35
|
||||
move -3 to ISINtest-result
|
||||
goback
|
||||
end-if
|
||||
move b to luhn-number(p + 1:2)
|
||||
add 2 to p
|
||||
end-if
|
||||
end-perform
|
||||
|
||||
call 'luhntest' using luhn-number(1:p) luhntest-result
|
||||
if luhntest-result <> 0
|
||||
move -4 to ISINtest-result
|
||||
goback
|
||||
end-if
|
||||
|
||||
move 0 to ISINtest-result
|
||||
goback
|
||||
.
|
||||
end program ISINtest.
|
||||
|
||||
identification division.
|
||||
program-id. luhntest.
|
||||
data division.
|
||||
working-storage section.
|
||||
01 i pic S99.
|
||||
01 check-sum pic 999.
|
||||
linkage section.
|
||||
01 test-number any length.
|
||||
01 luhntest-result binary-int.
|
||||
procedure division using test-number luhntest-result.
|
||||
start-luhntest.
|
||||
display space test-number with no advancing
|
||||
move 0 to check-sum
|
||||
|
||||
*> right to left sum the odd numbered digits
|
||||
compute i = function length(test-number)
|
||||
perform varying i from i by -2 until i < 1
|
||||
add function numval(test-number(i:1)) to check-sum
|
||||
end-perform
|
||||
display space check-sum with no advancing
|
||||
|
||||
*> right to left double sum the even numbered digits
|
||||
compute i = function length(test-number) - 1
|
||||
perform varying i from i by -2 until i < 1
|
||||
add function numval(test-number(i:1)) to check-sum
|
||||
add function numval(test-number(i:1)) to check-sum
|
||||
*> convert a two-digit double sum number to a single digit
|
||||
if test-number(i:1) >= '5'
|
||||
subtract 9 from check-sum
|
||||
end-if
|
||||
end-perform
|
||||
display space check-sum with no advancing
|
||||
|
||||
if function mod(check-sum,10) = 0
|
||||
move 0 to luhntest-result *> success
|
||||
else
|
||||
move -1 to luhntest-result *> failure
|
||||
end-if
|
||||
goback
|
||||
.
|
||||
end program luhntest.
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
(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)))
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
(defun alphap (char)
|
||||
(char<= #\A char #\Z))
|
||||
|
||||
(defun alpha-digit-char-p (char)
|
||||
(or (alphap char) (digit-char-p char)))
|
||||
|
||||
(defun valid-isin-format-p (isin)
|
||||
(and (= (length isin) 12)
|
||||
(alphap (char isin 0))
|
||||
(alphap (char isin 1))
|
||||
(loop for i from 2 to 10
|
||||
always (alpha-digit-char-p (char isin i)))
|
||||
(digit-char-p (char isin 11))))
|
||||
|
||||
(defun isin->digits (isin)
|
||||
(apply #'concatenate 'string
|
||||
(loop for c across isin
|
||||
collect (princ-to-string (digit-char-p c 36)))))
|
||||
|
||||
(defun luhn-test (string)
|
||||
(loop for c across (reverse string)
|
||||
for oddp = t then (not oddp)
|
||||
if oddp
|
||||
sum (digit-char-p c) into result
|
||||
else
|
||||
sum (let ((n (* 2 (digit-char-p c))))
|
||||
(if (> n 9) (- n 9) n))
|
||||
into result
|
||||
finally (return (zerop (mod result 10)))))
|
||||
|
||||
(defun valid-isin-p (isin)
|
||||
(and (valid-isin-format-p isin)
|
||||
(luhn-test (isin->digits isin))))
|
||||
|
||||
(defun test ()
|
||||
(dolist (isin '("US0378331005" "US0373831005" "U50378331005" "US03378331005"
|
||||
"AU0000XVGZA3" "AU0000VXGZA3" "FR0000988040"))
|
||||
(format t "~A: ~:[invalid~;valid~]~%" isin (valid-isin-p isin))))
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
import std.stdio;
|
||||
|
||||
void main() {
|
||||
auto isins = [
|
||||
"US0378331005",
|
||||
"US0373831005",
|
||||
"U50378331005",
|
||||
"US03378331005",
|
||||
"AU0000XVGZA3",
|
||||
"AU0000VXGZA3",
|
||||
"FR0000988040",
|
||||
];
|
||||
foreach (isin; isins) {
|
||||
writeln(isin, " is ", ISINvalidate(isin) ? "valid" : "not valid");
|
||||
}
|
||||
}
|
||||
|
||||
bool ISINvalidate(string isin) {
|
||||
import std.array : appender;
|
||||
import std.conv : to;
|
||||
import std.regex : matchFirst;
|
||||
import std.string : strip, toUpper;
|
||||
|
||||
isin = isin.strip.toUpper;
|
||||
|
||||
if (isin.matchFirst(`^[A-Z]{2}[A-Z0-9]{9}\d$`).empty) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto sb = appender!string;
|
||||
foreach (c; isin[0..12]) {
|
||||
sb.put(
|
||||
[c].to!int(36)
|
||||
.to!string
|
||||
);
|
||||
}
|
||||
|
||||
import luhn;
|
||||
return luhnTest(sb.data);
|
||||
}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
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;
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
isin? = fn str ->
|
||||
if str =~ ~r/\A[A-Z]{2}[A-Z0-9]{9}\d\z/ do
|
||||
String.codepoints(str)
|
||||
|> Enum.map_join(&String.to_integer(&1, 36))
|
||||
|> Luhn.valid?
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
IO.puts " ISIN Valid?"
|
||||
~w(US0378331005
|
||||
US0373831005
|
||||
U50378331005
|
||||
US03378331005
|
||||
AU0000XVGZA3
|
||||
AU0000VXGZA3
|
||||
FR0000988040)
|
||||
|> Enum.each(&IO.puts "#{&1}\t#{isin?.(&1)}")
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
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
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
program isin
|
||||
use ctype
|
||||
implicit none
|
||||
character(20) :: test(7) = ["US0378331005 ", &
|
||||
"US0373831005 ", &
|
||||
"U50378331005 ", &
|
||||
"US03378331005 ", &
|
||||
"AU0000XVGZA3 ", &
|
||||
"AU0000VXGZA3 ", &
|
||||
"FR0000988040 "]
|
||||
print *, check_isin(test)
|
||||
contains
|
||||
elemental logical function check_isin(a)
|
||||
character(*), intent(in) :: a
|
||||
integer :: s(24)
|
||||
integer :: i, j, k, n, v
|
||||
|
||||
check_isin = .false.
|
||||
|
||||
n = len_trim(a)
|
||||
if (n /= 12) return
|
||||
|
||||
! Convert to an array of digits
|
||||
j = 0
|
||||
do i = 1, n
|
||||
k = iachar(a(i:i))
|
||||
if (k >= 48 .and. k <= 57) then
|
||||
if (i < 3) return
|
||||
k = k - 48
|
||||
j = j + 1
|
||||
s(j) = k
|
||||
else if (k >= 65 .and. k <= 90) then
|
||||
if (i == 12) return
|
||||
k = k - 65 + 10
|
||||
j = j + 1
|
||||
s(j) = k / 10
|
||||
j = j + 1
|
||||
s(j) = mod(k, 10)
|
||||
else
|
||||
return
|
||||
end if
|
||||
end do
|
||||
|
||||
! Compute checksum
|
||||
v = 0
|
||||
do i = j - 1, 1, -2
|
||||
k = 2 * s(i)
|
||||
if (k > 9) k = k - 9
|
||||
v = v + k
|
||||
end do
|
||||
do i = j, 1, -2
|
||||
v = v + s(i)
|
||||
end do
|
||||
|
||||
check_isin = 0 == mod(v, 10)
|
||||
end function
|
||||
end program
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
' version 27-10-2016
|
||||
' compile with: fbc -s console
|
||||
|
||||
#Ifndef TRUE ' define true and false for older freebasic versions
|
||||
#Define FALSE 0
|
||||
#Define TRUE Not FALSE
|
||||
#EndIf
|
||||
|
||||
Function luhntest(cardnr As String) As Long
|
||||
|
||||
cardnr = Trim(cardnr) ' remove spaces
|
||||
|
||||
Dim As String reverse_nr = cardnr
|
||||
Dim As Long i, j, s1, s2, l = Len(cardnr) -1
|
||||
|
||||
' reverse string
|
||||
For i = 0 To l
|
||||
reverse_nr[i] = cardnr[l - i]
|
||||
Next
|
||||
' sum odd numbers
|
||||
For i = 0 To l Step 2
|
||||
s1 = s1 + (reverse_nr[i] - Asc("0"))
|
||||
Next
|
||||
' sum even numbers
|
||||
For i = 1 To l Step 2
|
||||
j = reverse_nr[i] - Asc("0")
|
||||
j = j * 2
|
||||
If j > 9 Then j = j Mod 10 +1
|
||||
s2 = s2 + j
|
||||
Next
|
||||
|
||||
If (s1 + s2) Mod 10 = 0 Then
|
||||
Return TRUE
|
||||
Else
|
||||
Return FALSE
|
||||
End If
|
||||
|
||||
End Function
|
||||
|
||||
' ------=< MAIN >=-----
|
||||
|
||||
Dim As String test_str
|
||||
Dim As String test_set(1 To ...) = { "US0378331005", "US0373831005", _
|
||||
"U50378331005", "US03378331005", "AU0000XVGZA3", _
|
||||
"AU0000VXGZA3", "FR0000988040" }
|
||||
|
||||
Dim As Long i, l, n, x
|
||||
|
||||
For i = 1 To UBound(test_set)
|
||||
test_str = ""
|
||||
l = Len(test_set(i))
|
||||
If l <> 12 Then
|
||||
Print test_set(i), "Invalid, length <> 12 char."
|
||||
Continue For
|
||||
End If
|
||||
If test_set(i)[0] < Asc("A") Or test_set(i)[1] < Asc("A") Then
|
||||
Print test_set(i), "Invalid, number needs to start with 2 characters"
|
||||
Continue For
|
||||
End If
|
||||
For n = 0 To l -1
|
||||
x = test_set(i)[n] - Asc("0")
|
||||
' if test_set(i)[i] is a letter we to correct for that
|
||||
If x > 9 Then x = x -7
|
||||
If x < 10 Then
|
||||
test_str = test_str + Str(x)
|
||||
Else ' two digest number
|
||||
test_str = test_str + Str(x \ 10) + Str(x Mod 10)
|
||||
End If
|
||||
Next
|
||||
Print test_set(i), IIf(luhntest(test_str) = TRUE, "Valid","Invalid, checksum error")
|
||||
Next
|
||||
|
||||
|
||||
' empty keyboard buffer
|
||||
While InKey <> "" : Wend
|
||||
Print : Print "hit any key to end program"
|
||||
Sleep
|
||||
End
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package main
|
||||
|
||||
import "regexp"
|
||||
|
||||
var r = regexp.MustCompile(`^[A-Z]{2}[A-Z0-9]{9}\d$`)
|
||||
|
||||
var inc = [2][10]int{
|
||||
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
|
||||
{0, 2, 4, 6, 8, 1, 3, 5, 7, 9},
|
||||
}
|
||||
|
||||
func ValidISIN(n string) bool {
|
||||
if !r.MatchString(n) {
|
||||
return false
|
||||
}
|
||||
var sum, p int
|
||||
for i := 10; i >= 0; i-- {
|
||||
p = 1 - p
|
||||
if d := n[i]; d < 'A' {
|
||||
sum += inc[p][d-'0']
|
||||
} else {
|
||||
d -= 'A'
|
||||
sum += inc[p][d%10]
|
||||
p = 1 - p
|
||||
sum += inc[p][d/10+1]
|
||||
}
|
||||
}
|
||||
sum += int(n[11] - '0')
|
||||
return sum%10 == 0
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package main
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestValidISIN(t *testing.T) {
|
||||
testcases := []struct {
|
||||
isin string
|
||||
valid bool
|
||||
}{
|
||||
{"US0378331005", true},
|
||||
{"US0373831005", false},
|
||||
{"U50378331005", false},
|
||||
{"US03378331005", false},
|
||||
{"AU0000XVGZA3", true},
|
||||
{"AU0000VXGZA3", true},
|
||||
{"FR0000988040", true},
|
||||
}
|
||||
|
||||
for _, testcase := range testcases {
|
||||
actual := ValidISIN(testcase.isin)
|
||||
if actual != testcase.valid {
|
||||
t.Errorf("expected %v for %q, got %v",
|
||||
testcase.valid, testcase.isin, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||
|
||||
int checksum(String prefix) {
|
||||
def digits = prefix.toUpperCase().collect { CHARS.indexOf(it).toString() }.sum()
|
||||
def groups = digits.collect { CHARS.indexOf(it) }.inject([[], []]) { acc, i -> [acc[1], acc[0] + i] }
|
||||
def ds = groups[1].collect { (2 * it).toString() }.sum().collect { CHARS.indexOf(it) } + groups[0]
|
||||
(10 - ds.sum() % 10) % 10
|
||||
}
|
||||
|
||||
assert checksum('AU0000VXGZA') == 3
|
||||
assert checksum('GB000263494') == 6
|
||||
assert checksum('US037833100') == 5
|
||||
assert checksum('US037833107') == 0
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
module ISINVerification2 where
|
||||
|
||||
import Data.Char (isUpper, isDigit, digitToInt)
|
||||
|
||||
verifyISIN :: String -> Bool
|
||||
verifyISIN isin =
|
||||
correctFormat isin && mod (oddsum + multiplied_even_sum) 10 == 0
|
||||
where
|
||||
reverted = reverse $ convertToNumber isin
|
||||
theOdds = fst $ collectOddandEven reverted
|
||||
theEvens = snd $ collectOddandEven reverted
|
||||
oddsum = sum $ map digitToInt theOdds
|
||||
multiplied_even_sum = addUpDigits $ map ((* 2) . digitToInt) theEvens
|
||||
|
||||
capitalLetters :: String
|
||||
capitalLetters = ['A','B' .. 'Z']
|
||||
|
||||
numbers :: String
|
||||
numbers = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
|
||||
|
||||
correctFormat :: String -> Bool
|
||||
correctFormat isin =
|
||||
(length isin == 12) &&
|
||||
all (`elem` capitalLetters) (take 2 isin) &&
|
||||
all (\c -> elem c capitalLetters || elem c numbers) (drop 2 $ take 11 isin) &&
|
||||
elem (last isin) numbers
|
||||
|
||||
convertToNumber :: String -> String
|
||||
convertToNumber = concatMap convert
|
||||
where
|
||||
convert :: Char -> String
|
||||
convert c =
|
||||
if isDigit c
|
||||
then show $ digitToInt c
|
||||
else show (fromEnum c - 55)
|
||||
|
||||
collectOddandEven :: String -> (String, String)
|
||||
collectOddandEven term
|
||||
| odd $ length term =
|
||||
( concat
|
||||
[ take 1 $ drop n term
|
||||
| n <- [0,2 .. length term - 1] ]
|
||||
, concat
|
||||
[ take 1 $ drop d term
|
||||
| d <- [1,3 .. length term - 2] ])
|
||||
| otherwise =
|
||||
( concat
|
||||
[ take 1 $ drop n term
|
||||
| n <- [0,2 .. length term - 2] ]
|
||||
, concat
|
||||
[ take 1 $ drop d term
|
||||
| d <- [1,3 .. length term - 1] ])
|
||||
|
||||
addUpDigits :: [Int] -> Int
|
||||
addUpDigits list =
|
||||
sum $
|
||||
map
|
||||
(\d ->
|
||||
if d > 9
|
||||
then sum $ map digitToInt $ show d
|
||||
else d)
|
||||
list
|
||||
|
||||
printSolution :: String -> IO ()
|
||||
printSolution str = do
|
||||
putStr $ str ++ " is"
|
||||
if verifyISIN str
|
||||
then putStrLn " valid"
|
||||
else putStrLn " not valid"
|
||||
|
||||
main :: IO ()
|
||||
main = do
|
||||
let isinnumbers =
|
||||
[ "US0378331005"
|
||||
, "US0373831005"
|
||||
, "U50378331005"
|
||||
, "US03378331005"
|
||||
, "AU0000XVGZA3"
|
||||
, "AU0000VXGZA3"
|
||||
, "FR0000988040"
|
||||
]
|
||||
mapM_ printSolution isinnumbers
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
import Control.Monad ((<=<))
|
||||
import Data.Bifunctor (first)
|
||||
import Data.List (foldl') -- '
|
||||
import qualified Data.Map as M
|
||||
import Data.Maybe (fromMaybe)
|
||||
|
||||
-------------------- VALID ISIN STRING -------------------
|
||||
|
||||
validISIN :: String -> Bool
|
||||
validISIN =
|
||||
(&&) . isinPattern
|
||||
<*> luhn . (show <=< stringInts)
|
||||
|
||||
isinPattern :: String -> Bool
|
||||
isinPattern s =
|
||||
12 == length s
|
||||
&& all (`elem` capitals) l
|
||||
&& all (`elem` (capitals <> digits)) m
|
||||
&& head r `elem` digits
|
||||
where
|
||||
[l, m, r] = bites s [2, 9, 1]
|
||||
|
||||
luhn :: String -> Bool
|
||||
luhn x = 0 == rem (s1 + s2) 10
|
||||
where
|
||||
odds = [(: []), const []]
|
||||
evens = reverse odds
|
||||
stream f =
|
||||
concat $
|
||||
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 [] . traverse (`M.lookup` charMap)
|
||||
|
||||
bites :: [a] -> [Int] -> [[a]]
|
||||
bites xs =
|
||||
(reverse . fst)
|
||||
. foldl' -- '
|
||||
(\(a, r) x -> first (: a) (splitAt x r))
|
||||
([], xs)
|
||||
|
||||
capitals, digits :: String
|
||||
capitals = ['A' .. 'Z']
|
||||
digits = ['0' .. '9']
|
||||
|
||||
--------------------------- TEST -------------------------
|
||||
main :: IO ()
|
||||
main =
|
||||
mapM_
|
||||
(print . ((,) <*> validISIN))
|
||||
[ "US0378331005",
|
||||
"US0373831005",
|
||||
"U50378331005",
|
||||
"US03378331005",
|
||||
"AU0000XVGZA3",
|
||||
"AU0000VXGZA3",
|
||||
"FR0000988040"
|
||||
]
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
require'regex'
|
||||
validFmt=: 0 -: '^[A-Z]{2}[A-Z0-9]{9}[0-9]{1}$'&rxindex
|
||||
|
||||
df36=: ;@([: <@":"0 '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'&i.) NB. decimal from base 36
|
||||
luhn=: 0 = 10 (| +/@,) 10 #.inv 1 2 *&|: _2 "."0\ |. NB. as per task Luhn_test_of_credit_card_numbers#J
|
||||
|
||||
validISIN=: validFmt *. luhn@df36
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
Tests=: 'US0378331005';'US0373831005';'U50378331005';'US03378331005';'AU0000XVGZA3';'AU0000VXGZA3';'FR0000988040'
|
||||
validISIN&> Tests
|
||||
1 0 0 0 1 1 1
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
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", 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 luhnTest(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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
# 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);
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
def task:
|
||||
"US0378331005",
|
||||
"US0373831005",
|
||||
"U50378331005",
|
||||
"US03378331005",
|
||||
"AU0000XVGZA3",
|
||||
"AU0000VXGZA3",
|
||||
"FR0000988040"
|
||||
| . + " => " + (if is_ISIN then "valid" else "invalid" end);
|
||||
|
||||
task
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
using Printf
|
||||
|
||||
luhntest(x) = luhntest(parse(Int, x))
|
||||
|
||||
function checkISIN(inum::AbstractString)
|
||||
if length(inum) != 12 || !all(isalpha, inum[1:2]) return false end
|
||||
return parse.(Int, collect(inum), 36) |> join |> luhntest
|
||||
end
|
||||
|
||||
for inum in ["US0378331005", "US0373831005", "U50378331005",
|
||||
"US03378331005", "AU0000XVGZA3", "AU0000VXGZA3", "FR0000988040"]
|
||||
@printf("%-15s %5s\n", inum, ifelse(checkISIN(inum), "pass", "fail"))
|
||||
end
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
// version 1.1
|
||||
|
||||
object Isin {
|
||||
val r = Regex("^[A-Z]{2}[A-Z0-9]{9}[0-9]$")
|
||||
|
||||
fun isValid(s: String): Boolean {
|
||||
// check format
|
||||
if (!s.matches(r)) return false
|
||||
// validate checksum
|
||||
val sb = StringBuilder()
|
||||
for (c in s) {
|
||||
when (c) {
|
||||
in '0'..'9' -> sb.append(c)
|
||||
in 'A'..'Z' -> sb.append((c.toInt() - 55).toString().padStart(2, '0'))
|
||||
}
|
||||
}
|
||||
return luhn(sb.toString())
|
||||
}
|
||||
|
||||
private fun luhn(s: String): Boolean {
|
||||
fun sumDigits(n: Int) = n / 10 + n % 10
|
||||
val t = s.reversed()
|
||||
val s1 = t.filterIndexed { i, _ -> i % 2 == 0 }.sumBy { it - '0' }
|
||||
val s2 = t.filterIndexed { i, _ -> i % 2 == 1 }.map { sumDigits((it - '0') * 2) }.sum()
|
||||
return (s1 + s2) % 10 == 0
|
||||
}
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val isins = arrayOf(
|
||||
"US0378331005", "US0373831005", "U50378331005", "US03378331005",
|
||||
"AU0000XVGZA3", "AU0000VXGZA3", "FR0000988040"
|
||||
)
|
||||
for (isin in isins) {
|
||||
println("$isin\t -> ${if (Isin.isValid(isin)) "valid" else "not valid"}")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
val .luhntest = f(.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]; .t[.numbers[.i]+1])
|
||||
} div 10
|
||||
}
|
||||
|
||||
val .isintest = f(.s) {
|
||||
matching(re/^[A-Z][A-Z][0-9A-Z]{9}[0-9]$/, .s) and
|
||||
.luhntest(join s2n .s)
|
||||
}
|
||||
|
||||
val .tests = h{
|
||||
"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)")
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
function luhn (n)
|
||||
local revStr, s1, s2, digit, mod = n:reverse(), 0, 0
|
||||
for pos = 1, #revStr do
|
||||
digit = tonumber(revStr:sub(pos, pos))
|
||||
if pos % 2 == 1 then
|
||||
s1 = s1 + digit
|
||||
else
|
||||
digit = digit * 2
|
||||
if digit > 9 then
|
||||
mod = digit % 10
|
||||
digit = mod + ((digit - mod) / 10)
|
||||
end
|
||||
s2 = s2 + digit
|
||||
end
|
||||
end
|
||||
return (s1 + s2) % 10 == 0
|
||||
end
|
||||
|
||||
function checkISIN (inStr)
|
||||
if #inStr ~= 12 then return false end
|
||||
local numStr = ""
|
||||
for pos = 1, #inStr do
|
||||
numStr = numStr .. tonumber(inStr:sub(pos, pos), 36)
|
||||
end
|
||||
return luhn(numStr)
|
||||
end
|
||||
|
||||
local testCases = {
|
||||
"US0378331005",
|
||||
"US0373831005",
|
||||
"US0373831005",
|
||||
"US03378331005",
|
||||
"AU0000XVGZA3",
|
||||
"AU0000VXGZA3",
|
||||
"FR0000988040"
|
||||
}
|
||||
for _, ISIN in pairs(testCases) do print(ISIN, checkISIN(ISIN)) end
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
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"}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
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()}."
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
use strict;
|
||||
use English;
|
||||
use POSIX;
|
||||
use Test::Simple tests => 7;
|
||||
|
||||
ok( validate_isin('US0378331005'), 'Test 1');
|
||||
ok( ! validate_isin('US0373831005'), 'Test 2');
|
||||
ok( ! validate_isin('U50378331005'), 'Test 3');
|
||||
ok( ! validate_isin('US03378331005'), 'Test 4');
|
||||
ok( validate_isin('AU0000XVGZA3'), 'Test 5');
|
||||
ok( validate_isin('AU0000VXGZA3'), 'Test 6');
|
||||
ok( validate_isin('FR0000988040'), 'Test 7');
|
||||
exit 0;
|
||||
|
||||
sub validate_isin {
|
||||
my $isin = shift;
|
||||
$isin =~ /\A[A-Z]{2}[A-Z\d]{9}\d\z/s or return 0;
|
||||
my $base10 = join(q{}, map {scalar(POSIX::strtol($ARG, 36))}
|
||||
split(//s, $isin));
|
||||
return luhn_test($base10);
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
(phixonline)-->
|
||||
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
|
||||
<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>
|
||||
|
||||
<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>
|
||||
<span style="color: #000080;font-style:italic;">-- returns 1 if valid, else 0/2/3/4.
|
||||
-- (feel free to return 0 instead of 2/3/4)</span>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<span style="color: #008080;">return</span> <span style="color: #000000;">4</span>
|
||||
<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>
|
||||
<span style="color: #008080;">return</span> <span style="color: #000000;">3</span>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
|
||||
<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>
|
||||
|
||||
<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>
|
||||
<span style="color: #008000;">"US0373831005"</span><span style="color: #0000FF;">,</span> <span style="color: #000080;font-style:italic;">-- not valid The transposition typo is caught by the checksum 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>
|
||||
<span style="color: #008000;">"US03378331005"</span><span style="color: #0000FF;">,</span> <span style="color: #000080;font-style:italic;">-- not valid The duplication typo is caught by the format constraint.</span>
|
||||
<span style="color: #008000;">"AU0000XVGZA3"</span><span style="color: #0000FF;">,</span> <span style="color: #000080;font-style:italic;">-- valid </span>
|
||||
<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>
|
||||
|
||||
<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>
|
||||
|
||||
<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>
|
||||
<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>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
|
||||
<!--
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
(de isin (Str)
|
||||
(let Str (mapcar char (chop Str))
|
||||
(and
|
||||
(= 12 (length Str))
|
||||
(<= 65 (car Str) 90)
|
||||
(<= 65 (cadr Str) 90)
|
||||
(luhn
|
||||
(pack
|
||||
(mapcar
|
||||
'((N)
|
||||
(- N (if (<= 48 N 57) 48 55)) )
|
||||
Str ) ) ) ) ) )
|
||||
(println
|
||||
(mapcar
|
||||
isin
|
||||
(quote
|
||||
"US0378331005"
|
||||
"US0373831005"
|
||||
"U50378331005"
|
||||
"US03783310005"
|
||||
"AU0000XVGZA3"
|
||||
"AU0000VXGZA3"
|
||||
"FR0000988040" ) ) )
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
function Test-ISIN
|
||||
{
|
||||
[CmdletBinding()]
|
||||
[OutputType([bool])]
|
||||
Param
|
||||
(
|
||||
[Parameter(Mandatory=$true, Position=0)]
|
||||
[ValidatePattern("[A-Z]{2}\w{9}\d")]
|
||||
[ValidateScript({$_.Length -eq 12})]
|
||||
[string]
|
||||
$Number
|
||||
)
|
||||
|
||||
function Split-Array
|
||||
{
|
||||
$array = @(), @()
|
||||
$input | ForEach-Object {$array[($index = -not $index)] += $_}
|
||||
$array[1], $array[0]
|
||||
}
|
||||
|
||||
filter ConvertTo-Digit
|
||||
{
|
||||
if ($_ -gt 9)
|
||||
{
|
||||
$_.ToString().ToCharArray() | ForEach-Object -Begin {$n = 0} -Process {$n += [Char]::GetNumericValue($_)} -End {$n}
|
||||
}
|
||||
else
|
||||
{
|
||||
$_
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$checkDigit = $Number[-1]
|
||||
|
||||
$digits = ($Number -replace ".$").ToCharArray() | ForEach-Object {
|
||||
if ([Char]::IsDigit($_))
|
||||
{
|
||||
[Char]::GetNumericValue($_)
|
||||
}
|
||||
else
|
||||
{
|
||||
[int][char]$_ - 55
|
||||
}
|
||||
}
|
||||
|
||||
$odds, $evens = ($digits -join "").ToCharArray() | Split-Array
|
||||
|
||||
if ($odds.Count -gt $evens.Count)
|
||||
{
|
||||
$odds = $odds | ForEach-Object {[Char]::GetNumericValue($_) * 2} | ConvertTo-Digit
|
||||
$evens = $evens | ForEach-Object {[Char]::GetNumericValue($_)}
|
||||
}
|
||||
else
|
||||
{
|
||||
$odds = $odds | ForEach-Object {[Char]::GetNumericValue($_)}
|
||||
$evens = $evens | ForEach-Object {[Char]::GetNumericValue($_) * 2} | ConvertTo-Digit
|
||||
}
|
||||
|
||||
$sum = ($odds | Measure-Object -Sum).Sum + ($evens | Measure-Object -Sum).Sum
|
||||
|
||||
(10 - ($sum % 10)) % 10 -match $checkDigit
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
"US0378331005","US0373831005","US0337833103","AU0000XVGZA3","AU0000VXGZA3","FR0000988040" | ForEach-Object {
|
||||
[PSCustomObject]@{
|
||||
ISIN = $_
|
||||
IsValid = Test-ISIN -Number $_
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
EnableExplicit
|
||||
|
||||
Procedure.b Check_ISIN(*c.Character)
|
||||
Define count.i=0, Idx.i=1, v.i=0, i.i
|
||||
Dim s.i(24)
|
||||
|
||||
If MemoryStringLength(*c) > 12 : ProcedureReturn #False : EndIf
|
||||
|
||||
While *c\c
|
||||
count+1
|
||||
If *c\c>='0' And *c\c<='9'
|
||||
If count<=2 : ProcedureReturn #False : EndIf
|
||||
s(Idx)= *c\c - '0'
|
||||
Idx+1
|
||||
ElseIf *c\c>='A' And *c\c<='Z'
|
||||
s(Idx)= (*c\c - ('A'-10)) / 10
|
||||
Idx+1
|
||||
s(Idx)= (*c\c - ('A'-10)) % 10
|
||||
Idx+1
|
||||
Else
|
||||
ProcedureReturn #False
|
||||
EndIf
|
||||
*c + SizeOf(Character)
|
||||
Wend
|
||||
|
||||
For i=Idx-2 To 0 Step -2
|
||||
If s(i)*2 > 9
|
||||
v+ s(i)*2 -9
|
||||
Else
|
||||
v+ s(i)*2
|
||||
EndIf
|
||||
v+s(i+1)
|
||||
Next
|
||||
|
||||
ProcedureReturn Bool(v%10=0)
|
||||
EndProcedure
|
||||
|
||||
Define.s s
|
||||
OpenConsole("Validate_International_Securities_Identification_Number (ISIN)")
|
||||
|
||||
If ReadFile(0,"c:\code_pb\rosettacode\data\isin.txt")
|
||||
While Not Eof(0)
|
||||
s=ReadString(0)
|
||||
Print(s+~"\t")
|
||||
If Check_ISIN(@s) : PrintN("TRUE") : Else : PrintN("FALSE") : EndIf
|
||||
Wend
|
||||
CloseFile(0)
|
||||
EndIf
|
||||
Input()
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
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
|
||||
s = "".join(str(int(c, 36)) for c in a)
|
||||
return 0 == (sum(sum(divmod(2 * (ord(c) - 48), 10)) for c in s[-2::-2]) +
|
||||
sum(ord(c) - 48 for c in s[::-2])) % 10
|
||||
|
||||
# A more readable version
|
||||
def check_isin_alt(a):
|
||||
if len(a) != 12:
|
||||
return False
|
||||
s = []
|
||||
for i, c in enumerate(a):
|
||||
if c.isdigit():
|
||||
if i < 2:
|
||||
return False
|
||||
s.append(ord(c) - 48)
|
||||
elif c.isupper():
|
||||
if i == 11:
|
||||
return False
|
||||
s += divmod(ord(c) - 55, 10)
|
||||
else:
|
||||
return False
|
||||
v = sum(s[::-2])
|
||||
for k in s[-2::-2]:
|
||||
k = 2 * k
|
||||
v += k - 9 if k > 9 else k
|
||||
return v % 10 == 0
|
||||
|
||||
[check_isin(s) for s in ["US0378331005", "US0373831005", "U50378331005", "US03378331005",
|
||||
"AU0000XVGZA3", "AU0000VXGZA3", "FR0000988040"]]
|
||||
|
||||
# [True, False, False, False, True, True, True]
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
[ 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
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
/*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" /*see 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 /*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*/
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
#lang racket
|
||||
|
||||
;; convert a base36 character (#\0 - #\Z) to its equivalent
|
||||
;; in base 10 as a string ("0" - "35")
|
||||
(define (base36-char->base10-string c)
|
||||
(let ([char-int (char->integer (char-upcase c))]
|
||||
[zero-int (char->integer #\0)]
|
||||
[nine-int (char->integer #\9)]
|
||||
[A-int (char->integer #\A)]
|
||||
[Z-int (char->integer #\Z)])
|
||||
(cond [(and (>= char-int zero-int) (<= char-int nine-int)) (~a c)]
|
||||
[(and (>= char-int A-int) (<= char-int Z-int)) (~a (+ (- char-int A-int) 10))]
|
||||
[else null])))
|
||||
|
||||
;; substitute equivalent base 10 numbers for base 36 characters in string
|
||||
;; this is a character-by-character substitution not a conversion
|
||||
;; of a base36 number to a base10 number
|
||||
(define (base36-string-characters->base10-string-characters s)
|
||||
(for/fold ([joined ""])
|
||||
([tenstr (map base36-char->base10-string (string->list (string-upcase s)))])
|
||||
(values (string-append joined tenstr))))
|
||||
|
||||
;; This uses the Racket Luhn solution
|
||||
(define [isin-test? s]
|
||||
(let ([RE (pregexp "^[A-Z]{2}[A-Z0-9]{9}[0-9]{1}$")])
|
||||
(and
|
||||
(regexp-match? RE s)
|
||||
(luhn-test (string->number (base36-string-characters->base10-string-characters s))))))
|
||||
|
||||
(define test-cases '("US0378331005" "US0373831005" "U50378331005" "US03378331005" "AU0000XVGZA3" "AU0000VXGZA3" "FR0000988040"))
|
||||
|
||||
(map isin-test? test-cases)
|
||||
;; -> '(#t #f #f #f #t #t #t)
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
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
|
||||
>;
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
# Project : Validate International Securities Identification Number
|
||||
|
||||
decimals(0)
|
||||
|
||||
test = ["US0378331005",
|
||||
"US0373831005",
|
||||
"U50378331005",
|
||||
"US03378331005",
|
||||
"AU0000XVGZA3",
|
||||
"AU0000VXGZA3",
|
||||
"FR0000988040"]
|
||||
|
||||
for n = 1 to len(test)
|
||||
testold = test[n]
|
||||
ascii1 = ascii(left(test[n],1))
|
||||
ascii2 = ascii(substr(test[n],2,1))
|
||||
if len(test[n]) != 12 or (ascii1 < 65 or ascii1 > 90) or (ascii2 < 65 or ascii2 > 90)
|
||||
see test[n] + " -> Invalid" + nl
|
||||
loop
|
||||
ok
|
||||
for m = 1 to len(test[n])
|
||||
if ascii(test[n][m]) > 64 and ascii(test[n][m]) < 91
|
||||
asc = ascii(test[n][m]) - 55
|
||||
test[n] = left(test[n],m-1) + string(asc) + right(test[n],len(test[n])-m)
|
||||
ok
|
||||
next
|
||||
see testold + " -> " + cardtest(test[n]) + nl
|
||||
next
|
||||
|
||||
func cardtest(numstr)
|
||||
revstring = revstr(numstr)
|
||||
s1 = revodd(revstring)
|
||||
s2 = reveven(revstring)
|
||||
s3 =right(string(s1+s2), 1)
|
||||
if s3 = "0"
|
||||
return "Valid"
|
||||
else
|
||||
return "Invalid"
|
||||
ok
|
||||
|
||||
func revstr(str)
|
||||
strnew = ""
|
||||
for nr = len(str) to 1 step -1
|
||||
strnew = strnew + str[nr]
|
||||
next
|
||||
return strnew
|
||||
|
||||
func revodd(str)
|
||||
strnew = ""
|
||||
for nr = 1 to len(str) step 2
|
||||
strnew = strnew + str[nr]
|
||||
next
|
||||
sumodd = 0
|
||||
for p = 1 to len(strnew)
|
||||
sumodd = sumodd + number(strnew[p])
|
||||
next
|
||||
return sumodd
|
||||
|
||||
func reveven(str)
|
||||
strnew = ""
|
||||
for nr = 2 to len(str) step 2
|
||||
strnew = strnew + str[nr]
|
||||
next
|
||||
lsteven = []
|
||||
for p = 1 to len(strnew)
|
||||
add(lsteven, string(2*number(strnew[p])))
|
||||
next
|
||||
arreven = list(len(lsteven))
|
||||
for q = 1 to len(lsteven)
|
||||
sum = 0
|
||||
for w = 1 to len(lsteven[q])
|
||||
sum = sum + lsteven[q][w]
|
||||
next
|
||||
arreven[q] = sum
|
||||
next
|
||||
sumarr = 0
|
||||
for x = 1 to len(arreven)
|
||||
sumarr = sumarr + arreven[x]
|
||||
next
|
||||
return sumarr
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
RE = /\A[A-Z]{2}[A-Z0-9]{9}[0-9]{1}\z/
|
||||
|
||||
def valid_isin?(str)
|
||||
return false unless str =~ RE
|
||||
luhn(str.chars.map{|c| c.to_i(36)}.join)
|
||||
end
|
||||
|
||||
p %w(US0378331005
|
||||
US0373831005
|
||||
U50378331005
|
||||
US03378331005
|
||||
AU0000XVGZA3
|
||||
AU0000VXGZA3
|
||||
FR0000988040).map{|tc| valid_isin?(tc) }
|
||||
|
||||
# => [true, false, false, false, true, true, true]
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
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);
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
data test;
|
||||
length isin $20 ok $1;
|
||||
input isin;
|
||||
keep isin ok;
|
||||
array s{24};
|
||||
link isin;
|
||||
return;
|
||||
isin:
|
||||
ok="N";
|
||||
n=length(isin);
|
||||
if n=12 then do;
|
||||
j=0;
|
||||
do i=1 to n;
|
||||
k=rank(substr(isin,i,1));
|
||||
if k>=48 & k<=57 then do;
|
||||
if i<3 then return;
|
||||
j+1;
|
||||
s{j}=k-48;
|
||||
end;
|
||||
else if k>=65 & k<=90 then do;
|
||||
if i=12 then return;
|
||||
k+-55;
|
||||
j+1;
|
||||
s{j}=int(k/10);
|
||||
j+1;
|
||||
s{j}=mod(k,10);
|
||||
end;
|
||||
else return;
|
||||
end;
|
||||
|
||||
v=sum(of s{*});
|
||||
do i=j-1 to 1 by -2;
|
||||
v+s{i}-9*(s{i}>4);
|
||||
end;
|
||||
|
||||
if mod(v,10)=0 then ok="Y";
|
||||
end;
|
||||
return;
|
||||
cards;
|
||||
US0378331005
|
||||
US0373831005
|
||||
U50378331005
|
||||
US03378331005
|
||||
AU0000XVGZA3
|
||||
AU0000VXGZA3
|
||||
FR0000988040
|
||||
;
|
||||
run;
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
--#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 @
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
object Isin extends App {
|
||||
val isins = Seq("US0378331005", "US0373831005", "U50378331005",
|
||||
"US03378331005", "AU0000XVGZA3","AU0000VXGZA3", "FR0000988040")
|
||||
|
||||
private def ISINtest(isin: String): Boolean = {
|
||||
val isin0 = isin.trim.toUpperCase
|
||||
|
||||
def luhnTestS(number: String): Boolean = {
|
||||
|
||||
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"))
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
package require Tcl 8.6 ;# mostly needed for [assert]. Substitute a simpler one or a NOP if required.
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
proc assert {expr} { ;# for "static" assertions that throw nice errors
|
||||
if {![uplevel 1 [list expr $expr]]} {
|
||||
set msg "{$expr}"
|
||||
catch {append msg " {[uplevel 1 [list subst -noc $expr]]}"}
|
||||
tailcall throw {ASSERT ERROR} $msg
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
namespace eval isin {
|
||||
proc _init {} { ;# sets up the map used on every call
|
||||
variable map
|
||||
set alphabet abcdefghijklmnopqrstuvwxyz
|
||||
set n 9
|
||||
lmap c [split $alphabet ""] {
|
||||
lappend map $c [incr n]
|
||||
}
|
||||
}
|
||||
_init
|
||||
|
||||
proc normalize {isin} {
|
||||
variable map
|
||||
string map $map [string tolower [string trim $isin]]
|
||||
}
|
||||
|
||||
# copied from "Luhn test of credit card numbers"
|
||||
# included here for ease of testing, and because it is short
|
||||
proc luhn digitString {
|
||||
if {[regexp {[^0-9]} $digitString]} {error "not a number"}
|
||||
set sum 0
|
||||
set flip 1
|
||||
foreach ch [lreverse [split $digitString {}]] {
|
||||
incr sum [lindex {
|
||||
{0 1 2 3 4 5 6 7 8 9}
|
||||
{0 2 4 6 8 1 3 5 7 9}
|
||||
} [expr {[incr flip] & 1}] $ch]
|
||||
}
|
||||
return [expr {($sum % 10) == 0}]
|
||||
}
|
||||
|
||||
proc validate {isin} {
|
||||
if {![regexp {^[A-Z]{2}[A-Z0-9]{9}[0-9]$} $isin]} {return false}
|
||||
luhn [normalize $isin]
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package require tcltest
|
||||
|
||||
tcltest::test isin-1 "Test isin validation" -body {
|
||||
foreach {isin ok} {
|
||||
US0378331005 yes
|
||||
US0373831005 no
|
||||
U50378331005 no
|
||||
US03378331005 no
|
||||
AU0000XVGZA3 yes
|
||||
AU0000VXGZA3 yes
|
||||
FR0000988040 yes
|
||||
} {
|
||||
if {$ok} {
|
||||
assert {[isin::validate $isin]}
|
||||
} else {
|
||||
assert {![isin::validate $isin]}
|
||||
}
|
||||
}
|
||||
return ok
|
||||
} -result ok
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
CREATE FUNCTION dbo._ISINCheck( @strISIN VarChar(40) )
|
||||
RETURNS bit
|
||||
AS
|
||||
BEGIN
|
||||
--*** Test an ISIN code and return 1 if it is valid, 0 if invalid.
|
||||
DECLARE @bValid bit;
|
||||
|
||||
SET @bValid = CASE WHEN @strISIN LIKE '[A-Z][A-Z][A-Z,0-9][A-Z,0-9][A-Z,0-9][A-Z,0-9][A-Z,0-9][A-Z,0-9][A-Z,0-9][A-Z,0-9][A-Z,0-9][0-9]' THEN 1 ELSE 0 END
|
||||
IF @bValid = 1
|
||||
BEGIN
|
||||
DECLARE @strTest VarChar(40) = '';
|
||||
DECLARE @strAdd VarChar(2);
|
||||
DECLARE @p INT = 0;
|
||||
WHILE @p < LEN(@strISIN)
|
||||
BEGIN
|
||||
SET @p = @p+1;
|
||||
SET @strAdd = SUBSTRING(@strISIN,@p,1);
|
||||
IF @strAdd LIKE '[A-Z]' SET @strAdd = CONVERT(VarChar(2),ASCII(UPPER(@strAdd))-55);
|
||||
SET @strTest = @strTest + @strAdd;
|
||||
END;
|
||||
|
||||
-- Proceed with Luhn test
|
||||
DECLARE @strLuhn VarChar(40) = REVERSE(@strTest); -- usage: set once, never changed
|
||||
DECLARE @strS2Values VarChar(10) = '0246813579'; -- constant: maps digits to their S2 summed values
|
||||
SET @p = 0; -- reset loop counter
|
||||
DECLARE @intValue INT;
|
||||
DECLARE @intSum INT = 0;
|
||||
-- loop through the reversed string, get the value (even-positioned digits are mapped) and add it to @intSum
|
||||
WHILE @p < LEN(@strLuhn)
|
||||
BEGIN
|
||||
SET @p = @p+1;
|
||||
SET @intValue = CONVERT(INT, SUBSTRING(@strLuhn,@p,1) ) -- value of the digit at position @p in the string
|
||||
IF @p % 2 = 0 SET @intValue = CONVERT(INT,SUBSTRING(@strS2Values,@intValue+1,1))
|
||||
SET @intSum = @intSum + @intValue
|
||||
END
|
||||
-- If the of the digits' mapped values ends in 0 (modulo 10 = 0) then the Luhn test succeeds
|
||||
SET @bValid = CASE WHEN @intSum % 10 = 0 THEN 1 ELSE 0 END
|
||||
END;
|
||||
|
||||
RETURN @bValid
|
||||
END
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
-- Testing. The following tests all pass.
|
||||
;WITH ISIN_Tests AS
|
||||
( SELECT 'US0378331005' AS ISIN, 1 Expected
|
||||
UNION SELECT 'US0373831005',0
|
||||
UNION SELECT 'U50378331005',0
|
||||
UNION SELECT 'US03378331005',0
|
||||
UNION SELECT 'AU0000XVGZA3',1
|
||||
UNION SELECT 'AU0000VXGZA3',1
|
||||
UNION SELECT 'FR0000988040',1
|
||||
UNION SELECT '0___garbage',0
|
||||
UNION SELECT '',0
|
||||
)
|
||||
SELECT ISIN, Expected, dbo._ISINCheck(ISIN) AS TestResult FROM ISIN_Tests ORDER BY ISIN
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
' Validate International Securities Identification Number - 03/03/2019
|
||||
|
||||
buf=buf&test("US0378331005")&vbCrLf
|
||||
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)
|
||||
dim err,c,r,s,i1,i2
|
||||
if len(cc)=12 then
|
||||
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
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
Option Strict On
|
||||
Imports System.Text.RegularExpressions
|
||||
|
||||
Module Module1
|
||||
ReadOnly IsinRegex As New Regex("^[A-Z]{2}[A-Z0-9]{9}\d$", RegexOptions.Compiled)
|
||||
|
||||
Function DigitValue(c As Char) As Integer
|
||||
Dim temp As Integer
|
||||
If Asc(c) >= Asc("0"c) AndAlso Asc(c) <= Asc("9"c) Then
|
||||
temp = Asc(c) - Asc("0"c)
|
||||
Else
|
||||
temp = Asc(c) - Asc("A"c) + 10
|
||||
End If
|
||||
Return temp
|
||||
End Function
|
||||
|
||||
Function LuhnTest(number As String) As Boolean
|
||||
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
|
||||
End Function
|
||||
|
||||
Function Digitize(isin As String) As String
|
||||
Return String.Join("", isin.Select(Function(c) $"{DigitValue(c)}"))
|
||||
End Function
|
||||
|
||||
Function IsValidIsin(isin As String) As Boolean
|
||||
Return IsinRegex.IsMatch(isin) AndAlso LuhnTest(Digitize(isin))
|
||||
End Function
|
||||
|
||||
Sub Main()
|
||||
Dim isins() = {
|
||||
"US0378331005",
|
||||
"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} is not valid", isin)
|
||||
End If
|
||||
Next
|
||||
End Sub
|
||||
|
||||
End Module
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
Function IsValidISIN(ByVal ISIN As String) As Boolean
|
||||
Dim s As String, c As String
|
||||
Dim i As Long
|
||||
If Len(ISIN) = 12 Then
|
||||
For i = 1 To Len(ISIN)
|
||||
c = UCase$(Mid(ISIN, i, 1))
|
||||
Select Case c
|
||||
Case "A" To "Z"
|
||||
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
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
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
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
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)")
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
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);
|
||||
];
|
||||
]
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
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$)
|
||||
If l <> 12 Then
|
||||
Print test_item$, " Invalid, length <> 12 char."
|
||||
Continue
|
||||
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
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
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())
|
||||
}
|
||||
fcn luhnTest(n){
|
||||
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)
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
println(" ISIN Valid?");
|
||||
foreach isin in (T("US0378331005","US0373831005","U50378331005",
|
||||
"US03378331005","AU0000XVGZA3","AU0000VXGZA3","FR0000988040")){
|
||||
println(isin," --> ",validateISIN(isin));
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue