# fraction.py
# Walker M. White (wmw2)
# October 15, 2015
"""Module with a simple Fraction class.

This module shows off operator overloading.  This allows us to define complex
mathematical objects."""

def gcd(a,b):
    """Returns: Greatest common divisor of a and b.
    
    Parameter a: the first integer
    Precondition: a is an int
    
    Parameter b: the second integer
    Precondition: b is an int"""
    assert type(a) == int,`a`+' is not an int'
    assert type(b) == int,`b`+' is not an int'
    while b != 0:
       t = b
       b = a % b
       a = t
    return a


class Fraction(object):
    """Instance is a fraction n/d
    
    INSTANCE ATTRIBUTES:
        numerator:   The fraction numerator   [int]
        denominator: The fraction denomenator [int > 0]
    """
    
    # INITIALIZER
    def __init__(self,n=0,d=1): 
        """Initializer: Creates a new Fraction n/d
        
        Parameter n: the numerator (default is 0)
        Precondition: n is an int (or optional)
        
        Parameter d: the denomenator (default is 1)
        Precondition: d is an int (or optional)
        """
        assert type(n) == int,`n`+' is not an int'
        assert type(d) == int,`d`+' is not an int'
        assert d > 0,`d`+' is not positive'
    
        self.numerator = n
        self.denominator = d
    
    def __str__(self):
        """Returns: this Fraction as a string 'n/d'"""
        return str(self.numerator)+'/'+str(self.denominator)
    
    def __repr__(self):
        """Returns: The unambiguous representation of Fraction"""
        return str(self.__class__)+'['+str(self)+']'
    
    def __mul__(self,other):
        """Returns: Product of self and other as a new Fraction
        
        This method does not modify the contents of self or other
    
        Parameter other: the fraction to multiply on the right
        Precondition: other is a Fraction"""
        assert type(other) == Fraction
        top = self.numerator*other.numerator
        bot = self.denominator*other.denominator
        return Fraction(top,bot)
    
    def __add__(self,other):
        """Returns: Sum of self and other as a new Fraction
        
        This method does not modify the contents of self or other
        
        Parameter other: the fraction to add on the right
        Precondition: other is a Fraction"""
        assert type(other) == Fraction
        bot = self.denominator*other.denominator
        top = (self.numerator*other.denominator+
               self.denominator*other.numerator)
        result = Fraction(top,bot)
        return result

    def __eq__(self,other):
        """Returns: True if self, other are equal Fractions.
        
        It returns False if they are not equal, or other is not a Fraction
        
        Parameter other: value to compare to this fraction
        Precondition: NONE"""
        if type(other) != Fraction:
            return False
        
        # Cross multiply
        left = self.numerator*other.denominator
        rght = self.denominator*other.numerator
        return left == rght

    def __ne__(self,other):
        """Returns: False if self, other are equal Fractions.
        
        It returns True if they are not equal, or other is not a Fraction
        
        Parameter other: value to compare to this fraction
        Precondition: NONE"""
        return not self == other

    # Will uncomment in class
    #def __cmp__(self,other):
    #    """Returns: whether self < other, self == other, or self > other
    #    
    #    This method returns an integer.  The value is negative is self < other.
    #    The value is zero if self == other.  Finally, the value is positive if
    #    self > other.  The actual value returned does not matter and is not
    #    specified.
    #    
    #    This method is used to implement all comparison operations.
    #    
    #    Parameter other: value to compare to this fraction
    #    Precondition: other is a Fraction"""
    #    assert type(other) == Fraction
    #    # Cross multiply
    #    left = self.numerator*other.denominator
    #    rght = self.denominator*other.numerator
    #    return left - rght
    
    def reduce(self):
        """Normalizes this fraction into simplest form.
        
        Normalization ensures that the numerator and denominator have no
        common divisors."""
        g = gcd(self.numerator,self.denominator)
        self.numerator = self.numerator/g
        self.denominator = self.denominator/g