add random module

This commit is contained in:
blueloveTH 2024-08-08 13:39:44 +08:00
parent 471d4ae578
commit 81edd22a76
8 changed files with 315 additions and 76 deletions

View File

@ -4,3 +4,4 @@ void pk__add_module_pkpy();
void pk__add_module_os();
void pk__add_module_math();
void pk__add_module_dis();
void pk__add_module_random();

View File

@ -450,6 +450,7 @@ py_TmpRef py_list_getitem(py_Ref self, int i);
void py_list_setitem(py_Ref self, int i, py_Ref val);
void py_list_delitem(py_Ref self, int i);
int py_list_len(py_Ref self);
void py_list_swap(py_Ref self, int i, int j);
void py_list_append(py_Ref self, py_Ref val);
void py_list_clear(py_Ref self);
void py_list_insert(py_Ref self, int i, py_Ref val);

View File

@ -534,7 +534,7 @@ FrameResult VM__run_top_frame(VM* self) {
if(magic) {
if(magic->type == tp_nativefunc) {
if(!magic->_cfunc(2, SECOND())) goto __ERROR;
POP();
STACK_SHRINK(2);
} else {
INSERT_THIRD(); // [?, b, a]
*THIRD() = *magic; // [__contains__, a, b]

View File

@ -3,6 +3,7 @@
#include "pocketpy/common/sstream.h"
#include "pocketpy/common/utils.h"
#include "pocketpy/interpreter/generator.h"
#include "pocketpy/interpreter/modules.h"
#include "pocketpy/objects/base.h"
#include "pocketpy/common/_generated.h"
#include "pocketpy/pocketpy.h"
@ -194,6 +195,7 @@ void VM__ctor(VM* self) {
pk__add_module_os();
pk__add_module_math();
pk__add_module_dis();
pk__add_module_random();
// add python builtins
do {

302
src/modules/random.c Normal file
View File

@ -0,0 +1,302 @@
#include "pocketpy/interpreter/vm.h"
#include "pocketpy/pocketpy.h"
#include <time.h>
/* https://github.com/clibs/mt19937ar
Copyright (c) 2011 Mutsuo Saito, Makoto Matsumoto, Hiroshima
University and The University of Tokyo. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of the Hiroshima University nor the names of
its contributors may be used to endorse or promote products
derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* Period parameters */
#define N 624
#define M 397
#define MATRIX_A 0x9908b0dfUL /* constant vector a */
#define UPPER_MASK 0x80000000UL /* most significant w-r bits */
#define LOWER_MASK 0x7fffffffUL /* least significant r bits */
typedef struct mt19937 {
uint32_t mt[N]; /* the array for the state vector */
int mti; /* mti==N+1 means mt[N] is not initialized */
} mt19937;
/* initializes mt[N] with a seed */
static void mt19937__seed(mt19937* self, uint32_t s) {
self->mt[0] = s & 0xffffffffUL;
for(self->mti = 1; self->mti < N; self->mti++) {
self->mt[self->mti] =
(1812433253UL * (self->mt[self->mti - 1] ^ (self->mt[self->mti - 1] >> 30)) +
self->mti);
/* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */
/* In the previous versions, MSBs of the seed affect */
/* only MSBs of the array mt[]. */
/* 2002/01/09 modified by Makoto Matsumoto */
self->mt[self->mti] &= 0xffffffffUL;
/* for >32 bit machines */
}
}
static void mt19937__ctor(mt19937* self) { self->mti = N + 1; }
/* generates a random number on [0,0xffffffff]-interval */
static uint32_t mt19937__next_uint32(mt19937* self) {
uint32_t* mt = self->mt;
uint32_t y;
static uint32_t mag01[2] = {0x0UL, MATRIX_A};
/* mag01[x] = x * MATRIX_A for x=0,1 */
if(self->mti >= N) { /* generate N words at one time */
int kk;
if(self->mti == N + 1) { /* if init_genrand() has not been called, */
mt19937__seed(self, clock());
// seed(5489UL); /* a default initial seed is used */
}
for(kk = 0; kk < N - M; kk++) {
y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK);
mt[kk] = mt[kk + M] ^ (y >> 1) ^ mag01[y & 0x1UL];
}
for(; kk < N - 1; kk++) {
y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK);
mt[kk] = mt[kk + (M - N)] ^ (y >> 1) ^ mag01[y & 0x1UL];
}
y = (mt[N - 1] & UPPER_MASK) | (mt[0] & LOWER_MASK);
mt[N - 1] = mt[M - 1] ^ (y >> 1) ^ mag01[y & 0x1UL];
self->mti = 0;
}
y = mt[self->mti++];
/* Tempering */
y ^= (y >> 11);
y ^= (y << 7) & 0x9d2c5680UL;
y ^= (y << 15) & 0xefc60000UL;
y ^= (y >> 18);
return y;
}
static uint64_t mt19937__next_uint64(mt19937* self) {
return (uint64_t)mt19937__next_uint32(self) << 32 | mt19937__next_uint32(self);
}
/* generates a random number on [0,1)-real-interval */
static float mt19937__random(mt19937* self) {
return mt19937__next_uint32(self) * (1.0 / 4294967296.0); /* divided by 2^32 */
}
static float mt19937__uniform(mt19937* self, float a, float b) {
if(a > b) { return b + mt19937__random(self) * (a - b); }
return a + mt19937__random(self) * (b - a);
}
/* generates a random number on [a, b]-interval */
int64_t mt19937__randint(mt19937* self, int64_t a, int64_t b) {
uint64_t delta = b - a + 1;
if(delta < 0x80000000UL) {
return a + mt19937__next_uint32(self) % delta;
} else {
return a + mt19937__next_uint64(self) % delta;
}
}
static bool Random__new__(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
mt19937* ud = py_newobject(py_retval(), py_totype(argv), 0, sizeof(mt19937));
mt19937__ctor(ud);
return true;
}
static bool Random_seed(int argc, py_Ref argv) {
PY_CHECK_ARGC(2);
PY_CHECK_ARG_TYPE(1, tp_int);
mt19937* ud = py_touserdata(py_arg(0));
py_i64 seed = py_toint(py_arg(1));
mt19937__seed(ud, seed);
py_newnone(py_retval());
return true;
}
static bool Random_random(int argc, py_Ref argv) {
PY_CHECK_ARGC(1);
mt19937* ud = py_touserdata(py_arg(0));
py_f64 res = mt19937__random(ud);
py_newfloat(py_retval(), res);
return true;
}
static bool Random_uniform(int argc, py_Ref argv) {
PY_CHECK_ARGC(3);
mt19937* ud = py_touserdata(py_arg(0));
py_f64 a, b;
if(!py_castfloat(py_arg(1), &a)) return false;
if(!py_castfloat(py_arg(2), &b)) return false;
py_f64 res = mt19937__uniform(ud, a, b);
py_newfloat(py_retval(), res);
return true;
}
static bool Random_shuffle(int argc, py_Ref argv) {
PY_CHECK_ARGC(2);
PY_CHECK_ARG_TYPE(1, tp_list);
mt19937* ud = py_touserdata(py_arg(0));
py_Ref L = py_arg(1);
int length = py_list_len(L);
for(int i = length - 1; i > 0; i--) {
int j = mt19937__randint(ud, 0, i);
py_list_swap(L, i, j);
}
py_newnone(py_retval());
return true;
}
static bool Random_randint(int argc, py_Ref argv) {
PY_CHECK_ARGC(3);
PY_CHECK_ARG_TYPE(1, tp_int);
PY_CHECK_ARG_TYPE(2, tp_int);
mt19937* ud = py_touserdata(py_arg(0));
py_i64 a = py_toint(py_arg(1));
py_i64 b = py_toint(py_arg(2));
if(a > b) return ValueError("randint(a, b): a must be less than or equal to b");
py_i64 res = mt19937__randint(ud, a, b);
py_newint(py_retval(), res);
return true;
}
static bool Random_choice(int argc, py_Ref argv) {
PY_CHECK_ARGC(2);
mt19937* ud = py_touserdata(py_arg(0));
int length;
py_TValue* p = pk_arrayview(py_arg(1), &length);
if(!p) return TypeError("choice(): argument must be a list or tuple");
if(length == 0) return IndexError("cannot choose from an empty sequence");
int index = mt19937__randint(ud, 0, length - 1);
py_assign(py_retval(), p + index);
return true;
}
static bool Random_choices(int argc, py_Ref argv) {
mt19937* ud = py_touserdata(py_arg(0));
int length;
py_TValue* p = pk_arrayview(py_arg(1), &length);
if(!p) return TypeError("choices(): argument must be a list or tuple");
if(length == 0) return IndexError("cannot choose from an empty sequence");
py_Ref weights = py_arg(2);
if(!py_checktype(py_arg(3), tp_int)) return false;
py_i64 k = py_toint(py_arg(3));
py_f64* cum_weights = malloc(sizeof(py_f64) * length);
if(py_isnone(weights)) {
for(int i = 0; i < length; i++)
cum_weights[i] = i + 1;
} else {
int wlen;
py_TValue* w = pk_arrayview(weights, &wlen);
if(!w) {
free(cum_weights);
return TypeError("choices(): weights must be a list or tuple");
}
if(wlen != length) {
free(cum_weights);
return ValueError("len(weights) != len(population)");
}
if(!py_castfloat(&w[0], &cum_weights[0])) {
free(cum_weights);
return false;
}
for(int i = 1; i < length; i++) {
py_f64 tmp;
if(!py_castfloat(&w[i], &tmp)) {
free(cum_weights);
return false;
}
cum_weights[i] = cum_weights[i - 1] + tmp;
}
}
py_f64 total = cum_weights[length - 1];
if(total <= 0) {
free(cum_weights);
return ValueError("total of weights must be greater than zero");
}
py_newlistn(py_retval(), k);
for(int i = 0; i < k; i++) {
py_f64 key = mt19937__random(ud) * total;
int index;
c11__lower_bound(py_f64, cum_weights, length, key, c11__less, &index);
assert(index != length);
py_list_setitem(py_retval(), i, p + index);
}
free(cum_weights);
return true;
}
void pk__add_module_random() {
py_Ref mod = py_newmodule("random");
py_Type type = py_newtype("Random", tp_object, mod, NULL);
py_bindmagic(type, __new__, Random__new__);
py_bindmethod(type, "seed", Random_seed);
py_bindmethod(type, "random", Random_random);
py_bindmethod(type, "uniform", Random_uniform);
py_bindmethod(type, "randint", Random_randint);
py_bindmethod(type, "shuffle", Random_shuffle);
py_bindmethod(type, "choice", Random_choice);
py_bind(py_tpobject(type), "choices(self, population, weights=None, k=1)", Random_choices);
py_Ref inst = py_pushtmp();
if(!py_tpcall(type, 0, NULL)) goto __ERROR;
py_assign(inst, py_retval());
#define ADD_INST_BOUNDMETHOD(name) \
if(!py_getattr(inst, py_name(name))) goto __ERROR; \
py_setdict(mod, py_name(name), py_retval());
ADD_INST_BOUNDMETHOD("seed");
ADD_INST_BOUNDMETHOD("random");
ADD_INST_BOUNDMETHOD("uniform");
ADD_INST_BOUNDMETHOD("randint");
ADD_INST_BOUNDMETHOD("shuffle");
ADD_INST_BOUNDMETHOD("choice");
ADD_INST_BOUNDMETHOD("choices");
#undef ADD_INST_BOUNDMETHOD
py_pop(); // pop inst
return;
__ERROR:
py_printexc();
c11__abort("failed to add module random");
}

View File

@ -44,6 +44,13 @@ int py_list_len(py_Ref self) {
return userdata->count;
}
void py_list_swap(py_Ref self, int i, int j){
py_TValue* data = py_list_data(self);
py_TValue tmp = data[i];
data[i] = data[j];
data[j] = tmp;
}
void py_list_append(py_Ref self, py_Ref val) {
List* userdata = py_touserdata(self);
c11_vector__push(py_TValue, userdata, *val);

View File

@ -1,4 +1,4 @@
import random as r, sys as s
import random as r
r.seed(10)

View File

@ -1,74 +0,0 @@
import c
a = c.malloc(100)
c.free(a)
a = c.malloc(100)
c.memset(a, 0, 100)
b = c.malloc(100)
b = c.memcpy(b, a, 100)
bp = c.p_cast(a, c.int_p)
assert c.p_value(c.NULL) == 0
assert c.NULL == c.NULL
assert c.NULL != a
for i in range(10):
bp[i] = i
assert bp[i] == i
(bp+i).write(i)
assert (bp+i).read() == i
i = c.float_(10)
assert i.sizeof() == 4
j = i.copy()
assert i == j
assert i is not j
####################
class Vec2(c.struct):
def __new__(cls, x: float, y: float):
obj = c.struct.__new__(cls, 8)
obj.write_float(x, 0)
obj.write_float(y, 4)
return obj
@property
def x(self) -> float:
return self.read_float(0)
@property
def y(self) -> float:
return self.read_float(4)
def __repr__(self) -> str:
return f"Vec2({self.x}, {self.y})"
a = Vec2(1, 2)
assert isinstance(a, c.struct)
assert type(a) is Vec2
assert repr(a) == "Vec2(1.0, 2.0)"
a = c.struct(10)
p = c.p_cast(a.addr(), c.char_p)
p.write_string("Hello!")
assert p[0] == ord("H")
assert p[1] == ord("e")
assert p[2] == ord("l")
assert p[3] == ord("l")
assert p[4] == ord("o")
assert p[5] == ord("!")
assert p[6] == 0
assert p.read_string() == "Hello!"
s = c.struct(67)
for i in range(67):
s.write_char(i, i)
s_hex = s.hex()
s_r = c.struct.fromhex(s_hex)
assert (s == s_r and s is not s_r), (s_hex, s_r.hex())
assert s_hex == s_r.hex()