# CMPSCI 311: Theory of Algorithms

### Multiplying Large Integers (Levitin 4.5)

Java provides a `BigInteger` class for storing integers too long for an `int` or `long`. Here we will look at the complexity of addition and multiplication of `BigInteger` objects, and determine whether the unusual multiplication algorithm of Levitin section 4.5 is useful in this context.

We'll assume that a (non-negative) `BigInteger` is written in the form "the sum for i from 0 to n of biri, where r is a base such as `MAXINT+1`. Each bi is kept in an `int` variable, and these `int` variables are kept in some sort of list. We can get bi from b, for example, in O(1) time by the call `b.getTerm(i)`, and change bi to x in O(1) time by `b.setTerm(i,x)`.

• Question 1: Here are my sketched versions of two methods from the `BigInteger` class:

``````
{// returns sum of this and c
int n = max(this.size, c.size);
BigInteger result = new BigInteger(n+1);
int carryBit = 0;
for (int i=0; i < n; i++) {
result.setTerm(i, this.getTerm(i) + c.getTerm(i) + carryBit);
result.setTerm(n, carryBit);
return result;}

BigInteger multiply (int x)
{// returns this times x
int n = this.size;
BigInteger result = new BigInteger(n+1);
int carryInt = 0;
for (int i=0; i < n; i++) {
result.setTerm(i, this.getTerm(i) * x + carryInt);
carryInt = multCarry(this.getTerm(i), x, carryInt);}
result.setTerm(n,carryInt);
return result;}
``````

Argue that each of these methods takes \$O(n)\$ time.

• Question 2: Write Java pseudocode using the `add` and `multiply` methods above to implement `BigInteger multiply (BigInteger c)` using the ordinary algorithm for multiplication. Analyze the running time.

• Question 3: Write Java pseudocode to reimplement `multiply` utilizing the trick from section 4.5, where it is observed that if a = a1rn/2 + a0 and b = b1rn/2 + b0, then ab = (a1b1)rn + ((a1 + a0)(b1 + b0) - a1b1 - a0b0)rn/2 + (a0b0).

Assume you also have a method `shift` in the `BigInteger` class, taking an `int` argument, such that `b.shift(i)` is b times ri if i is positive and b divided by ri (with no remainder) if i is negative. There is also a method `tail` such that `b.tail(i)` gives the remainder when b is divided by ri. Each of these methods takes Θ(i) time.

Analyze the running time of your method.