New Extra Credit Policy!

To encourage you to start projects early, I will be awarding one extra credit point for each day early you turn in the project. This project is due at 11:59pm on Monday, April 28, so if you turn it in by 11:59pm on Sunday the 27th, you get one bonus point.

COMP 142 Project 6: Large Integers

To be done in partners, if desired. No more than two people per group. Turn in only one copy of your files per group.

The built-in C++ integer types such as int, long, and long long, all use a fixed number of bytes to store integers. For instance, on many computers, an int always takes up 32 bits (4 bytes), meaning you can only represent 2^32 possible integer values, from -(2^31) to (2^31)-1, or -2,147,483,648 to 2,147,483,647.

When you exceed the limits of what an int (or long or long long) can handle, C++ will not realize it. Consider this code below:

int main()
{
  int x = 2147483646;
  cout << x << endl;
  x++;
  cout << x << endl;
  x++;
  cout << x << endl;
  x++;
  cout << x << endl;
}
When this program runs, it prints the following:
2147483646
2147483647
-2147483648
-2147483647
What is happening is that when x becomes equal to 2147483647, adding 1 to the number causes an overflow error, which, from the user's standpoint, causes the value of the variable to wrap around to the most negative number possible in the range.

To mitigate this issue, you will construct a largeint class, which is capable of representing nonnegative integers with any number of digits. You will use a vector of integers to store the digits (each digit will take up one slot in the vector).

The largeint class, conceptually

Largeints use a vector of ints to store a nonnegative integer. Though it sounds weird, you should store the digits in backwards order (it will make your later algorithms easier). For instance, the integer 6805 would be represented as a vector of ints as in the diagram below.
position:    [0]   [1]   [2]   [3]
           -------------------------
value:     |  5  |  0  |  8  |  6  |
           -------------------------

Why backwards?

Eventually, you will have to write an algorithm to add two largeints together. Your algorithm will simulate the elementary-school addition algorithm, which aligns integers to the right so the place-values match up between them:
  6805
 +  32
======
  6837
If you make your vectors store the digits in "normal" order, you get the following:
position:    [0]   [1]   [2]   [3]
           -------------------------
value:     |  6  |  8  |  0  |  5  |
           -------------------------
           -------------
value:     |  3  |  2  |
           -------------
In this representation, the digits don't line up right. You can still write the addition algorithm, but the logic is trickier because the positions don't match up between the two vectors. However, if you store the digits in "backwards" order, everything lines up:
position:    [0]   [1]   [2]   [3]
           -------------------------
value:     |  5  |  0  |  8  |  6  |
           -------------------------
           -------------
value:     |  2  |  3  |
           -------------

Getting started

Download these three files and bring them into a Visual Studio project: largeint.h largeint.cpp main.cpp

Why are there three files?

In the real world, class definitions are often split into separate files from your main code that uses the classes. Furthermore, often time the class definitions are split into a ".h" file with the class definition and method prototypes, and a ".cpp" file with the method bodies.

OK, the files are in Visual Studio, now what?

The project should compile and run without any changes. The output will not be correct, but everything should compile and run.

Modifying largeint.cpp

Let's take a look at the largeint class definition, in largeint.h:
class largeint
{
    public:
    largeint();
    largeint(string s);
    
    int num_digits() const;
    
    friend ostream& operator<<(ostream & os, const largeint & lint);
    largeint operator+(const largeint & other) const;
    largeint operator*(const largeint & other) const;
    bool operator==(const largeint & other) const;
    bool operator!=(const largeint & other) const;
    
    private:
    vector<int> digits;
}; 
Notice that the only field the class has is the vector of ints, called digits.

The bodies of these functions are already defined in largeint.cpp. You should not make any changes to largeint.h.

The functions you will have to fill in are the following (in order given in the code above):

Modifying main.cpp

You should feel free to add any code to the main() function that you want, but please remove anything you add before turning in your code.

You should fill in the factorial function for largeints. Do not try to write this recursively, because the regular recursive factorial algorithm needs a subtraction operator. Instead, use a for loop similar to your multiplication algorithm to calculate factorial by repeated multiplication. That is, 5! = 1 * 2 * 3 * 4 * 5.

How do I know when it works?

When your code successfully prints the largeints 0 through 100, and can calculate 100 factorial, you should be confident that your code works.

Sample output

large int counter is: 0
large int counter is: 1
large int counter is: 2
[ removed middle lines here ]
large int counter is: 98
large int counter is: 99
large int counter is: 100
100 factorial is 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000
This number has 158 digits.

The addition algorithm

You should use the same idea that you learned in elementary school to add two numbers. You will use a loop to iterate over the digits from right-most digit to left-most, adding pairs of digits as you go. If you get a sum less than ten, that becomes a digit in your answer. If you get a sum greater than or equal to ten, only the one's digit goes into your sum. Use an int variable called carry to carry the extra 1 to the next column.

Special considerations:

size_t

You may run into some problems using size_t if you want to make a loop count backwards (ending at zero). The reason for this is that size_t is an unsigned type, so it can't go below zero (which is what would signal the loop to stop). If you start getting infinite loops, try switching to an int and see if that helps.

Turning in your code

Do not rename your files. Upload all three files (largeint.h, largeint.cpp, main.cpp) to Moodle. Upload only one copy per group (if you're working with a partner). Make sure both of your names are in the comments at the top of your code.

Challenge Problems