#ifndef BOOST_COMPUTE_RANDOM_DISCRETE_DISTRIBUTION_HPP
#define BOOST_COMPUTE_RANDOM_DISCRETE_DISTRIBUTION_HPP
#include <numeric>
#include <boost/config.hpp>
#include <boost/type_traits.hpp>
#include <boost/static_assert.hpp>
#include <boost/compute/command_queue.hpp>
#include <boost/compute/function.hpp>
#include <boost/compute/algorithm/accumulate.hpp>
#include <boost/compute/algorithm/copy.hpp>
#include <boost/compute/algorithm/transform.hpp>
#include <boost/compute/detail/literal.hpp>
#include <boost/compute/types/fundamental.hpp>
namespace boost {
namespace compute {
template<class IntType = uint_>
class discrete_distribution
{
public:
typedef IntType result_type;
discrete_distribution()
: m_probabilities(1, double(1)),
m_scanned_probabilities(1, double(1))
{
}
template<class InputIterator>
discrete_distribution(InputIterator first, InputIterator last)
: m_probabilities(first, last),
m_scanned_probabilities(std::distance(first, last))
{
if(first != last) {
std::partial_sum(first, last, m_scanned_probabilities.begin());
std::vector<double>::iterator i = m_probabilities.begin();
std::vector<double>::iterator j = m_scanned_probabilities.begin();
for(; i != m_probabilities.end(); ++i, ++j)
{
*i = *i / m_scanned_probabilities.back();
*j = *j / m_scanned_probabilities.back();
}
}
else {
m_probabilities.push_back(double(1));
m_scanned_probabilities.push_back(double(1));
}
}
~discrete_distribution()
{
}
::std::vector<double> probabilities() const
{
return m_probabilities;
}
result_type min BOOST_PREVENT_MACRO_SUBSTITUTION () const
{
return result_type(0);
}
result_type max BOOST_PREVENT_MACRO_SUBSTITUTION () const
{
size_t type_max = static_cast<size_t>(
(std::numeric_limits<result_type>::max)()
);
if(m_probabilities.size() - 1 > type_max) {
return (std::numeric_limits<result_type>::max)();
}
return static_cast<result_type>(m_probabilities.size() - 1);
}
template<class OutputIterator, class Generator>
void generate(OutputIterator first,
OutputIterator last,
Generator &generator,
command_queue &queue)
{
std::string source = "inline IntType scale_random(uint x)\n";
source = source +
"{\n" +
"float rno = convert_float(x) / UINT_MAX;\n";
for(size_t i = 0; i < m_scanned_probabilities.size() - 1; i++)
{
source = source +
"if(rno <= " + detail::make_literal<float>(m_scanned_probabilities[i]) + ")\n" +
" return " + detail::make_literal(i) + ";\n";
}
source = source +
"return " + detail::make_literal(m_scanned_probabilities.size() - 1) + ";\n" +
"}\n";
BOOST_COMPUTE_FUNCTION(IntType, scale_random, (const uint_ x), {});
scale_random.set_source(source);
scale_random.define("IntType", type_name<IntType>());
generator.generate(first, last, scale_random, queue);
}
private:
::std::vector<double> m_probabilities;
::std::vector<double> m_scanned_probabilities;
BOOST_STATIC_ASSERT_MSG(
boost::is_integral<IntType>::value,
"Template argument must be integral"
);
};
} }
#endif