#ifndef ARBUTILS_RANDOM_HPP #define ARBUTILS_RANDOM_HPP // PCG uses unsigned shifts, and overflows a lot. Disable the sanitizer for that. #if defined(__clang__) // If we don't ignore this diagnostic, and the sanitizer is not known yet in the clang version, this spams tens of // thousands of warnings. Hence we disable it for a bit. #pragma GCC diagnostic push #pragma clang diagnostic ignored "-Wunknown-sanitizers" #pragma clang attribute push(__attribute__((no_sanitize("unsigned-shift-base", "unsigned-integer-overflow"))), \ apply_to = function) #endif #include #if defined(__clang__) #pragma clang attribute pop #pragma clang diagnostic pop #endif #include #include #include #include "Ensure.hpp" namespace ArbUt { /// @brief A helper class for getting random numbers. /// @tparam RandomT A type for the desired random number generator. template class BaseRandom { private: uint_fast32_t _seed; RandomT _rng; std::uniform_real_distribution _distribution; public: inline constexpr BaseRandom() noexcept : _seed(std::chrono::duration_cast( std::chrono::system_clock::now().time_since_epoch()) .count()), _rng(_seed), _distribution(0.0, 1.0) {} /// @brief Instantiate random class with a specific seed. /// @param seed The seed the random number generator should be instantiated with. explicit inline constexpr BaseRandom(uint_fast32_t seed) noexcept : _seed(seed), _rng(seed), _distribution(0.0, 1.0){}; /// @brief The random number generator that is backing the random class. inline RandomT& GetRandomEngine() noexcept { return _rng; } /// @brief Gets a random float between 0.0 and 1.0. [[nodiscard]] inline constexpr f32 GetFloat() noexcept { return static_cast(GetDouble()); } /// @brief Gets a random double between 0.0 and 1.0. [[nodiscard]] inline constexpr f64 GetDouble() noexcept { return _distribution(_rng); } /// @brief Gets a random 32 bit integer inline constexpr i32 Get() noexcept { return static_cast(_rng()); } /// @brief Gets a random 32 bit integer between 0, and given max parameter. /// @param max The exclusive max value the random value should be. [[nodiscard]] inline i32 Get(i32 max) { Ensure(max > 0); std::uniform_int_distribution distribution(0, max - 1); return distribution(_rng); } /// @brief Gets a random 32 bit integer between given min and max parameters. /// @param min The inclusive min value the random value should be. /// @param max The exclusive max value the random value should be. [[nodiscard]] inline i32 Get(i32 min, i32 max) { Ensure(max > min); std::uniform_int_distribution distribution(min, max - 1); return distribution(_rng); } /// @brief Gets a random 32 bit unsigned integer between 0 and max unsigned int. [[nodiscard]] inline constexpr u32 GetUnsigned() noexcept { return _rng(); } /// @brief Gets a random 32 bit unsigned integer between 0, and given max parameter. /// @param max The exclusive max value the random value should be. [[nodiscard]] inline u32 GetUnsigned(u32 max) noexcept { std::uniform_int_distribution distribution(0, max - 1); return distribution(_rng); } /// @brief Gets a random 32 bit unsigned integer between given min and max parameters. /// @param min The inclusive min value the random value should be. /// @param max The exclusive max value the random value should be. [[nodiscard]] inline u32 GetUnsigned(u32 min, u32 max) { Ensure(max > min); std::uniform_int_distribution distribution(min, max - 1); return distribution(_rng); } /// @brief The seed the random class is generating from. [[nodiscard]] inline constexpr uint_fast32_t GetSeed() const noexcept { return _seed; } }; /// @brief Implementation of the BaseRandom class with pcg32 as random number generator. class Random : public BaseRandom { public: constexpr Random() noexcept : BaseRandom() {} /// @brief Instantiate random class with a specific seed. /// @param seed The seed the random number generator should be instantiated with. explicit constexpr Random(uint_fast32_t seed) noexcept : BaseRandom(seed) {} }; } #endif // ARBUTILS_RANDOM_HPP