Browse Source

Merged bartbes/love-experiments/RandomGenerator-2 into default (resolves issue #692)

Alex Szpakowski 12 years ago
parent
commit
a60929e9b0

+ 2 - 2
src/modules/graphics/opengl/ParticleSystem.cpp

@@ -217,8 +217,8 @@ void ParticleSystem::initParticle(particle *p)
 		p->position[1] += (float) rng.random(-areaSpread.getY(), areaSpread.getY());
 		break;
 	case DISTRIBUTION_NORMAL:
-		p->position[0] += (float) rng.randomnormal(areaSpread.getX());
-		p->position[1] += (float) rng.randomnormal(areaSpread.getY());
+		p->position[0] += (float) rng.randomNormal(areaSpread.getX());
+		p->position[1] += (float) rng.randomNormal(areaSpread.getY());
 		break;
 	case DISTRIBUTION_NONE:
 	default:

+ 20 - 8
src/modules/math/MathModule.h

@@ -53,12 +53,24 @@ public:
 	virtual ~Math()
 	{}
 
-	/**
-	 * @copydoc RandomGenerator::randomseed()
-	 **/
-	inline void randomseed(uint64 seed)
+	inline void setRandomState(RandomGenerator::State state)
+	{
+		rng.setState(state);
+	}
+
+	inline void setRandomState(uint32 low, uint32 high)
+	{
+		rng.setState(low, high);
+	}
+
+	inline RandomGenerator::State getRandomState() const
+	{
+		return rng.getState();
+	}
+
+	inline void getRandomState(uint32 &low, uint32 &high) const
 	{
-		rng.randomseed(seed);
+		rng.getState(low, high);
 	}
 
 	/**
@@ -86,11 +98,11 @@ public:
 	}
 
 	/**
-	 * @copydoc RandomGenerator::randomnormal()
+	 * @copydoc RandomGenerator::randomNormal()
 	 **/
-	inline double randomnormal(double stddev)
+	inline double randomNormal(double stddev)
 	{
-		return rng.randomnormal(stddev);
+		return rng.randomNormal(stddev);
 	}
 
 	/**

+ 17 - 20
src/modules/math/RandomGenerator.cpp

@@ -36,37 +36,34 @@ RandomGenerator::RandomGenerator()
 {
 	// 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;
+	rng_state.b32.a = 0x0139408D;
+	rng_state.b32.b = 0xCBBF7A44;
 #else
-	converter.b32.b = 0x0139408D;
-	converter.b32.a = 0xCBBF7A44;
+	rng_state.b32.b = 0x0139408D;
+	rng_state.b32.a = 0xCBBF7A44;
 #endif
+}
+
+void RandomGenerator::setState(RandomGenerator::State state)
+{
+	// 0 xor 0 is still 0, so Xorshift can't generate new numbers.
+	if (state.b64 == 0)
+		throw love::Exception("Invalid random state.");
 
-	rng_state = converter.b64;
+	rng_state = state;
 }
 
 uint64 RandomGenerator::rand()
 {
-	rng_state ^= (rng_state << 13);
-	rng_state ^= (rng_state >> 7);
-	rng_state ^= (rng_state << 17);
-	return rng_state;
+	rng_state.b64 ^= (rng_state.b64 << 13);
+	rng_state.b64 ^= (rng_state.b64 >> 7);
+	rng_state.b64 ^= (rng_state.b64 << 17);
+	return rng_state.b64;
 }
 
 // Box–Muller transform
-double RandomGenerator::randomnormal(double stddev)
+double RandomGenerator::randomNormal(double stddev)
 {
 	// use cached number if possible
 	if (last_randomnormal != std::numeric_limits<double>::infinity())

+ 49 - 9
src/modules/math/RandomGenerator.h

@@ -22,6 +22,8 @@
 #define LOVE_MATH_RANDOM_GENERATOR_H
 
 // LOVE
+#include "common/config.h"
+#include "common/Exception.h"
 #include "common/math.h"
 #include "common/int.h"
 #include "common/Object.h"
@@ -38,19 +40,57 @@ class RandomGenerator : public Object
 {
 public:
 
-	RandomGenerator();
+	union State
+	{
+		uint64 b64;
+		struct
+		{
+			uint32 a;
+			uint32 b;
+		} b32;
+	};
 
-	virtual ~RandomGenerator() {};
+	RandomGenerator();
+	virtual ~RandomGenerator() {}
 
 	/**
-	 * Set pseudo random seed.
+	 * Set pseudo-random state.
 	 * It's up to the implementation how to use this.
-	 *
-	 * @param seed The random seed.
 	 **/
-	inline void randomseed(uint64 seed)
+	void setState(State state);
+
+	/**
+	 * Separately set the low and high parts of the pseudo-random state.
+	 **/
+	inline void setState(uint32 low, uint32 high)
+	{
+		State newstate;
+
+#ifdef LOVE_BIG_ENDIAN
+		newstate.b32.a = high;
+		newstate.b32.b = low;
+#else
+		newstate.b32.b = high;
+		newstate.b32.a = low;
+#endif
+
+		setState(newstate);
+	}
+
+	inline State getState() const
+	{
+		return rng_state;
+	}
+
+	inline void getState(uint32 &low, uint32 &high) const
 	{
-		rng_state = seed;
+#ifdef LOVE_BIG_ENDIAN
+		high = rng_state.b32.a;
+		low = rng_state.b32.b;
+#else
+		high = rng_state.b32.b;
+		low = rng_state.b32.a;
+#endif
 	}
 
 	/**
@@ -96,11 +136,11 @@ public:
 	 * @param stddev Standard deviation of the distribution.
 	 * @return Normally distributed random number with mean 0 and variance (stddev)².
 	 **/
-	double randomnormal(double stddev);
+	double randomNormal(double stddev);
 
 private:
 
-	uint64 rng_state;
+	State rng_state;
 	double last_randomnormal;
 
 }; // RandomGenerator

+ 39 - 19
src/modules/math/wrap_Math.cpp

@@ -32,44 +32,62 @@ namespace love
 namespace math
 {
 
-int w_randomseed(lua_State *L)
+int w_setRandomState(lua_State *L)
 {
-	uint64 seed = luax_checkrandomseed(L, 1);
-	Math::instance.randomseed(seed);
+	try
+	{
+		Math::instance.setRandomState(luax_checkrandomstate(L, 1));
+	}
+	catch (love::Exception &e)
+	{
+		return luaL_error(L, "%s", e.what());
+	}
 	return 0;
 }
 
+int w_getRandomState(lua_State *L)
+{
+	uint32 low = 0, high = 0;
+	Math::instance.getRandomState(low, high);
+	lua_pushnumber(L, (lua_Number) low);
+	lua_pushnumber(L, (lua_Number) high);
+	return 2;
+}
+
 int w_random(lua_State *L)
 {
 	return luax_getrandom(L, 1, Math::instance.random());
 }
 
-int w_randomnormal(lua_State *L)
+int w_randomNormal(lua_State *L)
 {
-	double mean = 0.0, stddev = 1.0;
-	if (lua_gettop(L) > 1)
-	{
-		mean = luaL_checknumber(L, 1);
-		stddev = luaL_checknumber(L, 2);
-	}
-	else
-	{
-		stddev = luaL_optnumber(L, 1, 1.);
-	}
+	double stddev = luaL_optnumber(L, 1, 1.0);
+	double mean = luaL_optnumber(L, 2, 0.0);
+	double r = Math::instance.randomNormal(stddev);
 
-	double r = Math::instance.randomnormal(stddev);
 	lua_pushnumber(L, r + mean);
 	return 1;
 }
 
 int w_newRandomGenerator(lua_State *L)
 {
+	RandomGenerator::State s;
+	if (lua_gettop(L) > 0)
+		s = luax_checkrandomstate(L, 1);
+
 	RandomGenerator *t = Math::instance.newRandomGenerator();
 
 	if (lua_gettop(L) > 0)
 	{
-		uint64 seed = luax_checkrandomseed(L, 1);
-		t->randomseed(seed);
+		try
+		{
+			t->setState(s);
+		}
+		catch (love::Exception &e)
+		{
+			t->release();
+			return luaL_error(L, "%s", e.what());
+		}
 	}
 
 	luax_newtype(L, "RandomGenerator", MATH_RANDOM_GENERATOR_T, (void *) t);
@@ -264,9 +282,10 @@ int w_noise(lua_State *L)
 // List of functions to wrap.
 static const luaL_Reg functions[] =
 {
-	{ "randomseed", w_randomseed },
+	{ "setRandomState", w_setRandomState },
+	{ "getRandomState", w_getRandomState },
 	{ "random", w_random },
-	{ "randomnormal", w_randomnormal },
+	{ "randomNormal", w_randomNormal },
 	{ "newRandomGenerator", w_newRandomGenerator },
 	{ "newBezierCurve", w_newBezierCurve },
 	{ "triangulate", w_triangulate },
@@ -285,6 +304,7 @@ static const lua_CFunction types[] =
 extern "C" int luaopen_love_math(lua_State *L)
 {
 	Math::instance.retain();
+
 	WrappedModule w;
 	w.module = &Math::instance;
 	w.name = "math";

+ 3 - 2
src/modules/math/wrap_Math.h

@@ -30,9 +30,10 @@ namespace love
 namespace math
 {
 
-int w_randomseed(lua_State *L);
+int w_setRandomState(lua_State *L);
+int w_getRandomState(lua_State *L);
 int w_random(lua_State *L);
-int w_randomnormal(lua_State *L);
+int w_randomNormal(lua_State *L);
 int w_newRandomGenerator(lua_State *L);
 int w_newBezierCurve(lua_State *L);
 int w_triangulate(lua_State *L);

+ 60 - 25
src/modules/math/wrap_RandomGenerator.cpp

@@ -21,23 +21,47 @@
 #include "wrap_RandomGenerator.h"
 
 #include <cmath>
+#include <algorithm>
 
 namespace love
 {
 namespace math
 {
 
-uint64 luax_checkrandomseed(lua_State *L, int idx)
+template <typename T>
+static T checkrandomstate_part(lua_State *L, int idx)
 {
-	union
-	{
-		double seed_double;
-		uint64 seed_uint;
-	} s;
+	double num = luaL_checknumber(L, idx);
+	double inf = std::numeric_limits<double>::infinity();
+
+	// Disallow conversions from infinity and NaN.
+	if (num == inf || num == -inf || num != num)
+		luaL_argerror(L, idx, "invalid random state");
+
+	return (T) num;
+}
+
+RandomGenerator::State luax_checkrandomstate(lua_State *L, int idx)
+{
+	RandomGenerator::State s;
 
-	s.seed_double = luaL_checknumber(L, idx);
+	if (!lua_isnoneornil(L, idx + 1))
+	{
+		uint32 low = checkrandomstate_part<uint32>(L, idx);
+		uint32 high = checkrandomstate_part<uint32>(L, idx + 1);
+
+#ifdef LOVE_BIG_ENDIAN
+		s.b32.a = high;
+		s.b32.b = low;
+#else
+		s.b32.b = high;
+		s.b32.a = low;
+#endif
+	}
+	else
+		s.b64 = checkrandomstate_part<uint64>(L, idx);
 
-	return s.seed_uint;
+	return s;
 }
 
 int luax_getrandom(lua_State *L, int startidx, double r)
@@ -71,45 +95,56 @@ 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)
+int w_RandomGenerator_setState(lua_State *L)
 {
 	RandomGenerator *rng = luax_checkrandomgenerator(L, 1);
-	uint64 seed = luax_checkrandomseed(L, 2);
-	rng->randomseed(seed);
+	try
+	{
+		rng->setState(luax_checkrandomstate(L, 2));
+	}
+	catch (love::Exception &e)
+	{
+		return luaL_error(L, "%s", e.what());
+	}
 	return 0;
 }
 
+int w_RandomGenerator_getState(lua_State *L)
+{
+	RandomGenerator *rng = luax_checkrandomgenerator(L, 1);
+
+	uint32 low = 0, high = 0;
+	rng->getState(low, high);
+
+	lua_pushnumber(L, (lua_Number) low);
+	lua_pushnumber(L, (lua_Number) high);
+	return 2;
+}
+
 int w_RandomGenerator_random(lua_State *L)
 {
 	RandomGenerator *rng = luax_checkrandomgenerator(L, 1);
 	return luax_getrandom(L, 2, rng->random());
 }
 
-int w_RandomGenerator_randomnormal(lua_State *L)
+int w_RandomGenerator_randomNormal(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 stddev = luaL_optnumber(L, 2, 1.0);
+	double mean = luaL_optnumber(L, 3, 0.0);
+	double r = rng->randomNormal(stddev);
 
-	double r = rng->randomnormal(stddev);
 	lua_pushnumber(L, r + mean);
 	return 1;
 }
 
 static const luaL_Reg functions[] =
 {
-	{ "randomseed", w_RandomGenerator_randomseed },
+	{ "setState", w_RandomGenerator_setState },
+	{ "getState", w_RandomGenerator_getState },
 	{ "random", w_RandomGenerator_random },
-	{ "randomnormal", w_RandomGenerator_randomnormal },
+	{ "randomNormal", w_RandomGenerator_randomNormal },
 	{ 0, 0 }
 };
 

+ 4 - 3
src/modules/math/wrap_RandomGenerator.h

@@ -32,13 +32,14 @@ namespace math
 {
 
 // Helper functions.
-uint64 luax_checkrandomseed(lua_State *L, int idx);
+RandomGenerator::State luax_checkrandomstate(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_setState(lua_State *L);
+int w_RandomGenerator_getState(lua_State *L);
 int w_RandomGenerator_random(lua_State *L);
-int w_RandomGenerator_randomnormal(lua_State *L);
+int w_RandomGenerator_randomNormal(lua_State *L);
 extern "C" int luaopen_randomgenerator(lua_State *L);
 
 } // math