/* -*- mode: c++; indent-tabs-mode: nil -*- */
/*
  Pseudo_QC_Number.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/qore_number_private.h"

/** @defgroup number_formatting_constants Number Formatting Constants
*/
///@{
//! for the default format with a rounding heuristic to try to remove noise in insignificant digits from the binary to digital conversion of high-precision numbers
const NF_Default = QORE_NF_DEFAULT;

//! for the scientific format (exponential notation)
const NF_Scientific = QORE_NF_SCIENTIFIC;

//! for the raw format without the noise reduction heuristic in the @ref NF_Default format
/** note that this can be combined with binary or with @ref NF_Scientific as well to get an exponential output without rounding
    @par Example:
    @code{.py}
    string str = n.toString(NF_Scientific|NF_Raw);
    @endcode
 */
const NF_Raw = QORE_NF_RAW;
///@}

/** @defgroup number_string_formatting_constants Number String Formatting Constants
*/
///@{
//! This is a special constant to be used with string number formatting pseudo-methods that take an integer \a prec argument; this value indicates that all available digits should be included in the output string
/** @see the following pseudo-methods that accept this value in the \a prec argument:
    - @ref <number>::format(int, string, string)
    - @ref <float>::format(int, string, string)
    - @ref <int>::format(int, string, string)
 */
const SNF_AllDigits = QORE_NUM_ALL_DIGITS;
///@}

//! Methods in this pseudo-class can be executed on @ref number "arbitrary precision number values"
/**
 */
qclass <number> [arg=const QoreNumberNode* n = v.get<const QoreNumberNode>();vparent=<value>];

//! Returns Qore::NT_NUMBER
/**
    @return Qore::NT_NUMBER

    @par Example:
    @code{.py}
switch (f.typeCode()) {
    case NT_NUMBER:
        printf("%y: is a number\n", f);
        break;
}
    @endcode

    @since %Qore 0.8.6
*/
int <number>::typeCode() [flags=CONSTANT] {
   return NT_NUMBER;
}

//! Returns @ref True if the number is non-zero, @ref False if zero
/**
    @return @ref True if the number is non-zero, @ref False if zero

    @par Example:
    @code{.py}
bool b = f.val();
    @endcode

    @see
    - @ref perl-bool-eval "%perl-bool-eval"
    - @ref strict-bool-eval "%strict-bool-eval"

    @since %Qore 0.8.6
*/
bool <number>::val() [flags=CONSTANT] {
   return !n->zero();
}

//! Returns @ref True because number values can be converted to integers
/**
    @return @ref True because number values can be converted to integers

    @par Example:
    @code{.py}
if (n.intp())
    printf("%y: can be converted to an integer: %d\n", n, int(n));
    @endcode

    @since %Qore 0.8.6
*/
bool <number>::intp() [flags=CONSTANT] {
   return true;
}

//! Returns @ref True because number values can be converted to strings
/**
    @return @ref True because number values can be converted to strings

    @par Example:
    @code{.py}
if (n.strp())
    printf("%y: can be converted to a string: '%s'\n", n, string(n));
    @endcode

    @since %Qore 0.8.6
*/
bool <number>::strp() [flags=CONSTANT] {
   return true;
}

//! Returns @ref True if the number is NaN (not a number)
/**
    @return @ref True if the number is NaN (not a number)

    @par Example:
    @code{.py}
if (n.nanp())
    print("the operation resulted in NaN\n");
    @endcode

    @since %Qore 0.8.6
*/
bool <number>::nanp() [flags=CONSTANT] {
   return qore_number_private::get(*n)->nan();
}

//! Returns @ref True if the number is infinity (+ or -)
/**
    @return @ref True if the number is infinity (+ or -)

    @par Example:
    @code{.py}
if (n.infp())
    print("the operation resulted in infinity\n");
    @endcode

    @since %Qore 0.8.6
*/
bool <number>::infp() [flags=CONSTANT] {
   return qore_number_private::get(*n)->inf();
}

//! Returns -1 if the number is negative, 0 if it is zero, or 1 if it is positive
/** @return -1 if the number is negative, 0 if it is zero, or 1 if it is positive

    @par Example:
    @code{.py}
printf("sign: %d\n", n.sign());
    @endcode

    @since %Qore 0.8.6
*/
int <number>::sign() [flags=CONSTANT] {
   return qore_number_private::get(*n)->sign();
}

//! Returns the precision of the current number
/** @return the precision of the current number

    @par Example:
    @code{.py}
printf("precision: %d\n", n.prec());
    @endcode

    @since %Qore 0.8.6
*/
int <number>::prec() [flags=CONSTANT] {
   return (int64)qore_number_private::get(*n)->getPrec();
}

//! Returns the string representation of the number according to the format argument
/** @param fmt see @ref number_formatting_constants for possible values; note that this argument is interpreted as a bit field; if the format argument is not a valid format value (i.e. if none of the formatting bits are set in the argument) then @ref NF_Default is assumed instead

    @return the string representation of the number according to the format argument

    @since %Qore 0.8.6
*/
string <number>::toString(int fmt) [flags=CONSTANT] {
   QoreStringNode* rv = new QoreStringNode;
   n->toString(*rv, fmt);
   return rv;
}

//! Returns a formatted string of the number according to the format argument
/** @param fmt the format string has the following format:\n
    <em>\<thousands_separator\></em><tt>[</tt><em>\<decimal_separator\>[\<decimals\>]</em><tt>]</tt>\n
    where:\n
    - \a thousands_separator and \a decimal_separator are single characters defining the thousands and decimal separator characters respectively, and
    - \a decimals is one or more digits defining how may decimals should appear after the decimal point; a negative number means to round that number of digits before the decimal place

    @return a string of a formatted number according to a format string; if the format string does not follow the given format, then the number without formatting is returned

    @par Example:
    @code{.py}
float f = -48392093894.2349;
string fstr = f.format(".,3"); # returns "-48.392.093.894,235"
    @endcode

    @see
    - @ref <float>::format(string)
    - @ref <int>::format(string)
    - @ref <number>::format(int, string, string)
    - @ref <float>::format(int, string, string)
    - @ref <int>::format(int, string, string)

    @since %Qore 0.8.6
 */
string <number>::format(string fmt) [flags=RET_VALUE_ONLY] {
   SimpleRefHolder<QoreStringNode> rv(new QoreStringNode(fmt->getEncoding()));
   return qore_number_private::get(*n)->format(**rv, *fmt, xsink) ? 0 : rv.release();
}

//! Returns a formatted string of the number according to format arguments
/** @param prec the number of digits after the decimal point; a negative value means to round that number of digits before the decimal place, whereas the special constant @ref SNF_AllDigits means to include all possible digits after the decimal point
    @param decimal_sep a single character that defines the decimal separator; an empty string means no decimal separator
    @param thousands_sep a single character that defines the thousands and further separators every three digits before the decimal point; an empty string means no thousands separator

    @return a string representing the number according to the format arguments as described above

    @par Example:
    @code{.py}
float f = -48392093894.2349;
string fstr = f.format(3, ".", ","); # returns "-48.392.093.894,235"
    @endcode

    @see
    - @ref <float>::format(int, string, string)
    - @ref <int>::format(int, string, string)
    - @ref <number>::format(string)
    - @ref <float>::format(string)
    - @ref <int>::format(string)

    @since %Qore 0.8.13
 */
string <number>::format(int prec = 2, string decimal_sep = ".", string thousands_sep = "") [flags=RET_VALUE_ONLY] {
   SimpleRefHolder<QoreStringNode> rv(new QoreStringNode);
   return qore_number_private::get(*n)->format(**rv, prec, *decimal_sep, *thousands_sep, xsink) ? 0 : rv.release();
}

//! Returns the absolute value of the number
/** @par Example:
    @code{.py}
n = n.abs();
    @endcode

    @return the absolute value of the number

    @note equivalent to abs(number)

    @since %Qore 0.8.8
*/
number <number>::abs() [flags=CONSTANT] {
   return qore_number_private::doUnary(*n, mpfr_abs);
}

//! Converts the number to a different <b>base</b> (and returns it as a string).
/** @param base the base to convert the number to; this value must be 2 - 36 inclusive or an \c INVALID-BASE exception will be thrown

    @return the converted value as a string

    @par Example:
    @code{.py}
        (-276536798792387623n).toBase(29)); // returns "-MJ934KRG1O3R"
          276536798792387623n.toBase(2));   // returns "1111010110011101001100000011011011010110000000110000100111"
          (15.5n).toBase(20));              // returns "F.A"
         (-13.8n).toBase(20));              // returns "-D.G"
         (21.48n).toBase(15));              // returns "16.73"
    @endcode

    @throw INVALID-BASE the base is invalid; must be 2 - 36 inclusive

    @see \link <int>::toBase(int base) \endlink
    @see \link <float>::toBase(int base) \endlink

    @since %Qore 0.8.13
*/
string <number>::toBase(int base = 10) [flags=RET_VALUE_ONLY] {
    return qore_number_private::toBase(*n, base, xsink);
}
