CS 215 Fundamentals of Programming II Spring 2011 Project 2 20 points Out: February 2, 2011 Due: February 9, 2011 Reminder: Programming Projects (as opposed to Homework exercises) are to be your own work. See syllabus for definitions of acceptable assistance from others. The instructor's format for class specifications is defined in the on line handouts An and Design Style Guideline and An and Design Style Guideline for Classes. (They are used by the instructor in CS 210.) Links to these handouts also is provided on the CS 215 home page. Problem Statement A rational number is a number that can be expressed as an integer or the quotient of an integer divided by a nonzero integer. Expressed as a/b, a is called the numerator and b is called the denominator. Results of the binary arithmetic operations on rational numbers represented by a/b and c/d are as follows: Operator a/b + c/d a/b c/d a/b * c/d Result (ad + bc) / bd (ad bc) / bd ac / bd a/b / c/d ad / bc, where c not equal to 0 In addition, a/b = c/d when ad = bc. From this relationship, the equality and relational operations can be defined. Consider the following specification for a Rational class that models a rational number. The analysis of each operation is given formally, but the design of each operation is given informally in English. Specification for Rational Class Attributes: Objects Type Name numerator of the Rational int numerator denominator of the Rational int denominator The class invariant is as follows (i.e., all valid objects must meet these conditions): Revised: 02/02/2011 Page 1 of 6 D. Hwang
The denominator must be greater than 0. For example, a Rational created with initial values 3/ 4 will be stored as 3/4, and a Rational created with initial values 3/ 4 will be stored as 3/4. The ratio must be in reduced form. That is, the greatest common divisor of the numerator and denominator is 1. This can be achieved by dividing both the numerator and denominator by the greatest common divisor. For example, a Rational created with initial values 6/8 will be reduced to 3/4. Note this means that all integer values (including 0) have denominators of 1. Operations Explicit value constructor initialize attributes from passed values Must have default argument values of 0 and 1 (i.e. value 0) Precondition: Initial denominator must be non zero. Use assert() to check this. Post condition: ratio conforms to the class invariant as described above Objects Type Default Movement Name initial numerator int 0 received initialnumerator initial denominator int 1 received initialdenominator Notes: the greatest common divisor of two positive integers m and n may be computed using Euclid's algorithm. Here is an iterative version of this algorithm: 1. If m < n then swap values so that n < m 2. While n > 0 do 2.1. Save the value of n 2.2. Recompute n as the remainder of m divided by n 2.3. Set m to the saved value of n 3. m is now the greatest common divisor GetNumerator Returns numerator of this Rational numerator of Rational int returned numerator GetDenominator Returns denominator of this Rational Revised: 02/02/2011 Page 2 of 6 D. Hwang
denominator of Rational int returned denominator Reciprocal Returns a Rational that is the reciprocal of this Rational. Precondition: This Rational must not be value 0. Use assert() to check this. Reciprocal Rational Rational returned The reciprocal of this Rational is the Rational that produces value 1 when multiplied with this Rational. AsMixedFraction Returns a string that is this Rational in the form "i+n/d" (no spaces) where i is an integer and n < d. For example, 8/3 would return a string "2+2/3". If n = 0, then write just "i". If i = 0 then write just "n/d". Hint: you should use an ostringstream to do this. See design notes below. mixed fraction string returned friend overloaded arithmetic operator functions: operator+, operator-, operator*, operator/ Returns a Rational object whose value is the result of performing the appropriate operation. Precondition for operator/: The right operand must not be value 0. Use assert() to check this. a Rational Rational received leftoperand another Rational Rational received rightoperand result of computation Rational returned friend overloaded equality and relational operator functions: operator==, operator!=, operator<, operator<=, operator>, operator>= Returns true if the relationship between the left and right operands is true, false otherwise Revised: 02/02/2011 Page 3 of 6 D. Hwang
a Rational Rational received leftoperand another Rational Rational received rightoperand result of comparison bool returned operator>> friend overloaded operator function that reads Rational values from an input stream without prompting in format "n/d" (no spaces) or just "n" and store appropriate values in the attributes. Must check that the input denominator is not 0. If so, the Rational object is unchanged and the input stream should be put in the failed state and returned. Also, if the input stream fails for any reason, it should just be returned. See design notes below. Post condition: ratio conforms to the class invariant as specified above. input stream istream received, passed back, returned in a Rational Rational passed back target Note: the operation in.peek() will return the next character in the input stream without removing it from the stream. operator<< friend overloaded operator function that writes Rational to an output stream in format "n/d" (no spaces), except when d = 1, then write just "n". output stream ostream received, passed back, returned out a Rational Rational received source Assignment Write the implementation for this Rational class. Note that the class and member function names must be as specified above, including case. All other identifiers must have consistent case usage, but do not have to match the style of these names. The Rational class definition and friend operator function prototypes are to be put in header file rational.h with suitable compilation guards. The implementations of the Rational class and friend operator functions are to be put in source file rational.cpp. Revised: 02/02/2011 Page 4 of 6 D. Hwang
Write a main program in file testrational.cpp. This program should be an interactive, menudriven program similar to that presented in Section 3.3 of the textbook that allows the user to test the Rational class. The submission system only will check that the submitted makefile will compile this program; it will not run this program. A separate testing program will be compiled with the submitted Rational class and be used to test the class operations. Examples of responses from the submission system for this project will be made available on the course webpage. You must submit a makefile named Makefile.project2 for your project that creates an executable file named testrational. It should conform to the examples demonstrated in class. REMINDER: Your project must compile for it to be graded. Submissions that do not compile will be returned for resubmission and assessed a late penalty. Submissions that do not substantially work also will be returned for resubmission and assessed a late penalty. Follow the guidelines in the C++ Programming Style Guideline handout. As stated in the syllabus, part of the grade on a programming project depends on how well you adhere to the guidelines. The grader will look at your code listing and grade it according to the guidelines. What to submit Electronically submit a tarfile containing (only) Makefile.project2, rational.h, rational.cpp, and testrational.cpp as explained in the handout Submission Instructions for CS 215. Do not submit object files or executable files. Design Notes By convention, when an operator>> encounters invalid input, it changes the input stream state to a failed state. For this project, the input data should be read into local variables first. If the stream has already failed (tested using in.fail(), which returns true when the stream is in a failed state), the function should return in immediately. After this test, the input data should be checked against the class invariant. If it does not meet the invariant, the stream is put into a failed state using in.setstate (ios_base::failbit). In other words, operator>> will have the following structure: // code for reading in data values goes here if (in.fail()) // stream failed or eof return in; if (<invariant test is not met>) { in.setstate (ios_base::failbit); return in; } // code for setting data members goes here A stringstream is a stream object that is attached to a string. (This is analogous to an fstream being attached to a file.) Stringstreams are defined in the <sstream> library header. As with fstreams, there Revised: 02/02/2011 Page 5 of 6 D. Hwang
is an input stringstream (istringstream) and an output stringstream (ostringstream). Since these objects are a kind of I/O stream, the extraction (>>) and insertion (<<) operators are used. In particular for this assignment, an ostringstream can be used to create strings from mixed non string types. The following program demonstrates how to use an ostringstream: #include <iostream> #include <sstream> #include <string> using namespace std; int main () { // Some data string str = "January"; int i1 = 1, i2 = 2010; // Declare an ostringstream ostringstream ostr; // Put the data into ostr ostr << str << ' ' << i1 << ", " << i2; // Get the string out of the ostr string name = ostr.str() } // Display: January 1, 2010 cout << name << endl; return 0; Revised: 02/02/2011 Page 6 of 6 D. Hwang