""" A module with a simple Fraction class. This version of Fraction has properties, which allow us to enforce our invariants. While we will not ask you to do this on an exam, this is the preferred way to create getters and setters by commercial Python developers. Author: Walker M. White (wmw2) Date: October 20, 2017 (Python 3 Version) """ def gcd(a,b): """ Returns: Greatest common divisor of x and y. Precondition: x and y are integers. """ assert type(a) == int,repr(x)+' is not an int' assert type(b) == int,repr(y)+' is not an int' while b != 0: t = b b = a % b a = t return a class Fraction(object): """ A class to represent a fraction n/d INSTANCE ATTRIBUTES (hidden): numerator: The fraction numerator [int] denominator: The fraction denomenator [int > 0] """ # PROPERTIES @property # getter def numerator(self): """ The fraction numerator. Invariant: Must be an int. """ return self._numerator # returns the attribute @numerator.setter # setter def numerator(self,value): # DO NOT WRITE A SEPARATE SPECIFICATION FOR SETTER # enforce invariant assert type(value) == int, repr(value)+' is not an int' # assign to attribute self._numerator = value @property # getter def denominator(self): """ Fraction denominator. Invariant: Must be an int > 0. """ return self._denominator # returns the attribute @denominator.setter # setter def denominator(self,value): # DO NOT WRITE A SEPARATE SPECIFICATION FOR SETTER # enforce invariant assert type(value) == int, repr(value)+' is not an int' assert value > 0, repr(value)+' is not positive' # assign to field self._denominator = value # 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 > 0 (or optional) """ # No need for asserts; setters handle everything self.numerator = n self.denominator = d def __str__(self): """ Returns: this Fraction as a string 'n/d' """ return repr(self.numerator)+'/'+repr(self.denominator) def __repr__(self): """ Returns: The unambiguous representation of Fraction """ return str(self.__class__)+'['+str(self)+']' # MATH METHODS def __mul__(self,other): """ Returns: The product of self and other as a new Fraction This method does not modify the contents of self or other Parameter other: the value to multiply on the right Precondition: other is a Fraction or an int """ assert type(other) == Fraction or type(other) == int, repr(other)+' is not a valid operand' if type(other) == int: return self._multiplyInt(other) return self._multiplyFraction(other) # Private helper to multiply fractions def _multiplyFraction(self,other): """ Returns: The 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 """ # No need to enforce preconditions on a hidden method top = self.numerator*other.numerator bot = self.denominator*other.denominator return Fraction(top,bot) # Private helper to multiply ints def _multiplyInt(self,x): """ Returns: The product of self and other as a new Fraction This method does not modify the contents of self or other Parameter other: the value to multiply on the right Precondition: other is a int """ # No need to enforce preconditions on a hidden method top = self.numerator*x bot = self.denominator return Fraction(top,bot) def __add__(self,other): """ Returns: The sum of self and other as a new Fraction This method does not modify the contents of self or other Parameter other: the value to add on the right Precondition: other is a Fraction or an int """ assert type(other) == Fraction or type(other) == int, repr(other)+' is not a valid operand' if type(other) == int: return self._addInt(other) return self._addFraction(other) # Private helper to multiply fractions def _addFraction(self,other): """ Returns: The 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 """ # No need to enforce preconditions on a hidden method bot = self.denominator*other.denominator top = (self.numerator*other.denominator+ self.denominator*other.numerator) return Fraction(top,bot) # Private helper to multiply ints def _addInt(self,x): """ Returns: The sum of self and other as a new Fraction This method does not modify the contents of self or other Parameter other: the value to add on the right Precondition: other is an int """ # No need to enforce preconditions on a hidden method bot = self.denominator top = (self.numerator+self.denominator*x) return Fraction(top,bot) # COMPARISONS 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 and type(other) != int: return False if type(other) == int: return self.numerator == other*self.denominator # Cross multiply left = self.numerator*other.denominator rght = self.denominator*other.numerator return left == rght def __lt__(self,other): """ Returns: True if self < other, False otherwise This method is used to implement all strict comparison operations. Both < and > are determined automatically from this method. Parameter other: value to compare to this fraction Precondition: other is a Fraction """ assert type(other) == Fraction or type(other) == int, repr(other)+' is not a valid operand' if type(other) == int: return self.numerator < other*self.denominator # Cross multiply left = self.numerator*other.denominator rght = self.denominator*other.numerator return left < rght def __le__(self,other): """ Returns: True if self < other, False otherwise This method is used to implement all inclusive comparison operations. Both <= and >= are determined automatically from this method. Parameter other: value to compare to this fraction Precondition: other is a Fraction """ assert type(other) == Fraction or type(other) == int, repr(other)+' is not a valid operand' if type(other) == int: return self.numerator <= other*self.denominator # Cross multiply left = self.numerator*other.denominator rght = self.denominator*other.numerator return left <= rght # OTHER METHODS 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)