/* Network-Integrated Multimedia Middleware (NMM)
 * http://www.motama.com/nmm.html
 *  
 * Copyright (C) 2006-2007
 * Motama GmbH, Saarbruecken, Germany
 * http://www.motama.com
 *  
 * This file is part of NMM. 
 * 
 * NMM is free software; you can redistribute it and/or modify it    
 * under the terms of the GNU General Public License as published by 
 * the Free Software Foundation; only version 2 of the License.      
 * 
 * NMM is distributed in the hope that it will be useful, but WITHOUT 
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 
 * License for more details.  
 * 
 * You should have received a copy of the GNU General Public License 
 * along with NMM; if not, write to the Free Software Foundation,    
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 */

#ifndef NMM_RATIONAL_HPP
#define NMM_RATIONAL_HPP NMM_RATIONAL_HPP

#include "nmm/nmm_prefix.hpp"

#include <iosfwd>

#include "nmm/nmm_inttypes.hpp"
#include "nmm/nmm_types.hpp"

#include "visibility.hpp"

namespace NMM {

  //! RationalDivisionByZeroException
  /*!
   *  This exception is thrown if a division by zero occurs with rational numbers.
   *  That means perhaps the denominator is zero (in the constructor or if you try
   *  to build the reciprocal value) or you try to divide a rational number by
   *  zero.
   */
  class DLLEXPORT RationalDivisionByZeroException {
  public:
    RationalDivisionByZeroException();
  };

  //! This class provides basic support for rational numbers.
  class DLLEXPORT Rational {
  private:
    nmm_int64 numerator;    //!< The numerator. 
                          //!< It also carries the sign information.
    nmm_int64 denominator;  //!< The denominator.
                          //!< The denominator is always greater than 0.

  public:  
    //! Computes the greatest common divisors of two given nmm_int64egers.
    //! 
    //! @param a first integer
    //! @param b second integer
    //! @return greatest common divisors
    static nmm_int64 greatestCommonDivisor(nmm_int64 a, nmm_int64 b);

    //! Computes the leastCommonMultiple of two given nmm_int64egers.
    //! 
    //! @param a first integer
    //! @param b second integer
    //! @return the leastCommonMultiple of two given int64_tegers.
    static nmm_int64 leastCommonMultiple(nmm_int64 a, nmm_int64 b);

    //! The main constructor.
    /*! 
     *  \param numerator   The numerator can be an arbitrary integer.
     *  \param denominator The denominator must be non-zero.
     *  Throws RationalDivisionByZeroException if the denominator is zero.
     */
    Rational(const nmm_int64& numerator = 0, const nmm_int64& denominator = 1)
      throw (const RationalDivisionByZeroException&);

    //! Returns the numerator.
    //!
    //! @return the numerator
    inline nmm_int64 getNumerator() const
    {
      return numerator;
    };

    //! Returns the denominator.
    //!
    //! @return the denominator
   inline nmm_int64 getDenominator() const
    {
      return denominator;
    };

    //! Completely reduces the fraction.
    void reduce();

    //! Inverts the value of the fraction.
    /*!
     *  Throws RationalDivisionByZeroException if the rational number was zero.
     */
    void invert()
      throw (const RationalDivisionByZeroException&);

    //! Returns a new Rational number with the reciprocal value.
    //!
    //! @return Rational number with the reciprocal value.
    /*!
     *  Throws RationalDivisionByZeroException if the rational number was zero.
     */
    Rational getReciprocal()
      throw (const RationalDivisionByZeroException&);

    //! Returns the negative value of a rational number.
    //! 
    //! @return the negative value of a rational number.
    Rational& operator-();

    //! Adds a reational number to another.
    //! 
    //! @param b summand
    //! @return Rational including result 
    Rational& operator+=(const Rational& b);
  
    //! Subtracts a rational number from another.
    //! 
    //! @param b specifies subtrahend
    //! @return Rational including result 
    Rational& operator-=(const Rational& b);

    //! Multiplies a rational number with another.
    //! 
    //! @param b specifies multiplier
    //! @return Rational including result 
    Rational& operator*=(const Rational& b);

    //! Divides a rational number by another.
    /*!
     *  If the divisor is zero, RationalDivisionByZeroException is thrown.
     *
     * @param b specifies divisor
     * @return Rational including result
     */
    Rational& operator/=(const Rational& b)
      throw (const RationalDivisionByZeroException&);

    //! Returns true if a and b are equal.
    //!
    //! @param a specifies first Rational
    //! @param b specifies second Rational
    //! @return true if a and b are equal.
    friend bool operator== (const Rational& a, const Rational& b);

    //! Returns true if a and b are not equal.
    //!
    //! @param a specifies first Rational
    //! @param b specifies second Rational
    //! @return true if a and b are not equal.
    friend bool operator!= (const Rational& a, const Rational& b);

    //! Returns true if a is greater than b.
    //!
    //! @param a specifies first Rational
    //! @param b specifies second Rational
    //! @return true true if a is greater than b.
    friend bool operator>  (const Rational& a, const Rational& b);

    //! Returns true if a is greater than or equal to b.
    //!
    //! @param a specifies first Rational
    //! @param b specifies second Rational
    //! @return true true if a is greater than or equal to b.
    friend bool operator>= (const Rational& a, const Rational& b);

    //! Returns true if a is less than b.
    //!
    //! @param a specifies first Rational
    //! @param b specifies second Rational
    //! @return true true if a is less than b.
    friend bool operator<  (const Rational& a, const Rational& b);

    //! Returns true if a is less than or equal to b.
    //!
    //! @param a specifies first Rational
    //! @param b specifies second Rational
    //! @return true true if a is less than or equal to b.
    friend bool operator<= (const Rational& a, const Rational& b);

    //! The output operator for Rational.
    /*! 
     * Represents the rational number in the form "numerator / denominator".
     * 
     * @param s specifies the output stream to write the rational to.  
     * @param a Rational that is written to s 
     * @return reference to output stream s 
     */
    DLLEXPORT friend std::ostream& operator<<(std::ostream& s, const Rational& a);
  };

  //! Adds two rational numbers and returns the sum as a new object.
  //!
  //! @param a first summand
  //! @param b second summand
  //! @return Rational including the result 
  DLLEXPORT Rational operator+ (const Rational& a, const Rational& b);

  //! Subtracts two rational numbers and returns the diference as a new object.
  //!
  //! @param a specifies minuend. 
  //! @param b specifies subtrahend
  //! @return Rational including the result 
  DLLEXPORT Rational operator- (const Rational& a, const Rational& b);

  //! Multiplies two rational numbers and returns the product as a new object.
  //!
  //! @param a first multiplier
  //! @param b second mulitplier
  //! @return Rational including the result 
  DLLEXPORT Rational operator* (const Rational& a, const Rational& b);

  //! Divides two rational numbers and returns the quotient as a new object.
  //!
  //! @param a specifies dividend
  //! @param b specifies divisor
  //! @return Interval including the result
  DLLEXPORT Rational operator/ (const Rational& a, const Rational& b);

  /** Output stream for Rational. 
   * Represents the rational number in the form "numerator / denominator".
   * @param s specifies the output stream to write the rational to.  
   * @param a Rational that is written to s 
   * @return reference to output stream s 
   */
  DLLEXPORT std::ostream& operator<< (std::ostream& s, const Rational& a);

  inline bool operator== (const Rational& a, const Rational& b)
  {
    return a.numerator == b.numerator && a.denominator == b.denominator;
  };

  inline bool operator!= (const Rational& a, const Rational& b)
  {
    return a.numerator != b.numerator || a.denominator != b.denominator;
  };

  inline bool operator>  (const Rational& a, const Rational& b)
  {
    return a.numerator * b.denominator >  b.numerator * a.denominator;
  };

  inline bool operator>= (const Rational& a, const Rational& b)
  {
    return a.numerator * b.denominator >= b.numerator * a.denominator;
  };

  inline bool operator<  (const Rational& a, const Rational& b)
  {
    return a.numerator * b.denominator <  b.numerator * a.denominator;
  };

  inline bool operator<= (const Rational& a, const Rational& b)
  {
    return a.numerator * b.denominator <= b.numerator * a.denominator;
  };

}

#include "nmm/nmm_suffix.hpp"

#endif
