'Identify an IBAN
'see: https://rosettacode.org/wiki/IBAN
'see: https://en.wikipedia.org/wiki/International_Bank_Account_Number

$ filename "IBANcheck.exe"

'uses rtl32
'uses rtl64

uses console

function replace(string t,w,r) as string  'ParseUtil.inc
========================================
/**
  substitutes each w with r in t

  @param t is the main string where substitutions are made
  @param w is the key string to be replaced
  @param r is the replacement string
  @return the main string with replacements
**/
int a,b,lw,lr
string s=t
'
lw=len(w)
lr=len(r)
a=1
do
  a=instr(a,s,w)
  if a=0 then exit do
  s=left(s,a-1)+r+mid(s,a+lw)
  a+=lr
loop
return s
end function

function mod97(string s) as uint
--------------------------------
'Special division of a big number, a bit like in school
'with paper and pencil, determine the remainder
  uint start = 10
  uint r = mod(val(left(s, 9)), 97)   
  while start <= len(s)
    r = mod(val(str(r) & mid(s, start, 7)), 97)
    start += 7
  wend
  if r != 1 then print "Remainder = " r ": "
  return r
end function


' List updated to release 96, February 2024, of IBAN Registry for ISO 13616 (87 countries)
string countryCodes =
  "AD24001 AE23002 AL28003 AT20004 AZ28005 BA20006 BE16007 BG22008 BH22009 BI27010 " +
  "BR29011 BY28012 CH21013 CR22014 CY28015 CZ24016 DE22017 DJ27018 DK18019 DO28020 " +
  "EE20021 EG29022 ES24023 FI18024 FK18025 FO18026 FR27027 GB22028 GE22029 GI23030 " +
  "GL18031 GR27032 GT28033 HR21034 HU28035 IE22036 IL23037 IQ23038 IS26039 IT27040 " +
  "JO30041 KW30042 KZ20043 LB28044 LC32045 LI21046 LT20047 LU20048 LV21049 LY25050 " +
  "MC27051 MD24052 ME22053 MK19054 MN20055 MR27056 MT31057 MU30058 NI28059 NL18060 " +
  "NO15061 OM23062 PK24063 PL28064 PS29065 PT25066 QA29067 RO24068 RS22069 RU33070 " +
  "SA24071 SC31072 SD18073 SE24074 SI19075 SK24076 SM27077 SO23078 ST25079 SV28080 " +
  "TL23081 TN24082 TR26083 UA29084 VA22085 VG24086 XK20087"

dim string countries(87)
countries = {
  "Andorra","United Arab Emirates","Albania","Austria","Azerbaijan","Bosnia and Herzegovina","Belgium","Bulgaria","Bahrein","Burundi",
  "Brazil","Republic of Belarus","Switzerland","Costa Rica","Cyprus","Czech Republic","Germany","Djibouti","Denmark","Dominican Republic",
  "Estonia","Egypt","Spain","Finnland","Falkland Islands","Faroe Islands","France","United Kingdom","Georgia","Gibraltar",
  "Greenland","Greece","Guatemala","Croatia","Hungary","Ireland","Israel","Iraq","Iceland","Italy",
  "Jordan","Kuwait","Kazakhstan","Lebanon","Saint Lucia","Liechtenstein","Lithuania","Luxembourg","Latvia","Libya",
  "Monaco","Moldova","Montenegro","Macedonia","Mongolia","Mauritania","Malta","Mauritius","Nicaragua","Netherlands",
  "Norway","Oman","Pakistan","Poland","Palestine, State of","Portugal","Qatar","Romania","Serbia","Russia",
  "Saudi Arabia","Seychelles","Sudan","Sweden","Slovenia","Slovakia","San Marino","Somalia","Sao Tome and Principe","El Salvador",
  "Timor-Leste","Tunisia","Turkey","Ukraine","Vatican City State","Virgin Islands","Kosovo"
}

function findCountryCode(string cc) as int
  return instr(countryCodes, ucase(cc))
end function

string country

function validateIBAN(string IBAN) as bool
  int pos, idx
  string s_IBAN = ucase(IBAN)
  string n

  'remove spaces and hyphen
  s_IBAN = replace (s_IBAN, " ","")
  s_IBAN = replace (s_IBAN, "-","")

  pos = findCountryCode(left(s_IBAN,2) + str(len(s_IBAN)))
  if pos = 0 then
    printl "Either Country code does not exist or length of code not correct!"
    printl "IBAN is not valid!" : printl
    return false
  else
    idx=val(mid(countryCodes,pos+4,3))
    country = countries(idx)     
  end if
   
  ' move first 4 characters to end
  s_IBAN = mid(s_IBAN, 5) + left(s_IBAN, 4)
   
  ' replace A to Z with numbers 10 To 35
  n = ""
  int i
  for i = 1 to len(s_IBAN) 
    if mid(s_IBAN,i,1) >= "0" and mid(s_IBAN,i,1) <= "9" then
      'is number
      n &= mid(s_IBAN,i,1)
    elseif mid(s_IBAN,i,1) >= "A" and mid(s_IBAN,i,1) <= "Z" then     
      'convert to number 10..35
      n &= str(asc(mid(s_IBAN,i,1))-55)
    else
      printl "something wrong with IBAN number"
      printl "IBAN is not valid!" : printl
      return false
    end If
  next 
  'printl "IBAN converted to number :"
  'printl n

  if mod97(n) != 1 then
    print "Remainder not 1 - "
    printl "IBAN is not valid!" : printl
    return false
  end if

  print "Country: " Country " - "
  printl "IBAN may be valid." : printl
  return true   
end function

--------------------------------------------------------------
SetConsoleTitle "IBAN Checker"
bool iv
string IBAN

printl "This little program can be helpful for checking an IBAN "
printl "(International Bank Account Number) for errors e.g.:"
printl
IBAN = "GB82 WEST 1234 5698 7654 32" : printl IBAN
iv = validateIBAN(IBAN)
IBAN = "GB82 TEST 1234 5698 7654 32" : printl IBAN
iv = validateIBAN(IBAN)
IBAN = "GB81 WEST 1234 5698 7654 3" : printl IBAN
iv = validateIBAN(IBAN)
IBAN = "SA03 8000 0000 6080 1016 7519" : printl IBAN
iv = validateIBAN(IBAN)
IBAN ="CH93 0076 2011 6238 5295 7" : printl IBAN
iv = validateIBAN(IBAN)
--------------------------------------------------------------

string a
start:

printl : print "Would you like to check another IBAN? (Y/N) "
a = ucase(input())
if left(a,1) != "Y" then goto fin

cls
printl "Please enter an IBAN:"
IBAN = rtrim ltrim input()
if len(IBAN) > 3 then
  iv = validateIBAN(IBAN)
end if 

goto start

fin:
