Added love.math.newRandomGenerator. Returns a RNG object with its own seed and random methods.

--HG--
branch : math-randobject
This commit is contained in:
Alex Szpakowski
2013-04-14 23:02:44 -03:00
parent 96f4f532bb
commit 3046981fce
10 changed files with 463 additions and 122 deletions
+7 -45
View File
@@ -18,7 +18,7 @@
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "modules/math/MathModule.h"
#include "MathModule.h"
#include "common/math.h"
#include <cmath>
@@ -81,60 +81,22 @@ namespace math
Math Math::instance;
// 64 bit Xorshift implementation taken from the end of Sec. 3 (page 4) in
// George Marsaglia, "Xorshift RNGs", Journal of Statistical Software, Vol.8 (Issue 14), 2003
Math::Math()
: last_randnormal(numeric_limits<double>::infinity())
: rng(newRandomGenerator())
{
// because it is too big for some compilers to handle ... if you know what
// i mean
union
{
uint64 b64;
struct
{
uint32 a;
uint32 b;
} b32;
} converter;
#ifdef LOVE_BIG_ENDIAN
converter.b32.a = 0x0139408D;
converter.b32.b = 0xCBBF7A44;
#else
converter.b32.b = 0x0139408D;
converter.b32.a = 0xCBBF7A44;
#endif
rng_state = converter.b64;
// prevent the runtime from free()-ing this
retain();
}
uint64 Math::rand()
Math::~Math()
{
rng_state ^= (rng_state << 13);
rng_state ^= (rng_state >> 7);
rng_state ^= (rng_state << 17);
return rng_state;
if (rng)
rng->release();
}
// BoxMuller transform
double Math::randnormal(double stddev)
RandomGenerator *Math::newRandomGenerator()
{
// use cached number if possible
if (last_randnormal != numeric_limits<double>::infinity())
{
double r = last_randnormal;
last_randnormal = numeric_limits<double>::infinity();
return r * stddev;
}
double r = sqrt(-2.0 * log(1. - random()));
double phi = 2.0 * LOVE_M_PI * (1. - random());
last_randnormal = r * cos(phi);
return r * sin(phi) * stddev;
return new RandomGenerator();
}
vector<Triangle> Math::triangulate(const vector<vertex> &polygon)
+45 -45
View File
@@ -21,6 +21,8 @@
#ifndef LOVE_MATH_MODMATH_H
#define LOVE_MATH_MODMATH_H
#include "RandomGenerator.h"
// LOVE
#include "common/Module.h"
#include "common/math.h"
@@ -28,7 +30,6 @@
// STL
#include <limits>
#include <stdint.h>
#include <vector>
namespace love
@@ -38,80 +39,79 @@ namespace math
class Math : public Module
{
public:
virtual ~Math()
{}
private:
/** Set pseudo random seed.
*
* It's up to the implementation how to use this.
*
* @param seed The random seed.
*/
inline void randomseed(uint64_t seed)
RandomGenerator *rng;
public:
virtual ~Math();
/**
* @copydoc RandomGenerator::randomseed()
**/
inline void randomseed(uint64 seed)
{
rng_state = seed;
rng->randomseed(seed);
}
/** Return uniformly distributed pseudo random integer.
*
* @returns Pseudo random integer in [0,2^64).
*/
uint64 rand();
/** Get uniformly distributed pseudo random number in [0,1).
*
* @returns Pseudo random number in [0,1).
*/
/**
* @copydoc RandomGenerator::random()
**/
inline double random()
{
return double(rand()) / (double(std::numeric_limits<uint64>::max()) + 1.0);
return rng->random();
}
/** Get uniformly distributed pseudo random number in [0,max).
*
* @returns Pseudo random number in [0,max).
*/
/**
* @copydoc RandomGenerator::random(double)
**/
inline double random(double max)
{
return random() * max;
return rng->random(max);
}
/** Get uniformly distributed pseudo random number in [min, max).
*
* @returns Pseudo random number in [min, max).
*/
/**
* @copydoc RandomGenerator::random(double,double)
**/
inline double random(double min, double max)
{
return random() * (max - min) + min;
return rng->random(min, max);
}
/** Get normally distributed pseudo random number.
*
* @param stddev Standard deviation of the distribution.
* @returns Normally distributed random number with mean 0 and variance (stddev)².
*/
double randnormal(double stddev);
/**
* @copydoc RandomGenerator::randnormal()
**/
inline double randnormal(double stddev)
{
return rng->randnormal(stddev);
}
/**
* Create a new random number generator.
**/
RandomGenerator *newRandomGenerator();
virtual const char *getName() const
{
return "love.math";
}
/** Triangulate a simple polygon.
/**
* Triangulate a simple polygon.
*
* @param polygon Polygon to triangulate. Must not intersect itself.
* @returns List of triangles the polygon is composed of.
*/
* @return List of triangles the polygon is composed of.
**/
std::vector<Triangle> triangulate(const std::vector<vertex> &polygon);
static Math instance;
private:
Math();
uint64 rng_state;
double last_randnormal;
};
}; // Math
} // math
} // love
+87
View File
@@ -0,0 +1,87 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "RandomGenerator.h"
// STL
#include <cmath>
namespace love
{
namespace math
{
// 64 bit Xorshift implementation taken from the end of Sec. 3 (page 4) in
// George Marsaglia, "Xorshift RNGs", Journal of Statistical Software, Vol.8 (Issue 14), 2003
RandomGenerator::RandomGenerator()
: last_randnormal(std::numeric_limits<double>::infinity())
{
// because it is too big for some compilers to handle ... if you know what
// i mean
union
{
uint64 b64;
struct
{
uint32 a;
uint32 b;
} b32;
} converter;
#ifdef LOVE_BIG_ENDIAN
converter.b32.a = 0x0139408D;
converter.b32.b = 0xCBBF7A44;
#else
converter.b32.b = 0x0139408D;
converter.b32.a = 0xCBBF7A44;
#endif
rng_state = converter.b64;
}
uint64 RandomGenerator::rand()
{
rng_state ^= (rng_state << 13);
rng_state ^= (rng_state >> 7);
rng_state ^= (rng_state << 17);
return rng_state;
}
// BoxMuller transform
double RandomGenerator::randnormal(double stddev)
{
// use cached number if possible
if (last_randnormal != std::numeric_limits<double>::infinity())
{
double r = last_randnormal;
last_randnormal = std::numeric_limits<double>::infinity();
return r * stddev;
}
double r = sqrt(-2.0 * log(1. - random()));
double phi = 2.0 * LOVE_M_PI * (1. - random());
last_randnormal = r * cos(phi);
return r * sin(phi) * stddev;
}
} // math
} // love
+111
View File
@@ -0,0 +1,111 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_MATH_RANDOM_GENERATOR_H
#define LOVE_MATH_RANDOM_GENERATOR_H
// LOVE
#include "common/math.h"
#include "common/int.h"
#include "common/Object.h"
// STL
#include <limits>
namespace love
{
namespace math
{
class RandomGenerator : public Object
{
public:
RandomGenerator();
virtual ~RandomGenerator() {};
/**
* Set pseudo random seed.
* It's up to the implementation how to use this.
*
* @param seed The random seed.
**/
inline void randomseed(uint64 seed)
{
rng_state = seed;
}
/**
* Return uniformly distributed pseudo random integer.
*
* @return Pseudo random integer in [0,2^64).
**/
uint64 rand();
/**
* Get uniformly distributed pseudo random number in [0,1).
*
* @return Pseudo random number in [0,1).
**/
inline double random()
{
return double(rand()) / (double(std::numeric_limits<uint64>::max()) + 1.0);
}
/**
* Get uniformly distributed pseudo random number in [0,max).
*
* @return Pseudo random number in [0,max).
**/
inline double random(double max)
{
return random() * max;
}
/**
* Get uniformly distributed pseudo random number in [min, max).
*
* @return Pseudo random number in [min, max).
**/
inline double random(double min, double max)
{
return random() * (max - min) + min;
}
/**
* Get normally distributed pseudo random number.
*
* @param stddev Standard deviation of the distribution.
* @return Normally distributed random number with mean 0 and variance (stddev)².
**/
double randnormal(double stddev);
private:
uint64 rng_state;
double last_randnormal;
}; // RandomGenerator
} // math
} // love
#endif // LOVE_MATH_RANDOM_GENERATOR_H
+21 -32
View File
@@ -19,7 +19,8 @@
**/
#include "wrap_Math.h"
#include "modules/math/MathModule.h"
#include "wrap_RandomGenerator.h"
#include "MathModule.h"
#include <cmath>
#include <iostream>
@@ -31,42 +32,14 @@ namespace math
int w_randomseed(lua_State *L)
{
union
{
double seed_double;
uint64_t seed_uint;
} s;
s.seed_double = luaL_checknumber(L, 1);
Math::instance.randomseed(s.seed_uint);
uint64 seed = luax_checkrandomseed(L, 1);
Math::instance.randomseed(seed);
return 0;
}
int w_random(lua_State *L)
{
double r = Math::instance.random();
int l, u;
// verbatim from lua 5.1.4 source code: lmathlib.c:185 ff.
switch (lua_gettop(L))
{
case 0:
lua_pushnumber(L, r);
break;
case 1:
u = luaL_checkint(L, 1);
luaL_argcheck(L, 1 <= u, 1, "interval is empty");
lua_pushnumber(L, floor(r * u) + 1);
break;
case 2:
l = luaL_checkint(L, 1);
u = luaL_checkint(L, 2);
luaL_argcheck(L, l <= u, 2, "interval is empty");
lua_pushnumber(L, floor(r * (u - l + 1)) + l);
break;
default:
return luaL_error(L, "wrong number of arguments");
}
return 1;
return luax_getrandom(L, 1, Math::instance.random());
}
int w_randnormal(lua_State *L)
@@ -87,6 +60,20 @@ int w_randnormal(lua_State *L)
return 1;
}
int w_newRandomGenerator(lua_State *L)
{
RandomGenerator *t = Math::instance.newRandomGenerator();
if (lua_gettop(L) > 0)
{
uint64 seed = luax_checkrandomseed(L, 1);
t->randomseed(seed);
}
luax_newtype(L, "RandomGenerator", MATH_RANDOM_GENERATOR_T, (void *) t);
return 1;
}
int w_triangulate(lua_State *L)
{
std::vector<vertex> vertices;
@@ -167,12 +154,14 @@ static const luaL_Reg functions[] =
{ "randomseed", w_randomseed },
{ "random", w_random },
{ "randnormal", w_randnormal },
{ "newRandomGenerator", w_newRandomGenerator },
{ "triangulate", w_triangulate },
{ 0, 0 }
};
static const lua_CFunction types[] =
{
luaopen_randomgenerator,
0
};
+1
View File
@@ -33,6 +33,7 @@ namespace math
int w_randomseed(lua_State *L);
int w_random(lua_State *L);
int w_randnormal(lua_State *L);
int w_newRandomGenerator(lua_State *L);
int w_triangulate(lua_State *L);
extern "C" LOVE_EXPORT int luaopen_love_math(lua_State *L);
+122
View File
@@ -0,0 +1,122 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "wrap_RandomGenerator.h"
#include <cmath>
namespace love
{
namespace math
{
uint64 luax_checkrandomseed(lua_State *L, int idx)
{
union
{
double seed_double;
uint64 seed_uint;
} s;
s.seed_double = luaL_checknumber(L, idx);
return s.seed_uint;
}
int luax_getrandom(lua_State *L, int startidx, double r)
{
int l, u;
// from lua 5.1.4 source code: lmathlib.c:185 ff.
switch (lua_gettop(L) - (startidx - 1))
{
case 0:
lua_pushnumber(L, r);
break;
case 1:
u = luaL_checkint(L, startidx);
luaL_argcheck(L, 1 <= u, startidx, "interval is empty");
lua_pushnumber(L, floor(r * u) + 1);
break;
case 2:
l = luaL_checkint(L, startidx);
u = luaL_checkint(L, startidx + 1);
luaL_argcheck(L, l <= u, startidx + 1, "interval is empty");
lua_pushnumber(L, floor(r * (u - l + 1)) + l);
break;
default:
return luaL_error(L, "wrong number of arguments");
}
return 1;
}
RandomGenerator *luax_checkrandomgenerator(lua_State *L, int idx)
{
return luax_checktype<RandomGenerator>(L, idx, "RandomGenerator", MATH_RANDOM_GENERATOR_T);
}
int w_RandomGenerator_randomseed(lua_State *L)
{
RandomGenerator *rng = luax_checkrandomgenerator(L, 1);
uint64 seed = luax_checkrandomseed(L, 2);
rng->randomseed(seed);
return 0;
}
int w_RandomGenerator_random(lua_State *L)
{
RandomGenerator *rng = luax_checkrandomgenerator(L, 1);
return luax_getrandom(L, 2, rng->random());
}
int w_RandomGenerator_randnormal(lua_State *L)
{
RandomGenerator *rng = luax_checkrandomgenerator(L, 1);
double mean = 0.0, stddev = 1.0;
if (lua_gettop(L) > 2)
{
mean = luaL_checknumber(L, 2);
stddev = luaL_checknumber(L, 3);
}
else
{
stddev = luaL_optnumber(L, 2, 1.0);
}
double r = rng->randnormal(stddev);
lua_pushnumber(L, r + mean);
return 1;
}
static const luaL_Reg functions[] =
{
{ "randomseed", w_RandomGenerator_randomseed },
{ "random", w_RandomGenerator_random },
{ "randnormal", w_RandomGenerator_randnormal },
{ 0, 0 }
};
extern "C" int luaopen_randomgenerator(lua_State *L)
{
return luax_register_type(L, "RandomGenerator", functions);
}
} // math
} // love
+47
View File
@@ -0,0 +1,47 @@
/**
* Copyright (c) 2006-2013 LOVE Development Team
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
#ifndef LOVE_MATH_WRAP_RANDOM_GENERATOR_H
#define LOVE_MATH_WRAP_RANDOM_GENERATOR_H
// LOVE
#include "RandomGenerator.h"
#include "common/config.h"
#include "common/runtime.h"
namespace love
{
namespace math
{
// Helper functions.
uint64 luax_checkrandomseed(lua_State *L, int idx);
int luax_getrandom(lua_State *L, int startidx, double r);
RandomGenerator *luax_checkrandomgenerator(lua_State *L, int idx);
int w_RandomGenerator_randomseed(lua_State *L);
int w_RandomGenerator_random(lua_State *L);
int w_RandomGenerator_randnormal(lua_State *L);
extern "C" int luaopen_randomgenerator(lua_State *L);
} // math
} // love
#endif // LOVE_MATH_WRAP_RANDOM_GENERATOR_H