1/*
2   +----------------------------------------------------------------------+
3   | PHP Version 7                                                        |
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# ifdef _WIN64
223    if (z > 0) {
224        return log(z + sqrt(z * z + 1));
225    }
226    else {
227        return -log(-z + sqrt(z * z + 1));
228    }
229# else
230    return(log(z + sqrt(1 + pow(z, 2))) / log(M_E));
231# endif
232#endif
233}
234/* }}} */
235
236/* {{{ php_acosh
237*/
238static double php_acosh(double x)
239{
240#ifdef HAVE_ACOSH
241    return(acosh(x));
242#else
243# ifdef _WIN64
244    if (x >= 1) {
245        return log(x + sqrt(x * x - 1));
246    } else {
247        return (DBL_MAX+DBL_MAX)-(DBL_MAX+DBL_MAX);
248    }
249# else
250    return(log(x + sqrt(x * x - 1)));
251# endif
252#endif
253}
254/* }}} */
255
256/* {{{ php_atanh
257*/
258static double php_atanh(double z)
259{
260#ifdef HAVE_ATANH
261    return(atanh(z));
262#else
263    return(0.5 * log((1 + z) / (1 - z)));
264#endif
265}
266/* }}} */
267
268/* {{{ php_log1p
269*/
270static double php_log1p(double x)
271{
272#ifdef HAVE_LOG1P
273    return(log1p(x));
274#else
275    return(log(1 + x));
276#endif
277}
278/* }}} */
279
280/* {{{ php_expm1
281*/
282static double php_expm1(double x)
283{
284#if !defined(PHP_WIN32) && !defined(NETWARE)
285    return(expm1(x));
286#else
287    return(exp(x) - 1);
288#endif
289}
290/* }}}*/
291
292/* {{{ proto int abs(int number)
293   Return the absolute value of the number */
294PHP_FUNCTION(abs)
295{
296    zval *value;
297
298    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE) {
299        return;
300    }
301    convert_scalar_to_number_ex(value);
302
303    if (Z_TYPE_P(value) == IS_DOUBLE) {
304        RETURN_DOUBLE(fabs(Z_DVAL_P(value)));
305    } else if (Z_TYPE_P(value) == IS_LONG) {
306        if (Z_LVAL_P(value) == ZEND_LONG_MIN) {
307            RETURN_DOUBLE(-(double)ZEND_LONG_MIN);
308        } else {
309            RETURN_LONG(Z_LVAL_P(value) < 0 ? -Z_LVAL_P(value) : Z_LVAL_P(value));
310        }
311    }
312    RETURN_FALSE;
313}
314/* }}} */
315
316/* {{{ proto float ceil(float number)
317   Returns the next highest integer value of the number */
318PHP_FUNCTION(ceil)
319{
320    zval *value;
321
322    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE) {
323        return;
324    }
325    convert_scalar_to_number_ex(value);
326
327    if (Z_TYPE_P(value) == IS_DOUBLE) {
328        RETURN_DOUBLE(ceil(Z_DVAL_P(value)));
329    } else if (Z_TYPE_P(value) == IS_LONG) {
330        RETURN_DOUBLE(zval_get_double(value));
331    }
332    RETURN_FALSE;
333}
334/* }}} */
335
336/* {{{ proto float floor(float number)
337   Returns the next lowest integer value from the number */
338PHP_FUNCTION(floor)
339{
340    zval *value;
341
342    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE) {
343        return;
344    }
345    convert_scalar_to_number_ex(value);
346
347    if (Z_TYPE_P(value) == IS_DOUBLE) {
348        RETURN_DOUBLE(floor(Z_DVAL_P(value)));
349    } else if (Z_TYPE_P(value) == IS_LONG) {
350        RETURN_DOUBLE(zval_get_double(value));
351    }
352    RETURN_FALSE;
353}
354/* }}} */
355
356/* {{{ proto float round(float number [, int precision [, int mode]])
357   Returns the number rounded to specified precision */
358PHP_FUNCTION(round)
359{
360    zval *value;
361    int places = 0;
362    zend_long precision = 0;
363    zend_long mode = PHP_ROUND_HALF_UP;
364    double return_val;
365
366    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|ll", &value, &precision, &mode) == FAILURE) {
367        return;
368    }
369
370    if (ZEND_NUM_ARGS() >= 2) {
371        places = (int) precision;
372    }
373    convert_scalar_to_number_ex(value);
374
375    switch (Z_TYPE_P(value)) {
376        case IS_LONG:
377            /* Simple case - long that doesn't need to be rounded. */
378            if (places >= 0) {
379                RETURN_DOUBLE((double) Z_LVAL_P(value));
380            }
381            /* break omitted intentionally */
382
383        case IS_DOUBLE:
384            return_val = (Z_TYPE_P(value) == IS_LONG) ? (double)Z_LVAL_P(value) : Z_DVAL_P(value);
385            return_val = _php_math_round(return_val, places, mode);
386            RETURN_DOUBLE(return_val);
387            break;
388
389        default:
390            RETURN_FALSE;
391            break;
392    }
393}
394/* }}} */
395
396/* {{{ proto float sin(float number)
397   Returns the sine of the number in radians */
398PHP_FUNCTION(sin)
399{
400    double num;
401
402#ifndef FAST_ZPP
403    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
404        return;
405    }
406#else
407    ZEND_PARSE_PARAMETERS_START(1, 1)
408        Z_PARAM_DOUBLE(num)
409    ZEND_PARSE_PARAMETERS_END();
410#endif
411    RETURN_DOUBLE(sin(num));
412}
413/* }}} */
414
415/* {{{ proto float cos(float number)
416   Returns the cosine of the number in radians */
417PHP_FUNCTION(cos)
418{
419    double num;
420
421#ifndef FAST_ZPP
422    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
423        return;
424    }
425#else
426    ZEND_PARSE_PARAMETERS_START(1, 1)
427        Z_PARAM_DOUBLE(num)
428    ZEND_PARSE_PARAMETERS_END();
429#endif
430    RETURN_DOUBLE(cos(num));
431}
432/* }}} */
433
434/* {{{ proto float tan(float number)
435   Returns the tangent of the number in radians */
436PHP_FUNCTION(tan)
437{
438    double num;
439
440#ifndef FAST_ZPP
441    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
442        return;
443    }
444#else
445    ZEND_PARSE_PARAMETERS_START(1, 1)
446        Z_PARAM_DOUBLE(num)
447    ZEND_PARSE_PARAMETERS_END();
448#endif
449    RETURN_DOUBLE(tan(num));
450}
451/* }}} */
452
453/* {{{ proto float asin(float number)
454   Returns the arc sine of the number in radians */
455PHP_FUNCTION(asin)
456{
457    double num;
458
459#ifndef FAST_ZPP
460    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
461        return;
462    }
463#else
464    ZEND_PARSE_PARAMETERS_START(1, 1)
465        Z_PARAM_DOUBLE(num)
466    ZEND_PARSE_PARAMETERS_END();
467#endif
468    RETURN_DOUBLE(asin(num));
469}
470/* }}} */
471
472/* {{{ proto float acos(float number)
473   Return the arc cosine of the number in radians */
474PHP_FUNCTION(acos)
475{
476    double num;
477
478#ifndef FAST_ZPP
479    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
480        return;
481    }
482#else
483    ZEND_PARSE_PARAMETERS_START(1, 1)
484        Z_PARAM_DOUBLE(num)
485    ZEND_PARSE_PARAMETERS_END();
486#endif
487    RETURN_DOUBLE(acos(num));
488}
489/* }}} */
490
491/* {{{ proto float atan(float number)
492   Returns the arc tangent of the number in radians */
493PHP_FUNCTION(atan)
494{
495    double num;
496
497#ifndef FAST_ZPP
498    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
499        return;
500    }
501#else
502    ZEND_PARSE_PARAMETERS_START(1, 1)
503        Z_PARAM_DOUBLE(num)
504    ZEND_PARSE_PARAMETERS_END();
505#endif
506    RETURN_DOUBLE(atan(num));
507}
508/* }}} */
509
510/* {{{ proto float atan2(float y, float x)
511   Returns the arc tangent of y/x, with the resulting quadrant determined by the signs of y and x */
512PHP_FUNCTION(atan2)
513{
514    double num1, num2;
515
516#ifndef FAST_ZPP
517    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "dd", &num1, &num2) == FAILURE) {
518        return;
519    }
520#else
521    ZEND_PARSE_PARAMETERS_START(2, 2)
522        Z_PARAM_DOUBLE(num1)
523        Z_PARAM_DOUBLE(num2)
524    ZEND_PARSE_PARAMETERS_END();
525#endif
526    RETURN_DOUBLE(atan2(num1, num2));
527}
528/* }}} */
529
530/* {{{ proto float sinh(float number)
531   Returns the hyperbolic sine of the number, defined as (exp(number) - exp(-number))/2 */
532PHP_FUNCTION(sinh)
533{
534    double num;
535
536#ifndef FAST_ZPP
537    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
538        return;
539    }
540#else
541    ZEND_PARSE_PARAMETERS_START(1, 1)
542        Z_PARAM_DOUBLE(num)
543    ZEND_PARSE_PARAMETERS_END();
544#endif
545    RETURN_DOUBLE(sinh(num));
546}
547/* }}} */
548
549/* {{{ proto float cosh(float number)
550   Returns the hyperbolic cosine of the number, defined as (exp(number) + exp(-number))/2 */
551PHP_FUNCTION(cosh)
552{
553    double num;
554
555#ifndef FAST_ZPP
556    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
557        return;
558    }
559#else
560    ZEND_PARSE_PARAMETERS_START(1, 1)
561        Z_PARAM_DOUBLE(num)
562    ZEND_PARSE_PARAMETERS_END();
563#endif
564    RETURN_DOUBLE(cosh(num));
565}
566/* }}} */
567
568/* {{{ proto float tanh(float number)
569   Returns the hyperbolic tangent of the number, defined as sinh(number)/cosh(number) */
570PHP_FUNCTION(tanh)
571{
572    double num;
573
574#ifndef FAST_ZPP
575    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
576        return;
577    }
578#else
579    ZEND_PARSE_PARAMETERS_START(1, 1)
580        Z_PARAM_DOUBLE(num)
581    ZEND_PARSE_PARAMETERS_END();
582#endif
583    RETURN_DOUBLE(tanh(num));
584}
585/* }}} */
586
587/* {{{ proto float asinh(float number)
588   Returns the inverse hyperbolic sine of the number, i.e. the value whose hyperbolic sine is number */
589PHP_FUNCTION(asinh)
590{
591    double num;
592
593#ifndef FAST_ZPP
594    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
595        return;
596    }
597#else
598    ZEND_PARSE_PARAMETERS_START(1, 1)
599        Z_PARAM_DOUBLE(num)
600    ZEND_PARSE_PARAMETERS_END();
601#endif
602    RETURN_DOUBLE(php_asinh(num));
603}
604/* }}} */
605
606/* {{{ proto float acosh(float number)
607   Returns the inverse hyperbolic cosine of the number, i.e. the value whose hyperbolic cosine is number */
608PHP_FUNCTION(acosh)
609{
610    double num;
611
612#ifndef FAST_ZPP
613    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
614        return;
615    }
616#else
617    ZEND_PARSE_PARAMETERS_START(1, 1)
618        Z_PARAM_DOUBLE(num)
619    ZEND_PARSE_PARAMETERS_END();
620#endif
621    RETURN_DOUBLE(php_acosh(num));
622}
623/* }}} */
624
625/* {{{ proto float atanh(float number)
626   Returns the inverse hyperbolic tangent of the number, i.e. the value whose hyperbolic tangent is number */
627PHP_FUNCTION(atanh)
628{
629    double num;
630
631#ifndef FAST_ZPP
632    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
633        return;
634    }
635#else
636    ZEND_PARSE_PARAMETERS_START(1, 1)
637        Z_PARAM_DOUBLE(num)
638    ZEND_PARSE_PARAMETERS_END();
639#endif
640    RETURN_DOUBLE(php_atanh(num));
641}
642/* }}} */
643
644/* {{{ proto float pi(void)
645   Returns an approximation of pi */
646PHP_FUNCTION(pi)
647{
648    RETURN_DOUBLE(M_PI);
649}
650/* }}} */
651
652/* {{{ proto bool is_finite(float val)
653   Returns whether argument is finite */
654PHP_FUNCTION(is_finite)
655{
656    double dval;
657
658#ifndef FAST_ZPP
659    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &dval) == FAILURE) {
660        return;
661    }
662#else
663    ZEND_PARSE_PARAMETERS_START(1, 1)
664        Z_PARAM_DOUBLE(dval)
665    ZEND_PARSE_PARAMETERS_END();
666#endif
667    RETURN_BOOL(zend_finite(dval));
668}
669/* }}} */
670
671/* {{{ proto bool is_infinite(float val)
672   Returns whether argument is infinite */
673PHP_FUNCTION(is_infinite)
674{
675    double dval;
676
677#ifndef FAST_ZPP
678    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &dval) == FAILURE) {
679        return;
680    }
681#else
682    ZEND_PARSE_PARAMETERS_START(1, 1)
683        Z_PARAM_DOUBLE(dval)
684    ZEND_PARSE_PARAMETERS_END();
685#endif
686    RETURN_BOOL(zend_isinf(dval));
687}
688/* }}} */
689
690/* {{{ proto bool is_nan(float val)
691   Returns whether argument is not a number */
692PHP_FUNCTION(is_nan)
693{
694    double dval;
695
696#ifndef FAST_ZPP
697    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &dval) == FAILURE) {
698        return;
699    }
700#else
701    ZEND_PARSE_PARAMETERS_START(1, 1)
702        Z_PARAM_DOUBLE(dval)
703    ZEND_PARSE_PARAMETERS_END();
704#endif
705    RETURN_BOOL(zend_isnan(dval));
706}
707/* }}} */
708
709/* {{{ proto number pow(number base, number exponent)
710   Returns base raised to the power of exponent. Returns integer result when possible */
711PHP_FUNCTION(pow)
712{
713    zval *zbase, *zexp;
714
715    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z/z/", &zbase, &zexp) == FAILURE) {
716        return;
717    }
718
719    pow_function(return_value, zbase, zexp TSRMLS_CC);
720}
721/* }}} */
722
723/* {{{ proto float exp(float number)
724   Returns e raised to the power of the number */
725PHP_FUNCTION(exp)
726{
727    double num;
728
729#ifndef FAST_ZPP
730    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
731        return;
732    }
733#else
734    ZEND_PARSE_PARAMETERS_START(1, 1)
735        Z_PARAM_DOUBLE(num)
736    ZEND_PARSE_PARAMETERS_END();
737#endif
738
739    RETURN_DOUBLE(exp(num));
740}
741/* }}} */
742
743/* {{{ proto float expm1(float number)
744   Returns exp(number) - 1, computed in a way that accurate even when the value of number is close to zero */
745/*
746   WARNING: this function is expermental: it could change its name or
747   disappear in the next version of PHP!
748*/
749PHP_FUNCTION(expm1)
750{
751    double num;
752
753#ifndef FAST_ZPP
754    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
755        return;
756    }
757#else
758    ZEND_PARSE_PARAMETERS_START(1, 1)
759        Z_PARAM_DOUBLE(num)
760    ZEND_PARSE_PARAMETERS_END();
761#endif
762
763    RETURN_DOUBLE(php_expm1(num));
764}
765/* }}} */
766
767/* {{{ proto float log1p(float number)
768   Returns log(1 + number), computed in a way that accurate even when the value of number is close to zero */
769/*
770   WARNING: this function is expermental: it could change its name or
771   disappear in the next version of PHP!
772*/
773PHP_FUNCTION(log1p)
774{
775    double num;
776
777#ifndef FAST_ZPP
778    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
779        return;
780    }
781#else
782    ZEND_PARSE_PARAMETERS_START(1, 1)
783        Z_PARAM_DOUBLE(num)
784    ZEND_PARSE_PARAMETERS_END();
785#endif
786
787    RETURN_DOUBLE(php_log1p(num));
788}
789/* }}} */
790
791/* {{{ proto float log(float number, [float base])
792   Returns the natural logarithm of the number, or the base log if base is specified */
793PHP_FUNCTION(log)
794{
795    double num, base = 0;
796
797#ifndef FAST_ZPP
798    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d|d", &num, &base) == FAILURE) {
799        return;
800    }
801#else
802    ZEND_PARSE_PARAMETERS_START(1, 2)
803        Z_PARAM_DOUBLE(num)
804        Z_PARAM_OPTIONAL
805        Z_PARAM_DOUBLE(base)
806    ZEND_PARSE_PARAMETERS_END();
807#endif
808
809    if (ZEND_NUM_ARGS() == 1) {
810        RETURN_DOUBLE(log(num));
811    }
812
813#ifdef HAVE_LOG2
814    if (base == 2.0) {
815        RETURN_DOUBLE(log2(num));
816    }
817#endif
818
819    if (base == 10.0) {
820        RETURN_DOUBLE(log10(num));
821    }
822
823    if (base == 1.0) {
824        RETURN_DOUBLE(php_get_nan());
825    }
826
827    if (base <= 0.0) {
828        php_error_docref(NULL TSRMLS_CC, E_WARNING, "base must be greater than 0");
829        RETURN_FALSE;
830    }
831
832    RETURN_DOUBLE(log(num) / log(base));
833}
834/* }}} */
835
836/* {{{ proto float log10(float number)
837   Returns the base-10 logarithm of the number */
838PHP_FUNCTION(log10)
839{
840    double num;
841
842#ifndef FAST_ZPP
843    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
844        return;
845    }
846#else
847    ZEND_PARSE_PARAMETERS_START(1, 1)
848        Z_PARAM_DOUBLE(num)
849    ZEND_PARSE_PARAMETERS_END();
850#endif
851
852    RETURN_DOUBLE(log10(num));
853}
854/* }}} */
855
856/* {{{ proto float sqrt(float number)
857   Returns the square root of the number */
858PHP_FUNCTION(sqrt)
859{
860    double num;
861
862#ifndef FAST_ZPP
863    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
864        return;
865    }
866#else
867    ZEND_PARSE_PARAMETERS_START(1, 1)
868        Z_PARAM_DOUBLE(num)
869    ZEND_PARSE_PARAMETERS_END();
870#endif
871
872    RETURN_DOUBLE(sqrt(num));
873}
874/* }}} */
875
876/* {{{ proto float hypot(float num1, float num2)
877   Returns sqrt(num1*num1 + num2*num2) */
878PHP_FUNCTION(hypot)
879{
880    double num1, num2;
881
882#ifndef FAST_ZPP
883    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "dd", &num1, &num2) == FAILURE) {
884        return;
885    }
886#else
887    ZEND_PARSE_PARAMETERS_START(2, 2)
888        Z_PARAM_DOUBLE(num1)
889        Z_PARAM_DOUBLE(num2)
890    ZEND_PARSE_PARAMETERS_END();
891#endif
892
893#if HAVE_HYPOT
894    RETURN_DOUBLE(hypot(num1, num2));
895#elif defined(_MSC_VER)
896    RETURN_DOUBLE(_hypot(num1, num2));
897#else
898    RETURN_DOUBLE(sqrt((num1 * num1) + (num2 * num2)));
899#endif
900}
901/* }}} */
902
903/* {{{ proto float deg2rad(float number)
904   Converts the number in degrees to the radian equivalent */
905PHP_FUNCTION(deg2rad)
906{
907    double deg;
908
909#ifndef FAST_ZPP
910    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &deg) == FAILURE) {
911        return;
912    }
913#else
914    ZEND_PARSE_PARAMETERS_START(1, 1)
915        Z_PARAM_DOUBLE(deg)
916    ZEND_PARSE_PARAMETERS_END();
917#endif
918    RETURN_DOUBLE((deg / 180.0) * M_PI);
919}
920/* }}} */
921
922/* {{{ proto float rad2deg(float number)
923   Converts the radian number to the equivalent number in degrees */
924PHP_FUNCTION(rad2deg)
925{
926    double rad;
927
928#ifndef FAST_ZPP
929    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &rad) == FAILURE) {
930        return;
931    }
932#else
933    ZEND_PARSE_PARAMETERS_START(1, 1)
934        Z_PARAM_DOUBLE(rad)
935    ZEND_PARSE_PARAMETERS_END();
936#endif
937
938    RETURN_DOUBLE((rad / M_PI) * 180);
939}
940/* }}} */
941
942/* {{{ _php_math_basetolong */
943/*
944 * Convert a string representation of a base(2-36) number to a long.
945 */
946PHPAPI zend_long _php_math_basetolong(zval *arg, int base)
947{
948    zend_long num = 0, digit, onum;
949    int i;
950    char c, *s;
951
952    if (Z_TYPE_P(arg) != IS_STRING || base < 2 || base > 36) {
953        return 0;
954    }
955
956    s = Z_STRVAL_P(arg);
957
958    for (i = Z_STRLEN_P(arg); i > 0; i--) {
959        c = *s++;
960
961        digit = (c >= '0' && c <= '9') ? c - '0'
962            : (c >= 'A' && c <= 'Z') ? c - 'A' + 10
963            : (c >= 'a' && c <= 'z') ? c - 'a' + 10
964            : base;
965
966        if (digit >= base) {
967            continue;
968        }
969
970        onum = num;
971        num = num * base + digit;
972        if (num > onum)
973            continue;
974
975        {
976            TSRMLS_FETCH();
977
978            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Number '%s' is too big to fit in long", s);
979            return ZEND_LONG_MAX;
980        }
981    }
982
983    return num;
984}
985/* }}} */
986
987/* {{{ _php_math_basetozval */
988/*
989 * Convert a string representation of a base(2-36) number to a zval.
990 */
991PHPAPI int _php_math_basetozval(zval *arg, int base, zval *ret)
992{
993    zend_long num = 0;
994    double fnum = 0;
995    int i;
996    int mode = 0;
997    char c, *s;
998    zend_long cutoff;
999    int cutlim;
1000
1001    if (Z_TYPE_P(arg) != IS_STRING || base < 2 || base > 36) {
1002        return FAILURE;
1003    }
1004
1005    s = Z_STRVAL_P(arg);
1006
1007    cutoff = ZEND_LONG_MAX / base;
1008    cutlim = ZEND_LONG_MAX % base;
1009
1010    for (i = Z_STRLEN_P(arg); i > 0; i--) {
1011        c = *s++;
1012
1013        /* might not work for EBCDIC */
1014        if (c >= '0' && c <= '9')
1015            c -= '0';
1016        else if (c >= 'A' && c <= 'Z')
1017            c -= 'A' - 10;
1018        else if (c >= 'a' && c <= 'z')
1019            c -= 'a' - 10;
1020        else
1021            continue;
1022
1023        if (c >= base)
1024            continue;
1025
1026        switch (mode) {
1027        case 0: /* Integer */
1028            if (num < cutoff || (num == cutoff && c <= cutlim)) {
1029                num = num * base + c;
1030                break;
1031            } else {
1032                fnum = num;
1033                mode = 1;
1034            }
1035            /* fall-through */
1036        case 1: /* Float */
1037            fnum = fnum * base + c;
1038        }
1039    }
1040
1041    if (mode == 1) {
1042        ZVAL_DOUBLE(ret, fnum);
1043    } else {
1044        ZVAL_LONG(ret, num);
1045    }
1046    return SUCCESS;
1047}
1048/* }}} */
1049
1050/* {{{ _php_math_longtobase */
1051/*
1052 * Convert a long to a string containing a base(2-36) representation of
1053 * the number.
1054 */
1055PHPAPI zend_string * _php_math_longtobase(zval *arg, int base TSRMLS_DC)
1056{
1057    static char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
1058    char buf[(sizeof(zend_ulong) << 3) + 1];
1059    char *ptr, *end;
1060    zend_ulong value;
1061
1062    if (Z_TYPE_P(arg) != IS_LONG || base < 2 || base > 36) {
1063        return STR_EMPTY_ALLOC();
1064    }
1065
1066    value = Z_LVAL_P(arg);
1067
1068    end = ptr = buf + sizeof(buf) - 1;
1069    *ptr = '\0';
1070
1071    do {
1072        *--ptr = digits[value % base];
1073        value /= base;
1074    } while (ptr > buf && value);
1075
1076    return zend_string_init(ptr, end - ptr, 0);
1077}
1078/* }}} */
1079
1080/* {{{ _php_math_zvaltobase */
1081/*
1082 * Convert a zval to a string containing a base(2-36) representation of
1083 * the number.
1084 */
1085PHPAPI zend_string * _php_math_zvaltobase(zval *arg, int base TSRMLS_DC)
1086{
1087    static char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
1088
1089    if ((Z_TYPE_P(arg) != IS_LONG && Z_TYPE_P(arg) != IS_DOUBLE) || base < 2 || base > 36) {
1090        return STR_EMPTY_ALLOC();
1091    }
1092
1093    if (Z_TYPE_P(arg) == IS_DOUBLE) {
1094        double fvalue = floor(Z_DVAL_P(arg)); /* floor it just in case */
1095        char *ptr, *end;
1096        char buf[(sizeof(double) << 3) + 1];
1097
1098        /* Don't try to convert +/- infinity */
1099        if (fvalue == HUGE_VAL || fvalue == -HUGE_VAL) {
1100            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Number too large");
1101            return STR_EMPTY_ALLOC();
1102        }
1103
1104        end = ptr = buf + sizeof(buf) - 1;
1105        *ptr = '\0';
1106
1107        do {
1108            *--ptr = digits[(int) fmod(fvalue, base)];
1109            fvalue /= base;
1110        } while (ptr > buf && fabs(fvalue) >= 1);
1111
1112        return zend_string_init(ptr, end - ptr, 0);
1113    }
1114
1115    return _php_math_longtobase(arg, base TSRMLS_CC);
1116}
1117/* }}} */
1118
1119/* {{{ proto int bindec(string binary_number)
1120   Returns the decimal equivalent of the binary number */
1121PHP_FUNCTION(bindec)
1122{
1123    zval *arg;
1124
1125    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &arg) == FAILURE) {
1126        return;
1127    }
1128    convert_to_string_ex(arg);
1129    if (_php_math_basetozval(arg, 2, return_value) == FAILURE) {
1130        RETURN_FALSE;
1131    }
1132}
1133/* }}} */
1134
1135/* {{{ proto int hexdec(string hexadecimal_number)
1136   Returns the decimal equivalent of the hexadecimal number */
1137PHP_FUNCTION(hexdec)
1138{
1139    zval *arg;
1140
1141    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &arg) == FAILURE) {
1142        return;
1143    }
1144    convert_to_string_ex(arg);
1145    if (_php_math_basetozval(arg, 16, return_value) == FAILURE) {
1146        RETURN_FALSE;
1147    }
1148}
1149/* }}} */
1150
1151/* {{{ proto int octdec(string octal_number)
1152   Returns the decimal equivalent of an octal string */
1153PHP_FUNCTION(octdec)
1154{
1155    zval *arg;
1156
1157    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &arg) == FAILURE) {
1158        return;
1159    }
1160    convert_to_string_ex(arg);
1161    if (_php_math_basetozval(arg, 8, return_value) == FAILURE) {
1162        RETURN_FALSE;
1163    }
1164}
1165/* }}} */
1166
1167/* {{{ proto string decbin(int decimal_number)
1168   Returns a string containing a binary representation of the number */
1169PHP_FUNCTION(decbin)
1170{
1171    zval *arg;
1172    zend_string *result;
1173
1174    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &arg) == FAILURE) {
1175        return;
1176    }
1177    convert_to_long_ex(arg);
1178    result = _php_math_longtobase(arg, 2 TSRMLS_CC);
1179    RETURN_STR(result);
1180}
1181/* }}} */
1182
1183/* {{{ proto string decoct(int decimal_number)
1184   Returns a string containing an octal representation of the given number */
1185PHP_FUNCTION(decoct)
1186{
1187    zval *arg;
1188    zend_string *result;
1189
1190    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &arg) == FAILURE) {
1191        return;
1192    }
1193    convert_to_long_ex(arg);
1194    result = _php_math_longtobase(arg, 8 TSRMLS_CC);
1195    RETURN_STR(result);
1196}
1197/* }}} */
1198
1199/* {{{ proto string dechex(int decimal_number)
1200   Returns a string containing a hexadecimal representation of the given number */
1201PHP_FUNCTION(dechex)
1202{
1203    zval *arg;
1204    zend_string *result;
1205
1206    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &arg) == FAILURE) {
1207        return;
1208    }
1209    convert_to_long_ex(arg);
1210    result = _php_math_longtobase(arg, 16 TSRMLS_CC);
1211    RETURN_STR(result);
1212}
1213/* }}} */
1214
1215/* {{{ proto string base_convert(string number, int frombase, int tobase)
1216   Converts a number in a string from any base <= 36 to any base <= 36 */
1217PHP_FUNCTION(base_convert)
1218{
1219    zval *number, temp;
1220    zend_long frombase, tobase;
1221    zend_string *result;
1222
1223    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zll", &number, &frombase, &tobase) == FAILURE) {
1224        return;
1225    }
1226    convert_to_string_ex(number);
1227
1228    if (frombase < 2 || frombase > 36) {
1229        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid `from base' (%pd)", frombase);
1230        RETURN_FALSE;
1231    }
1232    if (tobase < 2 || tobase > 36) {
1233        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid `to base' (%pd)", tobase);
1234        RETURN_FALSE;
1235    }
1236
1237    if(_php_math_basetozval(number, frombase, &temp) == FAILURE) {
1238        RETURN_FALSE;
1239    }
1240    result = _php_math_zvaltobase(&temp, tobase TSRMLS_CC);
1241    RETVAL_STR(result);
1242}
1243/* }}} */
1244
1245/* {{{ _php_math_number_format
1246*/
1247PHPAPI zend_string *_php_math_number_format(double d, int dec, char dec_point, char thousand_sep)
1248{
1249    return _php_math_number_format_ex(d, dec, &dec_point, 1, &thousand_sep, 1);
1250}
1251
1252PHPAPI zend_string *_php_math_number_format_ex(double d, int dec, char *dec_point,
1253        size_t dec_point_len, char *thousand_sep, size_t thousand_sep_len)
1254{
1255    zend_string *res;
1256    zend_string *tmpbuf;
1257    char *s, *t;  /* source, target */
1258    char *dp;
1259    int integral;
1260    int reslen = 0;
1261    int count = 0;
1262    int is_negative=0;
1263
1264    if (d < 0) {
1265        is_negative = 1;
1266        d = -d;
1267    }
1268
1269    dec = MAX(0, dec);
1270    d = _php_math_round(d, dec, PHP_ROUND_HALF_UP);
1271    tmpbuf = strpprintf(0, "%.*F", dec, d);
1272    if (tmpbuf == NULL) {
1273        return NULL;
1274    } else if (!isdigit((int)tmpbuf->val[0])) {
1275        return tmpbuf;
1276    }
1277
1278    /* find decimal point, if expected */
1279    if (dec) {
1280        dp = strpbrk(tmpbuf->val, ".,");
1281    } else {
1282        dp = NULL;
1283    }
1284
1285    /* calculate the length of the return buffer */
1286    if (dp) {
1287        integral = dp - tmpbuf->val;
1288    } else {
1289        /* no decimal point was found */
1290        integral = tmpbuf->len;
1291    }
1292
1293    /* allow for thousand separators */
1294    if (thousand_sep) {
1295        integral += thousand_sep_len * ((integral-1) / 3);
1296    }
1297
1298    reslen = integral;
1299
1300    if (dec) {
1301        reslen += dec;
1302
1303        if (dec_point) {
1304            reslen += dec_point_len;
1305        }
1306    }
1307
1308    /* add a byte for minus sign */
1309    if (is_negative) {
1310        reslen++;
1311    }
1312    res = zend_string_alloc(reslen, 0);
1313
1314    s = tmpbuf->val + tmpbuf->len - 1;
1315    t = res->val + reslen;
1316    *t-- = '\0';
1317
1318    /* copy the decimal places.
1319     * Take care, as the sprintf implementation may return less places than
1320     * we requested due to internal buffer limitations */
1321    if (dec) {
1322        int declen = dp ? s - dp : 0;
1323        int topad = dec > declen ? dec - declen : 0;
1324
1325        /* pad with '0's */
1326        while (topad--) {
1327            *t-- = '0';
1328        }
1329
1330        if (dp) {
1331            s -= declen + 1; /* +1 to skip the point */
1332            t -= declen;
1333
1334            /* now copy the chars after the point */
1335            memcpy(t + 1, dp + 1, declen);
1336        }
1337
1338        /* add decimal point */
1339        if (dec_point) {
1340            t -= dec_point_len;
1341            memcpy(t + 1, dec_point, dec_point_len);
1342        }
1343    }
1344
1345    /* copy the numbers before the decimal point, adding thousand
1346     * separator every three digits */
1347    while (s >= tmpbuf->val) {
1348        *t-- = *s--;
1349        if (thousand_sep && (++count%3)==0 && s>=tmpbuf->val) {
1350            t -= thousand_sep_len;
1351            memcpy(t + 1, thousand_sep, thousand_sep_len);
1352        }
1353    }
1354
1355    /* and a minus sign, if needed */
1356    if (is_negative) {
1357        *t-- = '-';
1358    }
1359
1360    res->len = reslen;
1361    zend_string_release(tmpbuf);
1362    return res;
1363}
1364
1365/* {{{ proto string number_format(float number [, int num_decimal_places [, string dec_separator, string thousands_separator]])
1366   Formats a number with grouped thousands */
1367PHP_FUNCTION(number_format)
1368{
1369    double num;
1370    zend_long dec = 0;
1371    char *thousand_sep = NULL, *dec_point = NULL;
1372    char thousand_sep_chr = ',', dec_point_chr = '.';
1373    size_t thousand_sep_len = 0, dec_point_len = 0;
1374
1375#ifndef FAST_ZPP
1376    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) {
1377        return;
1378    }
1379#else
1380    ZEND_PARSE_PARAMETERS_START(1, 4)
1381        Z_PARAM_DOUBLE(num)
1382        Z_PARAM_OPTIONAL
1383        Z_PARAM_LONG(dec)
1384        Z_PARAM_STRING_EX(dec_point, dec_point_len, 1, 0)
1385        Z_PARAM_STRING_EX(thousand_sep, thousand_sep_len, 1, 0)
1386    ZEND_PARSE_PARAMETERS_END();
1387#endif
1388
1389    switch(ZEND_NUM_ARGS()) {
1390    case 1:
1391        RETURN_STR(_php_math_number_format(num, 0, dec_point_chr, thousand_sep_chr));
1392        break;
1393    case 2:
1394        RETURN_STR(_php_math_number_format(num, dec, dec_point_chr, thousand_sep_chr));
1395        break;
1396    case 4:
1397        if (dec_point == NULL) {
1398            dec_point = &dec_point_chr;
1399            dec_point_len = 1;
1400        }
1401
1402        if (thousand_sep == NULL) {
1403            thousand_sep = &thousand_sep_chr;
1404            thousand_sep_len = 1;
1405        }
1406
1407        RETVAL_STR(_php_math_number_format_ex(num, dec,
1408                dec_point, dec_point_len, thousand_sep, thousand_sep_len));
1409        break;
1410    default:
1411        WRONG_PARAM_COUNT;
1412        break;
1413    }
1414}
1415/* }}} */
1416
1417/* {{{ proto float fmod(float x, float y)
1418   Returns the remainder of dividing x by y as a float */
1419PHP_FUNCTION(fmod)
1420{
1421    double num1, num2;
1422
1423#ifndef FAST_ZPP
1424    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "dd",  &num1, &num2) == FAILURE) {
1425        return;
1426    }
1427#else
1428    ZEND_PARSE_PARAMETERS_START(2, 2)
1429        Z_PARAM_DOUBLE(num1)
1430        Z_PARAM_DOUBLE(num2)
1431    ZEND_PARSE_PARAMETERS_END();
1432#endif
1433
1434    RETURN_DOUBLE(fmod(num1, num2));
1435}
1436/* }}} */
1437
1438/* {{{ proto int intdiv(int numerator, int divisor)
1439   Returns the integer division of the numerator by the divisor */
1440PHP_FUNCTION(intdiv)
1441{
1442    zend_long numerator, divisor;
1443
1444    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ll", &numerator, &divisor) == FAILURE) {
1445        return;
1446    }
1447
1448    if (divisor == 0) {
1449        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Division by zero");
1450        RETURN_BOOL(0);
1451    } else if (divisor == -1 && numerator == ZEND_LONG_MIN) {
1452        /* Prevent overflow error/crash
1453           We don't return a float here as that violates function contract */
1454        RETURN_LONG(0);
1455    }
1456
1457    RETURN_LONG(numerator/divisor);
1458}
1459/* }}} */
1460
1461/*
1462 * Local variables:
1463 * tab-width: 4
1464 * c-basic-offset: 4
1465 * End:
1466 * vim600: fdm=marker
1467 * vim: noet sw=4 ts=4
1468 */
1469