/* -*- mode: c++; indent-tabs-mode: nil -*- */
/*
    ql_math.qpp

    Qore Programming Language

    Copyright (C) 2003 - 2023 Qore Technologies, s.r.o.

    Permission is hereby granted, free of charge, to any person obtaining a
    copy of this software and associated documentation files (the "Software"),
    to deal in the Software without restriction, including without limitation
    the rights to use, copy, modify, merge, publish, distribute, sublicense,
    and/or sell copies of the Software, and to permit persons to whom the
    Software is furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in
    all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    DEALINGS IN THE SOFTWARE.

    Note that the Qore library is released under a choice of three open-source
    licenses: MIT (as above), LGPL 2+, or GPL 2+; see README-LICENSE for more
    information.
*/

#include <qore/Qore.h>
#include "qore/intern/ql_math.h"
#include "qore/intern/qore_number_private.h"

#include <climits>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <string>

using namespace std;

static double minus_infinity() {
#ifdef INFINITY
   return -INFINITY;
#else
   // returns -infinity
   return log10((double)0.0);
#endif
}

/** @defgroup math_constants Math Constants
    Math constants
 */
///@{
//! PI (floating-point)
const M_PI = 3.14159265358979323846;

//! PI (arbitrary-precision numeric)
/** @since %Qore 0.8.6
 */
const M_PIn = qore(pi_number());

//! largest integer
/** @since %Qore 0.8.6
 */
const MAXINT = LLONG_MAX;

//! smallest integer
/** @since %Qore 0.8.6
 */
const MININT = qore(QoreValue(-LLONG_MAX - 1));
///@}

/** @defgroup math_functions Math Functions
    Math functions
*/
///@{

//! Returns the multiple of <b>-prec</b>-th power of 10 that is closest to <b>num</b> (numbers halfway between two such powers of 10 are rounded away from zero). (So, for non-negative <b>prec</b> just returns <b>num</b>).
/** @param num an integer number to round
  * @param prec precision
  *
  * @par Example:
  * @code{.py}
  *     round(-6450, -2);  // returns  -6500
  *     round( 6450, -3);  // returns   6000
  *     round(-6450, -4);  // returns -10000
  *     round( 6450, -5);  // returns      0
  * @endcode
  *
  * @since %Qore 0.8.13 this function accepts the <b>prec</b> argument to specify the rounding precision
  */
int round(softint num, int prec = 0) [flags=CONSTANT] {
    return round_ceil_floor_intern(num, prec, ROUND);
}

//! Returns the multiple of <b>-prec</b>-th power of 10 that is closest to <b>num</b> (numbers halfway between two such powers of 10 are rounded away from zero).
/** @par Platform Availability
  * @ref Qore::Option::HAVE_ROUND
  * @param num a number to round
  * @param prec precision
  *
  * @par Example:
  * @code{.py}
  *     round( 6450.36481,  3);  // returns  6450.365
  *     round(-6450.36481,  2);  // returns -6450.36
  *     round( 6450.36481, -2);  // returns  6500.0
  *     round(-6450.36481, -3);  // returns -6000.0
  *     round( 6450.36481, -4);  // returns 10000.0
  *     round(-6450.36481, -5);  // returns     0.0
  * @endcode
  *
  * @since %Qore 0.8.13 this function accepts the <b>prec</b> argument to specify the rounding precision
  */
float round(softfloat num, int prec = 0) [flags=CONSTANT] {
#ifdef HAVE_ROUND
    if (prec > 0) {
        int c = pow(10, prec);
        return round(c*num)/c;
    }
    if (prec < 0) {
        int c = pow(10, -prec);
        return c*round(num/c);
    }
    return round(num);
#else
    return missing_function_error("round", "ROUND", xsink);
#endif
}

//! Returns the multiple of <b>-prec</b>-th power of 10 that is closest to <b>num</b> (numbers halfway between two such powers of 10 are rounded away from zero).
/** @param num a number to round
  * @param prec precision
  *
  * @par Example:
  * @code{.py}
  *     round( 645084.36481772n,  5);  // returns  645084.36482n
  *     round(-645084.36481772n,  3);  // returns -645084.365n
  *     round( 645084.36481772n, -4);  // returns  650000n
  *     round(-645084.36481772n, -5);  // returns -600000n
  *     round( 645084.36481772n, -6);  // returns 1000000n
  *     round(-645084.36481772n, -7);  // returns       0n
  * @endcode
  *
  * @since %Qore 0.8.13 this function accepts the <b>prec</b> argument to specify the rounding precision
  */
number round(number num, int prec = 0) [flags=CONSTANT] {
    return qore_number_private::doRoundNR(*num, mpfr_round, prec);
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
float round() [flags=RUNTIME_NOOP] {
   return 0;
}

//! Returns the smallest multiple of <b>-prec</b>-th power of 10 that is greater than or equal to <b>num</b>. (Thus, for non-negative <b>prec</b> just returns <b>num</b>).
/** @param num an integer number to process (to round upwards)
  * @param prec precision
  *
  * @par Example:
  * @code{.py}
  *     ceil( 64501, -2);  // returns 64600
  *     ceil(-64999, -3);  // returns -64000
  *     ceil( 34500, -8);  // returns 100000000
  *     ceil(-34500, -8);  // returns 0
  * @endcode
  *
  * @see floor(softint num, int prec = 0)
  *
  * @since %Qore 0.8.13 this function accepts the <b>prec</b> argument to specify the rounding precision
  */
int ceil(int num, int prec = 0) [flags=CONSTANT] {
    return round_ceil_floor_intern(num, prec, CEIL);
}

//! Returns the smallest multiple of <b>-prec</b>-th power of 10 that is greater than or equal to <b>num</b>.
/** @param num the number to process (to round upwards)
  * @param prec precision
  *
  * @par Example:
  * @code{.py}
  *     ceil(   357.271, 2);  // returns 357.28
  *     ceil(  -357.271, 1);  // returns -357.2
  *     ceil(-64599.28, -2);  // returns -64500.0
  *     ceil( 14500.28, -4);  // returns 20000.0
  *     ceil( 34500.28, -7);  // returns 10000000.0
  *     ceil(-34500.28, -7);  // returns 0.0
  * @endcode
  *
  * @see floor(softfloat num, int prec = 0)
  *
  * @since %Qore 0.8.13 this function accepts the <b>prec</b> argument to specify the rounding precision
  */
float ceil(softfloat num, int prec = 0) [flags=CONSTANT] {
    if (prec > 0) {
        int c = pow(10, prec);
        return ceil(c*num)/c;
    }
    if (prec < 0) {
        int c = pow(10, -prec);
        return c*ceil(num/c);
    }
    return ceil(num);
}

//! Returns the smallest multiple of <b>-prec</b>-th power of 10 that is greater than or equal to <b>num</b>.
/** @param num the number to process (to round upwards)
  * @param prec precision
  *
  * @par Example:
  * @code{.py}
  *     ceil(   357.271n, 2);  // returns 357.28n
  *     ceil(  -357.271n, 1);  // returns -357.2n
  *     ceil(-64599.28n, -2);  // returns -64500n
  *     ceil( 14500n,    -4);  // returns 20000n
  *     ceil( 34500.28n, -7);  // returns 10000000n
  *     ceil(-34500.28n, -7);  // returns 0n
  * @endcode
  *
  * @see floor(number num, int prec = 0)
  *
  * @since %Qore 0.8.13 this function accepts the <b>prec</b> argument to specify the rounding precision
  */
number ceil(number num, int prec = 0) [flags=CONSTANT] {
    return qore_number_private::doRoundNR(*num, mpfr_ceil, prec);
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
float ceil() [flags=RUNTIME_NOOP] {
   return 0;
}

//! Returns the largest multiple of <b>-prec</b>-th power of 10 that is less than or equal to <b>num</b>. (Thus, for non-negative <b>prec</b> just returns <b>num</b>).
/** @param num an integer number to process (to round downwards)
  * @param prec precision
  *
  * @par Example:
  * @code{.py}
  *     floor(-64501, -2);  // returns -64600
  *     floor( 64999, -3);  // returns 64000
  *     floor(-34500, -8);  // returns -100000000
  *     floor( 34500, -8);  // returns 0
  * @endcode
  *
  * @see ceil(softint num, int prec = 0)
  *
  * @since %Qore 0.8.13 this function accepts the <b>prec</b> argument to specify the rounding precision
  */
int floor(int num, int prec = 0) [flags=CONSTANT] {
    return round_ceil_floor_intern(num, prec, FLOOR);
}

//! Returns the largest multiple of <b>-prec</b>-th power of 10 that is less than or equal to <b>num</b>.
/** @param f the number to process (to round downwards)
  * @param prec precision
  *
  * @par Example:
  * @code{.py}
  *     floor(   357.271, 2);  // returns 357.27
  *     floor(  -357.271, 1);  // returns -357.3
  *     floor( 64599.28, -2);  // returns 64500.0
  *     floor( 17500.28, -4);  // returns 10000.0
  *     floor( 34500.28, -7);  // returns 0.0
  *     floor(-34500.28, -7);  // returns -10000000.0
  * @endcode
  *
  * @see ceil(softfloat num, int prec = 0)
  *
  * @since %Qore 0.8.13 this function accepts the <b>prec</b> argument to specify the rounding precision
  */
float floor(softfloat f, int prec = 0) [flags=CONSTANT] {
    if (prec > 0) {
        int c = pow(10, prec);
        return floor(c*f)/c;
    }
    if (prec < 0) {
        int c = pow(10, -prec);
        return c*floor(f/c);
    }
    return floor(f);
}

//! Returns the largest multiple of <b>-prec</b>-th power of 10 that is less than or equal to <b>num</b>.
/** @param num the number to process (to round downwards)
  * @param prec precision
  *
  * @par Example:
  * @code{.py}
  *     floor(   357.271n, 2);  // returns 357.27n
  *     floor(  -357.271n, 1);  // returns -357.3n
  *     floor( 64599.28n, -2);  // returns 64500n
  *     floor( 17500.28n, -4);  // returns 10000n
  *     floor( 34500.28n, -7);  // returns 0n
  *     floor(-34500.28n, -7);  // returns -10000000n
  * @endcode
  *
  * @see ceil(number num, int prec = 0)
  *
  * @since %Qore 0.8.13 this function accepts the <b>prec</b> argument to specify the rounding precision
  */
number floor(number num, int prec = 0) [flags=CONSTANT] {
    return qore_number_private::doRoundNR(*num, mpfr_floor, prec);
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
float floor() [flags=RUNTIME_NOOP] {
   return 0;
}

//! Returns a number raised to the power of another number
/**
    @param x the number to raise to the power of \a y
    @param y the power to raise \a x to

    @return a number raised to the power of another number

    @par Example:
    @code{.py}
number z = pow(x, y);
    @endcode

    @throw DIVISION-BY-ZERO in pow(x, y), if x = 0 then y must be a non-negative value
 */
number pow(number x, number y) [flags=RET_VALUE_ONLY] {
    return qore_number_private::doBinary(*x, mpfr_pow, *y, xsink);
}

//! Returns a number raised to the power of another number
/**
    @param x the number to raise to the power of \a y
    @param y the power to raise \a x to

    @return a number raised to the power of another number

    @par Example:
    @code{.py}
float z = pow(x, y);
    @endcode

    @throw DIVISION-BY-ZERO in pow(x, y), if x = 0 then y must be a non-negative value
    @throw INVALID-POW-ARGUMENTS in pow(x, y), x cannot be negative when y is not an integer value
 */
float pow(softfloat x = 0.0, softfloat y = 0.0) [flags=RET_VALUE_ONLY] {
    if (x == 0.0 && y < 0)
        return xsink->raiseException("DIVISION-BY-ZERO", "pow(x, y) if x = 0 then y must be a non-negative value");

    if (x < 0 && y != ceil(y))
        return xsink->raiseException("INVALID-POW-ARGUMENTS", "pow(x, y) x cannot be negative when y is not an integer value");

    return pow(x, y);
}

//! Returns the absolute value of the argument passed
/**
    @param i the value to process

    @return the absolute value of the argument passed

    @par Example:
    @code{.py}
int i = abs(-20); # returns 20
    @endcode

    @note equivalent to <int>::abs()
 */
int abs(int i) [flags=CONSTANT] {
    return i < 0 ? -i : i;
}

//! Returns the absolute value of the argument passed
/**
    @param n the value to process

    @return the absolute value of the argument passed

    @par Example:
    @code{.py}
number n = abs(-20n); # returns 20n
    @endcode

    @note equivalent to <number>::abs()
 */
number abs(number n) [flags=CONSTANT] {
    return qore_number_private::doUnary(*n, mpfr_abs);
}

//! Returns the absolute value of the argument passed
/**
    @param f the value to process

    @return the absolute value of the argument passed

    @par Example:
    @code{.py}
float f = abs(-20.2); # returns 20.2
    @endcode

    @note equivalent to <float>::abs()
 */
float abs(softfloat f) [flags=CONSTANT] {
    return fabs(f);
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
float abs() [flags=RUNTIME_NOOP] {
    return 0;
}

//! Returns the length of the hypotenuse of a right-angle triangle with sides given as the two arguments
/**
    @param x the length of side x of the right-angle triangle
    @param y the length of side y of the right-angle triangle

    @return the length of the hypotenuse of a right-angle triangle with sides given as the two arguments

    @par Example:
    @code{.py}
number z = hypot(x, y);
    @endcode
 */
number hypot(number x, number y) [flags=CONSTANT] {
    return qore_number_private::doBinary(*x, mpfr_hypot, *y);
}

//! Returns the length of the hypotenuse of a right-angle triangle with sides given as the two arguments
/**
    @param x the length of side x of the right-angle triangle
    @param y the length of side y of the right-angle triangle

    @return the length of the hypotenuse of a right-angle triangle with sides given as the two arguments

    @par Example:
    @code{.py}
float z = hypot(x, y);
    @endcode
 */
float hypot(softfloat x, softfloat y) [flags=CONSTANT] {
    return hypot(x, y);
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
float hypot() [flags=RUNTIME_NOOP] {
    return 0;
}

//! Returns the square root of the number passed
/**
    @param n the value to process

    @return the square root of the number passed

    @par Example:
    @code{.py}
number x = sqrt(y);
    @endcode
 */
number sqrt(number n) [flags=CONSTANT] {
    return qore_number_private::doUnary(*n, mpfr_sqrt);
}

//! Returns the square root of the number passed
/**
    @param f the value to process

    @return the square root of the number passed

    @par Example:
    @code{.py}
float x = sqrt(y);
    @endcode
 */
float sqrt(softfloat f) [flags=CONSTANT] {
    return sqrt(f);
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
float sqrt() [flags=RUNTIME_NOOP] {
   return 0;
}

//! Returns the cube root of the number passed
/**
    @param n the value to process

    @return the cube root of the number passed

    @par Example:
    @code{.py}
number x = cbrt(y);
    @endcode
 */
number cbrt(number n) [flags=CONSTANT] {
    return qore_number_private::doUnary(*n, mpfr_cbrt);
}

//! Returns the cube root of the number passed
/**
    @param f the value to process

    @return the cube root of the number passed

    @par Example:
    @code{.py}
float x = cbrt(y);
    @endcode
 */
float cbrt(softfloat f) [flags=CONSTANT] {
    return cbrt(f);
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
float cbrt() [flags=RUNTIME_NOOP] {
    return 0;
}

//! Returns the sine of the number in radians passed
/**
    @param n the angle in radians

    @return the sine of the number in radians passed

    @par Example:
    @code{.py}
number x = sin(y);
    @endcode
 */
number sin(number n) [flags=CONSTANT] {
    return qore_number_private::doUnary(*n, mpfr_sin);
}

//! Returns the sine of the number in radians passed
/**
    @param f the angle in radians

    @return the sine of the number in radians passed

    @par Example:
    @code{.py}
float x = sin(y);
    @endcode
 */
float sin(softfloat f) [flags=CONSTANT] {
    return sin(f);
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
float sin() [flags=RUNTIME_NOOP] {
    return 0;
}

//! Returns the cosine of the number in radians passed
/**
    @param n the angle in radians

    @return the cosine of the number in radians passed

    @par Example:
    @code{.py}
number x = cos(y);
    @endcode
 */
number cos(number n) [flags=CONSTANT] {
    return qore_number_private::doUnary(*n, mpfr_cos);
}

//! Returns the cosine of the number in radians passed
/**
    @param f the angle in radians

    @return the cosine of the number in radians passed

    @par Example:
    @code{.py}
float x = cos(y);
    @endcode
 */
float cos(float f) [flags=CONSTANT] {
    return cos(f);
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
float cos() [flags=RUNTIME_NOOP] {
    return 1;
}

//! Returns the tangent of the number in radians passed
/**
    @param n the angle in radians

    @return the tangent of the number in radians passed

    @par Example:
    @code{.py}
number x = tan(y);
    @endcode
 */
number tan(number n) [flags=CONSTANT] {
    return qore_number_private::doUnary(*n, mpfr_tan);
}

//! Returns the tangent of the number in radians passed
/**
    @param f the angle in radians

    @return the tangent of the number in radians passed

    @par Example:
    @code{.py}
float x = tan(y);
    @endcode
 */
float tan(softfloat f) [flags=CONSTANT] {
    return tan(f);
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
float tan() [flags=RUNTIME_NOOP] {
    return 0;
}

//! Returns the value in radians of the arc sine of the given value
/**
    @param n the sine value to process

    @return the value in radians of the arc sine of the given value

    @par Example:
    @code{.py}
number x = asin(y);
    @endcode
 */
number asin(number n) [flags=CONSTANT] {
   return qore_number_private::doUnary(*n, mpfr_asin);
}

//! Returns the value in radians of the arc sine of the given value
/**
    @param f the sine value to process

    @return the value in radians of the arc sine of the given value

    @par Example:
    @code{.py}
float x = asin(y);
    @endcode
 */
float asin(softfloat f) [flags=CONSTANT] {
   return asin(f);
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
float asin() [flags=RUNTIME_NOOP] {
   return 0;
}

//! Returns the value in radians of the arc cosine of the given value
/**
    @param n the cosine value to process

    @return the value in radians of the arc cosine of the given value

    @par Example:
    @code{.py}
number x = acos(y);
    @endcode
 */
number acos(number n) [flags=CONSTANT] {
   return qore_number_private::doUnary(*n, mpfr_acos);
}

//! Returns the value in radians of the arc cosine of the given value
/**
    @param f the cosine value to process

    @return the value in radians of the arc cosine of the given value

    @par Example:
    @code{.py}
float x = acos(y);
    @endcode
 */
float acos(softfloat f) [flags=CONSTANT] {
   return acos(f);
}

//! Returns the value in radians of the arc tangent of the given value
/**
    @param n the tangent value to process

    @return the value in radians of the arc tangent of the given value

    @par Example:
    @code{.py}
number x = atan(y);
    @endcode
 */
number atan(number n) [flags=CONSTANT] {
   return qore_number_private::doUnary(*n, mpfr_atan);
}

//! Returns the value in radians of the arc tangent of the given value
/**
    @param f the tangent value to process

    @return the value in radians of the arc tangent of the given value

    @par Example:
    @code{.py}
float x = atan(y);
    @endcode
 */
float atan(softfloat f) [flags=CONSTANT] {
   return atan(f);
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
float atan() [flags=RUNTIME_NOOP] {
   return 0;
}

//! Returns the principal value of the arc tangent of y/x, using the signs of the two arguments to determine the quadrant of the result
/**
    @param y the y value for the function
    @param x the x value for the function

    @return the principal value of the arc tangent of y/x, using the signs of the two arguments to determine the quadrant of the result

    @par Example:
    @code{.py}
number f = atan2(y, x);
    @endcode
 */
number atan2(number y, number x) [flags=CONSTANT] {
   return qore_number_private::doBinary(*y, mpfr_atan2, *x);
}

//! Returns the principal value of the arc tangent of y/x, using the signs of the two arguments to determine the quadrant of the result
/**
    @param y the y value for the function
    @param x the x value for the function

    @return the principal value of the arc tangent of y/x, using the signs of the two arguments to determine the quadrant of the result

    @par Example:
    @code{.py}
float f = atan2(y, x);
    @endcode
 */
float atan2(softfloat y, softfloat x) [flags=CONSTANT] {
   return atan2(y, x);
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
float atan2() [flags=RUNTIME_NOOP] {
   return 0;
}

//! Returns the hyperbolic sine of the given value
/**
    @param n the value to process

    @return the hyperbolic sine of the given value

    @par Example:
    @code{.py}
number x = sinh(y);
    @endcode
 */
number sinh(number n) [flags=CONSTANT] {
   return qore_number_private::doUnary(*n, mpfr_sinh);
}

//! Returns the hyperbolic sine of the given value
/**
    @param f the value to process

    @return the hyperbolic sine of the given value

    @par Example:
    @code{.py}
float x = sinh(y);
    @endcode
 */
float sinh(softfloat f) [flags=CONSTANT] {
   return sinh(f);
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
float sinh() [flags=RUNTIME_NOOP] {
   return 0;
}

//! Returns the hyperbolic cosine of the given value
/**
    @param n the value to process

    @return the hyperbolic cosine of the given value

    @par Example:
    @code{.py}
number x = cosh(y);
    @endcode
 */
number cosh(number n) [flags=CONSTANT] {
   return qore_number_private::doUnary(*n, mpfr_cosh);
}

//! Returns the hyperbolic cosine of the given value
/**
    @param f the value to process

    @return the hyperbolic cosine of the given value

    @par Example:
    @code{.py}
float x = cosh(y);
    @endcode
 */
float cosh(softfloat f) [flags=CONSTANT] {
   return cosh(f);
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
float cosh() [flags=RUNTIME_NOOP] {
   return 1;
}

//! Returns the hyperbolic tangent of the given value
/**
    @param n the value to process

    @return the hyperbolic tangent of the given value

    @par Example:
    @code{.py}
number x = tanh(y);
    @endcode
 */
number tanh(number n) [flags=CONSTANT] {
   return qore_number_private::doUnary(*n, mpfr_tanh);
}

//! Returns the hyperbolic tangent of the given value
/**
    @param f the value to process

    @return the hyperbolic tangent of the given value

    @par Example:
    @code{.py}
float x = tanh(y);
    @endcode
 */
float tanh(softfloat f) [flags=CONSTANT] {
   return tanh(f);
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
float tanh() [flags=RUNTIME_NOOP] {
   return 0;
}

//! Returns the natural logarithm of the given value
/**
    @param n the value to process

    @return the natural logarithm of the given value

    @par Example:
    @code{.py}
float x = nlog(y);
    @endcode
 */
number nlog(number n) [flags=CONSTANT] {
   return qore_number_private::doUnary(*n, mpfr_log);
}

//! Returns the natural logarithm of the given value
/**
    @param f the value to process

    @return the natural logarithm of the given value

    @par Example:
    @code{.py}
float x = nlog(y);
    @endcode
 */
float nlog(softfloat f) [flags=CONSTANT] {
   return log(f);
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
float nlog() [flags=RUNTIME_NOOP] {
   return minus_infinity();
}

//! Returns the base 10 logarithm of the given number
/**
    @param n the value to process

    @return the base 10 logarithm of the given value

    @par Example:
    @code{.py}
number x = log10(y);
    @endcode
 */
number log10(number n) [flags=CONSTANT] {
   return qore_number_private::doUnary(*n, mpfr_log10);
}

//! Returns the base 10 logarithm of the given number
/**
    @param f the value to process

    @return the base 10 logarithm of the given value

    @par Example:
    @code{.py}
float x = log10(y);
    @endcode
 */
float log10(softfloat f) [flags=CONSTANT] {
   return log10(f);
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
float log10() [flags=RUNTIME_NOOP] {
   return minus_infinity();
}

//! Returns the natural logarithm of 1 + the given number
/**
    @param n the value to process

    @return the natural logarithm of 1 + the given number

    @par Example:
    @code{.py}
number x = log1p(y);
    @endcode
 */
number log1p(number n) [flags=CONSTANT] {
   return qore_number_private::doUnary(*n, mpfr_log1p);
}

//! Returns the natural logarithm of 1 + the given number
/**
    @param f the value to process

    @return the natural logarithm of 1 + the given number

    @par Example:
    @code{.py}
float x = log1p(y);
    @endcode
 */
float log1p(softfloat f) [flags=CONSTANT] {
   return log1p(f);
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
float log1p() [flags=RUNTIME_NOOP] {
   return 0;
}

//! Returns the exponent of the given number
/**
    @param f the value to process

    @return the exponent of the given number

    @par Example:
    @code{.py}
float x = logb(y);
    @endcode
 */
float logb(softfloat f) [flags=CONSTANT] {
   return logb(f);
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
float logb() [flags=RUNTIME_NOOP] {
   return minus_infinity();
}

//! Returns the value of \a e (the base of natural logarithms) raised to the power of the given number
/**
    @param n the value to process

    @return the value of \a e (the base of natural logarithms) raised to the power of the given number

    @par Example:
    @code{.py}
number x = exp(y);
    @endcode
 */
number exp(number n) [flags=CONSTANT] {
   return qore_number_private::doUnary(*n, mpfr_exp);
}

//! Returns the value of \a e (the base of natural logarithms) raised to the power of the given number
/**
    @param f the value to process

    @return the value of \a e (the base of natural logarithms) raised to the power of the given number

    @par Example:
    @code{.py}
float x = exp(y);
    @endcode
 */
float exp(softfloat f) [flags=CONSTANT] {
   return exp(f);
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
float exp() [flags=RUNTIME_NOOP] {
   return 1;
}

//! Returns the value of 2 raised to the power of the given number
/**
    @param n the value to process

    @return the value of 2 raised to the power of the given number

    @par Example:
    @code{.py}
number x = exp2(y);
    @endcode
 */
number exp2(number n) [flags=CONSTANT] {
   return qore_number_private::doUnary(*n, mpfr_exp2);
}

//! Returns the value of 2 raised to the power of the given number
/**
    @param f the value to process

    @return the value of 2 raised to the power of the given number

    @par Example:
    @code{.py}
float x = exp2(y);
    @endcode
 */
float exp2(softfloat f) [flags=CONSTANT] {
#ifdef HAVE_EXP2
   return exp2(f);
#else
   return pow(2, f);
#endif
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
float exp2() [flags=RUNTIME_NOOP] {
   return 1;
}

//! Returns the value of \a e (the base of natural logarithms) raised to the power of the given number - 1
/**
    @param n the value to process

    @return the value of \a e (the base of natural logarithms) raised to the power of the given number - 1

    @par Example:
    @code{.py}
number x = exp1m(y);
    @endcode
 */
number expm1(number n) [flags=CONSTANT] {
   return qore_number_private::doUnary(*n, mpfr_expm1);
}

//! Returns the value of \a e (the base of natural logarithms) raised to the power of the given number - 1
/**
    @param f the value to process

    @return the value of \a e (the base of natural logarithms) raised to the power of the given number - 1

    @par Example:
    @code{.py}
float x = exp1m(y);
    @endcode
 */
float expm1(softfloat f) [flags=CONSTANT] {
    return expm1(f);
}

//! This function variant does nothing at all; it is only included for backwards-compatibility with qore prior to version 0.8.0 for functions that would ignore type errors in arguments
/**
 */
float expm1() [flags=RUNTIME_NOOP] {
    return 0;
}

//! Compares numbers with a given epsilon
/** @param n1 the first number
    @param n2 the second number
    @param epsilon the maximum difference in the numbers under which they are considered equal

    @return -1 if \a n1 is less then \a n2, 0 if the difference between the numbers is less than \a epsilon, 1 if
    \a n1 is greater than \a n2

    @since %Qore 0.9.5
*/
int compare(number n1, number n2, number epsilon) [flags=CONSTANT] {
    SimpleRefHolder<QoreNumberNode> v(n1->doMinus(*n2));
    if (v->zero()) {
        return 0;
    }
    int rv = v->sign();
    SimpleRefHolder<QoreNumberNode> tmp(qore_number_private::doUnary(**v, mpfr_abs));
    if (tmp->lessThan(*epsilon)) {
        return 0;
    }
    return rv;
}

//! Compares numbers with a given epsilon
/** @param n1 the first number
    @param n2 the second number
    @param epsilon the maximum difference in the numbers under which they are considered equal

    @return -1 if \a n1 is less then \a n2, 0 if the difference between the numbers is less than \a epsilon, 1 if
    \a n1 is greater than \a n2

    @since %Qore 0.9.5
*/
int compare(float n1, float n2, float epsilon) [flags=CONSTANT] {
    double v = n1 - n2;
    if (v == 0.0) {
        return 0;
    }
    int rv = v < 0 ? -1 : 1;
    if (fabs(v) < epsilon) {
        return 0;
    }
    return rv;
}
///@}

typedef enum {ROUND, CEIL, FLOOR} FUN;
long long round_ceil_floor_intern(long long num, int prec, FUN fun) {
    if (prec >= 0)
        return num;

    string s = string(to_string(LLONG_MAX).length() + 4, '0') + to_string(num < 0 ? -num : num);
    int len = s.length();
    if (len < -prec)
        return 0;

    int pos = len + prec;
    unsigned long long n = stoll(s.substr(0, pos));

    switch (fun) {
        case ROUND: if (s[pos] - '0' > 4)                 n++; break;
        case CEIL:  if (num >= 0 && stoll(s.substr(pos))) n++; break;
        case FLOOR: if (num <  0 && stoll(s.substr(pos))) n++;
    }

    unsigned long long res = stoll(to_string(n) + string(-prec, '0'));
    return num < 0 ? -res : res;
}
