#include "inc/priv.h"#include "inc/cvalues.h"#include "inc/equal.h"#include "inc/operators.h"#include "inc/types.h"#include "pcg_variants.h" typedef pcg32x2_random_t pcg_t; static sl_v pcg32x2sym, randomstatesym;static const sl_type *u64arr; sl_nonnull(1)static u64intgenrand64_int64(pcg_t *c){ return pcg32x2_random_r(c);} sl_nonnull(1)static doublegenrand64_real1(pcg_t *c){ return (pcg32x2_random_r(c) >> 11) * (1.0/9007199254740991.0);} sl_nonnull(1)sl_wr(1, 2)static voidgenrnd(u8int *b, int n){ for(int i = 0; i < n;){ u64int x = genrand64_int64(sl.randstatectx); for(int j = 0; j < 8 && i < n; j++, i++){ *b++ = x; x >>= 8; } }} sl_nonnull(1)static mpint *bigrand(pcg_t *c, mpint *n){ int bits = mpsignif(n); mpint *b = mpnew(bits); sl.randstatectx = c; do mprand(bits, genrnd, b); while(mpmagcmp(b, n) > 0); sl.randstatectx = nil; return b;} static boolispcg(sl_v v){ if(!iscons(v) || car_(v) != pcg32x2sym) return false; v = cdr_(v); sl_cv *cv = ptr(sl_cv, v); return iscvalue(v) && cv_class(cv) == u64arr && cv_len(cv) == 4*sizeof(u64int);} BUILTIN("random-state?", random_statep){ argcount(1); return ispcg(args[0]) ? sl_t : sl_nil;} static voidtopcg(pcg_t *c, sl_v v){ if(sl_unlikely(!ispcg(v))) cthrow(type_error(nil, "random state", v), v); u64int *r = cvalue_data(cdr_(v)); c->gen[0].state = r[0]; c->gen[0].inc = r[1]; c->gen[1].state = r[2]; c->gen[1].inc = r[3];} static sl_vmk_pcg(sl_v v, const pcg_t *c){ if(v == sl_nil) v = mk_cons(pcg32x2sym, cvalue(u64arr, 4*sizeof(u64int))); u64int *r = cvalue_data(cdr_(v)); r[0] = c->gen[0].state; r[1] = c->gen[0].inc; r[2] = c->gen[1].state; r[3] = c->gen[1].inc; return v;} BUILTIN("make-random-state", make_random_state){ sl_v v = sl_nil; if(nargs != 0){ argcount_most(1); v = args[0]; } u64int r[4]; pcg_t c; if(v == sl_nil) // NIL = refer to the current state v = sl.randstate; if(v == sl_t || v == sl_nil){ // make a new one sys_seed(r, sizeof(r)); pcg32x2_srandom_r(&c, r[0], r[1], r[2], r[3]); }else if(ispcg(v)){ // a copy of the supplied state topcg(&c, v); }else{ bthrow(type_error("state", "random state", v)); } if(v == sl_nil){ // no current state? make a copy and set sl.randstate = mk_pcg(sl_nil, &c); set(randomstatesym, sl.randstate); // FIXME(sigrid): proc env } return mk_pcg(sl_nil, &c);} BUILTIN("random", random){ argcount_least(1); argcount_most(2); sl_v state = nargs == 2 ? args[1] : sl.randstate; pcg_t c; if(nargs == 2 || state != sl_nil) topcg(&c, state); else{ // no current state, make one u64int r[4]; sys_seed(r, sizeof(r)); pcg32x2_srandom_r(&c, r[0], r[1], r[2], r[3]); } sl_v v = args[0]; if(sl_unlikely(!sl_isnum(v))) cthrow(type_error("max", "num", v), v); soffset max; if(isfixnum(v)){ max = numval(v); if(max <= 0){badrange: bthrow(error(sl_errarg, "invalid range")); } sl_fx x = genrand64_int64(&c) & (((sl_fx)1<<(FIXNUM_BITS-1))-1); x %= max + 1; v = fixnum(x); }else if(iscvalue(v)){ sl_cv *cv = ptr(sl_cv, v); void *data = cv_data(cv); int nt = cv_numtype(cv); if(nt == T_F32){ float f = *(float*)data; if(f <= 0) goto badrange; v = mk_f32(f * genrand64_real1(&c)); goto done; } if(nt == T_F64){ double d = *(double*)data; if(d <= 0) goto badrange; v = mk_f64(d * genrand64_real1(&c)); goto done; } if(sl_unlikely(numeric_compare(v, fixnum(0), false) <= 0)) goto badrange; mpint *m, *b; if(nt == T_BIG){ int n; b = *(mpint**)data; m = bigrand(&c, b);big: n = mpsignif(m); if(n >= FIXNUM_BITS) v = mk_bignum(m); else{ sl_fx x = mptov(m); mpfree(m); v = fixnum(x); } goto done; } max = conv_to_u64(v, data, nt); if(max > FIXNUM_MAX){ b = uvtomp(max, nil); m = bigrand(&c, b); mpfree(b); goto big; } u64int x = genrand64_int64(&c); x %= max + 1; v = fixnum(x); }else{ max = tooffset(v); if(max <= 0) goto badrange; u64int x = genrand64_int64(&c); x %= max + 1; v = fixnum(x); } done: if(state == sl_nil){ PUSH(v); sl.randstate = mk_pcg(sl_nil, &c); set(randomstatesym, sl.randstate); // FIXME(sigrid): proc env v = POP(); }else mk_pcg(state, &c); return v;} voidrandom_init(void){ pcg32x2sym = mk_csym("pcg32x2"); randomstatesym = mk_csym("*random-state*"); u64arr = get_arr_type(sl_u64sym); set(randomstatesym, sl_nil); // FIXME(sigrid): proc env}
#include "inc/priv.h"
#include "inc/cvalues.h"
#include "inc/equal.h"
#include "inc/operators.h"
#include "inc/types.h"
#include "pcg_variants.h"
typedef pcg32x2_random_t pcg_t;
static sl_v pcg32x2sym, randomstatesym;
static const sl_type *u64arr;
sl_nonnull(1)
static u64int
genrand64_int64(pcg_t *c)
{
return pcg32x2_random_r(c);
}
sl_nonnull(1)
static double
genrand64_real1(pcg_t *c)
{
return (pcg32x2_random_r(c) >> 11) * (1.0/9007199254740991.0);
}
sl_nonnull(1)
sl_wr(1, 2)
static void
genrnd(u8int *b, int n)
{
for(int i = 0; i < n;){
u64int x = genrand64_int64(sl.randstatectx);
for(int j = 0; j < 8 && i < n; j++, i++){
*b++ = x;
x >>= 8;
}
}
}
sl_nonnull(1)
static mpint *
bigrand(pcg_t *c, mpint *n)
{
int bits = mpsignif(n);
mpint *b = mpnew(bits);
sl.randstatectx = c;
do
mprand(bits, genrnd, b);
while(mpmagcmp(b, n) > 0);
sl.randstatectx = nil;
return b;
}
static bool
ispcg(sl_v v)
{
if(!iscons(v) || car_(v) != pcg32x2sym)
return false;
v = cdr_(v);
sl_cv *cv = ptr(sl_cv, v);
return iscvalue(v) && cv_class(cv) == u64arr && cv_len(cv) == 4*sizeof(u64int);
}
BUILTIN("random-state?", random_statep)
{
argcount(1);
return ispcg(args[0]) ? sl_t : sl_nil;
}
static void
topcg(pcg_t *c, sl_v v)
{
if(sl_unlikely(!ispcg(v)))
cthrow(type_error(nil, "random state", v), v);
u64int *r = cvalue_data(cdr_(v));
c->gen[0].state = r[0];
c->gen[0].inc = r[1];
c->gen[1].state = r[2];
c->gen[1].inc = r[3];
}
static sl_v
mk_pcg(sl_v v, const pcg_t *c)
{
if(v == sl_nil)
v = mk_cons(pcg32x2sym, cvalue(u64arr, 4*sizeof(u64int)));
u64int *r = cvalue_data(cdr_(v));
r[0] = c->gen[0].state;
r[1] = c->gen[0].inc;
r[2] = c->gen[1].state;
r[3] = c->gen[1].inc;
return v;
}
BUILTIN("make-random-state", make_random_state)
{
sl_v v = sl_nil;
if(nargs != 0){
argcount_most(1);
v = args[0];
}
u64int r[4];
pcg_t c;
if(v == sl_nil) // NIL = refer to the current state
v = sl.randstate;
if(v == sl_t || v == sl_nil){ // make a new one
sys_seed(r, sizeof(r));
pcg32x2_srandom_r(&c, r[0], r[1], r[2], r[3]);
}else if(ispcg(v)){ // a copy of the supplied state
topcg(&c, v);
}else{
bthrow(type_error("state", "random state", v));
}
if(v == sl_nil){ // no current state? make a copy and set
sl.randstate = mk_pcg(sl_nil, &c);
set(randomstatesym, sl.randstate); // FIXME(sigrid): proc env
}
return mk_pcg(sl_nil, &c);
}
BUILTIN("random", random)
{
argcount_least(1);
argcount_most(2);
sl_v state = nargs == 2 ? args[1] : sl.randstate;
pcg_t c;
if(nargs == 2 || state != sl_nil)
topcg(&c, state);
else{ // no current state, make one
u64int r[4];
sys_seed(r, sizeof(r));
pcg32x2_srandom_r(&c, r[0], r[1], r[2], r[3]);
}
sl_v v = args[0];
if(sl_unlikely(!sl_isnum(v)))
cthrow(type_error("max", "num", v), v);
soffset max;
if(isfixnum(v)){
max = numval(v);
if(max <= 0){
badrange:
bthrow(error(sl_errarg, "invalid range"));
}
sl_fx x = genrand64_int64(&c) & (((sl_fx)1<<(FIXNUM_BITS-1))-1);
x %= max + 1;
v = fixnum(x);
}else if(iscvalue(v)){
sl_cv *cv = ptr(sl_cv, v);
void *data = cv_data(cv);
int nt = cv_numtype(cv);
if(nt == T_F32){
float f = *(float*)data;
if(f <= 0)
goto badrange;
v = mk_f32(f * genrand64_real1(&c));
goto done;
}
if(nt == T_F64){
double d = *(double*)data;
if(d <= 0)
goto badrange;
v = mk_f64(d * genrand64_real1(&c));
goto done;
}
if(sl_unlikely(numeric_compare(v, fixnum(0), false) <= 0))
goto badrange;
mpint *m, *b;
if(nt == T_BIG){
int n;
b = *(mpint**)data;
m = bigrand(&c, b);
big:
n = mpsignif(m);
if(n >= FIXNUM_BITS)
v = mk_bignum(m);
else{
sl_fx x = mptov(m);
mpfree(m);
v = fixnum(x);
}
goto done;
}
max = conv_to_u64(v, data, nt);
if(max > FIXNUM_MAX){
b = uvtomp(max, nil);
m = bigrand(&c, b);
mpfree(b);
goto big;
}
u64int x = genrand64_int64(&c);
x %= max + 1;
v = fixnum(x);
}else{
max = tooffset(v);
if(max <= 0)
goto badrange;
u64int x = genrand64_int64(&c);
x %= max + 1;
v = fixnum(x);
}
done:
if(state == sl_nil){
PUSH(v);
sl.randstate = mk_pcg(sl_nil, &c);
set(randomstatesym, sl.randstate); // FIXME(sigrid): proc env
v = POP();
}else
mk_pcg(state, &c);
return v;
}
void
random_init(void)
{
pcg32x2sym = mk_csym("pcg32x2");
randomstatesym = mk_csym("*random-state*");
u64arr = get_arr_type(sl_u64sym);
set(randomstatesym, sl_nil); // FIXME(sigrid): proc env
}