diff --git a/libs/random/random.hpp b/libs/random/random.hpp index 80ce8816..08f5427a 100644 --- a/libs/random/random.hpp +++ b/libs/random/random.hpp @@ -36,1460 +36,1524 @@ SOFTWARE. #include #include #include -#include // std::forward, std::declval +#include // std::forward, std::declval #include // std::shuffle, std::next, std::distance -#include // std::begin, std::end, std::iterator_traits -#include // std::numeric_limits +#include // std::begin, std::end, std::iterator_traits +#include // std::numeric_limits #include #include -namespace effolkronium { - - namespace details { - /// Key type for getting common type numbers or objects - struct common{ }; - - /// Key type for weighted random number generation - struct weight{ }; - - /// True if type T is applicable by a std::uniform_int_distribution - template - struct is_uniform_int { - static constexpr bool value = - std::is_same::value - || std::is_same::value - || std::is_same::value - || std::is_same::value - || std::is_same::value - || std::is_same::value - || std::is_same::value - || std::is_same::value; - }; - - /// True if type T is applicable by a std::uniform_real_distribution - template - struct is_uniform_real { - static constexpr bool value = - std::is_same::value - || std::is_same::value - || std::is_same::value; - }; - - /// True if type T is plain byte - template - struct is_byte { - static constexpr bool value = - std::is_same::value - || std::is_same::value; - }; - - /// True if type T is plain number type - template - struct is_supported_number { - static constexpr bool value = - is_byte ::value - || is_uniform_real::value - || is_uniform_int ::value; - }; - - /// True if type T is character type - template - struct is_supported_character { - static constexpr bool value = - std::is_same::value - || std::is_same::value - || std::is_same::value - || std::is_same::value; - }; - - /// True if type T is iterator - template - struct is_iterator { - private: - static char test( ... ); - - template ::difference_type, - typename = typename std::iterator_traits::pointer, - typename = typename std::iterator_traits::reference, - typename = typename std::iterator_traits::value_type, - typename = typename std::iterator_traits::iterator_category - > static long test( U&& ); - public: - static constexpr bool value = std::is_same< - decltype( test( std::declval( ) ) ), long>::value; - }; - - template - class has_reserve - { - private: - template - static char test(...); - - template - static long test(decltype(&C::reserve)); - public: - static constexpr bool value = std::is_same< - decltype(test(0)), long>::value; - }; - - template - class has_insert - { - private: - template - static char test(...); - - template - static long test(decltype(&C::insert)); - public: - static constexpr bool value = std::is_same< - decltype(test(0)), long>::value; - }; - - template - using void_t = void; - - template - struct is_map : public std::false_type {}; - - template - struct is_map> : public std::true_type{}; - - } // namespace details - - /// Default seeder for 'random' classes - struct seeder_default { - /// return seed sequence - std::seed_seq& operator() ( ) { - // MinGW issue, std::random_device returns constant value - // Use std::seed_seq with additional seed from C++ chrono - return seed_seq; - } - private: - std::seed_seq seed_seq{ { - static_cast( std::random_device{ }( ) ), - static_cast( std::chrono::steady_clock::now( ) - .time_since_epoch( ).count( ) ), - } }; - }; +namespace effolkronium +{ + +namespace details +{ +/// Key type for getting common type numbers or objects +struct common { +}; + +/// Key type for weighted random number generation +struct weight { +}; + +/// True if type T is applicable by a std::uniform_int_distribution +template +struct is_uniform_int { + static constexpr bool value = + std::is_same::value || std::is_same::value || + std::is_same::value || std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value; +}; + +/// True if type T is applicable by a std::uniform_real_distribution +template +struct is_uniform_real { + static constexpr bool value = std::is_same::value || + std::is_same::value || + std::is_same::value; +}; + +/// True if type T is plain byte +template +struct is_byte { + static constexpr bool value = std::is_same::value || + std::is_same::value; +}; + +/// True if type T is plain number type +template +struct is_supported_number { + static constexpr bool value = is_byte::value || + is_uniform_real::value || + is_uniform_int::value; +}; + +/// True if type T is character type +template +struct is_supported_character { + static constexpr bool value = + std::is_same::value || std::is_same::value || + std::is_same::value || std::is_same::value; +}; + +/// True if type T is iterator +template +struct is_iterator { + private: + static char test(...); + + template ::difference_type, + typename = typename std::iterator_traits::pointer, + typename = typename std::iterator_traits::reference, + typename = typename std::iterator_traits::value_type, + typename = typename std::iterator_traits::iterator_category> + static long test(U&&); + + public: + static constexpr bool value = + std::is_same())), long>::value; +}; + +template +class has_reserve +{ + private: + template + static char test(...); + + template + static long test(decltype(&C::reserve)); + + public: + static constexpr bool value = + std::is_same(0)), long>::value; +}; + +template +class has_insert +{ + private: + template + static char test(...); + + template + static long test(decltype(&C::insert)); + + public: + static constexpr bool value = + std::is_same(0)), long>::value; +}; + +template +using void_t = void; + +template +struct is_map : public std::false_type { +}; + +template +struct is_map> : public std::true_type { +}; + +} // namespace details + +/// Default seeder for 'random' classes +struct seeder_default { + /// return seed sequence + std::seed_seq& operator()() + { + // MinGW issue, std::random_device returns constant value + // Use std::seed_seq with additional seed from C++ chrono + return seed_seq; + } + + private: + std::seed_seq seed_seq{{ + static_cast(std::random_device{}()), + static_cast( + std::chrono::steady_clock::now().time_since_epoch().count()), + }}; +}; + +/** + * \brief Base template class for random + * with static API and static internal member storage + * \note it is NOT thread safe but more efficient then + * basic_random_thread_local + * \param Engine A random engine with interface like in the std::mt19937 + * \param Seeder A seeder type which return seed for internal engine + * through operator() + */ +template class IntegerDist = std::uniform_int_distribution, + template class RealDist = std::uniform_real_distribution, + typename BoolDist = std::bernoulli_distribution> +class basic_random_base +{ + public: + /// Type of used random number engine + using engine_type = Engine; + + /// Type of used random number seeder + using seeder_type = Seeder; + + /// Type of used integer distribution + template + using integer_dist_t = IntegerDist; + + /// Type of used real distribution + template + using real_dist_t = RealDist; + + /// Type of used bool distribution + using bool_dist_t = BoolDist; + + /// Key type for getting common type numbers or objects + using common = details::common; + + /// Key type for weighted random number generation + using weight = details::weight; /** - * \brief Base template class for random - * with static API and static internal member storage - * \note it is NOT thread safe but more efficient then - * basic_random_thread_local - * \param Engine A random engine with interface like in the std::mt19937 - * \param Seeder A seeder type which return seed for internal engine - * through operator() - */ - template< - typename Derived, - typename Engine, - typename Seeder = seeder_default, - template class IntegerDist = std::uniform_int_distribution, - template class RealDist = std::uniform_real_distribution, - typename BoolDist = std::bernoulli_distribution - > - class basic_random_base { - public: - /// Type of used random number engine - using engine_type = Engine; - - /// Type of used random number seeder - using seeder_type = Seeder; - - /// Type of used integer distribution - template - using integer_dist_t = IntegerDist; - - /// Type of used real distribution - template - using real_dist_t = RealDist; - - /// Type of used bool distribution - using bool_dist_t = BoolDist; - - /// Key type for getting common type numbers or objects - using common = details::common; - - /// Key type for weighted random number generation - using weight = details::weight; - - /** - * \return The minimum value - * potentially generated by the random-number engine - */ - static constexpr typename Engine::result_type (min)( ) { - return (Engine::min)( ); - } + * \return The minimum value + * potentially generated by the random-number engine + */ + static constexpr typename Engine::result_type(min)() + { + return (Engine::min)(); + } - /** - * \return The maximum value - * potentially generated by the random-number engine - */ - static constexpr typename Engine::result_type (max)( ) { - return (Engine::max)( ); - } - - /// Advances the internal state by z times - static void discard( const unsigned long long z ) { - engine_instance( ).discard( z ); - } - - /// Reseed by Seeder - static void reseed( ) { - Seeder seeder; - seed( seeder( ) ); - } - - /** - * \brief Reinitializes the internal state - * of the random-number engine using new seed value - * \param value The seed value to use - * in the initialization of the internal state - */ - static void seed( const typename Engine::result_type value = - Engine::default_seed ) { - engine_instance( ).seed( value ); - } + /** + * \return The maximum value + * potentially generated by the random-number engine + */ + static constexpr typename Engine::result_type(max)() + { + return (Engine::max)(); + } + + /// Advances the internal state by z times + static void discard(const unsigned long long z) + { + engine_instance().discard(z); + } + + /// Reseed by Seeder + static void reseed() + { + Seeder seeder; + seed(seeder()); + } - /** - * \brief Reinitializes the internal state - * of the random-number engine using new seed value - * \param seq The seed sequence - * to use in the initialization of the internal state - */ - template - static void seed( Sseq& seq ) { - engine_instance( ).seed( seq ); - } + /** + * \brief Reinitializes the internal state + * of the random-number engine using new seed value + * \param value The seed value to use + * in the initialization of the internal state + */ + static void + seed(const typename Engine::result_type value = Engine::default_seed) + { + engine_instance().seed(value); + } - /// return random number from engine in [min(), max()] range - static typename Engine::result_type get( ) { - return engine_instance( )( ); - } + /** + * \brief Reinitializes the internal state + * of the random-number engine using new seed value + * \param seq The seed sequence + * to use in the initialization of the internal state + */ + template + static void seed(Sseq& seq) + { + engine_instance().seed(seq); + } + + /// return random number from engine in [min(), max()] range + static typename Engine::result_type get() + { + return engine_instance()(); + } - /** - * \brief Compares internal pseudo-random number engine - * with 'other' pseudo-random number engine. - * Two engines are equal, if their internal states - * are equivalent, that is, if they would generate - * equivalent values for any number of calls of operator() - * \param other The engine, with which the internal engine will be compared - * \return true, if other and internal engine are equal - */ - static bool is_equal( const Engine& other ) { - return engine_instance( ) == other; - } + /** + * \brief Compares internal pseudo-random number engine + * with 'other' pseudo-random number engine. + * Two engines are equal, if their internal states + * are equivalent, that is, if they would generate + * equivalent values for any number of calls of operator() + * \param other The engine, with which the internal engine will be compared + * \return true, if other and internal engine are equal + */ + static bool is_equal(const Engine& other) + { + return engine_instance() == other; + } - /** - * \brief Serializes the internal state of the - * internal pseudo-random number engine as a sequence - * of decimal numbers separated by one or more spaces, - * and inserts it to the stream ost. The fill character - * and the formatting flags of the stream are - * ignored and unaffected. - * \param ost The output stream to insert the data to - */ - template - static void serialize( std::basic_ostream& ost ) { - ost << engine_instance( ); - } + /** + * \brief Serializes the internal state of the + * internal pseudo-random number engine as a sequence + * of decimal numbers separated by one or more spaces, + * and inserts it to the stream ost. The fill character + * and the formatting flags of the stream are + * ignored and unaffected. + * \param ost The output stream to insert the data to + */ + template + static void serialize(std::basic_ostream& ost) + { + ost << engine_instance(); + } - /** - * \brief Restores the internal state of the - * internal pseudo-random number engine from - * the serialized representation, which - * was created by an earlier call to 'serialize' - * using a stream with the same imbued locale and - * the same CharT and Traits. - * If the input cannot be deserialized, - * internal engine is left unchanged and failbit is raised on ist - * \param ost The input stream to extract the data from - */ - template - static void deserialize( std::basic_istream& ist ) { - ist >> engine_instance( ); - } + /** + * \brief Restores the internal state of the + * internal pseudo-random number engine from + * the serialized representation, which + * was created by an earlier call to 'serialize' + * using a stream with the same imbued locale and + * the same CharT and Traits. + * If the input cannot be deserialized, + * internal engine is left unchanged and failbit is raised on ist + * \param ost The input stream to extract the data from + */ + template + static void deserialize(std::basic_istream& ist) + { + ist >> engine_instance(); + } - /** - * \brief Generate a random integer number in a [from; to] range - * by std::uniform_int_distribution - * \param from The first limit number of a random range - * \param to The second limit number of a random range - * \return A random integer number in a [from; to] range - * \note Allow both: 'from' <= 'to' and 'from' >= 'to' - * \note Prevent implicit type conversion - */ - template - static typename std::enable_if::value - , T>::type get( T from = (std::numeric_limits::min)( ), - T to = (std::numeric_limits::max)( ) ) { - if( from < to ) // Allow range from higher to lower - return IntegerDist{ from, to }( engine_instance( ) ); - return IntegerDist{ to, from }( engine_instance( ) ); - } + /** + * \brief Generate a random integer number in a [from; to] range + * by std::uniform_int_distribution + * \param from The first limit number of a random range + * \param to The second limit number of a random range + * \return A random integer number in a [from; to] range + * \note Allow both: 'from' <= 'to' and 'from' >= 'to' + * \note Prevent implicit type conversion + */ + template + static typename std::enable_if::value, T>::type + get(T from = (std::numeric_limits::min)(), + T to = (std::numeric_limits::max)()) + { + if (from < to) // Allow range from higher to lower + return IntegerDist{from, to}(engine_instance()); + return IntegerDist{to, from}(engine_instance()); + } - /** - * \brief Generate a random real number in a [from; to] range - * by std::uniform_real_distribution - * \param from The first limit number of a random range - * \param to The second limit number of a random range - * \return A random real number in a [from; to] range - * \note Allow both: 'from' <= 'to' and 'from' >= 'to' - * \note Prevent implicit type conversion - */ - template - static typename std::enable_if::value - , T>::type get( T from = (std::numeric_limits::min)( ), - T to = (std::numeric_limits::max)( ) ) { - if( from < to ) // Allow range from higher to lower - return RealDist{ from, to }( engine_instance( ) ); - return RealDist{ to, from }( engine_instance( ) ); - } + /** + * \brief Generate a random real number in a [from; to] range + * by std::uniform_real_distribution + * \param from The first limit number of a random range + * \param to The second limit number of a random range + * \return A random real number in a [from; to] range + * \note Allow both: 'from' <= 'to' and 'from' >= 'to' + * \note Prevent implicit type conversion + */ + template + static typename std::enable_if::value, T>::type + get(T from = (std::numeric_limits::min)(), + T to = (std::numeric_limits::max)()) + { + if (from < to) // Allow range from higher to lower + return RealDist{from, to}(engine_instance()); + return RealDist{to, from}(engine_instance()); + } - /** - * \brief Generate a random byte number in a [from; to] range - * \param from The first limit number of a random range - * \param to The second limit number of a random range - * \return A random byte number in a [from; to] range - * \note Allow both: 'from' <= 'to' and 'from' >= 'to' - * \note Prevent implicit type conversion - */ - template - static typename std::enable_if::value - , T>::type get( T from = (std::numeric_limits::min)( ), - T to = (std::numeric_limits::max)( ) ) { - // Choose between short and unsigned short for byte conversion - using short_t = typename std::conditional::value, - short, unsigned short>::type; - - return static_cast( get( from, to ) ); - } + /** + * \brief Generate a random byte number in a [from; to] range + * \param from The first limit number of a random range + * \param to The second limit number of a random range + * \return A random byte number in a [from; to] range + * \note Allow both: 'from' <= 'to' and 'from' >= 'to' + * \note Prevent implicit type conversion + */ + template + static typename std::enable_if::value, T>::type + get(T from = (std::numeric_limits::min)(), + T to = (std::numeric_limits::max)()) + { + // Choose between short and unsigned short for byte conversion + using short_t = typename std::conditional::value, + short, unsigned short>::type; + + return static_cast(get(from, to)); + } - /** - * \brief Generate a random common_type number in a [from; to] range - * \param Key The Key type for this version of 'get' method - * Type should be '(THIS_TYPE)::common' struct - * \param from The first limit number of a random range - * \param to The second limit number of a random range - * \return A random common_type number in a [from; to] range - * \note Allow both: 'from' <= 'to' and 'from' >= 'to' - * \note Allow implicit type conversion - * \note Prevent implicit type conversion from signed to unsigned types - * Why? std::common_type chooses unsigned value, - * then Signed value will be converted to Unsigned value - * which gives us a wrong range for random values. - * https://stackoverflow.com/a/5416498/5734836 - */ - template< - typename Key, - typename A, - typename B, - typename C = typename std::common_type::type - > - static typename std::enable_if< - std::is_same::value - && details::is_supported_number::value - && details::is_supported_number::value + /** + * \brief Generate a random common_type number in a [from; to] range + * \param Key The Key type for this version of 'get' method + * Type should be '(THIS_TYPE)::common' struct + * \param from The first limit number of a random range + * \param to The second limit number of a random range + * \return A random common_type number in a [from; to] range + * \note Allow both: 'from' <= 'to' and 'from' >= 'to' + * \note Allow implicit type conversion + * \note Prevent implicit type conversion from signed to unsigned types + * Why? std::common_type chooses unsigned value, + * then Signed value will be converted to Unsigned value + * which gives us a wrong range for random values. + * https://stackoverflow.com/a/5416498/5734836 + */ + template ::type> + static typename std::enable_if< + std::is_same::value && + details::is_supported_number::value && + details::is_supported_number::value // Prevent implicit type conversion from signed to unsigned types - && std::is_signed::value != std::is_unsigned::value - , C>::type get( A from = (std::numeric_limits::min)( ), - B to = (std::numeric_limits::max)( ) ) { - return get( static_cast( from ), static_cast( to ) ); - } + && std::is_signed::value != std::is_unsigned::value, + C>::type + get(A from = (std::numeric_limits::min)(), + B to = (std::numeric_limits::max)()) + { + return get(static_cast(from), static_cast(to)); + } - /** - * \brief Generate a random character in a [from; to] range - * by std::uniform_int_distribution - * \param from The first limit number of a random range - * \param to The second limit number of a random range - * \return A random character in a [from; to] range - * \note Allow both: 'from' <= 'to' and 'from' >= 'to' - * \note Prevent implicit type conversion - */ - template - static typename std::enable_if::value - , T>::type get(T from = (std::numeric_limits::min)(), - T to = (std::numeric_limits::max)()) { - if (from < to) // Allow range from higher to lower - return static_cast(IntegerDist{ static_cast(from), static_cast(to) }(engine_instance())); - return static_cast(IntegerDist{ static_cast(to), static_cast(from) }(engine_instance())); - } + /** + * \brief Generate a random character in a [from; to] range + * by std::uniform_int_distribution + * \param from The first limit number of a random range + * \param to The second limit number of a random range + * \return A random character in a [from; to] range + * \note Allow both: 'from' <= 'to' and 'from' >= 'to' + * \note Prevent implicit type conversion + */ + template + static typename std::enable_if::value, + T>::type + get(T from = (std::numeric_limits::min)(), + T to = (std::numeric_limits::max)()) + { + if (from < to) // Allow range from higher to lower + return static_cast(IntegerDist{ + static_cast(from), + static_cast(to)}(engine_instance())); + return static_cast(IntegerDist{ + static_cast(to), + static_cast(from)}(engine_instance())); + } - /** - * \brief Generate a bool value with specific probability - * by std::bernoulli_distribution - * \param probability The probability of generating true in [0; 1] range - * 0 means always false, 1 means always true - * \return 'true' with 'probability' probability ('false' otherwise) - */ - template - static typename std::enable_if::value - , bool>::type get( const double probability = 0.5 ) { - assert( 0 <= probability && 1 >= probability ); // out of [0; 1] range - return BoolDist{ probability }( engine_instance( ) ); - } + /** + * \brief Generate a bool value with specific probability + * by std::bernoulli_distribution + * \param probability The probability of generating true in [0; 1] range + * 0 means always false, 1 means always true + * \return 'true' with 'probability' probability ('false' otherwise) + */ + template + static typename std::enable_if::value, bool>::type + get(const double probability = 0.5) + { + assert(0 <= probability && 1 >= probability); // out of [0; 1] range + return BoolDist{probability}(engine_instance()); + } - /** - * \brief Return random value from initializer_list - * \param init_list initializer_list with values - * \return Random value from initializer_list - * \note Should be 1 or more elements in initializer_list - * \note Warning! Elements in initializer_list can't be moved: - * https://stackoverflow.com/a/8193157/5734836 - */ - template - static T get( std::initializer_list init_list ) { - assert( 0u != init_list.size( ) ); - return *get( init_list.begin( ), init_list.end( ) ); - } + /** + * \brief Return random value from initializer_list + * \param init_list initializer_list with values + * \return Random value from initializer_list + * \note Should be 1 or more elements in initializer_list + * \note Warning! Elements in initializer_list can't be moved: + * https://stackoverflow.com/a/8193157/5734836 + */ + template + static T get(std::initializer_list init_list) + { + assert(0u != init_list.size()); + return *get(init_list.begin(), init_list.end()); + } - /** - * \brief Return random iterator from iterator range - * \param first, last - the range of elements - * \return Random iterator from [first, last) range - * \note If first == last, return last - */ - template - static typename std::enable_if::value - , InputIt>::type get( InputIt first, InputIt last ) { - const auto size = std::distance( first, last ); - if( 0 == size ) return last; - using diff_t = typename std::iterator_traits::difference_type; - return std::next( first, get( 0, size - 1 ) ); - } + /** + * \brief Return random iterator from iterator range + * \param first, last - the range of elements + * \return Random iterator from [first, last) range + * \note If first == last, return last + */ + template + static typename std::enable_if::value, + InputIt>::type + get(InputIt first, InputIt last) + { + const auto size = std::distance(first, last); + if (0 == size) return last; + using diff_t = typename std::iterator_traits::difference_type; + return std::next(first, get(0, size - 1)); + } - /** - * \brief Return random iterator from Container - * \param container The container with elements - * \return Random iterator from container - * \note If container is empty return std::end( container ) iterator - */ - template - static auto get( Container& container ) -> - typename std::enable_if::value - , decltype(std::begin(container)) - >::type { - return get( std::begin( container ), std::end( container ) ); - } + /** + * \brief Return random iterator from Container + * \param container The container with elements + * \return Random iterator from container + * \note If container is empty return std::end( container ) iterator + */ + template + static auto get(Container& container) -> typename std::enable_if< + details::is_iterator::value, + decltype(std::begin(container))>::type + { + return get(std::begin(container), std::end(container)); + } - /** - * \brief Return container filled with random values - * \param from The first limit number of a random range - * \param to The second limit number of a random range - * \param size The number of elements in resulting container - * \return Container filled with random values - * \note Container "reserve" method will be called before generation - */ - template class Container, typename A> - static typename std::enable_if< - details::has_reserve>::value - , Container>::type get(A from, A to, std::size_t size) { - Container container; - - container.reserve(size); - for (std::size_t i = 0; i < size; ++i) - container.insert(std::end(container), get(from, to)); - - return container; - } + /** + * \brief Return container filled with random values + * \param from The first limit number of a random range + * \param to The second limit number of a random range + * \param size The number of elements in resulting container + * \return Container filled with random values + * \note Container "reserve" method will be called before generation + */ + template