CS 1321 (Principles of Algorithm Design II):
Homework #2

Assigned:
January 21, 2000
Due:
January 28, 2000, at the beginning of class
Credit:
20 points.

Tips of the week

Software design tip

Unlike writing essays, it is frequently easier to build programs by iteratively adding features. If you test after each adding each feature, any mistakes are likely to be in the newly added code.

UNIX text editors, revisited

If you tried vi, hated it, and went back to the Windows world, be advised that there are other editors available on the CS Linux machines. emacs is probably the most widely available; it is very powerful, though it also requires some learning. There are also "friendlier" GUI-oriented versions of both vi (gvim) and emacs (xemacs). See my CS1320 links page for more information.

UNIX file commands and file security

Unless you take explicit action, anyone with a computer science account can look at all of your files in your CS account. This also means anyone can copy all of your files, submitting them as homework solutions. Do not fall victim to this form of identity stealing. Read Securing One's UNIX Files for instructions on how to secure your files. After you make the suggested changes, ask a friend to see if you did it correctly.

Compilation tips

Many compilers can be asked to provide warning messages (about what might be wrong with your program) in addition to the usual error messages. These warning messages may help find programming mistakes more easily than the default of producing only error messages.

To ask the g++ compiler to produce these messages, use the -Wall and -pedantic options, e.g., type g++ -Wall -pedantic hello.cc. If you use a different compiler, find out whether it has similar options and how to turn them on.

To ask the g++ compiler to name the resulting output something other than a.out, use the -o option. For example, to compile hello.cc and put the result in hello rather than a.out, type g++ -Wall -pedantic hello.cc -o hello.

It is a pain to type these every compilation. Instead, one can store compilation commands in a Makefile and use the make command to compile your program, creating the executable.

For example, suppose you use the command g++ -Wall -pedantic foo.cc -o foo to compile a C++ file named foo.cc and create an executable called foo. Instead, you can create a file called Makefile containing these two lines of code:

foo:
tab g++ -Wall -pedantic foo.cc -o foo
tab denotes a tab character; using eight spaces will not work correctly. To create the executable called foo, type make foo.

You could accomplish the same thing by creating a Makefile containing the single line

CXXFLAGS -Wall -pedantic
This defines a general rule for compiling a file named whatever.cc and putting the resulting executable in whatever. With this Makefile, typing make foo will work as described above, as will make hello, etc.

The make programming language can automate many tasks. If you want to learn more, read the online GNU make documentation, particularly the first chapter.

Warm-up exercises

There is no need to submit solutions to these exercises, but be sure you can solve them.
  1. Convert the pseudocode isMember() function presented in class to C++ code, in the same way we converted add() (a.k.a. sum() or adder()), using this linked list class presented in class.
  2. Rewrite the isMember() function as a ``one-liner,'' i.e., using only one line for the function's body.

Hint: The C++ boolean operators || and && use short-circuit evaluation, i.e., they evaluate only as many arguments as necessary (textbook, p. 107). For example, false && foo() need not evaluate foo() because the result is false.

Problem statement

Warning! Understanding the recursive definition of unary numbers is fundamental to this homework. Please do not wait until Wednesday evening to determine whether you understand the definition. End Warning.

According to a former Stanford graduate student now a professor at Columbia University, the database program running on the main Stanford mainframe computer has no built-in arithmetic operations. Even though numbers such as tuition and housing charges were computed by this program, the program had no integer or floating point number type.

Our task is to implement arithmetic on natural numbers, i.e., nonnegative integers, to use with this program. Let's use unary number notation. Each number n is represented by the repeating a character n times. For example, the decimal number 7 would be represented by ``0000000'' and 3 would be represented by ``000''. Zero is represented by no digits: ``''.

We can recursively define any natural number as

For example, ``000'' is one more than one more than one more than zero. More succinctly, ``000'' is add1(add1(add1(zero))).

Note this recursive definition for numbers is very similar to the recursive definition of lists.

Since we assume that the database program supports lists of booleans, we can implement unary arithmetic using the linked list class discussed in class. Our database program must compute tuition charges so it needs +, -, *, integer division /, and exponentiation ^ (to compute interest on overdue balances). Our database program will never use subtraction to produce negative numbers, but it needs to be able to compare numbers, i.e., >, >=, <, <=, ==, and !=. You need not define arithmetic assignment operators such as += and -=, but your code must not use them either.

We have provided some starter source code (unary.h). For example, it defines

For this assignment, write the omitted functions and add the other necessary functions to unary.h.

We have also provided a simple main() function (test-unary.cc) for preliminary testing of your code. Of course, you will want to write more extensive testing code.

(Note that you will also need files seq.h and seq.cc, from the linked list class presented in class.)

Hints and tips for this assignment

Operator overloading

For our natural numbers, we would like to be able to write expressions using the usual infix operators. For example, if m and n have type natural, we would like to be able to write expressions such as n + m and n == m. But since m and n are instances of a user-defined type rather than a built-in C++ type (such as int or double), the compiler has no idea how to evaluate such expressions. Instead, it converts these expressions to function calls, converting m + n into

        operator+ (m, n)
and m == n into
        operator== (m, n) .
We can thus ensure that m + n and m == n have the meanings we desire by writing functions with the following prototypes:
        natural operator+ (const natural & m, const natural & n);
        bool operator== (const natural & m, const natural & n);
The first function should return a natural number representing the sum of its parameters; the second function should return true if its parameters are equal and false otherwise. By providing such functions, we have ``overloaded'' the + and == operators. We can overload other operators in a similar fashion. See also Section 2.5 of the textbook.

Dealing with multiple C++ files

Fitting all the source code for complicated programs into one file would be unrealistic, so C++ supports storing code in multiple files. Historically, files ending with ``.cc'' stored definitions, e.g., function definitions, while files ending with ``.h'' stored the corresponding declarations. Unfortunately, recent C++ changes have blurred these distinctions, so sometimes we put function definitions in header files, as we have done with unary.h.

For this assignment, we provide these suggestions:

Note the code, as distributed, should compile, albeit with numerous warnings. Also, it will not run until code for the necessary functions is written. Later in the course, we will discuss the rules for dealing with multiple files.

(If you are using a compiler other than g++, it is your responsibility to determine how to deal with multiple files.)

What to turn in

Submit your source code as described in the guidelines for programming assignments. For this assignment:

Problems using Visual C++ for this assignment

A student has brought to our attention that the linked-list class may not work properly with all versions of the Visual C++ compiler. One problem is that Visual C++ is a bit stricter than g++. This can be fixed by adding the following line to unary.h, just after all the #include lines:
	 using namespace std; 
Alternatively, you can find every place the code uses the STL vector class and change vector to std::vector.

However, the version of Visual C++ to which I have access (6.0) has an additional problem: It does not implement a recent change to the C++ standard, and neither Jeffrey (a.k.a. "Dr. Oldham") nor I can figure out how to modify the linked-list class to make it work with the older standard. This problem is documented on Microsoft's Web page; they say it's fixed in version 6.0, but my experience suggests otherwise.

So, if you're using Visual C++ for this assignment, I recommend that you:

If you get a lot of messages complaining about errors in the operator== function, the odds are good that your version of Visual C++ doesn't support that crucial change to the standard. If that's the case, I think you will simply have to transfer your code to your CS account and do final testing and debugging in that environment.

See A Few Tips for Using Microsoft Visual C++ for more details.