#include #include #include "Rational.hpp" ///////////////////////////////////////////////// // This file contains all function definitions // // - All function declarations are in // // the file Rational.hpp // ///////////////////////////////////////////////// // This example extends the previous Rational project by // introducing operator overloading. Refer to the previous // project for a discussion of rational arithmetic, // comparisons, inverses, and absolute values. // // The focus here is on how operator overloading allows // expressions involving Rational objects to resemble the // corresponding mathematical expressions. // // For example, if '+' is overloaded for Rational objects, // we can write // // p + q // // instead of // // add( p, q ) // // The expression p + q results in a call to // // operator+( p, q ) // // Multiple overloads allow Rational objects and integers // to appear together in expressions. ////////////////////////// // Arithmetic operators // ////////////////////////// // Calculate p + q. // // This is the fundamental addition operation. The other // overloads of + below reuse this overload. Rational operator+( Rational const &p, Rational const &q ) { return Rational{ p.numer_*q.denom_ + q.numer_*p.denom_, p.denom_*q.denom_ }; } // Calculate p + n. // // Convert the integer n into a Rational object and then // reuse the Rational + Rational overload. Rational operator+( Rational const &p, int const n ) { return p + Rational{ n, 1 }; } // Calculate n + p. // // Again, convert the integer n into a Rational object. // Addition is commutative, so we can reuse p + n. Rational operator+( int const n, Rational const &p ) { return p + Rational{ n, 1 }; } // Calculate p - q. // // Subtraction is implemented using addition and unary -. Rational operator-( Rational const &p, Rational const &q ) { return p + (-q); } // Calculate p - n. Rational operator-( Rational const &p, int const n ) { return p + (-n); } // Calculate n - p. // // Unlike addition, subtraction is not commutative, so the // order of the two operands must be preserved. Rational operator-( int const n, Rational const &p ) { return n + (-p); } // Calculate p*q. // // This is the fundamental multiplication operation. The // other overloads of * below reuse this overload. Rational operator*( Rational const &p, Rational const &q ) { return Rational{ p.numer_*q.numer_, p.denom_*q.denom_ }; } // Calculate p*n. Rational operator*( Rational const &p, int const n ) { return p*Rational{ n, 1 }; } // Calculate n*p. Rational operator*( int const n, Rational const &p ) { return Rational{ n, 1 }*p; } // Calculate p/q. // // Division by q is multiplication by its multiplicative // inverse. The numerator of q must therefore be non-zero. Rational operator/( Rational const &p, Rational const &q ) { assert( q.numer_ != 0 ); return p*inverse( q ); } // Calculate p/n. // // The integer n must be non-zero. Rational operator/( Rational const &p, int const n ) { assert( n != 0 ); return p*Rational{ 1, n }; } // Calculate n/p. // // Unlike multiplication, division is not commutative, so // the order of the operands must be preserved. Rational operator/( int const n, Rational const &p ) { assert( p.numer_ != 0 ); return Rational{ n, 1 }/p; } ////////////////////// // Unary operators // ////////////////////// // Apply unary + to p. // // Unary + leaves the rational number unchanged. Rational operator+( Rational const &p ) { return p; } // Apply unary - to p. // // Unary - returns the additive inverse. Rational operator-( Rational const &p ) { return Rational{ -p.numer_, p.denom_ }; } /////////////////////////////// // Other rational operations // /////////////////////////////// // Return the multiplicative inverse of p. // // There is no natural C++ operator for this operation, so // inverse remains an ordinary function. Rational inverse( Rational const &p ) { assert( p.numer_ != 0 ); return Rational{ p.denom_, p.numer_ }; } // Return the absolute value of p. // // There is already an abs(...) function for built-in // numerical types, so using the same identifier is natural. Rational abs( Rational const &p ) { return Rational{ std::abs( p.numer_ ), std::abs( p.denom_ ) }; } ////////////////////////// // Comparison operators // ////////////////////////// // Determine whether p == q. // // Cross multiplication allows equivalent representations // such as 1/2 and 2/4 to compare equal. bool operator==( Rational const &p, Rational const &q ) { return p.numer_*q.denom_ == q.numer_*p.denom_; } // Determine whether p == n. bool operator==( Rational const &p, int const n ) { return p == Rational{ n, 1 }; } // Determine whether n == p. bool operator==( int const n, Rational const &p ) { return Rational{ n, 1 } == p; } // Determine whether p != q. // // Rather than repeating the comparison, != is implemented // by negating the result of ==. bool operator!=( Rational const &p, Rational const &q ) { return !(p == q); } // Determine whether p != n. bool operator!=( Rational const &p, int const n ) { return !(p == n); } // Determine whether n != p. bool operator!=( int const n, Rational const &p ) { return !(n == p); } // Determine whether p < q. // // The direction of the comparison depends on the sign of // the product of the two denominators. bool operator<( Rational const &p, Rational const &q ) { if ( p.denom_*q.denom_ > 0 ) { return p.numer_*q.denom_ < q.numer_*p.denom_; } else { return p.numer_*q.denom_ > q.numer_*p.denom_; } } // Determine whether p < n. bool operator<( Rational const &p, int const n ) { return p < Rational{ n, 1 }; } // Determine whether n < p. bool operator<( int const n, Rational const &p ) { return Rational{ n, 1 } < p; } // Determine whether p <= q. // // Reuse the already defined < and == operators. bool operator<=( Rational const &p, Rational const &q ) { return (p < q) || (p == q); } // Determine whether p <= n. bool operator<=( Rational const &p, int const n ) { return (p < n) || (p == n); } // Determine whether n <= p. bool operator<=( int const n, Rational const &p ) { return (n < p) || (n == p); } // Determine whether p >= q. // // If p is not less than q, then p must be greater than or // equal to q. bool operator>=( Rational const &p, Rational const &q ) { return !(p < q); } // Determine whether p >= n. bool operator>=( Rational const &p, int const n ) { return !(p < n); } // Determine whether n >= p. bool operator>=( int const n, Rational const &p ) { return !(n < p); } // Determine whether p > q. // // Reuse the already defined <= operator. bool operator>( Rational const &p, Rational const &q ) { return !(p <= q); } // Determine whether p > n. bool operator>( Rational const &p, int const n ) { return !(p <= n); } // Determine whether n > p. bool operator>( int const n, Rational const &p ) { return !(n <= p); } /////////////////////////// // Output and conversion // /////////////////////////// // Approximate p by a double-precision floating-point value. double real( Rational const &p ) { assert( p.denom_ != 0 ); return static_cast( p.numer_ ) /static_cast( p.denom_ ); } // Return a string representation of p. // // If the denominator is negative, move the negative sign // to the numerator. If the denominator is 1 or -1, display // the rational number as an integer. std::string to_string( Rational const &p ) { if ( p.denom_ == 1 ) { return std::to_string( p.numer_ ); } else if ( p.denom_ == -1 ) { return std::to_string( -p.numer_ ); } else if ( p.denom_ > 0 ) { return std::to_string( p.numer_ ) + "/" + std::to_string( p.denom_ ); } else { return std::to_string( -p.numer_ ) + "/" + std::to_string( -p.denom_ ); } } // Insert p into an output stream. // // Overloading << allows a Rational object to be used with // an output stream in the same manner as built-in types: // // std::cout << p; // // The output stream is returned by reference so that // insertions can be chained: // // std::cout << p << q << std::endl; std::ostream &operator<<( std::ostream &out, Rational const &p ) { out << to_string( p ); return out; }