RosettaCodeData/Task/IBAN/ALGOL-68/iban.alg
2025-02-27 18:35:13 -05:00

104 lines
6 KiB
Text

BEGIN # validate IBAN numbers #
# country code and expected bank account length for IBAN codes #
MODE IBANCOUNTRY = STRUCT( [ 1 : 2 ]CHAR code, INT length );
[]IBANCOUNTRY country info =
( ("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)
);
# returns "" if s could be a valid IBAN, an error message otherwise #
OP POSSIBLEIBAN = ( STRING s )STRING:
BEGIN
BOOL is valid := TRUE;
STRING result := "";
# remove spaces and check we only have digits and spaces #
STRING iban := "";
FOR s pos FROM LWB s TO UPB s WHILE is valid DO
CHAR c = s[ s pos ];
IF c /= " " THEN
IF NOT ( is valid := ( c >= "A" AND c <= "Z" ) OR ( c >= "0" AND c <= "9" ) )
THEN result := "Invalid character: " + c
FI;
iban +:= c
FI
OD;
IF is valid THEN
# OK so far - check the length matches the country code #
INT supplied length = ( UPB iban - LWB iban ) + 1;
is valid := FALSE;
IF supplied length <= 4
THEN result := "Code is too short"
ELSE # enough characters for a country code and a check sum #
INT expected length := -1;
STRING supplied country = iban[ LWB iban : LWB iban + 1 ];
FOR c pos FROM LWB country info TO UPB country info WHILE NOT is valid DO
IF is valid := code OF country info[ c pos ] = supplied country THEN
expected length := length OF country info[ c pos ]
FI
OD;
IF NOT is valid
THEN result := "Not a known IBAN country: " + supplied country
ELIF NOT ( is valid := expected length = supplied length )
THEN result := "Expected code length: " + whole( expected length, 0 )
+ " but actual length is: " + whole( supplied length, 0 )
ELSE # OK so far - check the checksum #
STRING rearranged iban = iban[ LWB iban + 4 : ] + iban[ LWB iban : LWB iban + 3 ];
LONG LONG INT numeric iban := 0;
FOR c pos FROM LWB rearranged iban TO UPB rearranged iban DO
CHAR c = rearranged iban[ c pos ];
IF c >= "0" AND c <= "9" THEN
numeric iban *:= 10 +:= ( ABS c - ABS "0" )
ELSE
numeric iban *:= 100 +:= ( ABS c - ABS "A" ) + 10
FI
OD;
IF NOT ( is valid := numeric iban MOD 97 = 1 )
THEN result := "Incorrect checksum"
FI
FI
FI
FI;
IF is valid THEN "" ELIF result = "" THEN "Unknown error" ELSE result FI
END # POSSIBLEIBAN # ;
BEGIN # tests #
[]STRING valid codes = ( "GB82 WEST 1234 5698 7654 32", "GB82WEST12345698765432"
, "GR16 0110 1250 0000 0001 2300 695", "GB29 NWBK 6016 1331 9268 19"
, "SA03 8000 0000 6080 1016 7519", "CH93 0076 2011 6238 5295 7"
, "IL62 0108 0000 0009 9999 999"
);
[]STRING invalid codes = ( "gb82 west 1234 5698 7654 32" # invalid characters #
, "GB82 TEST 1234 5698 7654 32" # invalid check digits #
, "IL62-0108-0000-0009-9999-999" # invalid characters #
, "US12 3456 7890 0987 6543 210" # invalid country code #
, "GR16 0110 1250 0000 0001 2300 695X" # invalid code length #
, "" # code is too short #
, "G2" # code is too short #
, "GB271" # invalid code length #
);
FOR t pos FROM LWB valid codes TO UPB valid codes DO
STRING code = valid codes[ t pos ];
STRING message = POSSIBLEIBAN code;
IF message = ""
THEN print( ( "Possible IBAN: ", code, newline ) )
ELSE print( ( "INVALID IBAN: ", code, ": UNEXPECTED ERROR: ", message, newline ) )
FI
OD;
FOR t pos FROM LWB invalid codes TO UPB invalid codes DO
STRING code = invalid codes[ t pos ];
STRING message = POSSIBLEIBAN code;
IF message = ""
THEN print( ( "POSSIBLE IBAN: ", code, " BUT EXPECTED AN ERROR MESSAGE", newline ) )
ELSE print( ( "Invalid IBAN: ", code, ": ", message, newline ) )
FI
OD
END
END