/* HMath: C++ high precision math routines
   Copyright (C) 2004 Ariya Hidayat <ariya.hidayat@gmail.com>
   2007 Helder Correia <helder.pereira.correia@gmail.com>

   This program 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; either version 2
   of the License, or (at your option) any later version.

   This program 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 this program; see the file COPYING.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301, USA.
*/

#ifndef HMATH_H
#define HMATH_H

#include <iostream>

class HMath;
class HNumberPrivate;

class HNumber
{
    friend class HMath;

public:
    /*!
     * Creates a new number.
     */
    HNumber();

    /*!
     * Destroys this number.
     */
    ~HNumber();

    /*!
     * Copies from another number.
     */
    HNumber( const HNumber& );

    /*!
     * Assigns from another number.
     */
    HNumber& operator=( const HNumber& );

    /*!
     * Creates a new number from an integer value.
     */
    HNumber( int i );

    /*!
     * Creates a new number from a string.
     */
    HNumber( const char* );

    /*!
     * Returns true if this number is Not a Number (NaN).
     */
    bool isNan() const;

    /*!
     * Returns true if this number is zero.
     */
    bool isZero() const;

    /*!
     * Returns true if this number is more than zero.
     */
    bool isPositive() const;

    /*!
     * Returns true if this number is less than zero.
     */
    bool isNegative() const;

    /*!
     * Returns true if this number is integer.
     */
    bool isInteger() const;

    /*!
     * Returns the preferred format (base/precision), default is 0
     */
    char format() const;

    /*!
     * Sets the preferred format (base/precision), default is 0
     */
    void setFormat(char c = 0) const;

    /*!
     * Adds another number.
     */
    HNumber operator+( const HNumber& ) const;

    /*!
     * Adds another number.
     */
    HNumber& operator+=( const HNumber& );

    /*!
     * Subtract from another number.
     */
    HNumber operator-( const HNumber& ) const;

    /*!
     * Subtract from another number.
     */
    HNumber& operator-=( const HNumber& );

    /*!
     * Multiplies with another number.
     */
    HNumber operator*( const HNumber& ) const;

    /*!
     * Multiplies with another number.
     */
    HNumber& operator*=( const HNumber& );

    /*!
     * Divides with another number.
     */
    HNumber operator/( const HNumber& ) const;

    /*!
     * Divides with another number.
     */
    HNumber& operator/=( const HNumber& );

    /*!
     * Modulo (rest of integer division)
     */
    HNumber operator%( const HNumber& ) const;

    /*!
     * Returns true if this number is greater than n.
     */
    bool operator>( const HNumber& n ) const;

    /*!
     * Returns true if this number is less than n.
     */
    bool operator<( const HNumber& n ) const;

    /*!
     * Returns true if this number is greater than or equal to n.
     */
    bool operator>=( const HNumber& n ) const;

    /*!
     * Returns true if this number is less than or equal to n.
     */
    bool operator<=( const HNumber& n ) const;

    /*!
     * Returns true if this number is equal to n.
     */
    bool operator==( const HNumber& n ) const;

    /*!
     * Returns true if this number is not equal to n.
     */
    bool operator!=( const HNumber& n ) const;

    /*!
     * Returns a NaN (Not a Number).
     */
    static HNumber nan();

    /*!
     * Returns the number as an int.
     * It is meant to convert small (integer) numbers only and no
     * checking is done whatsoever.
     */
    int toInt();

private:
    HNumberPrivate* d;
};

class HMath
{
public:
    /*!
     * Formats the given number as string, using specified decimal digits.
     * Note that the returned string must be freed.
     */
    static char* format( const HNumber&n, char format = 'g', int prec = -1 );

    /*!
     * Formats the given number as string, in engineering notation.
     * Note that the returned string must be freed.
     */
    static char* formatEngineering( const HNumber&n, int prec = -1 );

    /*!
     * Formats the given number as string, using specified decimal digits.
     * Note that the returned string must be freed.
     */
    static char* formatFixed( const HNumber&n, int prec = -1 );

    /*!
     * Formats the given number as string, in scientific format.
     * Note that the returned string must be freed.
     */
    static char* formatScientific( const HNumber&n, int prec = -1 );

    /*!
     * Formats the given number as string, using specified decimal digits.
     * Note that the returned string must be freed.
     */
    static char* formatGeneral( const HNumber&n, int prec = -1 );

    /*!
     * Formats the given number as string, using hexadecimal digits. (NaN if hn isn't integer)
     * Note that the returned string must be freed.
     */
    static char* formatHexadec( const HNumber&n );

    /*!
     * Formats the given number as string, using octal digits. (NaN if hn isn't integer)
     * Note that the returned string must be freed.
     */
    static char* formatOctal( const HNumber&n );

    /*!
     * Formats the given number as string, using binary digits. (NaN if hn isn't integer)
     * Note that the returned string must be freed.
     */
    static char* formatBinary( const HNumber&n );

    /*!
     * Returns the constant phi (golden number).
     */
    static HNumber phi();

    /*!
     * Returns the constant pi.
     */
    static HNumber pi();

    /*!
     * Adds two numbers.
     */
    static HNumber add( const HNumber& n1, const HNumber& n2 );

    /*!
     * Subtracts two numbers.
     */
    static HNumber sub( const HNumber& n1, const HNumber& n2 );

    /*!
     * Multiplies two numbers.
     */
    static HNumber mul( const HNumber& n1, const HNumber& n2 );

    /*!
     * Divides two numbers.
     */
    static HNumber div( const HNumber& n1, const HNumber& n2 );

    /*!
     * Returns -1, 0, 1 if n1 is less than, equal to, or more than n2.
     */
    static int compare( const HNumber& n1, const HNumber& n2 );

    /*!
     * Returns the maximum of two numbers.
     */
    static HNumber max( const HNumber& n1, const HNumber& n2 );

    /*!
     * Returns the minimum of two numbers.
     */
    static HNumber min( const HNumber& n1, const HNumber& n2 );

    /*!
     * Returns the absolute value of n.
     */
    static HNumber abs( const HNumber& n );

    /*!
     * Returns the negative of n.
     */
    static HNumber negate( const HNumber& n );

    /*!
     * Returns the integer part of n.
     */
    static HNumber integer( const HNumber& n );

    /*!
     * Returns the fraction part of n.
     */
    static HNumber frac( const HNumber& n );

    /*!
     * Returns the floor of n.
     */
    static HNumber floor( const HNumber& n );

    /*!
     * Returns the ceiling of n.
     */
    static HNumber ceil( const HNumber& n );

    /*!
     * Returns the greatest common divisor of n1 and n2.
     */
    static HNumber gcd( const HNumber& n1, const HNumber& n2 );

    /*!
     * Rounds n to the specified decimal digits.
     */
    static HNumber round( const HNumber&n, int prec = 0 );

    /*!
     * Truncates n to the specified decimal digits.
     */
    static HNumber trunc( const HNumber&n, int prec = 0 );

    /*!
     * Returns the square root of n. If n is negative, returns NaN.
     */
    static HNumber sqrt( const HNumber& n );

    /*!
     * Returns the cube root of n.
     */
    static HNumber cbrt( const HNumber& n );

    /*!
     * Raises n1 to an integer n.
     */
    static HNumber raise( const HNumber& n1, int n );

    /*!
     * Raises n1 to n2.
     */
    static HNumber raise( const HNumber& n1, const HNumber& n2 );

    /*!
     * Returns e raised to x.
     */
    static HNumber exp( const HNumber& x );

    /*!
     * Returns the natural logarithm of x.
     * If x is non positive, returns NaN.
     */
    static HNumber ln( const HNumber& x );

    /*!
     * Returns the base-10 logarithm of x.
     * If x is non positive, returns NaN.
     */
    static HNumber log( const HNumber& x );

    /*!
     * Returns the base-2 logarithm of x.
     * If x is non positive, returns NaN.
     */
    static HNumber lg( const HNumber& x );

    /*!
     * Returns the sine of x. Note that x must be in radians.
     */
    static HNumber sin( const HNumber& x );

    /*!
     * Returns the cosine of x. Note that x must be in radians.
     */
    static HNumber cos( const HNumber& x );

    /*!
     * Returns the tangent of x. Note that x must be in radians.
     */
    static HNumber tan( const HNumber& x );

    /*!
     * Returns the cotangent of x. Note that x must be in radians.
     */
    static HNumber cot( const HNumber& x );

    /*!
     * Returns the secant of x. Note that x must be in radians.
     */
    static HNumber sec( const HNumber& x );

    /*!
     * Returns the cosecant of x. Note that x must be in radians.
     */
    static HNumber csc( const HNumber& x );

    /*!
     * Returns the arc sine of x.
     */
    static HNumber asin( const HNumber& x );

    /*!
     * Returns the arc cosine of x.
     */
    static HNumber acos( const HNumber& x );

    /*!
     * Returns the arc tangent of x.
     */
    static HNumber atan( const HNumber& x );

    /*!
     * Returns the hyperbolic sine of x. Note that x must be in radians.
     */
    static HNumber sinh( const HNumber& x );

    /*!
     * Returns the hyperbolic cosine of x. Note that x must be in radians.
     */
    static HNumber cosh( const HNumber& x );

    /*!
     * Returns the hyperbolic tangent of x. Note that x must be in radians.
     */
    static HNumber tanh( const HNumber& x );

    /*!
     * Returns the sign of x.
     */
    static HNumber sign( const HNumber& x );

    /*!
     * Returns the combinations of n elements choosen k elements.
     */
    static HNumber nCr( const HNumber& n, const HNumber& r );

    /*!
     * Returns the permutations of n elements chosen r elements.
     */
    static HNumber nPr( const HNumber& n, const HNumber& r );

    /*!
     * Returns the factorial of x.
     * If x is non integer, returns NaN.
     */
    static HNumber factorial( const HNumber& x, const HNumber& base = HNumber(1) );

    /*
      PROBABILITY
    */
    /**
     * Calculates the binomial discrete distribution probability mass function:
     * \f[X{\sim}B(n,p)\f]
     * \f[\Pr(X=k|n,p)={n\choose k}p^{k}(1-p)^{n-k}\f]
     *
     * \param[in] k the number of probed exact successes
     * \param[in] n the number of trials
     * \param[in] p the probability of success in a single trial
     *
     * \return the probability of exactly \p k successes, otherwise \p NaN if the
     * function is not defined for the specified parameters.
     */
    static HNumber binomialPmf( const HNumber & k,
                                const HNumber & n,
                                const HNumber & p );

    /**
     * Calculates the binomial cumulative distribution function:
     * \f[X{\sim}B(n,p)\f]
     * \f[\Pr(X \leq k|n,p)=\sum_{i=0}^{k}{n\choose i}p^{i}(1-p)^{n-i}\f]
     *
     * \param[in] k the maximum number of probed successes
     * \param[in] n the number of trials
     * \param[in] p the probability of success in a single trial
     *
     * \return the probability of up to \p k successes, otherwise \p NaN if the
     * function is not defined for the specified parameters.
     */
    static HNumber binomialCdf( const HNumber & k,
                                const HNumber & n,
                                const HNumber & p );

    /**
     * Calculates the expected value of a binomially distributed random variable:
     * \f[X{\sim}B(n,p)\f]
     * \f[E(X)=np\f]
     *
     * \param[in] n the number of trials
     * \param[in] p the probability of success in a single trial
     *
     * \return the expected value of the variable, otherwise \p NaN if the
     * function is not defined for the specified parameters.
     */
    static HNumber binomialMean( const HNumber & n,
                                 const HNumber & p );

    /**
     * Calculates the variance of a binomially distributed random variable:
     * \f[X{\sim}B(n,p)\f]
     * \f[Var(X)=np(1-p)\f]
     *
     * \param[in] n the number of trials
     * \param[in] p the probability of success in a single trial
     *
     * \return the variance of the variable, otherwise \p NaN if the
     * function is not defined for the specified parameters.
     */
    static HNumber binomialVariance( const HNumber & n,
                                     const HNumber & p );

    /**
     * Calculates the hypergeometric discrete distribution probability mass
     * function:
     * \f[X{\sim}H(N,M,n)\f]
     * \f[\Pr(X=k|N,M,n)=\frac{{M\choose k}{N-M\choose n-k}}{{N\choose n}}\f]
     *
     * \param[in] k the number of probed exact successes
     * \param[in] N the number of total elements
     * \param[in] M the number of success elements
     * \param[in] n the number of selected elements
     *
     * \return the probability of exactly \p k successes, otherwise \p NaN if the
     * function is not defined for the specified parameters.
     */
    static HNumber hypergeometricPmf( const HNumber & k,
                                      const HNumber & N,
                                      const HNumber & M,
                                      const HNumber & n );

    /**
     * Calculates the hypergeometric cumulative distribution function:
     * \f[X{\sim}H(N,M,n)\f]
     * \f[\Pr(X\leq k|N,M,n)=
     *   \sum_{i=0}^{k}\frac{{M\choose k}{N-M\choose n-k}}{{N\choose n}}\f]
     *
     * \param[in] k the maximum number of probed successes
     * \param[in] N the number of total elements
     * \param[in] M the number of success elements
     * \param[in] n the number of selected elements
     *
     * \return the probability of up to \p k successes, otherwise \p NaN if the
     * function is not defined for the specified parameters.
     */
    static HNumber hypergeometricCdf( const HNumber & k,
                                      const HNumber & N,
                                      const HNumber & M,
                                      const HNumber & n );

    /**
     * Calculates the expected value of a hypergeometrically distributed random
     * variable:
     * \f[X{\sim}H(N,M,n)\f]
     * \f[E(X)=n\frac{M}{N}\f]
     *
     * \param[in] N the number of total elements
     * \param[in] M the number of success elements
     * \param[in] n the number of selected elements
     *
     * \return the expected value of the variable, otherwise \p NaN if the
     * function is not defined for the specified parameter.
     */
    static HNumber hypergeometricMean( const HNumber & N,
                                       const HNumber & M,
                                       const HNumber & n );

    /**
     * Calculates the variance of a hypergeometrically distributed random variable:
     * \f[X{\sim}H(N,M,n)\f]
     * \f[Var(X)=\frac{n\frac{M}{N}(1-\frac{M}{N})(N-n)}{N-1}\f]
     *
     * \param[in] N the number of total elements
     * \param[in] M the number of success elements
     * \param[in] n the number of selected elements
     *
     * \return the variance of the variable, otherwise \p NaN if the function is
     * not defined for the specified parameter.
     */
    static HNumber hypergeometricVariance( const HNumber & N,
                                           const HNumber & M,
                                           const HNumber & n );

    /**
     * Calculates the poissonian discrete distribution probability mass function:
     * \f[X{\sim}P(\lambda)\f]
     * \f[\Pr(X=k|\lambda)=\frac{e^{-\lambda}\lambda^k}{k!}\f]
     *
     * \param[in] k the number of event occurrences
     * \param[in] l the expected number of occurrences that occur in an interval
     *
     * \return the probability of exactly \p k event occurrences, otherwise \p NaN
     * if the function is not defined for the specified parameters.
     */
    static HNumber poissonPmf( const HNumber & k,
                               const HNumber & l );

    /**
     * Calculates the poissonian cumulative distribution function:
     * \f[X{\sim}P(\lambda)\f]
     * \f[\Pr(X\leq k|\lambda)=\sum_{i=0}^{k}\frac{e^{-\lambda}\lambda^k}{k!}\f]
     *
     * \param[in] k the maximum number of event occurrences
     * \param[in] l the expected number of occurrences that occur in an interval
     *
     * \return the probability of up to \p k event occurrences, otherwise \p NaN
     * if the function is not defined for the specified parameters.
     */
    static HNumber poissonCdf( const HNumber & k,
                               const HNumber & l );

    /**
     * Calculates the expected value of a Poisson distributed random variable:
     * \f[X{\sim}P(\lambda)\f]
     * \f[E(X)=\lambda\f]
     *
     * \param[in] l the expected number of occurrences that occur in an interval
     *
     * \return the expected value of the variable, otherwise \p NaN if the
     * function is not defined for the specified parameter.
     */
    static HNumber poissonMean( const HNumber & l );

    /**
     * Calculates the variance of a Poisson distributed random variable:
     * \f[X{\sim}P(\lambda)\f]
     * \f[Var(X)=\lambda\f]
     *
     * \param[in] l the expected number of occurrences that occur in an interval
     *
     * \return the variance of the variable, otherwise \p NaN if the function is
     * not defined for the specified parameter.
     */
    static HNumber poissonVariance( const HNumber & l );

    /*!
     * Releases all resources. After calling this function, you can not use
     * any other functions as well as class HNumber.
     */
    static void finalize();
};

std::ostream& operator<<( std::ostream& s, const HNumber& n );

#endif // HMATH_H
