sort_detail_test.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. // Boost Sort library tests for integer_sort and float_sort details.
  2. // Copyright Steven Ross 2014. Use, modification and
  3. // distribution is subject to the Boost Software License, Version
  4. // 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  5. // http://www.boost.org/LICENSE_1_0.txt)
  6. // See http://www.boost.org/libs/sort for library home page.
  7. #include <boost/cstdint.hpp>
  8. #include <boost/sort/spreadsort/detail/spreadsort_common.hpp>
  9. #include <boost/sort/spreadsort/detail/integer_sort.hpp>
  10. #include <boost/sort/spreadsort/detail/float_sort.hpp>
  11. #include <boost/sort/spreadsort/detail/string_sort.hpp>
  12. #include <boost/sort/spreadsort/float_sort.hpp>
  13. // Include unit test framework
  14. #include <boost/test/included/test_exec_monitor.hpp>
  15. #include <boost/test/test_tools.hpp>
  16. #include <vector>
  17. #include <iostream>
  18. using namespace std;
  19. using namespace boost::sort::spreadsort;
  20. using namespace boost::sort::spreadsort::detail;
  21. namespace {
  22. struct int_right_shift {
  23. int operator()(const int x, const unsigned offset) const {
  24. return x >> offset;
  25. }
  26. };
  27. struct float_right_shift {
  28. int operator()(const float x, const unsigned offset) const {
  29. return float_mem_cast<float, int>(x) >> offset;
  30. }
  31. };
  32. const int max_int_bits = sizeof(boost::uintmax_t) * 8;
  33. const int max_size_bits = sizeof(size_t) * 8;
  34. const boost::uintmax_t one = 1;
  35. // spreadsort won't recurse for inputs smaller than min_count.
  36. const int int_min_log_count =
  37. (std::min)((int)int_log_finishing_count,
  38. (int)int_log_mean_bin_size + int_log_min_split_count);
  39. const int float_min_log_count =
  40. (std::min)((int)float_log_finishing_count,
  41. (int)float_log_mean_bin_size + float_log_min_split_count);
  42. const unsigned absolute_min_count = (std::min)(1 << int_min_log_count,
  43. 1 << float_min_log_count);
  44. // Verify that roughlog2 is floor(log base 2) + 1.
  45. void roughlog2_test()
  46. {
  47. for (boost::uintmax_t i = 0; i < max_int_bits; ++i) {
  48. BOOST_CHECK(detail::rough_log_2_size(one << i) == i + 1);
  49. BOOST_CHECK(detail::rough_log_2_size((one << i) - 1) == i);
  50. }
  51. }
  52. // Test the worst-case performance handling, and assure that is using the
  53. // correct formula for the worst-case number of radix iterations.
  54. template<unsigned log_mean_bin_size, unsigned log_min_split_count,
  55. unsigned log_finishing_count>
  56. void get_min_count_test()
  57. {
  58. const unsigned min_log_size = log_mean_bin_size + log_min_split_count;
  59. size_t prev_min_count = absolute_min_count;
  60. for (int log_range = 0; log_range <= max_int_bits; ++log_range) {
  61. size_t min_count = get_min_count<log_mean_bin_size, log_min_split_count,
  62. log_finishing_count>(log_range);
  63. BOOST_CHECK(min_count >= prev_min_count);
  64. prev_min_count = min_count;
  65. // When the range is really small, the radix sort will complete in one
  66. // iteration and worst-case handling doesn't apply. The code below
  67. // guarantees the worst-case number of radix sorting iteration.
  68. if (log_range > min_log_size) {
  69. BOOST_CHECK(min_count >= (1 << min_log_size));
  70. int iterations = rough_log_2_size(min_count) - min_log_size;
  71. BOOST_CHECK(iterations >= 1);
  72. int base_iterations = max_splits - log_min_split_count;
  73. int covered_log_range = 0;
  74. if (iterations > base_iterations) {
  75. covered_log_range += max_splits * (iterations - base_iterations);
  76. } else {
  77. base_iterations = iterations;
  78. }
  79. // sum of n to n + x = ((x + 1) * (n + (n + x)))/2 + log_mean_bin_size
  80. covered_log_range +=
  81. (base_iterations * (log_min_split_count * 2 + base_iterations - 1))/2 +
  82. log_mean_bin_size;
  83. BOOST_CHECK(covered_log_range >= log_range);
  84. BOOST_CHECK(covered_log_range - max_splits < log_range);
  85. }
  86. }
  87. }
  88. // Test the decision of how many pieces to split up the radix sort into
  89. // (roughly 2^(log_range - log_divisor)) to make sure the results are logical.
  90. void get_log_divisor_test()
  91. {
  92. for (int log_range = 0; log_range <= max_int_bits; ++log_range) {
  93. int prev_log_divisor = max_int_bits +
  94. (std::max)((int)int_log_mean_bin_size, (int)float_log_mean_bin_size);
  95. for (int log_count = 0; log_count < max_size_bits; ++log_count) {
  96. size_t count = (one << log_count) - 1;
  97. BOOST_CHECK(rough_log_2_size(count) == (unsigned)log_count);
  98. int log_divisor =
  99. get_log_divisor<int_log_mean_bin_size>(count, log_range);
  100. // Only process counts >= int_log_finishing_count in this function.
  101. if (count >= absolute_min_count)
  102. BOOST_CHECK(log_divisor <= log_range);
  103. // More pieces should be used the larger count is.
  104. BOOST_CHECK(log_divisor <= prev_log_divisor);
  105. prev_log_divisor = log_divisor;
  106. BOOST_CHECK(log_divisor >= 0);
  107. if (log_range > log_count) {
  108. BOOST_CHECK(log_range - log_divisor <= max_splits);
  109. } else if (log_range <= max_finishing_splits) {
  110. BOOST_CHECK(log_divisor == 0);
  111. }
  112. }
  113. }
  114. }
  115. // Verify that is_sorted_or_find_extremes returns true if the data is sorted,
  116. // and otherwise returns the actual min and max.
  117. void is_sorted_or_find_extremes_test()
  118. {
  119. vector<int> input;
  120. input.push_back(3);
  121. input.push_back(5);
  122. input.push_back(1);
  123. // Test a sorted input.
  124. vector<int> sorted_input(input);
  125. std::sort(sorted_input.begin(), sorted_input.end());
  126. vector<int>::iterator max, min;
  127. BOOST_CHECK(detail::is_sorted_or_find_extremes(sorted_input.begin(),
  128. sorted_input.end(), max, min));
  129. // Test an unsorted input.
  130. BOOST_CHECK(!detail::is_sorted_or_find_extremes(input.begin(), input.end(),
  131. max, min));
  132. BOOST_CHECK(*min == 1);
  133. BOOST_CHECK(*max == 5);
  134. // Test the comparison function version.
  135. BOOST_CHECK(detail::is_sorted_or_find_extremes(sorted_input.begin(),
  136. sorted_input.end(), max, min,
  137. std::less<int>()));
  138. BOOST_CHECK(!detail::is_sorted_or_find_extremes(sorted_input.begin(),
  139. sorted_input.end(),
  140. max, min,
  141. std::greater<int>()));
  142. BOOST_CHECK(*min == 5);
  143. BOOST_CHECK(*max == 1);
  144. // Test with floats
  145. vector<float> float_input;
  146. float_input.push_back(.3f);
  147. float_input.push_back(4.0f);
  148. float_input.push_back(.1f);
  149. vector<float> sorted_float_input(float_input);
  150. std::sort(sorted_float_input.begin(), sorted_float_input.end());
  151. // Test cast_float_iter
  152. int cast_min = detail::cast_float_iter<int, vector<float>::iterator>(
  153. sorted_float_input.begin());
  154. int cast_max = detail::cast_float_iter<int, vector<float>::iterator>(
  155. sorted_float_input.end() - 1);
  156. BOOST_CHECK(cast_min == float_right_shift()(.1f, 0));
  157. BOOST_CHECK(cast_max == float_right_shift()(4.0f, 0));
  158. // Test a sorted input
  159. int div_max, div_min;
  160. BOOST_CHECK(detail::is_sorted_or_find_extremes(sorted_float_input.begin(),
  161. sorted_float_input.end(),
  162. div_max, div_min));
  163. // Test an unsorted input.
  164. BOOST_CHECK(!detail::is_sorted_or_find_extremes(float_input.begin(),
  165. float_input.end(),
  166. div_max, div_min));
  167. BOOST_CHECK(div_min == cast_min);
  168. BOOST_CHECK(div_max == cast_max);
  169. // Test with a right_shift functor.
  170. BOOST_CHECK(detail::is_sorted_or_find_extremes(sorted_float_input.begin(),
  171. sorted_float_input.end(),
  172. div_max, div_min,
  173. float_right_shift()));
  174. // Test an unsorted input.
  175. BOOST_CHECK(!detail::is_sorted_or_find_extremes(float_input.begin(),
  176. float_input.end(), div_max,
  177. div_min,
  178. float_right_shift()));
  179. BOOST_CHECK(div_min == float_right_shift()(.1f, 0));
  180. BOOST_CHECK(div_max == float_right_shift()(4.0f, 0));
  181. }
  182. // Make sure bins are created correctly.
  183. void size_bins_test() {
  184. size_t bin_sizes[1 << detail::max_finishing_splits];
  185. bin_sizes[0] = 1;
  186. bin_sizes[2] = 7;
  187. const int old_bin_value = 7;
  188. std::vector<int> old_bins;
  189. old_bins.push_back(old_bin_value);
  190. std::vector<vector<int>::iterator> bin_cache;
  191. bin_cache.push_back(old_bins.begin());
  192. unsigned cache_offset = 1;
  193. unsigned cache_end;
  194. const unsigned bin_count = 2;
  195. std::vector<int>::iterator *new_cache_start =
  196. size_bins(bin_sizes, bin_cache, cache_offset, cache_end, bin_count);
  197. BOOST_CHECK((new_cache_start - &bin_cache[0]) == cache_offset);
  198. BOOST_CHECK(bin_sizes[0] == 0);
  199. BOOST_CHECK(bin_sizes[1] == 0);
  200. BOOST_CHECK(bin_sizes[2] == 7); // shouldn't modify past bin_count
  201. BOOST_CHECK(cache_end == 3);
  202. BOOST_CHECK(bin_cache.size() == cache_end);
  203. BOOST_CHECK(old_bins[0] == old_bin_value);
  204. }
  205. // Test the specialized 3-way swap loops.
  206. void swap_loop_test() {
  207. size_t bin_sizes[1 << detail::max_finishing_splits];
  208. bin_sizes[0] = bin_sizes[1] = 2;
  209. bin_sizes[2] = 1;
  210. // test integer swap loop
  211. vector<int> ints;
  212. const int int_div_min = 3;
  213. const int int_log_divisor = 1;
  214. const unsigned int_offset = int_div_min << int_log_divisor;
  215. ints.push_back(2 + int_offset);
  216. ints.push_back(1 + int_offset); // stays in place
  217. ints.push_back(4 + int_offset);
  218. ints.push_back(3 + int_offset);
  219. ints.push_back(0 + int_offset);
  220. vector<vector<int>::iterator> int_bin_vector;
  221. int_bin_vector.push_back(ints.begin());
  222. int_bin_vector.push_back(int_bin_vector[0] + bin_sizes[0]);
  223. int_bin_vector.push_back(int_bin_vector[1] + bin_sizes[1]);
  224. vector<int>::iterator next_int_bin_start = int_bin_vector[0];
  225. vector<int>::iterator *int_bins = &int_bin_vector[0];
  226. int_right_shift integer_right_shift;
  227. swap_loop(int_bins, next_int_bin_start, 0, integer_right_shift, bin_sizes,
  228. int_log_divisor, int_div_min);
  229. for (unsigned i = 0; i < ints.size(); ++i) {
  230. BOOST_CHECK(ints[i] == int(int_offset + i));
  231. }
  232. BOOST_CHECK(next_int_bin_start == ints.begin() + bin_sizes[0]);
  233. // test float swap loop
  234. vector<float> floats;
  235. const int float_four_as_int = float_mem_cast<float, int>(4.0f);
  236. const int float_log_divisor =
  237. rough_log_2_size(float_mem_cast<float, int>(5.0f) - float_four_as_int);
  238. const int float_div_min = float_four_as_int >> float_log_divisor;
  239. floats.push_back(6.0f);
  240. floats.push_back(5.0f); // stays in place
  241. floats.push_back(8.0f);
  242. floats.push_back(7.0f);
  243. floats.push_back(4.0f);
  244. vector<vector<float>::iterator> float_bin_vector;
  245. float_bin_vector.push_back(floats.begin());
  246. float_bin_vector.push_back(float_bin_vector[0] + bin_sizes[0]);
  247. float_bin_vector.push_back(float_bin_vector[1] + bin_sizes[1]);
  248. vector<float>::iterator next_float_bin_start = float_bin_vector[0];
  249. vector<float>::iterator *float_bins = &float_bin_vector[0];
  250. float_swap_loop(float_bins, next_float_bin_start, 0, bin_sizes,
  251. float_log_divisor, float_div_min);
  252. for (unsigned i = 0; i < floats.size(); ++i) {
  253. BOOST_CHECK(floats[i] == 4.0f + i);
  254. }
  255. BOOST_CHECK(next_float_bin_start == floats.begin() + bin_sizes[0]);
  256. }
  257. } // end anonymous namespace
  258. // test main
  259. int test_main( int, char*[] )
  260. {
  261. roughlog2_test();
  262. get_min_count_test<int_log_mean_bin_size, int_log_min_split_count,
  263. int_log_finishing_count>();
  264. get_min_count_test<float_log_mean_bin_size, float_log_min_split_count,
  265. float_log_finishing_count>();
  266. get_log_divisor_test();
  267. is_sorted_or_find_extremes_test();
  268. size_bins_test();
  269. swap_loop_test();
  270. return 0;
  271. }