diff --git a/atmel-samd/Makefile b/atmel-samd/Makefile index 239c801893..ec8cd9b268 100644 --- a/atmel-samd/Makefile +++ b/atmel-samd/Makefile @@ -272,6 +272,7 @@ SRC_SHARED_MODULE = \ bitbangio/SPI.c \ busio/OneWire.c \ os/__init__.c \ + random/__init__.c \ storage/__init__.c \ uheap/__init__.c \ diff --git a/atmel-samd/common-hal/os/__init__.c b/atmel-samd/common-hal/os/__init__.c index 2d2067ee0c..2df4e40ffd 100644 --- a/atmel-samd/common-hal/os/__init__.c +++ b/atmel-samd/common-hal/os/__init__.c @@ -55,3 +55,7 @@ STATIC MP_DEFINE_ATTRTUPLE( mp_obj_t common_hal_os_uname(void) { return (mp_obj_t)&os_uname_info_obj; } + +bool common_hal_os_urandom(uint8_t* buffer, uint32_t length) { + return false; +} diff --git a/atmel-samd/mpconfigport.h b/atmel-samd/mpconfigport.h index 06e789ed10..228518a1ed 100644 --- a/atmel-samd/mpconfigport.h +++ b/atmel-samd/mpconfigport.h @@ -57,8 +57,8 @@ #define MICROPY_PY_MATH (1) #define MICROPY_PY_CMATH (0) #define MICROPY_PY_IO (0) -#define MICROPY_PY_URANDOM (1) -#define MICROPY_PY_URANDOM_EXTRA_FUNCS (1) +#define MICROPY_PY_URANDOM (0) +#define MICROPY_PY_URANDOM_EXTRA_FUNCS (0) #define MICROPY_PY_STRUCT (1) #define MICROPY_PY_SYS (1) #define MICROPY_CPYTHON_COMPAT (0) @@ -138,6 +138,7 @@ extern const struct _mp_obj_module_t pulseio_module; extern const struct _mp_obj_module_t busio_module; extern const struct _mp_obj_module_t board_module; extern const struct _mp_obj_module_t os_module; +extern const struct _mp_obj_module_t random_module; extern const struct _mp_obj_module_t storage_module; extern const struct _mp_obj_module_t time_module; extern const struct _mp_obj_module_t neopixel_write_module; @@ -176,6 +177,7 @@ extern const struct _mp_obj_module_t usb_hid_module; { MP_OBJ_NEW_QSTR(MP_QSTR_busio), (mp_obj_t)&busio_module }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_board), (mp_obj_t)&board_module }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_os), (mp_obj_t)&os_module }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR_random), (mp_obj_t)&random_module }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_storage), (mp_obj_t)&storage_module }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_time), (mp_obj_t)&time_module }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_neopixel_write),(mp_obj_t)&neopixel_write_module }, \ diff --git a/esp8266/Makefile b/esp8266/Makefile index 54c8263517..168e743838 100644 --- a/esp8266/Makefile +++ b/esp8266/Makefile @@ -139,6 +139,7 @@ SRC_SHARED_MODULE = \ busio/I2C.c \ busio/OneWire.c \ os/__init__.c \ + random/__init__.c \ storage/__init__.c \ SRC_SHARED_MODULE_EXPANDED = $(addprefix shared-bindings/, $(SRC_SHARED_MODULE)) \ diff --git a/esp8266/common-hal/os/__init__.c b/esp8266/common-hal/os/__init__.c index df241ecaef..4f82a79e0e 100644 --- a/esp8266/common-hal/os/__init__.c +++ b/esp8266/common-hal/os/__init__.c @@ -27,6 +27,7 @@ #include +#include "etshal.h" #include "py/objtuple.h" #include "py/objstr.h" #include "genhdr/mpversion.h" @@ -60,3 +61,20 @@ mp_obj_t common_hal_os_uname(void) { os_uname_info_obj.items[2] = mp_obj_new_str(ver, strlen(ver), false); return (mp_obj_t)&os_uname_info_obj; } + +static uint32_t last_random; +bool common_hal_os_urandom(uint8_t* buffer, uint32_t length) { + uint32_t i = 0; + while (i < length) { + uint32_t new_random = last_random; + while (new_random == last_random) { + new_random = *WDEV_HWRNG; + } + for (int j = 0; j < 4 && i < length; j++) { + buffer[i] = new_random & 0xff; + i++; + new_random >>= 8; + } + } + return true; +} diff --git a/esp8266/mpconfigport.h b/esp8266/mpconfigport.h index 0667888326..faff05bbaa 100644 --- a/esp8266/mpconfigport.h +++ b/esp8266/mpconfigport.h @@ -68,7 +68,7 @@ #define MICROPY_PY_UHEAPQ (1) #define MICROPY_PY_UTIMEQ (1) #define MICROPY_PY_UJSON (1) -#define MICROPY_PY_URANDOM (1) +#define MICROPY_PY_URANDOM (0) #define MICROPY_PY_URE (1) #define MICROPY_PY_USELECT (1) #define MICROPY_PY_UTIME_MP_HAL (1) @@ -155,8 +155,8 @@ void *esp_native_code_commit(void*, size_t); // extra built in modules to add to the list of known ones extern const struct _mp_obj_module_t esp_module; extern const struct _mp_obj_module_t network_module; -extern const struct _mp_obj_module_t utime_module; extern const struct _mp_obj_module_t os_module; +extern const struct _mp_obj_module_t random_module; extern const struct _mp_obj_module_t storage_module; extern const struct _mp_obj_module_t mp_module_lwip; extern const struct _mp_obj_module_t mp_module_machine; @@ -186,6 +186,7 @@ extern const struct _mp_obj_module_t time_module; { MP_OBJ_NEW_QSTR(MP_QSTR_busio), (mp_obj_t)&busio_module }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_bitbangio), (mp_obj_t)&bitbangio_module }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_storage), (mp_obj_t)&storage_module }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR_random), (mp_obj_t)&random_module }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_time), (mp_obj_t)&time_module }, \ #define MICROPY_PORT_BUILTIN_MODULE_WEAK_LINKS \ diff --git a/shared-bindings/index.rst b/shared-bindings/index.rst index 918fb5675c..4c07415540 100644 --- a/shared-bindings/index.rst +++ b/shared-bindings/index.rst @@ -12,13 +12,13 @@ limited. For example, a microcontroller without analog features will not have Support Matrix --------------- -=============== ========== ========= =========== ======= ======= =========== ================= ================ ======= ========= ========= ======== ========= ======= ========= -Port `analogio` `audioio` `bitbangio` `board` `busio` `digitalio` `microcontroller` `neopixel_write` `os` `pulseio` `storage` `time` `touchio` `uheap` `usb_hid` -=============== ========== ========= =========== ======= ======= =========== ================= ================ ======= ========= ========= ======== ========= ======= ========= -SAMD21 **Yes** No No **Yes** **Yes** **Yes** **Yes** **Yes** **Yes** No **Yes** **Yes** **Yes** Debug **Yes** -SAMD21 Express **Yes** **Yes** **Yes** **Yes** **Yes** **Yes** **Yes** **Yes** **Yes** **Yes** **Yes** **Yes** **Yes** Debug **Yes** -ESP8266 **Yes** No **Yes** **Yes** **Yes** **Yes** **Yes** **Yes** **Yes** No **Yes** **Yes** No Debug No -=============== ========== ========= =========== ======= ======= =========== ================= ================ ======= ========= ========= ======== ========= ======= ========= +=============== ========== ========= =========== ======= ======= =========== ================= ================ ======= ========= ======== ========= ======== ========= ======= ========= +Port `analogio` `audioio` `bitbangio` `board` `busio` `digitalio` `microcontroller` `neopixel_write` `os` `pulseio` `random` `storage` `time` `touchio` `uheap` `usb_hid` +=============== ========== ========= =========== ======= ======= =========== ================= ================ ======= ========= ======== ========= ======== ========= ======= ========= +SAMD21 **Yes** No No **Yes** **Yes** **Yes** **Yes** **Yes** **Yes** No **Yes** **Yes** **Yes** **Yes** Debug **Yes** +SAMD21 Express **Yes** **Yes** **Yes** **Yes** **Yes** **Yes** **Yes** **Yes** **Yes** **Yes** **Yes** **Yes** **Yes** **Yes** Debug **Yes** +ESP8266 **Yes** No **Yes** **Yes** **Yes** **Yes** **Yes** **Yes** **Yes** No **Yes** **Yes** **Yes** No Debug No +=============== ========== ========= =========== ======= ======= =========== ================= ================ ======= ========= ======== ========= ======== ========= ======= ========= Modules --------- diff --git a/shared-bindings/os/__init__.c b/shared-bindings/os/__init__.c index 6185f0bf28..2772245385 100644 --- a/shared-bindings/os/__init__.c +++ b/shared-bindings/os/__init__.c @@ -33,6 +33,7 @@ #include "lib/oofatfs/diskio.h" #include "py/mpstate.h" #include "py/obj.h" +#include "py/runtime.h" #include "shared-bindings/os/__init__.h" //| :mod:`os` --- functions that an OS normally provides @@ -187,6 +188,20 @@ STATIC mp_obj_t os_sync(void) { } MP_DEFINE_CONST_FUN_OBJ_0(os_sync_obj, os_sync); +//| .. function:: urandom(size) +//| +//| Returns a string of *size* random bytes based on a hardware True Random +//| Number Generator. When not available, it will raise a NotImplementedError. +//| +STATIC mp_obj_t os_urandom(mp_obj_t size_in) { + mp_int_t size = mp_obj_get_int(size_in); + uint8_t tmp[size]; + if (!common_hal_os_urandom(tmp, size)) { + mp_raise_NotImplementedError(""); + } + return mp_obj_new_bytes(tmp, size); +} +MP_DEFINE_CONST_FUN_OBJ_1(os_urandom_obj, os_urandom); STATIC const mp_rom_map_elem_t os_module_globals_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_os) }, @@ -206,6 +221,8 @@ STATIC const mp_rom_map_elem_t os_module_globals_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_sync), MP_ROM_PTR(&os_sync_obj) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_urandom), MP_ROM_PTR(&os_urandom_obj) }, + //| .. data:: sep //| //| Separator used to dileneate path components such as folder and file names. diff --git a/shared-bindings/os/__init__.h b/shared-bindings/os/__init__.h index e04a393199..41715e7ace 100644 --- a/shared-bindings/os/__init__.h +++ b/shared-bindings/os/__init__.h @@ -45,4 +45,7 @@ void common_hal_os_rmdir(const char* path); mp_obj_t common_hal_os_stat(const char* path); mp_obj_t common_hal_os_statvfs(const char* path); +// Returns true if data was correctly sourced from a true random number generator. +bool common_hal_os_urandom(uint8_t* buffer, mp_uint_t length); + #endif // __MICROPY_INCLUDED_SHARED_BINDINGS_OS___INIT___H__ diff --git a/shared-bindings/random/__init__.c b/shared-bindings/random/__init__.c new file mode 100644 index 0000000000..fe52c1fc05 --- /dev/null +++ b/shared-bindings/random/__init__.c @@ -0,0 +1,188 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Paul Sokolovsky + * Copyright (c) 2016 Scott Shawcroft for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include + +#include "py/obj.h" +#include "py/runtime.h" +#include "shared-bindings/random/__init__.h" + +//| :mod:`random` --- psuedo-random numbers and choices +//| ======================================================== +//| +//| .. module:: random +//| :synopsis: psuedo-random numbers and choices +//| :platform: SAMD21, ESP8266 +//| +//| The `random` module is a strict subset of the CPython `cpython:random` +//| module. So, code written in CircuitPython will work in CPython but not +//| necessarily the other way around. +//| +//| Like its CPython cousin, CircuitPython's random seeds itself on first use +//| with a true random from os.urandom() when available or the uptime otherwise. +//| Once seeded, it will be deterministic, which is why its bad for cryptography. +//| +//| .. warning:: Numbers from this module are not cryptographically strong! Use +//| bytes from `os.urandom` directly for true randomness. +//| + +//| .. function:: seed(seed) +//| +//| Sets the starting seed of the random number generation. Further calls to +//| `random` will return deterministic results afterwards. +//| +STATIC mp_obj_t random_seed(mp_obj_t seed_in) { + mp_uint_t seed = mp_obj_get_int_truncated(seed_in); + shared_modules_random_seed(seed); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(random_seed_obj, random_seed); + +//| .. function:: getrandbits(k) +//| +//| Returns an integer with *k* random bits. +//| +STATIC mp_obj_t random_getrandbits(mp_obj_t num_in) { + int n = mp_obj_get_int(num_in); + if (n > 32 || n == 0) { + mp_raise_ValueError(NULL); + } + return mp_obj_new_int_from_uint(shared_modules_random_getrandbits((uint8_t) n)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(random_getrandbits_obj, random_getrandbits); + +//| .. function:: randrange(stop) +//| randrange(start, stop, step=1) +//| +//| Returns a randomly selected integer from `range(start, stop, step)`. +//| +STATIC mp_obj_t random_randrange(size_t n_args, const mp_obj_t *args) { + mp_int_t start = 0; + mp_int_t stop = mp_obj_get_int(args[0]); + mp_int_t step = 1; + if (n_args == 1) { + // range(stop) + if (stop <= 0) { + mp_raise_ValueError("stop not reachable from start"); + } + } else { + start = stop; + stop = mp_obj_get_int(args[1]); + if (n_args == 2) { + // range(start, stop) + if (start >= stop) { + mp_raise_ValueError("stop not reachable from start"); + } + } else { + // range(start, stop, step) + step = mp_obj_get_int(args[2]); + mp_int_t n; + if (step > 0) { + n = (stop - start + step - 1) / step; + } else if (step < 0) { + n = (stop - start + step + 1) / step; + } else { + mp_raise_ValueError("step must be non-zero"); + } + if (n <= 0) { + mp_raise_ValueError("invalid step"); + } + } + } + + return mp_obj_new_int(shared_modules_random_randrange(start, stop, step)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(random_randrange_obj, 1, 3, random_randrange); + +//| .. function:: randint(a, b) +//| +//| Returns a randomly selected integer between a and b inclusive. Equivalent +//| to `randrange(a, b + 1, 1)` +//| +STATIC mp_obj_t random_randint(mp_obj_t a_in, mp_obj_t b_in) { + mp_int_t a = mp_obj_get_int(a_in); + mp_int_t b = mp_obj_get_int(b_in); + if (a > b) { + mp_raise_ValueError(NULL); + } + return mp_obj_new_int(shared_modules_random_randrange(a, b + 1, 1)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(random_randint_obj, random_randint); + +//| .. function:: choice(seq) +//| +//| Returns a randomly selected element from the given sequence. Raises +//| IndexError when the sequence is empty. +//| +STATIC mp_obj_t random_choice(mp_obj_t seq) { + mp_int_t len = mp_obj_get_int(mp_obj_len(seq)); + if (len == 0) { + mp_raise_IndexError("empty sequence"); + } + return mp_obj_subscr(seq, mp_obj_new_int(shared_modules_random_randrange(0, len, 1)), MP_OBJ_SENTINEL); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(random_choice_obj, random_choice); + +//| .. function:: random() +//| +//| Returns a random float between 0 and 1.0. +//| +STATIC mp_obj_t random_random(void) { + return mp_obj_new_float(shared_modules_random_random()); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(random_random_obj, random_random); + +//| .. function:: uniform(a, b) +//| +//| Returns a random float between a and b. It may or may not be inclusive +//| depending on float rounding. +//| +STATIC mp_obj_t random_uniform(mp_obj_t a_in, mp_obj_t b_in) { + mp_float_t a = mp_obj_get_float(a_in); + mp_float_t b = mp_obj_get_float(b_in); + return mp_obj_new_float(shared_modules_random_uniform(a, b)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(random_uniform_obj, random_uniform); + +STATIC const mp_rom_map_elem_t mp_module_random_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_random) }, + { MP_ROM_QSTR(MP_QSTR_seed), MP_ROM_PTR(&random_seed_obj) }, + { MP_ROM_QSTR(MP_QSTR_getrandbits), MP_ROM_PTR(&random_getrandbits_obj) }, + { MP_ROM_QSTR(MP_QSTR_randrange), MP_ROM_PTR(&random_randrange_obj) }, + { MP_ROM_QSTR(MP_QSTR_randint), MP_ROM_PTR(&random_randint_obj) }, + { MP_ROM_QSTR(MP_QSTR_choice), MP_ROM_PTR(&random_choice_obj) }, + { MP_ROM_QSTR(MP_QSTR_random), MP_ROM_PTR(&random_random_obj) }, + { MP_ROM_QSTR(MP_QSTR_uniform), MP_ROM_PTR(&random_uniform_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(mp_module_random_globals, mp_module_random_globals_table); + +const mp_obj_module_t random_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&mp_module_random_globals, +}; diff --git a/shared-bindings/random/__init__.h b/shared-bindings/random/__init__.h new file mode 100644 index 0000000000..558b5f1942 --- /dev/null +++ b/shared-bindings/random/__init__.h @@ -0,0 +1,40 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Scott Shawcroft for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef __MICROPY_INCLUDED_SHARED_BINDINGS_RANDOM___INIT___H__ +#define __MICROPY_INCLUDED_SHARED_BINDINGS_RANDOM___INIT___H__ + +// This depends on shared_module because nearly all functionality is port +// agnostic. The random module only depends on the common_hal_os_urandom or +// common_hal_time_monotonic to seed it initially. + +void shared_modules_random_seed(mp_uint_t seed); +mp_uint_t shared_modules_random_getrandbits(uint8_t n); +mp_int_t shared_modules_random_randrange(mp_int_t start, mp_int_t stop, mp_int_t step); +mp_float_t shared_modules_random_random(void); +mp_float_t shared_modules_random_uniform(mp_float_t a, mp_float_t b); + +#endif // __MICROPY_INCLUDED_SHARED_BINDINGS_RANDOM___INIT___H__ diff --git a/shared-module/random/__init__.c b/shared-module/random/__init__.c new file mode 100644 index 0000000000..5cc20e5b5d --- /dev/null +++ b/shared-module/random/__init__.c @@ -0,0 +1,130 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Paul Sokolovsky + * Copyright (c) 2017 Scott Shawcroft for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include + +#include "py/runtime.h" +#include "shared-bindings/os/__init__.h" +#include "shared-bindings/time/__init__.h" + +// Yasmarang random number generator +// by Ilya Levin +// http://www.literatecode.com/yasmarang +// Public Domain + +STATIC uint32_t yasmarang_pad = 0xeda4baba, yasmarang_n = 69, yasmarang_d = 233; +STATIC uint8_t yasmarang_dat = 0; + +STATIC uint32_t yasmarang(void) +{ + if (yasmarang_pad == 0xeda4baba) { + if (!common_hal_os_urandom((uint8_t *)&yasmarang_pad, sizeof(uint32_t))) { + yasmarang_pad = common_hal_time_monotonic() & 0xffffffff; + } + } + yasmarang_pad += yasmarang_dat + yasmarang_d * yasmarang_n; + yasmarang_pad = (yasmarang_pad<<3) + (yasmarang_pad>>29); + yasmarang_n = yasmarang_pad | 2; + yasmarang_d ^= (yasmarang_pad<<31) + (yasmarang_pad>>1); + yasmarang_dat ^= (char) yasmarang_pad ^ (yasmarang_d>>8) ^ 1; + + return (yasmarang_pad^(yasmarang_d<<5)^(yasmarang_pad>>18)^(yasmarang_dat<<1)); +} /* yasmarang */ + +// End of Yasmarang + +// returns an unsigned integer below the given argument +// n must not be zero +STATIC uint32_t yasmarang_randbelow(uint32_t n) { + uint32_t mask = 1; + while ((n & mask) < n) { + mask = (mask << 1) | 1; + } + uint32_t r; + do { + r = yasmarang() & mask; + } while (r >= n); + return r; +} + +void shared_modules_random_seed(mp_uint_t seed) { + yasmarang_pad = seed; + yasmarang_n = 69; + yasmarang_d = 233; + yasmarang_dat = 0; +} + +mp_uint_t shared_modules_random_getrandbits(uint8_t n) { + uint32_t mask = ~0; + // Beware of C undefined behavior when shifting by >= than bit size + mask >>= (32 - n); + return yasmarang() & mask; +} + +mp_int_t shared_modules_random_randrange(mp_int_t start, mp_int_t stop, mp_int_t step) { + mp_int_t n; + if (step > 0) { + n = (stop - start + step - 1) / step; + } else { + n = (stop - start + step + 1) / step; + } + return start + step * yasmarang_randbelow(n); +} + +// returns a number in the range [0..1) using Yasmarang to fill in the fraction bits +STATIC mp_float_t yasmarang_float(void) { + #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE + typedef uint64_t mp_float_int_t; + #elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT + typedef uint32_t mp_float_int_t; + #endif + union { + mp_float_t f; + #if MP_ENDIANNESS_LITTLE + struct { mp_float_int_t frc:MP_FLOAT_FRAC_BITS, exp:MP_FLOAT_EXP_BITS, sgn:1; } p; + #else + struct { mp_float_int_t sgn:1, exp:MP_FLOAT_EXP_BITS, frc:MP_FLOAT_FRAC_BITS; } p; + #endif + } u; + u.p.sgn = 0; + u.p.exp = (1 << (MP_FLOAT_EXP_BITS - 1)) - 1; + if (MP_FLOAT_FRAC_BITS <= 32) { + u.p.frc = yasmarang(); + } else { + u.p.frc = ((uint64_t)yasmarang() << 32) | (uint64_t)yasmarang(); + } + return u.f - 1; +} + +mp_float_t shared_modules_random_random(void) { + return yasmarang_float(); +} + +mp_float_t shared_modules_random_uniform(mp_float_t a, mp_float_t b) { + return a + (b - a) * yasmarang_float(); +}