Wednesday, March 24, 2021

Printing the pattern of letters from A to Z using the respective letter itself

Hiya! Hope 🀞 this blog post will be fun πŸ˜€ as I really enjoyed writing this particular code which you will be witnessing shortly. 

When I saw the black screens πŸ’» for the first time when I was trained on Mainframe (that was in 2014 πŸ“…), I was totally amused.  The logon screen had something similar to what we have got now on the logon screen of IBM's Master the Mainframe 2020 system.

Click on the image for a larger version.

Each letter's shape in the string 'z/OS' is printed using the same letter, that too in Italics. 

Intro

I was longing to write a COBOL program which would print the ASCII character strings (just the letters A-Z for now) in large size to the output, using the respective letter itself (sometimes people prefer asterisk '*' or '#' to print shapes). This blog post is all about the approach I took to come up with such a program. 

At the end of this post, there's a short write-up on publishing a code on GitHub as I'm used to Endevor and GitHub (a code hosting platform) is new to me. 

Approach

Printing just one letter's shape in the output is simple. All you need is a PERFORM VARYING loop in COBOL, a logic to form the letter's shape and a DISPLAY statement printing the lines for each iteration of the loop. An example can be found πŸ‘‰ here.  

However, printing each letter's shape, of the string, from left to right is little bit tricky. I chose 7 x 7 cell to spread out the shape of each letter. An excel sheet, in which I drew all the 26 letter's shapes, came in handy as I referred the sheet before going to code the logic for each letter. 

An excel sheet with each alphabet's pattern spread out in a 7 x 7 cell.

I leveraged the two-dimensional table, to store the letter's shape. The COBOL code is as follows:


WS-LINE is an element of one-dimensional table that occurs 7 times. Assume each element of WS-LINE as a row.
 
WS-LETTER is an element of a two-dimensional table that occurs 70 times in each occurence of WS-LINE. Assume each element of WS-LETTER as a column. 

For readability, I've limited the maximum length of the string, input by the user, to 10 bytes. Hence, each line is 70 bytes long. 

When I was halfway with my code, I ran πŸƒ some tests only to realize that a space in between each letter's shape would be clear enough to read. 

That isn't easy to read. BAD πŸ™ˆ


Hence, I came up with yet another multi-dimensional table solely for printing purpose. 


This table gets data from the former 2D table (which we've already seen before) only at the point of displaying the entire stuff. 

In the PROCEDURE DIVISION, there are references to 3 para's,
  1. which would ask for the input string from the user; validate it. Upon successful validation of user input (read the next item),
  2. go through each letter of the string one by one with help of Reference Modification in COBOL; call the para corresponding to each letter - to print its shape - with the help of EVALUATE verb.
  3. display output and STOP RUN.
There are 26 para's coded to print the shape of 26 alphabets in English. A lot of PERFORM VARYING loops and COMPUTE statements are used to build 🧱 the logic in each para. Let's look at the code for one of those para.


This πŸ‘† part of code prints the shape of letter T. There are 2 PERFORM VARYING loops ➿. 
Let's look at the first loop ➰. 
  • The first loop iterates for 7 times with WS-J data item's value ranging from 0 to 6 and forms the horizontal line of letter T's shape.
  • WS-I data item holds the position of the letter in the string entered by the user. WS-I data item's value is multiplied with 7 to put the letter's shape in the right set of rows and columns. 
  • COMPUTE statement is coded before the MOVE statement because arithmetic expressions aren't supported (on IBM Enterprise COBOL for z/OS  6.3.0 compiler) in the subscripting.
  • The MOVE statement moves the letter T to WS-LETTER which is an element of two-dimensional table. In a two-dimensional table, the two subscripts correspond to the row and column numbers.
Given the following 7x7 cell:



The following happens in each iteration of the first loop, if WS-I's value is assumed as 1:

1st iteration:
WS-I = 1
WS-J = 0
WS-TEMP = 7
Letter T is moved to WS-LETTER(1, 7)

2nd iteration:
WS-I = 1
WS-J = 1
WS-TEMP = 6
Letter T is moved to WS-LETTER(1, 6)

3rd iteration:
WS-I = 1
WS-J = 2
WS-TEMP = 5
Letter T is moved to WS-LETTER(1, 5)

4th iteration:
WS-I = 1
WS-J = 3
WS-TEMP = 4
Letter T is moved to WS-LETTER(1, 4)

5th iteration:
WS-I = 1
WS-J = 4
WS-TEMP = 3
Letter T is moved to WS-LETTER(1, 3)

6th iteration:
WS-I = 1
WS-J = 5
WS-TEMP = 2
Letter T is moved to WS-LETTER(1, 2)

7th iteration:
WS-I = 1
WS-J = 6
WS-TEMP = 1
Letter T is moved to WS-LETTER(1, 1)

At the end of first loop, the cell will look like below:


The second loop does something similar to the first loop and it moves the letter to all the rows in 4th column of 7x7 cell. At the end of second loop, the shape of letter will be formed. 



Executing the code..

The full code is available πŸ‘‰ here in JDOODLE, an online compiler and editor for many programming langauges including COBOL. 

The COBOL program that I've written mimics the functionality of Banner command in Linux.

After clicking on the link, just scroll to the bottom of the code and give a string, max. of 10 characters, in Stdin Inputs and click Execute button in the blue box. After the execution, the result will be displayed in the Result area (black colored rectangle box). 

Output after running the code in JDOODLE.

Please note the following before providing input in Stdin Inputs tab:
  • Numbers and symbols like hyphen (-), dollar ($) etc., aren't supposed to be entered. The program code is hardwired with logics to form shapes only for the 26 alphabets (in upper-case). If numbers and symbols are part of the string, they will be replaced with spaces. 
  • I've used an intrinsic function (FUNCTION UPPER-CASE) to convert any string entered by the user to upper-case.
  • Please limit the input string to a maximum of 10 characters. String with length beyond 10 will be truncated.
  • If there are spaces in between the string, user will be prompted to re-enter another string without spaces in between. 
Stdin Inputs has got 3 lines of input string; first 2 lines has got strings with a space in between. Note the messages in the Result area.


Scope for improvement

  • Improvements can be made to the existing code to shorten the total number of lines.
  • Logics for lower-case alphabets, numbers and symbols can be added.
  • Length of the string, input by the user can be extended. 
  • The height and width of the cell is fixed for now and can be made scalable by altering the code.

GitHub

GitHub hosts millions of projects written in different programming languages. Each project is placed in its own container called a repository (repo) that can store code and other files of the project. Any changes to the files within a repo will be tracked via version control.

Each repo has got a name. There can be lots of repositories with same name. Hence, it's always better to use a link to locate the repo you're looking for. 

Go ahead and open this πŸ‘‰ repo I've created for this project. 

By default, this repository which I've created has got only one branch named main. Having the code in main branch is similar to having the code in the Production stage of  CA Endevor, a source code management tool for z/OS. 

If you want to do some edits on the code, you take a copy of the code residing in the Production stage of CA Endevor to your personal PDS. Likewise, in GitHub we use branches to make edits before commiting them to main. When a new branch is created, a new copy or snapshot of main is made.

There are 2 files in the main branch of the repository I've created for this project. A README file - which describes the project -  and a file named CBL1 which has got the COBOL program. 

All you need is an account on GitHub to create new branch for yourself in order to suggest edits/improvements for the code. commit by saving your changes. Open a pull request to propose your changes and request someone to review by using GitHub's @mention system. pull requests are merged  to the main branch when the new changes are reviewed and are good to go πŸ‘ .


That's it for now! πŸ”š

Hope you liked this post. Should you have any questions/suggestions, please post it in the comments section below.

ThxπŸ‘!



Friday, March 19, 2021

Solving a problem statement using IBM DFSORT

Folks, in this post let's try to solve a problem - which I recently came across - using IBM's DFSORT utility. 



Problem statement

There are 2 flags for an account number. 
If both the flags are 'Y', the output dataset should contain two records with account number and the name of the flag. 
If any one of the flag is 'Y', output dataset should contain only one record with account number and the name of the flag. 

For example, Account AAA has got two flags set to 'Y', so the output dataset should contain two records for the account AAA.

Input:



Output:



My approach


As soon as I realized that a record in the input must be broken into 2 when both the flags of an account number are 'Y', I recalled the usage of / or n/ which is used to insert blank records in the output. But,  / or n/ is supported only by the OUTFIL control statement. 

So, OUTFIL control statement with IFTHEN...WHEN condition can be used to validate the flags and write the account number and the name of the flag to the output dataset. 

As the problem statement implicitly states that an account number with both the flags as 'N' be omitted, OMIT COND can be used to exclude such records before sorting.

Alright! Let's code the control statements. 

 =COLS> ----+----1----+----2----+----3----+----4----+----5----+----6----+----7--  
 ****** ***************************** Top of Data ******************************  
 000001 //Z01071A JOB 1,NOTIFY=&SYSUID                       
 000002 //STEP01 EXEC PGM=SORT                           
 000003 //SORTIN DD *                               
 000004 AAA Y Y                                   
 000005 BBB Y N                                   
 000006 CCC N Y                                   
 000007 DDD N N                                   
 000008 //SORTOUT DD SYSOUT=*                            
 000009 //SYSOUT DD SYSOUT=*                            
 000010 //SYSIN  DD *                               
 000011  SORT FIELDS=COPY                             
 000012  OMIT COND=(5,1,CH,EQ,C'N',AND,7,1,CH,EQ,C'N')               
 000013  OUTFIL IFTHEN=(WHEN=(5,1,CH,EQ,C'Y',AND,7,1,CH,EQ,C'Y'),         
 000014     BUILD=(1,3,X,C'KA',/,1,3,X,C'TN')),                
 000015     IFTHEN=(WHEN=(5,1,CH,EQ,C'Y',AND,7,1,CH,EQ,C'N'),         
 000016     BUILD=(1,3,X,C'KA')),                       
 000017     IFTHEN=(WHEN=(5,1,CH,EQ,C'N',AND,7,1,CH,EQ,C'Y'),         
 000018     BUILD=(1,3,X,C'TN'))                        
 ****** **************************** Bottom of Data ****************************  
There are 3 conditions coded in the OUTFIL IFTHEN control statement. 
  • The first condition (in line #13) checks for both the flags to be 'Y'. If true, 2 records should be written in the output dataset with the account number and the name of the flag in each record. Note the BUILD parameter (in line #14) with a / to indicate a new output record to be started after writing the account number with the first flag name. The account number and the second flag's name will be written in the new output record. 
  • The second and third conditions writes to the output dataset, the account number and the flag name, if any of the flag is 'Y'.

Output after submitting the JCL:
 ********************************* TOP OF DATA **********************************  
 AAA KA                                       
 AAA TN                                       
 BBB KA                                       
 CCC TN                                       
 ******************************** BOTTOM OF DATA ********************************  
There you go!

Now, it's your turn. Use the comments section to show how your approach would've been (or will be) for this problem statement.