2016 Update

This commit is contained in:
Tina Müller 2016-12-05 22:15:40 +01:00
parent 948b86eafa
commit dcf5d15da3
7965 changed files with 139854 additions and 31002 deletions

View file

@ -23,12 +23,13 @@
;; 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)))))
(flet ((valid-alphanum (ch)
(or (and (char<= #\A ch)
(char>= #\Z ch))
(and (char<= #\0 ch)
(char>= #\9 ch)))))
(loop for char across iban
always (valid-alphanum char))))
;;
;; The function IBAN-length verifies that the length of the number is correct. The code lengths

View file

@ -0,0 +1,36 @@
defmodule IBAN do
@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 }
def valid?(iban) do
iban = String.replace(iban, ~r/\s/, "")
if Regex.match?(~r/^[\dA-Z]+$/, iban) do
cc = String.slice(iban, 0..1) |> String.to_atom
if String.length(iban) == @len[cc] do
{left, right} = String.split_at(iban, 4)
num = String.codepoints(right <> left)
|> Enum.map_join(fn c -> String.to_integer(c,36) end)
|> String.to_integer
rem(num,97) == 1
else
false
end
else
false
end
end
end
[ "GB82 WEST 1234 5698 7654 32",
"gb82 west 1234 5698 7654 32",
"GB82 WEST 1234 5698 7654 320",
"GB82WEST12345698765432",
"GB82 TEST 1234 5698 7654 32",
"ZZ12 3456 7890 1234 5678 12" ]
|> Enum.each(fn iban -> IO.puts "#{IBAN.valid?(iban)}\t#{iban}" end)

View file

@ -1,4 +1,5 @@
import Data.Char (toUpper)
import Data.Maybe (fromJust)
validateIBAN :: String -> Either String String
validateIBAN [] = Left "No IBAN number."
@ -38,12 +39,9 @@ validateIBAN xs =
-- 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
p4 = read $ concat $ map convertLetter p3
convertLetter x | x `elem` digits = [x]
| otherwise = fromJust $ lookup x replDigits
-- see if the number is valid
check = if sane
then if p4 `mod` 97 == 1

View file

@ -0,0 +1,27 @@
var ibanLen = {
NO:15, BE:16, DK:18, FI:18, FO:18, GL:18, NL:18, MK:19,
SI:19, AT:20, BA:20, EE:20, KZ:20, LT:20, LU:20, CR:21,
CH:21, HR:21, LI:21, LV:21, BG:22, BH:22, DE:22, GB:22,
GE:22, IE:22, ME:22, RS:22, AE:23, GI:23, IL:23, AD:24,
CZ:24, ES:24, MD:24, PK:24, RO:24, SA:24, SE:24, SK:24,
VG:24, TN:24, PT:25, IS:26, TR:26, FR:27, GR:27, IT:27,
MC:27, MR:27, SM:27, AL:28, AZ:28, CY:28, DO:28, GT:28,
HU:28, LB:28, PL:28, BR:29, PS:29, KW:30, MU:30, MT:31
}
function isValid(iban) {
iban = iban.replace(/\s/g, '')
if (!iban.match(/^[\dA-Z]+$/)) return false
var len = iban.length
if (len != ibanLen[iban.substr(0,2)]) return false
iban = iban.substr(4) + iban.substr(0,4)
for (var s='', i=0; i<len; i+=1) s+=parseInt(iban.charAt(i),36)
for (var m=s.substr(0,15)%97, s=s.substr(15); s; s=s.substr(13)) m=(m+s.substr(0,13))%97
return m == 1
}
document.write(isValid('GB82 WEST 1234 5698 7654 32'), '<br>') // true
document.write(isValid('GB82 WEST 1.34 5698 7654 32'), '<br>') // false
document.write(isValid('GB82 WEST 1234 5698 7654 325'), '<br>') // false
document.write(isValid('GB82 TEST 1234 5698 7654 32'), '<br>') // false
document.write(isValid('SA03 8000 0000 6080 1016 7519'), '<br>') // true

24
Task/IBAN/Lua/iban.lua Normal file
View file

@ -0,0 +1,24 @@
local length=
{
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
}
function validate(iban)
iban=iban:gsub("%s","")
local l=length[iban:sub(1,2)]
if not l or l~=#iban or iban:match("[^%d%u]") then
return false -- invalid character, country code or length
end
local mod=0
local rotated=iban:sub(5)..iban:sub(1,4)
for c in rotated:gmatch(".") do
mod=(mod..tonumber(c,36)) % 97
end
return mod==1
end

106
Task/IBAN/OCaml/iban.ocaml Normal file
View file

@ -0,0 +1,106 @@
#load "str.cma"
#load "nums.cma" (* for module Big_int *)
(* Countries and length of their IBAN. *)
(* Taken from https://en.wikipedia.org/wiki/International_Bank_Account_Number#IBAN_formats_by_country *)
let countries = [
("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); ("TL", 23); ("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); ("XK", 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); ("DZ", 24); ("AO", 25); ("BJ", 28); ("BF", 27);
("BI", 16); ("CM", 27); ("CV", 25); ("IR", 26); ("CI", 28); ("MG", 27);
("ML", 28); ("MZ", 25); ("SN", 28); ("UA", 29)
]
(* Put the countries in a Hashtbl for faster search... *)
let tbl_countries =
let htbl = Hashtbl.create (List.length countries) in
let _ = List.iter (fun (k, v) -> Hashtbl.add htbl k v) countries in
htbl
(* Delete spaces and put all letters in upper case. *)
let clean_iban iban =
Str.global_replace (Str.regexp " +") "" iban
|> String.uppercase_ascii
(* Each country has an IBAN with a specific length. *)
let check_length ib =
let iso_length = List.hd countries |> fst |> String.length in
let country_code = String.sub ib 0 iso_length in
try
Hashtbl.find tbl_countries country_code = String.length ib
with
Not_found -> false
(* Convert a string into a list of chars. *)
let charlist_of_string s =
let l = String.length s in
let rec doloop i =
if i >= l then []
else s.[i] :: doloop (i + 1)
in
doloop 0
(* Letters are associated to values: A=10, B=11, ..., Z=35. *)
let val_of_char c =
match c with
| '0' .. '9' -> int_of_char c - int_of_char '0'
| 'A' .. 'Z' -> int_of_char c - int_of_char 'A' + 10
| _ -> failwith (Printf.sprintf "Character not allowed: %c" c)
(* Compute the mod-97 value and check it is equal to 1. *)
let check_mod97 ib =
let l = String.length ib
and taken = 4 in
let prefix = String.sub ib 0 taken
and rest = String.sub ib taken (l - taken) in
let newval = rest ^ prefix (* move the 4 initial characters to the end of the string *)
|> charlist_of_string (* convert the string into a list of chars *)
|> List.map val_of_char (* convert each char into its integer value *)
|> List.map string_of_int (* convert the integers into strings... *)
|> List.fold_left (^) "" in (* ...and concatenate said strings *)
(* Now compute the mod-97 using the Big Integers provided by OCaml, and
* compare the result to 1. *)
Big_int.eq_big_int
(Big_int.mod_big_int (Big_int.big_int_of_string newval)
(Big_int.big_int_of_int 97))
(Big_int.big_int_of_int 1)
(* Do the validation as described in the Wikipedia article at
* https://en.wikipedia.org/wiki/International_Bank_Account_Number#Validating_the_IBAN *)
let validate iban =
let ib = clean_iban iban in
check_length ib && check_mod97 ib
let () =
let ibans = [
("GB82 WEST 1234 5698 7654 32", true);
("GB82 TEST 1234 5698 7654 32", false);
("GB81 WEST 1234 5698 7654 32", false);
("GB82 WEST 1234 5698 7654 3", false);
("SA03 8000 0000 6080 1016 7519", true);
("CH93 0076 2011 6238 5295 7", true);
("\"Completely incorrect iban\"", false)
] in
let testit (ib, exp) =
let res = validate ib in
Printf.printf "%s is %svalid. Expected %b [%s]\n"
ib (if res then "" else "not ")
exp (if res = exp then "PASS" else "FAIL")
in
List.iter (fun pair -> testit pair) ibans

View file

@ -0,0 +1,60 @@
@'
"Country","Length","Example"
"Albania",28,"AL47212110090000000235698741"
"Andorra",24,"AD1200012030200359100100"
"Austria",20,"AT611904300235473201"
"Belgium",16,"BE68539007547034"
"Bosnia and Herzegovina",20,"BA391290079401028494"
"Bulgaria",22,"BG80BNBG96611020345678"
"Croatia",21,"HR1210010051863000160"
"Cyprus",28,"CY17002001280000001200527600"
"Czech Republic",24,"CZ6508000000192000145399"
"Denmark",18,"DK5000400440116243"
"Estonia",20,"EE382200221020145685"
"Faroe Islands",18,"FO1464600009692713"
"Finland",18,"FI2112345600000785"
"France",27,"FR1420041010050500013M02606"
"Georgia",22,"GE29NB0000000101904917"
"Germany",22,"DE89370400440532013000"
"Gibraltar",23,"GI75NWBK000000007099453"
"Greece",27,"GR1601101250000000012300695"
"Greenland",18,"GL8964710001000206"
"Hungary",28,"HU42117730161111101800000000"
"Iceland",26,"IS140159260076545510730339"
"Ireland",22,"IE29AIBK93115212345678"
"Italy",27,"IT60X0542811101000000123456"
"Kosovo",20,"XK051212012345678906"
"Latvia",21,"LV80BANK0000435195001"
"Liechtenstein",21,"LI21088100002324013AA"
"Lithuania",20,"LT121000011101001000"
"Luxembourg",20,"LU280019400644750000"
"Macedonia",19,"MK07300000000042425"
"Malta",31,"MT84MALT011000012345MTLCAST001S"
"Moldova",24,"MD24AG000225100013104168"
"Monaco",27,"MC5813488000010051108001292"
"Montenegro",22,"ME25505000012345678951"
"Netherlands",18,"NL91ABNA0417164300"
"Norway",15,"NO9386011117947"
"Poland",28,"PL27114020040000300201355387"
"Portugal",25,"PT50000201231234567890154"
"Romania",24,"RO49AAAA1B31007593840000"
"San Marino",27,"SM86U0322509800000000270100"
"Serbia",22,"RS35260005601001611379"
"Slovakia",24,"SK3112000000198742637541"
"Slovenia",19,"SI56191000000123438"
"Spain",24,"ES9121000418450200051332"
"Sweden",24,"SE3550000000054910000003"
"Switzerland",21,"CH9300762011623852957"
"Ukraine",29,"UA573543470006762462054925026"
"United Kingdom",22,"GB29NWBK60161331926819"
'@ -split "`r`n" | Set-Content -Path .\IBAN.csv -Force
$ibans = foreach ($iban in Import-Csv -Path .\IBAN.csv)
{
$iban | Select-Object -Property Country,
@{Name='Code' ; Expression={$iban.Example.Substring(0,2)}},
@{Name='Length'; Expression={[int]$iban.Length}},
Example
}
$ibans

View file

@ -0,0 +1,10 @@
$regex = [regex]'[a-zA-Z]{2}[0-9]{2}[a-zA-Z0-9]{6}[0-9]{5}([a-zA-Z0-9]?){0,16}'
foreach ($iban in $ibans)
{
[PSCustomObject]@{
Country = $iban.Country
Example = $iban.Example
IsValid = $regex.IsMatch($iban.Example)
}
}

View file

@ -0,0 +1,81 @@
* IBAN - International Bank Account Number validation
DEFINE('ibantable()') :(iban_table_end)
ibantable
ibantable = TABLE(70)
ibancodes =
+ 'AL28AD24AT20AZ28BE16BH22BA20BR29BG22CR21'
+ 'HR21CY28CZ24DK18DO28EE20FO18FI18FR27GE22'
+ 'DE22GI23GR27GL18GT28HU28IS26IE22IL23IT27'
+ 'KZ20KW30LV21LB28LI21LT20LU20MK19MT31MR27'
+ 'MU30MC27MD24ME22NL18NO15PK24PS29PL28PT25'
+ 'RO24SM27SA24RS22SK24SI19ES24SE24CH21TN24'
+ 'TR26AE23GB22VG24'
nordeacodes =
+ 'DZ24AO25BJ28FB27BI16CM27CV25IR26CI28MG27'
+ 'ML28MZ25SN28UA29'
allcodes = ibancodes nordeacodes
iban1 allcodes LEN(2) . country LEN(2) . length = :F(return)
ibantable<country> = length :(iban1)
iban_table_end
DEFINE('tonumbers(tonumbers)letter,p') :(tonumbers_end)
tonumbers
tonumbers ANY(&UCASE) . letter :f(RETURN)
&UCASE @p letter
tonumbers letter = p + 10 :(tonumbers)
tonumbers_end
* modulo for long integers
*
DEFINE('mod(m,n)') :(mod_end)
mod m LEN(9) . r = :f(modresult)
mod = REMDR(CONVERT(r,"INTEGER"), n)
mod0 m LEN(7) . r = :f(modresult)
mod = mod r
mod = REMDR(mod, n) :(mod0)
modresult
mod = GT(SIZE(m), 0) REMDR(mod m, n) :(RETURN)
mod_end
DEFINE('invalid(l,t)') :(invalid_end)
invalid
OUTPUT = "Invalid IBAN: " l ": " t :(RETURN)
invalid_end
***** main *****
ibant = ibantable()
FREEZE(ibant)
INPUT(.INPUT, 28,,'iban.dat')
read line = INPUT :f(END)
country = checkdigits = line2 =
** GB82 WEST 1234 5698 7654 32
** Uppercase line2 and remove spaces.
line2 = REPLACE(line, &LCASE, &UCASE)
space line2 ANY(" ") = :s(space)
** GB82WEST12345698765432
** Capture country code and checkdigits.
line2 LEN(2) . country LEN(2) . checkdigits
** 1. Is the country code known?
IDENT(ibant<country>)
+ invalid(line, "unlisted country: " country) :s(read)
** 2. Is the length correct for the country?
NE(SIZE(line2), ibant<country>)
+ invalid(line, "length: " SIZE(line2)
+ " not " ibant<country>) :s(read)
** 3. Move first four chars to end of line.
** Convert line2 letters to numbers.
** 3214282912345698765432161182
line2 = SUBSTR(line2,5) SUBSTR(line2,1,4)
line2 = tonumbers(line2)
** Mod_97 of line2 = 1?
modsum = mod(line2, 97)
NE(modsum, 1)
+ invalid(line, "mod_97 " modsum " not = 1") :s(read)
OUTPUT = "valid IBAN: " line :(read)
END