root/ext/standard/math.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. PHP_FUNCTION
  2. PHP_FUNCTION
  3. PHP_FUNCTION
  4. PHP_FUNCTION
  5. PHP_FUNCTION
  6. PHP_FUNCTION
  7. PHP_FUNCTION
  8. PHP_FUNCTION
  9. PHP_FUNCTION
  10. PHP_FUNCTION
  11. PHP_FUNCTION
  12. PHP_FUNCTION
  13. PHP_FUNCTION
  14. PHP_FUNCTION
  15. PHP_FUNCTION
  16. PHP_FUNCTION
  17. PHP_FUNCTION
  18. PHP_FUNCTION
  19. PHP_FUNCTION
  20. PHP_FUNCTION
  21. PHP_FUNCTION
  22. PHP_FUNCTION
  23. PHP_FUNCTION
  24. PHP_FUNCTION
  25. PHP_FUNCTION
  26. PHP_FUNCTION
  27. PHP_FUNCTION
  28. PHP_FUNCTION
  29. PHP_FUNCTION
  30. PHP_FUNCTION
  31. PHP_FUNCTION
  32. _php_math_basetolong
  33. _php_math_basetozval
  34. _php_math_longtobase
  35. _php_math_zvaltobase
  36. PHP_FUNCTION
  37. PHP_FUNCTION
  38. PHP_FUNCTION
  39. PHP_FUNCTION
  40. PHP_FUNCTION
  41. PHP_FUNCTION
  42. PHP_FUNCTION
  43. _php_math_number_format
  44. PHP_FUNCTION
  45. PHP_FUNCTION

/*
   +----------------------------------------------------------------------+
   | PHP Version 5                                                        |
   +----------------------------------------------------------------------+
   | Copyright (c) 1997-2008 The PHP Group                                |
   +----------------------------------------------------------------------+
   | This source file is subject to version 3.01 of the PHP license,      |
   | that is bundled with this package in the file LICENSE, and is        |
   | available through the world-wide-web at the following url:           |
   | http://www.php.net/license/3_01.txt                                  |
   | If you did not receive a copy of the PHP license and are unable to   |
   | obtain it through the world-wide-web, please send a note to          |
   | license@php.net so we can mail you a copy immediately.               |
   +----------------------------------------------------------------------+
   | Authors: Jim Winstead <jimw@php.net>                                 |
   |          Stig Sæther Bakken <ssb@php.net>                            |
   |          Zeev Suraski <zeev@zend.com>                                |
   | PHP 4.0 patches by Thies C. Arntzen <thies@thieso.net>               |
   +----------------------------------------------------------------------+
*/

/* $Id: math.c,v 1.131.2.2.2.9 2008/02/21 11:53:51 tony2001 Exp $ */

#include "php.h"
#include "php_math.h"
#include "zend_multiply.h"

#include <math.h>
#include <float.h>
#include <stdlib.h>

#ifndef PHP_ROUND_FUZZ
# ifndef PHP_WIN32
#  define PHP_ROUND_FUZZ 0.50000000001
# else
#  define PHP_ROUND_FUZZ 0.5
# endif
#endif

#define PHP_ROUND_WITH_FUZZ(val, places) {                      \
        double tmp_val=val, f = pow(10.0, (double) places);     \
        tmp_val *= f;                                   \
        if (tmp_val >= 0.0) {                           \
                tmp_val = floor(tmp_val + PHP_ROUND_FUZZ);      \
        } else {                                        \
                tmp_val = ceil(tmp_val - PHP_ROUND_FUZZ);       \
        }                                               \
        tmp_val /= f;                                   \
        val = !zend_isnan(tmp_val) ? tmp_val : val;     \
}                                                       \

/* {{{ proto int abs(int number)
   Return the absolute value of the number */
PHP_FUNCTION(abs) 
{
        zval **value;
        
        if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &value) == FAILURE) {
                WRONG_PARAM_COUNT;
        }

        convert_scalar_to_number_ex(value);
        
        if (Z_TYPE_PP(value) == IS_DOUBLE) {
                RETURN_DOUBLE(fabs(Z_DVAL_PP(value)));
        } else if (Z_TYPE_PP(value) == IS_LONG) {
                if (Z_LVAL_PP(value) == LONG_MIN) {
                        RETURN_DOUBLE(-(double)LONG_MIN);
                } else {
                        RETURN_LONG(Z_LVAL_PP(value) < 0 ? -Z_LVAL_PP(value) : Z_LVAL_PP(value));
                }
        }
        RETURN_FALSE;
}
/* }}} */ 

/* {{{ proto float ceil(float number)
   Returns the next highest integer value of the number */
PHP_FUNCTION(ceil) 
{
        zval **value;
        
        if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &value) == FAILURE) {
                WRONG_PARAM_COUNT;
        }

        convert_scalar_to_number_ex(value);

        if (Z_TYPE_PP(value) == IS_DOUBLE) {
                RETURN_DOUBLE(ceil(Z_DVAL_PP(value)));
        } else if (Z_TYPE_PP(value) == IS_LONG) {
                convert_to_double_ex(value);
                RETURN_DOUBLE(Z_DVAL_PP(value));
        }

        RETURN_FALSE;
}
/* }}} */

/* {{{ proto float floor(float number)
   Returns the next lowest integer value from the number */
PHP_FUNCTION(floor)
{
        zval **value;
        
        if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &value) == FAILURE) {
                WRONG_PARAM_COUNT;
        }

        convert_scalar_to_number_ex(value);

        if (Z_TYPE_PP(value) == IS_DOUBLE) {
                RETURN_DOUBLE(floor(Z_DVAL_PP(value)));
        } else if (Z_TYPE_PP(value) == IS_LONG) {
                convert_to_double_ex(value);
                RETURN_DOUBLE(Z_DVAL_PP(value));
        }

        RETURN_FALSE;
}
/* }}} */

/* {{{ proto float round(float number [, int precision])
   Returns the number rounded to specified precision */
PHP_FUNCTION(round)
{
        zval **value, **precision;
        int places = 0;
        double return_val;
        
        if (ZEND_NUM_ARGS() < 1 || ZEND_NUM_ARGS() > 2 ||
                zend_get_parameters_ex(ZEND_NUM_ARGS(), &value, &precision) == FAILURE) {
                WRONG_PARAM_COUNT;
        }

        if (ZEND_NUM_ARGS() == 2) {
                convert_to_long_ex(precision);
                places = (int) Z_LVAL_PP(precision);
        }

        convert_scalar_to_number_ex(value);

        switch (Z_TYPE_PP(value)) {
                case IS_LONG:
                        /* Simple case - long that doesn't need to be rounded. */
                        if (places >= 0) {
                                RETURN_DOUBLE((double) Z_LVAL_PP(value));
                        }
                        /* break omitted intentionally */

                case IS_DOUBLE:
                        return_val = (Z_TYPE_PP(value) == IS_LONG) ?
                                                        (double)Z_LVAL_PP(value) : Z_DVAL_PP(value);

                        PHP_ROUND_WITH_FUZZ(return_val, places);

                        RETURN_DOUBLE(return_val);
                        break;

                default:
                        RETURN_FALSE;
                        break;
        }
}
/* }}} */

/* {{{ proto float sin(float number)
   Returns the sine of the number in radians */
PHP_FUNCTION(sin)
{
        zval **num;

        if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &num) == FAILURE) {
                WRONG_PARAM_COUNT;
        }
        convert_to_double_ex(num);
        Z_DVAL_P(return_value) = sin(Z_DVAL_PP(num));
        Z_TYPE_P(return_value) = IS_DOUBLE;
}
/* }}} */

/* {{{ proto float cos(float number)
   Returns the cosine of the number in radians */
PHP_FUNCTION(cos)
{
        zval **num;

        if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &num) == FAILURE) {
                WRONG_PARAM_COUNT;
        }
        convert_to_double_ex(num);
        Z_DVAL_P(return_value) = cos(Z_DVAL_PP(num));
        Z_TYPE_P(return_value) = IS_DOUBLE;
}
/* }}} */

/* {{{ proto float tan(float number)
   Returns the tangent of the number in radians */
PHP_FUNCTION(tan)
{
        zval **num;

        if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &num) == FAILURE) {
                WRONG_PARAM_COUNT;
        }
        convert_to_double_ex(num);
        Z_DVAL_P(return_value) = tan(Z_DVAL_PP(num));
        Z_TYPE_P(return_value) = IS_DOUBLE;
}
/* }}} */

/* {{{ proto float asin(float number)
   Returns the arc sine of the number in radians */
PHP_FUNCTION(asin)
{
        zval **num;

        if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &num) == FAILURE) {
                WRONG_PARAM_COUNT;
        }
        convert_to_double_ex(num);
        Z_DVAL_P(return_value) = asin(Z_DVAL_PP(num));
        Z_TYPE_P(return_value) = IS_DOUBLE;
}
/* }}} */

/* {{{ proto float acos(float number)
   Return the arc cosine of the number in radians */
PHP_FUNCTION(acos)
{
        zval **num;

        if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &num) == FAILURE) {
                WRONG_PARAM_COUNT;
        }
        convert_to_double_ex(num);
        Z_DVAL_P(return_value) = acos(Z_DVAL_PP(num));
        Z_TYPE_P(return_value) = IS_DOUBLE;
}
/* }}} */

/* {{{ proto float atan(float number)
   Returns the arc tangent of the number in radians */
PHP_FUNCTION(atan)
{
        zval **num;

        if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &num) == FAILURE) {
                WRONG_PARAM_COUNT;
        }
        convert_to_double_ex(num);
        Z_DVAL_P(return_value) = atan(Z_DVAL_PP(num));
        Z_TYPE_P(return_value) = IS_DOUBLE;
}
/* }}} */

/* {{{ proto float atan2(float y, float x)
   Returns the arc tangent of y/x, with the resulting quadrant determined by the signs of y and x */
PHP_FUNCTION(atan2)
{
        zval **num1, **num2;

        if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &num1, &num2) == FAILURE) {
                WRONG_PARAM_COUNT;
        }
        convert_to_double_ex(num1);
        convert_to_double_ex(num2);
        Z_DVAL_P(return_value) = atan2(Z_DVAL_PP(num1), Z_DVAL_PP(num2));
        Z_TYPE_P(return_value) = IS_DOUBLE;
}
/* }}} */

/* {{{ proto float sinh(float number)
   Returns the hyperbolic sine of the number, defined as (exp(number) - exp(-number))/2 */
PHP_FUNCTION(sinh)
{
        zval **num;

        if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &num) == FAILURE) {
                WRONG_PARAM_COUNT;
        }
        convert_to_double_ex(num);
        Z_DVAL_P(return_value) = sinh(Z_DVAL_PP(num));
        Z_TYPE_P(return_value) = IS_DOUBLE;
}
/* }}} */

/* {{{ proto float cosh(float number)
   Returns the hyperbolic cosine of the number, defined as (exp(number) + exp(-number))/2 */
PHP_FUNCTION(cosh)
{
        zval **num;

        if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &num) == FAILURE) {
                WRONG_PARAM_COUNT;
        }
        convert_to_double_ex(num);
        Z_DVAL_P(return_value) = cosh(Z_DVAL_PP(num));
        Z_TYPE_P(return_value) = IS_DOUBLE;
}
/* }}} */

/* {{{ proto float tanh(float number)
   Returns the hyperbolic tangent of the number, defined as sinh(number)/cosh(number) */
PHP_FUNCTION(tanh)
{
        zval **num;

        if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &num) == FAILURE) {
                WRONG_PARAM_COUNT;
        }
        convert_to_double_ex(num);
        Z_DVAL_P(return_value) = tanh(Z_DVAL_PP(num));
        Z_TYPE_P(return_value) = IS_DOUBLE;
}
/* }}} */

#if !defined(PHP_WIN32) && !defined(NETWARE)
#ifdef HAVE_ASINH
/* {{{ proto float asinh(float number)
   Returns the inverse hyperbolic sine of the number, i.e. the value whose hyperbolic sine is number */
PHP_FUNCTION(asinh)
{
        zval **num;

        if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &num) == FAILURE) {
                WRONG_PARAM_COUNT;
        }
        convert_to_double_ex(num);
        Z_DVAL_P(return_value) = asinh(Z_DVAL_PP(num));
        Z_TYPE_P(return_value) = IS_DOUBLE;
}
/* }}} */
#endif /* HAVE_ASINH */

#ifdef HAVE_ACOSH
/* {{{ proto float acosh(float number)
   Returns the inverse hyperbolic cosine of the number, i.e. the value whose hyperbolic cosine is number */
PHP_FUNCTION(acosh)
{
        zval **num;

        if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &num) == FAILURE) {
                WRONG_PARAM_COUNT;
        }
        convert_to_double_ex(num);
        Z_DVAL_P(return_value) = acosh(Z_DVAL_PP(num));
        Z_TYPE_P(return_value) = IS_DOUBLE;
}
/* }}} */
#endif /* HAVE_ACOSH */

#ifdef HAVE_ATANH
/* {{{ proto float atanh(float number)
   Returns the inverse hyperbolic tangent of the number, i.e. the value whose hyperbolic tangent is number */
PHP_FUNCTION(atanh)
{
        zval **num;

        if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &num) == FAILURE) {
                WRONG_PARAM_COUNT;
        }
        convert_to_double_ex(num);
        Z_DVAL_P(return_value) = atanh(Z_DVAL_PP(num));
        Z_TYPE_P(return_value) = IS_DOUBLE;
}
/* }}} */
#endif /* HAVE_ATANH */
#endif /* !defined(PHP_WIN32) && !defined(NETWARE) */

/* {{{ proto float pi(void)
   Returns an approximation of pi */
PHP_FUNCTION(pi)
{
        Z_DVAL_P(return_value) = M_PI;
        Z_TYPE_P(return_value) = IS_DOUBLE;
}
/* }}} */

/* {{{ proto bool is_finite(float val)
   Returns whether argument is finite */
PHP_FUNCTION(is_finite)
{
        double dval;


        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &dval) == FAILURE) {
                return;
        }
        RETURN_BOOL(zend_finite(dval));
}
/* }}} */

/* {{{ proto bool is_infinite(float val)
   Returns whether argument is infinite */
PHP_FUNCTION(is_infinite)
{
        double dval;

        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &dval) == FAILURE) {
                return;
        }
        RETURN_BOOL(zend_isinf(dval));
}
/* }}} */

/* {{{ proto bool is_nan(float val)
   Returns whether argument is not a number */
PHP_FUNCTION(is_nan)
{
        double dval;

        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &dval) == FAILURE) {
                return;
        }
        RETURN_BOOL(zend_isnan(dval));
}
/* }}} */

/* {{{ proto number pow(number base, number exponent)
   Returns base raised to the power of exponent. Returns integer result when possible */
PHP_FUNCTION(pow)
{
        zval *zbase, *zexp;

        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/z/", &zbase, &zexp) == FAILURE) {
                return;
        }

        /* make sure we're dealing with numbers */
        convert_scalar_to_number(zbase TSRMLS_CC);
        convert_scalar_to_number(zexp TSRMLS_CC);

        /* if both base and exponent were longs, we'll try to get a long out */
        if (Z_TYPE_P(zbase) == IS_LONG && Z_TYPE_P(zexp) == IS_LONG && Z_LVAL_P(zexp) >= 0) {
                long l1 = 1, l2 = Z_LVAL_P(zbase), i = Z_LVAL_P(zexp);
                
                if (i == 0) {
                        RETURN_LONG(1L);
                } else if (l2 == 0) {
                        RETURN_LONG(0);
                }

                /* calculate pow(long,long) in O(log exp) operations, bail if overflow */
                while (i >= 1) {
                        int overflow;
                        double dval = 0.0;

                        if (i % 2) {
                                --i;
                                ZEND_SIGNED_MULTIPLY_LONG(l1,l2,l1,dval,overflow);
                                if (overflow) RETURN_DOUBLE(dval * pow(l2,i));
                        } else {
                                i /= 2;
                                ZEND_SIGNED_MULTIPLY_LONG(l2,l2,l2,dval,overflow);
                                if (overflow) RETURN_DOUBLE((double)l1 * pow(dval,i));
                        }
                        if (i == 0) {
                                RETURN_LONG(l1);
                        }
                }
        }
        convert_to_double(zbase);
        convert_to_double(zexp);
        
        RETURN_DOUBLE( pow(Z_DVAL_P(zbase),Z_DVAL_P(zexp)) );
}
/* }}} */

/* {{{ proto float exp(float number)
   Returns e raised to the power of the number */
PHP_FUNCTION(exp)
{
        double num;

        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
                return;
        }

        RETURN_DOUBLE(exp(num));
}
/* }}} */

#if !defined(PHP_WIN32) && !defined(NETWARE)
/* {{{ proto float expm1(float number)
   Returns exp(number) - 1, computed in a way that accurate even when the value of number is close to zero */
/*
   WARNING: this function is expermental: it could change its name or 
   disappear in the next version of PHP!
*/
PHP_FUNCTION(expm1)
{
        zval **num;

        if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &num) == FAILURE) {
                WRONG_PARAM_COUNT;
        }
        convert_to_double_ex(num);
        Z_DVAL_P(return_value) = expm1(Z_DVAL_PP(num));
        Z_TYPE_P(return_value) = IS_DOUBLE;
}
/* }}} */

#ifdef HAVE_LOG1P
/* {{{ proto float log1p(float number)
   Returns log(1 + number), computed in a way that accurate even when the value of number is close to zero */ 
/*
   WARNING: this function is expermental: it could change its name or 
   disappear in the next version of PHP!
*/
PHP_FUNCTION(log1p)
{
        zval **num;

        if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &num) == FAILURE) {
                WRONG_PARAM_COUNT;
        }
        convert_to_double_ex(num);
        Z_DVAL_P(return_value) = log1p(Z_DVAL_PP(num));
        Z_TYPE_P(return_value) = IS_DOUBLE;
}
/* }}} */
#endif /* HAVE_LOG1P */
#endif /* !defined(PHP_WIN32) && !defined(NETWARE) */

/* {{{ proto float log(float number, [float base])
   Returns the natural logarithm of the number, or the base log if base is specified */
PHP_FUNCTION(log)
{
        zval **num, **base;
        
        switch (ZEND_NUM_ARGS()) {
                case 1:
                        if (zend_get_parameters_ex(1, &num) == FAILURE) {
                                WRONG_PARAM_COUNT;
                        }
                        convert_to_double_ex(num);
                        RETURN_DOUBLE(log(Z_DVAL_PP(num)));
                case 2:
                        if (zend_get_parameters_ex(2, &num, &base) == FAILURE) {
                                WRONG_PARAM_COUNT;
                        }
                        convert_to_double_ex(num);
                        convert_to_double_ex(base);
                
                        if (Z_DVAL_PP(base) <= 0.0) {
                                php_error_docref(NULL TSRMLS_CC, E_WARNING, "base must be greater than 0");                             
                                RETURN_FALSE;
                        }
                        RETURN_DOUBLE(log(Z_DVAL_PP(num)) / log(Z_DVAL_PP(base)));
                default:
                        WRONG_PARAM_COUNT;
        }
}
/* }}} */

/* {{{ proto float log10(float number)
   Returns the base-10 logarithm of the number */
PHP_FUNCTION(log10)
{
        zval **num;

        if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &num) == FAILURE) {
                WRONG_PARAM_COUNT;
        }
        convert_to_double_ex(num);
        Z_DVAL_P(return_value) = log10(Z_DVAL_PP(num));
        Z_TYPE_P(return_value) = IS_DOUBLE;
}
/* }}} */

/* {{{ proto float sqrt(float number)
   Returns the square root of the number */
PHP_FUNCTION(sqrt)
{
        zval **num;

        if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &num) == FAILURE) {
                WRONG_PARAM_COUNT;
        }
        convert_to_double_ex(num);
        Z_DVAL_P(return_value) = sqrt(Z_DVAL_PP(num));
        Z_TYPE_P(return_value) = IS_DOUBLE;
}
/* }}} */

/* {{{ proto float hypot(float num1, float num2)
   Returns sqrt(num1*num1 + num2*num2) */ 
PHP_FUNCTION(hypot)
{
        zval **num1, **num2;

        if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &num1, &num2) == FAILURE) {
                WRONG_PARAM_COUNT;
        }
        convert_to_double_ex(num1);
        convert_to_double_ex(num2);
#if HAVE_HYPOT
        Z_DVAL_P(return_value) = hypot(Z_DVAL_PP(num1), Z_DVAL_PP(num2));
#elif defined(_MSC_VER)
        Z_DVAL_P(return_value) = _hypot(Z_DVAL_PP(num1), Z_DVAL_PP(num2));
#else
        Z_DVAL_P(return_value) = sqrt((Z_DVAL_PP(num1) * Z_DVAL_PP(num1)) +
                (Z_DVAL_PP(num2) * Z_DVAL_PP(num2)));
#endif
        Z_TYPE_P(return_value) = IS_DOUBLE;
}
/* }}} */

/* {{{ proto float deg2rad(float number)
   Converts the number in degrees to the radian equivalent */
PHP_FUNCTION(deg2rad)
{
        zval **deg;

        if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &deg) == FAILURE) {
                WRONG_PARAM_COUNT;
        }
        convert_to_double_ex(deg);
        RETVAL_DOUBLE((Z_DVAL_PP(deg) / 180.0) * M_PI);
}
/* }}} */

/* {{{ proto float rad2deg(float number)
   Converts the radian number to the equivalent number in degrees */
PHP_FUNCTION(rad2deg)
{
        zval **rad;

        if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &rad) == FAILURE) {
                WRONG_PARAM_COUNT;
        }
        convert_to_double_ex(rad);
        RETVAL_DOUBLE((Z_DVAL_PP(rad) / M_PI) * 180);
}
/* }}} */

/* {{{ _php_math_basetolong */
/*
 * Convert a string representation of a base(2-36) number to a long.
 */
PHPAPI long _php_math_basetolong(zval *arg, int base)
{
        long num = 0, digit, onum;
        int i;
        char c, *s;

        if (Z_TYPE_P(arg) != IS_STRING || base < 2 || base > 36) {
                return 0;
        }

        s = Z_STRVAL_P(arg);

        for (i = Z_STRLEN_P(arg); i > 0; i--) {
                c = *s++;
                
                digit = (c >= '0' && c <= '9') ? c - '0'
                        : (c >= 'A' && c <= 'Z') ? c - 'A' + 10
                        : (c >= 'a' && c <= 'z') ? c - 'a' + 10
                        : base;
                
                if (digit >= base) {
                        continue;
                }

                onum = num;
                num = num * base + digit;
                if (num > onum)
                        continue;

                {
                        TSRMLS_FETCH();

                        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Number '%s' is too big to fit in long", s);
                        return LONG_MAX;
                }
        }

        return num;
}
/* }}} */

/* {{{ _php_math_basetozval */
/*
 * Convert a string representation of a base(2-36) number to a zval.
 */
PHPAPI int _php_math_basetozval(zval *arg, int base, zval *ret)
{
        long num = 0;
        double fnum = 0;
        int i;
        int mode = 0;
        char c, *s;
        long cutoff;
        int cutlim;

        if (Z_TYPE_P(arg) != IS_STRING || base < 2 || base > 36) {
                return FAILURE;
        }

        s = Z_STRVAL_P(arg);

        cutoff = LONG_MAX / base;
        cutlim = LONG_MAX % base;
        
        for (i = Z_STRLEN_P(arg); i > 0; i--) {
                c = *s++;

                /* might not work for EBCDIC */
                if (c >= '0' && c <= '9') 
                        c -= '0';
                else if (c >= 'A' && c <= 'Z') 
                        c -= 'A' - 10;
                else if (c >= 'a' && c <= 'z') 
                        c -= 'a' - 10;
                else
                        continue;

                if (c >= base)
                        continue;
                
                switch (mode) {
                case 0: /* Integer */
                        if (num < cutoff || (num == cutoff && c <= cutlim)) {
                                num = num * base + c;
                                break;
                        } else {
                                fnum = num;
                                mode = 1;
                        }
                        /* fall-through */
                case 1: /* Float */
                        fnum = fnum * base + c;
                }       
        }

        if (mode == 1) {
                ZVAL_DOUBLE(ret, fnum);
        } else {
                ZVAL_LONG(ret, num);
        }
        return SUCCESS;
}
/* }}} */

/* {{{ _php_math_longtobase */
/*
 * Convert a long to a string containing a base(2-36) representation of
 * the number.
 */
PHPAPI char * _php_math_longtobase(zval *arg, int base)
{
        static char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
        char buf[(sizeof(unsigned long) << 3) + 1];
        char *ptr, *end;
        unsigned long value;

        if (Z_TYPE_P(arg) != IS_LONG || base < 2 || base > 36) {
                return STR_EMPTY_ALLOC();
        }

        value = Z_LVAL_P(arg);

        end = ptr = buf + sizeof(buf) - 1;
        *ptr = '\0';

        do {
                *--ptr = digits[value % base];
                value /= base;
        } while (ptr > buf && value);

        return estrndup(ptr, end - ptr);
}
/* }}} */

/* {{{ _php_math_zvaltobase */
/*
 * Convert a zval to a string containing a base(2-36) representation of
 * the number.
 */
PHPAPI char * _php_math_zvaltobase(zval *arg, int base TSRMLS_DC)
{
        static char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";

        if ((Z_TYPE_P(arg) != IS_LONG && Z_TYPE_P(arg) != IS_DOUBLE) || base < 2 || base > 36) {
                return STR_EMPTY_ALLOC();
        }

        if (Z_TYPE_P(arg) == IS_DOUBLE) {
                double fvalue = floor(Z_DVAL_P(arg)); /* floor it just in case */
                char *ptr, *end;
                char buf[(sizeof(double) << 3) + 1];

                /* Don't try to convert +/- infinity */
                if (fvalue == HUGE_VAL || fvalue == -HUGE_VAL) {
                        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Number too large");
                        return STR_EMPTY_ALLOC();
                }

                end = ptr = buf + sizeof(buf) - 1;
                *ptr = '\0';

                do {
                        *--ptr = digits[(int) fmod(fvalue, base)];
                        fvalue /= base;
                } while (ptr > buf && fabs(fvalue) >= 1);

                return estrndup(ptr, end - ptr);
        }
        
        return _php_math_longtobase(arg, base);
}       
/* }}} */

/* {{{ proto int bindec(string binary_number)
   Returns the decimal equivalent of the binary number */
PHP_FUNCTION(bindec)
{
        zval **arg;
        
        if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &arg) == FAILURE) {
                WRONG_PARAM_COUNT;
        }

        convert_to_string_ex(arg);
        if(_php_math_basetozval(*arg, 2, return_value) != SUCCESS) {
                RETURN_FALSE;
        }
}
/* }}} */

/* {{{ proto int hexdec(string hexadecimal_number)
   Returns the decimal equivalent of the hexadecimal number */
PHP_FUNCTION(hexdec)
{
        zval **arg;
        
        if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &arg) == FAILURE) {
                WRONG_PARAM_COUNT;
        }

        convert_to_string_ex(arg);

        if(_php_math_basetozval(*arg, 16, return_value) != SUCCESS) {
                RETURN_FALSE;
        }
}
/* }}} */

/* {{{ proto int octdec(string octal_number)
   Returns the decimal equivalent of an octal string */
PHP_FUNCTION(octdec)
{
        zval **arg;
        
        if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &arg) == FAILURE) {
                WRONG_PARAM_COUNT;
        }

        convert_to_string_ex(arg);

        if(_php_math_basetozval(*arg, 8, return_value) != SUCCESS) {
                RETURN_FALSE;
        }
}
/* }}} */

/* {{{ proto string decbin(int decimal_number)
   Returns a string containing a binary representation of the number */
PHP_FUNCTION(decbin)
{
        zval **arg;
        char *result;

        if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &arg) == FAILURE) {
                WRONG_PARAM_COUNT;
        }

        convert_to_long_ex(arg);

        result = _php_math_longtobase(*arg, 2);
        RETURN_STRING(result, 0);
}
/* }}} */

/* {{{ proto string decoct(int decimal_number)
   Returns a string containing an octal representation of the given number */
PHP_FUNCTION(decoct)
{
        zval **arg;
        char *result;

        if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &arg) == FAILURE) {
                WRONG_PARAM_COUNT;
        }

        convert_to_long_ex(arg);

        result = _php_math_longtobase(*arg, 8);
        RETURN_STRING(result, 0);
}
/* }}} */

/* {{{ proto string dechex(int decimal_number)
   Returns a string containing a hexadecimal representation of the given number */
PHP_FUNCTION(dechex)
{
        zval **arg;
        char *result;

        if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &arg) == FAILURE) {
                WRONG_PARAM_COUNT;
        }

        convert_to_long_ex(arg);

        result = _php_math_longtobase(*arg, 16);
        RETURN_STRING(result, 0);
}
/* }}} */

/* {{{ proto string base_convert(string number, int frombase, int tobase)
   Converts a number in a string from any base <= 36 to any base <= 36 */
PHP_FUNCTION(base_convert)
{
        zval **number, **frombase, **tobase, temp;
        char *result;

        if (ZEND_NUM_ARGS() != 3 || zend_get_parameters_ex(3, &number, &frombase, &tobase) == FAILURE) {
                WRONG_PARAM_COUNT;
        }
        convert_to_string_ex(number);
        convert_to_long_ex(frombase);
        convert_to_long_ex(tobase);
        if (Z_LVAL_PP(frombase) < 2 || Z_LVAL_PP(frombase) > 36) {
                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid `from base' (%ld)", Z_LVAL_PP(frombase));
                RETURN_FALSE;
        }
        if (Z_LVAL_PP(tobase) < 2 || Z_LVAL_PP(tobase) > 36) {
                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid `to base' (%ld)", Z_LVAL_PP(tobase));
                RETURN_FALSE;
        }

        if(_php_math_basetozval(*number, Z_LVAL_PP(frombase), &temp) != SUCCESS) {
                RETURN_FALSE;
        }
        result = _php_math_zvaltobase(&temp, Z_LVAL_PP(tobase) TSRMLS_CC);
        RETVAL_STRING(result, 0);
} 
/* }}} */

/* {{{ _php_math_number_format 
*/
PHPAPI char *_php_math_number_format(double d, int dec, char dec_point, char thousand_sep)
{
        char *tmpbuf = NULL, *resbuf;
        char *s, *t;  /* source, target */
        char *dp;
        int integral;
        int tmplen, reslen=0;
        int count=0;
        int is_negative=0;

        if (d < 0) {
                is_negative = 1;
                d = -d;
        }

        dec = MAX(0, dec);
        PHP_ROUND_WITH_FUZZ(d, dec);

        tmplen = spprintf(&tmpbuf, 0, "%.*F", dec, d);

        if (tmpbuf == NULL || !isdigit((int)tmpbuf[0])) {
                return tmpbuf;
        }

        /* find decimal point, if expected */
        if (dec) {
                dp = strpbrk(tmpbuf, ".,");
        } else {
                dp = NULL;
        }

        /* calculate the length of the return buffer */
        if (dp) {
                integral = dp - tmpbuf;
        } else {
                /* no decimal point was found */
                integral = tmplen;
        }

        /* allow for thousand separators */
        if (thousand_sep) {
                integral += (integral-1) / 3;
        }
        
        reslen = integral;
        
        if (dec) {
                reslen += dec;

                if (dec_point) {
                        reslen++;
                }
        }

        /* add a byte for minus sign */
        if (is_negative) {
                reslen++;
        }
        resbuf = (char *) emalloc(reslen+1); /* +1 for NUL terminator */

        s = tmpbuf+tmplen-1;
        t = resbuf+reslen;
        *t-- = '\0';

        /* copy the decimal places.
         * Take care, as the sprintf implementation may return less places than
         * we requested due to internal buffer limitations */
        if (dec) {
                int declen = dp ? s - dp : 0;
                int topad = dec > declen ? dec - declen : 0;

                /* pad with '0's */
                while (topad--) {
                        *t-- = '0';
                }
                
                if (dp) {
                        s -= declen + 1; /* +1 to skip the point */
                        t -= declen;

                        /* now copy the chars after the point */
                        memcpy(t + 1, dp + 1, declen);
                }

                /* add decimal point */
                if (dec_point) {
                        *t-- = dec_point;
                }
        }

        /* copy the numbers before the decimal point, adding thousand
         * separator every three digits */
        while(s >= tmpbuf) {
                *t-- = *s--;
                if (thousand_sep && (++count%3)==0 && s>=tmpbuf) {
                        *t-- = thousand_sep;
                }
        }

        /* and a minus sign, if needed */
        if (is_negative) {
                *t-- = '-';
        }

        efree(tmpbuf);
        
        return resbuf;
}
/* }}} */

/* {{{ proto string number_format(float number [, int num_decimal_places [, string dec_seperator, string thousands_seperator]])
   Formats a number with grouped thousands */
PHP_FUNCTION(number_format)
{
        zval **num, **dec, **t_s, **d_p;
        char thousand_sep=',', dec_point='.';
        
        switch(ZEND_NUM_ARGS()) {
        case 1:
                if (zend_get_parameters_ex(1, &num)==FAILURE) {
                        RETURN_FALSE;
                }
                convert_to_double_ex(num);
                RETURN_STRING(_php_math_number_format(Z_DVAL_PP(num), 0, dec_point, thousand_sep), 0);
                break;
        case 2:
                if (zend_get_parameters_ex(2, &num, &dec)==FAILURE) {
                        RETURN_FALSE;
                }
                convert_to_double_ex(num);
                convert_to_long_ex(dec);
                RETURN_STRING(_php_math_number_format(Z_DVAL_PP(num), Z_LVAL_PP(dec), dec_point, thousand_sep), 0);
                break;
        case 4:
                if (zend_get_parameters_ex(4, &num, &dec, &d_p, &t_s)==FAILURE) {
                        RETURN_FALSE;
                }
                convert_to_double_ex(num);
                convert_to_long_ex(dec);

                if (Z_TYPE_PP(d_p) != IS_NULL) { 
                        convert_to_string_ex(d_p);
                        if (Z_STRLEN_PP(d_p)>=1) {
                                dec_point=Z_STRVAL_PP(d_p)[0];
                        } else if (Z_STRLEN_PP(d_p)==0) {
                                dec_point=0;
                        }
                }
                if (Z_TYPE_PP(t_s) != IS_NULL) {
                        convert_to_string_ex(t_s);
                        if (Z_STRLEN_PP(t_s)>=1) {
                                thousand_sep=Z_STRVAL_PP(t_s)[0];
                        } else if(Z_STRLEN_PP(t_s)==0) {
                                thousand_sep=0; 
                        }
                }
                RETURN_STRING(_php_math_number_format(Z_DVAL_PP(num), Z_LVAL_PP(dec), dec_point, thousand_sep), 0);
                break;
        default:
                WRONG_PARAM_COUNT;
                break;
        }
}
/* }}} */

/* {{{ proto float fmod(float x, float y)
   Returns the remainder of dividing x by y as a float */
PHP_FUNCTION(fmod)
{
        double num1, num2;

        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "dd",  &num1, &num2) == FAILURE) {
                return;
        }
        
        Z_DVAL_P(return_value) = fmod(num1, num2);
        Z_TYPE_P(return_value) = IS_DOUBLE;
}
/* }}} */



/*
 * Local variables:
 * tab-width: 4
 * c-basic-offset: 4
 * End:
 * vim600: fdm=marker
 * vim: noet sw=4 ts=4
 */

/* [<][>][^][v][top][bottom][index][help] */