Description
$Id: asg1dcbigint.mm,v 1.25 1213 10:56:1808 – – $
PWD: /afs/cats.ucsc.edu/courses/cmps109wm/Assignments/asg1dcbigint
URL: http://www2.ucsc.edu/courses/cmps109wm/:/Assignments/asg1dcbigint/
1. Using C++11/14/17
All programming in this course will be done C++ style, not C style, as shown in the following table.

Do not use :
Instead, use :
char* strings
<string>
C arrays
<vector>
<stdio.h>, <cstdio>
<iostream>, <iomanip>
pointers
<shared_ptr>
union
inheritance
<header.h>
<cheader>
Header files : Include only C++11/14/17 header files and facilities where feasable, and use namespace std. Include <cheader> files only when C++style files are unavailable. Include <header.h> files from C only when an appropriate <cheader> files is unavailable. Use the script cpplint.py.perl (a wrapper for cpplint.py) to check style.
2. Overview
This assignment will involve overloading basic integer operators to perform arbitrary precision integer arithmetic in the style of dc(1). Your class bigint will intermix arbitrarily with simple integer arithmetic.
To begin read the man(1) page for the command dc(1) :
man s 1 dc
A copy of that page is also in this directory. Your program will use the standard dc as a reference implemention and must produce exactly the same output for the commands you have to implement :
+ – * / % ^ c d f p q
If you have any questions as to the exact format of your output, just run dc(1) and make sure that, for the operators specified above, your program produces exactly the same output. A useful program to compare output from your program with that of dc(1) is diff(1), which compares the contents of two files and prints only the differences.
Also look in the subdirectory misc/ for some examples and in output/ for the result of running the test data using dc.
3. Implementation strategy
As before, you have been given starter code.

Makefile, debug, and util If you find you need a function which does not properly belong to a given module, you may add it to util.

The module scanner reads in tokens, namely a NUMBER, an OPERATOR, or SCANEOF. Each token returns a token_t, which indicates what kind of token it is (the terminal_symbol symbol), and the string lexinfo associated with the token.
2 of 5 

Only in the case of a number is there more than one character. Note that on input, an underscore (_) indicates a negative number. The minus sign (–) is reserved only as a binary operator. The scanner also has defined a couple of operator<< for printing out scanner results in debug mode.


The main program main.cpp, has been implemented for you. For the six binary arithmetic functions, the right operand is popped from the stack, then the left operand, then the result is pushed onto the stack.



The module iterstack can not just be the STL stack, since we want to iterate from top to bottom, and the STL stack does not have an iterator. A stack depends on the operations back(), push_back(), and pop_back() in the underlying container. We could use a vector, a deque, or just a list, as long as the requisite operations are available.


Class bigint
Then we come to the most complex part of the assignment, namely the class bigint.
Operators in this class are heavily overloaded.


Most of the functions take a arguments of type const bigint&, i.e., a constant reference, for the sake of efficiency. But they have to return the result by value.



The operator<< can’t be a member since its left operand is an ostream, so we make it a friend, so that it can see the innards of a bigint. Note now dc prints really big numbers.



The relational operators == and < are coded individually as member functions. The others, !=, <=, >, and >= are defined in terms of the essential two.



All of the functions of bigint only work with the sign, using ubigint to do the actual computations. So bigint::operator+ and bigint::operator will check the signs of the two operands then call ubigint::operator+ or ubigint::operator, as appropriate, depending on the relative signs and magnitudes. The multiplication and division operators just call the corresponding ubigint operators, then adjust the resulting sign according to the rule of signs.


Class ubigint
Class ubigint implements unsigned large integers and is where the computational work takes place. Class bigint takes care of the sign. Now we turn to the representation of a ubigint, which will be represented by vector of bytes.
(a) Replace the declaration
using unumber = unsigned long;
unumber uvalue {};
with
using udigit_t = unsigned char;
using ubigvalue_t = vector<udigit_t>;
ubigvalue_t ubig_value;
in ubigint.h.
3 of 5 



In storing the big integer, each digit is kept as a number in the range 0 to 9 in an element of the vector. Since the arithmetic operators add and subtract work from least significant digit to most significant digit, store the elements of

the vector in the same order. That means, for example, that the number 4629 would be stored in a vector v as : v_{3} = 4, v_{2} = 6, v_{1} = 2, v_{0} = 9. In other words, if a digit’s value is d × 10^{k}, then v_{k} = d.


In order for the comparisons to work correctly, always store numbers in a canonical form : After computing a value from any one of the six arithmetic operators, always trim the vector by removing all highorder zeros :

while (size() > 0 and back() == 0) pop_back();
Zero should be represented as a vector of zero length and a positive sign.


The scanner will produce numbers as strings, so scan each string from the end of the string, using a const_reverse_iterator (or other means) from the end of the string (least significant digit) to the beginning of the string (most significant digit) using push_back to append them to the vector.


Implementation of Operators


For bigint::operator+, check to see if the signs are the same or different. If the same, call ubigint::operator+ to compute the sum, and set the result sign as appopriate. If the signs are different, call ubigint::operator with the larger number first and the smaller number second. The sign is the sign of the larger number.



The operator bigint::operator should perform similarly. If the signs are different, it uses ubigint::operator+ but if the same, it uses ubigint::operator.



To implement ubigint::operator+, create a new ubigint and proceed from the low order end to the high order end, adding digits pairwise. For any sum >= 10, take the remainder and add the carry to the next digit. Use push_back to append the new digits to the ubigint. When you run out of digits in the shorter number, continue, matching the longer vector with zeros, until it is done. Make sure the sign of 0 is positive.



To implement ubigint::operator, also create a new empty vector, starting from the low order end and continuing until the high end. If the left digit is smaller than the right digit, the subtraction will be less than zero. In that case, add 10 to the digit, and set the borrow to the next digit to −1. After the algorithm is done, pop_back all high order zeros from the vector before returning it. Make sure the sign of 0 is positive.



To implement operator==, check to see if the signs are the same and the lengths of the vectors are the same. If not, return false. Otherwise run down both vectors and return false as soon a difference is found. Otherwise return true.



To implement operator<, remember that a negative number is less than a positive number. If the signs are the same, for positive numbers, the shorter one is less, and for negative nubmers, the longer one is less. If the signs and lengths are the same, run down the parallel vectors from the high order end to the low order end. When a difference is found, return true or false, as appropriate. If

4 of 5 

no difference is found, return false.

Implement function bigint::operator*, which uses the rule of signs to determine the result. The number crunching is delegated to ubigint::operator*, which produces the unsigned result.

Multiplication in ubigint::operator* proceeds by allocating a new vector whose size is equal to the sum of the sizes of the other two operands. If u is a vector of size m and v is a vector of size n, then in O(mn) speed, perform an outer loop over one argument and an inner loop over the other argument, adding the new partial products to the product p as you would by hand. The algorithm can be described mathematically as follows :
p ← Φ
for i ∈ [0, m) :
c ← 0
for j ∈ [0, n) :

← p_{i}_{+} _{j} + u_{i}v _{j} + c
p_{i}_{+} _{j} ← d rem 10

← d ÷ 10
p_{i}_{+}_{n} ← c
Note that the interval [a, b) refers to the set {xa ≤ x < b}, i.e., to a halfopen interval including a but excluding b. In the same way, a pair of iterators in C++ bound an interval.

Long division is complicated if done correctly. See a paper by P. Brinch Hansen, ‘‘Multiplelength division revisited : A tour of the minefield’’, Software
— Practice and Experience 24, (June 1994), 579–601. Algorithms 1 to 12 are on pages 13–23, Note that in Pascal, array bounds are part of the type, which is not true for vectors in C++.
multiplelengthdivision.pdf http://brinchhansen.net/papers/1994b.pdf http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.14.5815

The function divide as implemented uses the ancient Egyptian division algorithm, which is slower than Hansen’s Pascal program, but is easier to understand. Replace the long values in it by vector<digit_t>. The logic is shown also in misc/divisioncpp.cpp. The algorithm is rather slow, but the bigO analysis is reasonable.

Modify operator<<, first just to print out the number all in one line. You will need this to debug your program. When you are finished, make it print numbers in the same way as dc(1) does.

The pow function uses other operations to raise a number to a power. If the exponent does not fit into a single long print an error message, otherwise do the computation. The power function is not a member of either bigint or ubigint, and is just considered a library function that is implemented using more primitive operations.
5 of 5 

7. Memory leak and other problems
Make sure that you test your program completely so that it does not crash on a Segmentation Fault or any other unexpected error. Since you are not using pointers, and all values are inline, there should be no memory leak. Use valgrind(1) to check for and eliminate uninitialized variables and memory leak.
If your program is producing strange output or segmentation faults, use gdb(1) and the debug macros in the debug module of the code/ subdirectory.
8. Version of g++
The code must compile and run using g++ on unix.ucsc.edu, regardless of whether it runs elsewhere. When this document was formatted (December 13, ) that was :
bash$ which g++
/opt/rh/devtoolset7/root/usr/bin/g++
bash$ g++ –version  head 1
g++ (GCC) 7.3.1 0303 (Red Hat 7.3.15) bash$ echo $(uname sp) $(hostname) Linux x86_64 unix1.lt.ucsc.edu
9. What to submit
Submit source files and only source files : Makefile, README, and all of the header and implementation files necessary to build the target executable. If gmake does not build ydc your program can not be tested and you lose 1/2 of the points for the assignment. Use checksource on your code to verify basic formatting. Look in the .score subdirectory for instructions to graders.
If you are doing pair programming, follow the additional instructions in Syllabus/ pairprogramming/ and also submit PARTNER.