/* IBAN (Rosetta Code) in Picat. From http://rosettacode.org/wiki/IBAN """ 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. The task here is to validate the following fictitious IBAN: GB82 WEST 1234 5698 7654 32. Details of the algorithm can be found on the Wikipedia page. """ http://en.wikipedia.org/wiki/International_Bank_Account_Number """ Validating the IBAN An IBAN is validated by converting it into an integer and performing a basic mod-97 operation (as described in ISO 7064) on it. If the IBAN is valid, the remainder equals 1.[Note 1] The algorithm of IBAN validation is as follows:[8] - Check that the total IBAN length is correct as per the country. If not, the IBAN is invalid - Move the four initial characters to the end of the string - Replace each letter in the string with two digits, thereby expanding the string, where A = 10, B = 11, ..., Z = 35 - Interpret the string as a decimal integer and compute the remainder of that number on division by 97 If the remainder is 1, the check digit test is passed and the IBAN might be valid. Example (fictitious United Kingdom bank, sort code 12-34-56, account number 98765432): • IBAN: GB82 WEST 1234 5698 7654 32 • Rearrange: W E S T12345698765432 G B82 • Convert to integer: 3214282912345698765432161182 • Compute remainder: 3214282912345698765432161182 mod 97 = 1 """ This Picat model was created by Hakan Kjellerstrand, hakank@gmail.com See also my Picat page: http://www.hakank.org/picat/ */ import util. % import cp. main => go. go => IBAN = "GB82 WEST 1234 5698 7654 32", iban(IBAN), println(ok), nl. go2 => 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", "GB82 TEST 1234 5698 7654 32", "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" ], foreach(IBAN in Ibans) println(IBAN), if iban(IBAN) then println("ok") end end, nl. go3 => S = "0123456789ABCDEFG", println(S), Converted = [convert(C) : C in S], println(Converted), nl. iban(IBAN) => nations(Nations), IBAN2 = IBAN.delete_all(' ').to_uppercase(), length(IBAN2) > 2, % sanity check Len = IBAN2.length, if Len != Nations.get(slice(IBAN2,1,2),0).to_integer() then println("wrong country length"), false end, IBAN3 = slice(IBAN2,5,Len) ++ slice(IBAN2,1,4), if [convert(C) : C in IBAN3].join('').to_integer() mod 97 != 1 then println("bad mod"), false end. % convert char: 1=1,...9=9, A=10, B=11,Z=35 convert(C) = cond(ord(C)-65 >= 0,ord(C)-55,ord(C)-48).to_string(). % lengths for different nations nations(Nations) => Nations = new_map( ["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]).