Assigned | Tuesday, October 28thth |
---|---|
Program Due | Friday, November 7th by 11:59pm |
Weight | 8% |
Updates | None yet. |
To gain experience:
This project is considered an CLOSED project. Please review the open vs. closed project policy before beginning your project.
You will be developing a desk calculator to accept and compute the solution to fraction-based equations. It will do the usual addition, subtraction, multiplication, and division operations, but will work on full, general-form fractions. Even most smartphone calculators won't accept that! Also, your calculator will accept complex, nested expressions, with parentheses. On top of all this, it will accept input in a cool format called "prefix notation", different from the "infix" notation most boring calculators use. Here is an example of prefix notation:
( * 2 ( + 3 4 ) )Try to predict what this will yield. Prefix notation will be coverd in more detail later.
There will be one more feature to your calculator: It will accept AND PRESERVE the exact form of fractions as input by the user, but all calculated values returned by the operators will be in a strictly normalized form. In other words, it will be nice to the user and kindly reduce all computed terms to a more friendly form, taking care of both minimizing and factoring the fractional part of the number, matching the signs, etc. An example would be: An input (and therefore construction) of "4/3" stays as 4/3, but adding 0 to it would result in 1 1/3. The exact details are given later.
For your part of the project, you will be coding up the guts of the computational part of the calculator. We will be providing the part of the code that accepts and interprets the user input, and dispatches to the appropriate functions in your classes. For this project, we will need a very important type of program known as a "parser". A parser is a program that takes a stream of input, breaks it up into smaller components, infers the value and purpose of each component, and executes a specific sequence of tasks. For example, a C++ parser would take the sequence of characters that make up your source file, and figure out what you want your program to do. Parsers are often very difficult to write. However, it is much easier to understand one than to create one on your own, which is why we provide one to you. It is definitely worth looking through our code to learn how a simple parser works, so we would highly encourage you to try to understand the details.
So, we provide the U/I and parser, and you are responsible for creating a Fraction class that can support the required arithmetic operations, as well as normalization. Simple enough? No so fast...
Since you've just learned about operator overloading in class, it
is time to apply it on a project, and a calculator is a perfect fit
for overloading the arithmetic operators.
If you look through the provided code, you will see a section where
we first figure out which operation the user wants to do (addition,
subtraction, etc.), and then do that operation on the
operands. However, both operands are instances of your
Fraction class, and our code simply takes these operands and,
instead of invoking named methods like:
sum = operand1.AddFraction(operand2);
it expects you to have set things up so that it can use an overloaded
addition operator, thus:
result = operand1 + operand2; // and later: cout << result;But since operand1, operand2, and result are all instances of the Fraction class, you will have to get C++ to accept those as legal operands to its '+' operator, and get cout to accept Fractions as parameters: operator overloading to the rescue!
Infix: 3 * 4 Prefix: ( * 3 4 )
You probably noticed the '(...)' we threw around our simple expression above.
This points out the second requirement on our calculator's input format: all
subexpressions must be parenthesized. So, we have the following
equivalents:
Infix: 3 * 4 + 9 Prefix: ( + ( * 3 4 ) 9 )
The nice thing about prefix notation is that we've removed the need for any notion of operator precedence or associativity; that's why we chose it.
The third point: in order to
keep the parser code simple, we require spaces around all of our operators
and parentheses, but require an entire fraction specification to
be concatenated together without any embedded whitespace:
Bad: (*3 -17 & 36 / 12) Good: ( * 3 -17&36/12 )
You will be creating a class called Fraction
, which
implements the Fraction class that supports the storage as
well as arithmetic manipulation of numerical fractions.
It will model a Fraction as 4 parts: a an optional sign; a whole-number part;
a numerator; and a denominator. The sign applies to the entire number; so,
"-3&6/2" is equivalent to -6.
The user is allowed to enter any value for any of these parts,
with the one restriction that the denominator must not be 0 (zero).
NB: the optional sign applies to the entire fraction: "-1&2/2" would be equivalent to "-2". The parser separates out the sign part, and passes it in as a separate boolean input parameter to the constructor (it is the first parameter), "true" if the user entered a leading '-' sign.
So, some legal fractions are:
2 1/3 2&5/6 -2&21/6 (== -5.5) -7/2 (== -3.5)
The only thing your constructor should reject is a 0 for the denominator: if your constructor is called with that, you should print out an error and exit.
Your default constructor should create a Fraction of 0&0/1
You will be implementing the 4 basic arithmetic operations for Fractions, as well as the output function. That means you will need to write code to implement things like multiplication and subtraction between Fraction objects. Also, you will need to overload the '+', '-', '*', '/', and '<<' operators, in order to allow the code in Proj3Aux.cpp to compile and work.
Other than that, you will need to provide functions to overload the four arithmetic operators and the "<<" operator, and that's about it for the public interface for your Fraction class.
You are free to add any helper functions you'd like to make the Fraction class more modular and cleaner in design.
The only files we are providing are:
main()
$ ./Proj3.out // Some simple numbers: first a whole, then fraction, then mixed. > 1 1&0/1 > 2/3 0&2/3 > 1&2/3 1&2/3 // Now, some strange cases. Notice following is not normalized, // because it is merely constructed, and no arithmetic has been done yet: > -2&15/6 -2&15/6 // Now, to see normalization in action: taking the same number // as above, but add 0 to it to trigger normalization: > ( + -2&15/6 0 ) -4&1/2 // Another normalization example: > ( + 1&2/3 2&1/3 ) 4&0/1Note the answer is not 3&3/3, nor is it 4&0/3
You should probably first start with the constructors. You should then be able to test the program by just entering a fraction into the calculator, and making sure it prints out exactly as entered.
You should then add the overloaded operator functions,, starting with "<<". This one, you need right away, but it is really easy to implement: one or two lines of code. Note the following, though:
You should then move on to the overloaded arithmetic operators, but not actually implement the bodies yet. Just have them print out their operands, and then return an innocuous result (e.g., just construct a default 0&0/1 and return it). That way, you can test that the operator overloading is being called properly. Remember, the calculator's parser code cannot tell whether you're passing back numerically correct answers, so 0&0/1 is always a fine return value. It only demands that they be Fraction instances.
Next, you should actually implement the guts of the four operations, working on and debugging one at a time. After the '+' operator, '-' should be easy; same with '*' and '/'.
Lastly, work on the normalization code. Note that to implement this, you will probably have to implement algorithms to calculate the greatest common divisor (GCD) and lowest common multiple (LCM) of two numbers; brief outlines are given below.
As always, you will have to create a Makefile.
See the course website for a description of how your project will be graded.
We will be automating much of the grading for this project. It is absolutely essential that you do not modify any of the files that we provided to you; You should not even submit these files. If you do inadvertantly submit your copies of these files, we will be replacing them with our own versions before trying to compile and run your program.
Another requirement is that your executable, as produced by your Makefile, is called Proj3.out; adjust your Makefile accordingly.
Before submitting your project, be sure to compile and test your code on the GL system. See the Project Compilation section of the course Projects page for details on how to compile and execute your project on GL.
Assuming you’ve used the specified file names, submit your project by typing the command
submit cs202 proj3 [all of your various .h and .cpp files] Makefile
See the Project Submission page for detailed instructions. Note that we require you to submit the Makefile, also. We will in fact be using your own Makefile to build your program, so it must work properly.
You may resubmit your files as often as you like, but only the last submittal will be graded and will be used to determine if your project is late. Also note that if you rename or remove certain files, the old versions that you submitted earlier will stay in the submit directory unless you use "submitrm" to clean them out, which you should. At the end, a "submitls" should show exactly those files that are part of your project: no more, no less. For more information, see the Projects page on the course web site.
Remember — if you make any change to your program, no matter how insignificant it may seem, you should recompile and retest your program before submitting it. Even the smallest typo can cause errors and a reduction in your grade.