#include "quickjs.h" #include "cell.h" #include // 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_DefinePropertyValueStr(js, obj, "seed", JS_NewFloat64(js, seed), JS_PROP_ENUMERABLE | JS_PROP_CONFIGURABLE // Read-only (no WRITABLE) ); 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_constructor, 0); )