CS 141 Winter 2004 -- Project 1

Posted: Jan 15th 2004
Due: Jan 29th 2004, 11:59pm (Pacific Time)

Introduction

In this assignment you will refresh your C++ skills, and get some experience with dynamic memory management by implementing a class which manages its own dynamic data.

You have two weeks to complete the assignment. It's a non-trivial assignment, so you should get started early.

Files

These files constitute the skeleton of the code that you are going to turn in. Download the files in your home directory.

Assignment

You are going to implement a BigInt class. Its purpose is support for arbitrarily large non-negative integers.

We'll give you the specification for this class, and the implementation for some of the member functions. You will have to decide how to represent internally the BigInt first. Then, you will complete the member (and related) functions and test it on some programs.

Summary of requirements (details appear in following sections):

BigInt interface

Here are the operations on BigInt. You must implement them all. Do NOT change the interface of the class (otherwise we will not be able to test your code, and you will get a bad score). Assume that the big integers are always positive or zero (no sign is necessary).

class BigInt
{
  public:
    
    BigInt();                          // default constructor, value = 0
    BigInt(const BigInt &);        // copy constructor

    BigInt(int);               // assign an integer value
    BigInt(const String &);    // assign a string     
    ~BigInt();                 // destructor

    // operators: arithmetic, relational, output

    friend ostream & operator <<(ostream &, const BigInt &);

    const BigInt & operator += (const BigInt &);    

    const BigInt & operator *= (const BigInt &);    
    BigInt & operator = (const BigInt &);

    friend int operator == (const BigInt &, const BigInt &);
    friend int operator < (const BigInt &, const BigInt &);
  private:

    // add your representation here
};


BigInt operator +(const BigInt &, const BigInt &);
BigInt operator *(const BigInt &, const BigInt &);
int operator != (const BigInt &, const BigInt &);
int operator > (const BigInt &, const BigInt &);
int operator >= (const BigInt &, const BigInt &);
int operator <= (const BigInt &, const BigInt &);

Multiplication is a special case. There is an implementation of *= in the current BigInt.cc, implemented in terms of += (i.e., using repeated additions). You may use this version of the function to help you test your += function, although won't be able to use it until you've implemented several of the other functions too. However, you are required to reimplement *= in a more efficient way for your final program (see Hints on Arithmetic Operations, below).

See BigInt.h for more thorough specification of these functions.

Representation of BigInts

The internal representation is your choice, as long as it is dynamically allocated (i.e., dynamic array, STL vectors, strings, etc.).

Hints on arithmetic operations

Make sure that you grow your array/vector/string enough to store the result.

You are required to add and multiply using the grade school methods you learned. For add that means adding digit by digit, and carrying a 1 where necessary. When working on your algorithm for a+=b we recommend you consider cases where a has more digits, b has more digits, and they are the same size.

To multiply a BigInt by a BigInt consider the example below:

1 2 3 4 5 6
x 7 8 9
----------------------------------
1 1 1 1 1 0 4
9 8 7 6 4 8 0
8 6 4 1 9 2 0 0
----------------------------------
9 7 4 0 6 7 8 4

Note that each partial product is shifted-left (padded with the appropriate number of zeros). Rather than shift the partial product by adding the appropriate number of zeros, one of the numbers being multiplied can be shifted (of course this is what is really happening in the example above where the top number is multiplied by 9, 80, and 700). This allows the product to be accumulated by summing a sequence of partial products where each partial product involves multiplying by a digit in the range 0-9. In the example above the final product is computed by

             (123,456 X 9) + (1,234,560 X 8) + (12,345,600 X 7)
Note that each partial product involves the product of a BigInt and a digit in the range 0-9. Thus the partial products can be computed using a private auxiliary member function which multiplies its implicit parameter by a single digit. The prototype for this member function might be void BigInt::multByDigit(int digit)

Other implementation constraints

Note that * is implemented in terms of *=. Because of that, * is a non-member, non-friend function. The same can be said for +.

Once you implement < and ==, the other relational operators are already defined in terms of those two.

Testing your code

You are responsible for writing a test program, called bigtest.cc, that will thoroughly test all of your member functions; bigtest.cc will be compiled separately from your BigInt code. You are also required to add the necessary rules to the Makefile so that make bigtest will create the executable.

You may structure your test program in a few possible ways: one option is a program that takes no input, and runs several fixed tests over BigInts created in the program. Your output should be somewhat self-explanatory. E.g., for one of the tests of your add function, you could print the value of the BigInts being added (before the add), and what the result was, e.g.:

30 + 4 = 70
(and with the output shown we could easily see that there is a bug in the BigInt class being tested!).

Or your program could take some input for BigInt values (i.e., they would be read in as Strings), to make it easier to test the same operations on different values. If your program takes input, you are also required to provide test data files for the program which collectively completely test your program. Your program should just read from cin, and you can use UNIX input redirection to get the input from a file.

How thoroughly you test your BigInt code will be a non-trivial portion of your grade on this assignment.

READ ME

You must document in your README file how to run your test program, and what cases the various data files test (if you have test data files). If your program doesn't take input, you should document in your source code what particular case a specific line of code (or group of lines) tests.

Factorial program

We have given you another program which you can use to test your BigInt. It's a program that computes some factorials. If you have implemented +=, but not yet *=, you can still use fact.cc to help test addition, since it's used in the current multiplication function.

We have given you a Makefile which makes an executable fact from fact.cc when you type make. You need to add rules to this Makefile for compiling your test program.

The program prompts for a number and then prints factorials from the entered number down to 1. If you invoke the program with a command-line argument as in fact 14 it will compute and print all factorials from the command-line argument down to 1.

For you convience, we are providing the executable for fact. You can use our executable to check the correctness of your program. Download the executable for Linux/i386 here (right-click, "Save Link As...", and then change the permission to executable)

Incremental development

A good way to do many programming assignments it to get one part of the code working at a time. One reason this is better is that it's easier to debug a small program than a big program. This is in contrast to the popular student strategy of typing in the whole program, trying to compile, and once the compiler errors are out, then trying to debug the whole thing at once.

For example, if your code to build a BigInt is untested, and you are testing += at the same time, it's difficult to determine where to look for any bugs that appear when you try to add. If, instead, you already know that your BigInts are being built correctly (because you tested this code in isolation), then you can isolate mistakes to +=.

A minimal subset you might want to test initially is one or more of your BigInt constructors with the printing routine (already written for you). Once you get your copy constructor, assignment operator and destructor working, you can then tackle the relational operators and +=, testing the latter in part using the *= operator (which is currently implemented in terms of +=). Then you can reimplement *=.

Style and Documentation

Yes, they still count here. There is not a lot to document in the BigInt class, since we gave you already the interface. You can break up member functions further by calling auxiliary member functions. You will need to fully document bigtest.cc since you are designing it yourself.

Grading

This assignment is worth 100 points, divided as follows
description points
program compiles with no errors/warnings 10 points
constructors/destructors/output operator work 10 points
comparison operators work 5 points
operator + works 15 points
re-implemented operator * works 20 points
robustness (i.e., program survives nasty cases) 20 points
program style (program design, comments, README) 20 points

Submitting

Submit electronically by Jan 29th 2004, 11:59pm (Pacific Time), the following files "README *.h *.cc Makefile"


Adapted from an assignment by Owen Astrachan.