libostd/ostd/algorithm.hh

1154 lines
32 KiB
C++
Raw Normal View History

2017-04-06 20:14:52 +02:00
/* Algorithms for libostd.
*
2017-04-06 20:14:52 +02:00
* This file is part of libostd. See COPYING.md for futher information.
*/
2015-07-13 21:08:55 +02:00
#ifndef OSTD_ALGORITHM_HH
#define OSTD_ALGORITHM_HH
2017-04-09 16:44:45 +02:00
#include <cmath>
#include <utility>
2017-02-08 01:06:50 +01:00
#include <functional>
2017-02-16 20:39:05 +01:00
#include <type_traits>
2017-02-18 17:54:51 +01:00
#include <algorithm>
2015-07-13 21:08:55 +02:00
#include "ostd/range.hh"
2015-07-13 21:07:14 +02:00
namespace ostd {
2015-04-27 20:53:48 +02:00
2015-06-03 23:54:18 +02:00
/* partitioning */
2017-04-14 15:03:54 +02:00
/** @brief Partitions a range.
*
* Given a predicate `pred`, this rearranges the range so that items for
* which the predicate returns true are in the first part of the range.
*
* The range must be at least ostd::forward_range_tag. The range must also
* meet the conditions of ostd::is_range_element_swappable.
2017-04-14 15:03:54 +02:00
*
* The predicate is applied `N` times and the swap is done at most `N` times.
*
* @returns The second part of the range.
*/
2015-06-04 23:57:06 +02:00
template<typename R, typename U>
2016-04-26 23:25:46 +02:00
inline R partition(R range, U pred) {
static_assert(
is_range_element_swappable<R>,
"The range element accessors must allow swapping"
);
2015-06-04 23:57:06 +02:00
R ret = range;
2015-06-03 23:54:18 +02:00
for (; !range.empty(); range.pop_front()) {
if (pred(range.front())) {
2017-01-29 15:56:02 +01:00
using std::swap;
swap(range.front(), ret.front());
2015-06-03 23:54:18 +02:00
ret.pop_front();
2015-04-18 22:46:31 +02:00
}
}
2015-06-03 23:54:18 +02:00
return ret;
}
2015-04-18 22:46:31 +02:00
2017-04-14 15:03:54 +02:00
/** @brief A pipeable version of ostd::partition().
*
* The predicate is forwarded.
*/
template<typename F>
inline auto partition(F &&func) {
2017-04-12 00:07:21 +02:00
return [func = std::forward<F>(func)](auto &obj) mutable {
return partition(obj, std::forward<F>(func));
};
2016-04-26 23:25:46 +02:00
}
2017-04-14 15:03:54 +02:00
/** @brief Checks if a range is partitioned as in ostd::partition().
*
* First, all elements matching `pred` are skipped and then if any of
* the elements following that match `pred`, false is returned. Otherwise,
* true is returned.
*
* The predicate is applied at most `N` times.
*/
2015-06-04 23:57:06 +02:00
template<typename R, typename P>
2016-04-26 23:25:46 +02:00
inline bool is_partitioned(R range, P pred) {
2015-06-03 23:54:18 +02:00
for (; !range.empty() && pred(range.front()); range.pop_front());
for (; !range.empty(); range.pop_front()) {
if (pred(range.front())) {
return false;
}
}
2015-06-03 23:54:18 +02:00
return true;
}
2015-04-20 03:06:42 +02:00
2017-04-14 15:03:54 +02:00
/** @brief A pipeable version of ostd::is_partitioned().
*
* The predicate is forwarded.
*/
template<typename F>
inline auto is_partitioned(F &&func) {
2017-04-12 00:07:21 +02:00
return [func = std::forward<F>(func)](auto &obj) mutable {
return is_partitioned(obj, std::forward<F>(func));
};
2016-04-26 23:25:46 +02:00
}
2015-06-03 23:54:18 +02:00
/* sorting */
2015-04-24 19:16:35 +02:00
2015-06-03 23:54:18 +02:00
namespace detail {
2015-06-04 23:57:06 +02:00
template<typename R, typename C>
static void insort(R range, C &compare) {
range_size_t<R> rlen = range.size();
for (range_size_t<R> i = 1; i < rlen; ++i) {
range_size_t<R> j = i;
2017-04-01 16:49:38 +02:00
range_value_t<R> v{std::move(range[i])};
2015-06-03 23:54:18 +02:00
while (j > 0 && !compare(range[j - 1], v)) {
2017-04-01 16:49:38 +02:00
range[j] = std::move(range[j - 1]);
2015-06-03 23:54:18 +02:00
--j;
2015-06-03 23:43:07 +02:00
}
2017-01-25 01:44:22 +01:00
range[j] = std::move(v);
2015-04-19 00:12:45 +02:00
}
2015-06-03 23:54:18 +02:00
}
2015-04-19 00:12:45 +02:00
2015-06-04 23:57:06 +02:00
template<typename R, typename C>
static void hs_sift_down(
R range, range_size_t<R> s, range_size_t<R> e, C &compare
) {
range_size_t<R> r = s;
2015-06-03 23:54:18 +02:00
while ((r * 2 + 1) <= e) {
range_size_t<R> ch = r * 2 + 1;
range_size_t<R> sw = r;
if (compare(range[sw], range[ch])) {
2015-06-03 23:54:18 +02:00
sw = ch;
}
if (((ch + 1) <= e) && compare(range[sw], range[ch + 1])) {
2015-06-03 23:54:18 +02:00
sw = ch + 1;
}
2015-06-03 23:54:18 +02:00
if (sw != r) {
2017-01-29 15:56:02 +01:00
using std::swap;
swap(range[r], range[sw]);
2015-06-03 23:54:18 +02:00
r = sw;
} else {
return;
}
2015-04-18 23:33:19 +02:00
}
2015-06-03 23:54:18 +02:00
}
2015-04-24 19:16:35 +02:00
2015-06-04 23:57:06 +02:00
template<typename R, typename C>
static void heapsort(R range, C &compare) {
range_size_t<R> len = range.size();
range_size_t<R> st = (len - 2) / 2;
2015-06-03 23:54:18 +02:00
for (;;) {
detail::hs_sift_down(range, st, len - 1, compare);
if (st-- == 0) {
break;
}
2015-06-03 23:54:18 +02:00
}
range_size_t<R> e = len - 1;
2015-06-03 23:54:18 +02:00
while (e > 0) {
2017-01-29 15:56:02 +01:00
using std::swap;
swap(range[e], range[0]);
2015-06-03 23:54:18 +02:00
--e;
detail::hs_sift_down(range, 0, e, compare);
2015-06-03 23:43:07 +02:00
}
2015-04-18 23:33:19 +02:00
}
2015-06-04 23:57:06 +02:00
template<typename R, typename C>
static void introloop(R range, C &compare, range_size_t<R> depth) {
2017-01-29 15:56:02 +01:00
using std::swap;
2015-06-03 23:54:18 +02:00
if (range.size() <= 10) {
detail::insort(range, compare);
2015-06-03 23:54:18 +02:00
return;
}
if (depth == 0) {
detail::heapsort(range, compare);
2015-06-03 23:54:18 +02:00
return;
}
2017-01-29 15:56:02 +01:00
swap(range[range.size() / 2], range.back());
range_size_t<R> pi = 0;
2015-06-09 23:56:40 +02:00
R pr = range;
pr.pop_back();
for (; !pr.empty(); pr.pop_front()) {
if (compare(pr.front(), range.back())) {
2017-01-29 15:56:02 +01:00
swap(pr.front(), range[pi++]);
}
2015-06-09 23:56:40 +02:00
}
2017-01-29 15:56:02 +01:00
swap(range[pi], range.back());
detail::introloop(range.slice(0, pi), compare, depth - 1);
2017-04-01 16:49:38 +02:00
detail::introloop(range.slice(pi + 1), compare, depth - 1);
2015-04-18 21:20:40 +02:00
}
2015-06-04 23:57:06 +02:00
template<typename R, typename C>
inline void introsort(R range, C &compare) {
2017-04-09 16:44:45 +02:00
detail::introloop(range, compare, static_cast<range_size_t<R>>(
2 * (std::log(range.size()) / std::log(2))
));
2015-04-18 21:20:40 +02:00
}
2015-06-03 23:54:18 +02:00
} /* namespace detail */
2017-04-14 15:03:54 +02:00
/** @brief Sorts a range given a comparison function.
*
* The range must be at least ostd::finite_random_access_range_tag. The
* comparison function takes two `ostd::range_reference_t<R>` and must
* return a boolean equivalent to `a < b` for ascending order.
*
* The items are swapped in the range, which means the range must also
* meet the conditions of ostd::is_range_element_swappable.
2017-04-14 15:03:54 +02:00
*
* The worst-case and average performance of this algorithm os `O(n log n)`.
* The best-case performance is `O(n)`. This happens when the range is small
* enough and already sorted (insertion sort is used for small ranges).
*
* The actual algorithm used is a hybrid algorithm between quicksort and
* heapsort (intosort) with insertion sort for small ranges.
*
* @see ostd::sort()
*/
2015-06-04 23:57:06 +02:00
template<typename R, typename C>
inline R sort_cmp(R range, C compare) {
static_assert(
is_range_element_swappable<R>,
"The range element accessors must allow swapping"
);
detail::introsort(range, compare);
return range;
}
2017-04-14 15:03:54 +02:00
/** @brief A pipeable version of ostd::sort_cmp().
*
* The comparison function is forwarded.
*/
template<typename C>
inline auto sort_cmp(C &&compare) {
2017-04-12 00:07:21 +02:00
return [compare = std::forward<C>(compare)](auto &obj) mutable {
return sort_cmp(obj, std::forward<C>(compare));
};
2015-06-03 23:54:18 +02:00
}
2015-04-24 19:16:35 +02:00
2017-04-14 15:03:54 +02:00
/** @brief Like ostd::sort_cmp() using `std::less<ostd::range_value_t<R>>{}`. */
2015-06-04 23:57:06 +02:00
template<typename R>
inline R sort(R range) {
static_assert(
is_range_element_swappable<R>,
"The range element accessors must allow swapping"
);
2017-04-14 15:03:54 +02:00
return sort_cmp(range, std::less<range_value_t<R>>{});
}
2017-04-14 15:03:54 +02:00
/** @brief A pipeable version of ostd::sort(). */
inline auto sort() {
2017-04-12 00:07:21 +02:00
return [](auto &obj) { return sort(obj); };
2015-06-03 23:54:18 +02:00
}
2015-06-03 23:54:18 +02:00
/* min/max(_element) */
2017-04-14 15:03:54 +02:00
/** @brief Finds the smallest element in the range.
*
2017-04-14 16:57:02 +02:00
* It works like std::min_element(). The range must be at least
* ostd::forward_range_tag. The `<` operator is used for comparisons.
2017-04-14 15:03:54 +02:00
*
* @see ostd::min_element_cmp(), ostd::max_element()
*/
2015-06-04 23:57:06 +02:00
template<typename R>
inline R min_element(R range) {
R r = range;
for (; !range.empty(); range.pop_front()) {
2017-02-18 17:54:51 +01:00
if (std::min(r.front(), range.front()) == range.front()) {
2015-06-03 23:54:18 +02:00
r = range;
}
}
2015-06-03 23:54:18 +02:00
return r;
}
2017-04-14 15:03:54 +02:00
/** @brief Finds the smallest element in the range.
*
* It works like std::min_element. The range must be at least
2017-04-14 16:57:02 +02:00
* ostd::forward_range_tag. The `compare` function is used for comparisons.
2017-04-14 15:03:54 +02:00
*
* @see ostd::min_element(), ostd::max_element_cmp()
*/
2015-06-04 23:57:06 +02:00
template<typename R, typename C>
inline R min_element_cmp(R range, C compare) {
2015-06-04 23:57:06 +02:00
R r = range;
for (; !range.empty(); range.pop_front()) {
2017-02-18 17:54:51 +01:00
if (std::min(r.front(), range.front(), compare) == range.front()) {
2015-06-03 23:54:18 +02:00
r = range;
}
}
2015-06-03 23:54:18 +02:00
return r;
}
2017-04-14 15:03:54 +02:00
/** @brief A pipeable version of ostd::min_element(). */
inline auto min_element() {
2017-04-12 00:07:21 +02:00
return [](auto &obj) {
return min_element(obj);
2017-01-25 01:44:22 +01:00
};
}
2017-04-14 15:03:54 +02:00
/** @brief A pipeable version of ostd::min_element_cmp().
*
* The comparison function is forwarded.
*/
template<typename C>
inline auto min_element_cmp(C &&compare) {
2017-04-12 00:07:21 +02:00
return [compare = std::forward<C>(compare)](auto &obj) mutable {
return min_element_cmp(obj, std::forward<C>(compare));
};
}
2017-04-14 15:03:54 +02:00
/** @brief Finds the largest element in the range.
*
2017-04-14 16:57:02 +02:00
* It works like std::max_element(). The range must be at least
* ostd::forward_range_tag. The `<` operator is used for comparisons.
2017-04-14 15:03:54 +02:00
*
* @see ostd::max_element_cmp(), ostd::min_element()
*/
2015-06-04 23:57:06 +02:00
template<typename R>
inline R max_element(R range) {
R r = range;
for (; !range.empty(); range.pop_front()) {
2017-02-18 17:54:51 +01:00
if (std::max(r.front(), range.front()) == range.front()) {
2015-06-03 23:54:18 +02:00
r = range;
}
}
2015-06-03 23:54:18 +02:00
return r;
}
2017-04-14 15:03:54 +02:00
/** @brief Finds the largest element in the range.
*
* It works like std::max_element. The range must be at least
2017-04-14 16:57:02 +02:00
* ostd::forward_range_tag. The `compare` function is used for comparisons.
2017-04-14 15:03:54 +02:00
*
* @see ostd::max_element(), ostd::min_element_cmp()
*/
2015-06-04 23:57:06 +02:00
template<typename R, typename C>
inline R max_element_cmp(R range, C compare) {
2015-06-04 23:57:06 +02:00
R r = range;
for (; !range.empty(); range.pop_front()) {
2017-02-18 17:54:51 +01:00
if (std::max(r.front(), range.front(), compare) == range.front()) {
2015-06-03 23:54:18 +02:00
r = range;
}
}
2015-06-03 23:54:18 +02:00
return r;
}
2017-04-14 15:03:54 +02:00
/** @brief A pipeable version of ostd::max_element(). */
inline auto max_element() {
2017-04-12 00:07:21 +02:00
return [](auto &obj) {
return max_element(obj);
2017-01-25 01:44:22 +01:00
};
}
2017-04-14 15:03:54 +02:00
/** @brief A pipeable version of ostd::max_element_cmp().
*
* The comparison function is forwarded.
*/
template<typename C>
inline auto max_element_cmp(C &&compare) {
2017-04-12 00:07:21 +02:00
return [compare = std::forward<C>(compare)](auto &obj) mutable {
return max_element_cmp(obj, std::forward<C>(compare));
};
}
2015-04-20 03:21:27 +02:00
/* lexicographical compare */
2017-04-14 16:57:02 +02:00
/** @brief Like std::lexicographical_compare(), but for ranges.
*
* This version uses the `<` operator for comparisons. This algorithm
* is not multi-pass, so an ostd::input_range_tag is perfectly fine here.
*
* @see ostd::lexicographical_compare_cmp()
*/
template<typename R1, typename R2>
2016-04-26 23:25:46 +02:00
inline bool lexicographical_compare(R1 range1, R2 range2) {
while (!range1.empty() && !range2.empty()) {
if (range1.front() < range2.front()) {
return true;
}
if (range2.front() < range1.front()) {
return false;
}
range1.pop_front();
range2.pop_front();
}
return (range1.empty() && !range2.empty());
}
2017-04-14 16:57:02 +02:00
/** @brief A pipeable version of ostd::lexicographical_compare().
*
* The range is forwarded.
*/
template<typename R>
inline auto lexicographical_compare(R &&range) {
2017-04-12 00:07:21 +02:00
return [range = std::forward<R>(range)](auto &obj) mutable {
return lexicographical_compare(obj, std::forward<R>(range));
};
}
2017-04-14 16:57:02 +02:00
/** @brief Like std::lexicographical_compare(), but for ranges.
*
* This version uses the `compare` function for comparisons. This algorithm
* is not multi-pass, so an ostd::input_range_tag is perfectly fine here.
*
* @see ostd::lexicographical_compare()
*/
template<typename R1, typename R2, typename C>
inline bool lexicographical_compare_cmp(R1 range1, R2 range2, C compare) {
while (!range1.empty() && !range2.empty()) {
if (compare(range1.front(), range2.front())) {
return true;
}
if (compare(range2.front(), range1.front())) {
return false;
}
range1.pop_front();
range2.pop_front();
}
return (range1.empty() && !range2.empty());
}
2017-04-14 16:57:02 +02:00
/** @brief A pipeable version of ostd::lexicographical_compare_cmp().
*
* The range and comparison function are forwarded.
*/
template<typename R, typename C>
inline auto lexicographical_compare_cmp(R &&range, C &&compare) {
return [
2017-01-25 01:44:22 +01:00
range = std::forward<R>(range), compare = std::forward<C>(compare)
2017-04-12 00:07:21 +02:00
](auto &obj) mutable {
return lexicographical_compare_cmp(
2017-04-12 00:07:21 +02:00
obj, std::forward<R>(range), std::forward<C>(compare)
);
};
}
2015-06-03 23:54:18 +02:00
/* algos that don't change the range */
2015-04-20 03:21:27 +02:00
2017-04-14 16:57:02 +02:00
/** @brief Executes `func` on each element of `range`.
*
* The `func` is called like `func(range.front())`. The algorithm is
* not multi-pass, so an ostd::input_range_tag is perfectly fine.
*
* @returns The `func` by move.
*/
2015-06-04 23:57:06 +02:00
template<typename R, typename F>
2016-04-26 23:25:46 +02:00
inline F for_each(R range, F func) {
for (; !range.empty(); range.pop_front()) {
2015-06-03 23:54:18 +02:00
func(range.front());
}
2017-01-25 01:44:22 +01:00
return std::move(func);
2015-06-03 23:54:18 +02:00
}
2015-04-20 03:21:27 +02:00
2017-04-14 16:57:02 +02:00
/** @brief A pipeable version of ostd::for_each().
*
* The function is forwarded.
*/
template<typename F>
inline auto for_each(F &&func) {
2017-04-12 00:07:21 +02:00
return [func = std::forward<F>(func)](auto &obj) mutable {
return for_each(obj, std::forward<F>(func));
2016-04-26 23:25:46 +02:00
};
}
2017-04-14 16:57:02 +02:00
/** @brief Checks if every element of `range` matches `pred`.
*
* The `pred` has to return true when called like `pred(range.front())`.
* If it doesn't, `false` is returned.
*
* The predicate is called at most `N` times, where `N` is the range length.
* The range is an ostd::input_range_tag or better.
*
* @see ostd::any_of(), ostd::none_of()
*/
2015-06-04 23:57:06 +02:00
template<typename R, typename P>
2016-04-26 23:25:46 +02:00
inline bool all_of(R range, P pred) {
for (; !range.empty(); range.pop_front()) {
if (!pred(range.front())) {
return false;
}
}
2015-06-03 23:54:18 +02:00
return true;
}
2015-04-21 03:33:58 +02:00
2017-04-14 16:57:02 +02:00
/** @brief A pipeable version of ostd::all_of().
*
* The function is forwarded.
*/
template<typename F>
inline auto all_of(F &&func) {
2017-04-12 00:07:21 +02:00
return [func = std::forward<F>(func)](auto &obj) mutable {
return all_of(obj, std::forward<F>(func));
};
2016-04-26 23:25:46 +02:00
}
2017-04-14 16:57:02 +02:00
/** @brief Checks if any element of `range` matches `pred`.
*
* As soon as `pred` returns true when called like `pred(range.front())`,
* this returns true. Otherwise it returns false.
*
* The predicate is called at most `N` times, where `N` is the range length.
* The range is an ostd::input_range_tag or better.
*
* @see ostd::all_of(), ostd::none_of()
*/
2015-06-04 23:57:06 +02:00
template<typename R, typename P>
2016-04-26 23:25:46 +02:00
inline bool any_of(R range, P pred) {
2015-06-03 23:54:18 +02:00
for (; !range.empty(); range.pop_front())
if (pred(range.front())) return true;
return false;
}
2015-04-21 03:33:58 +02:00
2017-04-14 16:57:02 +02:00
/** @brief A pipeable version of ostd::any_of().
*
* The function is forwarded.
*/
template<typename F>
inline auto any_of(F &&func) {
2017-04-12 00:07:21 +02:00
return [func = std::forward<F>(func)](auto &obj) mutable {
return any_of(obj, std::forward<F>(func));
};
2016-04-26 23:25:46 +02:00
}
2017-04-14 16:57:02 +02:00
/** @brief Checks if no element of `range` matches `pred`.
*
* As soon as `pred` returns true when called like `pred(range.front())`,
* this returns false. Otherwise it returns true.
*
* The predicate is called at most `N` times, where `N` is the range length.
* The range is an ostd::input_range_tag or better.
*
* @see ostd::all_of(), ostd::any_of()
*/
2015-06-04 23:57:06 +02:00
template<typename R, typename P>
2016-04-26 23:25:46 +02:00
inline bool none_of(R range, P pred) {
2015-06-03 23:54:18 +02:00
for (; !range.empty(); range.pop_front())
if (pred(range.front())) return false;
return true;
}
2015-04-21 03:33:58 +02:00
2017-04-14 16:57:02 +02:00
/** @brief A pipeable version of ostd::none_of().
*
* The function is forwarded.
*/
template<typename F>
inline auto none_of(F &&func) {
2017-04-12 00:07:21 +02:00
return [func = std::forward<F>(func)](auto &obj) mutable {
return none_of(obj, std::forward<F>(func));
};
2016-04-26 23:25:46 +02:00
}
2017-04-14 17:30:21 +02:00
/** @brief Finds `v` in `range`.
*
* Iterates the range and as soon as `range.front()` is equal to `v`,
* returns `range`. The `range` is at least ostd::input_range_tag.
*
* @sse ostd::find_last(), ostd::find_if(), ostd::find_if_not(),
* ostd::find_one_of()
*/
2015-06-04 23:57:06 +02:00
template<typename R, typename T>
2016-06-23 20:18:35 +02:00
inline R find(R range, T const &v) {
for (; !range.empty(); range.pop_front()) {
if (range.front() == v) {
2015-06-03 23:54:18 +02:00
break;
}
}
2015-06-03 23:54:18 +02:00
return range;
}
2015-04-21 03:33:58 +02:00
2017-04-14 17:30:21 +02:00
/** @brief A pipeable version of ostd::find().
*
* The `v` is forwarded.
*/
template<typename T>
inline auto find(T &&v) {
2017-04-12 00:07:21 +02:00
return [v = std::forward<T>(v)](auto &obj) mutable {
return find(obj, std::forward<T>(v));
};
2016-04-26 23:25:46 +02:00
}
2017-04-14 17:30:21 +02:00
/** @brief Finds the last occurence of `v` in `range`.
*
* Keeps attempting ostd::find() from the point of previous ostd::find()
* until no next matching element is found. As this algorithm has to save
* the previous result of ostd::find() in case nothing is found next, this
* algortihm requires `range` to be at least ostd::forward_range_tag.
*
* @sse ostd::find(), ostd::find_if(), ostd::find_if_not(),
* ostd::find_one_of()
*/
2015-08-16 20:35:06 +02:00
template<typename R, typename T>
2016-06-23 20:18:35 +02:00
inline R find_last(R range, T const &v) {
2015-08-16 20:35:06 +02:00
range = find(range, v);
if (!range.empty()) {
for (;;) {
R prev = range;
prev.pop_front();
R r = find(prev, v);
if (r.empty()) {
break;
}
range = r;
}
2015-08-16 20:35:06 +02:00
}
return range;
}
2017-04-14 17:30:21 +02:00
/** @brief A pipeable version of ostd::find_last().
*
* The `v` is forwarded.
*/
template<typename T>
inline auto find_last(T &&v) {
2017-04-12 00:07:21 +02:00
return [v = std::forward<T>(v)](auto &obj) mutable {
return find_last(obj, std::forward<T>(v));
};
2016-04-26 23:25:46 +02:00
}
2017-04-14 17:30:21 +02:00
/** @brief Finds an element matching `pred` in `range`.
*
* Iterates the range and as soon as `pred(range.front())` is true,
* returns `range`. The `range` is at least ostd::input_range_tag.
*
* @sse ostd::find(), ostd::find_last(), ostd::find_if_not(),
* ostd::find_one_of()
*/
2015-06-04 23:57:06 +02:00
template<typename R, typename P>
2016-04-26 23:25:46 +02:00
inline R find_if(R range, P pred) {
for (; !range.empty(); range.pop_front()) {
if (pred(range.front())) {
2015-06-03 23:54:18 +02:00
break;
}
}
2015-06-03 23:54:18 +02:00
return range;
}
2015-04-21 03:33:58 +02:00
2017-04-14 17:30:21 +02:00
/** @brief A pipeable version of ostd::find_if().
*
* The `pred` is forwarded.
*/
template<typename P>
inline auto find_if(P &&pred) {
return [pred = std::forward<P>(pred)](auto &obj) mutable {
return find_if(obj, std::forward<P>(pred));
};
2016-04-26 23:25:46 +02:00
}
2017-04-14 17:30:21 +02:00
/** @brief Finds an element not matching `pred` in `range`.
*
* Iterates the range and as soon as `!pred(range.front())` is true,
* returns `range`. The `range` is at least ostd::input_range_tag.
*
* @sse ostd::find(), ostd::find_last(), ostd::find_if(), ostd::find_one_of()
*/
2015-06-04 23:57:06 +02:00
template<typename R, typename P>
2016-04-26 23:25:46 +02:00
inline R find_if_not(R range, P pred) {
for (; !range.empty(); range.pop_front()) {
if (!pred(range.front())) {
2015-06-03 23:54:18 +02:00
break;
}
}
2015-06-03 23:54:18 +02:00
return range;
}
2015-04-21 03:33:58 +02:00
2017-04-14 17:30:21 +02:00
/** @brief A pipeable version of ostd::find_if_not().
*
* The `pred` is forwarded.
*/
template<typename P>
inline auto find_if_not(P &&pred) {
return [pred = std::forward<P>(pred)](auto &obj) mutable {
return find_if_not(obj, std::forward<P>(pred));
};
2016-04-26 23:25:46 +02:00
}
2017-04-14 17:30:21 +02:00
/** @brief Finds the first element matching any element in `values`.
*
* The `compare` function is used to compare the values. The `range`
* is iterated and each item is compared with each item in `values`
* and once a match is found, `range` is returned.
*
* The `range` has to be at least ostd::input_iterator_tag as it's
* iterated only once, `values` has to be ostd::forward_range_tag or
* better.
*
* The time complexity is up to `N * M` where `N` is the length of
* `range` and `M` is the length of `values`.
*
* Use ostd::find_one_of() if you want to use the `==` operator
* instead of calling `compare`.
*
* @sse ostd::find(), ostd::find_last(), ostd::find_if(), ostd::find_if_not(),
* ostd::find_one_of()
*/
template<typename R1, typename R2, typename C>
inline R1 find_one_of_cmp(R1 range, R2 values, C compare) {
for (; !range.empty(); range.pop_front()) {
for (R2 rv = values; !rv.empty(); rv.pop_front()) {
if (compare(range.front(), rv.front())) {
return range;
}
}
}
return range;
}
2017-04-14 17:30:21 +02:00
/** @brief A pipeable version of ostd::find_one_of_cmp().
*
* The `values` and `compare` are forwarded.
*/
template<typename R, typename C>
inline auto find_one_of_cmp(R &&values, C &&compare) {
return [
2017-01-25 01:44:22 +01:00
values = std::forward<R>(values), compare = std::forward<C>(compare)
2017-04-12 00:07:21 +02:00
](auto &obj) mutable {
return find_one_of_cmp(
2017-04-12 00:07:21 +02:00
obj, std::forward<R>(values), std::forward<C>(compare)
);
};
}
2017-04-14 17:30:21 +02:00
/** @brief Finds the first element matching any element in `values`.
*
* The `==` operator is used to compare the values. The `range`
* is iterated and each item is compared with each item in `values`
* and once a match is found, `range` is returned.
*
* The `range` has to be at least ostd::input_iterator_tag as it's
* iterated only once, `values` has to be ostd::forward_range_tag or
* better.
*
* The time complexity is up to `N * M` where `N` is the length of
* `range` and `M` is the length of `values`.
*
* Use ostd::find_one_of_cmp() if you want to use a comparison
* function instead of the `==` operator.
*
* @sse ostd::find(), ostd::find_last(), ostd::find_if(), ostd::find_if_not(),
* ostd::find_one_of_cmp()
*/
template<typename R1, typename R2>
2016-04-26 23:25:46 +02:00
inline R1 find_one_of(R1 range, R2 values) {
for (; !range.empty(); range.pop_front()) {
for (R2 rv = values; !rv.empty(); rv.pop_front()) {
if (range.front() == rv.front()) {
return range;
}
}
}
return range;
}
template<typename R>
inline auto find_one_of(R &&values) {
2017-04-12 00:07:21 +02:00
return [values = std::forward<R>(values)](auto &obj) mutable {
return find_one_of(obj, std::forward<R>(values));
};
}
2015-06-04 23:57:06 +02:00
template<typename R, typename T>
inline range_size_t<R> count(R range, T const &v) {
range_size_t<R> ret = 0;
for (; !range.empty(); range.pop_front()) {
if (range.front() == v) {
2015-06-03 23:54:18 +02:00
++ret;
}
}
2015-06-03 23:54:18 +02:00
return ret;
}
2015-04-21 03:33:58 +02:00
template<typename T>
inline auto count(T &&v) {
2017-04-12 00:07:21 +02:00
return [v = std::forward<T>(v)](auto &obj) mutable {
return count(obj, std::forward<T>(v));
};
2016-04-26 23:25:46 +02:00
}
2015-06-04 23:57:06 +02:00
template<typename R, typename P>
inline range_size_t<R> count_if(R range, P pred) {
range_size_t<R> ret = 0;
for (; !range.empty(); range.pop_front()) {
if (pred(range.front())) {
2015-06-03 23:54:18 +02:00
++ret;
}
}
2015-06-03 23:54:18 +02:00
return ret;
}
2015-04-21 03:33:58 +02:00
template<typename F>
inline auto count_if(F &&func) {
2017-04-12 00:07:21 +02:00
return [func = std::forward<F>(func)](auto &obj) mutable {
return count_if(obj, std::forward<F>(func));
};
2016-04-26 23:25:46 +02:00
}
2015-06-04 23:57:06 +02:00
template<typename R, typename P>
inline range_size_t<R> count_if_not(R range, P pred) {
range_size_t<R> ret = 0;
for (; !range.empty(); range.pop_front()) {
if (!pred(range.front())) {
2015-06-03 23:54:18 +02:00
++ret;
}
}
2015-06-03 23:54:18 +02:00
return ret;
}
2015-04-24 19:16:35 +02:00
template<typename F>
inline auto count_if_not(F &&func) {
2017-04-12 00:07:21 +02:00
return [func = std::forward<F>(func)](auto &obj) mutable {
return count_if_not(obj, std::forward<F>(func));
};
2016-04-26 23:25:46 +02:00
}
2015-06-04 23:57:06 +02:00
template<typename R>
2016-04-26 23:25:46 +02:00
inline bool equal(R range1, R range2) {
2015-06-03 23:54:18 +02:00
for (; !range1.empty(); range1.pop_front()) {
if (range2.empty() || (range1.front() != range2.front())) {
2015-06-03 23:54:18 +02:00
return false;
}
2015-06-03 23:54:18 +02:00
range2.pop_front();
}
2015-06-03 23:54:18 +02:00
return range2.empty();
}
template<typename R>
inline auto equal(R &&range) {
2017-04-12 00:07:21 +02:00
return [range = std::forward<R>(range)](auto &obj) mutable {
return equal(obj, std::forward<R>(range));
};
2016-04-26 23:25:46 +02:00
}
2015-06-03 23:54:18 +02:00
/* algos that modify ranges or work with output ranges */
2015-06-04 23:57:06 +02:00
template<typename R1, typename R2>
2016-04-26 23:25:46 +02:00
inline R2 copy(R1 irange, R2 orange) {
range_put_all(orange, irange);
2015-06-03 23:54:18 +02:00
return orange;
}
2015-06-04 23:57:06 +02:00
template<typename R1, typename R2, typename P>
2016-04-26 23:25:46 +02:00
inline R2 copy_if(R1 irange, R2 orange, P pred) {
for (; !irange.empty(); irange.pop_front()) {
if (pred(irange.front())) {
2015-06-03 23:54:18 +02:00
orange.put(irange.front());
}
}
2015-06-03 23:54:18 +02:00
return orange;
}
2015-06-04 23:57:06 +02:00
template<typename R1, typename R2, typename P>
2016-04-26 23:25:46 +02:00
inline R2 copy_if_not(R1 irange, R2 orange, P pred) {
for (; !irange.empty(); irange.pop_front()) {
if (!pred(irange.front())) {
2015-06-03 23:54:18 +02:00
orange.put(irange.front());
}
}
2015-06-03 23:54:18 +02:00
return orange;
}
2015-06-04 23:57:06 +02:00
template<typename R1, typename R2>
2016-04-26 23:25:46 +02:00
inline R2 move(R1 irange, R2 orange) {
for (; !irange.empty(); irange.pop_front()) {
2017-01-25 01:44:22 +01:00
orange.put(std::move(irange.front()));
}
2015-06-03 23:54:18 +02:00
return orange;
}
2015-06-04 23:57:06 +02:00
template<typename R>
2016-04-26 23:25:46 +02:00
inline void reverse(R range) {
2015-06-03 23:54:18 +02:00
while (!range.empty()) {
2017-01-29 15:56:02 +01:00
using std::swap;
swap(range.front(), range.back());
2015-06-03 23:54:18 +02:00
range.pop_front();
range.pop_back();
}
2015-06-03 23:54:18 +02:00
}
2015-06-04 23:57:06 +02:00
template<typename R1, typename R2>
2016-04-26 23:25:46 +02:00
inline R2 reverse_copy(R1 irange, R2 orange) {
for (; !irange.empty(); irange.pop_back()) {
2015-06-03 23:54:18 +02:00
orange.put(irange.back());
}
2015-06-03 23:54:18 +02:00
return orange;
}
2015-06-04 23:57:06 +02:00
template<typename R, typename T>
2016-06-23 20:18:35 +02:00
inline void fill(R range, T const &v) {
for (; !range.empty(); range.pop_front()) {
2015-06-03 23:54:18 +02:00
range.front() = v;
}
2015-06-03 23:54:18 +02:00
}
2015-04-22 00:54:09 +02:00
2015-06-04 23:57:06 +02:00
template<typename R, typename F>
2016-04-26 23:25:46 +02:00
inline void generate(R range, F gen) {
for (; !range.empty(); range.pop_front()) {
2015-06-03 23:54:18 +02:00
range.front() = gen();
}
2015-06-03 23:54:18 +02:00
}
2015-05-23 19:44:06 +02:00
2015-06-04 23:57:06 +02:00
template<typename R1, typename R2>
inline std::pair<R1, R2> swap_ranges(R1 range1, R2 range2) {
2015-06-03 23:54:18 +02:00
while (!range1.empty() && !range2.empty()) {
2017-01-29 15:56:02 +01:00
using std::swap;
swap(range1.front(), range2.front());
2015-06-03 23:54:18 +02:00
range1.pop_front();
range2.pop_front();
2015-05-25 21:46:49 +02:00
}
return std::make_pair(range1, range2);
2015-06-03 23:54:18 +02:00
}
2015-05-25 21:46:49 +02:00
2015-06-04 23:57:06 +02:00
template<typename R, typename T>
2016-04-26 23:25:46 +02:00
inline void iota(R range, T value) {
for (; !range.empty(); range.pop_front()) {
2015-06-03 23:54:18 +02:00
range.front() = value++;
}
2015-06-03 23:54:18 +02:00
}
2015-05-25 21:46:49 +02:00
2015-06-04 23:57:06 +02:00
template<typename R, typename T>
2016-04-26 23:25:46 +02:00
inline T foldl(R range, T init) {
for (; !range.empty(); range.pop_front()) {
2015-06-03 23:54:18 +02:00
init = init + range.front();
}
2015-06-03 23:54:18 +02:00
return init;
}
2015-05-25 21:46:49 +02:00
2015-06-04 23:57:06 +02:00
template<typename R, typename T, typename F>
2016-04-29 18:47:04 +02:00
inline T foldl_f(R range, T init, F func) {
for (; !range.empty(); range.pop_front()) {
2015-06-03 23:54:18 +02:00
init = func(init, range.front());
}
2015-06-03 23:54:18 +02:00
return init;
}
2015-05-25 21:46:49 +02:00
template<typename T>
inline auto foldl(T &&init) {
2017-04-12 00:07:21 +02:00
return [init = std::forward<T>(init)](auto &obj) mutable {
return foldl(obj, std::forward<T>(init));
};
2016-04-26 23:25:46 +02:00
}
template<typename T, typename F>
inline auto foldl_f(T &&init, F &&func) {
return [
2017-01-25 01:44:22 +01:00
init = std::forward<T>(init), func = std::forward<F>(func)
2017-04-12 00:07:21 +02:00
](auto &obj) mutable {
return foldl_f(obj, std::forward<T>(init), std::forward<F>(func));
2016-04-29 18:47:04 +02:00
};
}
2016-04-26 23:25:46 +02:00
2015-06-04 23:57:06 +02:00
template<typename R, typename T>
2016-04-26 23:25:46 +02:00
inline T foldr(R range, T init) {
for (; !range.empty(); range.pop_back()) {
2015-06-03 23:54:18 +02:00
init = init + range.back();
}
2015-06-03 23:54:18 +02:00
return init;
}
2015-05-23 19:44:06 +02:00
2015-06-04 23:57:06 +02:00
template<typename R, typename T, typename F>
2016-04-29 18:47:04 +02:00
inline T foldr_f(R range, T init, F func) {
for (; !range.empty(); range.pop_back()) {
2015-06-03 23:54:18 +02:00
init = func(init, range.back());
}
2015-06-03 23:54:18 +02:00
return init;
}
template<typename T>
inline auto foldr(T &&init) {
2017-04-12 00:07:21 +02:00
return [init = std::forward<T>(init)](auto &obj) mutable {
return foldr(obj, std::forward<T>(init));
};
2016-04-26 23:25:46 +02:00
}
template<typename T, typename F>
inline auto foldr_f(T &&init, F &&func) {
return [
2017-01-25 01:44:22 +01:00
init = std::forward<T>(init), func = std::forward<F>(func)
2017-04-12 00:07:21 +02:00
](auto &obj) mutable {
return foldr_f(obj, std::forward<T>(init), std::forward<F>(func));
2016-04-29 18:47:04 +02:00
};
}
2016-04-26 23:25:46 +02:00
namespace detail {
template<typename T, typename F, typename R>
struct map_range: input_range<map_range<T, F, R>> {
using range_category = std::common_type_t<
range_category_t<T>, finite_random_access_range_tag
>;
2017-04-14 16:57:02 +02:00
using value_type = std::remove_reference_t<R>;
using reference = R;
using size_type = range_size_t<T>;
using difference_type = range_difference_t<T>;
private:
T p_range;
std::decay_t<F> p_func;
public:
map_range() = delete;
template<typename FF>
map_range(T const &range, FF &&func):
p_range(range), p_func(std::forward<FF>(func)) {}
bool empty() const { return p_range.empty(); }
size_type size() const { return p_range.size(); }
void pop_front() { p_range.pop_front(); }
void pop_back() { p_range.pop_back(); }
R front() const { return p_func(p_range.front()); }
R back() const { return p_func(p_range.back()); }
R operator[](size_type idx) const {
return p_func(p_range[idx]);
}
map_range slice(size_type start, size_type end) const {
return map_range(p_range.slice(start, end), p_func);
}
map_range slice(size_type start) const {
return slice(start, size());
}
};
template<typename R, typename F>
2017-01-29 15:29:11 +01:00
using MapReturnType = decltype(
std::declval<F>()(std::declval<range_reference_t<R>>())
2017-01-29 15:29:11 +01:00
);
} /* namespace detail */
2017-04-14 16:57:02 +02:00
/** @brief Gets a wrapper range that maps each item by `func`.
*
* The resulting range is at most ostd::finite_random_access_range_tag.
* There are no restrictions on the input range. The resulting range is
* not mutable, it's purely input-type.
*
* The `reference` member type of the range is `R` where `R` is the return
* value of `func`. The `value_type` is `std::remove_reference_t<R>`. The
* size and difference types are preserved.
*
* On each access of a range item (front, back, indexing), the `func` is
* called with the actual wrapped range's item and the result is returned.
*
* An example:
*
* ~~~{.cc}
* auto r = ostd::map(ostd::range(5), [](auto v) { return v + 5; });
* for (auto i: r) {
* ostd::writeln(i); // 5, 6, 7, 8, 9
* }
* ~~~
*
* Because the range is lazy, a new value is computed on each access, but
* the wrapper range also needs no memory of its own.
*
* @see ostd::filter()
*/
2015-06-04 23:57:06 +02:00
template<typename R, typename F>
inline auto map(R range, F func) {
return detail::map_range<R, F, detail::MapReturnType<R, F>>(
range, std::move(func)
);
2015-06-03 23:54:18 +02:00
}
2017-04-14 16:57:02 +02:00
/** @brief A pipeable version of ostd::map().
*
* The `func` is forwarded.
*/
template<typename F>
inline auto map(F &&func) {
2017-04-12 00:07:21 +02:00
return [func = std::forward<F>(func)](auto &obj) mutable {
return map(obj, std::forward<F>(func));
};
}
namespace detail {
template<typename T, typename F>
struct filter_range: input_range<filter_range<T, F>> {
using range_category = std::common_type_t<
range_category_t<T>, forward_range_tag
>;
using value_type = range_value_t<T>;
using reference = range_reference_t<T>;
using size_type = range_size_t<T>;
using difference_type = range_difference_t<T>;
private:
T p_range;
std::decay_t<F> p_pred;
void advance_valid() {
while (!p_range.empty() && !p_pred(front())) {
p_range.pop_front();
}
}
public:
filter_range() = delete;
template<typename P>
filter_range(T const &range, P &&pred):
p_range(range), p_pred(std::forward<P>(pred))
{
advance_valid();
}
2015-06-03 23:54:18 +02:00
bool empty() const { return p_range.empty(); }
void pop_front() {
p_range.pop_front();
advance_valid();
}
2015-06-03 23:54:18 +02:00
range_reference_t<T> front() const { return p_range.front(); }
};
2015-06-03 23:54:18 +02:00
template<typename R, typename P>
2017-02-09 20:56:15 +01:00
using FilterPred = std::enable_if_t<std::is_same_v<
decltype(std::declval<P>()(std::declval<range_reference_t<R>>())), bool
2017-01-29 15:29:11 +01:00
>, P>;
} /* namespace detail */
2017-04-14 16:57:02 +02:00
/** @brief Gets a wrapper range that filters items by `pred`.
*
* The resulting range is ostd::forward_range_tag at most. The range will
* only contains items for which `pred` returns true. What this means is
* that upon creation, the given range is stored and all iterated until
* an item matching `pred` is reached. On `pop_front()`, this item is
* popped out and the same is done, i.e. again iterated until a new
* item matching `pred` is reached.
*
* In other words, this is done:
*
* ~~~{.cc}
* void advance(R &range, P &pred) {
* while (!range.empty() && !pred()) {
* range.pop_front();
* }
* }
*
* constructor(R range, P pred) {
* store_range_and_pred(range, pred);
* advance(stored_range, stored_pred);
* }
*
* void pop_front() {
* stored_range.pop_front();
* advance(stored_range, stored_pred);
* }
* ~~~
*
* The value, reference, size and difference types are preserved, as are
* calls to `front()` and `empty()`.
*
* @see ostd::map()
*/
2015-06-04 23:57:06 +02:00
template<typename R, typename P>
inline auto filter(R range, P pred) {
return detail::filter_range<R, P>(range, std::move(pred));
}
2017-04-14 16:57:02 +02:00
/** @brief A pipeable version of ostd::filter().
*
* The `pred` is forwarded.
*/
template<typename P>
inline auto filter(P &&pred) {
return [pred = std::forward<P>(pred)](auto &obj) mutable {
return filter(obj, std::forward<P>(pred));
};
}
2015-07-13 21:07:14 +02:00
} /* namespace ostd */
2015-06-03 23:54:18 +02:00
2016-02-07 22:17:15 +01:00
#endif