Skip to content

Commit

Permalink
probe::rem: don't always recompute size (#169)
Browse files Browse the repository at this point in the history
  • Loading branch information
Morwenn committed Aug 31, 2020
1 parent 3149c86 commit d64cd6c
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 56 deletions.
18 changes: 17 additions & 1 deletion include/cpp-sort/detail/type_traits.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2015-2019 Morwenn
* Copyright (c) 2015-2020 Morwenn
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
Expand Down Expand Up @@ -244,6 +244,22 @@ namespace detail
template<typename T>
using remove_cvref_t = typename remove_cvref<T>::type;

////////////////////////////////////////////////////////////
// std::is_bounded_array from C++20

template<typename T>
struct is_bounded_array:
std::false_type
{};

template<typename T, std::size_t N>
struct is_bounded_array<T[N]>:
std::true_type
{};

template<typename T>
constexpr bool is_bounded_array_v = is_bounded_array<T>::value;

////////////////////////////////////////////////////////////
// Type traits to take __int128 into account even when the
// standard library isn't instrumented but the type is still
Expand Down
161 changes: 106 additions & 55 deletions include/cpp-sort/probes/rem.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include <cpp-sort/sorter_traits.h>
#include <cpp-sort/utility/as_function.h>
#include <cpp-sort/utility/functional.h>
#include <cpp-sort/utility/size.h>
#include <cpp-sort/utility/static_const.h>
#include "../detail/iterator_traits.h"
#include "../detail/upper_bound.h"
Expand All @@ -45,8 +46,108 @@ namespace probe
{
namespace detail
{
template<
bool RecomputeSize,
typename ForwardIterator,
typename Compare,
typename Projection
>
auto rem_probe_algo(ForwardIterator first, ForwardIterator last,
cppsort::detail::difference_type_t<ForwardIterator> size,
Compare compare, Projection projection)
-> ::cppsort::detail::difference_type_t<ForwardIterator>
{
constexpr bool is_random_access = std::is_base_of<
std::random_access_iterator_tag,
cppsort::detail::iterator_category_t<ForwardIterator>
>::value;

// Compute Rem as n - longest non-decreasing subsequence,
// where the LNDS is computed with an altered patience
// sorting algorithm

if (first == last || std::next(first) == last) {
return 0;
}

// The size is only needed at the end to actually compute Rem, but
// we can compute it as-we-go when it is not known in order to avoid
// making two passes over the sequence - when the sequence is made
// of random-access iterators, we only compute it once
if (RecomputeSize && is_random_access) {
size = std::distance(first, last);
}

auto&& comp = utility::as_function(compare);
auto&& proj = utility::as_function(projection);

// Top (smaller) elements in patience sorting stacks
std::vector<ForwardIterator> stack_tops;

auto deref_compare = [&](const auto& lhs, auto rhs_it) mutable {
return comp(lhs, *rhs_it);
};
auto deref_proj = [&](const auto& value) mutable -> decltype(auto) {
return proj(value);
};

while (first != last) {
auto it = cppsort::detail::upper_bound(
stack_tops.begin(), stack_tops.end(),
proj(*first), deref_compare, deref_proj);

if (it == stack_tops.end()) {
// The element is bigger than everything else,
// create a new "stack" to put it
stack_tops.emplace_back(first);
} else {
// The element is strictly smaller than the top
// of a given stack, replace the stack top
*it = first;
}
++first;

if (RecomputeSize && not is_random_access) {
// Compute the size as-we-go if iterators are not random-access
++size;
}
}

return stack_tops.size() ? size - stack_tops.size() : 0;
}

struct rem_impl
{
template<
typename ForwardIterable,
typename Compare = std::less<>,
typename Projection = utility::identity,
typename = std::enable_if_t<
is_projection_v<Projection, ForwardIterable, Compare> && (
cppsort::detail::is_detected_v<
cppsort::utility::detail::has_size_method_t,
ForwardIterable
> ||
cppsort::detail::is_bounded_array_v<
cppsort::detail::remove_cvref_t<ForwardIterable>
>
)
>
>
auto operator()(ForwardIterable&& iterable,
Compare compare={}, Projection projection={}) const
-> decltype(auto)
{
// While most algorithms use utility::size() for everything, we only
// want to handle data structures that provide their own size() method
// with the assumption that it's better than O(n) - which is at least
// consistent as far as the standard library is concerned. We also
// handle C arrays whose size is known and part of the type.
return rem_probe_algo<false>(std::begin(iterable), std::end(iterable),
utility::size(iterable),
std::move(compare), std::move(projection));
}

template<
typename ForwardIterator,
typename Compare = std::less<>,
Expand All @@ -57,62 +158,12 @@ namespace probe
>
auto operator()(ForwardIterator first, ForwardIterator last,
Compare compare={}, Projection projection={}) const
-> cppsort::detail::difference_type_t<ForwardIterator>
-> decltype(auto)
{
// Compute Rem as n - longest non-decreasing subsequence,
// where the LNDS is computed with an altered patience
// sorting algorithm

if (first == last || std::next(first) == last) {
return 0;
}

// The size is only needed at the end to actually compute Rem, but we
// need to compute it as-we-go when we are not using random-access
// iterators to avoid making two passes over the sequence, hence a
// starting value of 0 in this case
auto size = std::is_base_of<std::random_access_iterator_tag,
cppsort::detail::iterator_category_t<ForwardIterator>
>::value ? std::distance(first, last) : 0;

auto&& comp = utility::as_function(compare);
auto&& proj = utility::as_function(projection);

// Top (smaller) elements in patience sorting stacks
std::vector<ForwardIterator> stack_tops;

auto deref_compare = [&](const auto& lhs, auto rhs_it) mutable {
return comp(lhs, *rhs_it);
};
auto deref_proj = [&](const auto& value) mutable -> decltype(auto) {
return proj(value);
};

while (first != last) {
auto it = cppsort::detail::upper_bound(
stack_tops.begin(), stack_tops.end(),
proj(*first), deref_compare, deref_proj);

if (it == stack_tops.end()) {
// The element is bigger than everything else,
// create a new "stack" to put it
stack_tops.emplace_back(first);
} else {
// The element is strictly smaller than the top
// of a given stack, replace the stack top
*it = first;
}
++first;

if (not std::is_base_of<std::random_access_iterator_tag,
cppsort::detail::iterator_category_t<ForwardIterator>
>::value) {
// Compute the size as-we-go if iterators are not random-access
++size;
}
}

return stack_tops.size() ? size - stack_tops.size() : 0;
// We give 0 as a "dummy" value since it will be recomputed, but it
// is also used by the non-random-access iterators version as the
// initial value used for the size count
return rem_probe_algo<true>(first, last, 0, std::move(compare), std::move(projection));
}
};
}
Expand Down

0 comments on commit d64cd6c

Please sign in to comment.