diff --git a/include/burst/algorithm/detail/counting_sort.hpp b/include/burst/algorithm/detail/counting_sort.hpp index c0278d3..d7be3c0 100644 --- a/include/burst/algorithm/detail/counting_sort.hpp +++ b/include/burst/algorithm/detail/counting_sort.hpp @@ -125,7 +125,13 @@ namespace burst }); } - template + template + < + typename Counter, + typename ForwardIterator, + typename RandomAccessIterator, + typename Map + > RandomAccessIterator counting_sort_impl ( @@ -138,15 +144,35 @@ namespace burst using value_type = iterator_value_t; using traits = counting_sort_traits; - using difference_type = iterator_difference_t; + using counter_type = Counter; // Единица для дополнительного нуля в начале массива. - difference_type counters[traits::value_range + 1] = {0}; + counter_type counters[traits::value_range + 1] = {0}; collect(first, last, map, std::next(std::begin(counters))); dispose(first, last, result, map, std::begin(counters)); return result + burst::cback(counters); } + + template + RandomAccessIterator + counting_sort_impl + ( + ForwardIterator first, + ForwardIterator last, + RandomAccessIterator result, + Map map + ) + { + return + counting_sort_impl> + ( + first, + last, + result, + map + ); + } } // namespace detail } // namespace burst diff --git a/include/burst/algorithm/detail/radix_sort.hpp b/include/burst/algorithm/detail/radix_sort.hpp index 035023d..c578406 100644 --- a/include/burst/algorithm/detail/radix_sort.hpp +++ b/include/burst/algorithm/detail/radix_sort.hpp @@ -86,7 +86,7 @@ namespace burst Вызывает сортировку подсчётом из входного диапазона в буфер, а потом переносит результат из буфера обратно во входной диапазон. */ - template + template typename std::enable_if < radix_sort_traits @@ -100,8 +100,9 @@ namespace burst > ::type radix_sort_impl (RandomAccessIterator1 first, RandomAccessIterator1 last, RandomAccessIterator2 buffer, Map map, Radix radix) { + using counter_type = Counter; auto buffer_end = - counting_sort_impl(move_assign_please(first), move_assign_please(last), buffer, + counting_sort_impl(move_assign_please(first), move_assign_please(last), buffer, [& map, & radix] (const auto & value) { return radix(map(value)); @@ -196,7 +197,7 @@ namespace burst Таким образом, в итоге во входном диапазоне оказывается отсортированная последовательность. */ - template + template typename std::enable_if < radix_sort_traits @@ -213,9 +214,9 @@ namespace burst using value_type = iterator_value_t; using traits = radix_sort_traits; - using difference_type = iterator_difference_t; - difference_type counters[traits::radix_count][traits::radix_value_range] = {{0}}; - difference_type maximums[traits::radix_count] = {0}; + using counter_type = Counter; + counter_type counters[traits::radix_count][traits::radix_value_range] = {{0}}; + counter_type maximums[traits::radix_count] = {0}; const auto is_sorted = collect(first, last, map, radix, counters, maximums); if (not is_sorted) { diff --git a/include/burst/algorithm/radix_sort/radix_sort_seq.hpp b/include/burst/algorithm/radix_sort/radix_sort_seq.hpp index 984b6ec..03208d7 100644 --- a/include/burst/algorithm/radix_sort/radix_sort_seq.hpp +++ b/include/burst/algorithm/radix_sort/radix_sort_seq.hpp @@ -6,8 +6,12 @@ #include #include #include +#include +#include #include +#include +#include #include namespace burst @@ -106,7 +110,39 @@ namespace burst Radix radix ) { - detail::radix_sort_impl(first, last, buffer, compose(to_unsigned, std::move(map)), radix); + using difference_type = iterator_difference_t; + using min_type = + typename std::conditional + < + sizeof(std::int32_t) < sizeof(difference_type), + std::int32_t, + difference_type + > + ::type; + + using std::distance; + if (distance(first, last) <= std::numeric_limits::max()) + { + detail::radix_sort_impl + ( + first, + last, + buffer, + compose(to_unsigned, std::move(map)), + radix + ); + } + else + { + detail::radix_sort_impl + ( + first, + last, + buffer, + compose(to_unsigned, std::move(map)), + radix + ); + } } template diff --git a/test/burst/algorithm/radix_sort.cpp b/test/burst/algorithm/radix_sort.cpp index 40cc380..1f4b435 100644 --- a/test/burst/algorithm/radix_sort.cpp +++ b/test/burst/algorithm/radix_sort.cpp @@ -1,21 +1,21 @@ #include #include +#include #include #include #include -#include -#include -#include -#include +#include #include #include +#include #include #include #include +#include #include #include #include @@ -563,4 +563,25 @@ TEST_SUITE("radix_sort") CHECK(thread_ids.size() == 1); CHECK(thread_ids.find(std::this_thread::get_id()) != thread_ids.end()); } + + // Тест нужен для обеспечения 100%-й метрики покрытия кода тестами. + TEST_CASE_TEMPLATE("Если размер сортируемого массива превышает максимальное значение " + "32-битного числа, то алгоритм идёт через ветку с int64-счётчиками", + integer_type, std::int8_t, std::int16_t) + { + const auto begin = utility::silly_iterator(0); + const auto end = utility::silly_iterator(std::int64_t{1} << 32); + + CHECK_THROWS_AS + ( + burst::radix_sort(begin, end, begin, + [] (auto x) + { + // И тут же выходит, потому что реальную сортировку производить чудовищно долго. + throw std::runtime_error("stop"); + return x; + }), + std::runtime_error + ); + } } diff --git a/test/utility/silly_iterator.hpp b/test/utility/silly_iterator.hpp new file mode 100644 index 0000000..9fbb947 --- /dev/null +++ b/test/utility/silly_iterator.hpp @@ -0,0 +1,106 @@ +#ifndef BURST_TEST__UTILITY__SILLY_ITERATOR_HPP +#define BURST_TEST__UTILITY__SILLY_ITERATOR_HPP + +#include + +#include +#include + +namespace utility +{ + /*! + \brief + Дурацкий итератор + + \details + Сущность с интерфейсом итератора произвольного доступа. + Умеет продвигаться на любое расстояние и правильно определять расстояние между двумя + итераторами, но при разыменовании всегда возвращает одно и то же значение. + */ + template + struct silly_iterator: + public + boost::iterator_facade + < + silly_iterator, + Value, + boost::random_access_traversal_tag, + Value &, + std::int64_t + > + { + private: + using base_type = + boost::iterator_facade + < + silly_iterator, + Value, + boost::random_access_traversal_tag, + Value &, + std::int64_t + >; + + public: + silly_iterator (): + m_pos(0), + m_value(std::make_unique(0)) + { + } + + explicit silly_iterator (std::int64_t pos): + m_pos(pos), + m_value(std::make_unique(0)) + { + } + + silly_iterator (const silly_iterator & that): + m_pos(that.m_pos), + m_value(std::make_unique(0)) + { + } + + silly_iterator & operator = (const silly_iterator & that) + { + m_pos = that.m_pos; + return *this; + } + + ~silly_iterator () = default; + + void increment () + { + ++m_pos; + } + + void decrement () + { + --m_pos; + } + + void advance (typename base_type::difference_type n) + { + m_pos += n; + } + + bool equal (const silly_iterator & that) const + { + return this->m_pos == that.m_pos; + } + + typename base_type::difference_type distance_to (const silly_iterator & that) const + { + return that.m_pos - this->m_pos; + } + + typename base_type::reference dereference () const + { + return *m_value; + } + + private: + std::int64_t m_pos; + std::unique_ptr m_value; + }; +} + +#endif // BURST_TEST__UTILITY__SILLY_ITERATOR_HPP