/* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright: * 2017, Danny Robson */ #ifndef CRUFT_UTIL_ALGO_SORT_HPP #define CRUFT_UTIL_ALGO_SORT_HPP #include "../debug.hpp" #include "../cast.hpp" #include #include #include #include namespace cruft::sort { namespace detail { template void index_swap (IndexA a, IndexB b, RandomIt value) { static_assert( std::is_base_of< std::random_access_iterator_tag, typename std::iterator_traits::iterator_category >::value ); std::swap (value[a], value[b]); } template void index_swap (IndexA a, IndexB b, RandomIt value, Tail ...tail) { index_swap (a, b, value); index_swap (a, b, tail...); }; } /////////////////////////////////////////////////////////////////////////// // rearrange the values in the arrays specified by the iterators value and // ...tail by moving values to the positions described by the mapping of // offset-to-dest_index in idx_first:idx_last. // // ie, the indices { 2, 1, 0 } will reverse a series of arrays of three // elements // // all operations occur in-place, and the indices may be rearranged during // the operation. template void reorder (IndexIt idx_first, IndexIt idx_last, ValueIt value, OtherIt ...tail) { static_assert ( std::is_base_of< std::random_access_iterator_tag, typename std::iterator_traits::iterator_category >::value ); static_assert ( std::is_base_of< std::random_access_iterator_tag, typename std::iterator_traits::iterator_category >::value ); // Bail early on zero size arrays, partly so we can simplify some // debugging checks auto size = std::distance (idx_first, idx_last); if (!size) return; CHECK_LT (*std::max_element (idx_first, idx_last), size); for (decltype(size) i = 0; i < size - 1; ++i) { while (i != decltype(size){idx_first[i]}) { auto j = idx_first[i]; detail::index_swap (i, j, value, tail..., idx_first); } } } /////////////////////////////////////////////////////////////////////////// // sort an array specified by the iterators key_first:key_last using a // comparator, and optionally a series of additional value iterators // specified by ...values. // // sorting is performed in-place and will invariably allocate memory. template void soa (RandomIt key_first, RandomIt key_last, Comparator comp, Args ...values) { static_assert ( std::is_base_of< std::random_access_iterator_tag, typename std::iterator_traits::iterator_category >::value ); // bail early on guaranteed sorted or degenerate cases. we can make some // assumptions about minimum array sizes and non-wrapping indices later on // this way. if (std::distance (key_first, key_last) <= 1) return; // generate a list of indices into the key array and sort them so we have, // in effect, a sorted list of pointers. auto size = std::distance (key_first, key_last); std::vector indices (size); std::iota (std::begin (indices), std::end (indices), 0); std::sort ( std::begin (indices), std::end (indices), [&] (const auto &cruft_util_sort_soa_a, const auto &cruft_util_sort_soa_b) { // GCC: we use the ridiculous parameter names to avoid a name aliasing // bug/warning under gcc 6.3.0; if the client passes a comparator // lambda that uses the same parameter names then a warning will be // generated. given 'a' and 'b' aren't unlikely names we try to avoid // them here. return comp ( key_first[cruft_util_sort_soa_a], key_first[cruft_util_sort_soa_b] ); } ); // convert from a sorted list of pointers to a mapping of pointers to // desired final offsets. this is done so we can palm it off to the // reorder function. // TODO: avoid the need for this inverse array. decltype (indices) dest (indices.size ()); for (decltype(size) i = 0; i < ::cruft::cast::sign (dest.size ()); ++i) { dest[indices[i]] = i; } // reorder all the arrays using the mapping we have discovered. reorder (std::begin (dest), std::end (dest), key_first, values...); } } #endif