Task: IBAN








VB: Public nations As New Collection
Private Sub init()
    nations.Add 24, "AD"
    nations.Add 21, "CH"
    nations.Add 22, "GB"
    nations.Add 24, "SA"
    nations.Add 20, "XK"
End Sub

Private Function mod97(ByVal c As String) As Integer
    Dim n As Long
    n = Val(Mid(c, 1, 9))
    c = Mid(c, 10, Len(c) - 9)
    n = n Mod 97
    Do While Len(c) > 6
        n = Val(Str(n) & Mid(c, 1, 7))
        n = n Mod 97
        c = Mid(c, 8, Len(c) - 7)
    Loop
    n = Val(Str(n) & c)
    mod97 = n Mod 97
End Function

Private Function iban(ByVal code As String) As Boolean


    On Error GoTo 1
    lengths = nations(Mid(code, 1, 2))
    If Len(code) = lengths Then
        code = code & Mid(code, 1, 4)
        code = Mid(code, 5, lengths)
        Dim c As String: c = ""
        Dim ch As String
        For i = 1 To lengths
            ch = Mid(code, i, 1)
            If ch >= "0" And ch <= "9" Then
                c = c & ch
            Else
                If ch >= "A" And ch <= "Z" Then
                    c = c & Str(Asc(ch) - 55)
                Else
                    iban = False
                End If
            End If
        Next i
        c = Replace(c, " ", "")
        iban = mod97(c) = 1
    End If
    Exit Function
1:
    iban = False
End Function
 
Private Function iban_s(code As String) As Boolean

    code = Replace(code, " ", "")
    code = Replace(code, "-", "")
    iban_s = iban(code)
End Function
 
Private Sub test(code As String, expected As Boolean)
    Dim valid As Boolean
    valid = iban_s(code)
    Dim state As String
    If valid = expected Then
        state = IIf(valid, "ok", "invalid (as expected)")
    Else
        state = IIf(valid, "OK!!", "INVALID!!") & " (NOT AS EXPECTED)"
    End If
    Debug.Print code, state
End Sub

Public Sub main()
    init
    test "GB82 WEST 1234 5698 7654 32", True
    test "GB82 TEST 1234 5698 7654 32", False
    test "GB81 WEST 1234 5698 7654 32", False
    test "SA03 8000 0000 6080 1016 7519", True
    test "CH93 0076 2011 6238 5295 7", True
End Sub


COBOL:        IDENTIFICATION DIVISION.
       PROGRAM-ID. iban-main.

       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  iban                    PIC X(50).
       01  iban-flag               PIC X.
           88  is-valid            VALUE "Y", FALSE "N".

       PROCEDURE DIVISION.
       main-line.
           MOVE "GB82 WEST 1234 5698 7654 32" TO iban
           PERFORM display-validity

           MOVE "GB82 TEST 1234 5698 7654 32" TO iban
           PERFORM display-validity

           GOBACK
           .
       display-validity.
           CALL "validate-iban" USING CONTENT iban, REFERENCE iban-flag
           IF is-valid
               DISPLAY FUNCTION TRIM(iban) " is valid."
           ELSE
               DISPLAY FUNCTION TRIM(iban) " is not valid."
           END-IF
           .
       END PROGRAM iban-main.


       IDENTIFICATION DIVISION.
       PROGRAM-ID. validate-iban.

       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  country-lengths-area    VALUE "AD24AE23AL28AT20AZ28BA20BE16"
           & "BG22BH22BR29CH21CR21CY28CZ24DE22DK18DO28EE20ES24FI18FO18F"
           & "R27GB22GE22GI23GL18GR27GT28HR21HU28IE22IL23IS26IT27KW30KZ"
           & "20LB28LI21LT20LU20LV21MC27MD24ME22MK19MR27MT31MU30NL18NO1"
           & "5PK24PL28PS29PT25RO24RS22SA24SE24SI19SK24SM27TN24TR26VG24"
           .
           03  country-lengths     OCCURS 64 TIMES
                                   INDEXED BY country-lengths-idx.
               05  country-code    PIC XX.
               05  country-len     PIC 99.

       01  offset                  PIC 99.

       01  i                       PIC 99.

       01  len                     PIC 99.

       LINKAGE SECTION.
       01  iban                    PIC X(50).

       01  valid-flag              PIC X.
           88  is-valid            VALUE "Y", FALSE "N".

       PROCEDURE DIVISION USING iban, valid-flag.
           MOVE FUNCTION UPPER-CASE(iban) TO iban
           CALL "remove-spaces" USING iban

           
           INITIALIZE len
           INSPECT iban TALLYING len FOR CHARACTERS BEFORE SPACE
           SET country-lengths-idx TO 1
           SEARCH country-lengths
               AT END
                   SET is-valid TO FALSE
                   GOBACK

               WHEN country-code (country-lengths-idx) = iban (1:2)
                   IF country-len (country-lengths-idx) NOT = len
                       SET is-valid TO FALSE
                       GOBACK
                   END-IF
           END-SEARCH

           CALL "create-iban-number" USING CONTENT len, REFERENCE iban

           
           IF FUNCTION MOD(iban, 97) = 1
               SET is-valid TO TRUE
           ELSE
               SET is-valid TO FALSE
           END-IF
           .

       IDENTIFICATION DIVISION.
       PROGRAM-ID. remove-spaces.

       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  i                       PIC 99.
       01  offset                  PIC 99.

       LINKAGE SECTION.
       01  str                     PIC X(50).

       PROCEDURE DIVISION USING str.
           INITIALIZE offset
           PERFORM VARYING i FROM 1 BY 1 UNTIL i > 50
               EVALUATE TRUE
                   WHEN str (i:1) = SPACE
                       ADD 1 TO offset
               
                   WHEN offset NOT = ZERO
                       MOVE str (i:1) TO str (i - offset:1)
               END-EVALUATE
           END-PERFORM
           MOVE SPACES TO str (50 - offset + 1:)
           .
       END PROGRAM remove-spaces.


       IDENTIFICATION DIVISION.
       PROGRAM-ID. create-iban-number.

       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  first-four              PIC X(4).

       01  iban-num                PIC X(50).
       01  digit-num               PIC 99 VALUE 1.       

       01  i                       PIC 99.

       01  letter-num              PIC 99.

       LINKAGE SECTION.
       01  len                     PIC 99.

       01  iban                    PIC X(50).

       PROCEDURE DIVISION USING len, iban.
           
           MOVE iban (1:4) TO first-four
           MOVE iban (5:) TO iban
           MOVE first-four TO iban (len - 3:)

           
           INITIALIZE iban-num, digit-num ALL TO VALUE
           PERFORM VARYING i FROM 1 BY 1
                   UNTIL i > len OR iban (i:1) = SPACE
               IF iban (i:1) IS NUMERIC
                   MOVE iban (i:1) TO iban-num (digit-num:1)
                   ADD 1 TO digit-num
               ELSE
                   COMPUTE letter-num =
                       FUNCTION ORD(iban (i:1)) - FUNCTION ORD("A") + 10
                   MOVE letter-num TO iban-num (digit-num:2)
                   ADD 2 TO digit-num
               END-IF
           END-PERFORM

           MOVE iban-num TO iban
           .
           
       END PROGRAM create-iban-number.

       END PROGRAM validate-iban.