# TheLongIntClass
# The CS 1110 Profs
# April 2016
""" Contains a class that supports long
integer addition and multiplication.
"""

class LongInt(object):
    """
    Attributes:
        String: a string composed of the digits for this LongInt
    """
    def __init__(self,x):
        """
        x is a string of digits, or a nonnegative integer, or
        a reference to a LongInt. (The last possibility is useful for
        making copies.)
        """
        if isinstance(x,str):
            self.String = x
        elif isinstance(x,int):
            self.String = str(x)
        elif isinstance(x,LongInt):
            self.String = x.getString()

    def getString(self):
        """Returns the String underlying this LongInt."""
        return self.String

    def __str__(self):
        """ For pretty printing. To display a LongInt object x
        just write  print x.
        """
        return self.getString()

    def __add__(self,other):
        """ Returns the sum of self and other as a LongInt.
        PreC: other is a reference to a LongInt object.
        """
        z = ''
        carry = 0
        A = self.getString()
        B = other.getString()
        nA = len(A)
        nB = len(B)
        # The usual gradeschool addition algorithm, i.e., digit-by-digit
        # summation with carries.
        for k in range(max(nA,nB)):
            # Process the 10**k place...
            tau = carry
            if k<nA:
                tau = tau + int(A[nA-k-1])
            if k<nB:
                tau = tau + int(B[nB-k-1])
            carry = tau/10
            z = str(tau%10) +z
        # There may be one final carry...
        if carry>0:
            z = str(carry) + z
        return LongInt(z)


    def DigitMult(self,d):
        """ Returns a LongInt whose value is int(d) times the
        value of self.
        PreC: d is a length-1 string that is a digit.
        """
        z = ''
        carry = 0
        A = self.getString()
        na = len(A)
        for k in range(na):
            # Process the 10**k place...
            tau = carry + int(d)*int(A[na-k-1])
            carry = tau/10
            z = str(tau%10) + z
        if carry>0:
            z = str(carry) + z
        return LongInt(z)

    def times10(self,k):
        """Modifies self so that it represents an integer
        that is 10**k times bigger.
        PreC: k is a nonnegative int
        """
        self.String = self.getString() + k*'0'

    def __mul__(self,other):
        """ Returns the product of self and other as a LongInt.
        PreC: other is a reference to a LongInt object.
        """
        n = len(other.getString())
        # M will serve as a running sum
        M = LongInt(0)
        for k in range(0,n):
            # Multiply self times the digit in other that is in
            # the 10**k place. Then multiply by 10**k and add in...
            s = other.getString()
            P = self.DigitMult(s[n-k-1])
            P.times10(k)
            M = M + P
        return M

def main():
    # Create a LongInt object that represents 123456789
    x = LongInt(123456789)
    # It is represented as a string
    print 'x = ', x.getString()
    print '1 + x + x**2 + ... + x**19 = '
    # Let's add up the first 20 powers of this integer
    # s will be our running sum...
    s = LongInt(1)
    # Create a LongInt that will house successive powers of x
    xPower = LongInt(x)
    for k in range(20):
        # Add in the current power and then raise t
        # We can use + and * because of th e__add__ and __mult__ methods,,,
        s = s + xPower
        xPower = x*xPower
    print s.getString()


if __name__ == '__main__':
    main()