2017-05-18 18:24:48 +10:00
|
|
|
/*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*
|
|
|
|
* Copyright:
|
|
|
|
* 2017, Danny Robson <danny@nerdcruft.net>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef CRUFT_UTIL_ALGO_SORT_IPP
|
|
|
|
#error
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define CRUFT_UTIL_ALGO_SORT_IPP
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
#include <array>
|
|
|
|
#include <iterator>
|
|
|
|
#include <numeric>
|
|
|
|
#include <vector>
|
2017-05-19 15:22:44 +10:00
|
|
|
#include <iterator>
|
|
|
|
#include <type_traits>
|
2017-05-18 18:24:48 +10:00
|
|
|
|
|
|
|
#include "../debug.hpp"
|
|
|
|
#include "../tuple.hpp"
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
namespace cruft::util::sort::detail {
|
|
|
|
template <typename IndexA, typename IndexB, typename RandomIt>
|
|
|
|
void
|
|
|
|
index_swap (IndexA a, IndexB b, RandomIt value)
|
|
|
|
{
|
2017-05-19 15:22:44 +10:00
|
|
|
static_assert(
|
|
|
|
std::is_base_of<
|
|
|
|
std::random_access_iterator_tag,
|
|
|
|
typename std::iterator_traits<RandomIt>::iterator_category
|
|
|
|
>::value
|
|
|
|
);
|
|
|
|
|
2017-05-18 18:24:48 +10:00
|
|
|
std::swap (value[a], value[b]);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename IndexA, typename IndexB, typename RandomIt, typename ...Tail>
|
|
|
|
void
|
|
|
|
index_swap (IndexA a, IndexB b, RandomIt value, Tail ...tail)
|
|
|
|
{
|
|
|
|
index_swap (a, b, value);
|
2017-05-19 15:23:10 +10:00
|
|
|
index_swap (a, b, tail...);
|
2017-05-18 18:24:48 +10:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
template <typename IndexIt, typename ValueIt, typename ...OtherIt>
|
|
|
|
void
|
|
|
|
cruft::util::sort::reorder (IndexIt idx_first,
|
|
|
|
IndexIt idx_last,
|
|
|
|
ValueIt value,
|
|
|
|
OtherIt ...tail)
|
|
|
|
{
|
2017-05-19 15:22:44 +10:00
|
|
|
static_assert (
|
|
|
|
std::is_base_of<
|
|
|
|
std::random_access_iterator_tag,
|
|
|
|
typename std::iterator_traits<IndexIt>::iterator_category
|
|
|
|
>::value
|
|
|
|
);
|
|
|
|
|
|
|
|
static_assert (
|
|
|
|
std::is_base_of<
|
|
|
|
std::random_access_iterator_tag,
|
|
|
|
typename std::iterator_traits<ValueIt>::iterator_category
|
|
|
|
>::value
|
|
|
|
);
|
|
|
|
|
2017-05-18 18:24:48 +10:00
|
|
|
// 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];
|
|
|
|
|
2017-05-19 15:23:10 +10:00
|
|
|
detail::index_swap (i, j, value, tail..., idx_first);
|
2017-05-18 18:24:48 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
template <typename RandomIt, class Comparator, class ...Args>
|
|
|
|
void
|
|
|
|
cruft::util::sort::soa (RandomIt key_first,
|
|
|
|
RandomIt key_last,
|
|
|
|
Comparator comp,
|
|
|
|
Args ...values)
|
|
|
|
{
|
2017-05-19 15:22:44 +10:00
|
|
|
static_assert (
|
|
|
|
std::is_base_of<
|
|
|
|
std::random_access_iterator_tag,
|
|
|
|
typename std::iterator_traits<RandomIt>::iterator_category
|
|
|
|
>::value
|
|
|
|
);
|
|
|
|
|
2017-05-18 18:24:48 +10:00
|
|
|
// 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<decltype(size)> indices (size);
|
|
|
|
std::iota (std::begin (indices), std::end (indices), 0);
|
|
|
|
|
|
|
|
std::sort (std::begin (indices),
|
|
|
|
std::end (indices),
|
2017-05-18 18:38:23 +10:00
|
|
|
[&] (const auto &cruft_util_sort_soa_a, const auto &cruft_util_sort_soa_b)
|
2017-05-18 18:24:48 +10:00
|
|
|
{
|
2017-05-18 18:38:23 +10:00
|
|
|
// 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]
|
|
|
|
);
|
2017-05-18 18:24:48 +10:00
|
|
|
});
|
|
|
|
|
|
|
|
// 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 < (decltype(size))dest.size (); ++i) {
|
|
|
|
dest[indices[i]] = i;
|
|
|
|
}
|
|
|
|
|
|
|
|
// reorder all the arrays using the mapping we have discovered.
|
2017-05-19 15:23:10 +10:00
|
|
|
reorder (std::begin (dest), std::end (dest), key_first, values...);
|
2017-05-18 18:24:48 +10:00
|
|
|
};
|