# Program Listing for File hiprand.hpp

↰ Return to documentation for file (`hiprand/hiprand.hpp`)

    // Copyright (c) 2017-2022 Advanced Micro Devices, Inc. All rights reserved.
    //
    // Permission is hereby granted, free of charge, to any person obtaining a copy
    // of this software and associated documentation files (the "Software"), to deal
    // in the Software without restriction, including without limitation the rights
    // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    // copies of the Software, and to permit persons to whom the Software is
    // furnished to do so, subject to the following conditions:
    //
    // The above copyright notice and this permission notice shall be included in
    // all copies or substantial portions of the Software.
    //
    // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
    // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    // THE SOFTWARE.

    #ifndef HIPRAND_HPP_
    #define HIPRAND_HPP_

    // At least C++11 required
    #if defined(__cplusplus) && __cplusplus >= 201103L

    #include <random>
    #include <exception>
    #include <string>
    #include <sstream>
    #include <type_traits>
    #include <limits>

    #include "hiprand/hiprand.h"
    #include "hiprand/hiprand_kernel.h"

    namespace hiprand_cpp {

    class error : public std::exception
    {
    public:
        typedef hiprandStatus_t error_type;

        error(error_type error) noexcept
            : m_error(error),
              m_error_string(to_string(error))
        {
        }

        ~error() noexcept
        {
        }

        error_type error_code() const noexcept
        {
            return m_error;
        }

        std::string error_string() const noexcept
        {
            return m_error_string;
        }

        const char* what() const noexcept
        {
            return m_error_string.c_str();
        }

        static std::string to_string(error_type error)
        {
            switch(error)
            {
                case HIPRAND_STATUS_SUCCESS:
                    return "Success";
                case HIPRAND_STATUS_VERSION_MISMATCH:
                    return "Header file and linked library version do not match";
                case HIPRAND_STATUS_NOT_INITIALIZED:
                    return "Generator was not created using hiprandCreateGenerator";
                case HIPRAND_STATUS_ALLOCATION_FAILED:
                    return "Memory allocation failed during execution";
                case HIPRAND_STATUS_TYPE_ERROR:
                    return "Generator type is wrong";
                case HIPRAND_STATUS_OUT_OF_RANGE:
                    return "Argument out of range";
                case HIPRAND_STATUS_LENGTH_NOT_MULTIPLE:
                    return "Length requested is not a multiple of dimension";
                case HIPRAND_STATUS_DOUBLE_PRECISION_REQUIRED:
                    return "GPU does not have double precision";
                case HIPRAND_STATUS_LAUNCH_FAILURE:
                    return "Kernel launch failure";
                case HIPRAND_STATUS_PREEXISTING_FAILURE:
                    return "Preexisting failure on library entry";
                case HIPRAND_STATUS_INITIALIZATION_FAILED:
                    return "Initialization of HIP failed";
                case HIPRAND_STATUS_ARCH_MISMATCH:
                    return "Architecture mismatch, GPU does not support requested feature";
                case HIPRAND_STATUS_INTERNAL_ERROR:
                    return "Internal library error";
                case HIPRAND_STATUS_NOT_IMPLEMENTED:
                    return "Feature not implemented yet";
                default: {
                    std::stringstream s;
                    s << "Unknown hipRAND error (" << error << ")";
                    return s.str();
                }
            }
        }

        friend
        bool operator==(const error& l, const error& r)
        {
            return l.error_code() == r.error_code();
        }

        friend
        bool operator!=(const error& l, const error& r)
        {
            return !(l == r);
        }

    private:
        error_type m_error;
        std::string m_error_string;
    };

    template<class IntType = unsigned int>
    class uniform_int_distribution
    {
        static_assert(std::is_same<unsigned char, IntType>::value
                          || std::is_same<unsigned short, IntType>::value
                          || std::is_same<unsigned int, IntType>::value
                          || std::is_same<unsigned long long int, IntType>::value,
                      "Only unsigned int type is supported in uniform_int_distribution");

    public:
        typedef IntType result_type;

        uniform_int_distribution()
        {
        }

        void reset()
        {
        }

        IntType min() const
        {
            return 0;
        }

        IntType max() const
        {
            return std::numeric_limits<IntType>::max();
        }

        template<class Generator>
        void operator()(Generator& g, IntType * output, size_t size)
        {
            hiprandStatus_t status;
            status = this->generate(g, output, size);
            if(status != HIPRAND_STATUS_SUCCESS) throw hiprand_cpp::error(status);
        }

        bool operator==(const uniform_int_distribution<IntType>& other)
        {
            (void) other;
            return true;
        }

        bool operator!=(const uniform_int_distribution<IntType>& other)
        {
            return !(*this == other);
        }

    private:
        template<class Generator>
        hiprandStatus_t generate(Generator& g, unsigned char * output, size_t size)
        {
            return hiprandGenerateChar(g.m_generator, output, size);
        }

        template<class Generator>
        hiprandStatus_t generate(Generator& g, unsigned short * output, size_t size)
        {
            return hiprandGenerateShort(g.m_generator, output, size);
        }

        template<class Generator>
        hiprandStatus_t generate(Generator& g, unsigned int * output, size_t size)
        {
            return hiprandGenerate(g.m_generator, output, size);
        }

        template<class Generator>
        hiprandStatus_t generate(Generator& g, unsigned long long int* output, size_t size)
        {
            return hiprandGenerateLongLong(g.m_generator, output, size);
        }
    };

    template<class RealType = float>
    class uniform_real_distribution
    {
        static_assert(
            std::is_same<float, RealType>::value
            || std::is_same<double, RealType>::value
            || std::is_same<half, RealType>::value,
            "Only float, double, and half types are supported in uniform_real_distribution"
        );

    public:
        typedef RealType result_type;

        uniform_real_distribution()
        {
        }

        void reset()
        {
        }

        RealType min() const
        {
            if(std::is_same<float, RealType>::value)
            {
                return static_cast<RealType>(2.3283064e-10f);
            }
            return static_cast<RealType>(2.3283064365386963e-10);
        }

        RealType max() const
        {
            return 1.0;
        }

        template<class Generator>
        void operator()(Generator& g, RealType * output, size_t size)
        {
            hiprandStatus_t status;
            status = this->generate(g, output, size);
            if(status != HIPRAND_STATUS_SUCCESS) throw hiprand_cpp::error(status);
        }

        bool operator==(const uniform_real_distribution<RealType>& other)
        {
            (void) other;
            return true;
        }

        bool operator!=(const uniform_real_distribution<RealType>& other)
        {
            return !(*this == other);
        }

    private:
        template<class Generator>
        hiprandStatus_t generate(Generator& g, float * output, size_t size)
        {
            return hiprandGenerateUniform(g.m_generator, output, size);
        }

        template<class Generator>
        hiprandStatus_t generate(Generator& g, double * output, size_t size)
        {
            return hiprandGenerateUniformDouble(g.m_generator, output, size);
        }

        template<class Generator>
        hiprandStatus_t generate(Generator& g, half * output, size_t size)
        {
            return hiprandGenerateUniformHalf(g.m_generator, output, size);
        }
    };

    template<class RealType = float>
    class normal_distribution
    {
        static_assert(
            std::is_same<float, RealType>::value
            || std::is_same<double, RealType>::value
            || std::is_same<half, RealType>::value,
            "Only float, double and half types are supported in normal_distribution"
        );

    public:
        typedef RealType result_type;

        class param_type
        {
        public:
            using distribution_type = normal_distribution<RealType>;
            param_type(RealType mean = 0.0, RealType stddev = 1.0)
                : m_mean(mean), m_stddev(stddev)
            {
            }

            param_type(const param_type& params) = default;

            RealType mean() const
            {
                return m_mean;
            }

            RealType stddev() const
            {
                return m_stddev;
            }

            bool operator==(const param_type& other)
            {
                return m_mean == other.m_mean && m_stddev == other.m_stddev;
            }

            bool operator!=(const param_type& other)
            {
                return !(*this == other);
            }
        private:
            RealType m_mean;
            RealType m_stddev;
        };

        normal_distribution(RealType mean = 0.0, RealType stddev = 1.0)
            : m_params(mean, stddev)
        {
        }

        normal_distribution(const param_type& params)
            : m_params(params)
        {
        }

        void reset()
        {
        }

        RealType mean() const
        {
            return m_params.mean();
        }

        RealType stddev() const
        {
            return m_params.stddev();
        }

        RealType min() const
        {
            return std::numeric_limits<RealType>::lowest();
        }

        RealType max() const
        {
            return std::numeric_limits<RealType>::max();
        }

        param_type param() const
        {
            return m_params;
        }

        void param(const param_type& params)
        {
            m_params = params;
        }

        template<class Generator>
        void operator()(Generator& g, RealType * output, size_t size)
        {
            hiprandStatus_t status;
            status = this->generate(g, output, size);
            if(status != HIPRAND_STATUS_SUCCESS) throw hiprand_cpp::error(status);
        }

        bool operator==(const normal_distribution<RealType>& other)
        {
            return this->m_params == other.m_params;
        }

        bool operator!=(const normal_distribution<RealType>& other)
        {
            return !(*this == other);
        }

    private:
        template<class Generator>
        hiprandStatus_t generate(Generator& g, float * output, size_t size)
        {
            return hiprandGenerateNormal(
                g.m_generator, output, size, this->mean(), this->stddev()
            );
        }

        template<class Generator>
        hiprandStatus_t generate(Generator& g, double * output, size_t size)
        {
            return hiprandGenerateNormalDouble(
                g.m_generator, output, size, this->mean(), this->stddev()
            );
        }

        template<class Generator>
        hiprandStatus_t generate(Generator& g, half * output, size_t size)
        {
            return hiprandGenerateNormalHalf(
                g.m_generator, output, size, this->mean(), this->stddev()
            );
        }

        param_type m_params;
    };

    template<class RealType = float>
    class lognormal_distribution
    {
        static_assert(
            std::is_same<float, RealType>::value
            || std::is_same<double, RealType>::value
            || std::is_same<half, RealType>::value,
            "Only float, double and half types are supported in lognormal_distribution"
        );

    public:
        typedef RealType result_type;

        class param_type
        {
        public:
            using distribution_type = lognormal_distribution<RealType>;
            param_type(RealType m = 0.0, RealType s = 1.0)
                : m_mean(m), m_stddev(s)
            {
            }

            param_type(const param_type& params) = default;

            RealType m() const
            {
                return m_mean;
            }

            RealType s() const
            {
                return m_stddev;
            }

            bool operator==(const param_type& other)
            {
                return m_mean == other.m_mean && m_stddev == other.m_stddev;
            }

            bool operator!=(const param_type& other)
            {
                return !(*this == other);
            }
        private:
            RealType m_mean;
            RealType m_stddev;
        };

        lognormal_distribution(RealType m = 0.0, RealType s = 1.0)
            : m_params(m, s)
        {
        }

        lognormal_distribution(const param_type& params)
            : m_params(params)
        {
        }

        void reset()
        {
        }

        RealType m() const
        {
            return m_params.m();
        }

        RealType s() const
        {
            return m_params.s();
        }

        param_type param() const
        {
            return m_params;
        }

        void param(const param_type& params)
        {
            m_params = params;
        }

        RealType min() const
        {
            return 0;
        }

        RealType max() const
        {
            return std::numeric_limits<RealType>::max();
        }

        template<class Generator>
        void operator()(Generator& g, RealType * output, size_t size)
        {
            hiprandStatus_t status;
            status = this->generate(g, output, size);
            if(status != HIPRAND_STATUS_SUCCESS) throw hiprand_cpp::error(status);
        }

        bool operator==(const lognormal_distribution<RealType>& other)
        {
            return this->m_params == other.m_params;
        }

        bool operator!=(const lognormal_distribution<RealType>& other)
        {
            return !(*this == other);
        }

    private:
        template<class Generator>
        hiprandStatus_t generate(Generator& g, float * output, size_t size)
        {
            return hiprandGenerateLogNormal(
                g.m_generator, output, size, this->m(), this->s()
            );
        }

        template<class Generator>
        hiprandStatus_t generate(Generator& g, double * output, size_t size)
        {
            return hiprandGenerateLogNormalDouble(
                g.m_generator, output, size, this->m(), this->s()
            );
        }

        template<class Generator>
        hiprandStatus_t generate(Generator& g, half * output, size_t size)
        {
            return hiprandGenerateLogNormalHalf(
                g.m_generator, output, size, this->m(), this->s()
            );
        }

        param_type m_params;
    };

    template<class IntType = unsigned int>
    class poisson_distribution
    {
        static_assert(
            std::is_same<unsigned int, IntType>::value,
            "Only unsigned int type is supported in poisson_distribution"
        );

    public:
        typedef IntType result_type;

        class param_type
        {
        public:
            using distribution_type = poisson_distribution<IntType>;
            param_type(double mean = 1.0)
                : m_mean(mean)
            {
            }

            param_type(const param_type& params) = default;

            double mean() const
            {
                return m_mean;
            }

            bool operator==(const param_type& other)
            {
                return m_mean == other.m_mean;
            }

            bool operator!=(const param_type& other)
            {
                return !(*this == other);
            }

        private:
            double m_mean;
        };

        poisson_distribution(double mean = 1.0)
            : m_params(mean)
        {
        }

        poisson_distribution(const param_type& params)
            : m_params(params)
        {
        }

        void reset()
        {
        }

        double mean() const
        {
            return m_params.mean();
        }

        IntType min() const
        {
            return 0;
        }

        IntType max() const
        {
            return std::numeric_limits<IntType>::max();
        }

        param_type param() const
        {
            return m_params;
        }

        void param(const param_type& params)
        {
            m_params = params;
        }

        template<class Generator>
        void operator()(Generator& g, IntType * output, size_t size)
        {
            hiprandStatus_t status;
            status = hiprandGeneratePoisson(g.m_generator, output, size, this->mean());
            if(status != HIPRAND_STATUS_SUCCESS) throw hiprand_cpp::error(status);
        }

        bool operator==(const poisson_distribution<IntType>& other)
        {
            return this->m_params == other.m_params;
        }

        bool operator!=(const poisson_distribution<IntType>& other)
        {
            return !(*this == other);
        }

    private:
        param_type m_params;
    };

    template<unsigned long long DefaultSeed = HIPRAND_PHILOX4x32_DEFAULT_SEED>
    class philox4x32_10_engine
    {
    public:
        typedef unsigned int result_type;
        typedef unsigned long long offset_type;
        typedef unsigned long long seed_type;
        static constexpr seed_type default_seed = DefaultSeed;

        philox4x32_10_engine(seed_type seed_value = DefaultSeed,
                             offset_type offset_value = 0)
        {
            hiprandStatus_t status;
            status = hiprandCreateGenerator(&m_generator, this->type());
            if(status != HIPRAND_STATUS_SUCCESS) throw hiprand_cpp::error(status);
            try
            {
                if(offset_value > 0)
                {
                    this->offset(offset_value);
                }
                this->seed(seed_value);
            }
            catch(...)
            {
                (void)hiprandDestroyGenerator(m_generator);
                throw;
            }
        }

        philox4x32_10_engine(hiprandGenerator_t& generator)
            : m_generator(generator)
        {
            if(generator == NULL)
            {
                throw hiprand_cpp::error(HIPRAND_STATUS_NOT_INITIALIZED);
            }
            generator = NULL;
        }

        ~philox4x32_10_engine() noexcept(false)
        {
            hiprandStatus_t status = hiprandDestroyGenerator(m_generator);
            if(status != HIPRAND_STATUS_SUCCESS) throw hiprand_cpp::error(status);
        }

        void stream(hipStream_t value)
        {
            hiprandStatus_t status = hiprandSetStream(m_generator, value);
            if(status != HIPRAND_STATUS_SUCCESS) throw hiprand_cpp::error(status);
        }

        void offset(offset_type value)
        {
            hiprandStatus_t status = hiprandSetGeneratorOffset(this->m_generator, value);
            if(status != HIPRAND_STATUS_SUCCESS) throw hiprand_cpp::error(status);
        }

        void seed(seed_type value)
        {
            hiprandStatus_t status = hiprandSetPseudoRandomGeneratorSeed(this->m_generator, value);
            if(status != HIPRAND_STATUS_SUCCESS) throw hiprand_cpp::error(status);
        }

        template<class Generator>
        void operator()(result_type * output, size_t size)
        {
            hiprandStatus_t status;
            status = hiprandGenerate(m_generator, output, size);
            if(status != HIPRAND_STATUS_SUCCESS) throw hiprand_cpp::error(status);
        }

        result_type min() const
        {
            return 0;
        }

        result_type max() const
        {
            return std::numeric_limits<unsigned int>::max();
        }

        static constexpr hiprandRngType type()
        {
            return HIPRAND_RNG_PSEUDO_PHILOX4_32_10;
        }

    private:
        hiprandGenerator_t m_generator;

        template<class T>
        friend class ::hiprand_cpp::uniform_int_distribution;

        template<class T>
        friend class ::hiprand_cpp::uniform_real_distribution;

        template<class T>
        friend class ::hiprand_cpp::normal_distribution;

        template<class T>
        friend class ::hiprand_cpp::lognormal_distribution;

        template<class T>
        friend class ::hiprand_cpp::poisson_distribution;
    };

    template<unsigned long long DefaultSeed>
    constexpr typename philox4x32_10_engine<DefaultSeed>::seed_type philox4x32_10_engine<DefaultSeed>::default_seed;

    template<unsigned long long DefaultSeed = HIPRAND_XORWOW_DEFAULT_SEED>
    class xorwow_engine
    {
    public:
        typedef unsigned int result_type;
        typedef unsigned long long offset_type;
        typedef unsigned long long seed_type;
        static constexpr seed_type default_seed = DefaultSeed;

        xorwow_engine(seed_type seed_value = DefaultSeed,
                      offset_type offset_value = 0)
        {
            hiprandStatus_t status;
            status = hiprandCreateGenerator(&m_generator, this->type());
            if(status != HIPRAND_STATUS_SUCCESS) throw hiprand_cpp::error(status);
            try
            {
                if(offset_value > 0)
                {
                    this->offset(offset_value);
                }
                this->seed(seed_value);
            }
            catch(...)
            {
                (void)hiprandDestroyGenerator(m_generator);
                throw;
            }
        }

        xorwow_engine(hiprandGenerator_t& generator)
            : m_generator(generator)
        {
            if(generator == NULL)
            {
                throw hiprand_cpp::error(HIPRAND_STATUS_NOT_INITIALIZED);
            }
            generator = NULL;
        }

        ~xorwow_engine() noexcept(false)
        {
            hiprandStatus_t status = hiprandDestroyGenerator(m_generator);
            if(status != HIPRAND_STATUS_SUCCESS) throw hiprand_cpp::error(status);
        }

        void stream(hipStream_t value)
        {
            hiprandStatus_t status = hiprandSetStream(m_generator, value);
            if(status != HIPRAND_STATUS_SUCCESS) throw hiprand_cpp::error(status);
        }

        void offset(offset_type value)
        {
            hiprandStatus_t status = hiprandSetGeneratorOffset(this->m_generator, value);
            if(status != HIPRAND_STATUS_SUCCESS) throw hiprand_cpp::error(status);
        }

        void seed(seed_type value)
        {
            hiprandStatus_t status = hiprandSetPseudoRandomGeneratorSeed(this->m_generator, value);
            if(status != HIPRAND_STATUS_SUCCESS) throw hiprand_cpp::error(status);
        }

        template<class Generator>
        void operator()(result_type * output, size_t size)
        {
            hiprandStatus_t status;
            status = hiprandGenerate(m_generator, output, size);
            if(status != HIPRAND_STATUS_SUCCESS) throw hiprand_cpp::error(status);
        }

        result_type min() const
        {
            return 0;
        }

        result_type max() const
        {
            return std::numeric_limits<unsigned int>::max();
        }

        static constexpr hiprandRngType type()
        {
            return HIPRAND_RNG_PSEUDO_XORWOW;
        }

    private:
        hiprandGenerator_t m_generator;

        template<class T>
        friend class ::hiprand_cpp::uniform_int_distribution;

        template<class T>
        friend class ::hiprand_cpp::uniform_real_distribution;

        template<class T>
        friend class ::hiprand_cpp::normal_distribution;

        template<class T>
        friend class ::hiprand_cpp::lognormal_distribution;

        template<class T>
        friend class ::hiprand_cpp::poisson_distribution;
    };

    template<unsigned long long DefaultSeed>
    constexpr typename xorwow_engine<DefaultSeed>::seed_type xorwow_engine<DefaultSeed>::default_seed;

    template<unsigned long long DefaultSeed = HIPRAND_MRG32K3A_DEFAULT_SEED>
    class mrg32k3a_engine
    {
    public:
        typedef unsigned int result_type;
        typedef unsigned long long offset_type;
        typedef unsigned long long seed_type;
        static constexpr seed_type default_seed = DefaultSeed;

        mrg32k3a_engine(seed_type seed_value = DefaultSeed,
                        offset_type offset_value = 0)
        {
            hiprandStatus_t status;
            status = hiprandCreateGenerator(&m_generator, this->type());
            if(status != HIPRAND_STATUS_SUCCESS) throw hiprand_cpp::error(status);
            try
            {
                if(offset_value > 0)
                {
                    this->offset(offset_value);
                }
                this->seed(seed_value);
            }
            catch(...)
            {
                (void)hiprandDestroyGenerator(m_generator);
                throw;
            }
        }

        mrg32k3a_engine(hiprandGenerator_t& generator)
            : m_generator(generator)
        {
            if(generator == NULL)
            {
                throw hiprand_cpp::error(HIPRAND_STATUS_NOT_INITIALIZED);
            }
            generator = NULL;
        }

        ~mrg32k3a_engine() noexcept(false)
        {
            hiprandStatus_t status = hiprandDestroyGenerator(m_generator);
            if(status != HIPRAND_STATUS_SUCCESS) throw hiprand_cpp::error(status);
        }

        void stream(hipStream_t value)
        {
            hiprandStatus_t status = hiprandSetStream(m_generator, value);
            if(status != HIPRAND_STATUS_SUCCESS) throw hiprand_cpp::error(status);
        }

        void offset(offset_type value)
        {
            hiprandStatus_t status = hiprandSetGeneratorOffset(this->m_generator, value);
            if(status != HIPRAND_STATUS_SUCCESS) throw hiprand_cpp::error(status);
        }

        void seed(seed_type value)
        {
            hiprandStatus_t status = hiprandSetPseudoRandomGeneratorSeed(this->m_generator, value);
            if(status != HIPRAND_STATUS_SUCCESS) throw hiprand_cpp::error(status);
        }

        template<class Generator>
        void operator()(result_type * output, size_t size)
        {
            hiprandStatus_t status;
            status = hiprandGenerate(m_generator, output, size);
            if(status != HIPRAND_STATUS_SUCCESS) throw hiprand_cpp::error(status);
        }

        result_type min() const
        {
            return 1;
        }

        result_type max() const
        {
            return std::numeric_limits<unsigned int>::max();
        }

        static constexpr hiprandRngType type()
        {
            return HIPRAND_RNG_PSEUDO_MRG32K3A;
        }

    private:
        hiprandGenerator_t m_generator;

        template<class T>
        friend class ::hiprand_cpp::uniform_int_distribution;

        template<class T>
        friend class ::hiprand_cpp::uniform_real_distribution;

        template<class T>
        friend class ::hiprand_cpp::normal_distribution;

        template<class T>
        friend class ::hiprand_cpp::lognormal_distribution;

        template<class T>
        friend class ::hiprand_cpp::poisson_distribution;
    };

    template<unsigned long long DefaultSeed>
    constexpr typename mrg32k3a_engine<DefaultSeed>::seed_type mrg32k3a_engine<DefaultSeed>::default_seed;

    template<unsigned long long DefaultSeed = HIPRAND_MTGP32_DEFAULT_SEED>
    class mtgp32_engine
    {
    public:
        typedef unsigned int result_type;
        typedef unsigned long long seed_type;
        static constexpr seed_type default_seed = DefaultSeed;

        mtgp32_engine(seed_type seed_value = DefaultSeed)
        {
            hiprandStatus_t status;
            status = hiprandCreateGenerator(&m_generator, this->type());
            if(status != HIPRAND_STATUS_SUCCESS) throw hiprand_cpp::error(status);
            try
            {
                this->seed(seed_value);
            }
            catch(...)
            {
                (void)hiprandDestroyGenerator(m_generator);
                throw;
            }
        }

        mtgp32_engine(hiprandGenerator_t& generator)
            : m_generator(generator)
        {
            if(generator == NULL)
            {
                throw hiprand_cpp::error(HIPRAND_STATUS_NOT_INITIALIZED);
            }
            generator = NULL;
        }

        ~mtgp32_engine() noexcept(false)
        {
            hiprandStatus_t status = hiprandDestroyGenerator(m_generator);
            if(status != HIPRAND_STATUS_SUCCESS) throw hiprand_cpp::error(status);
        }

        void stream(hipStream_t value)
        {
            hiprandStatus_t status = hiprandSetStream(m_generator, value);
            if(status != HIPRAND_STATUS_SUCCESS) throw hiprand_cpp::error(status);
        }

        void seed(seed_type value)
        {
            hiprandStatus_t status = hiprandSetPseudoRandomGeneratorSeed(this->m_generator, value);
            if(status != HIPRAND_STATUS_SUCCESS) throw hiprand_cpp::error(status);
        }

        template<class Generator>
        void operator()(result_type * output, size_t size)
        {
            hiprandStatus_t status;
            status = hiprandGenerate(m_generator, output, size);
            if(status != HIPRAND_STATUS_SUCCESS) throw hiprand_cpp::error(status);
        }

        result_type min() const
        {
            return 0;
        }

        result_type max() const
        {
            return std::numeric_limits<unsigned int>::max();
        }

        static constexpr hiprandRngType type()
        {
            return HIPRAND_RNG_PSEUDO_MTGP32;
        }

    private:
        hiprandGenerator_t m_generator;

        template<class T>
        friend class ::hiprand_cpp::uniform_int_distribution;

        template<class T>
        friend class ::hiprand_cpp::uniform_real_distribution;

        template<class T>
        friend class ::hiprand_cpp::normal_distribution;

        template<class T>
        friend class ::hiprand_cpp::lognormal_distribution;

        template<class T>
        friend class ::hiprand_cpp::poisson_distribution;
    };

    template<unsigned long long DefaultSeed>
    constexpr typename mtgp32_engine<DefaultSeed>::seed_type mtgp32_engine<DefaultSeed>::default_seed;

    template<unsigned long long DefaultSeed = HIPRAND_MT19937_DEFAULT_SEED>
    class mt19937_engine
    {
    public:
        typedef unsigned int result_type;
        typedef unsigned long long seed_type;
        static constexpr seed_type default_seed = DefaultSeed;

        mt19937_engine(seed_type seed_value = DefaultSeed)
        {
            hiprandStatus_t status;
            status = hiprandCreateGenerator(&m_generator, this->type());
            if(status != HIPRAND_STATUS_SUCCESS)
                throw hiprand_cpp::error(status);
            try
            {
                this->seed(seed_value);
            }
            catch(...)
            {
                (void)hiprandDestroyGenerator(m_generator);
                throw;
            }
        }

        mt19937_engine(hiprandGenerator_t& generator) : m_generator(generator)
        {
            if(generator == NULL)
            {
                throw hiprand_cpp::error(HIPRAND_STATUS_NOT_INITIALIZED);
            }
            generator = NULL;
        }

        mt19937_engine(const mt19937_engine&) = delete;

        mt19937_engine(mt19937_engine&&) = delete;

        mt19937_engine& operator=(const mt19937_engine&) = delete;

        mt19937_engine& operator=(mt19937_engine&&) = delete;

        ~mt19937_engine() noexcept(false)
        {
            hiprandStatus_t status = hiprandDestroyGenerator(m_generator);
            if(status != HIPRAND_STATUS_SUCCESS)
                throw hiprand_cpp::error(status);
        }

        void stream(hipStream_t value)
        {
            hiprandStatus_t status = hiprandSetStream(m_generator, value);
            if(status != HIPRAND_STATUS_SUCCESS)
                throw hiprand_cpp::error(status);
        }

        void seed(seed_type value)
        {
            hiprandStatus_t status = hiprandSetPseudoRandomGeneratorSeed(this->m_generator, value);
            if(status != HIPRAND_STATUS_SUCCESS)
                throw hiprand_cpp::error(status);
        }

        template<class Generator>
        void operator()(result_type* output, size_t size)
        {
            hiprandStatus_t status;
            status = hiprandGenerate(m_generator, output, size);
            if(status != HIPRAND_STATUS_SUCCESS)
                throw hiprand_cpp::error(status);
        }

        result_type min() const
        {
            return 0;
        }

        result_type max() const
        {
            return std::numeric_limits<unsigned int>::max();
        }

        static constexpr hiprandRngType type()
        {
            return HIPRAND_RNG_PSEUDO_MT19937;
        }

    private:
        hiprandGenerator_t m_generator;

        template<class T>
        friend class ::hiprand_cpp::uniform_int_distribution;

        template<class T>
        friend class ::hiprand_cpp::uniform_real_distribution;

        template<class T>
        friend class ::hiprand_cpp::normal_distribution;

        template<class T>
        friend class ::hiprand_cpp::lognormal_distribution;

        template<class T>
        friend class ::hiprand_cpp::poisson_distribution;
    };

    template<unsigned long long DefaultSeed>
    constexpr typename mt19937_engine<DefaultSeed>::seed_type mt19937_engine<DefaultSeed>::default_seed;

    template<unsigned int DefaultNumDimensions = 1>
    class sobol32_engine
    {
    public:
        typedef unsigned int result_type;
        typedef unsigned long long offset_type;
        typedef unsigned int dimensions_num_type;
        static constexpr dimensions_num_type default_num_dimensions = DefaultNumDimensions;

        sobol32_engine(dimensions_num_type num_of_dimensions = DefaultNumDimensions,
                       offset_type offset_value = 0)
        {
            hiprandStatus_t status;
            status = hiprandCreateGenerator(&m_generator, this->type());
            if(status != HIPRAND_STATUS_SUCCESS) throw hiprand_cpp::error(status);
            try
            {
                if(offset_value > 0)
                {
                    this->offset(offset_value);
                }
                this->dimensions(num_of_dimensions);
            }
            catch(...)
            {
                (void)hiprandDestroyGenerator(m_generator);
                throw;
            }
        }

        sobol32_engine(hiprandGenerator_t& generator)
            : m_generator(generator)
        {
            if(generator == NULL)
            {
                throw hiprand_cpp::error(HIPRAND_STATUS_NOT_INITIALIZED);
            }
            generator = NULL;
        }

        ~sobol32_engine() noexcept(false)
        {
            hiprandStatus_t status = hiprandDestroyGenerator(m_generator);
            if(status != HIPRAND_STATUS_SUCCESS) throw hiprand_cpp::error(status);
        }

        void stream(hipStream_t value)
        {
            hiprandStatus_t status = hiprandSetStream(m_generator, value);
            if(status != HIPRAND_STATUS_SUCCESS) throw hiprand_cpp::error(status);
        }

        void offset(offset_type value)
        {
            hiprandStatus_t status = hiprandSetGeneratorOffset(this->m_generator, value);
            if(status != HIPRAND_STATUS_SUCCESS) throw hiprand_cpp::error(status);
        }

        void dimensions(dimensions_num_type value)
        {
            hiprandStatus_t status =
                hiprandSetQuasiRandomGeneratorDimensions(this->m_generator, value);
            if(status != HIPRAND_STATUS_SUCCESS) throw hiprand_cpp::error(status);
        }

        template<class Generator>
        void operator()(result_type * output, size_t size)
        {
            hiprandStatus_t status;
            status = hiprandGenerate(m_generator, output, size);
            if(status != HIPRAND_STATUS_SUCCESS) throw hiprand_cpp::error(status);
        }

        result_type min() const
        {
            return 0;
        }

        result_type max() const
        {
            return std::numeric_limits<unsigned int>::max();
        }

        static constexpr hiprandRngType type()
        {
            return HIPRAND_RNG_QUASI_SOBOL32;
        }

    private:
        hiprandGenerator_t m_generator;

        template<class T>
        friend class ::hiprand_cpp::uniform_int_distribution;

        template<class T>
        friend class ::hiprand_cpp::uniform_real_distribution;

        template<class T>
        friend class ::hiprand_cpp::normal_distribution;

        template<class T>
        friend class ::hiprand_cpp::lognormal_distribution;

        template<class T>
        friend class ::hiprand_cpp::poisson_distribution;
    };

    template<unsigned int DefaultNumDimensions>
    constexpr typename sobol32_engine<DefaultNumDimensions>::dimensions_num_type
    sobol32_engine<DefaultNumDimensions>::default_num_dimensions;

    template<unsigned int DefaultNumDimensions = 1>
    class scrambled_sobol32_engine
    {
    public:
        typedef unsigned int result_type;
        typedef unsigned long long offset_type;
        typedef unsigned int dimensions_num_type;
        static constexpr dimensions_num_type default_num_dimensions = DefaultNumDimensions;

        scrambled_sobol32_engine(dimensions_num_type num_of_dimensions = DefaultNumDimensions,
                                 offset_type         offset_value      = 0)
        {
            hiprandStatus_t status;
            status = hiprandCreateGenerator(&m_generator, this->type());
            if(status != HIPRAND_STATUS_SUCCESS)
                throw hiprand_cpp::error(status);
            try
            {
                if(offset_value > 0)
                {
                    this->offset(offset_value);
                }
                this->dimensions(num_of_dimensions);
            }
            catch(...)
            {
                (void)hiprandDestroyGenerator(m_generator);
                throw;
            }
        }

        scrambled_sobol32_engine(hiprandGenerator_t& generator) : m_generator(generator)
        {
            if(generator == NULL)
            {
                throw hiprand_cpp::error(HIPRAND_STATUS_NOT_INITIALIZED);
            }
            generator = NULL;
        }

        scrambled_sobol32_engine(const scrambled_sobol32_engine&) = delete;

        scrambled_sobol32_engine(scrambled_sobol32_engine&&) = delete;

        scrambled_sobol32_engine& operator=(const scrambled_sobol32_engine&) = delete;

        scrambled_sobol32_engine& operator=(scrambled_sobol32_engine&&) = delete;

        ~scrambled_sobol32_engine() noexcept(false)
        {
            hiprandStatus_t status = hiprandDestroyGenerator(m_generator);
            if(status != HIPRAND_STATUS_SUCCESS)
                throw hiprand_cpp::error(status);
        }

        void stream(hipStream_t value)
        {
            hiprandStatus_t status = hiprandSetStream(m_generator, value);
            if(status != HIPRAND_STATUS_SUCCESS)
                throw hiprand_cpp::error(status);
        }

        void offset(offset_type value)
        {
            hiprandStatus_t status = hiprandSetGeneratorOffset(this->m_generator, value);
            if(status != HIPRAND_STATUS_SUCCESS)
                throw hiprand_cpp::error(status);
        }

        void dimensions(dimensions_num_type value)
        {
            hiprandStatus_t status = hiprandSetQuasiRandomGeneratorDimensions(this->m_generator, value);
            if(status != HIPRAND_STATUS_SUCCESS)
                throw hiprand_cpp::error(status);
        }

        template<class Generator>
        void operator()(result_type* output, size_t size)
        {
            hiprandStatus_t status;
            status = hiprandGenerate(m_generator, output, size);
            if(status != HIPRAND_STATUS_SUCCESS)
                throw hiprand_cpp::error(status);
        }

        result_type min() const
        {
            return 0;
        }

        result_type max() const
        {
            return std::numeric_limits<unsigned int>::max();
        }

        static constexpr hiprandRngType type()
        {
            return HIPRAND_RNG_QUASI_SCRAMBLED_SOBOL32;
        }

    private:
        hiprandGenerator_t m_generator;

        template<class T>
        friend class ::hiprand_cpp::uniform_int_distribution;

        template<class T>
        friend class ::hiprand_cpp::uniform_real_distribution;

        template<class T>
        friend class ::hiprand_cpp::normal_distribution;

        template<class T>
        friend class ::hiprand_cpp::lognormal_distribution;

        template<class T>
        friend class ::hiprand_cpp::poisson_distribution;
    };

    template<unsigned int DefaultNumDimensions>
    constexpr typename scrambled_sobol32_engine<DefaultNumDimensions>::dimensions_num_type
        scrambled_sobol32_engine<DefaultNumDimensions>::default_num_dimensions;

    template<unsigned int DefaultNumDimensions = 1>
    class sobol64_engine
    {
    public:
        typedef unsigned long long int result_type;
        typedef unsigned long long offset_type;
        typedef unsigned int dimensions_num_type;
        static constexpr dimensions_num_type default_num_dimensions = DefaultNumDimensions;

        sobol64_engine(dimensions_num_type num_of_dimensions = DefaultNumDimensions,
                       offset_type         offset_value      = 0)
        {
            hiprandStatus_t status;
            status = hiprandCreateGenerator(&m_generator, this->type());
            if(status != HIPRAND_STATUS_SUCCESS)
                throw hiprand_cpp::error(status);
            try
            {
                if(offset_value > 0)
                {
                    this->offset(offset_value);
                }
                this->dimensions(num_of_dimensions);
            }
            catch(...)
            {
                (void)hiprandDestroyGenerator(m_generator);
                throw;
            }
        }

        sobol64_engine(hiprandGenerator_t& generator) : m_generator(generator)
        {
            if(generator == NULL)
            {
                throw hiprand_cpp::error(HIPRAND_STATUS_NOT_INITIALIZED);
            }
            generator = NULL;
        }

        sobol64_engine(const sobol64_engine&) = delete;

        sobol64_engine(sobol64_engine&&) = delete;

        sobol64_engine& operator=(const sobol64_engine&) = delete;

        sobol64_engine& operator=(sobol64_engine&&) = delete;

        ~sobol64_engine() noexcept(false)
        {
            hiprandStatus_t status = hiprandDestroyGenerator(m_generator);
            if(status != HIPRAND_STATUS_SUCCESS)
                throw hiprand_cpp::error(status);
        }

        void stream(hipStream_t value)
        {
            hiprandStatus_t status = hiprandSetStream(m_generator, value);
            if(status != HIPRAND_STATUS_SUCCESS)
                throw hiprand_cpp::error(status);
        }

        void offset(offset_type value)
        {
            hiprandStatus_t status = hiprandSetGeneratorOffset(this->m_generator, value);
            if(status != HIPRAND_STATUS_SUCCESS)
                throw hiprand_cpp::error(status);
        }

        void dimensions(dimensions_num_type value)
        {
            hiprandStatus_t status = hiprandSetQuasiRandomGeneratorDimensions(this->m_generator, value);
            if(status != HIPRAND_STATUS_SUCCESS)
                throw hiprand_cpp::error(status);
        }

        template<class Generator>
        void operator()(result_type* output, size_t size)
        {
            hiprandStatus_t status;
            status = hiprandGenerateLongLong(m_generator, output, size);
            if(status != HIPRAND_STATUS_SUCCESS)
                throw hiprand_cpp::error(status);
        }

        result_type min() const
        {
            return 0;
        }

        result_type max() const
        {
            return std::numeric_limits<unsigned int>::max();
        }

        static constexpr hiprandRngType type()
        {
            return HIPRAND_RNG_QUASI_SOBOL64;
        }

    private:
        hiprandGenerator_t m_generator;

        template<class T>
        friend class ::hiprand_cpp::uniform_int_distribution;

        template<class T>
        friend class ::hiprand_cpp::uniform_real_distribution;

        template<class T>
        friend class ::hiprand_cpp::normal_distribution;

        template<class T>
        friend class ::hiprand_cpp::lognormal_distribution;

        template<class T>
        friend class ::hiprand_cpp::poisson_distribution;
    };

    template<unsigned int DefaultNumDimensions>
    constexpr typename sobol64_engine<DefaultNumDimensions>::dimensions_num_type
        sobol64_engine<DefaultNumDimensions>::default_num_dimensions;

    template<unsigned int DefaultNumDimensions = 1>
    class scrambled_sobol64_engine
    {
    public:
        typedef unsigned long long int result_type;
        typedef unsigned long long offset_type;
        typedef unsigned int dimensions_num_type;
        static constexpr dimensions_num_type default_num_dimensions = DefaultNumDimensions;

        scrambled_sobol64_engine(dimensions_num_type num_of_dimensions = DefaultNumDimensions,
                                 offset_type         offset_value      = 0)
        {
            hiprandStatus_t status;
            status = hiprandCreateGenerator(&m_generator, this->type());
            if(status != HIPRAND_STATUS_SUCCESS)
                throw hiprand_cpp::error(status);
            try
            {
                if(offset_value > 0)
                {
                    this->offset(offset_value);
                }
                this->dimensions(num_of_dimensions);
            }
            catch(...)
            {
                (void)hiprandDestroyGenerator(m_generator);
                throw;
            }
        }

        scrambled_sobol64_engine(hiprandGenerator_t& generator) : m_generator(generator)
        {
            if(generator == NULL)
            {
                throw hiprand_cpp::error(HIPRAND_STATUS_NOT_INITIALIZED);
            }
            generator = NULL;
        }

        scrambled_sobol64_engine(const scrambled_sobol64_engine&) = delete;

        scrambled_sobol64_engine(scrambled_sobol64_engine&&) = delete;

        scrambled_sobol64_engine& operator=(const scrambled_sobol64_engine&) = delete;

        scrambled_sobol64_engine& operator=(scrambled_sobol64_engine&&) = delete;

        ~scrambled_sobol64_engine() noexcept(false)
        {
            hiprandStatus_t status = hiprandDestroyGenerator(m_generator);
            if(status != HIPRAND_STATUS_SUCCESS)
                throw hiprand_cpp::error(status);
        }

        void stream(hipStream_t value)
        {
            hiprandStatus_t status = hiprandSetStream(m_generator, value);
            if(status != HIPRAND_STATUS_SUCCESS)
                throw hiprand_cpp::error(status);
        }

        void offset(offset_type value)
        {
            hiprandStatus_t status = hiprandSetGeneratorOffset(this->m_generator, value);
            if(status != HIPRAND_STATUS_SUCCESS)
                throw hiprand_cpp::error(status);
        }

        void dimensions(dimensions_num_type value)
        {
            hiprandStatus_t status = hiprandSetQuasiRandomGeneratorDimensions(this->m_generator, value);
            if(status != HIPRAND_STATUS_SUCCESS)
                throw hiprand_cpp::error(status);
        }

        template<class Generator>
        void operator()(result_type* output, size_t size)
        {
            hiprandStatus_t status;
            status = hiprandGenerateLongLong(m_generator, output, size);
            if(status != HIPRAND_STATUS_SUCCESS)
                throw hiprand_cpp::error(status);
        }

        result_type min() const
        {
            return 0;
        }

        result_type max() const
        {
            return std::numeric_limits<unsigned int>::max();
        }

        static constexpr hiprandRngType type()
        {
            return HIPRAND_RNG_QUASI_SCRAMBLED_SOBOL64;
        }

    private:
        hiprandGenerator_t m_generator;

        template<class T>
        friend class ::hiprand_cpp::uniform_int_distribution;

        template<class T>
        friend class ::hiprand_cpp::uniform_real_distribution;

        template<class T>
        friend class ::hiprand_cpp::normal_distribution;

        template<class T>
        friend class ::hiprand_cpp::lognormal_distribution;

        template<class T>
        friend class ::hiprand_cpp::poisson_distribution;
    };

    template<unsigned int DefaultNumDimensions>
    constexpr typename scrambled_sobol64_engine<DefaultNumDimensions>::dimensions_num_type
        scrambled_sobol64_engine<DefaultNumDimensions>::default_num_dimensions;

    typedef philox4x32_10_engine<> philox4x32_10;
    typedef xorwow_engine<> xorwow;
    typedef mrg32k3a_engine<> mrg32k3a;
    typedef mtgp32_engine<> mtgp32;
    typedef mt19937_engine<> mt19937;
    typedef sobol32_engine<> sobol32;
    typedef scrambled_sobol32_engine<> scrambled_sobol32;
    typedef sobol64_engine<> sobol64;
    typedef scrambled_sobol64_engine<> scrambled_sobol64;

    typedef xorwow default_random_engine;

    typedef std::random_device random_device;

    inline int version()
    {
        int x;
        hiprandStatus_t status = hiprandGetVersion(&x);
        if(status != HIPRAND_STATUS_SUCCESS)
        {
            throw hiprand_cpp::error(status);
        }
        return x;
    }

    } // end namespace hiprand_cpp

    #endif // #if __cplusplus >= 201103L
    #endif // HIPRAND_HPP_