Saturday, December 29, 2018

TCS Hiring Challenge 2018 - Mainframe - Part 1

Hello,

TCS has come up with hiring challenge for Mainframe professionals who are interested in pursuing an employment opportunity with them. I attempted the TCS Hiring Challenge on Dec 15th, 2018. It reminded me of those days I spent in Cognizant Academy, undergoing the training on Mainframes. To successfully pass the training, we had given tests which were very much similar to this challenge.

A day before the challenge, I received 2 emails from TCS. One with a sample question and the other one with instructions to join the challenge on Hackerrank. I used the latter email to join the challenge, the next day. Before starting the challenge, I was asked to agree to the consent that I'll not consult/copy code from any sources though I can refer manuals/use an IDE.



There were 2 questions which had to be finished within an hour. 1 for COBOL and the other one for SQL. I started the challenge with COBOL program.

The task was to write a COBOL program to display the even and odd numbers between two given numbers.

The program has to get 2 numbers from the user as input. Let's label the first input as LOWER and the 2nd input as UPPER. The code has to print the list of even and odd numbers between LOWER and UPPER.

For example, if LOWER and UPPER values are 01 and 19 respectively, the COBOL program should print the output in the following way:

Even numbers are:
02
04
06
08
10
12
14
16
18
Odd numbers are:
01
03
05
07
09
11
13
15
17
19

One constraint is that LOWER and UPPER should have values between 1 and 99.

I wrote the following code:
  • The code accept two inputs from user. The input values are assigned to WS-LOWER and WS-UPPER data-items respectively. 
  • I preferred using 88 Conditional names for WS-LOWER and WS-UPPER data-items to validate the constraint. This helps me avoid coding lengthy IF condtions.
  • Iterate between WS-LOWER and WS-UPPER using PERFORM loop.  
  • FUNCTION MOD, an intrinsic function in COBOL is being used to find whether a number is Even or odd. FUNCTION MOD outputs remainder which can be evaluated further. 
  • I've used tables with OCCURS DEPENDING ON to load the even and odd numbers and to display them as per the requirement. 

Output: The below output was displayed for WS-LOWER and WS-UPPER values as 01 and 28 respectively.

Even numbers are:
02
04
06
08
10
12
14
16
18
20
22
24
26
28
Odd numbers are:
01
03
05
07
09
11
13
15
17
19
21
23
25
27


I preferred coding in COBOL Coding Ground by Tutorialspoint rather than coding in the IDE supported by Hackerrank. You just need to write a working piece of code in COBOL Coding Ground. Once done with code, you can copy and paste it in Hackerranks's COBOL IDE and compile it.

You can try my code in COBOL Coding Ground here.

Things to note when you're compiling the COBOL program in Hackerrank:
Be cautious about AREA A and AREA B and code accordingly.
Use period and END-PERFORM or END-IF, for that matter, to end the scope of a loop or conditional statement. For example, it is better to end PERFORM loop by coding END-PERFORM.

I took 50 minutes to get the COBOL code running and passing the test cases. With 10 minutes left for the contest to end, I moved on with the next question. Unfortunately, the SQL query was a lengthy one requiring INNER JOIN on 4 tables. The contest ended while I was typing the query. Ufff!

I received an email within a week from TCS saying I was not selected. Hmm, That's alright!
Hope you found this article useful. Share your thoughts in the comments section.


Sunday, December 16, 2018

How to extract last byte of each record in VB and FB file using DFSORT


Hello,

In this post, I'll be showing how to extract last byte of each record, from a VB and FB file (in zOS).  I prefer using IBM's DFSORT utility to complete this task.

The last byte in a record might be anywhere within the record. 

Let's switch to apply our thinking process on real world example. Let's assume that we have a word document with following sentences.

My favourite number is 7
My favourite destination is Bhutan
I love Mainframes
I love IBM DFSORT
I love COBOL
You have this task of extracting the last letter of each sentence. As you see, the last letter is occurring in different positon for each sentence. With rich set of formatting tools in Word, we can align the sentences to look something like the below.



My favourite number is 7
My favourite destination is Bhutan
I love Mainframes
I love IBM DFSORT
I love COBOL


Right! Now that all the sentence are aligned to the right side, it's pretty much easy to read out the last letters in each sentence. You can even write a code to scan the word document and read last position of each sentence in the word document.

Let's switch back to Mainframes. Let's start with FB file. I'll be using JUSTIFY option in DFSORT. JUSTIFY option will align the data to make it look more presentable.

Access Left-justifying and right-justifying data to learn more about JUSTIFY.

I have created a FB file with LRECL of 80 and with some records as shown in the Picture 5.1  below.
Picture 5.1

I have prepared a JCL with some DFSORT statements as shown in Picture 5.2. I have preferred to format the records before sorting by using INREC FIELDS. Basically, with JUSTIFY, I am aligning each record to the right side, so that, the last byte, wherever it may be in the record, will be FORCED to occupy the 80th position, as the LRECL is 80.
Picture 5.2

Now, I can use OUTREC FIELDS to write the 80th byte to output. See Picture 5.3 for output.
Picture 5.3

We can do the same stuff with VB files, but we have to take care of two things. RDW and SHORT records.
  • RDW (Record Descriptor Word) is a 4 byte field describing the record. The first 2 bytes contain the length of the logical record. 
  • SHORT records: A variable-length input record may be too short to contain all specified control fields. A short record causes DFSORT to issue message ICE218A and terminate. 
Here's a VB file with LRECL 84 and with a record as shown in Picture 5.4 below. The record is 28 byte long (4bytes RDW + 24 bytes of record).
Picture 5.4

I have prepared a JCL as shown in Picture 5.5. 
Picture 5.5

I've used INREC IFTHEN to,
  1. align the record to the right side
  2. check if the logical record length (access first 2 bytes of RDW for the length) is less than the LRECL of the file and overlay the 84th byte with a blank. This will ensure short records are being padded with blanks and will prevent DFSORT from terminating.  Note that this will change the actual length in RDW (0028) to 84.
The output after submitting the JCL is shown in Picture 5.6.
  
Picture 5.6

Hope this helps. Use the Comments section below, to share your thoughts on this article. 


Screenshot courtesy: Mainframe access obtained via Master the Mainframe contest run by IBM.


Wednesday, December 5, 2018

How to reverse a string in COBOL without using FUNCTION REVERSE

Hello,

In this post I'll be showing how to reverse a string in COBOL without using an instrinsic (read instrinsic as in-built) function in COBOL, called FUNCTION REVERSE. This is also a common question asked in COBOL language interviews.

Pseudocode goes like this:
  1. Have the string you want to reverse, in a data-item.
  2. Define a data item (or) a table to store the reversed string.
  3. PERFORM a loop, with a data-item whose value is the length of the string, decremented by 1 in each iteration, until the data-item's value reached zero.
  4. Inside the loop, for each iteration, MOVE, starting from the last character of the string, either to the destination table (or) data-item.
  5. After loop, DISPLAY the reversed string.
In the following code, I've used a table to store the characters of the reversed string.

 IDENTIFICATION DIVISION.  
 PROGRAM-ID. REVERS.  
 DATA DIVISION.  
 WORKING-STORAGE SECTION.  
 01 WS-A   PIC X(50) VALUE 'SRINIVASAN'.  
 01 WS-TABLE.  
   05 WS-O OCCURS 0 TO 50 TIMES DEPENDING ON WS-LEN PIC X.   
 01 WS-I   PIC   9(3) VALUE 0.  
 01 WS-J   PIC   9(2) VALUE 1.   
 01 WS-LEN PIC 9(3) VALUE 0.   
 PROCEDURE DIVISION.  
     MOVE FUNCTION LENGTH(WS-A) TO WS-LEN.  
     PERFORM VARYING WS-I FROM WS-LEN BY -1 UNTIL WS-I = 0  
     MOVE WS-A(WS-I:1) TO WS-O(WS-J)  
     ADD 1 TO WS-J  
     END-PERFORM  
     DISPLAY WS-TABLE.   
     STOP RUN.  

Running the above COBOL program will give the following output. 


                                        NASAVINIRS

If you wish to use a data-item to store the reversed string, you can very well make use of reference modification to write the characters one by one to the destination data item as shown in the following code snippet.
 WORKING-STORAGE SECTION.  
 01 WS-A PIC X(50) VALUE 'SRINIVASAN'.  
 01 WS-OUT PIC X(50).   
 ...  
 ...  
 ...  
 PROCEDURE DIVISION.  
 MOVE FUNCTION LENGTH(WS-A) TO WS-LEN.  
 PERFORM VARYING WS-I FROM WS-LEN BY -1 UNTIL WS-I = 0  
 MOVE WS-A(WS-I:1) TO WS-OUT(WS-J:1)  
 ...  
 ...  
 ...  
 DISPLAY WS-OUT.   
 STOP RUN.  

Hope this helps!


Friday, July 13, 2018

Static and Dynamic call in COBOL and their compile JCL's

Much information on this topic is available in Google, but I personally felt that they are all scattered here and there. In this blog post, I've tried my best to explain the difference between Static and Dynamic call in COBOL and how to compile the COBOL programs that involve CALL statements.

We use CALL statements in COBOL to call sub-program(s).


What happens during a call?
Control from the Main program/Calling program is passed to the sub-program/Called program. The control however is returned back to the Main program, once the sub-program is done. EXIT/GO BACK statements are coded in the sub program to return the control back to the main program. 

Main program: Calling program.
Sub program: Called program. 


CALL statement in COBOL is usually coded inside the PROCEDURE DIVISION and in AREA B, as like the other statements. If you wonder what I just wrote in the previous line, then I strongly recommend you to check this link. If you wish to check the link later, PROCEDURE DIVISION in COBOL is where all the statements that does the processing, resides. AREA B is nothing but a rule that you should follow while writing COBOL programs. Certain entries must begin in AREA A (Columns 8 - 11) and others like CALL, must begin in  AREA B (Columns 12 - 72).


Right. Here we go!


What are the advantages in calling sub programs? 
Modularity: Anything which is modular is cool! Take a Desktop computer as example. There are various modules like RAM, Graphics Card, hard drive etc. If something doesn't work the way you want, you can just change that module. The sub programs that are being called from the Main program are the modules here. We can do changes to the sub program without modifying the main program. 

Reusability: Avoids duplication of effort.

Let's do a deep dive into COBOL's static and dynamic calls:
To explain calls, I'll be using a sample COBOL program to calculate the Tax amount (CGST+SGST) and the total amount payable while taking Amount and Tax percentage (GST) as Input to the main program. The tax amount and total amount payable will be calculated inside the Sub program. 

Static call and dynamic call both does the same stuff - to call the sub program. But, there are slight differences between the two and I've listed them below.



Static Call:
  • The called sub program is link-edited along with main program. 
  • The sub program's name will be enclosed within single quotes in the CALL statement. 
      • Ex: CALL 'SUBPGM' USING A B C. SUBPGM is the sub program's name. A, B and C are the arguments. 
  • Any changes made to the sub program will require the main program to be compiled along with the sub program. 
The main program is shown in Picture 3.1. Amount and GST percentage are passed as Input to the main program from the run JCL. 

Notice the CALL statement in line #17. This is CALL literal, where literal is the explicit name of the sub program, in this case, PGMB. 3 arguments are passed to the sub program; Amount, GST percentage and Total amount (for holding the total amount value i.e., Amount + Tax).

Note: Click on the picture to get an enlarged view.

Picture 3.1: Main program


The sub program is shown in Picture 3.2. Linkage section is defined to handle the data items that are passed as arguments to the sub program from the main program. In the PROCEDURE DIVISION, tax amount is calculated using Amount and GST percentage. GST is split into 2 components, CGST and SGST. Tax percentage is calculated for each component. Then, tax percentage is applied to the amount to derive total tax amount. Total amount payable is computed by adding amount and tax amount. LS-TOT-AMT data item in the sub program holds the final value. The corresponding data item of LS-TOT-AMT is used in the main program to display the Total amount payable. 
Picture 3.2: Sub program

GST helped me write a memory efficient program!
The Goods and Service Tax (GST) is a value-added tax levied on most goods and services sold for domestic consumption. The GST  shall have two components: one levied by the Centre (referred to as Central GST or CGST), and the other levied by the States (referred to as State GST or SGST). Since tax will be shared equally between the Central and State Government, I thought of using REDEFINES in COBOL for one of the tax data item. In the Picture 3.2, WS-SGST-P redefines WS-CGST-P, so it is enough to calculate the tax percentage for one data item i.e., WS-CGST-P. The other one (WS-SGST-P) will hold the same tax percentage as it shares the same memory as that of WS-CGST-P data item. 

Compile and link-edit the sub program(PGMB) as shown in Picture 3.3.
Picture 3.3: Compile and Link edit the Sub program


In COBOL's Static Call, Main program and Sub program are tied together in a same Load module:
When compiling the main program(PGMA), do a composite link by adding INCLUDE statements in the SYSIN of IEWL program, as shown in Picture 3.4. We are instructing the Link-edit program (in lines 35 thru 37) to include the load of PGMB from a private library (PRIVLIB) while creating the load module for PGMA. 
Picture 3.4: Do a Composite link of multiple COBOL programs and create an executable. 

Compile and Link-editing are done. It's time to run the main program.
Picture 3.5: Run JCL

The result, Picture 3.6. So much of tax!
Picture 3.6: Result!


Dynamic call:
  • The main program and the called program are part of different load modules i.e.sub program is not link-edited with the main program. 
  • If you make any changes to the sub program, you will only compile and link-edit the sub program. 
Notice the CALL statement in Picture 3.7, line #17. This is CALL identifier, where identifier is the data item, in this case, it is WS-PGM which contains the name of the sub program. 
Picture 3.7: Changes in the Main program for Dynamic call



Main program and sub program are compiled and link-edited separately. While compiling the main program, Compiler option DYNAM is provided in the PARM list, as shown in Picture 3.8. 
Picture 3.8: Compile Main program with compiler option as DYNAM. 

I obtained same results as shown in Picture 3.6, when calling the sub program dynamically. 

Key points to be noted: 
  • Default compiler option is NODYNAM. All calls made are static.
  • With DYNAM as compiler option, CALL literal statement calls the sub program dynamically. 
  • CALL identifier type calls are always dynamic.  

Same Stuff using Python:
I'm learning Python and I've just coded the example program in Python too. Feel free to use the interactive console on the right side of Trinket's tool to run Python. You have to call the main function and pass two arguments, Amount & GST %, as input. 
Ex: >>> main(3000,12)



Hope you found this post useful. Should you have any questions or suggestions, please share it in the Comments section below. thx.



Tuesday, February 13, 2018

How to pass PARM values from JCL to Easytrieve and use them to update a VSAM file.

Hello,

In this blog post, let's see how we can make use of EZTPX01 sub program in Easytrieve to get PARM values from the JCL. Also, we'll make use of the PARM values to update a VSAM file, all through Easytrieve.

The VSAM file I'm using in this example will have the following contents.

Contents of the VSAM file used for this example.


The first six bytes are the ID (which acts as the key) and the next 20 bytes starting from 8th position is the text field. Our objective is to add a string in the text field of the 3rd record with 345678 as the key.

The JCL with Easytrieve program is shown below:

JCL with the Easytrieve program.

What the program does? 

  • In the EXEC statement of the JCL, we are passing values using PARM keyword. 
  • In the Work variables' section, we have defined some variables for handling the PARM data. PARM-LEN is used to store the length of the PARM data and it should always be defined in 2 bytes of binary. PARM-DATA variable is used to store the PARM data i.e., '345678SRINI               '.
  • Remember that YOU are responsible for placing the maximum length you expect for the PARM information, into the 2-byte binary field before calling EZTPX01 sub program. 
  • Take a look at the fields that are being redefined in the Work variables section. PARM-LEN and PARM-DATA fields are redefining the PARM-INFO field which is of length 28 (26 bytes for the PARM data and 2 bytes for storing the length of the PARM data). Again, WS-ID and WS-NAME are redefining the PARM-DATA field in order to disintegrate the PARM data into two parts, ID and Text. 
  • Since we are going to use Controlled file processing, the JOB INPUT statement is NULL. 
  • The START procedure executes first and the control is passed to PARM-PARA. In the PARM-PARA, we are defining the maximum length of PARM data in the PARM-LEN field before calling EZTPX01 sub program. 
  • Please note that PARM data is truncated on the right if the data's length is more than the length you specified in the program. 
  • EZTPX01 requires two parameters, the system defined PARM-REGISTER, and a User-defined input/output field (in this example, it is PARM-INFO field). 
  • Issue a call to EZTPX01 sub program to get the PARM data. 
  • After receiving the PARM data, WS-ID field have the key (345678) and WS-NAME will have the text field data (SRINI               )  as these fields are redefining PARM-DATA field. 
  • READ the VSAM file with a special form of READ statement. Here, we do a Random read by accessing the VSAM file using the key. 
  • Note the READ statement with STATUS parameter. Specify the STATUS parameter whenever there is a possibility for unsuccessful completion of the input/output request. STATUS checks input/output processing to see if it was performed properly. STATUS causes the file's FILE-STATUS field to be set with the appropriate return code. FILE-STATUS is a read only field in Easytrieve. 
  • We evaluate the FILE-STATUS field to know the status of READ operation. If the READ is success, then we move the WS-NAME field to the corresponding field in the VSAM file and we issue a WRITE statement to the same VSAM file. 
  • Use UPDATE parameter in the WRITE statement to let Easytrieve know that you're updating the file. You can also use ADD or DELETE in the place of UPDATE. Don't forget to capture the STATUS though. 
  • The control comes out of the para when the WRITE is success and the program ends. 
Output:
The 3rd record is updated now.


Note the text field in the 3rd record which is now updated with the data passed in the PARM. 

Hope this helps! Share your thoughts in the comments section. 


References: