random_shuffle.cpp 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. // Copyright Neil Groves 2009. Use, modification and
  2. // distribution is subject to the Boost Software License, Version
  3. // 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  4. // http://www.boost.org/LICENSE_1_0.txt)
  5. //
  6. //
  7. // For more information, see http://www.boost.org/libs/range/
  8. //
  9. #include <boost/range/algorithm/random_shuffle.hpp>
  10. #include <boost/test/test_tools.hpp>
  11. #include <boost/test/unit_test.hpp>
  12. #include <boost/assign.hpp>
  13. #include <boost/bind.hpp>
  14. #include "../test_function/counted_function.hpp"
  15. #include <algorithm>
  16. #include <functional>
  17. #include <list>
  18. #include <numeric>
  19. #include <deque>
  20. #include <vector>
  21. namespace boost
  22. {
  23. namespace
  24. {
  25. template<class Int>
  26. class counted_generator
  27. : private range_test_function::counted_function
  28. {
  29. public:
  30. typedef Int result_type;
  31. typedef Int argument_type;
  32. using range_test_function::counted_function::invocation_count;
  33. result_type operator()(argument_type modulo_value)
  34. {
  35. invoked();
  36. return static_cast<result_type>(std::rand() % modulo_value);
  37. }
  38. };
  39. template<class Container>
  40. bool test_shuffle_result(
  41. const Container& old_cont,
  42. const Container& new_cont
  43. )
  44. {
  45. typedef BOOST_DEDUCED_TYPENAME range_iterator<const Container>::type iterator_t;
  46. // The size must remain equal
  47. BOOST_CHECK_EQUAL( old_cont.size(), new_cont.size() );
  48. if (old_cont.size() != new_cont.size())
  49. return false;
  50. if (new_cont.size() < 2)
  51. {
  52. BOOST_CHECK_EQUAL_COLLECTIONS(
  53. old_cont.begin(), old_cont.end(),
  54. new_cont.begin(), new_cont.end()
  55. );
  56. return std::equal(old_cont.begin(), old_cont.end(),
  57. new_cont.begin());
  58. }
  59. // Elements must only be moved around. This is tested by
  60. // ensuring the count of each element remains the
  61. // same.
  62. bool failed = false;
  63. iterator_t last = old_cont.end();
  64. for (iterator_t it = old_cont.begin(); !failed && (it != last); ++it)
  65. {
  66. const std::size_t old_count
  67. = std::count(old_cont.begin(), old_cont.end(), *it);
  68. const std::size_t new_count
  69. = std::count(new_cont.begin(), new_cont.end(), *it);
  70. BOOST_CHECK_EQUAL( old_count, new_count );
  71. failed = (old_count != new_count);
  72. }
  73. return !failed;
  74. }
  75. template<class Container>
  76. void test_random_shuffle_nogen_impl(Container& cont)
  77. {
  78. typedef BOOST_DEDUCED_TYPENAME range_iterator<Container>::type
  79. iterator_t BOOST_RANGE_UNUSED;
  80. const int MAX_RETRIES = 10000;
  81. bool shuffled = false;
  82. for (int attempt = 0; !shuffled && (attempt < MAX_RETRIES); ++attempt)
  83. {
  84. Container test(cont);
  85. boost::random_shuffle(test);
  86. bool ok = test_shuffle_result(cont, test);
  87. if (!ok)
  88. break;
  89. // Since the counts are equal, then if they are
  90. // not equal the contents must have been shuffled
  91. if (cont.size() == test.size()
  92. && !std::equal(cont.begin(), cont.end(), test.begin()))
  93. {
  94. shuffled = true;
  95. }
  96. // Verify that the shuffle can be performed on a
  97. // temporary range
  98. Container test2(cont);
  99. boost::random_shuffle(boost::make_iterator_range(test2));
  100. ok = test_shuffle_result(cont, test2);
  101. if (!ok)
  102. break;
  103. }
  104. }
  105. template<class RandomGenerator, class Container>
  106. void test_random_shuffle_gen_impl(Container& cont)
  107. {
  108. RandomGenerator gen;
  109. Container old_cont(cont);
  110. boost::random_shuffle(cont, gen);
  111. test_shuffle_result(cont, old_cont);
  112. if (cont.size() > 2)
  113. {
  114. BOOST_CHECK( gen.invocation_count() > 0 );
  115. }
  116. // Test that random shuffle works when
  117. // passed a temporary range
  118. RandomGenerator gen2;
  119. Container cont2(old_cont);
  120. boost::random_shuffle(boost::make_iterator_range(cont2), gen2);
  121. test_shuffle_result(cont2, old_cont);
  122. if (cont2.size() > 2)
  123. {
  124. BOOST_CHECK( gen2.invocation_count() > 0 );
  125. }
  126. }
  127. template<class Container>
  128. void test_random_shuffle_impl(Container& cont)
  129. {
  130. Container old_cont(cont);
  131. boost::random_shuffle(cont);
  132. test_shuffle_result(cont, old_cont);
  133. }
  134. template<class Container>
  135. void test_random_shuffle_impl()
  136. {
  137. using namespace boost::assign;
  138. typedef counted_generator<
  139. BOOST_DEDUCED_TYPENAME range_difference<Container>::type > generator_t;
  140. Container cont;
  141. test_random_shuffle_nogen_impl(cont);
  142. test_random_shuffle_gen_impl<generator_t>(cont);
  143. cont.clear();
  144. cont += 1;
  145. test_random_shuffle_nogen_impl(cont);
  146. test_random_shuffle_gen_impl<generator_t>(cont);
  147. cont.clear();
  148. cont += 1,2,3,4,5,6,7,8,9;
  149. test_random_shuffle_nogen_impl(cont);
  150. test_random_shuffle_gen_impl<generator_t>(cont);
  151. }
  152. void test_random_shuffle()
  153. {
  154. test_random_shuffle_impl< std::vector<int> >();
  155. test_random_shuffle_impl< std::deque<int> >();
  156. }
  157. }
  158. }
  159. boost::unit_test::test_suite*
  160. init_unit_test_suite(int argc, char* argv[])
  161. {
  162. boost::unit_test::test_suite* test
  163. = BOOST_TEST_SUITE( "RangeTestSuite.algorithm.random_shuffle" );
  164. test->add( BOOST_TEST_CASE( &boost::test_random_shuffle ) );
  165. return test;
  166. }