Saturday, January 9, 2021

Writing a REXX Program to create a Valid Luhn Number generator | Master the Mainframe 2020 | Level 3.3: REXX2: Nexxt Level Rexx

Welcome to my blog! This is my first post of this new year, 2021. Happy New Year to one and all 😄

In this post, we'll look at Level 3.3 of Master the Mainframe 2020. If you haven't registered for MTM2020 yet, then you're missing the fun 😐. Master the Mainframe has been a lot interesting this year (2020) as we are using Visual Studio Code (for the first time) to establish a connection with z/OS. Hit this link to register.

Level 3.3: REXX2: Nexxt Level Rexx is all about building complex logic and functionality using REXX. The task is to write a REXX Exec that acts as a generator, which when executed, will output Luhn-Algorithm compatible 16 digit numbers. The code shouldn't take any parameters though.



If you do not have prior experience in writing REXX exec's, do not worry at all. 

  • MTM2020 is the right place to get your hands on REXX. There are couple of challenges in Level 3 which will improve your REXX skills in the best way possible.
  • Advertisement alert ⚠: I also recommend you to read a blog post of mine 😀 which is intended for beginners in REXX. 
  • Jim Barry's REXX tutorial is my personal favourite, so I recommend that as well. 


It's time ⏰ to go further... 

The first step in the challenge instructions will ask you to copy a member named CCVIEW from MTM2020.PUBLIC.SOURCE to your own SOURCE dataset. Let's take a look at the REXX Exec residing in CCVIEW member, section by section.

 In a nutshell, this REXX Exec reads an input file, MTM2020.PUBLIC.CUST16, line by line over a do while loop until end of the file and writes each line to an output file, if the read line is longer than 10 characters. We should be aware of few commands in REXX before venturing into a REXX Exec that is dealing with files (like this one 👆). 

First things first, the dataset(s) that you're going to use in a REXX Exec should be allocated to the address space that the REXX is running under. This is usually done using the ALLOC command as shown in the lines 11 and 12. The ALLOC command can be used by a REXX program to dynamically allocate necessary datasets. 

Defining ALLOC command for a dataset in REXX Exec is very much similar to defining a dataset in a JCL DD statement for I/O. 

Let's go thru the command given in line 11. 

"ALLOC FI(indd) DA('MTM2020.PUBLIC.CUST16') SHR REUSE" 

FI refers to the name that will be used by other commands in this REXX Exec, like EXECIO,  to refer to this file (Kinda shorthand 😉). DA refers to the real dataset name. The disposition SHR indicates that the file is intended for reading. It is imperative to note that any dataset allocated within a REXX Program using the ALLOC command should be released back to the system using the FREE command, after the file has been used. More about ALLOC can be found 👉 here.

After the dataset is allocated, we can read the file by the EXECIO command using the DISKR parameter. Let's look at line 23. 

"EXECIO 0 DISKR indd (OPEN"

The EXECIO command is used for processing files. 0 along with DISKR indicates that 0 records should be read from the file referenced by indd DD name.
R in the DISKR is for Read. Similarly, W in the DISKW is for write. X in the DISKX will issue an error because there is no such thing as DISKX 😅. Just DISKW and DISKR.
(OPEN indicates that the file should be opened for future use. Another example of EXECIO command can be found in line 31. 

"EXECIO 1 DISKR indd"

This EXECIO command resides within a DO loop ➿. Under each iteration, this EXECIO reads 1 record from indd. The records read will be written to the stack on a first-in, first-out basis . The records are then available by using the PULL or PARSE PULL instructions. 

Lines 30-45 are already explained in the challenge instructions. Shamelessly copying it here 😒. 
There are three nested DO statetments here. The outermost loop is just a check to make sure there are more lines to read, the middle loop iterates through those lines, and the innermost loop checks if the length of the line is greater than 10 characters, before writing it to the output file.

Make note of line 41 which is commented out. 

call INSPECT

The call instruction calls 📞 an internal routine named as INSPECT. Routines are made up of sequence of instructions that can receive data, process it, and return a value. INSPECT label with a colon can be found at line 80. When REXX executes the call instruction at line 41, the control passes to line 80. The instructions under the INSPECT routine are then executed, in this case, just a SAY instruction, which outputs a message to user. RETURN statement at line 82 returns back the control to the CALL instruction. 

Both the input and output files are closed in lines 46 and 57 respectively. FINIS in the command indicates that the dataset should be closed after use. If FINIS is omitted, the datset will remain open for future use. 

The datasets are freed by FREE instruction in lines 76 and 77 🆓. 

When you run this REXX Exec after replacing ZXXXXX with your user ID in line 12, you will notice that the output file looks the same as the input file. You now have a code that runs and it's time to build upon that foundation to make it do something else. 

What's the ask?

Your first task is to add a logic to this program that checks whether the credit card number being read in satisfies the Luhn algorithm. You may probably have the logic for validation coded inside the INSPECT routine. 

Once you have the logic for validating input for a Luhn number, the final task of this challenge is to build a generator that outputs Luhn-algorithm compatible 16 digit numbers. The final task should not read any input files or parameters. When the code is executed, it should write 500 unique numbers, one on each line, to a PDS member (ZXXXXX.OUTPUT(CUST16)). You have to use the ALLOC command to allocate the PDS member. 

Reading the challenge instructions carefully plays a vital role in finishing this challenge successfully. You have got several hints 💡 in there and let me list them out here. 

  • REXX is a dynamically typed language. This means you are able to store several values of different types in a single variable during your code execution and no errors will occur. 
    • For example, consider the following 2 lines.

       odd_digits = 0
       odd_digits = odd_digits + substr(cc_digits,5,1)

    • In the second line, REXX actually understands the data by its usage. It automatically converts the type of the output from the second operand (substr() function) to perform the arithmetic operation. 
  • Step 9 in the challenge instructions list out some built-in functions which you may find useful to accomplish the task.
    • SUBSTR – Substring – returns just the characters at a specific location within a string. There’s an example of this in action at line 40. 
    • LENGTH – Returns the total length, in characters, of a string. 
    • MATH OPERATORS – In particular, the // symbol, which returns the remainder after dividing by a number.
    • RANDOM – is a built-in function in REXX and it is used to generate a random non-negative whole number between the min and max range that are provided as arguments. 


What was my approach to finish this challenge 🤔? 

I came across the Credit card anatomy which goes like below:

Perceive lines 4 thru 16 as a Credit Card 💳. The first number is Major Industry Identifier (MII) which tells you what sort of institution issued the card. Lines 21 thru 27 shows the list of institutions. 

The first six digits are the Issuer Identification Number (IIN). These can be used to look up where the card originated from. Lines 31 thru 35 might ring some bells 🔔.

The 7th digit to second-to-last digit is the customer account number. Most companies use just 9 digits for the account number, thereby making the Credit card number as 16 digits. But, it's possible to use up to 12 digits for the account number. 

The last digit of a credit card is the check digit or checksum. 

With this info in hand, I generated the random credit card number in the following way: 

/* Major Industry Identifier */                               
random1 = random(1,9)                                         
                                                              
/* random1 to random3 together generates BIN */               
random2 = random(111,999)                                     
random3 = random(10,99)                                       
                                                              
/* random4 to random6 together generates account identifier */
random4 = random(1,9)                                         
random5 = random(1111,9999)                                   
random6 = random(1111,9999)                                   
part2 = random1||random2||random3||random4||random5||random6  
cc_digits = '000' || substr(part2,1,15)    

Note that I had generated only 15 digits of the credit card number using random built-in function (exclude the first 3 digits, 000). The last digit, checksum is calculated using a formula, 

c = (10 − (s mod 10) mod 10)      

where,
c is checksum,
s is  (sum of digits placed in odd positions) + (sum of each digit placed in even positions multiplied by 2) 
Note: if value of each digit placed in even positions multiplied by 2 is greater than 9, then subtract the value by 9. 

After the checksum is calcualted, the 16 digits of credit card number is formed by concatenating the 15 digits, generated out of the random function, with the checksum digit. 

cc_digits = strip(cc_digits||strip(checksum))

strip is a built-in function in REXX which is used to remove the leading and trailing spaces.
|| is the operator used for concatenating 2 strings. 

I had created a separate routine to calculate the checksum. Once the checksum is calculated, the 16 digit credit card number is passed on to another routine, which will validate the number to check if it is Luhn-compatible or not. If the number is valid, it will be written to a compound variable. 

Compound variables:

Variables are treated as compound variables if the variable name ends with a "." (period). All the 500 Luhn-compatible numbers are written to a compound variable named 'out.'. I had used do loops in combination with compound variables to write the numbers. After writing, if you refer to, out.1, it will fetch you the first Luhn-compatible number. Likewise, out.499 will fetch you the 499th Luhn-compatible number. out.0 will give you the count of total number of records present in the compound varaible, 'out.'. 

After storing 500 Luhn compatible numbers in the compound variable, I used the EXECIO command to write the compound variable to the output file. 

"EXECIO * DISKW outdd (STEM out."

The * and DISKW indicates that all the records from the STEM (i.e., compound variable), OUT. should be written to the output file outdd.

Some useful tips:

  • Try this website to validate the 500 numbers you generate out of your code, to be Luhn-compatible or not. 
  • I recommend you to use Vista TN3270 terminal for this challenge to logon to the MTM system (IP: 192.86.32.153 PORT: 623) rather than VS code. Coding and running your REXX exec will be a lot easier in TN3270 terminal. 
  • Coding TRACE I in the 2nd line of your REXX Exec and executing the code will result in debugging mode. This might help you in understanding the program's flow, if things are not working out. More about TRACE can be found here.
  • Bonus hint: While using DO loops to traverse across the even and odd positions of the 19 digit credit card number, you may have to step 2 times in each iteration. The syntax in such cases is as follows:
                    do i = 5 by 2 to 17
            (......)
         end
         
         
         do j = 4 by to 18
            (......)
         end

About sharing my solution.. 

Nope😐, I respect the individuality and the fun element in coding these challenges and have opted not to share the complete code. Instead, I believe I've done my best in explaining several jargons used in REXX and those which, when understood, might be of help to you in finishing this challenge. If you still need clarity on any topics, please let me know. 

Hope this helps. Should you have any questions, please post it in the comments section below. Thx.  


2 comments:

  1. This comment has been removed by the author.

    ReplyDelete
  2. Thank you for explaining in simple terms. I am a beginner and was struggling it.

    ReplyDelete