Ticket #1546: bug_1546.diff

File bug_1546.diff, 7.9 KB (added by Jos Hickson <jos.hickson@…>, 15 years ago)

Patch (to version 43682 of trunk) providing fix and test

  • boost/random/uniform_int.hpp

     
    5959  template<class Engine>
    6060  result_type operator()(Engine& eng)
    6161  {
     62      return generate(eng, _min, _max, _range);
     63  }
     64
     65  template<class Engine>
     66  result_type operator()(Engine& eng, result_type n)
     67  {
     68      assert(n > 0);
     69
     70      if (n == 1)
     71      {
     72        return 0;
     73      }
     74
     75      return generate(eng, 0, n - 1, n - 1);
     76  }
     77
     78#if !defined(BOOST_NO_OPERATORS_IN_NAMESPACE) && !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS)
     79  template<class CharT, class Traits>
     80  friend std::basic_ostream<CharT,Traits>&
     81  operator<<(std::basic_ostream<CharT,Traits>& os, const uniform_int& ud)
     82  {
     83    os << ud._min << " " << ud._max;
     84    return os;
     85  }
     86
     87  template<class CharT, class Traits>
     88  friend std::basic_istream<CharT,Traits>&
     89  operator>>(std::basic_istream<CharT,Traits>& is, uniform_int& ud)
     90  {
     91# if BOOST_WORKAROUND(_MSC_FULL_VER, BOOST_TESTED_AT(13102292)) && BOOST_MSVC == 1400
     92      return detail::extract_uniform_int(is, ud, ud.impl);
     93# else
     94   is >> std::ws >> ud._min >> std::ws >> ud._max;
     95    ud.init();
     96    return is;
     97# endif
     98  }
     99#endif
     100
     101private:
     102  template<class Engine>
     103  static result_type generate(Engine& eng, result_type min_value, result_type max_value, range_type range)
     104  {
    62105    typedef typename Engine::result_type base_result;
    63106    // ranges are always unsigned
    64107    typedef typename random::detail::make_unsigned<base_result>::type base_unsigned;
     
    66109    const base_unsigned brange =
    67110      random::detail::subtract<base_result>()((eng.max)(), (eng.min)());
    68111
    69     if(_range == 0) {
    70       return _min;   
    71     } else if(brange == _range) {
     112    if(range == 0) {
     113      return min_value;   
     114    } else if(brange == range) {
    72115      // this will probably never happen in real life
    73116      // basically nothing to do; just take care we don't overflow / underflow
    74117      base_unsigned v = random::detail::subtract<base_result>()(eng(), bmin);
    75       return random::detail::add<base_unsigned, result_type>()(v, _min);
    76     } else if(brange < _range) {
     118      return random::detail::add<base_unsigned, result_type>()(v, min_value);
     119    } else if(brange < range) {
    77120      // use rejection method to handle things like 0..3 --> 0..4
    78121      for(;;) {
    79122        // concatenate several invocations of the base RNG
    80123        // take extra care to avoid overflows
    81124        range_type limit;
    82         if(_range == (std::numeric_limits<range_type>::max)()) {
    83           limit = _range/(range_type(brange)+1);
    84           if(_range % range_type(brange)+1 == range_type(brange))
     125        if(range == (std::numeric_limits<range_type>::max)()) {
     126          limit = range/(range_type(brange)+1);
     127          if(range % range_type(brange)+1 == range_type(brange))
    85128            ++limit;
    86129        } else {
    87           limit = (_range+1)/(range_type(brange)+1);
     130          limit = (range+1)/(range_type(brange)+1);
    88131        }
    89132        // We consider "result" as expressed to base (brange+1):
    90133        // For every power of (brange+1), we determine a random factor
     
    95138          mult *= range_type(brange)+range_type(1);
    96139        }
    97140        if(mult == limit)
    98           // _range+1 is an integer power of brange+1: no rejections required
     141          // range+1 is an integer power of brange+1: no rejections required
    99142          return result;
    100         // _range/mult < brange+1  -> no endless loop
    101         result += uniform_int<range_type>(0, _range/mult)(eng) * mult;
    102         if(result <= _range)
    103           return random::detail::add<range_type, result_type>()(result, _min);
     143        // range/mult < brange+1  -> no endless loop
     144        result += uniform_int<range_type>(0, range/mult)(eng) * mult;
     145        if(result <= range)
     146          return random::detail::add<range_type, result_type>()(result, min_value);
    104147      }
    105148    } else {                   // brange > range
    106       if(brange / _range > 4 /* quantization_cutoff */ ) {
     149      if(brange / range > 4 /* quantization_cutoff */ ) {
    107150        // the new range is vastly smaller than the source range,
    108151        // so quantization effects are not relevant
    109         return boost::uniform_smallint<result_type>(_min, _max)(eng);
     152        return boost::uniform_smallint<result_type>(min_value, max_value)(eng);
    110153      } else {
    111154        // use rejection method to handle cases like 0..5 -> 0..4
    112155        for(;;) {
     
    114157            random::detail::subtract<base_result>()(eng(), bmin);
    115158          // result and range are non-negative, and result is possibly larger
    116159          // than range, so the cast is safe
    117           if(result <= static_cast<base_unsigned>(_range))
    118             return random::detail::add<base_unsigned, result_type>()(result, _min);
     160          if(result <= static_cast<base_unsigned>(range))
     161            return random::detail::add<base_unsigned, result_type>()(result, min_value);
    119162        }
    120163      }
    121164    }
    122165  }
    123166
    124 #if !defined(BOOST_NO_OPERATORS_IN_NAMESPACE) && !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS)
    125   template<class CharT, class Traits>
    126   friend std::basic_ostream<CharT,Traits>&
    127   operator<<(std::basic_ostream<CharT,Traits>& os, const uniform_int& ud)
    128   {
    129     os << ud._min << " " << ud._max;
    130     return os;
    131   }
    132 
    133   template<class CharT, class Traits>
    134   friend std::basic_istream<CharT,Traits>&
    135   operator>>(std::basic_istream<CharT,Traits>& is, uniform_int& ud)
    136   {
    137 # if BOOST_WORKAROUND(_MSC_FULL_VER, BOOST_TESTED_AT(13102292)) && BOOST_MSVC == 1400
    138       return detail::extract_uniform_int(is, ud, ud.impl);
    139 # else
    140    is >> std::ws >> ud._min >> std::ws >> ud._max;
    141     ud.init();
    142     return is;
    143 # endif
    144   }
    145 #endif
    146 
    147 private:
    148167  void init()
    149168  {
    150169    _range = random::detail::subtract<result_type>()(_max, _min);
  • libs/random/random_test.cpp

     
    500500{ }
    501501#endif
    502502
     503template <typename EngineT>
     504struct rand_for_random_shuffle
     505{
     506  explicit rand_for_random_shuffle(EngineT &engine)
     507    : m_engine(engine)
     508  { }
     509
     510  template <typename IntT>
     511  IntT operator()(IntT upperBound)
     512  {
     513    assert(upperBound > 0);
     514
     515    if (upperBound == 1)
     516    {
     517      return 0;
     518    }
     519
     520    typedef boost::uniform_int<IntT> distribution_type;
     521    typedef boost::variate_generator<EngineT &, distribution_type> generator_type;
     522
     523    return generator_type(m_engine, distribution_type(0, upperBound - 1))();
     524  }
     525
     526  EngineT &m_engine;
     527       
     528};
     529
     530// Test that uniform_int<> can be used with std::random_shuffle
     531// Author: Jos Hickson
     532void test_random_shuffle()
     533{
     534    typedef boost::uniform_int<> distribution_type;
     535    typedef boost::variate_generator<boost::mt19937 &, distribution_type> generator_type;
     536
     537    boost::mt19937 engine1(1234);
     538    boost::mt19937 engine2(1234);
     539
     540    rand_for_random_shuffle<boost::mt19937> referenceRand(engine1);
     541
     542    distribution_type dist(0,10);
     543    generator_type testRand(engine2, dist);
     544
     545    std::vector<int> referenceVec;
     546
     547    for (int i = 0; i < 200; ++i)
     548    {
     549      referenceVec.push_back(i);
     550    }
     551
     552    std::vector<int> testVec(referenceVec);
     553
     554    std::random_shuffle(referenceVec.begin(), referenceVec.end(), referenceRand);
     555    std::random_shuffle(testVec.begin(), testVec.end(), testRand);
     556
     557    typedef std::vector<int>::iterator iter_type;
     558    iter_type theEnd(referenceVec.end());
     559
     560    for (iter_type referenceIter(referenceVec.begin()), testIter(testVec.begin());
     561         referenceIter != theEnd;
     562         ++referenceIter, ++testIter)
     563    {
     564      BOOST_CHECK_EQUAL(*referenceIter, *testIter);
     565    }
     566}
     567
     568
    503569int test_main(int, char*[])
    504570{
    505571
     
    528594            << std::endl;
    529595
    530596  test_overflow_range();
     597  test_random_shuffle();
    531598
    532599  return 0;
    533600#else