Files
prosperon/mersenne.c
2025-11-30 17:28:42 -06:00

146 lines
4.2 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_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);
)