143 lines
4.1 KiB
C
143 lines
4.1 KiB
C
#include "quickjs.h"
|
|
#include "cell.h"
|
|
#include <stdint.h>
|
|
|
|
// Random number generation constants for MT19937-64
|
|
#define STATE_VECTOR_LENGTH 312
|
|
#define STATE_VECTOR_M 156
|
|
#define NN STATE_VECTOR_LENGTH
|
|
#define MM STATE_VECTOR_M
|
|
#define MATRIX_A 0xB5026F5AA96619E9ULL
|
|
#define UM 0xFFFFFFFF80000000ULL /* Most significant 33 bits */
|
|
#define LM 0x7FFFFFFFULL /* Least significant 31 bits */
|
|
|
|
typedef struct tagMTRand {
|
|
uint64_t mt[STATE_VECTOR_LENGTH];
|
|
int32_t index;
|
|
} MTRand;
|
|
|
|
// Random number generation functions
|
|
static void m_seedRand(MTRand* rand, uint64_t seed) {
|
|
rand->mt[0] = seed;
|
|
for(rand->index = 1; rand->index < NN; rand->index++) {
|
|
rand->mt[rand->index] = (6364136223846793005ULL * (rand->mt[rand->index-1] ^ (rand->mt[rand->index-1] >> 62)) + rand->index);
|
|
}
|
|
}
|
|
|
|
static int64_t genRandLong(MTRand* rand) {
|
|
int i;
|
|
uint64_t x;
|
|
static uint64_t mag01[2] = {0ULL, MATRIX_A};
|
|
|
|
if (rand->index >= NN) { /* generate NN words at one time */
|
|
/* if init_genrand64() has not been called, */
|
|
/* a default initial seed is used */
|
|
if (rand->index == NN+1)
|
|
m_seedRand(rand, 5489ULL);
|
|
|
|
for (i = 0; i < NN-MM; i++) {
|
|
x = (rand->mt[i] & UM) | (rand->mt[i+1] & LM);
|
|
rand->mt[i] = rand->mt[i+MM] ^ (x>>1) ^ mag01[(int)(x&1ULL)];
|
|
}
|
|
for (; i < NN-1; i++) {
|
|
x = (rand->mt[i] & UM) | (rand->mt[i+1] & LM);
|
|
rand->mt[i] = rand->mt[i+(MM-NN)] ^ (x>>1) ^ mag01[(int)(x&1ULL)];
|
|
}
|
|
x = (rand->mt[NN-1] & UM) | (rand->mt[0] & LM);
|
|
rand->mt[NN-1] = rand->mt[MM-1] ^ (x>>1) ^ mag01[(int)(x&1ULL)];
|
|
|
|
rand->index = 0;
|
|
}
|
|
|
|
x = rand->mt[rand->index++];
|
|
|
|
x ^= (x >> 29) & 0x5555555555555555ULL;
|
|
x ^= (x << 17) & 0x71D67FFFEDA60000ULL;
|
|
x ^= (x << 37) & 0xFFF7EEE000000000ULL;
|
|
x ^= (x >> 43);
|
|
|
|
return (int64_t)(x & 0x000FFFFFFFFFFFFFULL); /* return 52-bit value safe for JS */
|
|
}
|
|
|
|
static double genRand(MTRand* rand) {
|
|
/* generates a random number on [0,1)-real-interval */
|
|
return (genRandLong(rand) >> 11) * (1.0/9007199254740992.0);
|
|
}
|
|
|
|
/* JS Class Definition */
|
|
static JSClassID js_mersenne_class_id;
|
|
|
|
static void js_mersenne_finalizer(JSRuntime *rt, JSValue val) {
|
|
MTRand *mrand = JS_GetOpaque(val, js_mersenne_class_id);
|
|
js_free_rt(rt, mrand);
|
|
}
|
|
|
|
static JSClassDef js_mersenne_class = {
|
|
"Mersenne",
|
|
.finalizer = js_mersenne_finalizer,
|
|
};
|
|
|
|
static MTRand *js2mersenne(JSContext *js, JSValue v) {
|
|
return JS_GetOpaque(v, js_mersenne_class_id);
|
|
}
|
|
|
|
/* Methods */
|
|
JSC_CCALL(mersenne_get,
|
|
MTRand *mrand = js2mersenne(js, self);
|
|
if (!mrand) return JS_ThrowTypeError(js, "Invalid mersenne context");
|
|
return JS_NewFloat64(js, genRand(mrand));
|
|
)
|
|
|
|
static const JSCFunctionListEntry js_mersenne_funcs[] = {
|
|
JS_CFUNC_DEF("get", 0, js_mersenne_get),
|
|
};
|
|
|
|
/* Factory Function (constructor-style, see js_blob_constructor in qjs_blob.c) */
|
|
static JSValue js_mersenne_use_call(JSContext *js, JSValueConst new_target, int argc, JSValueConst *argv)
|
|
{
|
|
uint64_t seed;
|
|
|
|
if (argc == 0 || JS_IsNull(argv[0])) {
|
|
// Use OS random
|
|
extern int randombytes(void *buf, size_t n);
|
|
randombytes(&seed, 8);
|
|
} else {
|
|
if (JS_ToFloat64(js, (double*)&seed, argv[0])) {
|
|
// Fallback to number if bigint fails or is not provided as bigint
|
|
double d;
|
|
if (JS_ToFloat64(js, &d, argv[0])) return JS_EXCEPTION;
|
|
seed = (uint64_t)d;
|
|
}
|
|
}
|
|
|
|
MTRand *mrand = js_malloc(js, sizeof(MTRand));
|
|
if (!mrand) return JS_ThrowOutOfMemory(js);
|
|
|
|
m_seedRand(mrand, seed);
|
|
|
|
JSValue obj = JS_NewObjectClass(js, js_mersenne_class_id);
|
|
if (JS_IsException(obj)) {
|
|
js_free(js, mrand);
|
|
return obj;
|
|
}
|
|
|
|
JS_SetOpaque(obj, mrand);
|
|
|
|
// Store seed as a read-only property
|
|
JS_SetPropertyStr(js, obj, "seed", JS_NewFloat64(js, seed));
|
|
|
|
return obj;
|
|
}
|
|
|
|
CELL_USE_INIT(
|
|
JS_NewClassID(&js_mersenne_class_id);
|
|
JS_NewClass(JS_GetRuntime(js), js_mersenne_class_id, &js_mersenne_class);
|
|
|
|
JSValue proto = JS_NewObject(js);
|
|
JS_SetPropertyFunctionList(js, proto, js_mersenne_funcs, sizeof(js_mersenne_funcs)/sizeof(JSCFunctionListEntry));
|
|
JS_SetClassProto(js, js_mersenne_class_id, proto);
|
|
|
|
// Return the factory function
|
|
return JS_NewCFunction2(js, js_mersenne_use_call, "mersenne", 1, JS_CFUNC_generic, 0);
|
|
)
|