From 056b4dd0bf64be4a4c449e9f910dab83827b6cb8 Mon Sep 17 00:00:00 2001 From: Damiano Mazzella Date: Thu, 1 Jun 2023 10:41:21 +0200 Subject: [PATCH] py/objint.c: Add support for int.bit_length --- py/mpconfig.h | 5 +++++ py/mpz.c | 15 +++++++++++++++ py/mpz.h | 3 +++ py/objint.c | 19 +++++++++++++++++++ py/objint.h | 2 ++ py/objint_longlong.c | 17 +++++++++++++++++ py/objint_mpz.c | 21 +++++++++++++++++++++ tests/basics/builtin_help.py.exp | 2 ++ tests/basics/int_bit_length.py | 18 ++++++++++++++++++ 9 files changed, 102 insertions(+) create mode 100644 tests/basics/int_bit_length.py diff --git a/py/mpconfig.h b/py/mpconfig.h index afef744ab5b01..febd1a627ce79 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -1144,6 +1144,11 @@ typedef double mp_float_t; #define MICROPY_PY_BUILTINS_ROUND_INT (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) #endif +// Whether to support int.bit_length(n) +#ifndef MICROPY_PY_BUILTINS_INT_BIT_LENGTH +#define MICROPY_PY_BUILTINS_INT_BIT_LENGTH (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + // Whether to support complete set of special methods for user // classes, or only the most used ones. "Inplace" methods are // controlled by MICROPY_PY_ALL_INPLACE_SPECIAL_METHODS below. diff --git a/py/mpz.c b/py/mpz.c index b61997e2fd4ed..c6d31fb961d5a 100644 --- a/py/mpz.c +++ b/py/mpz.c @@ -1390,6 +1390,21 @@ void mpz_pow3_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs, const mpz_t mpz_free(n); } +#if MICROPY_PY_BUILTINS_INT_BIT_LENGTH +mp_uint_t mpz_bit_length_inpl(mpz_t *n) { + if (n->len == 0) { + return 0; + } + mpz_dig_t d = n->dig[n->len - 1]; + mp_uint_t num_bits = 0; + while (d) { + d >>= 1; + ++num_bits; + } + return MPZ_DIG_SIZE * (n->len - 1) + num_bits; +} +#endif + #if 0 these functions are unused diff --git a/py/mpz.h b/py/mpz.h index d27f5724047ae..6b59b840425fe 100644 --- a/py/mpz.h +++ b/py/mpz.h @@ -138,6 +138,9 @@ void mpz_and_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs); void mpz_or_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs); void mpz_xor_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs); void mpz_divmod_inpl(mpz_t *dest_quo, mpz_t *dest_rem, const mpz_t *lhs, const mpz_t *rhs); +#if MICROPY_PY_BUILTINS_INT_BIT_LENGTH +mp_uint_t mpz_bit_length_inpl(mpz_t *dest); +#endif static inline size_t mpz_max_num_bits(const mpz_t *z) { return z->len * MPZ_DIG_SIZE; diff --git a/py/objint.c b/py/objint.c index be5f4653a7dec..7162080446eca 100644 --- a/py/objint.c +++ b/py/objint.c @@ -367,6 +367,22 @@ mp_int_t mp_obj_int_get_checked(mp_const_obj_t self_in) { return MP_OBJ_SMALL_INT_VALUE(self_in); } +#if MICROPY_PY_BUILTINS_INT_BIT_LENGTH +STATIC mp_obj_t int_bit_length(mp_obj_t self_in) { + mp_int_t val = MP_OBJ_SMALL_INT_VALUE(self_in); + if (val < 0) { + val = -val; + } + mp_uint_t n = 0; + while (val) { + val >>= 1; + ++n; + } + return MP_OBJ_NEW_SMALL_INT(n); +} +MP_DEFINE_CONST_FUN_OBJ_1(int_bit_length_obj, int_bit_length); +#endif + #endif // MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_NONE // This dispatcher function is expected to be independent of the implementation of long int @@ -453,6 +469,9 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(int_to_bytes_obj, 3, 4, int_to_bytes) STATIC const mp_rom_map_elem_t int_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_from_bytes), MP_ROM_PTR(&int_from_bytes_obj) }, { MP_ROM_QSTR(MP_QSTR_to_bytes), MP_ROM_PTR(&int_to_bytes_obj) }, + #if MICROPY_PY_BUILTINS_INT_BIT_LENGTH + { MP_ROM_QSTR(MP_QSTR_bit_length), MP_ROM_PTR(&int_bit_length_obj) }, + #endif }; STATIC MP_DEFINE_CONST_DICT(int_locals_dict, int_locals_dict_table); diff --git a/py/objint.h b/py/objint.h index 5eed87705dedb..f0ce4ee0b3a2f 100644 --- a/py/objint.h +++ b/py/objint.h @@ -62,4 +62,6 @@ mp_obj_t mp_obj_int_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_i mp_obj_t mp_obj_int_binary_op_extra_cases(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in); mp_obj_t mp_obj_int_pow3(mp_obj_t base, mp_obj_t exponent, mp_obj_t modulus); +MP_DECLARE_CONST_FUN_OBJ_1(int_bit_length_obj); + #endif // MICROPY_INCLUDED_PY_OBJINT_H diff --git a/py/objint_longlong.c b/py/objint_longlong.c index ee499e0265b32..d78c107b1c962 100644 --- a/py/objint_longlong.c +++ b/py/objint_longlong.c @@ -292,4 +292,21 @@ mp_float_t mp_obj_int_as_float_impl(mp_obj_t self_in) { } #endif +#if MICROPY_PY_BUILTINS_INT_BIT_LENGTH +STATIC mp_obj_t int_bit_length(mp_obj_t self_in) { + mp_obj_int_t *self = self_in; + long long val = self->val; + if (val < 0) { + val = -val; + } + mp_uint_t n = 0; + while (val) { + val >>= 1; + ++n; + } + return MP_OBJ_NEW_SMALL_INT(n); +} +MP_DEFINE_CONST_FUN_OBJ_1(int_bit_length_obj, int_bit_length); +#endif + #endif diff --git a/py/objint_mpz.c b/py/objint_mpz.c index 8078441d66a0b..fd98f47e3c5ad 100644 --- a/py/objint_mpz.c +++ b/py/objint_mpz.c @@ -457,4 +457,25 @@ mp_float_t mp_obj_int_as_float_impl(mp_obj_t self_in) { } #endif +#if MICROPY_PY_BUILTINS_INT_BIT_LENGTH +STATIC mp_obj_t int_bit_length(mp_obj_t self_in) { + if (mp_obj_is_small_int(self_in)) { + mp_int_t val = MP_OBJ_SMALL_INT_VALUE(self_in); + if (val < 0) { + val = -val; + } + mp_uint_t n = 0; + while (val) { + val >>= 1; + ++n; + } + return MP_OBJ_NEW_SMALL_INT(n); + } else { + mp_obj_int_t *self = MP_OBJ_TO_PTR(self_in); + return MP_OBJ_NEW_SMALL_INT(mpz_bit_length_inpl(&self->mpz)); + } +} +MP_DEFINE_CONST_FUN_OBJ_1(int_bit_length_obj, int_bit_length); +#endif + #endif diff --git a/tests/basics/builtin_help.py.exp b/tests/basics/builtin_help.py.exp index ed8a7d74b8541..5f8d71c318d65 100644 --- a/tests/basics/builtin_help.py.exp +++ b/tests/basics/builtin_help.py.exp @@ -3,9 +3,11 @@ object is of type function object is of type type from_bytes -- to_bytes -- +######## object 1 is of type int from_bytes -- to_bytes -- +######## object is of type module __name__ -- micropython const -- diff --git a/tests/basics/int_bit_length.py b/tests/basics/int_bit_length.py new file mode 100644 index 0000000000000..e1ab02bc83e59 --- /dev/null +++ b/tests/basics/int_bit_length.py @@ -0,0 +1,18 @@ +# tests int.bit_length + +try: + int.bit_length +except AttributeError: + print('SKIP') + raise SystemExit + +for n in (0, 37, 1024, 1<<29, 1<<30, 1<<31, 1<<32, 1<<61, 1<<62, 1<<63, 1<<64): + for j in (-1921, -302, -3, -2, -1, 0, 1, 2, 3, 1821): + for s in (-1, 1): + x = (s * (n+j)) + print(x, x.bit_length()) + +print((2048).bit_length()) +print((-2048).bit_length()) + +print(int.bit_length(5))