1/*
2   +----------------------------------------------------------------------+
3   | PHP Version 5                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2014 The PHP Group                                |
6   +----------------------------------------------------------------------+
7   | This source file is subject to version 3.01 of the PHP license,      |
8   | that is bundled with this package in the file LICENSE, and is        |
9   | available through the world-wide-web at the following url:           |
10   | http://www.php.net/license/3_01.txt                                  |
11   | If you did not receive a copy of the PHP license and are unable to   |
12   | obtain it through the world-wide-web, please send a note to          |
13   | license@php.net so we can mail you a copy immediately.               |
14   +----------------------------------------------------------------------+
15   | Authors: Jim Winstead <jimw@php.net>                                 |
16   |          Stig Sæther Bakken <ssb@php.net>                            |
17   |          Zeev Suraski <zeev@zend.com>                                |
18   | PHP 4.0 patches by Thies C. Arntzen <thies@thieso.net>               |
19   +----------------------------------------------------------------------+
20*/
21
22/* $Id$ */
23
24#include "php.h"
25#include "php_math.h"
26#include "zend_multiply.h"
27
28#include <math.h>
29#include <float.h>
30#include <stdlib.h>
31
32#include "basic_functions.h"
33
34/* {{{ php_intlog10abs
35   Returns floor(log10(fabs(val))), uses fast binary search */
36static inline int php_intlog10abs(double value) {
37    int result;
38    value = fabs(value);
39
40    if (value < 1e-8 || value > 1e22) {
41        result = (int)floor(log10(value));
42    } else {
43        static const double values[] = {
44            1e-8, 1e-7, 1e-6, 1e-5, 1e-4, 1e-3, 1e-2, 1e-1,
45            1e0,  1e1,  1e2,  1e3,  1e4,  1e5,  1e6,  1e7,
46            1e8,  1e9,  1e10, 1e11, 1e12, 1e13, 1e14, 1e15,
47            1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22};
48        /* Do a binary search with 5 steps */
49        result = 15;
50        if (value < values[result]) {
51            result -= 8;
52        } else {
53            result += 8;
54        }
55        if (value < values[result]) {
56            result -= 4;
57        } else {
58            result += 4;
59        }
60        if (value < values[result]) {
61            result -= 2;
62        } else {
63            result += 2;
64        }
65        if (value < values[result]) {
66            result -= 1;
67        } else {
68            result += 1;
69        }
70        if (value < values[result]) {
71            result -= 1;
72        }
73        result -= 8;
74    }
75    return result;
76}
77/* }}} */
78
79/* {{{ php_intpow10
80       Returns pow(10.0, (double)power), uses fast lookup table for exact powers */
81static inline double php_intpow10(int power) {
82    static const double powers[] = {
83        1e0,  1e1,  1e2,  1e3,  1e4,  1e5,  1e6,  1e7,
84        1e8,  1e9,  1e10, 1e11, 1e12, 1e13, 1e14, 1e15,
85        1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22};
86
87    /* Not in lookup table */
88    if (power < 0 || power > 22) {
89        return pow(10.0, (double)power);
90    }
91    return powers[power];
92}
93/* }}} */
94
95/* {{{ php_math_is_finite */
96static inline int php_math_is_finite(double value) {
97#if defined(PHP_WIN32)
98    return _finite(value);
99#elif defined(isfinite)
100    return isfinite(value);
101#else
102    return value == value && (value == 0. || value * 2. != value);
103#endif
104}
105/* }}} */
106
107/* {{{ php_round_helper
108       Actually performs the rounding of a value to integer in a certain mode */
109static inline double php_round_helper(double value, int mode) {
110    double tmp_value;
111
112    if (value >= 0.0) {
113        tmp_value = floor(value + 0.5);
114        if ((mode == PHP_ROUND_HALF_DOWN && value == (-0.5 + tmp_value)) ||
115            (mode == PHP_ROUND_HALF_EVEN && value == (0.5 + 2 * floor(tmp_value/2.0))) ||
116            (mode == PHP_ROUND_HALF_ODD  && value == (0.5 + 2 * floor(tmp_value/2.0) - 1.0)))
117        {
118            tmp_value = tmp_value - 1.0;
119        }
120    } else {
121        tmp_value = ceil(value - 0.5);
122        if ((mode == PHP_ROUND_HALF_DOWN && value == (0.5 + tmp_value)) ||
123            (mode == PHP_ROUND_HALF_EVEN && value == (-0.5 + 2 * ceil(tmp_value/2.0))) ||
124            (mode == PHP_ROUND_HALF_ODD  && value == (-0.5 + 2 * ceil(tmp_value/2.0) + 1.0)))
125        {
126            tmp_value = tmp_value + 1.0;
127        }
128    }
129
130    return tmp_value;
131}
132/* }}} */
133
134/* {{{ _php_math_round */
135/*
136 * Rounds a number to a certain number of decimal places in a certain rounding
137 * mode. For the specifics of the algorithm, see http://wiki.php.net/rfc/rounding
138 */
139PHPAPI double _php_math_round(double value, int places, int mode) {
140    double f1, f2;
141    double tmp_value;
142    int precision_places;
143
144    if (!php_math_is_finite(value)) {
145        return value;
146    }
147
148    precision_places = 14 - php_intlog10abs(value);
149
150    f1 = php_intpow10(abs(places));
151
152    /* If the decimal precision guaranteed by FP arithmetic is higher than
153       the requested places BUT is small enough to make sure a non-zero value
154       is returned, pre-round the result to the precision */
155    if (precision_places > places && precision_places - places < 15) {
156        f2 = php_intpow10(abs(precision_places));
157        if (precision_places >= 0) {
158            tmp_value = value * f2;
159        } else {
160            tmp_value = value / f2;
161        }
162        /* preround the result (tmp_value will always be something * 1e14,
163           thus never larger than 1e15 here) */
164        tmp_value = php_round_helper(tmp_value, mode);
165        /* now correctly move the decimal point */
166        f2 = php_intpow10(abs(places - precision_places));
167        /* because places < precision_places */
168        tmp_value = tmp_value / f2;
169    } else {
170        /* adjust the value */
171        if (places >= 0) {
172            tmp_value = value * f1;
173        } else {
174            tmp_value = value / f1;
175        }
176        /* This value is beyond our precision, so rounding it is pointless */
177        if (fabs(tmp_value) >= 1e15) {
178            return value;
179        }
180    }
181
182    /* round the temp value */
183    tmp_value = php_round_helper(tmp_value, mode);
184
185    /* see if it makes sense to use simple division to round the value */
186    if (abs(places) < 23) {
187        if (places > 0) {
188            tmp_value = tmp_value / f1;
189        } else {
190            tmp_value = tmp_value * f1;
191        }
192    } else {
193        /* Simple division can't be used since that will cause wrong results.
194           Instead, the number is converted to a string and back again using
195           strtod(). strtod() will return the nearest possible FP value for
196           that string. */
197
198        /* 40 Bytes should be more than enough for this format string. The
199           float won't be larger than 1e15 anyway. But just in case, use
200           snprintf() and make sure the buffer is zero-terminated */
201        char buf[40];
202        snprintf(buf, 39, "%15fe%d", tmp_value, -places);
203        buf[39] = '\0';
204        tmp_value = zend_strtod(buf, NULL);
205        /* couldn't convert to string and back */
206        if (!zend_finite(tmp_value) || zend_isnan(tmp_value)) {
207            tmp_value = value;
208        }
209    }
210
211    return tmp_value;
212}
213/* }}} */
214
215/* {{{ php_asinh
216*/
217static double php_asinh(double z)
218{
219#ifdef HAVE_ASINH
220    return(asinh(z));
221#else
222    return(log(z + sqrt(1 + pow(z, 2))) / log(M_E));
223#endif
224}
225/* }}} */
226
227/* {{{ php_acosh
228*/
229static double php_acosh(double x)
230{
231#ifdef HAVE_ACOSH
232    return(acosh(x));
233#else
234    return(log(x + sqrt(x * x - 1)));
235#endif
236}
237/* }}} */
238
239/* {{{ php_atanh
240*/
241static double php_atanh(double z)
242{
243#ifdef HAVE_ATANH
244    return(atanh(z));
245#else
246    return(0.5 * log((1 + z) / (1 - z)));
247#endif
248}
249/* }}} */
250
251/* {{{ php_log1p
252*/
253static double php_log1p(double x)
254{
255#ifdef HAVE_LOG1P
256    return(log1p(x));
257#else
258    return(log(1 + x));
259#endif
260}
261/* }}} */
262
263/* {{{ php_expm1
264*/
265static double php_expm1(double x)
266{
267#if !defined(PHP_WIN32) && !defined(NETWARE)
268    return(expm1(x));
269#else
270    return(exp(x) - 1);
271#endif
272}
273/* }}}*/
274
275/* {{{ proto int abs(int number)
276   Return the absolute value of the number */
277PHP_FUNCTION(abs)
278{
279    zval **value;
280
281    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &value) == FAILURE) {
282        return;
283    }
284    convert_scalar_to_number_ex(value);
285
286    if (Z_TYPE_PP(value) == IS_DOUBLE) {
287        RETURN_DOUBLE(fabs(Z_DVAL_PP(value)));
288    } else if (Z_TYPE_PP(value) == IS_LONG) {
289        if (Z_LVAL_PP(value) == LONG_MIN) {
290            RETURN_DOUBLE(-(double)LONG_MIN);
291        } else {
292            RETURN_LONG(Z_LVAL_PP(value) < 0 ? -Z_LVAL_PP(value) : Z_LVAL_PP(value));
293        }
294    }
295    RETURN_FALSE;
296}
297/* }}} */
298
299/* {{{ proto float ceil(float number)
300   Returns the next highest integer value of the number */
301PHP_FUNCTION(ceil)
302{
303    zval **value;
304
305    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &value) == FAILURE) {
306        return;
307    }
308    convert_scalar_to_number_ex(value);
309
310    if (Z_TYPE_PP(value) == IS_DOUBLE) {
311        RETURN_DOUBLE(ceil(Z_DVAL_PP(value)));
312    } else if (Z_TYPE_PP(value) == IS_LONG) {
313        convert_to_double_ex(value);
314        RETURN_DOUBLE(Z_DVAL_PP(value));
315    }
316    RETURN_FALSE;
317}
318/* }}} */
319
320/* {{{ proto float floor(float number)
321   Returns the next lowest integer value from the number */
322PHP_FUNCTION(floor)
323{
324    zval **value;
325
326    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &value) == FAILURE) {
327        return;
328    }
329    convert_scalar_to_number_ex(value);
330
331    if (Z_TYPE_PP(value) == IS_DOUBLE) {
332        RETURN_DOUBLE(floor(Z_DVAL_PP(value)));
333    } else if (Z_TYPE_PP(value) == IS_LONG) {
334        convert_to_double_ex(value);
335        RETURN_DOUBLE(Z_DVAL_PP(value));
336    }
337    RETURN_FALSE;
338}
339/* }}} */
340
341/* {{{ proto float round(float number [, int precision [, int mode]])
342   Returns the number rounded to specified precision */
343PHP_FUNCTION(round)
344{
345    zval **value;
346    int places = 0;
347    long precision = 0;
348    long mode = PHP_ROUND_HALF_UP;
349    double return_val;
350
351    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z|ll", &value, &precision, &mode) == FAILURE) {
352        return;
353    }
354
355    if (ZEND_NUM_ARGS() >= 2) {
356        places = (int) precision;
357    }
358    convert_scalar_to_number_ex(value);
359
360    switch (Z_TYPE_PP(value)) {
361        case IS_LONG:
362            /* Simple case - long that doesn't need to be rounded. */
363            if (places >= 0) {
364                RETURN_DOUBLE((double) Z_LVAL_PP(value));
365            }
366            /* break omitted intentionally */
367
368        case IS_DOUBLE:
369            return_val = (Z_TYPE_PP(value) == IS_LONG) ? (double)Z_LVAL_PP(value) : Z_DVAL_PP(value);
370            return_val = _php_math_round(return_val, places, mode);
371            RETURN_DOUBLE(return_val);
372            break;
373
374        default:
375            RETURN_FALSE;
376            break;
377    }
378}
379/* }}} */
380
381/* {{{ proto float sin(float number)
382   Returns the sine of the number in radians */
383PHP_FUNCTION(sin)
384{
385    double num;
386
387    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
388        return;
389    }
390    RETURN_DOUBLE(sin(num));
391}
392/* }}} */
393
394/* {{{ proto float cos(float number)
395   Returns the cosine of the number in radians */
396PHP_FUNCTION(cos)
397{
398    double num;
399
400    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
401        return;
402    }
403    RETURN_DOUBLE(cos(num));
404}
405/* }}} */
406
407/* {{{ proto float tan(float number)
408   Returns the tangent of the number in radians */
409PHP_FUNCTION(tan)
410{
411    double num;
412
413    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
414        return;
415    }
416    RETURN_DOUBLE(tan(num));
417}
418/* }}} */
419
420/* {{{ proto float asin(float number)
421   Returns the arc sine of the number in radians */
422PHP_FUNCTION(asin)
423{
424    double num;
425
426    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
427        return;
428    }
429    RETURN_DOUBLE(asin(num));
430}
431/* }}} */
432
433/* {{{ proto float acos(float number)
434   Return the arc cosine of the number in radians */
435PHP_FUNCTION(acos)
436{
437    double num;
438
439    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
440        return;
441    }
442    RETURN_DOUBLE(acos(num));
443}
444/* }}} */
445
446/* {{{ proto float atan(float number)
447   Returns the arc tangent of the number in radians */
448PHP_FUNCTION(atan)
449{
450    double num;
451
452    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
453        return;
454    }
455    RETURN_DOUBLE(atan(num));
456}
457/* }}} */
458
459/* {{{ proto float atan2(float y, float x)
460   Returns the arc tangent of y/x, with the resulting quadrant determined by the signs of y and x */
461PHP_FUNCTION(atan2)
462{
463    double num1, num2;
464
465    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "dd", &num1, &num2) == FAILURE) {
466        return;
467    }
468    RETURN_DOUBLE(atan2(num1, num2));
469}
470/* }}} */
471
472/* {{{ proto float sinh(float number)
473   Returns the hyperbolic sine of the number, defined as (exp(number) - exp(-number))/2 */
474PHP_FUNCTION(sinh)
475{
476    double num;
477
478    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
479        return;
480    }
481    RETURN_DOUBLE(sinh(num));
482}
483/* }}} */
484
485/* {{{ proto float cosh(float number)
486   Returns the hyperbolic cosine of the number, defined as (exp(number) + exp(-number))/2 */
487PHP_FUNCTION(cosh)
488{
489    double num;
490
491    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
492        return;
493    }
494    RETURN_DOUBLE(cosh(num));
495}
496/* }}} */
497
498/* {{{ proto float tanh(float number)
499   Returns the hyperbolic tangent of the number, defined as sinh(number)/cosh(number) */
500PHP_FUNCTION(tanh)
501{
502    double num;
503
504    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
505        return;
506    }
507    RETURN_DOUBLE(tanh(num));
508}
509/* }}} */
510
511/* {{{ proto float asinh(float number)
512   Returns the inverse hyperbolic sine of the number, i.e. the value whose hyperbolic sine is number */
513PHP_FUNCTION(asinh)
514{
515    double num;
516
517    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
518        return;
519    }
520    RETURN_DOUBLE(php_asinh(num));
521}
522/* }}} */
523
524/* {{{ proto float acosh(float number)
525   Returns the inverse hyperbolic cosine of the number, i.e. the value whose hyperbolic cosine is number */
526PHP_FUNCTION(acosh)
527{
528    double num;
529
530    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
531        return;
532    }
533    RETURN_DOUBLE(php_acosh(num));
534}
535/* }}} */
536
537/* {{{ proto float atanh(float number)
538   Returns the inverse hyperbolic tangent of the number, i.e. the value whose hyperbolic tangent is number */
539PHP_FUNCTION(atanh)
540{
541    double num;
542
543    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
544        return;
545    }
546    RETURN_DOUBLE(php_atanh(num));
547}
548/* }}} */
549
550/* {{{ proto float pi(void)
551   Returns an approximation of pi */
552PHP_FUNCTION(pi)
553{
554    RETURN_DOUBLE(M_PI);
555}
556/* }}} */
557
558/* {{{ proto bool is_finite(float val)
559   Returns whether argument is finite */
560PHP_FUNCTION(is_finite)
561{
562    double dval;
563
564    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &dval) == FAILURE) {
565        return;
566    }
567    RETURN_BOOL(zend_finite(dval));
568}
569/* }}} */
570
571/* {{{ proto bool is_infinite(float val)
572   Returns whether argument is infinite */
573PHP_FUNCTION(is_infinite)
574{
575    double dval;
576
577    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &dval) == FAILURE) {
578        return;
579    }
580    RETURN_BOOL(zend_isinf(dval));
581}
582/* }}} */
583
584/* {{{ proto bool is_nan(float val)
585   Returns whether argument is not a number */
586PHP_FUNCTION(is_nan)
587{
588    double dval;
589
590    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &dval) == FAILURE) {
591        return;
592    }
593    RETURN_BOOL(zend_isnan(dval));
594}
595/* }}} */
596
597/* {{{ proto number pow(number base, number exponent)
598   Returns base raised to the power of exponent. Returns integer result when possible */
599PHP_FUNCTION(pow)
600{
601    zval *zbase, *zexp;
602
603    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/z/", &zbase, &zexp) == FAILURE) {
604        return;
605    }
606
607    pow_function(return_value, zbase, zexp TSRMLS_CC);
608}
609/* }}} */
610
611/* {{{ proto float exp(float number)
612   Returns e raised to the power of the number */
613PHP_FUNCTION(exp)
614{
615    double num;
616
617    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
618        return;
619    }
620
621    RETURN_DOUBLE(exp(num));
622}
623/* }}} */
624
625/* {{{ proto float expm1(float number)
626   Returns exp(number) - 1, computed in a way that accurate even when the value of number is close to zero */
627/*
628   WARNING: this function is expermental: it could change its name or
629   disappear in the next version of PHP!
630*/
631PHP_FUNCTION(expm1)
632{
633    double num;
634
635    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
636        return;
637    }
638    RETURN_DOUBLE(php_expm1(num));
639}
640/* }}} */
641
642/* {{{ proto float log1p(float number)
643   Returns log(1 + number), computed in a way that accurate even when the value of number is close to zero */
644/*
645   WARNING: this function is expermental: it could change its name or
646   disappear in the next version of PHP!
647*/
648PHP_FUNCTION(log1p)
649{
650    double num;
651
652    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
653        return;
654    }
655    RETURN_DOUBLE(php_log1p(num));
656}
657/* }}} */
658
659/* {{{ proto float log(float number, [float base])
660   Returns the natural logarithm of the number, or the base log if base is specified */
661PHP_FUNCTION(log)
662{
663    double num, base = 0;
664
665    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d|d", &num, &base) == FAILURE) {
666        return;
667    }
668    if (ZEND_NUM_ARGS() == 1) {
669        RETURN_DOUBLE(log(num));
670    }
671    if (base <= 0.0) {
672        php_error_docref(NULL TSRMLS_CC, E_WARNING, "base must be greater than 0");
673        RETURN_FALSE;
674    }
675    if (base == 1) {
676        RETURN_DOUBLE(php_get_nan());
677    } else {
678        RETURN_DOUBLE(log(num) / log(base));
679    }
680}
681/* }}} */
682
683/* {{{ proto float log10(float number)
684   Returns the base-10 logarithm of the number */
685PHP_FUNCTION(log10)
686{
687    double num;
688
689    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
690        return;
691    }
692    RETURN_DOUBLE(log10(num));
693}
694/* }}} */
695
696/* {{{ proto float sqrt(float number)
697   Returns the square root of the number */
698PHP_FUNCTION(sqrt)
699{
700    double num;
701
702    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
703        return;
704    }
705    RETURN_DOUBLE(sqrt(num));
706}
707/* }}} */
708
709/* {{{ proto float hypot(float num1, float num2)
710   Returns sqrt(num1*num1 + num2*num2) */
711PHP_FUNCTION(hypot)
712{
713    double num1, num2;
714
715    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "dd", &num1, &num2) == FAILURE) {
716        return;
717    }
718#if HAVE_HYPOT
719    RETURN_DOUBLE(hypot(num1, num2));
720#elif defined(_MSC_VER)
721    RETURN_DOUBLE(_hypot(num1, num2));
722#else
723    RETURN_DOUBLE(sqrt((num1 * num1) + (num2 * num2)));
724#endif
725}
726/* }}} */
727
728/* {{{ proto float deg2rad(float number)
729   Converts the number in degrees to the radian equivalent */
730PHP_FUNCTION(deg2rad)
731{
732    double deg;
733
734    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &deg) == FAILURE) {
735        return;
736    }
737    RETURN_DOUBLE((deg / 180.0) * M_PI);
738}
739/* }}} */
740
741/* {{{ proto float rad2deg(float number)
742   Converts the radian number to the equivalent number in degrees */
743PHP_FUNCTION(rad2deg)
744{
745    double rad;
746
747    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &rad) == FAILURE) {
748        return;
749    }
750    RETURN_DOUBLE((rad / M_PI) * 180);
751}
752/* }}} */
753
754/* {{{ _php_math_basetolong */
755/*
756 * Convert a string representation of a base(2-36) number to a long.
757 */
758PHPAPI long _php_math_basetolong(zval *arg, int base)
759{
760    long num = 0, digit, onum;
761    int i;
762    char c, *s;
763
764    if (Z_TYPE_P(arg) != IS_STRING || base < 2 || base > 36) {
765        return 0;
766    }
767
768    s = Z_STRVAL_P(arg);
769
770    for (i = Z_STRLEN_P(arg); i > 0; i--) {
771        c = *s++;
772
773        digit = (c >= '0' && c <= '9') ? c - '0'
774            : (c >= 'A' && c <= 'Z') ? c - 'A' + 10
775            : (c >= 'a' && c <= 'z') ? c - 'a' + 10
776            : base;
777
778        if (digit >= base) {
779            continue;
780        }
781
782        onum = num;
783        num = num * base + digit;
784        if (num > onum)
785            continue;
786
787        {
788            TSRMLS_FETCH();
789
790            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Number '%s' is too big to fit in long", s);
791            return LONG_MAX;
792        }
793    }
794
795    return num;
796}
797/* }}} */
798
799/* {{{ _php_math_basetozval */
800/*
801 * Convert a string representation of a base(2-36) number to a zval.
802 */
803PHPAPI int _php_math_basetozval(zval *arg, int base, zval *ret)
804{
805    long num = 0;
806    double fnum = 0;
807    int i;
808    int mode = 0;
809    char c, *s;
810    long cutoff;
811    int cutlim;
812
813    if (Z_TYPE_P(arg) != IS_STRING || base < 2 || base > 36) {
814        return FAILURE;
815    }
816
817    s = Z_STRVAL_P(arg);
818
819    cutoff = LONG_MAX / base;
820    cutlim = LONG_MAX % base;
821
822    for (i = Z_STRLEN_P(arg); i > 0; i--) {
823        c = *s++;
824
825        /* might not work for EBCDIC */
826        if (c >= '0' && c <= '9')
827            c -= '0';
828        else if (c >= 'A' && c <= 'Z')
829            c -= 'A' - 10;
830        else if (c >= 'a' && c <= 'z')
831            c -= 'a' - 10;
832        else
833            continue;
834
835        if (c >= base)
836            continue;
837
838        switch (mode) {
839        case 0: /* Integer */
840            if (num < cutoff || (num == cutoff && c <= cutlim)) {
841                num = num * base + c;
842                break;
843            } else {
844                fnum = num;
845                mode = 1;
846            }
847            /* fall-through */
848        case 1: /* Float */
849            fnum = fnum * base + c;
850        }
851    }
852
853    if (mode == 1) {
854        ZVAL_DOUBLE(ret, fnum);
855    } else {
856        ZVAL_LONG(ret, num);
857    }
858    return SUCCESS;
859}
860/* }}} */
861
862/* {{{ _php_math_longtobase */
863/*
864 * Convert a long to a string containing a base(2-36) representation of
865 * the number.
866 */
867PHPAPI char * _php_math_longtobase(zval *arg, int base)
868{
869    static char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
870    char buf[(sizeof(unsigned long) << 3) + 1];
871    char *ptr, *end;
872    unsigned long value;
873
874    if (Z_TYPE_P(arg) != IS_LONG || base < 2 || base > 36) {
875        return STR_EMPTY_ALLOC();
876    }
877
878    value = Z_LVAL_P(arg);
879
880    end = ptr = buf + sizeof(buf) - 1;
881    *ptr = '\0';
882
883    do {
884        *--ptr = digits[value % base];
885        value /= base;
886    } while (ptr > buf && value);
887
888    return estrndup(ptr, end - ptr);
889}
890/* }}} */
891
892/* {{{ _php_math_zvaltobase */
893/*
894 * Convert a zval to a string containing a base(2-36) representation of
895 * the number.
896 */
897PHPAPI char * _php_math_zvaltobase(zval *arg, int base TSRMLS_DC)
898{
899    static char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
900
901    if ((Z_TYPE_P(arg) != IS_LONG && Z_TYPE_P(arg) != IS_DOUBLE) || base < 2 || base > 36) {
902        return STR_EMPTY_ALLOC();
903    }
904
905    if (Z_TYPE_P(arg) == IS_DOUBLE) {
906        double fvalue = floor(Z_DVAL_P(arg)); /* floor it just in case */
907        char *ptr, *end;
908        char buf[(sizeof(double) << 3) + 1];
909
910        /* Don't try to convert +/- infinity */
911        if (fvalue == HUGE_VAL || fvalue == -HUGE_VAL) {
912            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Number too large");
913            return STR_EMPTY_ALLOC();
914        }
915
916        end = ptr = buf + sizeof(buf) - 1;
917        *ptr = '\0';
918
919        do {
920            *--ptr = digits[(int) fmod(fvalue, base)];
921            fvalue /= base;
922        } while (ptr > buf && fabs(fvalue) >= 1);
923
924        return estrndup(ptr, end - ptr);
925    }
926
927    return _php_math_longtobase(arg, base);
928}
929/* }}} */
930
931/* {{{ proto int bindec(string binary_number)
932   Returns the decimal equivalent of the binary number */
933PHP_FUNCTION(bindec)
934{
935    zval **arg;
936
937    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &arg) == FAILURE) {
938        return;
939    }
940    convert_to_string_ex(arg);
941    if (_php_math_basetozval(*arg, 2, return_value) == FAILURE) {
942        RETURN_FALSE;
943    }
944}
945/* }}} */
946
947/* {{{ proto int hexdec(string hexadecimal_number)
948   Returns the decimal equivalent of the hexadecimal number */
949PHP_FUNCTION(hexdec)
950{
951    zval **arg;
952
953    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &arg) == FAILURE) {
954        return;
955    }
956    convert_to_string_ex(arg);
957    if (_php_math_basetozval(*arg, 16, return_value) == FAILURE) {
958        RETURN_FALSE;
959    }
960}
961/* }}} */
962
963/* {{{ proto int octdec(string octal_number)
964   Returns the decimal equivalent of an octal string */
965PHP_FUNCTION(octdec)
966{
967    zval **arg;
968
969    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &arg) == FAILURE) {
970        return;
971    }
972    convert_to_string_ex(arg);
973    if (_php_math_basetozval(*arg, 8, return_value) == FAILURE) {
974        RETURN_FALSE;
975    }
976}
977/* }}} */
978
979/* {{{ proto string decbin(int decimal_number)
980   Returns a string containing a binary representation of the number */
981PHP_FUNCTION(decbin)
982{
983    zval **arg;
984    char *result;
985
986    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &arg) == FAILURE) {
987        return;
988    }
989    convert_to_long_ex(arg);
990    result = _php_math_longtobase(*arg, 2);
991    RETURN_STRING(result, 0);
992}
993/* }}} */
994
995/* {{{ proto string decoct(int decimal_number)
996   Returns a string containing an octal representation of the given number */
997PHP_FUNCTION(decoct)
998{
999    zval **arg;
1000    char *result;
1001
1002    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &arg) == FAILURE) {
1003        return;
1004    }
1005    convert_to_long_ex(arg);
1006    result = _php_math_longtobase(*arg, 8);
1007    RETURN_STRING(result, 0);
1008}
1009/* }}} */
1010
1011/* {{{ proto string dechex(int decimal_number)
1012   Returns a string containing a hexadecimal representation of the given number */
1013PHP_FUNCTION(dechex)
1014{
1015    zval **arg;
1016    char *result;
1017
1018    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &arg) == FAILURE) {
1019        return;
1020    }
1021    convert_to_long_ex(arg);
1022    result = _php_math_longtobase(*arg, 16);
1023    RETURN_STRING(result, 0);
1024}
1025/* }}} */
1026
1027/* {{{ proto string base_convert(string number, int frombase, int tobase)
1028   Converts a number in a string from any base <= 36 to any base <= 36 */
1029PHP_FUNCTION(base_convert)
1030{
1031    zval **number, temp;
1032    long frombase, tobase;
1033    char *result;
1034
1035    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Zll", &number, &frombase, &tobase) == FAILURE) {
1036        return;
1037    }
1038    convert_to_string_ex(number);
1039
1040    if (frombase < 2 || frombase > 36) {
1041        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid `from base' (%ld)", frombase);
1042        RETURN_FALSE;
1043    }
1044    if (tobase < 2 || tobase > 36) {
1045        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid `to base' (%ld)", tobase);
1046        RETURN_FALSE;
1047    }
1048
1049    if(_php_math_basetozval(*number, frombase, &temp) == FAILURE) {
1050        RETURN_FALSE;
1051    }
1052    result = _php_math_zvaltobase(&temp, tobase TSRMLS_CC);
1053    RETVAL_STRING(result, 0);
1054}
1055/* }}} */
1056
1057/* {{{ _php_math_number_format
1058*/
1059PHPAPI char *_php_math_number_format(double d, int dec, char dec_point, char thousand_sep)
1060{
1061    return _php_math_number_format_ex(d, dec, &dec_point, 1, &thousand_sep, 1);
1062}
1063
1064static char *_php_math_number_format_ex_len(double d, int dec, char *dec_point,
1065        size_t dec_point_len, char *thousand_sep, size_t thousand_sep_len,
1066        int *result_len)
1067{
1068    char *tmpbuf = NULL, *resbuf;
1069    char *s, *t;  /* source, target */
1070    char *dp;
1071    int integral;
1072    int tmplen, reslen=0;
1073    int count=0;
1074    int is_negative=0;
1075
1076    if (d < 0) {
1077        is_negative = 1;
1078        d = -d;
1079    }
1080
1081    dec = MAX(0, dec);
1082    d = _php_math_round(d, dec, PHP_ROUND_HALF_UP);
1083
1084    tmplen = spprintf(&tmpbuf, 0, "%.*F", dec, d);
1085
1086    if (tmpbuf == NULL || !isdigit((int)tmpbuf[0])) {
1087        if (result_len) {
1088            *result_len = tmplen;
1089        }
1090
1091        return tmpbuf;
1092    }
1093
1094    /* find decimal point, if expected */
1095    if (dec) {
1096        dp = strpbrk(tmpbuf, ".,");
1097    } else {
1098        dp = NULL;
1099    }
1100
1101    /* calculate the length of the return buffer */
1102    if (dp) {
1103        integral = dp - tmpbuf;
1104    } else {
1105        /* no decimal point was found */
1106        integral = tmplen;
1107    }
1108
1109    /* allow for thousand separators */
1110    if (thousand_sep) {
1111        integral += thousand_sep_len * ((integral-1) / 3);
1112    }
1113
1114    reslen = integral;
1115
1116    if (dec) {
1117        reslen += dec;
1118
1119        if (dec_point) {
1120            reslen += dec_point_len;
1121        }
1122    }
1123
1124    /* add a byte for minus sign */
1125    if (is_negative) {
1126        reslen++;
1127    }
1128    resbuf = (char *) emalloc(reslen+1); /* +1 for NUL terminator */
1129
1130    s = tmpbuf+tmplen-1;
1131    t = resbuf+reslen;
1132    *t-- = '\0';
1133
1134    /* copy the decimal places.
1135     * Take care, as the sprintf implementation may return less places than
1136     * we requested due to internal buffer limitations */
1137    if (dec) {
1138        int declen = dp ? s - dp : 0;
1139        int topad = dec > declen ? dec - declen : 0;
1140
1141        /* pad with '0's */
1142        while (topad--) {
1143            *t-- = '0';
1144        }
1145
1146        if (dp) {
1147            s -= declen + 1; /* +1 to skip the point */
1148            t -= declen;
1149
1150            /* now copy the chars after the point */
1151            memcpy(t + 1, dp + 1, declen);
1152        }
1153
1154        /* add decimal point */
1155        if (dec_point) {
1156            t -= dec_point_len;
1157            memcpy(t + 1, dec_point, dec_point_len);
1158        }
1159    }
1160
1161    /* copy the numbers before the decimal point, adding thousand
1162     * separator every three digits */
1163    while(s >= tmpbuf) {
1164        *t-- = *s--;
1165        if (thousand_sep && (++count%3)==0 && s>=tmpbuf) {
1166            t -= thousand_sep_len;
1167            memcpy(t + 1, thousand_sep, thousand_sep_len);
1168        }
1169    }
1170
1171    /* and a minus sign, if needed */
1172    if (is_negative) {
1173        *t-- = '-';
1174    }
1175
1176    efree(tmpbuf);
1177
1178    if (result_len) {
1179        *result_len = reslen;
1180    }
1181
1182    return resbuf;
1183}
1184
1185PHPAPI char *_php_math_number_format_ex(double d, int dec, char *dec_point,
1186        size_t dec_point_len, char *thousand_sep, size_t thousand_sep_len)
1187{
1188    return _php_math_number_format_ex_len(d, dec, dec_point, dec_point_len,
1189            thousand_sep, thousand_sep_len, NULL);
1190}
1191/* }}} */
1192
1193/* {{{ proto string number_format(float number [, int num_decimal_places [, string dec_separator, string thousands_separator]])
1194   Formats a number with grouped thousands */
1195PHP_FUNCTION(number_format)
1196{
1197    double num;
1198    long dec = 0;
1199    char *thousand_sep = NULL, *dec_point = NULL;
1200    char thousand_sep_chr = ',', dec_point_chr = '.';
1201    int thousand_sep_len = 0, dec_point_len = 0;
1202
1203    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d|ls!s!", &num, &dec, &dec_point, &dec_point_len, &thousand_sep, &thousand_sep_len) == FAILURE) {
1204        return;
1205    }
1206
1207    switch(ZEND_NUM_ARGS()) {
1208    case 1:
1209        RETURN_STRING(_php_math_number_format(num, 0, dec_point_chr, thousand_sep_chr), 0);
1210        break;
1211    case 2:
1212        RETURN_STRING(_php_math_number_format(num, dec, dec_point_chr, thousand_sep_chr), 0);
1213        break;
1214    case 4:
1215        if (dec_point == NULL) {
1216            dec_point = &dec_point_chr;
1217            dec_point_len = 1;
1218        }
1219
1220        if (thousand_sep == NULL) {
1221            thousand_sep = &thousand_sep_chr;
1222            thousand_sep_len = 1;
1223        }
1224
1225        Z_TYPE_P(return_value) = IS_STRING;
1226        Z_STRVAL_P(return_value) = _php_math_number_format_ex_len(num, dec,
1227                dec_point, dec_point_len, thousand_sep, thousand_sep_len,
1228                &Z_STRLEN_P(return_value));
1229        break;
1230    default:
1231        WRONG_PARAM_COUNT;
1232        break;
1233    }
1234}
1235/* }}} */
1236
1237/* {{{ proto float fmod(float x, float y)
1238   Returns the remainder of dividing x by y as a float */
1239PHP_FUNCTION(fmod)
1240{
1241    double num1, num2;
1242
1243    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "dd",  &num1, &num2) == FAILURE) {
1244        return;
1245    }
1246    RETURN_DOUBLE(fmod(num1, num2));
1247}
1248/* }}} */
1249
1250
1251
1252/*
1253 * Local variables:
1254 * tab-width: 4
1255 * c-basic-offset: 4
1256 * End:
1257 * vim600: fdm=marker
1258 * vim: noet sw=4 ts=4
1259 */
1260