Computer Science Assignment 3

COMP 2103X1 Assignment 2 Due Thursday, February 2 by 7:00 PM General information about assignments ( important!):

http://cs.acadiau.ca/ ~jdiamond/comp2103/assignments/General-info.html Information on passing in assignments:

http://cs.acadiau.ca/ ~jdiamond/comp2103/assignments/Pass-in-info.html Information on coding style:

http://cs.acadiau.ca/ ~jdiamond/comp2103/assignments/C-coding-style-notes [1] When reading input from a le (or from stdin), there are various ways to know when to stop reading. Here are some possibilities:

(i) the program always needs a xed and known amount of data; (ii) the program reads all data until EOF; (iii) the program reads until invalid data (or EOF) is found; (iv) the program reads until some special data item which indicates the end of the data (a sentinel) is found; or (v) there might be some information at or near the beginning of the data which gives the amount of data to read.

And, of course, there are others. Perhaps you can think of some.

In this problem, your program will read integers from stdinuntil EOF or invalid input. The program will then output the second smallest number in the input.

Here are some examples of running your program, assuming that you name your program a2p1.

Note how I can use the echocommand to pass short sequences of input into a program which reads from stdin . (This is another reason why programs which read from stdinand write to stdout are nice.) The user input is inred. %cat nums.dat 6 8 -12 0 33 99 17 11 3 %a2p1 < nums.dat 0 %echo 5 4 1 2 3 6 | a2p1 2 %echo -3 -4 -8 -12 11 | a2p1 -8 %echo 9 8 7 six five four | a2p1 8 %echo 4 2 7 8 2 3 999 | a2p1 2 1 In the last case, note that the two smallest data items are both 2. In this case, 2 is not only the smallest data item, it is also the second smallest. In the second last case, after the program reads 7 it attempts to read another integer and fails, and so at that point it stops and outputs its answer.

Also note that the input can be all on one line, it can be one number per line (no example of that is shown), and it can be mixed.

To give you an industrial­sized problem to work on, I will put a le called generate-input.o in the assignments directory. You can create a program from this by typing %gcc -Wall -Wextra -std=gnu11 generate-input.o -o generate-input and then you can run a test with the command %generate-input | a2p1 NOTE: if you are using a 32­bit version of Linux, download and use generate-input-32bit.o instead. If you don't want to use a browser to download the le, and you have the command wget on your system, you can download the le with this command: %wget http://cs.acadiau.ca/~jdiamond/comp2103/assignments/generate-input.o You must use this as one of your test cases. On my laptop with an Intel(R) Core(TM) i7­2760QM CPU @ 2.40GHz processor this took around 46 seconds. Your times will vary, depending on your CPU, but if your program takes an “immense” amount of time to run, you might ask yourself whether you are doing something the hard way. Also, when I ran the 32­bit version on my computer, it took around 76 seconds, so if you are using a 32­bit version of Linux, you can expect somewhat longer times.

As usual, you mustask yourself whether there are any special cases or conditions which you need to think about and that your program should handle. If you think of any, make sure your program comment describes these cases and what your program does in these cases, and create test case(s) showing your program works correctly.

[2] In this question you will write a program to produce an “ultimate low­resolution” depiction of a tree. The program must take one command­line argument, which speci es how big the tree is (see below). Your program must check that there is exactly one command­line argument and that it is a valid integer larger than 0. (The C library function strtol()will come in useful, although the man page might be hard for you to understand right now, so try size = strtol(argv[1], NULL, 10); afteryou have veri ed that you have one command­line argument).

If the command­line argument is odd, your program will output the tree centered in an 80­character wide page. Otherwise, the program will output the tree as far left on the page as possible. If the input is n, the tree has nlines of *'s, with 1 *on the top line, 3 on the next line, and so on. There are always two centered #'s at the bottom of each tree.

Here are two sample outputs, for the inputs 4 and 5, respectively. The “ |” represents the edge of the page (this does notshow up in your output, it is here as a visual aid). The rst output is 6 lines long, and the second is 7 lines long.

| * | *** | ***** 2 |******* | # | # | * | *** | ***** | ******* | ********* | # | # Your program should output an error message and terminate if the command­line argument speci es a tree which is wider than 78 characters (at its widest point).

[3] Write a program which opens two les, inand out. It copies the contents of intoout , except that it replaces (i) sequences of two or more spaces and/or tabs in inwith one space; (ii) single tab characters with a single space; and (iii) the two­character sequence “ BZ” with “ Bravo Zulu ”.

For example, if incontains “ a b ” then after running the program outwill contain “ a b ”.

Similarly, if inhas the line BZ to Bob... He likes cake!

then outwill have the corresponding line Bravo Zulu to Bob... He likes cake!

Your program should do all reasonable error checking. You should test your program on a variety of input les, including les with no characters at all, les with no spaces or tabs, les with only spaces and/or tabs, and les with non­space, non­tab characters as well as spaces and/or tabs.

Naturally, the latter type of les should have cases where spaces/tabs are found singly and in runs.

You can open a le for reading in C with the statements FILE * infile; char * infile_name = "some-file-name"; // for example ...

infile = fopen(infile_name, "r"); HOWEVER , you mustalwayscon rm that fopen()was able to successfully open the le before you attempt to read from the le. You do this by comparing the value that fopen()returned to NULL , as follows:

if (infile == NULL) { fprintf(stderr, "Unable to open %s for reading ", infile_name); fprintf(stderr, "Can 't continue; bye!

"); 3 return EXIT_FAILURE; } This assumes you are are opening the le in main(); if you are opening a le in some function other than main(), the above would output a message and then return to the calling function. This calling function might not know what to do with the value EXIT_FAILURE, so you may wish to use exit(EXIT_FAILURE) rather thanreturnis such situations.

Of course, Depending on the situation, you may want to output a different error message, or perhaps you will give the user a chance to supply a different lename, or something else yet. But for this problem your program can simply complain and exit.

You should do the same checking when opening a le for output. In the case of writing, you should use "w" as the second argument to fopen().

To input a single character at a time from an opened le, you can use (for example) fgetc(infile).

To output a single character to a le you can use a statement like fputc(c, outfile), where outfile is the value returned by fopen(..., "w");andcis the character you want to output.

And you can write more complicated outputs with fprintf(outfile, "format", args) (just like printf() ).

When you are nished with a le, close it with a statement line fclose(infile);. After you close it you can not read from or write to it any more (without opening it again).

For now, you can think of “ char * infile_name” as a declaration of a string. We will talk about that (a lot) more in class.

In order to make your output markable, please use input les of at most a few (2–10?) lines in total length. Also, so that the markers can distinguish between a tab character and a run of spaces, you should display the contents of your input and output les not only with cat, but also with the od command, run with the -cbargument. (To learn more about the useful odcommand, you can type “ man 1 od ” to read its manual page.) Since this program always reads from inand writes to out, you will have to copy new data into in between runs of your program. So your commands in your script le will look something like $cat a2p3.c ...

$gcc ... a2p3.c -o a2p3 $rm -f in $cp test1 in $a2p3 $cat in ...

$cat out ...

4 $od -cb in ...

$od -cb out ...

$rm -f in $cp test2 in ...

Try to make each test le count. That is, you should ensure that you test every (reasonable) situation you can think of, but you don't need to test a given situation 10 times. Finally, if you want to make your test cases easy to gure out, you could (for example) name the le containing the test case with a related name (e.g., “ has-a-single-tab.test ”) or the rst line in the le could describe what is being tested.

WARNING: this question might be a bit trickier than you think. Did you use functions in any of these questions? Should you have? Did you document them correctly?

Does you program “blow up” on unexpected input, or does it deal with bad input in a “graceful” way?

How does your program deal with boundary conditions, if there are any?

Did you remember to put all required comments in? Does your program call out for any other comments in the body of the code?

5