#include <iostream>
#include <random>
#include <algorithm>
#include <ctime>
#include <map>

struct Range
{
    int min;
    int max;
};

template <typename... T>
struct and_
    : std::true_type
{};

template <typename First, typename... Other>
struct and_<First, Other...>
    : std::integral_constant<bool, First::value && and_<Other...>::value>
{};

template < typename ...RangeT
         , typename = typename std::enable_if<
                    (and_<
                        std::is_same<RangeT, Range>...
                    >::value) && (sizeof...(RangeT) > 0)
                >::type
>
int random(std::mt19937 & mt, RangeT const & ...args)
{
    int sum = 0;
    struct MRange
    {
        const int min, max;
        const int distance;

        MRange(int min, int max, int & sum)
            : min(min), max(max), distance(std::abs(min - max))
        {
            sum += distance;
        }
    } const ranges[] =
    {
        MRange(args.min, args.max, sum)...
    };
    int index = std::uniform_int_distribution<int>(0, sum)(mt);

    int ret = ranges[0].min + index;
    if(ret > ranges[0].max)
    {
        index -= ranges[0].distance;
        for(size_t i = 1; i < sizeof...(args); ++i)
        {
            ret = ranges[i].min + index;
            if(ret > ranges[i].max)
            {
                index -= ranges[i].distance;
            }
            else
            {
                break;
            }
        }
    }
    return ret;
}
int main()
{
    std::mt19937 mt(static_cast<unsigned>(std::time(nullptr)));

    std::map<int, int> m;
    for(size_t n = 0; n < 10000; ++n)
    {
        ++m[random(mt, Range{ 0, 2 }, Range{ 50, 100 })];
    }
    for(auto p : m)
    {
        std::cout << p.first << " generated " << p.second << " times\n";
    }
}