diff --git a/ports/unix/variants/coverage/mpconfigvariant.h b/ports/unix/variants/coverage/mpconfigvariant.h index 2a48e57e7f013..ce6e344ac9a6c 100644 --- a/ports/unix/variants/coverage/mpconfigvariant.h +++ b/ports/unix/variants/coverage/mpconfigvariant.h @@ -42,3 +42,8 @@ #define MICROPY_TRACKED_ALLOC (1) #define MICROPY_WARNINGS_CATEGORY (1) #define MICROPY_PY_UCRYPTOLIB_CTR (1) + +// Enable int.bit_length(n) +#ifndef MICROPY_INT_BIT_LENGTH +#define MICROPY_INT_BIT_LENGTH (1) +#endif diff --git a/py/mpz.c b/py/mpz.c index b61997e2fd4ed..3c032bfefdf14 100644 --- a/py/mpz.c +++ b/py/mpz.c @@ -1390,6 +1390,24 @@ void mpz_pow3_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs, const mpz_t mpz_free(n); } +#if MICROPY_INT_BIT_LENGTH +mp_uint_t mpz_bit_length_inpl(mpz_t *n) { + if (n->len == 0) { + return 0; + } + + mpz_t *dest = mpz_clone(n); + mp_uint_t num_bits = 0; + while (dest->len > 0) { + dest->len = mpn_shr(dest->dig, dest->dig, dest->len, 1); + num_bits++; + } + + mpz_free(dest); + return num_bits; +} +#endif + #if 0 these functions are unused diff --git a/py/mpz.h b/py/mpz.h index d27f5724047ae..36087b0299570 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_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..ee2da0c22ad27 100644 --- a/py/objint.c +++ b/py/objint.c @@ -450,9 +450,30 @@ STATIC mp_obj_t int_to_bytes(size_t n_args, const mp_obj_t *args) { } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(int_to_bytes_obj, 3, 4, int_to_bytes); +#if MICROPY_INT_BIT_LENGTH +STATIC mp_obj_t int_bit_length(size_t n_args, const mp_obj_t *args) { + (void)n_args; + #if MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_MPZ + return mp_obj_int_mpz_bit_length(args[0]); + #else + mp_uint_t dest = MP_OBJ_SMALL_INT_VALUE(args[0]); + mp_uint_t num_bits = 0; + while (dest > 0) { + dest >>= 1; + num_bits++; + } + return mp_obj_new_int_from_uint(num_bits); + #endif +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(int_bit_length_obj, 0, 1, int_bit_length); +#endif + 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_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..46085d55e3a94 100644 --- a/py/objint.h +++ b/py/objint.h @@ -61,5 +61,8 @@ mp_obj_t mp_obj_int_unary_op(mp_unary_op_t op, mp_obj_t o_in); mp_obj_t mp_obj_int_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in); 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); +#if (MICROPY_INT_BIT_LENGTH && MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_MPZ) +mp_obj_t mp_obj_int_mpz_bit_length(mp_obj_t n); +#endif #endif // MICROPY_INCLUDED_PY_OBJINT_H diff --git a/py/objint_mpz.c b/py/objint_mpz.c index 8078441d66a0b..45eb571cc457a 100644 --- a/py/objint_mpz.c +++ b/py/objint_mpz.c @@ -334,7 +334,7 @@ mp_obj_t mp_obj_int_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_i } } -#if MICROPY_PY_BUILTINS_POW3 +#if (MICROPY_PY_BUILTINS_POW3 || MICROPY_INT_BIT_LENGTH) STATIC mpz_t *mp_mpz_for_int(mp_obj_t arg, mpz_t *temp) { if (mp_obj_is_small_int(arg)) { mpz_init_from_int(temp, MP_OBJ_SMALL_INT_VALUE(arg)); @@ -344,7 +344,9 @@ STATIC mpz_t *mp_mpz_for_int(mp_obj_t arg, mpz_t *temp) { return &(arp_p->mpz); } } +#endif +#if MICROPY_PY_BUILTINS_POW3 mp_obj_t mp_obj_int_pow3(mp_obj_t base, mp_obj_t exponent, mp_obj_t modulus) { if (!mp_obj_is_int(base) || !mp_obj_is_int(exponent) || !mp_obj_is_int(modulus)) { mp_raise_TypeError(MP_ERROR_TEXT("pow() with 3 arguments requires integers")); @@ -373,6 +375,18 @@ mp_obj_t mp_obj_int_pow3(mp_obj_t base, mp_obj_t exponent, mp_obj_t modulus) { } #endif +#if MICROPY_INT_BIT_LENGTH +mp_obj_t mp_obj_int_mpz_bit_length(mp_obj_t size) { + mpz_t n_temp; + mpz_t *n = mp_mpz_for_int(size, &n_temp); + mp_uint_t res = mpz_bit_length_inpl(n); + if (n == &n_temp) { + mpz_deinit(n); + } + return mp_obj_new_int_from_ull(res); +} +#endif + mp_obj_t mp_obj_new_int(mp_int_t value) { if (MP_SMALL_INT_FITS(value)) { return MP_OBJ_NEW_SMALL_INT(value); 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..a7f5cf305a6ab --- /dev/null +++ b/tests/basics/int_bit_length.py @@ -0,0 +1,20 @@ +# tests int.bit_length + +try: + int.bit_length +except AttributeError: + print('SKIP') + raise SystemExit + +n = -37 +print(n.bit_length()) + +n = 1024 +print(n.bit_length()) + +n = -1024 +print(n.bit_length()) + +print((2048).bit_length()) + +print((-2048).bit_length())