This lab will help you practice with classes and libraries.
Design and code a class to represent rational numbers. Recall that rational numbers have a numerator and a denominator both of which come from the set of integers.
Common mathematical operations on rational numbers are addition, subtraction, multiplication, division, and reciprocal:
A B Reciprocal of --- = --- B A A B A + B Sum of --- and --- = ------- C C C A C A*D + C*B Sum of --- and --- = ----------- B D B*D A B A - B Difference of --- and --- = ------- C C C A C A*D - C*B Difference of --- and --- = ----------- B D B*D A C A*C Product of --- and --- = ----- B D B*D A C A D A*D Quotient of --- and --- = --- * --- = ----- B D B C B*C A A -A A Additive Inverse of --- = - --- = ---- = ---- B B B -B Comparison: A C --- < --- --> A*D < C*B B D A C --- ≤ --- --> A*D ≤ C*B B D A C --- > --- --> A*D > C*B B D A C --- ≥ --- --> A*D ≥ C*B B D A C --- &eq; --- --> A*D &eq; C*B B D A C --- ≠ --- --> A*D ≠ C*B B D
Noting that we need not consider special cases such as like already common denominators for addition and subtraction, we throw those two formulas out. We also note the use of both multiplication and reciprocal during division and so we'll get to re-use our functionality (should we so desire).
To make sure the sum and difference (and maybe the product and quotient?) don't overflow our chosen integer type, we should make sure to keep the numerator and denominator 'reduced to lowest terms' at all times. This means we'll need a helper function to find the greatest common factor (aka greatest common divisor) of the numerator and denominator. We could make this a private function, but due to its generic nature — having nothing specifically to do with rational numbers — we should probably place it in its own library (maybe a math related one?).
(Note: You cannot use either the gcd or lcm functions from the standard library! You must code the greatest common divisor for yourself to receive full credit. Note also the option below for a faster method.)
Display and entry of rational numbers can be deceptively difficult so let's keep it as simple as possible and forgo the standard notation used above in favor of the more basic: A/B. (For further discussion, please see the notes elsewhere on the site.)
To keep the display format as readable as possible — not to mention making the comparison formulas guaranteed accurate, we should keep the denominator positive at all times. (So if we read or calculate a negative denominator somewhere, we should take the additive inverse of both it and the numerator — 'canceling' the common -1 factor from both.) Along with keeping the denominator positive, let's keep it non-zero, too. (It wouldn't do to be dividing by 0, now would it?)
Speaking of dividing, provide a method (asdecimal, decimalform, approximate, or whatever) which returns the decimal representation of the rational number (1/2 --> 0.5).
Noting that formulas are provided for unary negation and comparison tests, you should provide methods for those operations, too.
Don't forget to include a complete set of constructors, accessors, and mutators as well as the math specialty functions and input/output mentioned above.
Place your rational ADT in a library.
Write a test application (driver) to make sure ALL of your operations work.
Did you choose short or long for your numerator/denominator? Why do you feel this is the best choice?
How can you keep the denominator positive? What happens to the numerator?
Do you have any private method(s)? If so, what does it/them do? (Hint: *ahem* reduce() *ahem* normalize() *ahem*)
How many lines are in your less and equal functions? (Not many, I hope...)
Do your math functions change the calling object or their argument? Should they? (What happens when you code x + y in your program: does the compiler change either x or y to find the indicated sum?) How can you specify such a situation — where neither the argument nor calling objects are to be altered — to the compiler so it can better handle/enforce your decision?
How many lines are in your math functions? (Hint: You are constructing a new object and returning its value to the caller...)
Does your input method prompt the user before reading the rational number? Why should it not?
Does your output method print anything besides the rational number (in proper notation, of course) — even an endl? Why should it not?
Why do/can you only have one mutator? (Hint: Does this relate to the thought-provoking question above? What other things might also influence this decision? What might happen if the programmer could change the numerator but not the denominator? Vice versa? Would this complicate your validation rules?)
This assignment is (Level 5).
Add (Level 2) to properly apply inlineing and constness to all of your class methods. (This should include proper use of initialization lists on constructors!)
Should any of your methods be inlined for speed? Which ones will you inline? How does one do this for a class method?
If you end up inlineing all of your methods, do you still need an implementation file for your library? Why/Why not?
Do the accessors, output, or math operation methods alter the calling object in any way? How do you specify such a situation to the compiler so it can better handle/enforce your decision?
What needs to be in your constructor initialization lists? Is there a certain order to the list? Does it go on the prototype or the definition?
Add (Level 2) for using Euclid's algorithm — with full comments! — in your greatest common factor/divisor routine. (Hint: I'll let you find this one on your own, but I will say that I've always found the mathematics portal on Wikipedia to be of great help in situations like these.) (Hint to the hint: It's just three clicks away if you pay attention to the details...)
Add (Level 2) for overloading your math operation functions to allow mixing of rational numbers with built-in integer types. (Hint: What would be the result of adding 1/2 to 5? While answering, recall that the only data types you have handy are integer and Rational — floating point wouldn't make any sense as it would lose precision in most cases.)