diff options
Diffstat (limited to 'source/core/StarRandom.hpp')
-rw-r--r-- | source/core/StarRandom.hpp | 216 |
1 files changed, 216 insertions, 0 deletions
diff --git a/source/core/StarRandom.hpp b/source/core/StarRandom.hpp new file mode 100644 index 0000000..b3c8ba2 --- /dev/null +++ b/source/core/StarRandom.hpp @@ -0,0 +1,216 @@ +#ifndef STAR_RANDOM_HPP +#define STAR_RANDOM_HPP + +#include "StarStaticRandom.hpp" +#include "StarByteArray.hpp" + +namespace Star { + +STAR_EXCEPTION(RandomException, StarException); + +// Deterministic random number source. Uses multiply-with-carry algorithm. +// Much higher quality than the predictable random number generators. Not +// thread safe (won't crash or anything, but might return less than optimal +// values). +class RandomSource { +public: + // Generates a RandomSource with a seed from Random::randu64() + RandomSource(); + RandomSource(uint64_t seed); + + // Re-initializes the random number generator using the given seed. It is + // exactly equivalent to constructing a new RandomSource, just using the same + // buffer. + void init(); + void init(uint64_t seed); + + void addEntropy(); + void addEntropy(uint64_t seed); + + uint32_t randu32(); + uint64_t randu64(); + + int32_t randi32(); + int64_t randi64(); + + // Generates values in the range [0.0, 1.0] + float randf(); + // Generates values in the range [0.0, 1.0] + double randd(); + + // Random integer from [0, max], max must be >= 0 + int64_t randInt(int64_t max); + uint64_t randUInt(uint64_t max); + + // Random integer from [min, max] + int64_t randInt(int64_t min, int64_t max); + uint64_t randUInt(uint64_t min, uint64_t max); + + float randf(float min, float max); + double randd(double min, double max); + + bool randb(); + + // Generates values via normal distribution with box-muller algorithm + float nrandf(float stddev = 1.0f, float mean = 0.0f); + double nrandd(double stddev = 1.0, double mean = 0.0); + + // Round a fractional value statistically towards the floor or ceiling. For + // example, if a value is 5.2, 80% of the time it will round to 5, but 20% of + // the time it will round to 6. + int64_t stochasticRound(double val); + + void randBytes(char* buf, size_t len); + ByteArray randBytes(size_t len); + + // Pick a random value out of a container + template <typename Container> + typename Container::value_type const& randFrom(Container const& container); + template <typename Container> + typename Container::value_type& randFrom(Container& container); + template <typename Container> + typename Container::value_type randValueFrom(Container const& container); + template <typename Container> + typename Container::value_type randValueFrom(Container const& container, typename Container::value_type const& defaultVal); + + template <typename Container> + void shuffle(Container& container); + +private: + uint32_t gen32(); + + uint32_t m_data[256]; + uint32_t m_carry; + uint8_t m_index; +}; + +// Global static versions of the methods in RandomSource. It is not necessary +// to initialize the global RandomSource manually, it will be automatically +// initialized with a random seed on first use if it is not already initialized. +namespace Random { + void init(); + void init(uint64_t seed); + + void addEntropy(); + void addEntropy(uint64_t seed); + + uint32_t randu32(); + uint64_t randu64(); + int32_t randi32(); + int64_t randi64(); + float randf(); + double randd(); + long long randInt(long long max); + unsigned long long randUInt(unsigned long long max); + long long randInt(long long min, long long max); + unsigned long long randUInt(unsigned long long min, unsigned long long max); + float randf(float min, float max); + double randd(double min, double max); + bool randb(); + + float nrandf(float stddev = 1.0f, float mean = 0.0f); + double nrandd(double stddev = 1.0, double mean = 0.0); + + int64_t stochasticRound(double val); + + void randBytes(char* buf, size_t len); + ByteArray randBytes(size_t len); + + template <typename Container> + typename Container::value_type const& randFrom(Container const& container); + template <typename Container> + typename Container::value_type& randFrom(Container& container); + template <typename Container> + typename Container::value_type randValueFrom(Container const& container); + template <typename Container> + typename Container::value_type randValueFrom(Container const& container, typename Container::value_type const& defaultVal); + + template <typename Container> + void shuffle(Container& container); +} + +template <typename Container> +typename Container::value_type const& RandomSource::randFrom(Container const& container) { + if (container.empty()) + throw RandomException("Empty container in randFrom"); + + auto i = container.begin(); + std::advance(i, randUInt(container.size() - 1)); + return *i; +} + +template <typename Container> +typename Container::value_type& RandomSource::randFrom(Container& container) { + if (container.empty()) + throw RandomException("Empty container in randFrom"); + + auto i = container.begin(); + std::advance(i, randUInt(container.size() - 1)); + return *i; +} + +template <typename Container> +typename Container::value_type const& Random::randFrom(Container const& container) { + if (container.empty()) + throw RandomException("Empty container in randFrom"); + + auto i = container.begin(); + std::advance(i, Random::randUInt(container.size() - 1)); + return *i; +} + +template <typename Container> +typename Container::value_type& Random::randFrom(Container& container) { + if (container.empty()) + throw RandomException("Empty container in randFrom"); + + auto i = container.begin(); + std::advance(i, Random::randUInt(container.size() - 1)); + return *i; +} + +template <typename Container> +typename Container::value_type RandomSource::randValueFrom(Container const& container) { + return randValueFrom(container, typename Container::value_type()); +} + +template <typename Container> +typename Container::value_type RandomSource::randValueFrom( + Container const& container, typename Container::value_type const& defaultVal) { + if (container.empty()) + return defaultVal; + + auto i = container.begin(); + std::advance(i, randInt(container.size() - 1)); + return *i; +} + +template <typename Container> +void RandomSource::shuffle(Container& container) { + std::random_shuffle(container.begin(), container.end(), [this](size_t max) { return randUInt(max - 1); }); +} + +template <typename Container> +typename Container::value_type Random::randValueFrom(Container const& container) { + return randValueFrom(container, typename Container::value_type()); +} + +template <typename Container> +typename Container::value_type Random::randValueFrom( + Container const& container, typename Container::value_type const& defaultVal) { + if (container.empty()) + return defaultVal; + + auto i = container.begin(); + std::advance(i, Random::randInt(container.size() - 1)); + return *i; +} + +template <typename Container> +void Random::shuffle(Container& container) { + std::random_shuffle(container.begin(), container.end(), [](size_t max) { return Random::randUInt(max - 1); }); +} + +} + +#endif |