view common/random.h @ 0:a4671277546c tip

created the repository for the thymian project
author ferencd
date Tue, 17 Aug 2021 11:19:54 +0200
parents
children
line wrap: on
line source
#ifndef EFFOLKRONIUM_RANDOM_HPP
#define EFFOLKRONIUM_RANDOM_HPP

#include <random>
#include <chrono> // timed seed
#include <type_traits>
#include <cassert>
#include <initializer_list>
#include <utility> // std::forward, std::declval
#include <algorithm> // std::shuffle, std::next, std::distance
#include <iterator> // std::begin, std::end, std::iterator_traits
#include <limits> // std::numeric_limits
#include <ostream>
#include <istream>

namespace effolkronium {

    namespace details {
        /// Key type for getting common type numbers or objects
        struct common{ };

        /// True if type T is applicable by a std::uniform_int_distribution
        template<typename T>
        struct is_uniform_int {
            static constexpr bool value =
                   std::is_same<T,              short>::value
                || std::is_same<T,                int>::value
                || std::is_same<T,               long>::value
                || std::is_same<T,          long long>::value
                || std::is_same<T,     unsigned short>::value
                || std::is_same<T,       unsigned int>::value
                || std::is_same<T,      unsigned long>::value
                || std::is_same<T, unsigned long long>::value;
        };

        /// True if type T is applicable by a std::uniform_real_distribution
        template<typename T>
        struct is_uniform_real {
            static constexpr bool value =
                   std::is_same<T,       float>::value
                || std::is_same<T,      double>::value
                || std::is_same<T, long double>::value;
        };

        /// True if type T is plain byte
        template<typename T>
        struct is_byte {
            static constexpr bool value =
                   std::is_same<T,   signed char>::value
                || std::is_same<T, unsigned char>::value;
        };

        /// True if type T is plain number type
        template<typename T>
        struct is_supported_number {
            static constexpr bool value =
                   is_byte        <T>::value
                || is_uniform_real<T>::value
                || is_uniform_int <T>::value;
        };

        /// True if type T is iterator
        template<typename T>
        struct is_iterator {
        private:
            static char test( ... );

            template <typename U,
                typename = typename std::iterator_traits<U>::difference_type,
                typename = typename std::iterator_traits<U>::pointer,
                typename = typename std::iterator_traits<U>::reference,
                typename = typename std::iterator_traits<U>::value_type,
                typename = typename std::iterator_traits<U>::iterator_category
            > static long test( U&& );
        public:
            static constexpr bool value = std::is_same<
                decltype( test( std::declval<T>( ) ) ), long>::value;
        };

    } // 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::uintmax_t>( std::random_device{ }( ) ),
                static_cast<std::uintmax_t>( 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<
        typename Engine,
        typename Seeder = seeder_default,
        template<typename> class IntegerDist = std::uniform_int_distribution,
        template<typename> class RealDist = std::uniform_real_distribution,
        typename BoolDist = std::bernoulli_distribution
    >
    class basic_random_static {
    public:
        basic_random_static( ) = delete;

        /// 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<typename T>
        using integer_dist_t = IntegerDist<T>;

        /// Type of used real distribution
        template<typename T>
        using real_dist_t = RealDist<T>;

        /// Type of used bool distribution
        using bool_dist_t = BoolDist;

        /// Key type for getting common type numbers or objects
        using common = details::common;

        /**
        * \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.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.seed( value );
        }

        /**
        * \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<typename Sseq>
        static void seed( Sseq& seq ) {
            engine.seed( seq );
        }

        /// return random number from engine in [min(), max()] range
        static typename Engine::result_type get( ) {
            return engine( );
        }

        /**
        * \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 == 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<typename CharT, typename Traits>
        static void serialize( std::basic_ostream<CharT, Traits>& ost ) {
            ost << engine;
        }

        /**
        * \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<typename CharT, typename Traits>
        static void deserialize( std::basic_istream<CharT, Traits>& ist ) {
            ist >> engine;
        }

        /**
        * \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<typename T>
        static typename std::enable_if<details::is_uniform_int<T>::value
            , T>::type get( T from = std::numeric_limits<T>::min( ),
                            T to = std::numeric_limits<T>::max( ) ) {
            if( from < to ) // Allow range from higher to lower
                return IntegerDist<T>{ from, to }( engine );
            return IntegerDist<T>{ to, from }( engine );
        }

        /**
        * \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<typename T>
        static typename std::enable_if<details::is_uniform_real<T>::value
            , T>::type get( T from = std::numeric_limits<T>::min( ),
                            T to = std::numeric_limits<T>::max( ) ) {
            if( from < to ) // Allow range from higher to lower
                return RealDist<T>{ from, to }( engine );
            return RealDist<T>{ to, from }( engine );
        }

        /**
        * \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<typename T>
        static typename std::enable_if<details::is_byte<T>::value
            , T>::type get( T from = std::numeric_limits<T>::min( ),
                            T to = std::numeric_limits<T>::max( ) ) {
            // Choose between short and unsigned short for byte conversion
            using short_t = typename std::conditional<std::is_signed<T>::value,
                short, unsigned short>::type;

            return static_cast<T>( get<short_t>( 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 singed to unsigned types
        *       Why? std::common_type<Unsigned, Signed> 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<A, B>::type
        >
        static typename std::enable_if<
               std::is_same<Key, common>::value
            && details::is_supported_number<A>::value
            && details::is_supported_number<B>::value
            // Prevent implicit type conversion from singed to unsigned types
            && std::is_signed<A>::value != std::is_unsigned<B>::value
            , C>::type get( A from = std::numeric_limits<A>::min( ),
                            B to = std::numeric_limits<B>::max( ) ) {
            return get( static_cast<C>( from ), static_cast<C>( to ) );
        }

        /**
        * \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<typename T>
        static typename std::enable_if<std::is_same<T, bool>::value
            , bool>::type get( const double probability = 0.5 ) {
            assert( 0 <= probability && 1 >= probability ); // out of [0; 1] range
            return BoolDist{ probability }( engine );
        }

        /**
        * \brief Return random value from initilizer_list
        * \param init_list initilizer_list with values
        * \return Random value from initilizer_list
        * \note Should be 1 or more elements in initilizer_list
        * \note Warning! Elements in initilizer_list can't be moved:
        *               https://stackoverflow.com/a/8193157/5734836
        */
        template<typename T>
        static T get( std::initializer_list<T> 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<typename InputIt>
        static typename std::enable_if<details::is_iterator<InputIt>::value
            , InputIt>::type get( InputIt first, InputIt last ) {
            const auto size = std::distance( first, last );
            if( 0u == size ) return last;
            using diff_t = typename std::iterator_traits<InputIt>::difference_type;
            return std::next( first, get<diff_t>( 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<typename Container>
        static typename std::enable_if<details::is_iterator<
            decltype( std::begin( std::declval<Container>( ) ) )>::value
            , decltype( std::begin( std::declval<Container>( ) ) )
        >::type get( Container& container ) {
            return get( std::begin( container ), std::end( container ) );
        }

        /**
        * \brief Return value from custom Dist distribution
        *        seeded by internal random engine
        * \param Dist The type of custom distribution with next concept:
        *        http://en.cppreference.com/w/cpp/concept/RandomNumberDistribution
        * \param args The arguments which will be forwarded to Dist constructor
        * \return Value from custom distribution
        */
        template<typename Dist, typename... Args>
        static typename Dist::result_type get( Args&&... args ) {
            return Dist{ std::forward<Args>( args )... }( engine );
        }

        /**
        * \brief Return value from custom 'dist' distribution
        *        seeded by internal random engine
        * \param dist The custom distribution with next concept:
        *        http://en.cppreference.com/w/cpp/concept/RandomNumberDistribution
        * \param args The arguments which will be forwarded to Dist constructor
        * \return Value from custom 'dist' distribution
        */
        template<typename Dist>
        static typename Dist::result_type get( Dist& dist ) {
            return dist( engine );
        }

        /**
        * \brief Reorders the elements in the given range [first, last)
        *        such that each possible permutation of those elements
        *        has equal probability of appearance.
        * \param first, last - the range of elements to shuffle randomly
        */
        template<typename RandomIt>
        static void shuffle( RandomIt first, RandomIt last ) {
            std::shuffle( first, last, engine );
        }

        /**
        * \brief Reorders the elements in the given container
        *        such that each possible permutation of those elements
        *        has equal probability of appearance.
        * \param container - the container with elements to shuffle randomly
        */
        template<typename Container>
        static void shuffle( Container& container ) {
            shuffle( std::begin( container ), std::end( container ) );
        }

        /// return internal engine by copy
        static Engine get_engine( ) {
            return engine;
        }
    protected:
        /// return engine seeded by Seeder
        static Engine make_seeded_engine( ) {
            // Make seeder instance for seed return by reference like std::seed_seq
            Seeder seeder;
            return Engine{ seeder( ) };
        }
    protected:
        /// The random number engine
        static Engine engine;
    };

    /// Seed random number engine by Seeder
    template<
        typename Engine,
        typename Seeder,
        template<typename> class IntegerDist,
        template<typename> class RealDist,
        typename BoolDist
        >
    Engine basic_random_static<Engine, Seeder, IntegerDist, RealDist, BoolDist
    // VS2017 issue, can't init by Seeder from lambda
    >::engine( make_seeded_engine( ) );

    /**
    * \brief Base template class for random
    *        with thread_local API and thread_local internal member storage
    * \note it IS thread safe but less efficient then
    *                           basic_random_static
    * \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 Engine,
        typename Seeder = seeder_default,
        template<typename> class IntegerDist = std::uniform_int_distribution,
        template<typename> class RealDist = std::uniform_real_distribution,
        typename BoolDist = std::bernoulli_distribution
    >
    class basic_random_thread_local {
    public:
        basic_random_thread_local( ) = delete;

        /// 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<typename T>
        using integer_dist_t = IntegerDist<T>;

        /// Type of used real distribution
        template<typename T>
        using real_dist_t = RealDist<T>;

        /// Type of used bool distribution
        using bool_dist_t = BoolDist;

        /// Key type for getting common type numbers or objects
        using common = details::common;

        /**
        * \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.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.seed( value );
        }

        /**
        * \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<typename Sseq>
        static void seed( Sseq& seq ) {
            engine.seed( seq );
        }

        /// return random number from engine in [min(), max()] range
        static typename Engine::result_type get( ) {
            return engine( );
        }

        /**
        * \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 == 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<typename CharT, typename Traits>
        static void serialize( std::basic_ostream<CharT, Traits>& ost ) {
            ost << engine;
        }

        /**
        * \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<typename CharT, typename Traits>
        static void deserialize( std::basic_istream<CharT, Traits>& ist ) {
            ist >> engine;
        }

        /**
        * \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<typename T>
        static typename std::enable_if<details::is_uniform_int<T>::value
            , T>::type get( T from = std::numeric_limits<T>::min( ),
                            T to = std::numeric_limits<T>::max( ) ) {
            if( from < to ) // Allow range from higher to lower
                return IntegerDist<T>{ from, to }( engine );
            return IntegerDist<T>{ to, from }( engine );
        }

        /**
        * \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<typename T>
        static typename std::enable_if<details::is_uniform_real<T>::value
            , T>::type get( T from = std::numeric_limits<T>::min( ),
                            T to = std::numeric_limits<T>::max( ) ) {
            if( from < to ) // Allow range from higher to lower
                return RealDist<T>{ from, to }( engine );
            return RealDist<T>{ to, from }( engine );
        }

        /**
        * \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<typename T>
        static typename std::enable_if<details::is_byte<T>::value
            , T>::type get( T from = std::numeric_limits<T>::min( ),
                            T to = std::numeric_limits<T>::max( ) ) {
            // Choose between short and unsigned short for byte conversion
            using short_t = typename std::conditional<std::is_signed<T>::value,
                short, unsigned short>::type;

            return static_cast<T>( get<short_t>( 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 singed to unsigned types
        *       Why? std::common_type<Unsigned, Signed> 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<A, B>::type
        >
        static typename std::enable_if<
               std::is_same<Key, common>::value
            && details::is_supported_number<A>::value
            && details::is_supported_number<B>::value
            // Prevent implicit type conversion from singed to unsigned types
            && std::is_signed<A>::value != std::is_unsigned<B>::value
            , C>::type get( A from = std::numeric_limits<A>::min( ),
                            B to = std::numeric_limits<B>::max( ) ) {
            return get( static_cast<C>( from ), static_cast<C>( to ) );
        }

        /**
        * \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<typename T>
        static typename std::enable_if<std::is_same<T, bool>::value
            , bool>::type get( const double probability = 0.5 ) {
            assert( 0 <= probability && 1 >= probability ); // out of [0; 1] range
            return BoolDist{ probability }( engine );
        }

        /**
        * \brief Return random value from initilizer_list
        * \param init_list initilizer_list with values
        * \return Random value from initilizer_list
        * \note Should be 1 or more elements in initilizer_list
        * \note Warning! Elements in initilizer_list can't be moved:
        *               https://stackoverflow.com/a/8193157/5734836
        */
        template<typename T>
        static T get( std::initializer_list<T> 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<typename InputIt>
        static typename std::enable_if<details::is_iterator<InputIt>::value
            , InputIt>::type get( InputIt first, InputIt last ) {
            const auto size = std::distance( first, last );
            if( 0u == size ) return last;
            using diff_t = typename std::iterator_traits<InputIt>::difference_type;
            return std::next( first, get<diff_t>( 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<typename Container>
        static typename std::enable_if<details::is_iterator<
            decltype( std::begin( std::declval<Container>( ) ) )>::value
            , decltype( std::begin( std::declval<Container>( ) ) )
        >::type get( Container& container ) {
            return get( std::begin( container ), std::end( container ) );
        }

        /**
        * \brief Return value from custom Dist distribution
        *        seeded by internal random engine
        * \param Dist The type of custom distribution with next concept:
        *        http://en.cppreference.com/w/cpp/concept/RandomNumberDistribution
        * \param args The arguments which will be forwarded to Dist constructor
        * \return Value from custom distribution
        */
        template<typename Dist, typename... Args>
        static typename Dist::result_type get( Args&&... args ) {
            return Dist{ std::forward<Args>( args )... }( engine );
        }

        /**
        * \brief Return value from custom 'dist' distribution
        *        seeded by internal random engine
        * \param dist The custom distribution with next concept:
        *        http://en.cppreference.com/w/cpp/concept/RandomNumberDistribution
        * \param args The arguments which will be forwarded to Dist constructor
        * \return Value from custom 'dist' distribution
        */
        template<typename Dist>
        static typename Dist::result_type get( Dist& dist ) {
            return dist( engine );
        }

        /**
        * \brief Reorders the elements in the given range [first, last)
        *        such that each possible permutation of those elements
        *        has equal probability of appearance.
        * \param first, last - the range of elements to shuffle randomly
        */
        template<typename RandomIt>
        static void shuffle( RandomIt first, RandomIt last ) {
            std::shuffle( first, last, engine );
        }

        /**
        * \brief Reorders the elements in the given container
        *        such that each possible permutation of those elements
        *        has equal probability of appearance.
        * \param container - the container with elements to shuffle randomly
        */
        template<typename Container>
        static void shuffle( Container& container ) {
            shuffle( std::begin( container ), std::end( container ) );
        }

        /// return internal engine by copy
        static Engine get_engine( ) {
            return engine;
        }
    protected:
        /// return engine seeded by Seeder
        static Engine make_seeded_engine( ) {
            // Make seeder instance for seed return by reference like std::seed_seq
            Seeder seeder;
            return Engine{ seeder( ) };
        }
    protected:
        /// The random number engine
        static thread_local Engine engine;
    };

    /// Seed random number engine by Seeder
    template<
        typename Engine,
        typename Seeder,
        template<typename> class IntegerDist,
        template<typename> class RealDist,
        typename BoolDist
        >
    thread_local Engine basic_random_thread_local<Engine, Seeder, IntegerDist, RealDist, BoolDist
    // VS2017 issue, can't init by Seeder from lambda
    >::engine( make_seeded_engine( ) );

    /**
    * \brief Base template class for random
    *        with local API and local internal member storage
    * \note it IS thread safe but less efficient then
    *                           basic_random_static
    * \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 Engine,
        typename Seeder = seeder_default,
        template<typename> class IntegerDist = std::uniform_int_distribution,
        template<typename> class RealDist = std::uniform_real_distribution,
        typename BoolDist = std::bernoulli_distribution
    >
    class basic_random_local {
    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<typename T>
        using integer_dist_t = IntegerDist<T>;

        /// Type of used real distribution
        template<typename T>
        using real_dist_t = RealDist<T>;

        /// Type of used bool distribution
        using bool_dist_t = BoolDist;

        /// Key type for getting common type numbers or objects
        using common = details::common;

        /**
        * \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
        void discard( const unsigned long long z ) {
            engine.discard( z );
        }

        /// Reseed by Seeder
        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
        */
        void seed( const typename Engine::result_type value =
                          Engine::default_seed ) {
            engine.seed( value );
        }

        /**
        * \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<typename Sseq>
        void seed( Sseq& seq ) {
            engine.seed( seq );
        }

        /// return random number from engine in [min(), max()] range
        typename Engine::result_type get( ) {
            return engine( );
        }

        /**
        * \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
        */
        bool is_equal( const Engine& other ) {
            return engine == 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<typename CharT, typename Traits>
        void serialize( std::basic_ostream<CharT, Traits>& ost ) {
            ost << engine;
        }

        /**
        * \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<typename CharT, typename Traits>
        void deserialize( std::basic_istream<CharT, Traits>& ist ) {
            ist >> engine;
        }

        /**
        * \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<typename T>
        typename std::enable_if<details::is_uniform_int<T>::value
            , T>::type get( T from = std::numeric_limits<T>::min( ),
                            T to = std::numeric_limits<T>::max( ) ) {
            if( from < to ) // Allow range from higher to lower
                return IntegerDist<T>{ from, to }( engine );
            return IntegerDist<T>{ to, from }( engine );
        }

        /**
        * \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<typename T>
        typename std::enable_if<details::is_uniform_real<T>::value
            , T>::type get( T from = std::numeric_limits<T>::min( ),
                            T to = std::numeric_limits<T>::max( ) ) {
            if( from < to ) // Allow range from higher to lower
                return RealDist<T>{ from, to }( engine );
            return RealDist<T>{ to, from }( engine );
        }

        /**
        * \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<typename T>
        typename std::enable_if<details::is_byte<T>::value
            , T>::type get( T from = std::numeric_limits<T>::min( ),
                            T to = std::numeric_limits<T>::max( ) ) {
            // Choose between short and unsigned short for byte conversion
            using short_t = typename std::conditional<std::is_signed<T>::value,
                short, unsigned short>::type;

            return static_cast<T>( get<short_t>( 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 singed to unsigned types
        *       Why? std::common_type<Unsigned, Signed> 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<A, B>::type
        >
        typename std::enable_if<
               std::is_same<Key, common>::value
            && details::is_supported_number<A>::value
            && details::is_supported_number<B>::value
            // Prevent implicit type conversion from singed to unsigned types
            && std::is_signed<A>::value != std::is_unsigned<B>::value
            , C>::type get( A from = std::numeric_limits<A>::min( ),
                            B to = std::numeric_limits<B>::max( ) ) {
            return get( static_cast<C>( from ), static_cast<C>( to ) );
        }

        /**
        * \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<typename T>
        typename std::enable_if<std::is_same<T, bool>::value
            , bool>::type get( const double probability = 0.5 ) {
            assert( 0 <= probability && 1 >= probability ); // out of [0; 1] range
            return BoolDist{ probability }( engine );
        }

        /**
        * \brief Return random value from initilizer_list
        * \param init_list initilizer_list with values
        * \return Random value from initilizer_list
        * \note Should be 1 or more elements in initilizer_list
        * \note Warning! Elements in initilizer_list can't be moved:
        *               https://stackoverflow.com/a/8193157/5734836
        */
        template<typename T>
        T get( std::initializer_list<T> 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<typename InputIt>
        typename std::enable_if<details::is_iterator<InputIt>::value
            , InputIt>::type get( InputIt first, InputIt last ) {
            const auto size = std::distance( first, last );
            if( 0u == size ) return last;
            using diff_t = typename std::iterator_traits<InputIt>::difference_type;
            return std::next( first, get<diff_t>( 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<typename Container>
        typename std::enable_if<details::is_iterator<
            decltype( std::begin( std::declval<Container>( ) ) )>::value
            , decltype( std::begin( std::declval<Container>( ) ) )
        >::type get( Container& container ) {
            return get( std::begin( container ), std::end( container ) );
        }

        /**
        * \brief Return value from custom Dist distribution
        *        seeded by internal random engine
        * \param Dist The type of custom distribution with next concept:
        *        http://en.cppreference.com/w/cpp/concept/RandomNumberDistribution
        * \param args The arguments which will be forwarded to Dist constructor
        * \return Value from custom distribution
        */
        template<typename Dist, typename... Args>
        typename Dist::result_type get( Args&&... args ) {
            return Dist{ std::forward<Args>( args )... }( engine );
        }

        /**
        * \brief Return value from custom 'dist' distribution
        *        seeded by internal random engine
        * \param dist The custom distribution with next concept:
        *        http://en.cppreference.com/w/cpp/concept/RandomNumberDistribution
        * \param args The arguments which will be forwarded to Dist constructor
        * \return Value from custom 'dist' distribution
        */
        template<typename Dist>
        typename Dist::result_type get( Dist& dist ) {
            return dist( engine );
        }

        /**
        * \brief Reorders the elements in the given range [first, last)
        *        such that each possible permutation of those elements
        *        has equal probability of appearance.
        * \param first, last - the range of elements to shuffle randomly
        */
        template<typename RandomIt>
        void shuffle( RandomIt first, RandomIt last ) {
            std::shuffle( first, last, engine );
        }

        /**
        * \brief Reorders the elements in the given container
        *        such that each possible permutation of those elements
        *        has equal probability of appearance.
        * \param container - the container with elements to shuffle randomly
        */
        template<typename Container>
        void shuffle( Container& container ) {
            shuffle( std::begin( container ), std::end( container ) );
        }

        /// return internal engine by copy
        Engine get_engine( ) const {
            return engine;
        }
    protected:
        /// return engine seeded by Seeder
        static Engine make_seeded_engine( ) {
            // Make seeder instance for seed return by reference like std::seed_seq
            Seeder seeder;
            return Engine{ seeder( ) };
        }
    protected:
        /// The random number engine
        Engine engine{ make_seeded_engine( ) };
    };

    /**
    * \brief The basic static random alias based on a std::mt19937
    * \note It uses static methods API and data with static storage
    * \note Not thread safe but more prefomance
    */
    using random_static = basic_random_static<std::mt19937>;

    /**
    * \brief The basic static random alias based on a std::mt19937
    * \note It uses static methods API and data with thread_local storage
    * \note Thread safe but less perfomance
    */
    using random_thread_local = basic_random_thread_local<std::mt19937>;

    /**
    * \brief The basic static random alias based on a std::mt19937
    * \note It uses non static methods API and data with auto storage
    * \note Not thread safe. Should construct on the stack at local scope
    */
    using random_local = basic_random_local<std::mt19937>;

} // namespace effolkronium

#endif // #ifndef EFFOLKRONIUM_RANDOM_HPP