MIT OpenCourseWare
  • OCW home
  • Course List
  • about OCW
  • Help
  • Feedback
  • Support MIT OCW

Exercise 2

Due: Week 3

Introduction

In this problem set, you will practice reading and interpreting specifications, as well as reading and writing Java™ source code. You will implement a pair of classes that will complete the implementation of a graphing polynomial calculator, and answer questions about both the code you are given and the code you have written.

To complete this problem set, you will need to know

  • Basic algebra (rational and polynomial arithmetic)
  • How to read procedural specifications (requires, modifies, effects) and representation invariants
  • How to read and write basic Java™ code
    • code structure and layout (class and method definition, field and variable declaration)
    • method calls
    • control structure: loops (while and for) and conditional branches (if, then, else)
    • operators for:
      • object creation: new
      • field and method access: .
      • assignment: =
      • comparison: ==, !=, <, > , <=, >=
      • arithmetic: +, -, *, /
  • How to run a Java™ compiler such as Java™c to create .class files
  • How to run the Java™ Virtual Machine (JVM) such as Java to execute both your code and staff-provided libraries

Problem 1: RatNum (15 points)

Read the specifications for RatNum , a class representing rational numbers. Then read over the staff-provided implementation, RatNum.java. You may find it helpful to peruse the code in RatNumTest.java to see example usages of the RatNum class (albeit in the context of a test driver, rather than application code). Answer the following questions, writing your answers in the file problem1.txt

What is the point of the one-line comments at the start of the add, sub, mul, and div methods? Notice that add, sub, mul, and div all require that "arg != null". This is because all of the methods access fields of 'arg' without checking if 'arg' is null first. But the methods also access fields of 'this' without checking for null; why is "this != null" absent from the requires-clause for the methods? RatNum.div(RatNum) checks whether its argument is NaN (not-a-number). RatNum.add(RatNum) and RatNum.mul(RatNum) do not do that. Explain. Why is RatNum.parse(String) a static method? What alternative to static methods would allow one to accomplish the same goal of generating a RatNum from an input String? Imagine that the representation invariant were weakened so that we did not require that the numer and denom fields be stored in reduced form. This means that the method implementations could no longer assume this invariant held on entry to the method, but they also no longer were required to enforce the invariant on exit. The new rep invariant would then be:
// Rep Invariant for every RatNum r: ( r.denom >= 0 )

Which method or constructor implementations would have to change? For each changed piece of code, describe the changes informally, and indicate how much more or less complex the result would be (both in terms of code clarity, and also in terms of execution efficiency). Note that the new implementations must still adhere to the given spec; in particular, RatNum.unparse() needs to output fractions in reduced form.

Problem 2: RatPoly (45 points)

Read over the specifications for the RatTerm , RatTermVec , and RatPoly classes. The implementation of RatTerm and RatTermVec is provided for you (no need to write the code). Make sure that you understand the overview for RatPoly and the specifications for the given methods. Read through the provided skeletal implementation of RatPoly.java . The most significant parts of the provided file are the comments describing how you are to use the provided fields to implement this class. The Rep. Invariant is an especially important comment to understand, because what invariants you define can have a drastic effect on which implementations will be legal for the methods of RatPoly.

Fill in an implementation for the methods in the spec of RatPoly. You may define new private helper methods as you like; we have suggested a few ourselves, complete with specifications, but you are not obligated to use them. (The staff believes that doing so will drastically simplify your implementation, however).

Also, we have provided a fairly rigorous test suite in RatPolyTest.java . You can run the given test suite with JUnit as you program and evaluate your progress and the correctness of your code. To run the RatPoly test suite, type the following command:

athena% java junit.swingui.TestRunner ex2.RatPolyTest

For a non-graphical alternative method of running JUnit, use junit.textui.TestRunner instead of junit.swingui.TestRunner, as stated in the Hints.

Indicate clearly as a comment in your source code whether or not your code passes all the tests. We will assume that your code fails if you say nothing.

Problem 3: RatPolyStack (25 points)

Follow the same procedure given in Problem 2, but this time fill in the blanks for RatPolyStack.java . The same rules apply here (you may add private helper methods as you like).
We have provided a test suite in RatPolyStackTest.java . You can run the given test suite with JUnit as you program and evaluate your progress and the correctness of your code. To run the RatPolyStack test suite, type the following command:

athena% java junit.swingui.TestRunner ex2.RatPolyStackTest

Indicate clearly as a comment in your source code whether or not your code passes all the tests. We will assume that your code fails if you say nothing.

Problem 4: PolyCalc (5 points)

Now that you have implemented the two remaining classes in the system, you can run the PolyCalc application. This allows you to input polynomials and perform arithmetic operations on them, through a point-and-click user interface. The calculator graphs the resulting polynomials as well. To run PolyCalc, type the following command:

athena% java ex2.PolyCalcFrame

A window will pop up with a stack on the left, a graph display on the right, a text area to input polynomials into, and a set of buttons along the bottom. Click the buttons to input polynomials and to perform manipulations of the polynomials on the stack. The graph display will update on the fly, graphing the top four elements of the stack.

Submit your four favorite polynomial expression, in the RatPoly.unparse format, in the file problem4.txt.

Problem 5: Module Dependency Diagram (10 points)

Draw a module dependency diagram showing all the classes and interfaces used in the final running program, with their depends and meets relationships. You need not include PolyCalcFrame. You should make a reasonable judgment about which Java™ library classes and interfaces to include.
You (and your TA) may find it useful if you use Visio to generate the MDD's. Refer to the Tools Handout for more information about Visio.

Provided classes:
The following classes are all provided for you, in compiled form, by the staff:

  • With source code also provided :
    ex2.RatNum
    ex2.RatNumTest
    ex2.RatPolyTest
    ex2.RatPolyStackTest
    ex2.Cons (as part of the RatPolyStack.java starter code)
  • With source code not provided:
    ex2.PublicTest
    ex2.PolyGraph
    ex2.PolyCalcFrame
    ex2.RatTerm
    ex2.RatTermVec

Getting started

Make sure you have followed the instructions on the tools handout relevant to directory setup and using Java™ before you begin development.
Create an ex2 directory and copy the provided files to it:

mkdir ~/6.170/ex2
cp -p /mit/6.170/www/psets/ex2/ex2-spec/* ~/6.170/ex2

Then, edit the specification files into your implementation, and test your implementation. You do not need to compile the other sourcecode files that we have provided; compiled classfiles are already ready for your use in the 6.170 locker. See the Specifications for the classes you will implement and those that are provided for you.

By the end of the problem set, you should have the following files ready to submit in your ex2 directory: problem1.txt , RatPoly.java, RatPolyStack.java, and problem4.txt , along with the Manifest file listing your submission entries.

Hints

  • See the problem set guidelines and advice.
  • Think before you code! The polynomial arithmetic functions are not difficult, but if you begin implementation without a specific plan, it is easy to get yourself into a terrible mess.
  • JUnit reloads all of your classes each time you run a test, so you don't need to restart the JUnit application after making changes to your Java code (though you do need to recompile your code for the changes to take effect).
  • For a non-graphical alternative method of running JUnit, use junit.textui.TestRunner instead of junit.swingui.TestRunner .
  • The provided test suites in problem set 1 are the same ones we will be using to grade your implementation; in later problem sets the staff will not provide such a thorough set of test cases to run on your implementations, but for this problem set you can consider the provided set of tests to be rigorous enough that you do not need to write your tests.
  • Divison of polynomials over the rationals is similar to the long division that one learns in grade school. We draw an example of it here:

Errata

There was a small bug in the implementation of the provided code in RatPoly.java. This caused students to fail testcases with NaN errors. The fix is described as follows:
insert the following lines of code in RatPoly.appendTerm(StringBuffer sb, RatTerm rt):



                   if(sb.toString().equals("NaN"))


                     {


                     return;


                     }


                     


                     if(c.isNaN())


                     {


                     sb.replace(0, sb.length(), "NaN");


                     return;


                     }


right after the declarations:



                     RatNum c = rt.coeff;


                     int e = rt.expt;


at the beginning of the method.

Problem 2 incorectly stated that "The test suite depends heavily on the implementation of the RatPoly.unparse() method; if the test suite claims that all or many of the tests are failing, the source of the problem could be a bug in your implementation of unparse() ." The unparse() method is implemented for you.

Q & A

This section will list clarifications and answers to common questions about problem sets. We'll try to keep it as up-to-date as possible, so this should be the first place to look (after carefully rereading the problem set handout and the specifications) when you have a problem.

Q: What should RatPoly.eval() return on a RatPoly for which isNaN() is true?

A: The java.lang.Double class has a static final double called Double.NaN that represents NaN. That's what you should return in this case.

Q: Should RatPoly(0,n) make a "0" polynomial? How am I supposed to implement "0" polynomials? Can I represent it as a term with a zero coefficient?

A: Yes, RatPoly(0,n) should make a "0" polynomial. However, you can not create a term with a zero coefficient to represent it, because that would violate the representation invariant of RatPoly. You should carefully note the last sentence in the abstraction function of RatPoly: "If there are no terms, then the RatPoly represents the zero polynomial."

Q: But I don't get it.. Doesn't the rep. invariant say that terms can't be null? How can there be no terms, then?

A: Yes, but it can be empty. Please note that while the rep invariant states that the terms field itself cannot be null, you can have the terms field refer to a RatTermVec object that does not contain any terms. (This would lead to terms.size() returning 0.) Please talk to a TA if you're still confused about this point. (Please see the RatPoly code comments for the abstraction function and rep. invariant.)

Q: Why is the coeff argument to the RatPoly constructor an int instead of a RatNum?

A: This constructor is used by our test cases. We didn't provide another constructor which takes a RatNum for the coefficient because RatPoly.parse() is a more useful way of creating new RatPolys.

Q: But I don't get it.. Doesn't the rep. invariant say that terms can't be null? How can there be no terms, then?

A: Yes, but it can be empty. Please note that while the rep invariant states that the terms field itself cannot be null, you can have the terms field refer to a RatTermVec object that does not contain any terms. (This would lead to terms.size() returning 0.) Please talk to a TA if you're still confused about this point. (Please see the RatPoly code comments for the abstraction function and rep. invariant.)

Q: I experienced a problem with Visio 2000 popping up an error dialog on starup that says, "The procedure entry point GBL could not be located in the dynamic link library VISLIB32.DLL."? I am installing Visio 2000 on a Windows 2000 machine.

A: Refer to Microsoft Support: Visio2000: Error Message: The Procedure Entry Point GBL Could Not Be Located in the Dynamic Link Library Vislib32.dll.

Q: Is it possible to do Exercise 2 on my home (not Athena) machine?

A: Yes, but you must download the appropriate jars from the 6.170 course locker to your local machine. For Exercise 2, you will need the junit.jar and ex2-lib.jar from the /mit/6.170/lib directory. However, your code MUST work on Athena, so please take a few minutes before submission of your exercises to ensure that it runs correctly on Athena. [Download ex2-lib.jar] [Download junit.jar]

Q: Where can I find the JUnit API?

A: The JUnit API can be found here.