

 This page uses content from Wikipedia. The original article was at IBAN. The list of authors can be seen in the page history. As with Rosetta Code, the text of Wikipedia is available under the GNU FDL. (See links for details on variance)



The   International Bank Account Number (IBAN)   is an internationally agreed means of identifying bank accounts across national borders with a reduced risk of propagating transcription errors.

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). 


The check digits enable a sanity check of the bank account number to confirm its integrity even before submitting a transaction.



Task

Validate the following fictitious IBAN:    GB82 WEST 1234 5698 7654 32 



Details of the algorithm can be found on the Wikipedia page.




 
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.
 