LCOV - code coverage report
Current view: top level - py - modmath.c (source / functions) Hit Total Coverage
Test: unix_coverage_v1.24.0-148-g136058496.info Lines: 124 124 100.0 %
Date: 2024-12-20 19:05:39 Functions: 46 46 100.0 %
Branches: 56 56 100.0 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * This file is part of the MicroPython project, http://micropython.org/
       3                 :            :  *
       4                 :            :  * The MIT License (MIT)
       5                 :            :  *
       6                 :            :  * Copyright (c) 2013-2017 Damien P. George
       7                 :            :  *
       8                 :            :  * Permission is hereby granted, free of charge, to any person obtaining a copy
       9                 :            :  * of this software and associated documentation files (the "Software"), to deal
      10                 :            :  * in the Software without restriction, including without limitation the rights
      11                 :            :  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      12                 :            :  * copies of the Software, and to permit persons to whom the Software is
      13                 :            :  * furnished to do so, subject to the following conditions:
      14                 :            :  *
      15                 :            :  * The above copyright notice and this permission notice shall be included in
      16                 :            :  * all copies or substantial portions of the Software.
      17                 :            :  *
      18                 :            :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      19                 :            :  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      20                 :            :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      21                 :            :  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      22                 :            :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      23                 :            :  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
      24                 :            :  * THE SOFTWARE.
      25                 :            :  */
      26                 :            : 
      27                 :            : #include "py/builtin.h"
      28                 :            : #include "py/runtime.h"
      29                 :            : 
      30                 :            : #if MICROPY_PY_BUILTINS_FLOAT && MICROPY_PY_MATH
      31                 :            : 
      32                 :            : #include <math.h>
      33                 :            : 
      34                 :            : // M_PI is not part of the math.h standard and may not be defined
      35                 :            : // And by defining our own we can ensure it uses the correct const format.
      36                 :            : #define MP_PI MICROPY_FLOAT_CONST(3.14159265358979323846)
      37                 :            : #define MP_PI_4 MICROPY_FLOAT_CONST(0.78539816339744830962)
      38                 :            : #define MP_3_PI_4 MICROPY_FLOAT_CONST(2.35619449019234492885)
      39                 :            : 
      40                 :        420 : static NORETURN void math_error(void) {
      41                 :        420 :     mp_raise_ValueError(MP_ERROR_TEXT("math domain error"));
      42                 :            : }
      43                 :            : 
      44                 :       1212 : static mp_obj_t math_generic_1(mp_obj_t x_obj, mp_float_t (*f)(mp_float_t)) {
      45                 :       1212 :     mp_float_t x = mp_obj_get_float(x_obj);
      46                 :       1212 :     mp_float_t ans = f(x);
      47   [ +  +  +  +  :       1212 :     if ((isnan(ans) && !isnan(x)) || (isinf(ans) && !isinf(x))) {
                   +  + ]
      48                 :        204 :         math_error();
      49                 :            :     }
      50                 :       1008 :     return mp_obj_new_float(ans);
      51                 :            : }
      52                 :            : 
      53                 :        660 : static mp_obj_t math_generic_2(mp_obj_t x_obj, mp_obj_t y_obj, mp_float_t (*f)(mp_float_t, mp_float_t)) {
      54                 :        660 :     mp_float_t x = mp_obj_get_float(x_obj);
      55                 :        660 :     mp_float_t y = mp_obj_get_float(y_obj);
      56                 :        660 :     mp_float_t ans = f(x, y);
      57   [ +  +  +  +  :        660 :     if ((isnan(ans) && !isnan(x) && !isnan(y)) || (isinf(ans) && !isinf(x) && !isinf(y))) {
          +  +  +  +  +  
                      + ]
      58                 :         52 :         math_error();
      59                 :            :     }
      60                 :        608 :     return mp_obj_new_float(ans);
      61                 :            : }
      62                 :            : 
      63                 :            : #define MATH_FUN_1(py_name, c_name) \
      64                 :            :     static mp_obj_t mp_math_##py_name(mp_obj_t x_obj) { \
      65                 :            :         return math_generic_1(x_obj, MICROPY_FLOAT_C_FUN(c_name)); \
      66                 :            :     } \
      67                 :            :     static MP_DEFINE_CONST_FUN_OBJ_1(mp_math_##py_name##_obj, mp_math_##py_name);
      68                 :            : 
      69                 :            : #define MATH_FUN_1_TO_BOOL(py_name, c_name) \
      70                 :            :     static mp_obj_t mp_math_##py_name(mp_obj_t x_obj) { return mp_obj_new_bool(c_name(mp_obj_get_float(x_obj))); } \
      71                 :            :     static MP_DEFINE_CONST_FUN_OBJ_1(mp_math_##py_name##_obj, mp_math_##py_name);
      72                 :            : 
      73                 :            : #define MATH_FUN_1_TO_INT(py_name, c_name) \
      74                 :            :     static mp_obj_t mp_math_##py_name(mp_obj_t x_obj) { return mp_obj_new_int_from_float(MICROPY_FLOAT_C_FUN(c_name)(mp_obj_get_float(x_obj))); } \
      75                 :            :     static MP_DEFINE_CONST_FUN_OBJ_1(mp_math_##py_name##_obj, mp_math_##py_name);
      76                 :            : 
      77                 :            : #define MATH_FUN_2(py_name, c_name) \
      78                 :            :     static mp_obj_t mp_math_##py_name(mp_obj_t x_obj, mp_obj_t y_obj) { \
      79                 :            :         return math_generic_2(x_obj, y_obj, MICROPY_FLOAT_C_FUN(c_name)); \
      80                 :            :     } \
      81                 :            :     static MP_DEFINE_CONST_FUN_OBJ_2(mp_math_##py_name##_obj, mp_math_##py_name);
      82                 :            : 
      83                 :            : #define MATH_FUN_2_FLT_INT(py_name, c_name) \
      84                 :            :     static mp_obj_t mp_math_##py_name(mp_obj_t x_obj, mp_obj_t y_obj) { \
      85                 :            :         return mp_obj_new_float(MICROPY_FLOAT_C_FUN(c_name)(mp_obj_get_float(x_obj), mp_obj_get_int(y_obj))); \
      86                 :            :     } \
      87                 :            :     static MP_DEFINE_CONST_FUN_OBJ_2(mp_math_##py_name##_obj, mp_math_##py_name);
      88                 :            : 
      89                 :            : #if MP_NEED_LOG2
      90                 :            : #undef log2
      91                 :            : #undef log2f
      92                 :            : // 1.442695040888963407354163704 is 1/_M_LN2
      93                 :            : mp_float_t MICROPY_FLOAT_C_FUN(log2)(mp_float_t x) {
      94                 :            :     return MICROPY_FLOAT_C_FUN(log)(x) * MICROPY_FLOAT_CONST(1.442695040888963407354163704);
      95                 :            : }
      96                 :            : #endif
      97                 :            : 
      98                 :            : // sqrt(x): returns the square root of x
      99                 :         64 : MATH_FUN_1(sqrt, sqrt)
     100                 :            : // pow(x, y): returns x to the power of y
     101                 :            : #if MICROPY_PY_MATH_POW_FIX_NAN
     102                 :            : mp_float_t pow_func(mp_float_t x, mp_float_t y) {
     103                 :            :     // pow(base, 0) returns 1 for any base, even when base is NaN
     104                 :            :     // pow(+1, exponent) returns 1 for any exponent, even when exponent is NaN
     105                 :            :     if (x == MICROPY_FLOAT_CONST(1.0) || y == MICROPY_FLOAT_CONST(0.0)) {
     106                 :            :         return MICROPY_FLOAT_CONST(1.0);
     107                 :            :     }
     108                 :            :     return MICROPY_FLOAT_C_FUN(pow)(x, y);
     109                 :            : }
     110                 :            : MATH_FUN_2(pow, pow_func)
     111                 :            : #else
     112                 :        164 : MATH_FUN_2(pow, pow)
     113                 :            : #endif
     114                 :            : // exp(x)
     115                 :         84 : MATH_FUN_1(exp, exp)
     116                 :            : #if MICROPY_PY_MATH_SPECIAL_FUNCTIONS
     117                 :            : // expm1(x)
     118                 :         32 : MATH_FUN_1(expm1, expm1)
     119                 :            : // log2(x)
     120                 :         52 : MATH_FUN_1(log2, log2)
     121                 :            : // log10(x)
     122                 :         52 : MATH_FUN_1(log10, log10)
     123                 :            : // cosh(x)
     124                 :         44 : MATH_FUN_1(cosh, cosh)
     125                 :            : // sinh(x)
     126                 :         44 : MATH_FUN_1(sinh, sinh)
     127                 :            : // tanh(x)
     128                 :         60 : MATH_FUN_1(tanh, tanh)
     129                 :            : // acosh(x)
     130                 :         36 : MATH_FUN_1(acosh, acosh)
     131                 :            : // asinh(x)
     132                 :         44 : MATH_FUN_1(asinh, asinh)
     133                 :            : // atanh(x)
     134                 :         40 : MATH_FUN_1(atanh, atanh)
     135                 :            : #endif
     136                 :            : // cos(x)
     137                 :         68 : MATH_FUN_1(cos, cos)
     138                 :            : // sin(x)
     139                 :         64 : MATH_FUN_1(sin, sin)
     140                 :            : // tan(x)
     141                 :         64 : MATH_FUN_1(tan, tan)
     142                 :            : // acos(x)
     143                 :         68 : MATH_FUN_1(acos, acos)
     144                 :            : // asin(x)
     145                 :         68 : MATH_FUN_1(asin, asin)
     146                 :            : // atan(x)
     147                 :         64 : MATH_FUN_1(atan, atan)
     148                 :            : // atan2(y, x)
     149                 :            : #if MICROPY_PY_MATH_ATAN2_FIX_INFNAN
     150                 :            : mp_float_t atan2_func(mp_float_t x, mp_float_t y) {
     151                 :            :     if (isinf(x) && isinf(y)) {
     152                 :            :         return copysign(y < 0 ? MP_3_PI_4 : MP_PI_4, x);
     153                 :            :     }
     154                 :            :     return atan2(x, y);
     155                 :            : }
     156                 :            : MATH_FUN_2(atan2, atan2_func)
     157                 :            : #else
     158                 :        164 : MATH_FUN_2(atan2, atan2)
     159                 :            : #endif
     160                 :            : // ceil(x)
     161                 :        100 : MATH_FUN_1_TO_INT(ceil, ceil)
     162                 :            : // copysign(x, y)
     163                 :        168 : static mp_float_t MICROPY_FLOAT_C_FUN(copysign_func)(mp_float_t x, mp_float_t y) {
     164                 :        168 :     return MICROPY_FLOAT_C_FUN(copysign)(x, y);
     165                 :            : }
     166                 :        168 : MATH_FUN_2(copysign, copysign_func)
     167                 :            : // fabs(x)
     168                 :         64 : static mp_float_t MICROPY_FLOAT_C_FUN(fabs_func)(mp_float_t x) {
     169                 :         64 :     return MICROPY_FLOAT_C_FUN(fabs)(x);
     170                 :            : }
     171                 :         64 : MATH_FUN_1(fabs, fabs_func)
     172                 :            : // floor(x)
     173                 :        100 : MATH_FUN_1_TO_INT(floor, floor) // TODO: delegate to x.__floor__() if x is not a float
     174                 :            : // fmod(x, y)
     175                 :            : #if MICROPY_PY_MATH_FMOD_FIX_INFNAN
     176                 :            : mp_float_t fmod_func(mp_float_t x, mp_float_t y) {
     177                 :            :     return (!isinf(x) && isinf(y)) ? x : fmod(x, y);
     178                 :            : }
     179                 :            : MATH_FUN_2(fmod, fmod_func)
     180                 :            : #else
     181                 :        164 : MATH_FUN_2(fmod, fmod)
     182                 :            : #endif
     183                 :            : // isfinite(x)
     184         [ +  + ]:         40 : MATH_FUN_1_TO_BOOL(isfinite, isfinite)
     185                 :            : // isinf(x)
     186         [ +  + ]:         40 : MATH_FUN_1_TO_BOOL(isinf, isinf)
     187                 :            : // isnan(x)
     188         [ +  + ]:         48 : MATH_FUN_1_TO_BOOL(isnan, isnan)
     189                 :            : // trunc(x)
     190                 :        100 : MATH_FUN_1_TO_INT(trunc, trunc)
     191                 :            : // ldexp(x, exp)
     192                 :         52 : MATH_FUN_2_FLT_INT(ldexp, ldexp)
     193                 :            : #if MICROPY_PY_MATH_SPECIAL_FUNCTIONS
     194                 :            : // erf(x): return the error function of x
     195                 :         44 : MATH_FUN_1(erf, erf)
     196                 :            : // erfc(x): return the complementary error function of x
     197                 :         44 : MATH_FUN_1(erfc, erfc)
     198                 :            : // gamma(x): return the gamma function of x
     199                 :            : #if MICROPY_PY_MATH_GAMMA_FIX_NEGINF
     200                 :            : static mp_float_t MICROPY_FLOAT_C_FUN(tgamma_func)(mp_float_t x) {
     201                 :            :     if (isinf(x) && x < 0) {
     202                 :            :         math_error();
     203                 :            :     }
     204                 :            :     return MICROPY_FLOAT_C_FUN(tgamma)(x);
     205                 :            : }
     206                 :            : MATH_FUN_1(gamma, tgamma_func)
     207                 :            : #else
     208                 :         52 : MATH_FUN_1(gamma, tgamma)
     209                 :            : #endif
     210                 :            : // lgamma(x): return the natural logarithm of the gamma function of x
     211                 :         60 : MATH_FUN_1(lgamma, lgamma)
     212                 :            : #endif
     213                 :            : // TODO: fsum
     214                 :            : 
     215                 :            : #if MICROPY_PY_MATH_ISCLOSE
     216                 :        108 : static mp_obj_t mp_math_isclose(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
     217                 :        108 :     enum { ARG_rel_tol, ARG_abs_tol };
     218                 :        108 :     static const mp_arg_t allowed_args[] = {
     219                 :            :         {MP_QSTR_rel_tol, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}},
     220                 :            :         {MP_QSTR_abs_tol, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NEW_SMALL_INT(0)}},
     221                 :            :     };
     222                 :        108 :     mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
     223                 :        108 :     mp_arg_parse_all(n_args - 2, pos_args + 2, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
     224                 :        108 :     const mp_float_t a = mp_obj_get_float(pos_args[0]);
     225                 :        108 :     const mp_float_t b = mp_obj_get_float(pos_args[1]);
     226                 :        216 :     const mp_float_t rel_tol = args[ARG_rel_tol].u_obj == MP_OBJ_NULL
     227         [ +  + ]:        108 :         ? (mp_float_t)1e-9 : mp_obj_get_float(args[ARG_rel_tol].u_obj);
     228                 :        108 :     const mp_float_t abs_tol = mp_obj_get_float(args[ARG_abs_tol].u_obj);
     229   [ +  +  +  + ]:        108 :     if (rel_tol < (mp_float_t)0.0 || abs_tol < (mp_float_t)0.0) {
     230                 :          8 :         math_error();
     231                 :            :     }
     232         [ +  + ]:        100 :     if (a == b) {
     233                 :            :         return mp_const_true;
     234                 :            :     }
     235                 :         60 :     const mp_float_t difference = MICROPY_FLOAT_C_FUN(fabs)(a - b);
     236         [ +  + ]:         60 :     if (isinf(difference)) { // Either a or b is inf
     237                 :            :         return mp_const_false;
     238                 :            :     }
     239         [ +  + ]:         44 :     if ((difference <= abs_tol) ||
     240         [ +  + ]:         40 :         (difference <= MICROPY_FLOAT_C_FUN(fabs)(rel_tol * a)) ||
     241         [ +  + ]:         28 :         (difference <= MICROPY_FLOAT_C_FUN(fabs)(rel_tol * b))) {
     242                 :         20 :         return mp_const_true;
     243                 :            :     }
     244                 :            :     return mp_const_false;
     245                 :            : }
     246                 :            : MP_DEFINE_CONST_FUN_OBJ_KW(mp_math_isclose_obj, 2, mp_math_isclose);
     247                 :            : #endif
     248                 :            : 
     249                 :            : // Function that takes a variable number of arguments
     250                 :            : 
     251                 :            : // log(x[, base])
     252                 :        242 : static mp_obj_t mp_math_log(size_t n_args, const mp_obj_t *args) {
     253                 :        242 :     mp_float_t x = mp_obj_get_float(args[0]);
     254         [ +  + ]:        242 :     if (x <= (mp_float_t)0.0) {
     255                 :        112 :         math_error();
     256                 :            :     }
     257                 :        130 :     mp_float_t l = MICROPY_FLOAT_C_FUN(log)(x);
     258         [ +  + ]:        130 :     if (n_args == 1) {
     259                 :         30 :         return mp_obj_new_float(l);
     260                 :            :     } else {
     261                 :        100 :         mp_float_t base = mp_obj_get_float(args[1]);
     262         [ +  + ]:        100 :         if (base <= (mp_float_t)0.0) {
     263                 :         44 :             math_error();
     264         [ +  + ]:         56 :         } else if (base == (mp_float_t)1.0) {
     265                 :          8 :             mp_raise_msg(&mp_type_ZeroDivisionError, MP_ERROR_TEXT("divide by zero"));
     266                 :            :         }
     267                 :         48 :         return mp_obj_new_float(l / MICROPY_FLOAT_C_FUN(log)(base));
     268                 :            :     }
     269                 :            : }
     270                 :            : static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_math_log_obj, 1, 2, mp_math_log);
     271                 :            : 
     272                 :            : // Functions that return a tuple
     273                 :            : 
     274                 :            : // frexp(x): converts a floating-point number to fractional and integral components
     275                 :         32 : static mp_obj_t mp_math_frexp(mp_obj_t x_obj) {
     276                 :         32 :     int int_exponent = 0;
     277                 :         32 :     mp_float_t significand = MICROPY_FLOAT_C_FUN(frexp)(mp_obj_get_float(x_obj), &int_exponent);
     278                 :         32 :     mp_obj_t tuple[2];
     279                 :         32 :     tuple[0] = mp_obj_new_float(significand);
     280                 :         32 :     tuple[1] = mp_obj_new_int(int_exponent);
     281                 :         32 :     return mp_obj_new_tuple(2, tuple);
     282                 :            : }
     283                 :            : static MP_DEFINE_CONST_FUN_OBJ_1(mp_math_frexp_obj, mp_math_frexp);
     284                 :            : 
     285                 :            : // modf(x)
     286                 :         32 : static mp_obj_t mp_math_modf(mp_obj_t x_obj) {
     287                 :         32 :     mp_float_t int_part = 0.0;
     288                 :         32 :     mp_float_t x = mp_obj_get_float(x_obj);
     289                 :         32 :     mp_float_t fractional_part = MICROPY_FLOAT_C_FUN(modf)(x, &int_part);
     290                 :            :     #if MICROPY_PY_MATH_MODF_FIX_NEGZERO
     291                 :            :     if (fractional_part == MICROPY_FLOAT_CONST(0.0)) {
     292                 :            :         fractional_part = copysign(fractional_part, x);
     293                 :            :     }
     294                 :            :     #endif
     295                 :         32 :     mp_obj_t tuple[2];
     296                 :         32 :     tuple[0] = mp_obj_new_float(fractional_part);
     297                 :         32 :     tuple[1] = mp_obj_new_float(int_part);
     298                 :         32 :     return mp_obj_new_tuple(2, tuple);
     299                 :            : }
     300                 :            : static MP_DEFINE_CONST_FUN_OBJ_1(mp_math_modf_obj, mp_math_modf);
     301                 :            : 
     302                 :            : // Angular conversions
     303                 :            : 
     304                 :            : // radians(x)
     305                 :         64 : static mp_obj_t mp_math_radians(mp_obj_t x_obj) {
     306                 :         64 :     return mp_obj_new_float(mp_obj_get_float(x_obj) * (MP_PI / MICROPY_FLOAT_CONST(180.0)));
     307                 :            : }
     308                 :            : static MP_DEFINE_CONST_FUN_OBJ_1(mp_math_radians_obj, mp_math_radians);
     309                 :            : 
     310                 :            : // degrees(x)
     311                 :         64 : static mp_obj_t mp_math_degrees(mp_obj_t x_obj) {
     312                 :         64 :     return mp_obj_new_float(mp_obj_get_float(x_obj) * (MICROPY_FLOAT_CONST(180.0) / MP_PI));
     313                 :            : }
     314                 :            : static MP_DEFINE_CONST_FUN_OBJ_1(mp_math_degrees_obj, mp_math_degrees);
     315                 :            : 
     316                 :            : #if MICROPY_PY_MATH_FACTORIAL
     317                 :            : 
     318                 :            : #if MICROPY_OPT_MATH_FACTORIAL
     319                 :            : 
     320                 :            : // factorial(x): slightly efficient recursive implementation
     321                 :       1300 : static mp_obj_t mp_math_factorial_inner(mp_uint_t start, mp_uint_t end) {
     322         [ +  + ]:       1300 :     if (start == end) {
     323                 :          4 :         return mp_obj_new_int(start);
     324         [ +  + ]:       1296 :     } else if (end - start == 1) {
     325                 :        376 :         return mp_binary_op(MP_BINARY_OP_MULTIPLY, MP_OBJ_NEW_SMALL_INT(start), MP_OBJ_NEW_SMALL_INT(end));
     326         [ +  + ]:        920 :     } else if (end - start == 2) {
     327                 :        328 :         mp_obj_t left = MP_OBJ_NEW_SMALL_INT(start);
     328                 :        328 :         mp_obj_t middle = MP_OBJ_NEW_SMALL_INT(start + 1);
     329                 :        328 :         mp_obj_t right = MP_OBJ_NEW_SMALL_INT(end);
     330                 :        328 :         mp_obj_t tmp = mp_binary_op(MP_BINARY_OP_MULTIPLY, left, middle);
     331                 :        328 :         return mp_binary_op(MP_BINARY_OP_MULTIPLY, tmp, right);
     332                 :            :     } else {
     333                 :        592 :         mp_uint_t middle = start + ((end - start) >> 1);
     334                 :        592 :         mp_obj_t left = mp_math_factorial_inner(start, middle);
     335                 :        592 :         mp_obj_t right = mp_math_factorial_inner(middle + 1, end);
     336                 :        592 :         return mp_binary_op(MP_BINARY_OP_MULTIPLY, left, right);
     337                 :            :     }
     338                 :            : }
     339                 :        124 : static mp_obj_t mp_math_factorial(mp_obj_t x_obj) {
     340                 :        124 :     mp_int_t max = mp_obj_get_int(x_obj);
     341         [ +  + ]:        124 :     if (max < 0) {
     342                 :          4 :         mp_raise_ValueError(MP_ERROR_TEXT("negative factorial"));
     343         [ +  + ]:        120 :     } else if (max == 0) {
     344                 :            :         return MP_OBJ_NEW_SMALL_INT(1);
     345                 :            :     }
     346                 :        116 :     return mp_math_factorial_inner(1, max);
     347                 :            : }
     348                 :            : 
     349                 :            : #else
     350                 :            : 
     351                 :            : // factorial(x): squared difference implementation
     352                 :            : // based on http://www.luschny.de/math/factorial/index.html
     353                 :            : static mp_obj_t mp_math_factorial(mp_obj_t x_obj) {
     354                 :            :     mp_int_t max = mp_obj_get_int(x_obj);
     355                 :            :     if (max < 0) {
     356                 :            :         mp_raise_ValueError(MP_ERROR_TEXT("negative factorial"));
     357                 :            :     } else if (max <= 1) {
     358                 :            :         return MP_OBJ_NEW_SMALL_INT(1);
     359                 :            :     }
     360                 :            :     mp_int_t h = max >> 1;
     361                 :            :     mp_int_t q = h * h;
     362                 :            :     mp_int_t r = q << 1;
     363                 :            :     if (max & 1) {
     364                 :            :         r *= max;
     365                 :            :     }
     366                 :            :     mp_obj_t prod = MP_OBJ_NEW_SMALL_INT(r);
     367                 :            :     for (mp_int_t num = 1; num < max - 2; num += 2) {
     368                 :            :         q -= num;
     369                 :            :         prod = mp_binary_op(MP_BINARY_OP_MULTIPLY, prod, MP_OBJ_NEW_SMALL_INT(q));
     370                 :            :     }
     371                 :            :     return prod;
     372                 :            : }
     373                 :            : 
     374                 :            : #endif
     375                 :            : 
     376                 :            : static MP_DEFINE_CONST_FUN_OBJ_1(mp_math_factorial_obj, mp_math_factorial);
     377                 :            : 
     378                 :            : #endif
     379                 :            : 
     380                 :            : static const mp_rom_map_elem_t mp_module_math_globals_table[] = {
     381                 :            :     { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_math) },
     382                 :            :     { MP_ROM_QSTR(MP_QSTR_e), mp_const_float_e },
     383                 :            :     { MP_ROM_QSTR(MP_QSTR_pi), mp_const_float_pi },
     384                 :            :     #if MICROPY_PY_MATH_CONSTANTS
     385                 :            :     { MP_ROM_QSTR(MP_QSTR_tau), mp_const_float_tau },
     386                 :            :     { MP_ROM_QSTR(MP_QSTR_inf), mp_const_float_inf },
     387                 :            :     { MP_ROM_QSTR(MP_QSTR_nan), mp_const_float_nan },
     388                 :            :     #endif
     389                 :            :     { MP_ROM_QSTR(MP_QSTR_sqrt), MP_ROM_PTR(&mp_math_sqrt_obj) },
     390                 :            :     { MP_ROM_QSTR(MP_QSTR_pow), MP_ROM_PTR(&mp_math_pow_obj) },
     391                 :            :     { MP_ROM_QSTR(MP_QSTR_exp), MP_ROM_PTR(&mp_math_exp_obj) },
     392                 :            :     #if MICROPY_PY_MATH_SPECIAL_FUNCTIONS
     393                 :            :     { MP_ROM_QSTR(MP_QSTR_expm1), MP_ROM_PTR(&mp_math_expm1_obj) },
     394                 :            :     #endif
     395                 :            :     { MP_ROM_QSTR(MP_QSTR_log), MP_ROM_PTR(&mp_math_log_obj) },
     396                 :            :     #if MICROPY_PY_MATH_SPECIAL_FUNCTIONS
     397                 :            :     { MP_ROM_QSTR(MP_QSTR_log2), MP_ROM_PTR(&mp_math_log2_obj) },
     398                 :            :     { MP_ROM_QSTR(MP_QSTR_log10), MP_ROM_PTR(&mp_math_log10_obj) },
     399                 :            :     { MP_ROM_QSTR(MP_QSTR_cosh), MP_ROM_PTR(&mp_math_cosh_obj) },
     400                 :            :     { MP_ROM_QSTR(MP_QSTR_sinh), MP_ROM_PTR(&mp_math_sinh_obj) },
     401                 :            :     { MP_ROM_QSTR(MP_QSTR_tanh), MP_ROM_PTR(&mp_math_tanh_obj) },
     402                 :            :     { MP_ROM_QSTR(MP_QSTR_acosh), MP_ROM_PTR(&mp_math_acosh_obj) },
     403                 :            :     { MP_ROM_QSTR(MP_QSTR_asinh), MP_ROM_PTR(&mp_math_asinh_obj) },
     404                 :            :     { MP_ROM_QSTR(MP_QSTR_atanh), MP_ROM_PTR(&mp_math_atanh_obj) },
     405                 :            :     #endif
     406                 :            :     { MP_ROM_QSTR(MP_QSTR_cos), MP_ROM_PTR(&mp_math_cos_obj) },
     407                 :            :     { MP_ROM_QSTR(MP_QSTR_sin), MP_ROM_PTR(&mp_math_sin_obj) },
     408                 :            :     { MP_ROM_QSTR(MP_QSTR_tan), MP_ROM_PTR(&mp_math_tan_obj) },
     409                 :            :     { MP_ROM_QSTR(MP_QSTR_acos), MP_ROM_PTR(&mp_math_acos_obj) },
     410                 :            :     { MP_ROM_QSTR(MP_QSTR_asin), MP_ROM_PTR(&mp_math_asin_obj) },
     411                 :            :     { MP_ROM_QSTR(MP_QSTR_atan), MP_ROM_PTR(&mp_math_atan_obj) },
     412                 :            :     { MP_ROM_QSTR(MP_QSTR_atan2), MP_ROM_PTR(&mp_math_atan2_obj) },
     413                 :            :     { MP_ROM_QSTR(MP_QSTR_ceil), MP_ROM_PTR(&mp_math_ceil_obj) },
     414                 :            :     { MP_ROM_QSTR(MP_QSTR_copysign), MP_ROM_PTR(&mp_math_copysign_obj) },
     415                 :            :     { MP_ROM_QSTR(MP_QSTR_fabs), MP_ROM_PTR(&mp_math_fabs_obj) },
     416                 :            :     { MP_ROM_QSTR(MP_QSTR_floor), MP_ROM_PTR(&mp_math_floor_obj) },
     417                 :            :     { MP_ROM_QSTR(MP_QSTR_fmod), MP_ROM_PTR(&mp_math_fmod_obj) },
     418                 :            :     { MP_ROM_QSTR(MP_QSTR_frexp), MP_ROM_PTR(&mp_math_frexp_obj) },
     419                 :            :     { MP_ROM_QSTR(MP_QSTR_ldexp), MP_ROM_PTR(&mp_math_ldexp_obj) },
     420                 :            :     { MP_ROM_QSTR(MP_QSTR_modf), MP_ROM_PTR(&mp_math_modf_obj) },
     421                 :            :     { MP_ROM_QSTR(MP_QSTR_isfinite), MP_ROM_PTR(&mp_math_isfinite_obj) },
     422                 :            :     { MP_ROM_QSTR(MP_QSTR_isinf), MP_ROM_PTR(&mp_math_isinf_obj) },
     423                 :            :     { MP_ROM_QSTR(MP_QSTR_isnan), MP_ROM_PTR(&mp_math_isnan_obj) },
     424                 :            :     #if MICROPY_PY_MATH_ISCLOSE
     425                 :            :     { MP_ROM_QSTR(MP_QSTR_isclose), MP_ROM_PTR(&mp_math_isclose_obj) },
     426                 :            :     #endif
     427                 :            :     { MP_ROM_QSTR(MP_QSTR_trunc), MP_ROM_PTR(&mp_math_trunc_obj) },
     428                 :            :     { MP_ROM_QSTR(MP_QSTR_radians), MP_ROM_PTR(&mp_math_radians_obj) },
     429                 :            :     { MP_ROM_QSTR(MP_QSTR_degrees), MP_ROM_PTR(&mp_math_degrees_obj) },
     430                 :            :     #if MICROPY_PY_MATH_FACTORIAL
     431                 :            :     { MP_ROM_QSTR(MP_QSTR_factorial), MP_ROM_PTR(&mp_math_factorial_obj) },
     432                 :            :     #endif
     433                 :            :     #if MICROPY_PY_MATH_SPECIAL_FUNCTIONS
     434                 :            :     { MP_ROM_QSTR(MP_QSTR_erf), MP_ROM_PTR(&mp_math_erf_obj) },
     435                 :            :     { MP_ROM_QSTR(MP_QSTR_erfc), MP_ROM_PTR(&mp_math_erfc_obj) },
     436                 :            :     { MP_ROM_QSTR(MP_QSTR_gamma), MP_ROM_PTR(&mp_math_gamma_obj) },
     437                 :            :     { MP_ROM_QSTR(MP_QSTR_lgamma), MP_ROM_PTR(&mp_math_lgamma_obj) },
     438                 :            :     #endif
     439                 :            : };
     440                 :            : 
     441                 :            : static MP_DEFINE_CONST_DICT(mp_module_math_globals, mp_module_math_globals_table);
     442                 :            : 
     443                 :            : const mp_obj_module_t mp_module_math = {
     444                 :            :     .base = { &mp_type_module },
     445                 :            :     .globals = (mp_obj_dict_t *)&mp_module_math_globals,
     446                 :            : };
     447                 :            : 
     448                 :            : MP_REGISTER_MODULE(MP_QSTR_math, mp_module_math);
     449                 :            : 
     450                 :            : #endif // MICROPY_PY_BUILTINS_FLOAT && MICROPY_PY_MATH

Generated by: LCOV version 1.15-5-g462f71d