CS 3723
Programming Languages  
Fall 2014
  Homework 11. Python Classes 
Week 14-15: Nov 24 - Dec 5

Submit following directions at: submissions and rules at: rules. Deadlines are:
  • 2014-12-03  23:59:59 (that's Wed, 3 Dec 2014, 11:59:59 pm) for full credit.
  • 2014-12-08  23:59:59 (that's Mon, 8 Dec 2014, 11:59:59 pm) for 75% credit.

Important Notes: If you are using Python 3, you need to use __truediv__ instead of __div__ below.

This homework should be harder and more time-consuming than the next homework: H12 Time/Date Classes. It would be a mistake to get hung up on H11 and not get to the easier H12.


References: References for this homework are:

Additional references from our standard source are:


Fractions: This homework focuses on a Python implementation of fractions. Python already has an implementation of fractions in the Python library, but you are not to look at this. (The Python code is very long and sophisticated. I think it would be more confusing than helpful.) You are also not to look up other Python fractions code online.

Instead, for this homework, you are to add code to an incomplete fractions class given below. The partial code below comes from the first link above, which has a detailed discussion of the example. You must read over the material about this example. (My discussion here is not nearly enough for this homework.)

I have rewritten this program slightly, mainly to shorten identifiers and replace print.

Class Implementing Fractions Rest + Output
# fraction.py: implement fractions
import sys
# num = top = numerator
# den = bottom = denominator
def gcd(m,n):   # m can be negative
    while m%n != 0:
        oldm = m
        oldn = n

        m = oldn
        n = oldm%oldn
    return n

class Fraction:
    def __init__(self,top,bottom):
        self.num = top
        self.den = bottom
        sys.stdout.write("New: ")
        Fraction.show(self)
        sys.stdout.write("\n")

    def __str__(self):
        return str(self.num)+"/"+str(self.den)

    def show(self):
        sys.stdout.write(str(self.num) + "/" +
          str(self.den))

    def __add__(self,other):
        newnum = self.num*other.den + \
                     self.den*other.num
        newden = self.den * other.den
        com = gcd(newnum,newden)
        return Fraction(newnum//com,newden//com)

    def __eq__(self, other):
        firstnum = self.num * other.den
        secondnum = other.num * self.den
        return firstnum == secondnum
u = Fraction(1, 2)
v = Fraction(2, 3)
w = Fraction(3, 7)
x = Fraction(-4, 11)
y = Fraction(-7, 13)
z = Fraction(-2, 5)
sys.stdout.write("u + v: " +
      str(u+v) + "\n")
sys.stdout.write("u == v: "+
      str(x == y) + "\n")
sys.stdout.write("w + x: " +
      str(w+x) + "\n")
sys.stdout.write("u + y: " +
      str(u+y) + "\n")
sys.stdout.write("x + y: " +
      str(x+y) + "\n")
sys.stdout.write("x: " +
      str(x) + "\n")
x = w
sys.stdout.write("x: " +
      str(x) + "\n")

% python fraction.py New: 1/2 == u New: 2/3 == v New: 3/7 == w New: -4/11 == x New: -7/13 == y New: -2/5 == z New: 7/6 u + v: 7/6 u == v: False New: 5/77 w + x: 5/77 New: -1/26 u + y: -1/26 New: -129/143 x + y: -129/143 x: -4/11 x: 3/7 after: x = w


Additions to the Fractions class above: (These additions are taken from the same reference above.)

  1. Implement the simple methods getNum and getDen that will return the numerator and denominator of a fraction.

  2. Add functions to handle the other binary infix arithmetic operators: , *, and /.
    These must be functions __sub__, __mul__, and __div__, similar to __add__ above.

  3. Add functions for binary infix comparison operators: !=, <, <=, >, >=. These must be functions __ne__, __lt__, __le__, __gt__, __ge__, similar to __eq__ above.

  4. "In many ways it would be better if all fractions were maintained in lowest terms right from the start. Modify the constructor for the Fraction class so that gcd is used to reduce fractions immediately. Notice that this means the __add__ function no longer needs to reduce. Make the necessary modifications." (Taken from the link above.)


Testing Your Fractions class:
  • Test Code A: This assumes it is included in the same file with the class.

Test Code A (include with class file)
u = Fraction(1, 2)
v = Fraction(2, 3)
w = Fraction(3, 7)
x = Fraction(-4, 11)
y = Fraction(-7, 13)
z = Fraction(-2, 5)

sys.stdout.write("x.getNum(): " + str(x.getNum()) + "\n")
sys.stdout.write("x.getDen(): " + str(x.getDen()) + "\n")

sys.stdout.write("u != v: " + str(u != v) + "\n")
sys.stdout.write("u <  v: " + str(u < v) + "\n")
sys.stdout.write("u <= v: " + str(u <= v) + "\n")
sys.stdout.write("u == v: " + str(u == v) + "\n")
sys.stdout.write("u >  v: " + str(u > v) + "\n")
sys.stdout.write("u >= v: " + str(u >= v) + "\n")
sys.stdout.write("x != y: " + str(x != y) + "\n")
sys.stdout.write("x <  y: " + str(x < y) + "\n")
sys.stdout.write("x <= y: " + str(x <= y) + "\n")
sys.stdout.write("x == y: " + str(x == y) + "\n")
sys.stdout.write("x >  y: " + str(x > y) + "\n")
sys.stdout.write("x >= y: " + str(x >= y) + "\n")
sys.stdout.write("u + v: " + str(u + v) + "\n")
sys.stdout.write("u - v: " + str(u - v) + "\n")
sys.stdout.write("w + x: " + str(w + x) + "\n")
sys.stdout.write("w - x: " + str(w - x) + "\n")
sys.stdout.write("u + y: " + str(u + y) + "\n")
sys.stdout.write("u - y: " + str(u - y) + "\n")
sys.stdout.write("x + y: " + str(x + y) + "\n")
sys.stdout.write("x - y: " + str(x - y) + "\n")
sys.stdout.write("w * x: " + str(w * x) + "\n")
sys.stdout.write("y * z: " + str(y * z) + "\n")
sys.stdout.write("w * y: " + str(w * y) + "\n")
sys.stdout.write("u / v: " + str(u / v) + "\n")
sys.stdout.write("w / x: " + str(w / x) + "\n")
sys.stdout.write("x / z: " + str(x / z) + "\n")
sys.stdout.write("y / v: " + str(y / v) + "\n")

 

  • Test Code B: Here is the continued fraction expansion of π. Truncating this fraction gives successive "best" approximations to π by fractions.

    First truncation: 3+1/(7+1/(15+1))
    Second truncation: 3+1/(7+1/(15+1/(1+1/292)))
    Third truncation: 3+1/(7+1/(15+1/(1+1/(292+1/(1+
       1/(1+1/(1+1/(2+1/(1+1/(3+1/(1+1/14)))))))))))

    Here's how I suggest you do this in Python as fractions: for the First Trunctation, write each integer as a fraction, and then use the expression. Do the other two the same way. (You are to do this using your Python Fraction class, and not by hand or in any other way.)

    c1 = Fraction(1, 1)
    c2 = Fraction(2, 1)
    c3 = Fraction(3, 1)
    c7 = Fraction(7, 1)
    p = c3 + c1 / (c7 + c1 / (c15 + c1))
    


  • Test Code C: For the final testing, use the following more complicated continued fraction. You already saw this in two previous homeworks.

    In Homework 2 you were asked to produce an iterative Python program to compute the fraction to any depth. Here is one solution to that homework:

    Compute Continued Fraction
    # pi_iter.py: pi from cont. fraction
    import sys
    
    def pi_cf(i):
        t = 2.*i + 1
        while i >= 1:
            t = (2*i - 1) + (i*i)/t
            i = i - 1
        return 4/t
    
    sys.stdout.write("%18.15f, %18.15f\n" %
      (pi_cf(10), pi_cf(15)))
    
    % python pi_iter.py 3.141592673030334, 3.141592653586889

    Three recursive versions of this program are at: Homework 4 Answers.

    Convert the calculation of one or the other program into a calculation of fractions. Use a depth of 20.

    The final (large) fraction should be very close to π (15 digits). [For each of these programs I got the same fraction: 188557135970304/60019600489849, which is approximately 3.1415926535897936. Your results could differ, but they should be close to π. For much larger calculations of this type, see Larger Calculations.]


What you should turn in:
  1. Source code for the Python class Fraction as you have extended it with new arithmetic operators, comparisons, the two get's, and the relocated gcd calls.

  2. A run with Test Code A above, showing the resulting output.

  3. A run with Test Code B above, showing the resulting output.

  4. A run with Test Code C above, showing the resulting output.

( Revision date: 2014-11-20. Please use ISO 8601, the International Standard Date and Time Notation.)