187 lines
3.9 KiB
C++
187 lines
3.9 KiB
C++
#include <random>
|
|
#include "threads.h"
|
|
#include <time.h>
|
|
#include "constants.h"
|
|
#include "vec3.h"
|
|
#include "vec2.h"
|
|
#include "main/references.h"
|
|
#include "os/driver.h"
|
|
#include "random.h"
|
|
#if defined(_MSC_VER)
|
|
#include <intrin.h>
|
|
#define GET_MSB(var, x) do{ unsigned long _index_; _BitScanReverse(&_index_, x); var = _index_ + 1; } while(false)
|
|
#elif defined(__GNUC__)
|
|
#define GET_MSB(var, x) do { var = (32 - __builtin_clz(x)); } while(false)
|
|
#else
|
|
unsigned _get_msb(unsigned v) {
|
|
unsigned index = 0;
|
|
while((1 << index) <= v)
|
|
++index;
|
|
return index;
|
|
}
|
|
#define GET_MSB(var, x) do { var = _get_msb(x); } while(false)
|
|
#endif
|
|
|
|
Threaded(std::mt19937*) engine;
|
|
|
|
void seed(unsigned long seed);
|
|
|
|
void initRandomizer() {
|
|
engine = new std::mt19937();
|
|
seed((unsigned long)time(0) ^ (unsigned long)threads::getThreadID());
|
|
}
|
|
|
|
void freeRandomizer() {
|
|
delete engine;
|
|
}
|
|
|
|
void seed(unsigned long seed) {
|
|
engine->seed(seed);
|
|
}
|
|
|
|
unsigned sysRandomi() {
|
|
unsigned ret = 0;
|
|
if(!devices.driver->systemRandom((unsigned char*)&ret, 4)) {
|
|
if(engine)
|
|
ret = (unsigned)randomi();
|
|
else {
|
|
initRandomizer();
|
|
ret = (unsigned)randomi();
|
|
freeRandomizer();
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
double randomd() {
|
|
//Choose random numbers until we don't get max
|
|
unsigned m = engine->max();
|
|
unsigned r;
|
|
do {
|
|
r = (*engine)();
|
|
} while(r == m);
|
|
return (double)r / (double)m;
|
|
}
|
|
|
|
double randomd(double min, double max) {
|
|
return randomd() * (max - min) + min;
|
|
}
|
|
|
|
double normald(double min, double max, int steps) {
|
|
double sum = 0;
|
|
|
|
for(int i = 0; i < steps; ++i)
|
|
sum += randomd();
|
|
|
|
return min + (max-min)*sum/(double)steps;
|
|
}
|
|
|
|
float randomf() {
|
|
return (float)randomd();
|
|
}
|
|
|
|
float randomf(float min, float max) {
|
|
return (float)(randomd() * (double)(max - min)) + min;
|
|
}
|
|
|
|
unsigned randomi() {
|
|
return (*engine)();
|
|
}
|
|
|
|
int randomi(int min, int max) {
|
|
unsigned range = (unsigned)max - (unsigned)min;
|
|
if(range == 0)
|
|
return min;
|
|
|
|
unsigned msb;
|
|
GET_MSB(msb, range);
|
|
|
|
unsigned mask = 0xffffffff >> (32 - msb);
|
|
|
|
//Choose uniformly distributed values until one falls into our range
|
|
//Worst case scenario is the possible values is split in half (+1), so it will still resolve quickly
|
|
unsigned r;
|
|
do {
|
|
r = (unsigned)((*engine)());
|
|
} while((r & mask) > range);
|
|
|
|
return min + (r & mask);
|
|
}
|
|
|
|
vec3d random3d(double radius) {
|
|
double theta = randomd(0, twopi);
|
|
|
|
double u = randomd(-1.0, 1.0);
|
|
double s = sqrt(1.0-(u*u));
|
|
|
|
vec3d out;
|
|
out.x = s * cos(theta) * radius;
|
|
out.y = s * sin(theta) * radius;
|
|
out.z = u * radius;
|
|
|
|
return out;
|
|
}
|
|
|
|
vec3d random3d(double minRadius, double maxRadius) {
|
|
return random3d(minRadius + (maxRadius - minRadius) * sqrt(randomd()));
|
|
}
|
|
|
|
vec2d random2d(double radius) {
|
|
double theta = randomd(0, twopi);
|
|
return vec2d(radius * cos(theta), radius * sin(theta));
|
|
}
|
|
|
|
vec2d random2d(double minRadius, double maxRadius) {
|
|
return random2d(minRadius + (maxRadius - minRadius) * sqrt(randomd()));
|
|
}
|
|
|
|
class MersenneEngine : public RandomEngine {
|
|
std::mt19937 rnd;
|
|
public:
|
|
void seed(unsigned initial) {
|
|
rnd.seed((unsigned long)initial);
|
|
}
|
|
|
|
unsigned randomi() {
|
|
return rnd();
|
|
}
|
|
|
|
unsigned randomi(unsigned min, unsigned max) {
|
|
unsigned range = (unsigned)max - (unsigned)min;
|
|
if(range == 0)
|
|
return min;
|
|
|
|
unsigned msb;
|
|
GET_MSB(msb, range);
|
|
|
|
unsigned mask = 0xffffffff >> (32 - msb);
|
|
|
|
//Choose uniformly distributed values until one falls into our range
|
|
//Worst case scenario is the possible values is split in half (+1), so it will still resolve quickly
|
|
unsigned r;
|
|
do {
|
|
r = (unsigned)(rnd());
|
|
} while((r & mask) > range);
|
|
|
|
return min + (r & mask);
|
|
}
|
|
|
|
double randomd() {
|
|
unsigned m = rnd.max();
|
|
unsigned r;
|
|
do {
|
|
r = rnd();
|
|
} while(r == m);
|
|
return (double)r / (double)m;
|
|
}
|
|
|
|
double randomd(double min, double max) {
|
|
return (randomd() * (max - min)) + min;
|
|
}
|
|
};
|
|
|
|
RandomEngine* RandomEngine::makeMersenne(unsigned seed) {
|
|
auto* engine = new MersenneEngine();
|
|
engine->seed(seed);
|
|
return engine;
|
|
} |