Update all new Tasks
This commit is contained in:
parent
00a190b0a6
commit
91df62d461
5697 changed files with 93386 additions and 804 deletions
8
Task/IBAN/00DESCRIPTION
Normal file
8
Task/IBAN/00DESCRIPTION
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{{wikipedia}}
|
||||
The [[wp:International_Bank_Account_Number|International Bank Account Number (IBAN)]] is an internationally agreed means of identifying bank accounts across national borders with a reduced risk of propagating [[wp:Transcription_error|transcription errors]]. <br>
|
||||
The IBAN consists of up to 34 alphanumeric characters: first the two-letter ISO 3166-1 alpha-2 country code, then two check digits, and finally a country-specific Basic Bank Account Number (BBAN). <br>
|
||||
The check digits enable a sanity check of the bank account number to confirm its integrity even before submitting a transaction.
|
||||
|
||||
The task here is to validate the following fictitious IBAN: <tt>GB82 WEST 1234 5698 7654 32</tt>.
|
||||
|
||||
Details of the algorithm can be found on the Wikipedia page.
|
||||
2
Task/IBAN/00META.yaml
Normal file
2
Task/IBAN/00META.yaml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
---
|
||||
note: Checksums
|
||||
3
Task/IBAN/Ada/iban-1.ada
Normal file
3
Task/IBAN/Ada/iban-1.ada
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
package Iban_Code is
|
||||
function Is_Legal(Iban : String) return Boolean;
|
||||
end Iban_Code;
|
||||
88
Task/IBAN/Ada/iban-2.ada
Normal file
88
Task/IBAN/Ada/iban-2.ada
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
with Ada.Characters.Handling; use Ada.Characters.Handling;
|
||||
with Ada.Containers.Hashed_Maps;
|
||||
with Ada.Strings.Hash;
|
||||
|
||||
package body Iban_Code is
|
||||
|
||||
subtype Nation is String (1..2);
|
||||
|
||||
package String_Integer is new Ada.Containers.Hashed_Maps
|
||||
(Nation, Integer, Ada.Strings.Hash, Equivalent_Keys => "=");
|
||||
|
||||
Nations : String_Integer.Map;
|
||||
|
||||
function Is_Legal(Iban : String) return Boolean
|
||||
is
|
||||
Temp : String(Iban'Range) := (others => ' ');
|
||||
Count : Integer;
|
||||
Ch : Character;
|
||||
Num : Integer := 0;
|
||||
begin
|
||||
-- remove blank spaces and check characters
|
||||
Count := Temp'First;
|
||||
for I in Iban'Range loop
|
||||
case Iban(I) is
|
||||
when ' ' => null;
|
||||
when 'a'..'z' =>
|
||||
Temp(Count) := To_Upper(Iban(I));
|
||||
Count := Count + 1;
|
||||
when 'A'..'Z'|'0'..'9' =>
|
||||
Temp(Count) := Iban(I);
|
||||
Count := Count + 1;
|
||||
when others => return False;
|
||||
end case;
|
||||
end loop;
|
||||
-- check nation code and length
|
||||
if not Nations.Contains (Temp(1..2)) or else
|
||||
Nations.Element (Temp(1..2))/= Count - 1 then
|
||||
return False;
|
||||
end if;
|
||||
-- move the 4 initial characters to the end
|
||||
Temp(Temp'First..Count-1) := Temp(5..Count-1) & Temp(Temp'First..4);
|
||||
-- compute remainder modulo 97
|
||||
for I in Temp'First..Count-1 loop
|
||||
Ch := Temp(I);
|
||||
if Ch in '0'..'9' then
|
||||
Num := Integer'Value(Integer'Image(Num) & Ch) mod 97;
|
||||
else
|
||||
Num := (Num * 100 +
|
||||
(Character'Pos(Ch) - Character'Pos('A') + 10)) mod 97;
|
||||
end if;
|
||||
end loop;
|
||||
return Num = 1;
|
||||
end Is_Legal;
|
||||
|
||||
begin
|
||||
Nations.insert("AL", 28); Nations.insert("AD", 24);
|
||||
Nations.insert("AT", 20); Nations.insert("AZ", 28);
|
||||
Nations.insert("BE", 16); Nations.insert("BH", 22);
|
||||
Nations.insert("BA", 20); Nations.insert("BR", 29);
|
||||
Nations.insert("BG", 22); Nations.insert("CR", 21);
|
||||
Nations.insert("HR", 21); Nations.insert("CY", 28);
|
||||
Nations.insert("CZ", 24); Nations.insert("DK", 18);
|
||||
Nations.insert("DO", 28); Nations.insert("EE", 20);
|
||||
Nations.insert("FO", 18); Nations.insert("FI", 18);
|
||||
Nations.insert("FR", 27); Nations.insert("GE", 22);
|
||||
Nations.insert("DE", 22); Nations.insert("GI", 23);
|
||||
Nations.insert("GR", 27); Nations.insert("GL", 18);
|
||||
Nations.insert("GT", 28); Nations.insert("HU", 28);
|
||||
Nations.insert("IS", 26); Nations.insert("IE", 22);
|
||||
Nations.insert("IL", 23); Nations.insert("IT", 27);
|
||||
Nations.insert("KZ", 20); Nations.insert("KW", 30);
|
||||
Nations.insert("LV", 21); Nations.insert("LB", 28);
|
||||
Nations.insert("LI", 21); Nations.insert("LT", 20);
|
||||
Nations.insert("LU", 20); Nations.insert("MK", 19);
|
||||
Nations.insert("MT", 31); Nations.insert("MR", 27);
|
||||
Nations.insert("MU", 30); Nations.insert("MC", 27);
|
||||
Nations.insert("MD", 24); Nations.insert("ME", 22);
|
||||
Nations.insert("NL", 18); Nations.insert("NO", 15);
|
||||
Nations.insert("PK", 24); Nations.insert("PS", 29);
|
||||
Nations.insert("PL", 28); Nations.insert("PT", 25);
|
||||
Nations.insert("RO", 24); Nations.insert("SM", 27);
|
||||
Nations.insert("SA", 24); Nations.insert("RS", 22);
|
||||
Nations.insert("SK", 24); Nations.insert("SI", 19);
|
||||
Nations.insert("ES", 24); Nations.insert("SE", 24);
|
||||
Nations.insert("CH", 21); Nations.insert("TN", 24);
|
||||
Nations.insert("TR", 26); Nations.insert("AE", 23);
|
||||
Nations.insert("GB", 22); Nations.insert("VG", 24);
|
||||
end Iban_Code;
|
||||
21
Task/IBAN/Ada/iban-3.ada
Normal file
21
Task/IBAN/Ada/iban-3.ada
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
with Ada.Text_Io; use Ada.Text_Io;
|
||||
with Iban_Code;
|
||||
|
||||
procedure Check_Iban is
|
||||
|
||||
procedure Check(Iban : String) is
|
||||
begin
|
||||
if Iban_Code.Is_Legal(Iban) then
|
||||
Put_Line(Iban & " is valid.");
|
||||
else
|
||||
Put_Line(Iban & " is not valid.");
|
||||
end if;
|
||||
end Check;
|
||||
|
||||
begin
|
||||
Check("GB82 WEST 1234 5698 7654 32");
|
||||
Check("GB82WEST12345698765432");
|
||||
Check("gb82 west 1234 5698 7654 32");
|
||||
Check("GB82 TEST 1234 5698 7654 32");
|
||||
Check("GB82 WEST 1243 5698 7654 32");
|
||||
end Check_Iban;
|
||||
43
Task/IBAN/AutoHotkey/iban.ahk
Normal file
43
Task/IBAN/AutoHotkey/iban.ahk
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
IBANs := ["GB82 WEST 1234 5698 7654 32"
|
||||
, "gb82 WEST 1234 5698 7654 32"
|
||||
, "GB82WEST12345698765432"
|
||||
, "GB82 WEST 234 5698 7654 32"
|
||||
, "GB82 WEST 1234 5698 7654 33"
|
||||
, "AE82 WEST 1234 5698 7654 32"]
|
||||
for k, v in IBANs
|
||||
Output .= v " is" (ValidIBAN(v) ? "" : " not") " valid.`n"
|
||||
MsgBox, % Output
|
||||
|
||||
ValidIBAN(n) {
|
||||
static CC := {AL:28, AD:24, AT:20, AZ:28, BH:22, BE:16, BA:20, BR:29, BG:22, CR:21
|
||||
, HR:21, CY:28, CZ:24, DK:18, DO:28, EE:20, FO:18, FI:18, FR:27, GE:22
|
||||
, DE:22, GI:23, GR:27, GL:18, GT:28, HU:28, IS:26, IE:22, IL:23, IT:27
|
||||
, JO:30, KZ:20, KW:30, LV:21, LB:28, LI:21, LT:20, LU:20, MK:19, MT:31
|
||||
, MR:27, MU:30, MC:27, MD:24, ME:22, NL:18, NO:15, PK:24, PS:29, PL:28
|
||||
, PT:25, QA:29, RO:24, SM:27, SA:24, RS:22, SK:24, SI:19, ES:24, SE:24
|
||||
, CH:21, TN:24, TR:26, AE:23, GB:22, VG:24}
|
||||
StringReplace, n, n, % A_Space,, A
|
||||
;Check that the total IBAN length is correct as per the country
|
||||
if (StrLen(n) != CC[SubStr(n, 1, 2)])
|
||||
return false
|
||||
StringUpper, n, n
|
||||
;Move the four initial characters to the end of the string
|
||||
n := SubStr(n, 5) SubStr(n, 1, 4)
|
||||
;Replace each letter in the string with two digits
|
||||
Loop, Parse, n
|
||||
{
|
||||
if A_LoopField is alpha
|
||||
nn .= Asc(A_LoopField) - 55
|
||||
else
|
||||
nn .= A_LoopField
|
||||
}
|
||||
return Mod97(nn) = 1
|
||||
}
|
||||
|
||||
Mod97(a) {
|
||||
while a {
|
||||
rem := Mod(rem SubStr(a, 1, 15), 97)
|
||||
a := SubStr(a, 16)
|
||||
}
|
||||
return rem
|
||||
}
|
||||
93
Task/IBAN/BBC-BASIC/iban.bbc
Normal file
93
Task/IBAN/BBC-BASIC/iban.bbc
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
REM Used the following as official standard:
|
||||
REM http://www.cnb.cz/cs/platebni_styk/iban/download/EBS204.pdf
|
||||
|
||||
REM Pairs of ISO 3166 country code & expected IBAN length for this country
|
||||
COULEN$="AL28 AD24 AT20 AZ28 BE16 BH22 BA20 BR29 BG22 CR21 HR21 CY28 CZ24 DK18 DO28 EE20 "+\
|
||||
\ "FO18 FI18 FR27 GE22 DE22 GI23 GR27 GL18 GT28 HU28 IS26 IE22 IL23 IT27 KZ20 KW30 "+\
|
||||
\ "LV21 LB28 LI21 LT20 LU20 MK19 MT31 MR27 MU30 MC27 MD24 ME22 NL18 NO15 PK24 PS29 "+\
|
||||
\ "PL28 PT25 RO24 SM27 SA24 RS22 SK24 SI19 ES24 SE24 CH21 TN24 TR26 AE23 GB22 VG24"
|
||||
|
||||
PROCIBANcheck("GB82 WEST 1234 5698 7654 32"):REM Paper IBAN notation (with the spaces)
|
||||
PROCIBANcheck("GB82WEST12345698765432") :REM Digital IBAN notation (without the spaces)
|
||||
PROCIBANcheck("gb82 west 1234 5698 7654 32")
|
||||
PROCIBANcheck("GB82 TEST 1234 5698 7654 32")
|
||||
PROCIBANcheck("GR16 0110 1250 0000 0001 2300 695")
|
||||
PROCIBANcheck("GB29 NWBK 6016 1331 9268 19")
|
||||
PROCIBANcheck("SA03 8000 0000 6080 1016 7519")
|
||||
PROCIBANcheck("CH93 0076 2011 6238 5295 7")
|
||||
PROCIBANcheck("IL62 0108 0000 0009 9999 999")
|
||||
PROCIBANcheck("IL62-0108-0000-0009-9999-999")
|
||||
PROCIBANcheck("US12 3456 7890 0987 6543 210")
|
||||
PROCIBANcheck("GR16 0110 1250 0000 0001 2300 695X")
|
||||
END
|
||||
|
||||
DEF PROCIBANcheck(iban$)
|
||||
LOCAL err$,i%,match%,explen%,digiban$,tmpiban$,bignum$,c%,kk%
|
||||
|
||||
REM Search for country code and fetch expected length
|
||||
i%=1:explen%=0
|
||||
WHILE explen%=0 AND i%<LENCOULEN$
|
||||
IF LEFT$(iban$,2)=MID$(COULEN$,i%,2) explen%=VALMID$(COULEN$,i%+2,2)
|
||||
i%+=5
|
||||
ENDWHILE
|
||||
match%=explen%>0
|
||||
|
||||
REM Continue if country code found
|
||||
IF match% THEN
|
||||
REM Remove space = convert to digital IBAN
|
||||
digiban$=""
|
||||
FOR i%=1TOLENiban$
|
||||
IF MID$(iban$,i%,1)>" " digiban$+=MID$(iban$,i%,1)
|
||||
NEXT
|
||||
REM Compare length with expected length
|
||||
match%=explen%=LENdigiban$
|
||||
|
||||
REM Continue if length is correct
|
||||
IF match% THEN
|
||||
REM Create temporary string with country code appended
|
||||
tmpiban$=MID$(digiban$,5)+MID$(digiban$,1,2)
|
||||
REM Make big number, replacing letters by numbers using next conversion table: A=10 ... Z=35
|
||||
bignum$=""
|
||||
FOR i%=1TOLENtmpiban$
|
||||
c%=ASCMID$(tmpiban$,i%,1)
|
||||
IF c%>57 bignum$+=STR$(c%-55) ELSE bignum$+=STR$(c%-48)
|
||||
NEXT
|
||||
REM MOD 97 on bignum$+"00" and subtract result from 98 to obtain control number
|
||||
kk%=98-FNmod97(bignum$+"00")
|
||||
REM Compare with control number in IBAN
|
||||
match%=VALMID$(iban$,3,2)=kk%
|
||||
|
||||
REM Continue if control number matches
|
||||
IF match% THEN
|
||||
REM Append kk% to bignum$ and determine if MOD 97 results in 1
|
||||
match%=FNmod97(bignum$+RIGHT$("0"+STR$kk%,2))=1
|
||||
|
||||
REM Continue if MOD 97
|
||||
IF match% THEN
|
||||
REM Was last test
|
||||
ELSE
|
||||
err$="result from modulo 97"
|
||||
ENDIF
|
||||
ELSE
|
||||
err$="check digits, should be: "+STR$kk%
|
||||
ENDIF
|
||||
ELSE
|
||||
err$="code length, expected length: "+STR$explen%
|
||||
ENDIF
|
||||
ELSE
|
||||
err$="country code: "+LEFT$(iban$,2)
|
||||
ENDIF
|
||||
|
||||
IF match% PRINT " "; ELSE PRINT "in";:err$="***error!*** invalid "+err$
|
||||
PRINT "valid IBAN: ";iban$TAB(50)err$
|
||||
ENDPROC
|
||||
|
||||
DEF FNmod97(num$)
|
||||
LOCAL mod$
|
||||
mod$=LEFT$(num$,2)
|
||||
num$=MID$(num$,3)
|
||||
WHILE num$>""
|
||||
mod$=RIGHT$("0"+STR$(VAL(mod$+LEFT$(num$,7))MOD97),2)
|
||||
num$=MID$(num$,8)
|
||||
ENDWHILE
|
||||
=VALmod$
|
||||
51
Task/IBAN/Bracmat/iban.bracmat
Normal file
51
Task/IBAN/Bracmat/iban.bracmat
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
( ( IBAN-check
|
||||
= table country cd len N c
|
||||
. (AL.28) (AD.24) (AT.20) (AZ.28) (BE.16) (BH.22) (BA.20) (BR.29)
|
||||
(BG.22) (CR.21) (HR.21) (CY.28) (CZ.24) (DK.18) (DO.28) (EE.20)
|
||||
(FO.18) (FI.18) (FR.27) (GE.22) (DE.22) (GI.23) (GR.27) (GL.18)
|
||||
(GT.28) (HU.28) (IS.26) (IE.22) (IL.23) (IT.27) (KZ.20) (KW.30)
|
||||
(LV.21) (LB.28) (LI.21) (LT.20) (LU.20) (MK.19) (MT.31) (MR.27)
|
||||
(MU.30) (MC.27) (MD.24) (ME.22) (NL.18) (NO.15) (PK.24) (PS.29)
|
||||
(PL.28) (PT.25) (RO.24) (SM.27) (SA.24) (RS.22) (SK.24) (SI.19)
|
||||
(ES.24) (SE.24) (CH.21) (TN.24) (TR.26) (AE.23) (GB.22) (VG.24)
|
||||
: ?table
|
||||
& @(!arg:?country [2 ?cd [4 ?arg)
|
||||
& str$(!arg !country !cd):?arg
|
||||
& ( !table:? (!country.?len) ?
|
||||
& :?N
|
||||
& ( @( !arg
|
||||
: ?
|
||||
( %@?c ?
|
||||
& ( !c:#
|
||||
| !c:~<A:~>Z
|
||||
& asc$!c+-1*asc$A+10:?c
|
||||
& 1+!len:?len
|
||||
| !c:" "&:?c
|
||||
|
|
||||
)
|
||||
& !N !c:?N
|
||||
& ~
|
||||
)
|
||||
)
|
||||
| str$!N:?N:#
|
||||
& ( @(!N:? [!len)
|
||||
& ( mod$(!N,97):1&out$OK
|
||||
| out$"wrong check digits"
|
||||
)
|
||||
| out$"wrong length"
|
||||
)
|
||||
| @(!N:? ~#%?c ?)
|
||||
& out$(str$("invalid character: '" !c "'"))
|
||||
)
|
||||
| out$(str$("invalid country code: '" !country "'"))
|
||||
)
|
||||
)
|
||||
& IBAN-check$"GB82 WEST 1234 5698 7654 32 9"
|
||||
& IBAN-check$"GX82 WEST 1234 5698 7654 32"
|
||||
& IBAN-check$"GB82 WEST 1234 5698 7654 32"
|
||||
& IBAN-check$GB82WEST12345698765432
|
||||
& IBAN-check$"gb82 west 1234 5698 7654 32"
|
||||
& IBAN-check$"GB82 TEST 1234 5698 7654 32"
|
||||
& IBAN-check$"GB82 WEST 1243 5698 7654 32"
|
||||
& IBAN-check$"GB82 west 1243 5698 7654 32"
|
||||
);
|
||||
72
Task/IBAN/C++/iban.cpp
Normal file
72
Task/IBAN/C++/iban.cpp
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
#include <string>
|
||||
#include <iostream>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <map>
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
using namespace boost::algorithm ;
|
||||
|
||||
bool isValid ( const std::string &ibanstring ) {
|
||||
|
||||
static std::map<std::string, int> countrycodes
|
||||
{ {"AL" , 28} , {"AD" , 24} , {"AT" , 20} , {"AZ" , 28 } ,
|
||||
{"BE" , 16} , {"BH" , 22} , {"BA" , 20} , {"BR" , 29 } ,
|
||||
{"BG" , 22} , {"CR" , 21} , {"HR" , 21} , {"CY" , 28 } ,
|
||||
{"CZ" , 24} , {"DK" , 18} , {"DO" , 28} , {"EE" , 20 } ,
|
||||
{"FO" , 18} , {"FI" , 18} , {"FR" , 27} , {"GE" , 22 } ,
|
||||
{"DE" , 22} , {"GI" , 23} , {"GR" , 27} , {"GL" , 18 } ,
|
||||
{"GT" , 28} , {"HU" , 28} , {"IS" , 26} , {"IE" , 22 } ,
|
||||
{"IL" , 23} , {"IT" , 27} , {"KZ" , 20} , {"KW" , 30 } ,
|
||||
{"LV" , 21} , {"LB" , 28} , {"LI" , 21} , {"LT" , 20 } ,
|
||||
{"LU" , 20} , {"MK" , 19} , {"MT" , 31} , {"MR" , 27 } ,
|
||||
{"MU" , 30} , {"MC" , 27} , {"MD" , 24} , {"ME" , 22 } ,
|
||||
{"NL" , 18} , {"NO" , 15} , {"PK" , 24} , {"PS" , 29 } ,
|
||||
{"PL" , 28} , {"PT" , 25} , {"RO" , 24} , {"SM" , 27 } ,
|
||||
{"SA" , 24} , {"RS" , 22} , {"SK" , 24} , {"SI" , 19 } ,
|
||||
{"ES" , 24} , {"SE" , 24} , {"CH" , 21} , {"TN" , 24 } ,
|
||||
{"TR" , 26} , {"AE" , 23} , {"GB" , 22} , {"VG" , 24 } } ;
|
||||
std::string teststring( ibanstring ) ;
|
||||
erase_all( teststring , " " ) ; //defined in boost/algorithm/string.hpp
|
||||
if ( countrycodes.find( teststring.substr(0 , 2 )) == countrycodes.end( ) )
|
||||
return false ;
|
||||
if ( teststring.length( ) != countrycodes[ teststring.substr( 0 , 2 ) ] )
|
||||
return false ;
|
||||
if ( ! ( all ( teststring , is_alnum( ) ) ) )
|
||||
return false ;
|
||||
to_upper( teststring ) ;
|
||||
teststring = teststring.append( teststring.substr( 0 , 4 ) ) ;
|
||||
teststring.assign( teststring.substr( 4 ) ) ;
|
||||
std::string numberstring ;//will contain the letter substitutions
|
||||
for ( int i = 0 ; i < teststring.length( ) ; i++ ) {
|
||||
if ( std::isdigit( teststring[ i ] ) )
|
||||
numberstring = numberstring + teststring[ i ] ;
|
||||
if ( std::isupper( teststring[ i ] ) )
|
||||
numberstring = numberstring + std::to_string( static_cast<int>( teststring[ i ] ) - 55 ) ;
|
||||
}
|
||||
//implements a stepwise check for mod 97 in chunks of 9 at the first time
|
||||
// , then in chunks of seven prepended by the last mod 97 operation converted
|
||||
//to a string
|
||||
int segstart = 0 ;
|
||||
int step = 9 ;
|
||||
std::string prepended ;
|
||||
long number = 0 ;
|
||||
while ( segstart < numberstring.length( ) - step ) {
|
||||
number = std::stol( prepended + numberstring.substr( segstart , step ) ) ;
|
||||
int remainder = number % 97 ;
|
||||
prepended = std::to_string( remainder ) ;
|
||||
if ( remainder < 10 )
|
||||
prepended = "0" + prepended ;
|
||||
segstart = segstart + step ;
|
||||
step = 7 ;
|
||||
}
|
||||
number = std::stol( prepended + numberstring.substr( segstart )) ;
|
||||
return ( number % 97 == 1 ) ;
|
||||
}
|
||||
|
||||
int main( ) {
|
||||
std::cout << "GB82 WEST 1234 5698 7654 32 is " << ( isValid( "GB82 WEST 1234 5698 7654 32" ) ? "" : "not " )
|
||||
<< "valid!" << std::endl ;
|
||||
std::cout << "GB82TEST12345698765432 is " << ( isValid( "GB82TEST12345698765432" ) ? "" : "not " )
|
||||
<< "valid!" << std::endl ;
|
||||
return 0 ;
|
||||
}
|
||||
120
Task/IBAN/C-sharp/iban-1.cs
Normal file
120
Task/IBAN/C-sharp/iban-1.cs
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
public class IbanValidator : IValidateTypes
|
||||
{
|
||||
public ValidationResult Validate(string value)
|
||||
{
|
||||
// Check if value is missing
|
||||
if (string.IsNullOrEmpty(value))
|
||||
return ValidationResult.ValueMissing;
|
||||
|
||||
if (value.Length < 2)
|
||||
return ValidationResult.ValueTooSmall;
|
||||
|
||||
var countryCode = value.Substring(0, 2).ToUpper();
|
||||
|
||||
int lengthForCountryCode;
|
||||
|
||||
var countryCodeKnown = _lengths.TryGetValue(countryCode, out lengthForCountryCode);
|
||||
if (!countryCodeKnown)
|
||||
{
|
||||
return ValidationResult.CountryCodeNotKnown;
|
||||
}
|
||||
|
||||
// Check length.
|
||||
if (value.Length < lengthForCountryCode)
|
||||
return ValidationResult.ValueTooSmall;
|
||||
|
||||
if (value.Length > lengthForCountryCode)
|
||||
return ValidationResult.ValueTooBig;
|
||||
|
||||
value = value.ToUpper();
|
||||
var newIban = value.Substring(4) + value.Substring(0, 4);
|
||||
|
||||
newIban = Regex.Replace(newIban, @"\D", match => ((int) match.Value[0] - 55).ToString());
|
||||
|
||||
var remainder = BigInteger.Parse(newIban) % 97;
|
||||
|
||||
if (remainder != 1)
|
||||
return ValidationResult.ValueFailsModule97Check;
|
||||
|
||||
|
||||
return ValidationResult.IsValid;
|
||||
}
|
||||
|
||||
public enum ValidationResult
|
||||
{
|
||||
IsValid,
|
||||
ValueMissing,
|
||||
ValueTooSmall,
|
||||
ValueTooBig,
|
||||
ValueFailsModule97Check,
|
||||
CountryCodeNotKnown
|
||||
}
|
||||
|
||||
private static Dictionary<string, int> _lengths = new Dictionary<string, int>
|
||||
{
|
||||
{"AL", 28},
|
||||
{"AD", 24},
|
||||
{"AT", 20},
|
||||
{"AZ", 28},
|
||||
{"BE", 16},
|
||||
{"BH", 22},
|
||||
{"BA", 20},
|
||||
{"BR", 29},
|
||||
{"BG", 22},
|
||||
{"CR", 21},
|
||||
{"HR", 21},
|
||||
{"CY", 28},
|
||||
{"CZ", 24},
|
||||
{"DK", 18},
|
||||
{"DO", 28},
|
||||
{"EE", 20},
|
||||
{"FO", 18},
|
||||
{"FI", 18},
|
||||
{"FR", 27},
|
||||
{"GE", 22},
|
||||
{"DE", 22},
|
||||
{"GI", 23},
|
||||
{"GR", 27},
|
||||
{"GL", 18},
|
||||
{"GT", 28},
|
||||
{"HU", 28},
|
||||
{"IS", 26},
|
||||
{"IE", 22},
|
||||
{"IL", 23},
|
||||
{"IT", 27},
|
||||
{"KZ", 20},
|
||||
{"KW", 30},
|
||||
{"LV", 21},
|
||||
{"LB", 28},
|
||||
{"LI", 21},
|
||||
{"LT", 20},
|
||||
{"LU", 20},
|
||||
{"MK", 19},
|
||||
{"MT", 31},
|
||||
{"MR", 27},
|
||||
{"MU", 30},
|
||||
{"MC", 27},
|
||||
{"MD", 24},
|
||||
{"ME", 22},
|
||||
{"NL", 18},
|
||||
{"NO", 15},
|
||||
{"PK", 24},
|
||||
{"PS", 29},
|
||||
{"PL", 28},
|
||||
{"PT", 25},
|
||||
{"RO", 24},
|
||||
{"SM", 27},
|
||||
{"SA", 24},
|
||||
{"RS", 22},
|
||||
{"SK", 24},
|
||||
{"SI", 19},
|
||||
{"ES", 24},
|
||||
{"SE", 24},
|
||||
{"CH", 21},
|
||||
{"TN", 24},
|
||||
{"TR", 26},
|
||||
{"AE", 23},
|
||||
{"GB", 22},
|
||||
{"VG", 24}
|
||||
};
|
||||
}
|
||||
100
Task/IBAN/C-sharp/iban-2.cs
Normal file
100
Task/IBAN/C-sharp/iban-2.cs
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
public class When_the_IbanValidator_is_told_to_Validate
|
||||
{
|
||||
[Fact]
|
||||
public void It_should_return_an_error_when_there_is_no_value_provided()
|
||||
{
|
||||
// Assert
|
||||
const string value = "";
|
||||
var validator = new IbanValidator();
|
||||
|
||||
// Act
|
||||
var result = validator.Validate(value);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(ValidationResult.ValueMissing, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void It_should_return_an_error_when_the_value_length_is_to_short()
|
||||
{
|
||||
// Assert
|
||||
const string value = "BE1800165492356";
|
||||
var validator = new IbanValidator();
|
||||
|
||||
// Act
|
||||
var result = validator.Validate(value);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(ValidationResult.ValueTooSmall, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void It_should_return_an_error_when_the_value_length_is_to_big()
|
||||
{
|
||||
// Assert
|
||||
const string value = "BE180016549235656";
|
||||
var validator = new IbanValidator();
|
||||
|
||||
// Act
|
||||
var result = validator.Validate(value);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(ValidationResult.ValueTooBig, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void It_should_return_an_error_when_the_value_fails_the_module_check()
|
||||
{
|
||||
// Assert
|
||||
const string value = "BE18001654923566";
|
||||
var validator = new IbanValidator();
|
||||
|
||||
// Act
|
||||
var result = validator.Validate(value);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(ValidationResult.ValueFailsModule97Check, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void It_should_return_an_error_when_an_unkown_country_prefix_used()
|
||||
{
|
||||
// Assert
|
||||
const string value = "XX82WEST12345698765432";
|
||||
var validator = new IbanValidator();
|
||||
|
||||
// Act
|
||||
var result = validator.Validate(value);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(ValidationResult.CountryCodeNotKnown, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void It_should_return_valid_when_a_valid_value_is_provided()
|
||||
{
|
||||
// Assert
|
||||
const string value = "BE18001654923565";
|
||||
var validator = new IbanValidator();
|
||||
|
||||
// Act
|
||||
var result = validator.Validate(value);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(ValidationResult.IsValid, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void It_should_return_valid_when_a_valid_foreign_value_is_provided()
|
||||
{
|
||||
// Assert
|
||||
const string value = "GB82WEST12345698765432";
|
||||
var validator = new IbanValidator();
|
||||
|
||||
// Act
|
||||
var result = validator.Validate(value);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(ValidationResult.IsValid, result);
|
||||
}
|
||||
}
|
||||
97
Task/IBAN/C/iban.c
Normal file
97
Task/IBAN/C/iban.c
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
#include <alloca.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define V(cc, exp) if (!strncmp(iban, cc, 2)) return len == exp
|
||||
|
||||
/* Validate country code against expected length. */
|
||||
int valid_cc(const char *iban, int len)
|
||||
{
|
||||
V("AL", 28); V("AD", 24); V("AT", 20); V("AZ", 28); V("BE", 16); V("BH", 22); V("BA", 20); V("BR", 29);
|
||||
V("BG", 22); V("CR", 21); V("HR", 21); V("CY", 28); V("CZ", 24); V("DK", 18); V("DO", 28); V("EE", 20);
|
||||
V("FO", 18); V("FI", 18); V("FR", 27); V("GE", 22); V("DE", 22); V("GI", 23); V("GR", 27); V("GL", 18);
|
||||
V("GT", 28); V("HU", 28); V("IS", 26); V("IE", 22); V("IL", 23); V("IT", 27); V("KZ", 20); V("KW", 30);
|
||||
V("LV", 21); V("LB", 28); V("LI", 21); V("LT", 20); V("LU", 20); V("MK", 19); V("MT", 31); V("MR", 27);
|
||||
V("MU", 30); V("MC", 27); V("MD", 24); V("ME", 22); V("NL", 18); V("NO", 15); V("PK", 24); V("PS", 29);
|
||||
V("PL", 28); V("PT", 25); V("RO", 24); V("SM", 27); V("SA", 24); V("RS", 22); V("SK", 24); V("SI", 19);
|
||||
V("ES", 24); V("SE", 24); V("CH", 21); V("TN", 24); V("TR", 26); V("AE", 23); V("GB", 22); V("VG", 24);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Remove blanks from s in-place, return its new length. */
|
||||
int strip(char *s)
|
||||
{
|
||||
int i = -1, m = 0;
|
||||
|
||||
while(s[++i]) {
|
||||
s[i - m] = s[i];
|
||||
m += s[i] <= 32;
|
||||
}
|
||||
|
||||
s[i - m] = 0;
|
||||
return i - m;
|
||||
}
|
||||
|
||||
/* Calculate the mod 97 of an arbitrarily large number (as a string). */
|
||||
int mod97(const char *s, int len)
|
||||
{
|
||||
int i, j, parts = len / 7;
|
||||
char rem[10] = "00";
|
||||
|
||||
for (i = 1; i <= parts + (len % 7 != 0); ++i) {
|
||||
strncpy(rem + 2, s + (i - 1) * 7, 7);
|
||||
j = atoi(rem) % 97;
|
||||
rem[0] = j / 10 + '0';
|
||||
rem[1] = j % 10 + '0';
|
||||
}
|
||||
|
||||
return atoi(rem) % 97;
|
||||
}
|
||||
|
||||
int valid_iban(char *iban)
|
||||
{
|
||||
int i, j, l = 0, sz = strip(iban);
|
||||
char *rot, *trans;
|
||||
|
||||
/* Ensure upper alphanumeric input and count letters. */
|
||||
for (i = 0; i < sz; ++i) {
|
||||
if (!isdigit(iban[i]) && !isupper(iban[i]))
|
||||
return 0;
|
||||
l += !!isupper(iban[i]);
|
||||
}
|
||||
|
||||
if (!valid_cc(iban, sz))
|
||||
return 0;
|
||||
|
||||
/* Move the first four characters to the end. */
|
||||
rot = alloca(sz);
|
||||
strcpy(rot, iban + 4);
|
||||
strncpy(rot + sz - 4, iban, 4);
|
||||
|
||||
/* Allocate space for the transformed IBAN. */
|
||||
trans = alloca(sz + l);
|
||||
trans[sz + l] = 0;
|
||||
|
||||
/* Convert A to 10, B to 11, etc. */
|
||||
for (i = j = 0; i < sz; ++i, ++j) {
|
||||
if (isdigit(rot[i]))
|
||||
trans[j] = rot[i];
|
||||
else {
|
||||
trans[j] = (rot[i] - 55) / 10 + '0';
|
||||
trans[++j] = (rot[i] - 55) % 10 + '0';
|
||||
}
|
||||
}
|
||||
|
||||
return mod97(trans, sz + l) == 1;
|
||||
}
|
||||
|
||||
int main(int _, char **argv)
|
||||
{
|
||||
while (--_, *++argv)
|
||||
printf("%s is %svalid.\n", *argv, valid_iban(*argv) ? "" : "in");
|
||||
|
||||
return 0;
|
||||
}
|
||||
161
Task/IBAN/COBOL/iban.cobol
Normal file
161
Task/IBAN/COBOL/iban.cobol
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
IDENTIFICATION DIVISION.
|
||||
PROGRAM-ID. iban-main.
|
||||
|
||||
DATA DIVISION.
|
||||
WORKING-STORAGE SECTION.
|
||||
01 iban PIC X(50).
|
||||
01 iban-flag PIC X.
|
||||
88 is-valid VALUE "Y", FALSE "N".
|
||||
|
||||
PROCEDURE DIVISION.
|
||||
main-line.
|
||||
MOVE "GB82 WEST 1234 5698 7654 32" TO iban
|
||||
PERFORM display-validity
|
||||
|
||||
MOVE "GB82 TEST 1234 5698 7654 32" TO iban
|
||||
PERFORM display-validity
|
||||
|
||||
GOBACK
|
||||
.
|
||||
display-validity.
|
||||
CALL "validate-iban" USING CONTENT iban, REFERENCE iban-flag
|
||||
IF is-valid
|
||||
DISPLAY FUNCTION TRIM(iban) " is valid."
|
||||
ELSE
|
||||
DISPLAY FUNCTION TRIM(iban) " is not valid."
|
||||
END-IF
|
||||
.
|
||||
END PROGRAM iban-main.
|
||||
|
||||
|
||||
IDENTIFICATION DIVISION.
|
||||
PROGRAM-ID. validate-iban.
|
||||
|
||||
DATA DIVISION.
|
||||
WORKING-STORAGE SECTION.
|
||||
01 country-lengths-area VALUE "AD24AE23AL28AT20AZ28BA20BE16"
|
||||
& "BG22BH22BR29CH21CR21CY28CZ24DE22DK18DO28EE20ES24FI18FO18F"
|
||||
& "R27GB22GE22GI23GL18GR27GT28HR21HU28IE22IL23IS26IT27KW30KZ"
|
||||
& "20LB28LI21LT20LU20LV21MC27MD24ME22MK19MR27MT31MU30NL18NO1"
|
||||
& "5PK24PL28PS29PT25RO24RS22SA24SE24SI19SK24SM27TN24TR26VG24"
|
||||
.
|
||||
03 country-lengths OCCURS 64 TIMES
|
||||
INDEXED BY country-lengths-idx.
|
||||
05 country-code PIC XX.
|
||||
05 country-len PIC 99.
|
||||
|
||||
01 offset PIC 99.
|
||||
|
||||
01 i PIC 99.
|
||||
|
||||
01 len PIC 99.
|
||||
|
||||
LINKAGE SECTION.
|
||||
01 iban PIC X(50).
|
||||
|
||||
01 valid-flag PIC X.
|
||||
88 is-valid VALUE "Y", FALSE "N".
|
||||
|
||||
PROCEDURE DIVISION USING iban, valid-flag.
|
||||
MOVE FUNCTION UPPER-CASE(iban) TO iban
|
||||
CALL "remove-spaces" USING iban
|
||||
|
||||
*> Check if country-code and length are correct
|
||||
INITIALIZE len
|
||||
INSPECT iban TALLYING len FOR CHARACTERS BEFORE SPACE
|
||||
SET country-lengths-idx TO 1
|
||||
SEARCH country-lengths
|
||||
AT END
|
||||
SET is-valid TO FALSE
|
||||
GOBACK
|
||||
|
||||
WHEN country-code (country-lengths-idx) = iban (1:2)
|
||||
IF country-len (country-lengths-idx) NOT = len
|
||||
SET is-valid TO FALSE
|
||||
GOBACK
|
||||
END-IF
|
||||
END-SEARCH
|
||||
|
||||
CALL "create-iban-number" USING CONTENT len, REFERENCE iban
|
||||
|
||||
*> Mod 97 number formed.
|
||||
IF FUNCTION MOD(iban, 97) = 1
|
||||
SET is-valid TO TRUE
|
||||
ELSE
|
||||
SET is-valid TO FALSE
|
||||
END-IF
|
||||
.
|
||||
|
||||
IDENTIFICATION DIVISION.
|
||||
PROGRAM-ID. remove-spaces.
|
||||
|
||||
DATA DIVISION.
|
||||
WORKING-STORAGE SECTION.
|
||||
01 i PIC 99.
|
||||
01 offset PIC 99.
|
||||
|
||||
LINKAGE SECTION.
|
||||
01 str PIC X(50).
|
||||
|
||||
PROCEDURE DIVISION USING str.
|
||||
INITIALIZE offset
|
||||
PERFORM VARYING i FROM 1 BY 1 UNTIL i > 50
|
||||
EVALUATE TRUE
|
||||
WHEN str (i:1) = SPACE
|
||||
ADD 1 TO offset
|
||||
|
||||
WHEN offset NOT = ZERO
|
||||
MOVE str (i:1) TO str (i - offset:1)
|
||||
END-EVALUATE
|
||||
END-PERFORM
|
||||
MOVE SPACES TO str (50 - offset + 1:)
|
||||
.
|
||||
END PROGRAM remove-spaces.
|
||||
|
||||
|
||||
IDENTIFICATION DIVISION.
|
||||
PROGRAM-ID. create-iban-number.
|
||||
|
||||
DATA DIVISION.
|
||||
WORKING-STORAGE SECTION.
|
||||
01 first-four PIC X(4).
|
||||
|
||||
01 iban-num PIC X(50).
|
||||
01 digit-num PIC 99 VALUE 1.
|
||||
|
||||
01 i PIC 99.
|
||||
|
||||
01 letter-num PIC 99.
|
||||
|
||||
LINKAGE SECTION.
|
||||
01 len PIC 99.
|
||||
|
||||
01 iban PIC X(50).
|
||||
|
||||
PROCEDURE DIVISION USING len, iban.
|
||||
*> Move characters into final positions.
|
||||
MOVE iban (1:4) TO first-four
|
||||
MOVE iban (5:) TO iban
|
||||
MOVE first-four TO iban (len - 3:)
|
||||
|
||||
*> Convert letters to numbers.
|
||||
INITIALIZE iban-num, digit-num ALL TO VALUE
|
||||
PERFORM VARYING i FROM 1 BY 1
|
||||
UNTIL i > len OR iban (i:1) = SPACE
|
||||
IF iban (i:1) IS NUMERIC
|
||||
MOVE iban (i:1) TO iban-num (digit-num:1)
|
||||
ADD 1 TO digit-num
|
||||
ELSE
|
||||
COMPUTE letter-num =
|
||||
FUNCTION ORD(iban (i:1)) - FUNCTION ORD("A") + 10
|
||||
MOVE letter-num TO iban-num (digit-num:2)
|
||||
ADD 2 TO digit-num
|
||||
END-IF
|
||||
END-PERFORM
|
||||
|
||||
MOVE iban-num TO iban
|
||||
.
|
||||
|
||||
END PROGRAM create-iban-number.
|
||||
|
||||
END PROGRAM validate-iban.
|
||||
98
Task/IBAN/Cache-ObjectScript/iban.cos
Normal file
98
Task/IBAN/Cache-ObjectScript/iban.cos
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
Class Utils.Validate [ Abstract ]
|
||||
{
|
||||
|
||||
ClassMethod VerifyIBAN(pIBAN As %String = "") As %Boolean
|
||||
{
|
||||
// remove spaces and define parts
|
||||
Set iban=$Translate(pIBAN, " ")
|
||||
Set cc=$Extract(iban, 1, 2)
|
||||
Set cd=$Extract(iban, 3, 4)
|
||||
Set bban=$Extract(iban, 5, *)
|
||||
|
||||
// ensure IBAN is correct format
|
||||
If $Match(iban, ..GetIBANPattern(cc))=0 Quit 0
|
||||
|
||||
// compare result and return
|
||||
Quit cd=..GetIBANCheckDigit(cc, bban)
|
||||
}
|
||||
|
||||
ClassMethod GetIBANCheckDigit(pCC As %String, pBBAN As %String) As %Integer [ Internal, Private ]
|
||||
{
|
||||
Set str=pBBAN_pCC_"00"
|
||||
For i=1:1 {
|
||||
Set chr=$Extract(str, i) If chr="" Quit
|
||||
If chr?1U Set $Extract(str, i)=$ASCII(chr)-55
|
||||
}
|
||||
Set cd=98-..GetModulus(str, 97)
|
||||
Quit $Select($Length(cd)=2: cd, 1: "0"_cd)
|
||||
}
|
||||
|
||||
ClassMethod GetModulus(pNum As %Integer, pDiv As %Integer) As %Integer [ Internal, Private ]
|
||||
{
|
||||
While $Length(pNum)>9 {
|
||||
Set $Extract(pNum, 1, 9)=$Extract(pNum, 1, 9)#pDiv
|
||||
}
|
||||
Quit pNum#pDiv
|
||||
}
|
||||
|
||||
ClassMethod GetIBANPattern(pCC As %String = "") As %String [ Internal, Private ]
|
||||
{
|
||||
Quit $Case(pCC,
|
||||
"AL": "^AL\d{10}[0-9A-Z]{16}$",
|
||||
"AD": "^AD\d{10}[0-9A-Z]{12}$",
|
||||
"AT": "^AT\d{18}$",
|
||||
"BH": "^BH\d{2}[A-Z]{4}[0-9A-Z]{14}$",
|
||||
"BE": "^BE\d{14}$",
|
||||
"BA": "^BA\d{18}$",
|
||||
"BG": "^BG\d{2}[A-Z]{4}\d{6}[0-9A-Z]{8}$",
|
||||
"HR": "^HR\d{19}$",
|
||||
"CY": "^CY\d{10}[0-9A-Z]{16}$",
|
||||
"CZ": "^CZ\d{22}$",
|
||||
"DK": "^DK\d{16}$|^FO\d{16}$|^GL\d{16}$",
|
||||
"DO": "^DO\d{2}[0-9A-Z]{4}\d{20}$",
|
||||
"EE": "^EE\d{18}$",
|
||||
"FI": "^FI\d{16}$",
|
||||
"FR": "^FR\d{12}[0-9A-Z]{11}\d{2}$",
|
||||
"GE": "^GE\d{2}[A-Z]{2}\d{16}$",
|
||||
"DE": "^DE\d{20}$",
|
||||
"GI": "^GI\d{2}[A-Z]{4}[0-9A-Z]{15}$",
|
||||
"GR": "^GR\d{9}[0-9A-Z]{16}$",
|
||||
"HU": "^HU\d{26}$",
|
||||
"IS": "^IS\d{24}$",
|
||||
"IE": "^IE\d{2}[A-Z]{4}\d{14}$",
|
||||
"IL": "^IL\d{21}$",
|
||||
"IT": "^IT\d{2}[A-Z]\d{10}[0-9A-Z]{12}$",
|
||||
"KZ": "^[A-Z]{2}\d{5}[0-9A-Z]{13}$",
|
||||
"KW": "^KW\d{2}[A-Z]{4}22!$",
|
||||
"LV": "^LV\d{2}[A-Z]{4}[0-9A-Z]{13}$",
|
||||
"LB": "^LB\d{6}[0-9A-Z]{20}$",
|
||||
"LI": "^LI\d{7}[0-9A-Z]{12}$",
|
||||
"LT": "^LT\d{18}$",
|
||||
"LU": "^LU\d{5}[0-9A-Z]{13}$",
|
||||
"MK": "^MK\d{5}[0-9A-Z]{10}\d{2}$",
|
||||
"MT": "^MT\d{2}[A-Z]{4}\d{5}[0-9A-Z]{18}$",
|
||||
"MR": "^MR13\d{23}$",
|
||||
"MU": "^MU\d{2}[A-Z]{4}\d{19}[A-Z]{3}$",
|
||||
"MC": "^MC\d{12}[0-9A-Z]{11}\d{2}$",
|
||||
"ME": "^ME\d{20}$",
|
||||
"NL": "^NL\d{2}[A-Z]{4}\d{10}$",
|
||||
"NO": "^NO\d{13}$",
|
||||
"PL": "^PL\d{10}[0-9A-Z]{,16}n$",
|
||||
"PT": "^PT\d{23}$",
|
||||
"RO": "^RO\d{2}[A-Z]{4}[0-9A-Z]{16}$",
|
||||
"SM": "^SM\d{2}[A-Z]\d{10}[0-9A-Z]{12}$",
|
||||
"SA": "^SA\d{4}[0-9A-Z]{18}$",
|
||||
"RS": "^RS\d{20}$",
|
||||
"SK": "^SK\d{22}$",
|
||||
"SI": "^SI\d{17}$",
|
||||
"ES": "^ES\d{22}$",
|
||||
"SE": "^SE\d{22}$",
|
||||
"CH": "^CH\d{7}[0-9A-Z]{12}$",
|
||||
"TN": "^TN59\d{20}$",
|
||||
"TR": "^TR\d{7}[0-9A-Z]{17}$",
|
||||
"AE": "^AE\d{21}$",
|
||||
"GB": "^GB\d{2}[A-Z]{4}\d{14}$",
|
||||
: " ")
|
||||
}
|
||||
|
||||
}
|
||||
24
Task/IBAN/Clojure/iban.clj
Normal file
24
Task/IBAN/Clojure/iban.clj
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
(def explen
|
||||
{"AL" 28 "AD" 24 "AT" 20 "AZ" 28 "BE" 16 "BH" 22 "BA" 20 "BR" 29
|
||||
"BG" 22 "CR" 21 "HR" 21 "CY" 28 "CZ" 24 "DK" 18 "DO" 28 "EE" 20
|
||||
"FO" 18 "FI" 18 "FR" 27 "GE" 22 "DE" 22 "GI" 23 "GR" 27 "GL" 18
|
||||
"GT" 28 "HU" 28 "IS" 26 "IE" 22 "IL" 23 "IT" 27 "KZ" 20 "KW" 30
|
||||
"LV" 21 "LB" 28 "LI" 21 "LT" 20 "LU" 20 "MK" 19 "MT" 31 "MR" 27
|
||||
"MU" 30 "MC" 27 "MD" 24 "ME" 22 "NL" 18 "NO" 15 "PK" 24 "PS" 29
|
||||
"PL" 28 "PT" 25 "RO" 24 "SM" 27 "SA" 24 "RS" 22 "SK" 24 "SI" 19
|
||||
"ES" 24 "SE" 24 "CH" 21 "TN" 24 "TR" 26 "AE" 23 "GB" 22 "VG" 24})
|
||||
|
||||
(defn valid-iban? [iban]
|
||||
(let [iban (apply str (remove #{\space \tab} iban))]
|
||||
(cond
|
||||
; Ensure upper alphanumeric input.
|
||||
(not (re-find #"^[\dA-Z]+$" iban)) false
|
||||
; Validate country code against expected length.
|
||||
(not= (explen (subs iban 0 2)) (count iban)) false
|
||||
:else
|
||||
(let [rot (flatten (apply conj (split-at 4 iban)))
|
||||
trans (map #(read-string (str "36r" %)) rot)]
|
||||
(= 1 (mod (bigint (apply str trans)) 97))))))
|
||||
|
||||
(prn (valid-iban? "GB82 WEST 1234 5698 7654 32") ; true
|
||||
(valid-iban? "GB82 TEST 1234 5698 7654 32")) ; false
|
||||
60
Task/IBAN/Common-Lisp/iban.lisp
Normal file
60
Task/IBAN/Common-Lisp/iban.lisp
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
;;
|
||||
;; List of the IBAN code lengths per country.
|
||||
;;
|
||||
(defvar *IBAN-code-length* '((15 . ("NO"))
|
||||
(16 . ("BE"))
|
||||
(18 . ("DK" "FO" "FI" "GL" "NL"))
|
||||
(19 . ("MK" "SI"))
|
||||
(20 . ("AT" "BA" "EE" "KZ" "LT" "LU"))
|
||||
(21 . ("CR" "HR" "LV" "LI" "CH"))
|
||||
(22 . ("BH" "BG" "GE" "DE" "IE" "ME" "RS" "GB"))
|
||||
(23 . ("GI" "IL" "AE"))
|
||||
(24 . ("AD" "CZ" "MD" "PK" "RO" "SA" "SK" "ES" "SE" "TN" "VG"))
|
||||
(25 . ("PT"))
|
||||
(26 . ("IS" "TR"))
|
||||
(27 . ("FR" "GR" "IT" "MR" "MC" "SM"))
|
||||
(28 . ("AL" "AZ" "CY" "DO" "GT" "HU" "LB" "PL"))
|
||||
(29 . ("BR" "PS"))
|
||||
(30 . ("KW" "MU"))
|
||||
(31 . ("MT"))))
|
||||
|
||||
;;
|
||||
;; The IBAN-character function verifies whether the number contains the correct characters only. There is
|
||||
;; a built in function to verify for alphanumeric characters, but it includes characters beyond ASCII range.
|
||||
;;
|
||||
(defun IBAN-characters (iban)
|
||||
(flet ((valid-alphanum (ch) (or (and (char<= #\A ch) (char>= #\Z ch)) (and (char<= #\0 ch) (char>= #\9 ch)))))
|
||||
(loop :for char :across iban
|
||||
:finally (return t)
|
||||
:do
|
||||
(when (not (valid-alphanum char))
|
||||
(return nil)))))
|
||||
|
||||
;;
|
||||
;; The function IBAN-length verifies that the length of the number is correct. The code lengths
|
||||
;; are retrieved from the table *IBAN-code-lengths*.
|
||||
;;
|
||||
(defun IBAN-length (iban)
|
||||
(loop :for (len . country) :in *IBAN-code-length*
|
||||
:with iban-country = (subseq iban 0 2)
|
||||
:do
|
||||
(when (find iban-country country :test #'string=) (return (= len (length iban))))))
|
||||
|
||||
;;
|
||||
;; The function IBAN-to-integer converts an IBAN code into an integer number.
|
||||
;; Note: The conversion follows the rules stated in the wiki page.
|
||||
;;
|
||||
(defun IBAN-to-integer (iban)
|
||||
(let ((character-base (- (char-code #\A) 10)))
|
||||
(parse-integer
|
||||
(format nil "~{~a~}" (map 'list #'(lambda(X) (if (alpha-char-p X) (- (char-code X) character-base) X ))
|
||||
(concatenate 'string (subseq iban 4) (subseq iban 0 4)))))))
|
||||
;;
|
||||
;; The function IBAN-verify checks that the code contains right character set, has the
|
||||
;; country specific length and has the correct check sum.
|
||||
;;
|
||||
(defun IBAN-verify (iban)
|
||||
(flet ((validp (X) (and (IBAN-characters X)
|
||||
(IBAN-length X)
|
||||
(= 1 (mod (IBAN-to-integer X) 97)))))
|
||||
(validp (remove #\Space iban))))
|
||||
37
Task/IBAN/D/iban.d
Normal file
37
Task/IBAN/D/iban.d
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
import std.stdio, std.string, std.regex, std.conv, std.bigint,
|
||||
std.algorithm, std.ascii;
|
||||
|
||||
immutable int[string] country2len;
|
||||
static this() {
|
||||
country2len = ["AL":28, "AD":24, "AT":20, "AZ":28, "BE":16,
|
||||
"BH":22, "BA":20, "BR":29, "BG":22, "CR":21, "HR":21, "CY":28,
|
||||
"CZ":24, "DK":18, "DO":28, "EE":20, "FO":18, "FI":18, "FR":27,
|
||||
"GE":22, "DE":22, "GI":23, "GR":27, "GL":18, "GT":28, "HU":28,
|
||||
"IS":26, "IE":22, "IL":23, "IT":27, "KZ":20, "KW":30, "LV":21,
|
||||
"LB":28, "LI":21, "LT":20, "LU":20, "MK":19, "MT":31, "MR":27,
|
||||
"MU":30, "MC":27, "MD":24, "ME":22, "NL":18, "NO":15, "PK":24,
|
||||
"PS":29, "PL":28, "PT":25, "RO":24, "SM":27, "SA":24, "RS":22,
|
||||
"SK":24, "SI":19, "ES":24, "SE":24, "CH":21, "TN":24, "TR":26,
|
||||
"AE":23, "GB":22, "VG":24];
|
||||
}
|
||||
|
||||
bool validIBAN(string iban) {
|
||||
// Ensure upper alphanumeric input.
|
||||
iban = iban.removechars(whitespace);
|
||||
if (!iban.match(r"^[\dA-Z]+$"))
|
||||
return false;
|
||||
|
||||
// Validate country code against expected length.
|
||||
if (iban.length != country2len[iban[0 .. 2]])
|
||||
return false;
|
||||
|
||||
// Shift and convert. BASE 36: 0..9,A..Z -> 0..35.
|
||||
iban = iban[4 .. $] ~ iban[0 .. 4];
|
||||
return iban.map!(c => [c].to!int(36).text).join.BigInt % 97 == 1;
|
||||
}
|
||||
|
||||
void main() {
|
||||
foreach (account; ["GB82 WEST 1234 5698 7654 32",
|
||||
"GB82 TEST 1234 5698 7654 32"])
|
||||
writefln("%s validation is: %s", account, account.validIBAN);
|
||||
}
|
||||
61
Task/IBAN/Go/iban.go
Normal file
61
Task/IBAN/Go/iban.go
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"strconv"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
var lCode = map[string]int {
|
||||
"AL": 28, "AD": 24, "AT": 20, "AZ": 28, "BE": 16, "BH": 22, "BA": 20, "BR": 29,
|
||||
"BG": 22, "CR": 21, "HR": 21, "CY": 28, "CZ": 24, "DK": 18, "DO": 28, "EE": 20,
|
||||
"FO": 18, "FI": 18, "FR": 27, "GE": 22, "DE": 22, "GI": 23, "GR": 27, "GL": 18,
|
||||
"GT": 28, "HU": 28, "IS": 26, "IE": 22, "IL": 23, "IT": 27, "KZ": 20, "KW": 30,
|
||||
"LV": 21, "LB": 28, "LI": 21, "LT": 20, "LU": 20, "MK": 19, "MT": 31, "MR": 27,
|
||||
"MU": 30, "MC": 27, "MD": 24, "ME": 22, "NL": 18, "NO": 15, "PK": 24, "PS": 29,
|
||||
"PL": 28, "PT": 25, "RO": 24, "SM": 27, "SA": 24, "RS": 22, "SK": 24, "SI": 19,
|
||||
"ES": 24, "SE": 24, "CH": 21, "TN": 24, "TR": 26, "AE": 23, "GB": 22, "VG": 24,
|
||||
}
|
||||
|
||||
var sCode = map[string]int {
|
||||
"1": 1, "2": 2, "3": 3, "4": 4, "5": 5, "6": 6, "7": 7, "8": 8, "9": 9,
|
||||
"A": 10, "B": 11, "C": 12, "D": 13, "E": 14, "F": 15, "G": 16,
|
||||
"H": 17, "I": 18, "J": 19, "K": 20, "L": 21, "M": 22, "N": 23,
|
||||
"O": 24, "P": 25, "Q": 26, "R": 27, "S": 28, "T": 29, "U": 30,
|
||||
"V": 31, "W": 32, "X": 33, "Y": 34, "Z": 35,
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
var iban string
|
||||
var r, s, t, st []string
|
||||
u := new(big.Int)
|
||||
v := new(big.Int)
|
||||
w := new(big.Int)
|
||||
|
||||
iban = "GB82 TEST 1234 5698 7654 32"
|
||||
r = strings.Split(iban, " ")
|
||||
s = strings.Split(r[0], "")
|
||||
t = strings.Split(r[1], "")
|
||||
|
||||
st = []string{ strconv.Itoa(sCode[t[0]]),
|
||||
strconv.Itoa(sCode[t[1]]),
|
||||
strconv.Itoa(sCode[t[2]]),
|
||||
strconv.Itoa(sCode[t[3]]),
|
||||
strings.Join(r[2:6], ""),
|
||||
strconv.Itoa(sCode[s[0]]),
|
||||
strconv.Itoa(sCode[s[1]]),
|
||||
strings.Join(s[2:4], ""),
|
||||
}
|
||||
|
||||
u.SetString(strings.Join(st, ""), 10)
|
||||
v.SetInt64(97)
|
||||
w.Mod(u, v)
|
||||
|
||||
if w.Uint64() == 1 && lCode[strings.Join(s[0:2], "")] == len(strings.Join(r, "")) {
|
||||
fmt.Printf("IBAN %s looks good!\n", iban)
|
||||
} else {
|
||||
fmt.Printf("IBAN %s looks wrong!\n", iban)
|
||||
}
|
||||
}
|
||||
16
Task/IBAN/Groovy/iban-1.groovy
Normal file
16
Task/IBAN/Groovy/iban-1.groovy
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
def validateIBAN(String iban) {
|
||||
def iso = [AL: 28, AD: 24, AT: 20, AZ: 28, BE: 16, BH: 22, BA: 20, BR: 29, BG: 22,
|
||||
HR: 21, CY: 28, CZ: 24, DK: 18, DO: 28, EE: 20, FO: 18, FI: 18, FR: 27, GE: 22, DE: 22, GI: 23,
|
||||
GL: 18, GT: 28, HU: 28, IS: 26, IE: 22, IL: 23, IT: 27, KZ: 20, KW: 30, LV: 21, LB: 28, LI: 21,
|
||||
LT: 20, LU: 20, MK: 19, MT: 31, MR: 27, MU: 30, MC: 27, MD: 24, ME: 22, NL: 18, NO: 15, PK: 24,
|
||||
PS: 29, PL: 28, PT: 25, RO: 24, SM: 27, SA: 24, RS: 22, SK: 24, SI: 19, ES: 24, SE: 24, CH: 21,
|
||||
TN: 24, TR: 26, AE: 23, GB: 22, VG: 24, GR: 27, CR: 21]
|
||||
|
||||
iban = iban.replaceAll(/\s/, '').toUpperCase()
|
||||
if (iban.size() < 4 || iso[iban[0..1]] != iban.size()) return false
|
||||
|
||||
iban = iban[4..-1] + iban[0..<4]
|
||||
|
||||
def number = iban.collect { Character.digit(it as char, 36) }.join('')
|
||||
(number as BigInteger).mod(97) == 1
|
||||
}
|
||||
7
Task/IBAN/Groovy/iban-2.groovy
Normal file
7
Task/IBAN/Groovy/iban-2.groovy
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
[ 'GB82 WEST 1234 5698 7654 32',
|
||||
'GB82 TEST 1234 5698 7654 32',
|
||||
'GB81 WEST 1234 5698 7654 32',
|
||||
'SA03 8000 0000 6080 1016 7519',
|
||||
'CH93 0076 2011 6238 5295 7' ].each { iban ->
|
||||
println "$iban is ${validateIBAN(iban) ? 'valid' : 'invalid'}"
|
||||
}
|
||||
55
Task/IBAN/Haskell/iban.hs
Normal file
55
Task/IBAN/Haskell/iban.hs
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
import Data.Char (toUpper)
|
||||
|
||||
validateIBAN :: String -> Either String String
|
||||
validateIBAN [] = Left "No IBAN number."
|
||||
validateIBAN xs =
|
||||
case lookupCountry of
|
||||
Nothing -> invalidBecause "Country does not exist."
|
||||
Just l -> if length normalized /= l
|
||||
then invalidBecause "Number length does not match."
|
||||
else check
|
||||
where
|
||||
-- remove blanks and make all letters uppercase
|
||||
normalized = map toUpper $ concat $ words xs
|
||||
-- get the country code
|
||||
country = take 2 normalized
|
||||
-- search number length
|
||||
lookupCountry = lookup country countries
|
||||
countries :: [(String, Int)]
|
||||
countries = zip (words "AL AT BE BA BG HR CZ DO FO FR DE GR GT \
|
||||
\IS IL KZ LV LI LU MT MU MD NL PK PL RO SA SK ES CH TR GB \
|
||||
\AD AZ BH BR CR CY DK EE FI GE GI GL HU IE IT KW LB LT MK \
|
||||
\MR MC ME NO PS PT SM RS SI SE TN AE VG")
|
||||
[28,20,16,20,22,21,24,28,18,27,22,27,28,26,23,20,21,21,20,
|
||||
31,30,24,18,24,28,24,24,24,24,21,26,22,24,28,22,29,21,28,18,
|
||||
20,18,22,23,18,28,22,27,30,28,20,19,27,27,22,15,29,25,27,22,
|
||||
19,24,24,23,24]
|
||||
digits = ['0'..'9']
|
||||
letters = ['A'..'Z']
|
||||
-- letters to be replaced
|
||||
replDigits = zip letters $ map show [10..35]
|
||||
-- digits and letters allowed in the IBAN number
|
||||
validDigits = digits ++ letters
|
||||
-- see if all digits and letters in the IBAN number are allowed
|
||||
sane = all (`elem` validDigits) normalized
|
||||
-- take the first 4 digits from the number and put them at its end
|
||||
(p1, p2) = splitAt 4 normalized
|
||||
p3 = p2 ++ p1
|
||||
-- convert the letters to numbers and
|
||||
-- convert the result to an integer
|
||||
p4 :: Integer
|
||||
p4 = read $ concat $ convertLetters p3
|
||||
convertLetters [] = []
|
||||
convertLetters (x:xs)
|
||||
| x `elem` digits = [x] : convertLetters xs
|
||||
| otherwise = let (Just ys) = lookup x replDigits
|
||||
in ys : convertLetters xs
|
||||
-- see if the number is valid
|
||||
check = if sane
|
||||
then if p4 `mod` 97 == 1
|
||||
then Right xs
|
||||
else invalidBecause "Validation failed."
|
||||
else invalidBecause "Number contains illegal digits."
|
||||
|
||||
invalidBecause reason = Left $ "Invalid IBAN number " ++ xs ++
|
||||
": " ++ reason
|
||||
34
Task/IBAN/J/iban.j
Normal file
34
Task/IBAN/J/iban.j
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
NB. delete any blank characters
|
||||
delblk =. #~ ' '&~:
|
||||
NB. rearrange
|
||||
rot =. '00' ,~ 2&}. @: (2&|.)
|
||||
NB. characters -> "digits"
|
||||
dig =. a. {~ (a.i.'0')+i.10
|
||||
dig =. dig,a. {~ (a.i.'A')+i.26
|
||||
todig =. dig&i.
|
||||
coded =. [: ". 'x' ,~ delblk @: ": @: todig
|
||||
|
||||
NB. calculate check sum
|
||||
cs =: 98 - 97 | coded @: rot @: delblk f.
|
||||
|
||||
NB. check sum as text
|
||||
cstxt =. _2{. '0', [: ": cs
|
||||
NB. replace first two characters
|
||||
chgps =. [,2}.]
|
||||
NB. shift country code
|
||||
rotlc =. 2&|.
|
||||
NB. insert check digits (position 3 and 4)
|
||||
insertps =. chgps &.rotlc
|
||||
|
||||
NB. IBAN with newly calculated check digits
|
||||
ibancd =: (cstxt insertps ]) f.
|
||||
|
||||
NB. check / generate check digits
|
||||
ibancheck =: ] (]`('ok'"_) @. -:) ibancd
|
||||
|
||||
NB. groups of four characters
|
||||
insertblk =. #~ # $ 1 1 1 1j1"_
|
||||
quads =: insertblk @: delblk f.
|
||||
|
||||
NB. IBAN
|
||||
iban =: quads @: ibancheck
|
||||
47
Task/IBAN/Java/iban.java
Normal file
47
Task/IBAN/Java/iban.java
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
import java.math.BigInteger;
|
||||
import java.util.*;
|
||||
|
||||
public class IBAN {
|
||||
static final Map<String, Integer> isoPairs;
|
||||
|
||||
static final String iso = "AL28 AD24 AT20 AZ28 BE16 BH22 BA20 BR29 BG22 "
|
||||
+ "HR21 CY28 CZ24 DK18 DO28 EE20 FO18 FI18 FR27 GE22 DE22 GI23 "
|
||||
+ "GL18 GT28 HU28 IS26 IE22 IL23 IT27 KZ20 KW30 LV21 LB28 LI21 "
|
||||
+ "LT20 LU20 MK19 MT31 MR27 MU30 MC27 MD24 ME22 NL18 NO15 PK24 "
|
||||
+ "PS29 PL28 PT25 RO24 SM27 SA24 RS22 SK24 SI19 ES24 SE24 CH21 "
|
||||
+ "TN24 TR26 AE23 GB22 VG24 GR27 CR21";
|
||||
|
||||
static {
|
||||
isoPairs = new HashMap<>();
|
||||
for (String p : iso.split(" "))
|
||||
isoPairs.put(p.substring(0, 2), Integer.parseInt(p.substring(2)));
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
for (String iban : new String[]{"GB82 WEST 1234 5698 7654 32",
|
||||
"GB82 TEST 1234 5698 7654 32",
|
||||
"GB81 WEST 1234 5698 7654 32",
|
||||
"SA03 8000 0000 6080 1016 7519",
|
||||
"CH93 0076 2011 6238 5295 7"})
|
||||
System.out.printf("%s is valid: %s%n%n", iban, validateIBAN(iban));
|
||||
}
|
||||
|
||||
static boolean validateIBAN(String iban) {
|
||||
iban = iban.replaceAll("\\s", "").toUpperCase();
|
||||
|
||||
int len = iban.length();
|
||||
|
||||
if (len < 4 || isoPairs.get(iban.substring(0, 2)) != len)
|
||||
return false;
|
||||
|
||||
iban = iban.substring(4) + iban.substring(0, 4);
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < len; i++)
|
||||
sb.append(Character.digit(iban.charAt(i), 36));
|
||||
|
||||
BigInteger bigInt = new BigInteger(sb.toString());
|
||||
|
||||
return bigInt.mod(new BigInteger("97")).intValue() == 1;
|
||||
}
|
||||
}
|
||||
30
Task/IBAN/MATLAB/iban-1.m
Normal file
30
Task/IBAN/MATLAB/iban-1.m
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
function valid = validateIBAN(iban)
|
||||
% Determine if International Bank Account Number is valid IAW ISO 13616
|
||||
% iban - string containing account number
|
||||
if length(iban) < 5
|
||||
valid = false;
|
||||
else
|
||||
iban(iban == ' ') = ''; % Remove spaces
|
||||
iban = lower([iban(5:end) iban(1:4)])+0; % Rearrange and convert
|
||||
iban(iban > 96 & iban < 123) = iban(iban > 96 & iban < 123)-87; % Letters
|
||||
iban(iban > 47 & iban < 58) = iban(iban > 47 & iban < 58)-48; % Numbers
|
||||
valid = piecewiseMod97(iban) == 1;
|
||||
end
|
||||
end
|
||||
|
||||
function result = piecewiseMod97(x)
|
||||
% Conduct a piecewise version of mod(x, 97) to support large integers
|
||||
% x is a vector of integers
|
||||
x = sprintf('%d', x); % Get to single-digits per index
|
||||
nDig = length(x);
|
||||
i1 = 1;
|
||||
i2 = min(9, nDig);
|
||||
prefix = '';
|
||||
while i1 <= nDig
|
||||
y = str2double([prefix x(i1:i2)]);
|
||||
result = mod(y, 97);
|
||||
prefix = sprintf('%d', result);
|
||||
i1 = i2+1;
|
||||
i2 = min(i1+8, nDig);
|
||||
end
|
||||
end
|
||||
11
Task/IBAN/MATLAB/iban-2.m
Normal file
11
Task/IBAN/MATLAB/iban-2.m
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
tests = {'GB82 WEST 1234 5698 7654 32' ;
|
||||
'GB82 TEST 1234 5698 7654 32' ;
|
||||
'CH93 0076 2011 6238 5295 7' ;
|
||||
'SA03 8000 0000 6080 1016 7519' ;
|
||||
'SA03 1234 5678 9101 1121 3141' ;
|
||||
'GB29 NWBK 6016 1331 9268 19' ;
|
||||
'GB29' ;
|
||||
'GR16 0110 1250 0000 0001 2300 695'};
|
||||
for k = 1:length(tests)
|
||||
fprintf('%s -> %svalid\n', tests{k}, char(~validateIBAN(tests{k}).*'in'))
|
||||
end
|
||||
29
Task/IBAN/Mathematica/iban-1.math
Normal file
29
Task/IBAN/Mathematica/iban-1.math
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
CountryCodes={{"AL",28},{"AD",24},{"AT",20},{"AZ",28},{"BE",16},{"BH",22},{"BA",20},{"BR",29},{"BG",22},{"CR",21},{"HR",21},{"CY",28},{"CZ",24},{"DK",18},{"DO",28},{"EE",20},{"FO",18},{"FI",18},{"FR",27},{"GE",22},{"DE",22},{"GI",23},{"GR",27},{"GL",18},{"GT",28},{"HU",28},{"IS",26},{"IE",22},{"IL",23},{"IT",27},{"KZ",20},{"KW",30},{"LV",21},{"LB",28},{"LI",21},{"LT",20},{"LU",20},{"MK",19},{"MT",31},{"MR",27},{"MU",30},{"MC",27},{"MD",24},{"ME",22},{"NL",18},{"NO",15},{"PK",24},{"PS",29},{"PL",28},{"PT",25},{"RO",24},{"SM",27},{"SA",24},{"RS",22},{"SK",24},{"SI",19},{"ES",24},{"SE",24},{"CH",21},{"TN",24},{"TR",26},{"AE",23},{"GB",22},{"VG",24}};
|
||||
ClearAll[IBANVerify]
|
||||
IBANVerify[input_String]:=Module[{i,cc,rules},
|
||||
i=StringReplace[StringTrim[input],{" "->"","\t"->""}];
|
||||
cc=StringTake[i,2];
|
||||
If[MemberQ[CountryCodes[[All,1]],cc]
|
||||
,
|
||||
cc=Select[CountryCodes,First[#]==cc&][[1,2]];
|
||||
If[cc==StringLength[i]
|
||||
,
|
||||
i=StringRotateLeft[i,4];
|
||||
i=Characters[ToUpperCase[i]];
|
||||
rules=Rule@@@({CharacterRange["A","Z"],Range[10,35]}\[Transpose]);
|
||||
i=i/.rules;
|
||||
i=ToExpression/@i;
|
||||
i=FromDigits[Flatten[IntegerDigits/@i]];
|
||||
If[Mod[i,97]===1
|
||||
,
|
||||
True
|
||||
,
|
||||
False
|
||||
]
|
||||
,
|
||||
False
|
||||
]
|
||||
,
|
||||
False
|
||||
]
|
||||
]
|
||||
4
Task/IBAN/Mathematica/iban-2.math
Normal file
4
Task/IBAN/Mathematica/iban-2.math
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
IBANVerify["GB82 WEST 1234 5698 7654 32"]
|
||||
IBANVerify["GB82 WEST 1234 5698 7654 323"]
|
||||
IBANVerify["GB82 WEST 1234 5698 7654 31"]
|
||||
IBANVerify["XX82 WEST 1234 5698 7654 323"]
|
||||
161
Task/IBAN/Oberon-2/iban.oberon-2
Normal file
161
Task/IBAN/Oberon-2/iban.oberon-2
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
MODULE IBAN;
|
||||
IMPORT
|
||||
Out,
|
||||
Err,
|
||||
ADT:Dictionary,
|
||||
Object:Boxed,
|
||||
Object:BigInt,
|
||||
Object,
|
||||
Strings,
|
||||
IntStr;
|
||||
TYPE
|
||||
IBANLen = Boxed.LongInt;
|
||||
VAR
|
||||
nations: Dictionary.Dictionary(STRING,IBANLen);
|
||||
|
||||
PROCEDURE Check*(iban: ARRAY OF CHAR): BOOLEAN;
|
||||
VAR
|
||||
country,ibanStr: Object.String;
|
||||
nLetter: ARRAY 3 OF CHAR;
|
||||
block: ARRAY 5 OF CHAR;
|
||||
numIban: ARRAY 256 OF CHAR;
|
||||
num,den,res: BigInt.BigInt;
|
||||
ibanLen: IBANLen;
|
||||
i: LONGINT;
|
||||
BEGIN
|
||||
ibanStr := Object.NewLatin1(iban);
|
||||
country := ibanStr.Substring(0,2);
|
||||
IF ~nations.HasKey(country) THEN
|
||||
Err.Object("Country " + country + " has not IBAN codes. ");
|
||||
RETURN FALSE;
|
||||
END;
|
||||
ibanLen := nations.Get(country);
|
||||
|
||||
IF SHORT(ibanLen.value) # Strings.Length(iban) THEN
|
||||
Err.Object("IBAN length incorrect for " + country +". ");
|
||||
RETURN FALSE
|
||||
END;
|
||||
|
||||
block[0] := 0X;
|
||||
Strings.Extract(iban,0,4,block);
|
||||
Strings.Delete(iban,0,4);Strings.Append(block,iban);
|
||||
|
||||
numIban[0] := 0X;
|
||||
FOR i := 0 TO LEN(iban) - 1 DO
|
||||
nLetter[0] := 0X;
|
||||
IF (iban[i] >= 'A') & (iban[i] <= 'Z') THEN
|
||||
IntStr.IntToStr(ORD(iban[i]) - ORD('A') + 10,nLetter);
|
||||
ELSE
|
||||
nLetter[0] := iban[i];
|
||||
nLetter[1] := 0X
|
||||
END;
|
||||
Strings.Append(nLetter,numIban);
|
||||
END;
|
||||
Strings.Append(0X,numIban);
|
||||
|
||||
num := BigInt.New(Object.NewLatin1(numIban),10);
|
||||
den := BigInt.New("97",10);
|
||||
res := num.Mod(den);
|
||||
IF res.Equals(BigInt.one) THEN
|
||||
RETURN TRUE
|
||||
ELSE
|
||||
Err.String("IBAN code check failed. ");
|
||||
RETURN FALSE
|
||||
END
|
||||
END Check;
|
||||
|
||||
PROCEDURE CodeLengthFor*(country: ARRAY OF CHAR): LONGINT;
|
||||
VAR
|
||||
countryStr: Object.String;
|
||||
ibanLen: IBANLen;
|
||||
BEGIN
|
||||
countryStr := Object.NewLatin1(country);
|
||||
ibanLen := Boxed.zeroLongInt;
|
||||
IF nations.HasKey(countryStr) THEN
|
||||
ibanLen := nations.Get(countryStr)
|
||||
END;
|
||||
RETURN ibanLen.value
|
||||
END CodeLengthFor;
|
||||
|
||||
PROCEDURE Test*;
|
||||
PROCEDURE DoCheck(iban: ARRAY OF CHAR);
|
||||
BEGIN
|
||||
Out.String("IBAN[");Out.String(iban);Out.String("]=");Out.Bool(Check(iban));
|
||||
Out.Ln
|
||||
END DoCheck;
|
||||
BEGIN
|
||||
DoCheck("CH9300762011623852957");
|
||||
DoCheck("GB82WEST12345698765432");
|
||||
DoCheck("SA0380000000608010167519");
|
||||
DoCheck("XX0380000000608010167519");
|
||||
END Test;
|
||||
|
||||
BEGIN
|
||||
nations := NEW(Dictionary.Dictionary(STRING,Boxed.LongInt));
|
||||
nations.Set("AL",NEW(IBANLen,28));
|
||||
nations.Set("AD",NEW(IBANLen,24));
|
||||
nations.Set("AT",NEW(IBANLen,20));
|
||||
nations.Set("AZ",NEW(IBANLen,28));
|
||||
nations.Set("BE",NEW(IBANLen,16));
|
||||
nations.Set("BH",NEW(IBANLen,22));
|
||||
nations.Set("BA",NEW(IBANLen,20));
|
||||
nations.Set("BR",NEW(IBANLen,29));
|
||||
nations.Set("BG",NEW(IBANLen,22));
|
||||
nations.Set("CR",NEW(IBANLen,21));
|
||||
nations.Set("HR",NEW(IBANLen,21));
|
||||
nations.Set("CY",NEW(IBANLen,28));
|
||||
nations.Set("CZ",NEW(IBANLen,24));
|
||||
nations.Set("DK",NEW(IBANLen,18));
|
||||
nations.Set("DO",NEW(IBANLen,28));
|
||||
nations.Set("EE",NEW(IBANLen,20));
|
||||
nations.Set("FO",NEW(IBANLen,18));
|
||||
nations.Set("FI",NEW(IBANLen,18));
|
||||
nations.Set("FR",NEW(IBANLen,27));
|
||||
nations.Set("GE",NEW(IBANLen,22));
|
||||
nations.Set("DE",NEW(IBANLen,22));
|
||||
nations.Set("GI",NEW(IBANLen,23));
|
||||
nations.Set("GR",NEW(IBANLen,27));
|
||||
nations.Set("GL",NEW(IBANLen,18));
|
||||
nations.Set("GT",NEW(IBANLen,28));
|
||||
nations.Set("HU",NEW(IBANLen,28));
|
||||
nations.Set("IS",NEW(IBANLen,26));
|
||||
nations.Set("IE",NEW(IBANLen,22));
|
||||
nations.Set("IL",NEW(IBANLen,23));
|
||||
nations.Set("IT",NEW(IBANLen,27));
|
||||
nations.Set("KZ",NEW(IBANLen,20));
|
||||
nations.Set("KW",NEW(IBANLen,30));
|
||||
nations.Set("LV",NEW(IBANLen,21));
|
||||
nations.Set("LB",NEW(IBANLen,28));
|
||||
nations.Set("LI",NEW(IBANLen,21));
|
||||
nations.Set("LT",NEW(IBANLen,20));
|
||||
nations.Set("LU",NEW(IBANLen,20));
|
||||
nations.Set("MK",NEW(IBANLen,19));
|
||||
nations.Set("MT",NEW(IBANLen,31));
|
||||
nations.Set("MR",NEW(IBANLen,27));
|
||||
nations.Set("MU",NEW(IBANLen,30));
|
||||
nations.Set("MC",NEW(IBANLen,27));
|
||||
nations.Set("MD",NEW(IBANLen,24));
|
||||
nations.Set("ME",NEW(IBANLen,22));
|
||||
nations.Set("NL",NEW(IBANLen,18));
|
||||
nations.Set("NO",NEW(IBANLen,15));
|
||||
nations.Set("PK",NEW(IBANLen,24));
|
||||
nations.Set("PS",NEW(IBANLen,29));
|
||||
nations.Set("PL",NEW(IBANLen,28));
|
||||
nations.Set("PT",NEW(IBANLen,25));
|
||||
nations.Set("RO",NEW(IBANLen,24));
|
||||
nations.Set("SM",NEW(IBANLen,27));
|
||||
nations.Set("SA",NEW(IBANLen,24));
|
||||
nations.Set("RS",NEW(IBANLen,22));
|
||||
nations.Set("SK",NEW(IBANLen,24));
|
||||
nations.Set("SI",NEW(IBANLen,19));
|
||||
nations.Set("ES",NEW(IBANLen,24));
|
||||
nations.Set("SE",NEW(IBANLen,24));
|
||||
nations.Set("CH",NEW(IBANLen,21));
|
||||
nations.Set("TN",NEW(IBANLen,24));
|
||||
nations.Set("TR",NEW(IBANLen,26));
|
||||
nations.Set("AE",NEW(IBANLen,23));
|
||||
nations.Set("GB",NEW(IBANLen,22));
|
||||
nations.Set("VG",NEW(IBANLen,24));
|
||||
|
||||
Test;
|
||||
END IBAN.
|
||||
19
Task/IBAN/Perl-6/iban.pl6
Normal file
19
Task/IBAN/Perl-6/iban.pl6
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
subset IBAN of Str where sub ($_ is copy) {
|
||||
s:g/\s//;
|
||||
return False if m/<-[ 0..9 A..Z a..z ]>/ or .chars != <
|
||||
AD 24 AE 23 AL 28 AT 20 AZ 28 BA 20 BE 16 BG 22 BH 22 BR 29 CH 21
|
||||
CR 21 CY 28 CZ 24 DE 22 DK 18 DO 28 EE 20 ES 24 FI 18 FO 18 FR 27
|
||||
GB 22 GE 22 GI 23 GL 18 GR 27 GT 28 HR 21 HU 28 IE 22 IL 23 IS 26
|
||||
IT 27 KW 30 KZ 20 LB 28 LI 21 LT 20 LU 20 LV 21 MC 27 MD 24 ME 22
|
||||
MK 19 MR 27 MT 31 MU 30 NL 18 NO 15 PK 24 PL 28 PS 29 PT 25 RO 24
|
||||
RS 22 SA 24 SE 24 SI 19 SK 24 SM 27 TN 24 TR 26 VG 24
|
||||
>.hash{.substr(0,2).uc};
|
||||
|
||||
s/(.**4)(.+)/$1$0/;
|
||||
return .subst(:g, /\D/, { :36(~$_) }) % 97 == 1;
|
||||
}
|
||||
|
||||
say "$_ is {$_ ~~ IBAN ?? 'valid' !! 'invalid' }" for
|
||||
'GB82 WEST 1234 5698 7654 32',
|
||||
'gb82 west 1234 5698 7654 32',
|
||||
'GB82 TEST 1234 5698 7654 32';
|
||||
48
Task/IBAN/Perl/iban.pl
Normal file
48
Task/IBAN/Perl/iban.pl
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
#!/usr/bin/perl
|
||||
use strict ;
|
||||
use warnings ;
|
||||
use Math::BigInt ;
|
||||
|
||||
my %countrycodelengths = ( "AL" => 28, "AD" => 24, "AT" => 20, "AZ" => 28,
|
||||
"BE" => 16, "BH" => 22, "BA" => 20, "BR" => 29,
|
||||
"BG" => 22, "CR" => 21, "HR" => 21, "CY" => 28,
|
||||
"CZ" => 24, "DK" => 18, "DO" => 28, "EE" => 20,
|
||||
"FO" => 18, "FI" => 18, "FR" => 27, "GE" => 22,
|
||||
"DE" => 22, "GI" => 23, "GR" => 27, "GL" => 18,
|
||||
"GT" => 28, "HU" => 28, "IS" => 26, "IE" => 22,
|
||||
"IL" => 23, "IT" => 27, "KZ" => 20, "KW" => 30,
|
||||
"LV" => 21, "LB" => 28, "LI" => 21, "LT" => 20,
|
||||
"LU" => 20, "MK" => 19, "MT" => 31, "MR" => 27,
|
||||
"MU" => 30, "MC" => 27, "MD" => 24, "ME" => 22,
|
||||
"NL" => 18, "NO" => 15, "PK" => 24, "PS" => 29,
|
||||
"PL" => 28, "PT" => 25, "RO" => 24, "SM" => 27,
|
||||
"SA" => 24, "RS" => 22, "SK" => 24, "SI" => 19,
|
||||
"ES" => 24, "SE" => 24, "CH" => 21, "TN" => 24,
|
||||
"TR" => 26, "AE" => 23, "GB" => 22, "VG" => 24 ) ;
|
||||
sub validate_iban {
|
||||
my $ibanstring = shift ;
|
||||
$ibanstring =~ s/\s+//g ;
|
||||
return 0 unless $ibanstring =~ /[0-9a-zA-Z]+/ ;
|
||||
$ibanstring = uc $ibanstring ;
|
||||
return 0 if ( not exists $countrycodelengths{ substr( $ibanstring , 0 , 2 ) } );
|
||||
return 0 if length ( $ibanstring ) != $countrycodelengths{ substr( $ibanstring , 0 , 2 ) } ;
|
||||
$ibanstring =~ s/(.{4})(.+)/$2$1/ ;
|
||||
$ibanstring =~ s/([A-Z])/ord( $1 ) - 55/eg ;
|
||||
my $number = Math::BigInt->new( $ibanstring ) ;
|
||||
if ( $number->bmod( 97 ) == 1 ) {
|
||||
return 1 ;
|
||||
}
|
||||
else {
|
||||
return 0 ;
|
||||
}
|
||||
}
|
||||
|
||||
if ( validate_iban( "GB82 WEST 1234 5698 7654 32" ) ) {
|
||||
print "GB82 WEST 1234 5698 7654 32 is a valid IBAN number!\n" ;
|
||||
}
|
||||
else {
|
||||
print "Sorry! GB82 WEST 1234 5698 7654 32 is not valid!\n" ;
|
||||
}
|
||||
if ( validate_iban( "GB82TEST12345698765432" ) ) {
|
||||
print "GB82TEST12345698765432 is valid!\n" ;
|
||||
}
|
||||
30
Task/IBAN/PicoLisp/iban.l
Normal file
30
Task/IBAN/PicoLisp/iban.l
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
(setq *Sizes '((GB . 22) (CH . 21) (SA . 24)))
|
||||
|
||||
(de iban (Str)
|
||||
(let Lst
|
||||
(filter
|
||||
'((X) (not (sp? X)))
|
||||
(chop (uppc Str)) )
|
||||
(when
|
||||
(=
|
||||
(cdr (assoc (pack (head 2 Lst)) *Sizes))
|
||||
(length Lst) )
|
||||
(%
|
||||
(format
|
||||
(mapcar
|
||||
'((X)
|
||||
(if (upp? X)
|
||||
(- (char X) 55)
|
||||
X ) )
|
||||
(append (nth Lst 5) (head 4 Lst)) ) )
|
||||
97 ) ) ) )
|
||||
|
||||
(for I '("sa03 8000 0000 6080 1016 7519"
|
||||
"CH9300762011623852957"
|
||||
"gb82west1234 56987654 32"
|
||||
"GB82WEST000")
|
||||
(if (= 1 (iban I))
|
||||
(println 'Valid)
|
||||
(println 'Invalid) ) )
|
||||
|
||||
(bye)
|
||||
28
Task/IBAN/Python/iban.py
Normal file
28
Task/IBAN/Python/iban.py
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
import re
|
||||
|
||||
_country2length = dict(
|
||||
AL=28, AD=24, AT=20, AZ=28, BE=16, BH=22, BA=20, BR=29,
|
||||
BG=22, CR=21, HR=21, CY=28, CZ=24, DK=18, DO=28, EE=20,
|
||||
FO=18, FI=18, FR=27, GE=22, DE=22, GI=23, GR=27, GL=18,
|
||||
GT=28, HU=28, IS=26, IE=22, IL=23, IT=27, KZ=20, KW=30,
|
||||
LV=21, LB=28, LI=21, LT=20, LU=20, MK=19, MT=31, MR=27,
|
||||
MU=30, MC=27, MD=24, ME=22, NL=18, NO=15, PK=24, PS=29,
|
||||
PL=28, PT=25, RO=24, SM=27, SA=24, RS=22, SK=24, SI=19,
|
||||
ES=24, SE=24, CH=21, TN=24, TR=26, AE=23, GB=22, VG=24 )
|
||||
|
||||
def valid_iban(iban):
|
||||
# Ensure upper alphanumeric input.
|
||||
iban = iban.replace(' ','').replace('\t','')
|
||||
if not re.match(r'^[\dA-Z]+$', iban):
|
||||
return False
|
||||
# Validate country code against expected length.
|
||||
if len(iban) != _country2length[iban[:2]]:
|
||||
return False
|
||||
# Shift and convert.
|
||||
iban = iban[4:] + iban[:4]
|
||||
digits = int(''.join(str(int(ch, 36)) for ch in iban)) #BASE 36: 0..9,A..Z -> 0..35
|
||||
return digits % 97 == 1
|
||||
|
||||
if __name__ == '__main__':
|
||||
for account in ["GB82 WEST 1234 5698 7654 32", "GB82 TEST 1234 5698 7654 32"]:
|
||||
print('%s validation is: %s' % (account, valid_iban(account)))
|
||||
1
Task/IBAN/README
Normal file
1
Task/IBAN/README
Normal file
|
|
@ -0,0 +1 @@
|
|||
Data source: http://rosettacode.org/wiki/IBAN
|
||||
47
Task/IBAN/REXX/iban-1.rexx
Normal file
47
Task/IBAN/REXX/iban-1.rexx
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/*REXX program validates an IBAN (International Bank Account Number). */
|
||||
@. =
|
||||
@.1 = 'GB82 WEST 1234 5698 7654 32 '
|
||||
@.2 = 'Gb82 West 1234 5698 7654 32 '
|
||||
@.3 = 'GB82 TEST 1234 5698 7654 32 '
|
||||
@.4 = 'GR16 0110 1250 0000 0001 2300 695 '
|
||||
@.5 = 'GB29 NWBK 6016 1331 9268 19 '
|
||||
@.6 = 'SA03 8000 0000 6080 1016 7519 '
|
||||
@.7 = 'CH93 0076 2011 6238 5295 7 '
|
||||
@.8 = 'IL62 0108 0000 0009 9999 999 '
|
||||
@.9 = 'IL62-0108-0000-0009-9999-999 '
|
||||
@.10 = 'US12 3456 7890 0987 6543 210 '
|
||||
@.11 = 'GR16 0110 1250 0000 0001 2300 695X '
|
||||
parse arg @.0 /*get optional first argument.*/
|
||||
do k=0+(arg()==0) while @.k\=='' /*either: 0 or 1──►n*/
|
||||
r = validateIBAN(@.k)
|
||||
if r==0 then say ' valid IBAN:' @.k
|
||||
else say 'invalid IBAN:' @.k " " r
|
||||
if k==0 then leave /*if user specified IBAN, we done*/
|
||||
end /*k*/
|
||||
exit /*stick a fork in it, we're done.*/
|
||||
/*──────────────────────────────────VALIDATEIBAN subroutine───────────────────────────────*/
|
||||
valIdateIBAN: procedure; arg x; numeric digits 200 /*allow big #s*/
|
||||
x=space(x,0); L=length(x) /*elide blanks, determine length.*/
|
||||
cc = 'AD 24 AE 23 AL 28 AT 20 AZ 28 BA 20 BE 16 BG 22 BH 22 BR 29 CH 21',
|
||||
'CR 21 CY 28 CZ 24 DE 22 DK 18 DO 28 EE 20 ES 24 FI 18 FO 18 FR 27',
|
||||
'GB 22 GE 22 GI 23 GL 18 GR 27 GT 28 HR 21 HU 28 IE 22 IL 23 IS 26',
|
||||
'IT 27 KW 30 KZ 20 LB 28 LI 21 LT 20 LU 20 LV 21 MC 27 MD 24 ME 22',
|
||||
'MK 19 MR 27 MT 31 MU 30 NL 18 NO 15 PK 24 PL 28 PS 29 PT 25 RO 24',
|
||||
'RS 22 SA 24 SE 24 SI 19 SK 24 SM 27 TN 24 TR 26 VG 24' /*country,L*/
|
||||
@abc# = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' /*alphabet & decimal digs*/
|
||||
cc_=left(x,2); kk=substr(x,3,2) /*get IBAN country code, checkDig*/
|
||||
c#=wordpos(cc_,cc) /*find the country code index. */
|
||||
if c#==0 then return '***error!*** invalid country code:' cc_
|
||||
if \datatype(x,'A') then return '***error!*** invalid character:',
|
||||
substr(x,verify(x,@abc#),1)
|
||||
cL=word(cc,c#+1) /*get length of country's IBAN. */
|
||||
if cL\==L then return '***error!*** invalid IBAN length:' L ' (should be' cL")"
|
||||
y=substr(x,5)left(x,4) /*put 4 in front ───► the back. */
|
||||
z= /*translate characters──►digits. */
|
||||
do j=1 for L; _=substr(y,j,1)
|
||||
if datatype(_,'U') then z=z || pos(_,@abc#)+9
|
||||
else z=z || _
|
||||
end /*j*/
|
||||
|
||||
if z//97==1 then return 0 /*check to see if correct modulus*/
|
||||
return '***error!*** invalid check digits.'
|
||||
56
Task/IBAN/REXX/iban-2.rexx
Normal file
56
Task/IBAN/REXX/iban-2.rexx
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
/*REXX program validates an IBAN (International Bank Account Number). */
|
||||
@. =
|
||||
@.1 = 'GB82 WEST 1234 5698 7654 32 '
|
||||
@.2 = 'Gb82 West 1234 5698 7654 32 '
|
||||
@.3 = 'GB82 TEST 1234 5698 7654 32 '
|
||||
@.4 = 'GR16 0110 1250 0000 0001 2300 695 '
|
||||
@.5 = 'GB29 NWBK 6016 1331 9268 19 '
|
||||
@.6 = 'SA03 8000 0000 6080 1016 7519 '
|
||||
@.7 = 'CH93 0076 2011 6238 5295 7 '
|
||||
@.8 = 'IL62 0108 0000 0009 9999 999 '
|
||||
@.9 = 'IL62-0108-0000-0009-9999-999 '
|
||||
@.10 = 'US12 3456 7890 0987 6543 210 '
|
||||
@.11 = 'GR16 0110 1250 0000 0001 2300 695X '
|
||||
@.12 = 'GT11 2222 3333 4444 5555 6666 7777 '
|
||||
@.13 = 'MK11 2222 3333 4444 555 '
|
||||
parse arg @.0 /*get optional first argument.*/
|
||||
do k=0+(arg()==0) while @.k\=='' /*either: 0 or 1──►n*/
|
||||
r = validateIBAN(@.k)
|
||||
if r==0 then say ' valid IBAN:' @.k
|
||||
else say 'invalid IBAN:' @.k " " r
|
||||
if k==0 then leave /*if user specified IBAN, we done*/
|
||||
end /*k*/
|
||||
exit /*stick a fork in it, we're done.*/
|
||||
/*──────────────────────────────────VALIDATEIBAN subroutine───────────────────────────────────────────────────────────────*/
|
||||
valIdateIBAN: procedure; arg x; numeric digits 200 /*allow big #s*/
|
||||
x=space(x,0); L=length(x) /*elide blanks, determine length.*/
|
||||
cc = 'AD 24 AE 23 AL 28 AT 20 AZ 28 BA 20 BE 16 BG 22 BH 22 BR 29 CH 21',
|
||||
'CR 21 CY 28 CZ 24 DE 22 DK 18 DO 28 EE 20 ES 24 FI 18 FO 18 FR 27',
|
||||
'GB 22 GE 22 GI 23 GL 18 GR 27 GT 28 HR 21 HU 28 IE 22 IL 23 IS 26',
|
||||
'IT 27 KW 30 KZ 20 LB 28 LI 21 LT 20 LU 20 LV 21 MC 27 MD 24 ME 22',
|
||||
'MK 19 MR 27 MT 31 MU 30 NL 18 NO 15 PK 24 PL 28 PS 29 PT 25 RO 24',
|
||||
'RS 22 SA 24 SE 24 SI 19 SK 24 SM 27 TN 24 TR 26 VG 24' /*country,L*/
|
||||
@abc# = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' /*alphabet & decimal digs*/
|
||||
cc_=left(x,2); kk=substr(x,3,2) /*get IBAN country code, checkDig*/
|
||||
c#=wordpos(cc_,cc) /*find the country code index. */
|
||||
if c#==0 then return '***error!*** invalid country code:' cc_
|
||||
if \datatype(x,'A') then return '***error!*** invalid character:',
|
||||
substr(x,verify(x,@abc#),1)
|
||||
cL=word(cc,c#+1) /*get length of country's IBAN. */
|
||||
if cL\==L then return '***error!*** invalid IBAN length:' L ' (should be' cL")"
|
||||
if cc_=='BR' & date("S")<20130701 then return "***error!*** invalid IBAN country, Brazil isn't valid until 1-July-2013."
|
||||
if cc_=='GT' & date("S")<20140701 then return "***error!*** invalid IBAN country, Guatemala isn't valid until 1-July-2014."
|
||||
if cc_=='BA' & kk\==39 then return "***error!*** invalid check digits for Bosnia and Herzegovina:" kk
|
||||
if cc_=='MK' & kk\==07 then return "***error!*** invalid check digits for Macedonia:" kk
|
||||
if cc_=='ME' & kk\==25 then return "***error!*** invalid check digits for Montenegro:" kk
|
||||
if cc_=='PT' & kk\==50 then return "***error!*** invalid check digits for Portugal:" kk
|
||||
if cc_=='SI' & kk\==56 then return "***error!*** invalid check digits for Slovenia:" kk
|
||||
y=substr(x,5)left(x,4) /*put 4 in front ───► the back. */
|
||||
z= /*translate characters──►digits. */
|
||||
do j=1 for L; _=substr(y,j,1)
|
||||
if datatype(_,'U') then z=z || pos(_,@abc#)+9
|
||||
else z=z || _
|
||||
end /*j*/
|
||||
|
||||
if z//97==1 then return 0 /*check to see if correct modulus*/
|
||||
return '***error!*** invalid check digits.'
|
||||
25
Task/IBAN/Racket/iban.rkt
Normal file
25
Task/IBAN/Racket/iban.rkt
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
#lang racket
|
||||
(define lens
|
||||
'([AL 28] [AD 24] [AT 20] [AZ 28] [BH 22] [BE 16] [BA 20] [BR 29] [BG 22]
|
||||
[CR 21] [HR 21] [CY 28] [CZ 24] [DK 18] [DO 28] [EE 20] [FO 18] [FI 18]
|
||||
[FR 27] [GE 22] [DE 22] [GI 23] [GR 27] [GL 18] [GT 28] [HU 28] [IS 26]
|
||||
[IE 22] [IL 23] [IT 27] [KZ 20] [KW 30] [LV 21] [LB 28] [LI 21] [LT 20]
|
||||
[LU 20] [MK 19] [MT 31] [MR 27] [MU 30] [MC 27] [MD 24] [ME 22] [NL 18]
|
||||
[NO 15] [PK 24] [PS 29] [PL 28] [PT 25] [RO 24] [SM 27] [SA 24] [RS 22]
|
||||
[SK 24] [SI 19] [ES 24] [SE 24] [CH 21] [TN 24] [TR 26] [AE 23] [GB 22]
|
||||
[VG 24]))
|
||||
(define (valid-iban? str)
|
||||
(define str* (regexp-replace* #px"\\s+" str ""))
|
||||
(define c (cond [(regexp-match #rx"^[A-Z][A-Z]" str*)
|
||||
=> (λ(x) (assq (string->symbol (car x)) lens))]
|
||||
[else #f]))
|
||||
(define (letter c)
|
||||
(number->string (+ (char->integer (string-ref c 0)) -65 10)))
|
||||
(and c (= (cadr c) (string-length str*))
|
||||
(regexp-match? #rx"[A-Z0-9]" str*)
|
||||
(let* ([x (string-append (substring str* 4) (substring str* 0 4))]
|
||||
[x (string->number (regexp-replace* #rx"[A-Z]" x letter))])
|
||||
(= 1 (modulo x 97)))))
|
||||
|
||||
(valid-iban? "GB82 WEST 1234 5698 7654 32") ; => #t
|
||||
(valid-iban? "GB82 TEST 1234 5698 7654 32") ; => #f
|
||||
29
Task/IBAN/Ruby/iban.rb
Normal file
29
Task/IBAN/Ruby/iban.rb
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
def valid_iban? iban
|
||||
len = {
|
||||
AL: 28, AD: 24, AT: 20, AZ: 28, BE: 16, BH: 22, BA: 20, BR: 29,
|
||||
BG: 22, CR: 21, HR: 21, CY: 28, CZ: 24, DK: 18, DO: 28, EE: 20,
|
||||
FO: 18, FI: 18, FR: 27, GE: 22, DE: 22, GI: 23, GR: 27, GL: 18,
|
||||
GT: 28, HU: 28, IS: 26, IE: 22, IL: 23, IT: 27, KZ: 20, KW: 30,
|
||||
LV: 21, LB: 28, LI: 21, LT: 20, LU: 20, MK: 19, MT: 31, MR: 27,
|
||||
MU: 30, MC: 27, MD: 24, ME: 22, NL: 18, NO: 15, PK: 24, PS: 29,
|
||||
PL: 28, PT: 25, RO: 24, SM: 27, SA: 24, RS: 22, SK: 24, SI: 19,
|
||||
ES: 24, SE: 24, CH: 21, TN: 24, TR: 26, AE: 23, GB: 22, VG: 24
|
||||
}
|
||||
|
||||
# Ensure upper alphanumeric input.
|
||||
iban.delete! " \t"
|
||||
return false unless iban =~ /^[\dA-Z]+$/
|
||||
|
||||
# Validate country code against expected length.
|
||||
cc = iban[0, 2].to_sym
|
||||
return false unless iban.size == len[cc]
|
||||
|
||||
# Shift and convert.
|
||||
iban = iban[4..-1] + iban[0, 4]
|
||||
iban.gsub!(/./) { |c| c.to_i(36) }
|
||||
|
||||
iban.to_i % 97 == 1
|
||||
end
|
||||
|
||||
p valid_iban? "GB82 WEST 1234 5698 7654 32" #=> true
|
||||
p valid_iban? "GB82 TEST 1234 5698 7654 32" #=> false
|
||||
45
Task/IBAN/Scala/iban-1.scala
Normal file
45
Task/IBAN/Scala/iban-1.scala
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import scala.collection.immutable.SortedMap
|
||||
|
||||
class Iban(val iban: String) {
|
||||
// Isolated tests
|
||||
def isAllUpperCase = iban.toUpperCase == iban
|
||||
|
||||
def isValidpattern = (Iban.pattern findFirstIn iban).nonEmpty
|
||||
|
||||
def isNationalSize = {
|
||||
Iban.ccVsLength.getOrElse(iban.take(2), 0) == iban.size
|
||||
}
|
||||
|
||||
def isCheckNumberOK = {
|
||||
def rearrange = (iban.drop(4) + iban.take(4)). // Move left country code part to end
|
||||
// continue with each char converted to Int
|
||||
map(ch => if (ch.isDigit) ch.toInt - '0' else ch - 'A' + 10).mkString
|
||||
|
||||
(BigInt(rearrange) mod 97) == 1
|
||||
}
|
||||
|
||||
def isValidIban = {
|
||||
isAllUpperCase &&
|
||||
isValidpattern &&
|
||||
isNationalSize &&
|
||||
isCheckNumberOK
|
||||
}
|
||||
}
|
||||
|
||||
object Iban {
|
||||
// IBAN length database
|
||||
lazy val ccVsLength: SortedMap[String, Int] = SortedMap[String, Int]() ++
|
||||
"""AD24 AE23 AL28 AO25 AT20 AZ28 BA20 BE16 BF27 BG22 BH22 BI16
|
||||
|BJ28 BR29 CG27 CH21 CI28 CM27 CR21 CV25 CY28 CZ24 DE22 DK18
|
||||
|DO28 DZ24 EE20 EG27 ES24 FI18 FO18 FR27 GA27 GB22 GE22 GI23
|
||||
|GL18 GR27 GT28 HR21 HU28 IE22 IL23 IR26 IS26 IT27 JO30 KW30
|
||||
|KZ20 LB28 LI21 LT20 LU20 LV21 MC27 MD24 ME22 MG27 MK19 ML28
|
||||
|MR27 MT31 MU30 MZ25 NL18 NO15 PK24 PL28 PS29 PT25 QA29 RO24
|
||||
|RS22 SA24 SE24 SI19 SK24 SM27 SN28 TN24 TR26 UA29 VG24""".
|
||||
stripMargin.replaceAll( """\s""", " ").split(' ').
|
||||
map(v => (v.take(2), if (v.isEmpty) 0 else v.slice(2, 4).toInt))
|
||||
|
||||
lazy val pattern = "([A-Z]{2})([0-9]{2})([A-Z0-9]{4})([A-Z0-9]{0,2})([0-9]{7})(([A-Z0-9]?){0,16})".r
|
||||
|
||||
def apply(s: String) = new Iban(s.replaceAll( """\s""", ""))
|
||||
}
|
||||
98
Task/IBAN/Scala/iban-2.scala
Normal file
98
Task/IBAN/Scala/iban-2.scala
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
object IbanTest extends App {
|
||||
def blackCases = """AT611904300235473201
|
||||
|GB82TEST12345698765432
|
||||
|GB81WEST12345698765432""".stripMargin
|
||||
|
||||
def whiteCases = """AD1200012030200359100100
|
||||
|AE26 0211 0000 0023 0064 016
|
||||
|AL47 2121 1009 0000 0002 3569 8741
|
||||
|AO06000600000100037131174
|
||||
|AZ21NABZ00000000137010001944
|
||||
|BA391290079401028494
|
||||
|BE68539007547034
|
||||
|BF1030134020015400945000643
|
||||
|BG80BNBG96611020345678
|
||||
|BH29BMAG1299123456BH00
|
||||
|BI43201011067444
|
||||
|BJ11B00610100400271101192591
|
||||
|BR9700360305000010009795493P1
|
||||
|CG5230011000202151234567890
|
||||
|CH9300762011623852957
|
||||
|CI05A00060174100178530011852
|
||||
|CM2110003001000500000605306
|
||||
|CR0515202001026284066
|
||||
|CV64000300004547069110176
|
||||
|CY17002001280000001200527600
|
||||
|CZ6508000000192000145399
|
||||
|DE89370400440532013000
|
||||
|DK5000400440116243
|
||||
|DO28BAGR00000001212453611324
|
||||
|DZ4000400174401001050486
|
||||
|EE382200221020145685
|
||||
|EG1100006001880800100014553
|
||||
|ES9121000418450200051332
|
||||
|FI2112345600000785
|
||||
|FO1464600009692713
|
||||
|FR1420041010050500013M02606
|
||||
|FR7630007000110009970004942
|
||||
|GA2140002000055602673300064
|
||||
|GB29NWBK60161331926819
|
||||
|GE29NB0000000101904917
|
||||
|GI75NWBK000000007099453
|
||||
|GL8964710001000206
|
||||
|GR1601101250000000012300695
|
||||
|GT82TRAJ01020000001210029690
|
||||
|HR1210010051863000160
|
||||
|HU42117730161111101800000000
|
||||
|IE29AIBK93115212345678
|
||||
|IL620108000000099999999
|
||||
|IR580540105180021273113007
|
||||
|IS140159260076545510730339
|
||||
|IT60X0542811101000000123456
|
||||
|JO94CBJO0010000000000131000302
|
||||
|KW74NBOK0000000000001000372151
|
||||
|KZ176010251000042993
|
||||
|LB30099900000001001925579115
|
||||
|LI21088100002324013AA
|
||||
|LT121000011101001000
|
||||
|LU280019400644750000
|
||||
|LV80BANK0000435195001
|
||||
|MC5813488000010051108001292
|
||||
|MD24AG000225100013104168
|
||||
|ME25505000012345678951
|
||||
|MG4600005030010101914016056
|
||||
|MK07300000000042425
|
||||
|ML03D00890170001002120000447
|
||||
|MR1300012000010000002037372
|
||||
|MT84MALT011000012345MTLCAST001S
|
||||
|MU17BOMM0101101030300200000MUR
|
||||
|MZ59000100000011834194157
|
||||
|NL91ABNA0417164300
|
||||
|NL81 TRIO 0212 4710 66
|
||||
|NO9386011117947
|
||||
|PK24SCBL0000001171495101
|
||||
|PL27114020040000300201355387
|
||||
|PS92PALS000000000400123456702
|
||||
|PT50000200000163099310355
|
||||
|PT50000201231234567890154
|
||||
|QA58 DOHB 0000 1234 5678 90AB CDEF G
|
||||
|RO49 AAAA 1B31 0075 9384 0000
|
||||
|RS35260005601001611379
|
||||
|SA0380000000608010167519
|
||||
|SE3550000000054910000003
|
||||
|SI56191000000123438
|
||||
|SK3112000000198742637541
|
||||
|SM86U0322509800000000270100
|
||||
|SN12K00100152000025690007542
|
||||
|TN5914207207100707129648
|
||||
|TR330006100519786457841326
|
||||
|UA57 3543 4700 0676 2462 0549 2502 6
|
||||
|VG96 VPVG 0000 0123 4567 8901
|
||||
|GB82 WEST 1234 5698 7654 32
|
||||
|SA03 8000 0000 6080 1016 7519
|
||||
|CH93 0076 2011 6238 5295 7""".stripMargin
|
||||
|
||||
whiteCases.lines.foreach(l => assert(Iban(l).isValidIban))
|
||||
blackCases.lines.foreach(l => assert(!Iban(l).isValidIban))
|
||||
println(s"Successfully completed; ${whiteCases.lines.size + blackCases.lines.size} cases tested, no errors.")
|
||||
}
|
||||
57
Task/IBAN/Seed7/iban.seed7
Normal file
57
Task/IBAN/Seed7/iban.seed7
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
$ include "seed7_05.s7i";
|
||||
include "bigint.s7i";
|
||||
|
||||
const type: countryHash is hash [string] integer;
|
||||
|
||||
const func countryHash: initCountryCode is func
|
||||
result
|
||||
var countryHash: cc is countryHash.value;
|
||||
begin
|
||||
cc @:= ["AL"] 28; cc @:= ["AD"] 24; cc @:= ["AT"] 20; cc @:= ["AZ"] 28; cc @:= ["BE"] 16; cc @:= ["BH"] 22;
|
||||
cc @:= ["BA"] 20; cc @:= ["BR"] 29; cc @:= ["BG"] 22; cc @:= ["CR"] 21; cc @:= ["HR"] 21; cc @:= ["CY"] 28;
|
||||
cc @:= ["CZ"] 24; cc @:= ["DK"] 18; cc @:= ["DO"] 28; cc @:= ["EE"] 20; cc @:= ["FO"] 18; cc @:= ["FI"] 18;
|
||||
cc @:= ["FR"] 27; cc @:= ["GE"] 22; cc @:= ["DE"] 22; cc @:= ["GI"] 23; cc @:= ["GR"] 27; cc @:= ["GL"] 18;
|
||||
cc @:= ["GT"] 28; cc @:= ["HU"] 28; cc @:= ["IS"] 26; cc @:= ["IE"] 22; cc @:= ["IL"] 23; cc @:= ["IT"] 27;
|
||||
cc @:= ["KZ"] 20; cc @:= ["KW"] 30; cc @:= ["LV"] 21; cc @:= ["LB"] 28; cc @:= ["LI"] 21; cc @:= ["LT"] 20;
|
||||
cc @:= ["LU"] 20; cc @:= ["MK"] 19; cc @:= ["MT"] 31; cc @:= ["MR"] 27; cc @:= ["MU"] 30; cc @:= ["MC"] 27;
|
||||
cc @:= ["MD"] 24; cc @:= ["ME"] 22; cc @:= ["NL"] 18; cc @:= ["NO"] 15; cc @:= ["PK"] 24; cc @:= ["PS"] 29;
|
||||
cc @:= ["PL"] 28; cc @:= ["PT"] 25; cc @:= ["RO"] 24; cc @:= ["SM"] 27; cc @:= ["SA"] 24; cc @:= ["RS"] 22;
|
||||
cc @:= ["SK"] 24; cc @:= ["SI"] 19; cc @:= ["ES"] 24; cc @:= ["SE"] 24; cc @:= ["CH"] 21; cc @:= ["TN"] 24;
|
||||
cc @:= ["TR"] 26; cc @:= ["AE"] 23; cc @:= ["GB"] 22; cc @:= ["VG"] 24;
|
||||
end func;
|
||||
|
||||
const countryHash: countryCode is initCountryCode;
|
||||
|
||||
const func boolean: isLegal (in var string: iban) is func
|
||||
result
|
||||
var boolean: legal is FALSE;
|
||||
local
|
||||
var char: ch is ' ';
|
||||
var string: converted is "";
|
||||
begin
|
||||
iban := upper(replace(iban, " ", ""));
|
||||
legal := iban[.. 2] in countryCode and countryCode[iban[.. 2]] = length(iban);
|
||||
iban := iban[5 ..] & iban[.. 4];
|
||||
for ch range iban do
|
||||
case ch of
|
||||
when {'0' .. '9'}: converted &:= ch;
|
||||
when {'A' .. 'Z'}: converted &:= str(ord(ch) - ord('A') + 10);
|
||||
otherwise: legal := FALSE;
|
||||
end case;
|
||||
end for;
|
||||
legal := legal and (bigInteger parse converted) rem 97_ = 1_;
|
||||
end func;
|
||||
|
||||
const proc: check (in string: iban) is func
|
||||
begin
|
||||
writeln("Valid " <& iban <& ": " <& isLegal(iban));
|
||||
end func;
|
||||
|
||||
const proc: main is func
|
||||
begin
|
||||
check("GB82 WEST 1234 5698 7654 32");
|
||||
check("GB82WEST12345698765432");
|
||||
check("gb82 west 1234 5698 7654 32");
|
||||
check("GB82 TEST 1234 5698 7654 32");
|
||||
check("GB82 WEST 1243 5698 7654 32");
|
||||
end func;
|
||||
34
Task/IBAN/Tcl/iban-1.tcl
Normal file
34
Task/IBAN/Tcl/iban-1.tcl
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
proc verifyIBAN {iban} {
|
||||
# Normalize by up-casing and stripping illegal chars (e.g., space)
|
||||
set iban [regsub -all {[^A-Z0-9]+} [string toupper $iban] ""]
|
||||
# Get the expected length from the country-code part
|
||||
switch [string range $iban 0 1] {
|
||||
NO { set len 15 }
|
||||
BE { set len 16 }
|
||||
DK - FI - FO - GL - NL { set len 18}
|
||||
MK - SI { set len 19 }
|
||||
AT - BA - EE - KZ - LT - LU { set len 20 }
|
||||
CH - CR - HR - LI - LV { set len 21 }
|
||||
BG - BH - DE - GB - GE - IE - ME - RS { set len 22 }
|
||||
AE - GI - IL { set len 23 }
|
||||
AD - CZ - ES - MD - PK - RO - SA - SE - SK - TN - VG { set len 24 }
|
||||
PT { set len 25 }
|
||||
IS - TR { set len 26 }
|
||||
FR - GR - IT - MC - MR - SM { set len 27 }
|
||||
AL - AZ - CY - DO - GT - HU - LB - PL { set len 28 }
|
||||
BR - PS { set len 29 }
|
||||
KW - MU { set len 30 }
|
||||
MT { set len 31 }
|
||||
default {
|
||||
# unsupported country code
|
||||
return false
|
||||
}
|
||||
}
|
||||
# Convert to number
|
||||
set num [string map {
|
||||
A 10 B 11 C 12 D 13 E 14 F 15 G 16 H 17 I 18 J 19 K 20 L 21 M 22
|
||||
N 23 O 24 P 25 Q 26 R 27 S 28 T 29 U 30 V 31 W 32 X 33 Y 34 Z 35
|
||||
} [string range $iban 4 end][string range $iban 0 3]]
|
||||
# Verify length and modulus
|
||||
return [expr {[string length $iban] == $len && $num % 97 == 1}]
|
||||
}
|
||||
4
Task/IBAN/Tcl/iban-2.tcl
Normal file
4
Task/IBAN/Tcl/iban-2.tcl
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
set iban "GB82 WEST 1234 5698 7654 32"
|
||||
puts "$iban is [expr {[verifyIBAN $iban] ? {verified} : {unverified}}]"
|
||||
set not "GB42 WEST 1234 5698 7654 32"
|
||||
puts "$not is [expr {[verifyIBAN $not] ? {verified} : {unverified}}]"
|
||||
34
Task/IBAN/UNIX-Shell/iban.sh
Normal file
34
Task/IBAN/UNIX-Shell/iban.sh
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
declare -A base36=(
|
||||
[A]=10 [B]=11 [C]=12 [D]=13 [E]=14 [F]=15 [G]=16 [H]=17 [I]=18
|
||||
[J]=19 [K]=20 [L]=21 [M]=22 [N]=23 [O]=24 [P]=25 [Q]=26 [R]=27
|
||||
[S]=28 [T]=29 [U]=30 [V]=31 [W]=32 [X]=33 [Y]=34 [Z]=35
|
||||
)
|
||||
|
||||
function is_iban {
|
||||
local -u acct=${1//[^[:alnum:]]/}
|
||||
acct=${acct:4}${acct:0:4}
|
||||
local i char digits=""
|
||||
for ((i=0; i<${#acct}; i++)); do
|
||||
char=${acct:i:1}
|
||||
digits+=${base36[$char]:-$char}
|
||||
done
|
||||
local mod=$(mod97 $digits)
|
||||
(( mod == 1 ))
|
||||
}
|
||||
|
||||
function mod97 {
|
||||
local D=$1
|
||||
N=${D:0:9}
|
||||
D=${D:9}
|
||||
while [[ -n $D ]]; do
|
||||
mod=$(( N % 97 ))
|
||||
N=$(printf "%02d%s" $mod ${D:0:7})
|
||||
D=${D:7}
|
||||
done
|
||||
echo $(( N % 97 ))
|
||||
}
|
||||
|
||||
for test in "GB82 WEST 1234 5698 7654 32" "GB42 WEST 1234 5698 7654 32"; do
|
||||
printf "%s : " "$test"
|
||||
is_iban "$test" && echo yes || echo no
|
||||
done
|
||||
Loading…
Add table
Add a link
Reference in a new issue