/* ***************************************************************************** * kblib is a general utility library for C++14 and C++17, intended to provide * performant high-level abstractions and more expressive ways to do simple * things. * * Copyright (c) 2021 killerbee * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * ****************************************************************************/ /** * @file * @brief Provides general-purpose algorithms, similar to the * header. * * @author killerbee * @date 2019-2021 * @copyright GNU General Public Licence v3.0 */ #ifndef ALGORITHM_H #define ALGORITHM_H #include "tdecl.h" #include "iterators.h" #include "traits.h" #include #include #include namespace KBLIB_NS { /** * @brief Invoke a function N times. * * @param N The number of times to invoke func. * @param func The function to invoke. */ template constexpr auto repeat(std::size_t N, Callable func) noexcept(noexcept(func())) -> return_assert_t::value, void> { for (std::size_t I = 0; I != N; ++I) { func(); } return; } /** * @brief Abbreviation of the erase-remove idiom as a free function. * * @param c The container to erase from. * @param val The value to remove. */ template constexpr auto erase(Container& c, const Elem& val) noexcept( noexcept(c.erase(std::remove(c.begin(), c.end(), val), c.end()))) -> void { c.erase(std::remove(c.begin(), c.end(), val), c.end()); return; } /** * @brief Abbreviation of the erase-remove idiom as a free function. * * @param c The container to erase from. * @param p Erase all elements on which p returns true. */ template constexpr auto erase_if(Container& c, UnaryPredicate p) noexcept( noexcept(c.erase(std::remove_if(c.begin(), c.end(), std::ref(p)), c.end()))) -> void { c.erase(std::remove_if(c.begin(), c.end(), std::ref(p)), c.end()); return; } /** * @brief Synthesize an equivalence relation from <. * * @return bool Whether a is equivalent under < to b. */ template KBLIB_NODISCARD constexpr auto equals(const Obj& a, const Obj& b) noexcept(noexcept(a < b)) -> bool { return not (a < b) and not (b < a); } /** * @brief Synthesize an equivalence relation from comp. * * @return bool Whether a is equivalent under comp to b. */ template KBLIB_NODISCARD constexpr auto equals(const Obj& a, const Obj& b, Compare comp) noexcept(noexcept(comp(a, b))) -> bool { return not comp(a, b) and not comp(b, a); } /** * @brief A function object implementing the equivalence relationship over a * comparison predicate. * * @tparam Compare The predicate to compare over. If void, use the < operator. * (Equivalent to defaulting to std::less.) * @tparam Obj The type of objects to compare. If void, the operator() is a * template. */ template struct equivalent { KBLIB_NODISCARD constexpr auto operator()(const Obj& a, const Obj& b, Compare comp) const noexcept(noexcept(equals(a, b, comp))) -> bool { return equals(a, b, comp); } }; template struct equivalent { KBLIB_NODISCARD constexpr auto operator()(const Obj& a, const Obj& b) const noexcept(noexcept(equals(a, b))) -> bool { return equals(a, b); } }; template struct equivalent { template KBLIB_NODISCARD constexpr auto operator()(const Obj& a, const Obj& b, Compare comp) const noexcept(noexcept(equals(a, b, comp))) -> bool { return equals(a, b, comp); } }; template <> struct equivalent { template KBLIB_NODISCARD constexpr auto operator()(const Obj& a, const Obj& b) const noexcept(noexcept(equals(a, b))) -> bool { return equals(a, b); } }; /** * @brief A constexpr version of std::accumulate */ template KBLIB_NODISCARD constexpr auto accumulate(InputIt first, InputIt last, T init) -> T { for (; first != last; ++first) { init = std::move(init) + *first; } return init; } /** * @brief A constexpr version of std::accumulate */ template KBLIB_NODISCARD constexpr auto accumulate(InputIt first, InputIt last, T init, BinaryOperation op) -> T { for (; first != last; ++first) { init = op(std::move(init), *first); } return init; } /** * @brief Sum a range * * Convenience wrapper for std::accumulate. For an empty range, returns a * value-initialized temporary (usually 0). Deduces the correct type for the * initializer, which reduces risk of truncation and incorrect results. * * @param[in] first Beginning of range * @param[in] last End of range * @return The sum of the input range. */ template KBLIB_NODISCARD constexpr auto sum(InputIt first, InputIt last) -> std::decay_t { if (first == last) { return {}; } auto init = *first++; return kblib::accumulate(first, last, std::move(init)); } /** * @brief Fold a range over an operation. * * Convenience wrapper for std::accumulate. For an empty range, returns a * value-initialized temporary (usually 0). Deduces the correct type for the * initializer, which reduces risk of truncation and incorrect results. * * @param[in] first Beginning of range * @param[in] last End of range * @param[in] op The fold operation * @return The sum of the input range. */ template KBLIB_NODISCARD constexpr auto sum(InputIt first, InputIt last, BinaryOperation op) -> std::decay_t { if (first == last) { return {}; } auto init = *first++; return kblib::accumulate(first, last, std::move(init), op); } /** * @brief Sum a range * * Convenience wrapper for std::accumulate. For an empty range, returns a * value-initialized temporary (usually 0). Deduces the correct type for the * initializer, which reduces risk of truncation and incorrect results. * * @param[in] r The range to sum * @return The sum of the input range. */ template KBLIB_NODISCARD constexpr auto sum(Range&& r) -> auto { using std::begin; using std::end; auto first = begin(r); auto last = end(r); if (first == last) { return std::decay_t{}; } auto init = *first++; return kblib::accumulate(first, last, std::move(init)); } template constexpr auto transform_exclusive_scan(InputIt first, EndIt last, OutputIt d_first, T init, BinaryAccumulation accum, UnaryTransform proj) -> OutputIt { while (first != last) { *d_first++ = kblib::exchange(init, accum(std::move(init), proj(*first++))); } return d_first; } #if 0 template KBLIB_NODISCARD constexpr auto adjacent_reduce(InputIt begin, InputIt begin1, InputIt end, BinaryAccumulation acc, BinaryTransform op) {} template KBLIB_NODISCARD constexpr auto adjacent_transform(InputIt begin, InputIt begin1, InputIt end, BinaryTransform op) {} template KBLIB_NODISCARD constexpr auto adjacent_inclusive_scan(InputIt begin, InputIt begin1, InputIt end, BinaryAccumulation acc, BinaryTransform op) {} #endif /** * @brief Finds a value in range [begin, end). If not found, returns end. It * also allows for a sentinel end iterator. * * @param begin,end The range to search in * @param value The value to search for * @return It Either the position of the found value, or end if not found */ template KBLIB_NODISCARD constexpr auto find( ForwardIt begin, EndIt end, const Elem& value) noexcept(noexcept(*begin == value)) -> ForwardIt { while (begin != end and *begin != value) { ++begin; } return begin; } /** * @brief Finds a value in range [begin, end). If not found, returns end. It * also allows for a sentinel end iterator. * * @param begin,end The range to search in * @param value The value to search for * @param comp The comparison function * @return It Either the position of the found value, or end if not found */ template KBLIB_NODISCARD constexpr auto find( ForwardIt begin, EndIt end, const Elem& value, Comp&& comp) noexcept(noexcept(comp(*begin, value))) -> ForwardIt { while (begin != end and not equals(*begin, value, comp)) { ++begin; } return begin; } /** * @brief Finds the first value in range [begin, end) for which pred returns * true. If not found, returns end. It also allows for a sentinel end iterator. * * @param begin,end The range to search in * @param pred The predicate to scan with * @return It Either the position of the found value, or end if not found */ template KBLIB_NODISCARD constexpr auto find_if( ForwardIt begin, EndIt end, UnaryPredicate&& pred) noexcept(noexcept(kblib::invoke(pred, *begin))) -> ForwardIt { while (begin != end and not kblib::invoke(pred, *begin)) { ++begin; } return begin; } /** * @brief Finds the first value in range [begin, end) for which pred returns * false. If not found, returns end. It also allows for a sentinel end iterator. * * @param begin,end The range to search in * @param pred The predicate to scan with * @return It Either the position of the found value, or end if not found */ template KBLIB_NODISCARD constexpr auto find_if_not( ForwardIt begin, EndIt end, UnaryPredicate&& pred) noexcept(noexcept(kblib::invoke(pred, *begin))) -> ForwardIt { while (begin != end and kblib::invoke(pred, *begin)) { ++begin; } return begin; } /** * @brief Searches a range for the last occurence of a match, and returns an * iterator to it. It also allows for a sentinel end iterator. * * @param begin,end The range to search in * @param value The value to find * @return It Iterator to the last element equal to v, or end if no such * element. */ template KBLIB_NODISCARD constexpr auto find_last( ForwardIt begin, EndIt end, const Elem& value) noexcept(noexcept(*begin == value)) -> ForwardIt { if (begin == end) { return begin; } auto result = end; while (true) { auto new_result = kblib::find(begin, end, value); if (new_result == end) { break; } else { result = new_result; begin = result; ++begin; } } return result; } /** * @brief Searches a range for the last element on which a predicate returns * true. It also allows for a sentinel end iterator. * * @param begin,end The range to search in * @param pred The predicate for comparison * @return It Iterator to the last element for which p returned true, or end if * no such element. */ template KBLIB_NODISCARD constexpr auto find_last_if( ForwardIt begin, EndIt end, UnaryPredicate pred) noexcept(noexcept(kblib::invoke(pred, *begin))) -> ForwardIt { if (begin == end) { return begin; } auto result = end; while (true) { auto new_result = kblib::find_if(begin, end, pred); if (new_result == end) { break; } else { result = new_result; begin = result; ++begin; } } return result; } /** * @brief Searches a range for the last element on which a predicate returns * false. It also allows for a sentinel end iterator. * * @param begin,end The range to search in * @param pred The predicate for comparison * @return It Iterator to the last element for which p returned false, or end if * no such element. */ template KBLIB_NODISCARD constexpr auto find_last_if_not( ForwardIt begin, EndIt end, UnaryPredicate pred) noexcept(noexcept(kblib::invoke(pred, *begin))) -> ForwardIt { if (begin == end) { return begin; } auto result = end; while (true) { auto new_result = kblib::find_if_not(begin, end, pred); if (new_result == end) { break; } else { result = new_result; begin = result; ++begin; } } return result; } /** * @brief Find the offset of the first ocurrence of v in a range from the * beginning. It also allows for a sentinel end iterator. * * @param begin,end The range to search in * @param value The value to search for. * @return size_t The offset from begin of the first element equal to v, or * distance(begin, end) if not found. */ template KBLIB_NODISCARD constexpr auto find_in( ForwardIt begin, EndIt end, const Elem& value) noexcept(noexcept(*begin == value)) -> size_t { return to_unsigned(kblib::find(begin, end, value) - begin); } /** * @brief Find the offset of the first element for which p returns true. It also * allows for a sentinel end iterator. * * @param begin,end The range to search in * @param pred The predicate to check. * @return size_t The offset from begin of the element found, or distance(begin, * end) if not. */ template KBLIB_NODISCARD constexpr auto find_in_if( ForwardIt begin, EndIt end, UnaryPredicate pred) noexcept(noexcept(kblib::invoke(pred, *begin))) -> size_t { return to_unsigned(kblib::find_if(begin, end, pred) - begin); } /** * @brief Find the offset of the first element for which p returns false. It * also allows for a sentinel end iterator. * * @param begin,end The range to search in * @param pred The predicate to check. * @return size_t The offset from begin of the element found, or distance(begin, * end) if not. */ template KBLIB_NODISCARD constexpr auto find_in_if_not( ForwardIt begin, EndIt end, UnaryPredicate pred) noexcept(noexcept(kblib::invoke(pred, *begin))) -> size_t { return to_unsigned(kblib::find_if_not(begin, end, pred) - begin); } // find_last_in: // 1. Finds last v in range [begin, end) and returns the offset from begin /** * @brief Find the offset of the last ocurrence of v in a range from the * beginning. It also allows for a sentinel end iterator. * * @param begin,end The range to search in * @param value The value to search for. * @return size_t The offset from begin of the element found, or distance(begin, * end) if not. */ template KBLIB_NODISCARD constexpr auto find_last_in( ForwardIt begin, EndIt end, const Elem& value) noexcept(noexcept(*begin == value)) -> size_t { return to_unsigned(kblib::find_last(begin, end, value) - begin); } /** * @param begin,end The range to search in * * @param begin,end The range to search in * @param pred The predicate to check. * @return size_t The offset from begin of the element found, or distance(begin, * end) if not. */ template KBLIB_NODISCARD constexpr auto find_last_in_if( ForwardIt begin, EndIt end, UnaryPredicate pred) noexcept(noexcept(kblib::invoke(pred, *begin))) -> size_t { return to_unsigned(kblib::find_last_if(begin, end, pred) - begin); } /** * @brief Find the offset of the last element for which p returns false. It also * allows for a sentinel end iterator. * * @param begin,end The range to search in * @param pred The predicate to check. * @return size_t The offset from begin of the element found, or distance(begin, * end) if not. */ template KBLIB_NODISCARD constexpr auto find_last_in_if_not( ForwardIt begin, EndIt end, UnaryPredicate pred) noexcept(noexcept(kblib::invoke(pred, *begin))) -> size_t { return to_unsigned(kblib::find_last_if_not(begin, end, pred) - begin); } /** * @brief Find the first element in c equal to v and return the position. * * Equivalent to find_in(begin(c), end(c), v) * * @param c The container to search. * @param value The value to search for. * @return size_t The position of the element found, or size(c) if not. */ template KBLIB_NODISCARD constexpr auto find_in(const Container& c, const T& value) noexcept( noexcept(*std::declval&>() == value)) -> size_t { using std::begin; using std::end; return to_unsigned(kblib::find(begin(c), end(c), value) - begin(c)); } #if 0 template KBLIB_NODISCARD constexpr auto find_in(ExecutionPolicy&& policy, const Container& c, const T& v) -> size_t { return to_unsigned(std::find(policy, std::begin(c), std::end(c), v) - std::begin(c)); } #endif /** * @brief Find the first element in c for which p returns true and return the * position. * * Equivalent to find_in_if(begin(c), end(c), p) * * @param c The container to search in. * @param pred The predicate to check. * @return size_t The position of the element found, or size(c) if not. */ template KBLIB_NODISCARD constexpr auto find_in_if( const Container& c, UnaryPredicate pred) noexcept(noexcept(kblib::invoke(pred, *std::declval&>()))) -> size_t { using std::begin; using std::end; return to_unsigned(kblib::find_if(begin(c), end(c), pred) - begin(c)); } /** * @brief Find the first element in c for which p returns false and return the * position. * * Equivalent to find_in_if_not(begin(c), end(c), p) * * @param c The container to search in. * @param pred The predicate to check. * @return size_t The position of the element found, or size(c) if not. */ template KBLIB_NODISCARD constexpr auto find_in_if_not( const Container& c, UnaryPredicate pred) noexcept(noexcept(kblib::invoke(pred, *std::declval&>()))) -> size_t { using std::begin; using std::end; return to_unsigned(kblib::find_if_not(begin(c), end(c), pred) - begin(c)); } #if 0 template KBLIB_NODISCARD constexpr auto find_in_if(ExecutionPolicy&& policy, const Container& c, UnaryPredicate p) -> size_t { return std::find_if(policy, std::begin(c), std::end(c), p) - std::begin(c); } template KBLIB_NODISCARD constexpr auto find_in_if_not(ExecutionPolicy&& policy, const Container& c, UnaryPredicate p) -> size_t { return std::find_if_not(policy, std::begin(c), std::end(c), p) - std::begin(c); } #endif /** * @brief Find the last element in c equal to v and return the position. * * Equivalent to find_last_in(std::begin(c), std::end(c), v) * * @param c The container to search. * @param value The value to search for. * @return size_t The position of the element found, or c.size() if not. */ template KBLIB_NODISCARD constexpr auto find_last_in(const Container& c, const T& value) noexcept( noexcept(*std::declval&>() == value)) -> size_t { using std::begin; using std::end; return to_unsigned(kblib::find_last(begin(c), end(c), value) - begin(c)); } /** * @brief Find the last element in c for which p returns true and return the * position. * * Equivalent to find_last_in_if(std::begin(c), std::end(c), p) * * @param c The container to search in. * @param pred The predicate to check. * @return size_t The position of the element found, or c.size() if not. */ template KBLIB_NODISCARD constexpr auto find_last_in_if( const Container& c, UnaryPredicate pred) noexcept(noexcept(kblib::invoke(pred, *std::declval&>()))) -> size_t { using std::begin; using std::end; return to_unsigned(kblib::find_last_if(begin(c), end(c), pred) - begin(c)); } /** * @brief Find the last element in c for which p returns true and return the * position. * * Equivalent to find_last_in_if_not(std::begin(c), std::end(c), p) * * @param c The container to search in. * @param pred The predicate to check. * @return size_t The position of the element found, or c.size() if not. */ template KBLIB_NODISCARD constexpr auto find_last_in_if_not( const Container& c, UnaryPredicate pred) noexcept(noexcept(kblib::invoke(pred, *std::declval&>()))) -> size_t { using std::begin; using std::end; return to_unsigned(kblib::find_last_if_not(begin(c), end(c), pred) - begin(c)); } template > KBLIB_NODISCARD constexpr auto find_match(InputIt1 begin1, EndIt1 end1, InputIt2 begin2, BinaryPredicate cmp) -> enable_if_t::value and is_input_iterator::value and is_invocable::value, std::pair> { while (begin1 != end1) { if (kblib::invoke(cmp, *begin1++, *begin2++)) { return std::make_pair(begin1, begin2); } } return std::make_pair(begin1, begin2); } template > KBLIB_NODISCARD constexpr auto find_match(InputIt1 begin1, EndIt1 end1, InputIt2 begin2, EndIt2 end2, BinaryPredicate cmp) -> enable_if_t::value and is_input_iterator::value and is_invocable::value, std::pair> { while (begin1 != end1 and begin2 != end2) { if (kblib::invoke(cmp, *begin1++, *begin2++)) { return std::make_pair(begin1, begin2); } } return std::make_pair(begin1, begin2); } /** * @brief Checks if a given range starts with a particular subrange. */ template KBLIB_NODISCARD constexpr auto starts_with(InputIt1 begin1, EndIt1 end1, InputIt2 begin2, EndIt2 end2, BinaryPred pred) -> enable_if_t< (is_input_iterator_v and is_input_iterator_v) // and not (is_random_access_iterator_v< InputIt1> and is_random_access_iterator_v), bool> { while (begin1 != end1 and begin2 != end2) { if (not kblib::invoke(pred, *begin1++, *begin2++)) { return false; } } return begin2 == end2; } /** * @brief Checks if a given range starts with a particular subrange. */ template > KBLIB_NODISCARD constexpr auto starts_with(RandomAccessIt1 begin1, RandomAccessIt1 end1, RandomAccessIt2 begin2, RandomAccessIt2 end2, BinaryPred pred = {}) -> enable_if_t< is_random_access_iterator_v< RandomAccessIt1> and is_random_access_iterator_v, bool> { if (end2 - begin2 > end1 - begin1) { return false; } else { auto N = end2 - begin2; return kblib::equal(begin1, begin1 + N, begin2, pred); } } /** * @brief Checks if a given range ends with a particular subrange. */ template > KBLIB_NODISCARD constexpr auto ends_with(BidirIt1 begin1, BidirIt1 end1, BidirIt2 begin2, BidirIt2 end2, BinaryPred pred = {}) -> enable_if_t< (is_bidirectional_iterator_v // and is_bidirectional_iterator_v) // and not (is_random_access_iterator_v< BidirIt1> and is_random_access_iterator_v), bool> { while (begin1 != end1 and begin2 != end2) { if (not kblib::invoke(pred, *--end1, *--end2)) { return false; } } return begin2 == end2; } /** * @brief Checks if a given range ends with a particular subrange. */ template > KBLIB_NODISCARD constexpr auto ends_with(RandomAccessIt1 begin1, RandomAccessIt1 end1, RandomAccessIt2 begin2, RandomAccessIt2 end2, BinaryPred pred = {}) -> enable_if_t< is_random_access_iterator_v< RandomAccessIt1> and is_random_access_iterator_v, bool> { if (end2 - begin2 > end1 - begin1) { return false; } else { auto N = end2 - begin2; return kblib::equal(end1 - N, end1, begin2, pred); } } template KBLIB_NODISCARD constexpr auto first_result(InputIt begin, EndIt end, T def, UnaryTransform op) -> enable_if_t::value, std::decay_t> { for (; begin != end; ++begin) { auto cur = op(*begin); if (cur != def) { return cur; } } return def; } template KBLIB_NODISCARD constexpr auto first_result(InputIt1 begin1, EndIt1 end1, InputIt2 begin2, T def, BinaryTransform op) -> enable_if_t::value and is_input_iterator::value, std::decay_t> { for (; begin1 != end1; ++begin1, ++begin2) { auto cur = op(*begin1, *begin2); if (cur != def) { return cur; } } return def; } template KBLIB_NODISCARD constexpr auto first_result(InputIt1 begin1, EndIt1 end1, InputIt2 begin2, EndIt2 end2, T def, BinaryTransform op) -> enable_if_t::value and is_input_iterator::value, std::decay_t> { for (; begin1 != end1 and begin2 != end2; ++begin1, ++begin2) { auto cur = op(*begin1, *begin2); if (cur != def) { return cur; } } return def; } template KBLIB_NODISCARD constexpr auto first_result_if(InputIt begin, EndIt end, T def, UnaryTransform op, UnaryPredicate ch) -> enable_if_t::value, decltype(op(*begin))> { for (; begin != end; ++begin) { if (ch(*begin)) { return op(*begin); } } return def; } template KBLIB_NODISCARD constexpr auto first_result_if(InputIt1 begin1, EndIt1 end1, InputIt2 begin2, T def, BinaryTransform op, BinaryPredicate ch) -> enable_if_t::value and is_input_iterator::value, decltype(op(*begin1, *begin2))> { for (; begin1 != end1; ++begin1, ++begin2) { if (ch(*begin1, *begin2)) { return op(*begin1, *begin2); } } return def; } template KBLIB_NODISCARD constexpr auto first_result_if(InputIt1 begin1, EndIt1 end1, InputIt2 begin2, EndIt2 end2, T def, BinaryTransform op, BinaryPredicate ch) -> enable_if_t::value and is_input_iterator::value, decltype(op(*begin1, *begin2))> { for (; begin1 != end1 and begin2 != end2; ++begin1, ++begin2) { if (ch(*begin1, *begin2)) { return op(*begin1, *begin2); } } return def; } #if KBLIB_USE_CXX17 template struct is_optional : std::false_type {}; template struct is_optional> : std::true_type {}; template KBLIB_NODISCARD constexpr auto first_result_opt(InputIt begin, EndIt end, T def, UnaryTransform op) -> enable_if_t::value, std::decay_t> { for (; begin != end; ++begin) { auto cur = op(*begin); if (cur) { return cur; } } return def; } template KBLIB_NODISCARD constexpr auto first_result_opt(InputIt1 begin1, EndIt1 end1, InputIt2 begin2, T def, BinaryTransform op) -> enable_if_t::value and is_input_iterator::value, std::decay_t> { for (; begin1 != end1; ++begin1, ++begin2) { auto cur = op(*begin1, *begin2); if (cur) { return cur; } } return def; } template KBLIB_NODISCARD constexpr auto first_result_opt(InputIt1 begin1, EndIt1 end1, InputIt2 begin2, EndIt2 end2, T def, BinaryTransform op) -> enable_if_t::value and is_input_iterator::value, std::decay_t> { for (; begin1 != end1 and begin2 != end2; ++begin1, ++begin2) { auto cur = op(*begin1, *begin2); if (cur) { return cur; } } return def; } #endif /** * @brief Determine if pred is true for every element of the range. */ template KBLIB_NODISCARD constexpr auto all_of(InputIt begin, InputIt end, UnaryPredicate pred) -> enable_if_t::value, bool> { for (; begin != end; ++begin) { if (not kblib::invoke(pred, *begin)) { return false; } } return true; } /** * @brief Determine if pred is true for every element of the range. */ template KBLIB_NODISCARD constexpr auto all_of(Range&& rng, UnaryPredicate pred) -> enable_if_t::value, bool> { for (auto&& el : std::forward(rng)) { if (not kblib::invoke(pred, static_cast(el))) { return false; } } return true; } /** * @brief Determine if pred is false for every element of the range. */ template KBLIB_NODISCARD constexpr auto none_of(InputIt begin, InputIt end, UnaryPredicate pred) -> enable_if_t::value, bool> { for (; begin != end; ++begin) { if (kblib::invoke(pred, *begin)) { return false; } } return true; } /** * @brief Determine if pred is true for every element of the range. */ template KBLIB_NODISCARD constexpr auto none_of(Range&& rng, UnaryPredicate pred) -> enable_if_t::value, bool> { for (auto&& el : std::forward(rng)) { if (kblib::invoke(pred, static_cast(el))) { return false; } } return true; } /** * @brief Determine if pred is true for at least one element of the range. */ template KBLIB_NODISCARD constexpr auto any_of(InputIt begin, InputIt end, UnaryPredicate pred) -> enable_if_t::value, bool> { for (; begin != end; ++begin) { if (kblib::invoke(pred, *begin)) { return true; } } return false; } /** * @brief Determine if pred is true for every element of the range. */ template KBLIB_NODISCARD constexpr auto any_of(Range&& rng, UnaryPredicate pred) -> enable_if_t::value, bool> { for (auto&& el : std::forward(rng)) { if (kblib::invoke(pred, static_cast(el))) { return true; } } return false; } /** * @brief Determine if a range contains a value. */ template KBLIB_NODISCARD constexpr auto contains( InputIt begin, InputIt end, const Value& val) noexcept(noexcept(*begin == val)) -> enable_if_t::value, bool> { return kblib::any_of(begin, end, [&](const auto& e) { return e == val; }); } /** * @brief Determine if a range contains a value. */ template KBLIB_NODISCARD constexpr auto contains( const Set& set, const Value& val) noexcept(noexcept(*std::declval&>() == val)) -> enable_if_t::value, bool> { using std::begin; using std::end; return kblib::any_of(begin(set), end(set), [&](const auto& e) { return e == val; }); } template KBLIB_NODISCARD constexpr auto contains_any(InputIt1 begin, InputIt1 end, InputIt2 n_begin, InputIt2 n_end) -> enable_if_t::value and is_input_iterator::value, bool> { return kblib::any_of(begin, end, [=](const auto& v) { return kblib::contains(n_begin, n_end, v); }); } template KBLIB_NODISCARD constexpr auto contains_any(InputIt begin, InputIt end, Range2&& needle) -> enable_if_t::value and is_iterable::value, bool> { return kblib::any_of(begin, end, [&needle](const auto& v) { return kblib::contains(needle, v); }); } template KBLIB_NODISCARD constexpr auto contains_any(Range1&& haystack, Range2&& needle) -> enable_if_t::value and is_iterable::value, bool> { using std::begin; using std::end; return kblib::any_of( begin(haystack), end(haystack), [&needle](const auto& v) { return kblib::contains(needle, v); }); } template > constexpr auto max_element(ForwardIt first, EndIt last, Compare comp = {}) -> ForwardIt { if (first == last) { return first; } ForwardIt largest = first; ++first; for (; first != last; ++first) { if (comp(*largest, *first)) { largest = first; } } return largest; } /** * @brief Returns a container of the greatest count elements according to cmp of * the range [first, last), in arbitrary order. This overload works for linear * containers. * * This function is included because its performance is sometimes better than * the new version, and additionally, it does not rely on * default-constructibility for the value type. * * @attention The returned container will not be sorted, unless it is something * like std::multiset which will use the other overload. * * @param first The beginning of the range. * @param last One past the end of the range. * @param count The number of elements to copy out of the range. * @param cmp The comparison function to use. * @return SequenceContainer The greatest count elements of the range, in * arbitrary order. */ template , typename It, enable_if_t, int> = 0> KBLIB_NODISCARD constexpr auto get_max_n_old(It first, It last, std::size_t count, Comp cmp = {}) -> SequenceContainer { using std::begin; using std::end; assert(first + count <= last); SequenceContainer c{first, first + count}; std::for_each(first + count, last, [&](const auto& v) { auto& min_v = *std::min_element(begin(c), end(c), cmp); if (kblib::invoke(cmp, min_v, v)) { min_v = v; } }); return c; } /** * @brief Returns a container of the greatest count elements according to cmp of * the range [first, last). This overload works for set-like types. * * This function is included because its performance is sometimes better than * the new version, and additionally, it does not rely on * default-constructibility for the value type. * * @param first The beginning of the range. * @param first One past the end of the range. * @param count The number of elements to copy out of the range. * @param cmp The comparison function to use. * @return SetlikeContainer The greatest count elements of the range, in arbitrary * order. */ template , typename It, enable_if_t, int> = 0> KBLIB_NODISCARD constexpr auto get_max_n_old(It first, It last, std::size_t count, Comp cmp = {}) -> SetlikeContainer { auto temp = get_max_n_old>>( first, last, count, cmp); return SetlikeContainer{std::make_move_iterator(temp.begin()), std::make_move_iterator(temp.end())}; } /** * @brief Returns a container of the greatest count elements according to cmp of * the range [first, last), in descending order. This overload works for linear * containers. * * @param first The beginning of the range. * @param last One past the end of the range. * @param count The number of elements to copy out of the range. * @param cmp The comparison function to use. * @return SequenceContainer The greatest count elements of the range, in * arbitrary order. */ template , typename It, enable_if_t, int> = 0> KBLIB_NODISCARD constexpr auto get_max_n(It first, It last, std::size_t count, Comp cmp = {}) -> SequenceContainer { using std::begin; using std::end; SequenceContainer c(count); std::partial_sort_copy(first, last, begin(c), end(c), [&](auto&& a, auto&& b) -> decltype(auto) { return kblib::invoke(cmp, std::forward(b), std::forward(a)); }); return c; } /** * @brief Returns a container of the greatest count elements according to cmp of * the range [first, last). This overload works for set-like containers. * * @param first The beginning of the range. * @param last One past the end of the range. * @param count The number of elements to copy out of the range. * @param cmp The comparison function to use. * @return SetlikeContainer The greatest count elements of the range, in * arbitrary order. */ template , typename It, enable_if_t, int> = 0> KBLIB_NODISCARD constexpr auto get_max_n(It first, It last, std::size_t count, Comp cmp = {}) -> SetlikeContainer { auto temp = get_max_n>>( first, last, count, cmp); return SetlikeContainer{std::make_move_iterator(temp.begin()), std::make_move_iterator(temp.end())}; } /** * @brief Copies the count greatest elements according to cmp of the range * [first, last) to the range beginning at d_begin. * * Note that this function uses O(count) extra memory to store a mutable range * for sorting. Directly calling std::partial_sort_copy with a properly sized * container will be more efficient than this function because it avoids * allocating extra working memory. This function should rather be used for * non-random-access output ranges. * * @param first The beginning of the range. * @param last One past the end of the range. * @param d_begin The beginning of the output range. * @param count The number of elements to copy out of the range. * @param cmp The comparison function to use. * @return OutputIt An iterator to past the last element written. */ template , typename InputIt, typename OutputIt, typename Elem = typename std::iterator_traits::value_type> constexpr auto get_max_n(InputIt first, InputIt last, OutputIt d_begin, std::size_t count, Comp cmp = {}) -> return_assert_t::value, OutputIt> { auto temp = get_max_n>(first, last, count, cmp); return std::move(temp.begin(), temp.end(), d_begin); } /** * @brief Applies a binary operation to each pair of corresponding elements in * two input ranges. It also allows for a sentinel end iterator. * * In the style of algorithms, the second range is simply assumed to * be at least as large as the first. * * @param first The beginning of the first input range. * @param last The end of the first input range. * @param second The beginning of the second input range. * @param f The operation to apply. * @return BinaryFunction f */ template constexpr auto for_each(ForwardIt first, EndIt last, ForwardIt2 second, BinaryFunction f) -> BinaryFunction { for (; first != last; (void)++first, (void)++second) { kblib::invoke(f, *first, *second); } return std::move(f); } /** * @brief Applies a binary operation to each pair of corresponding elements in * two input ranges. * * @param first The beginning of the first input range. * @param n The number of elements to operate on in each input range. * @param second The beginning of the second input range. * @param f The operation to apply. * @return std::pair Equivalent to `{std::advance(first, * n), std::advance(second, n)}` */ template constexpr auto for_each_n(ForwardIt first, Size n, ForwardIt2 second, BinaryFunction f) -> std::pair { for (Size i = 0; i < n; (void)++first, (void)++second, (void)++i) { kblib::invoke(f, *first, *second); } return {first, second}; } /** * @brief Copies all elements of [`first`, `last`) to out. It also allows for a * sentinel end iterator. * * @remark This function is `constexpr` in C++14. * * @param first The beginning of the input range * @param last The end of the input range * @param out The beginning of the output range * @return OutputIt An iterator to past the last element written */ template constexpr auto copy(InputIt first, EndIt last, OutputIt out) -> OutputIt { while (first != last) { *out++ = *first++; } return out; } /** * @brief Copies those elements of [`first`, `last`) which satisfy pred to out. * It also allows for a sentinel end iterator. * * @remark This function is `constexpr` in C++14. * * @param first The beginning of the input range * @param last The end of the input range * @param out The beginning of the output range * @param pred The predicate to apply * @return OutputIt An iterator to past the last element written */ template constexpr auto copy_if(InputIt first, EndIt last, OutputIt out, UnaryPredicate pred) -> OutputIt { while (first != last) { if (kblib::invoke(pred, *first)) { *out++ = *first; } first++; } return out; } /** * @brief Copies all elements of [`first`, `std::advance(first, n)`) to out. * * @remark This function is `constexpr` in C++14. * * @param first The beginning of the input range. * @param count The number of elements in the input range. * @param out The output range. * @return OutputIt OutputIt An iterator to past the last element written */ template constexpr auto copy_n(InputIt first, Size count, OutputIt out) -> OutputIt { for (Size i = 0; i < count; ++i) { *out++ = *first++; } return out; } /** * @brief Copies those elements of [`first`, `std::advance(first, n)`) which * satisfy pred to out. * * @remark This function is `constexpr` in C++14. * * @param first The beginning of the input range. * @param count The number of elements in the input range. * @param out The output range. * @param pred The predicate to apply. * @return OutputIt OutputIt An iterator to past the last element written */ template constexpr auto copy_n_if(InputIt first, Size count, OutputIt out, UnaryPredicate pred) -> OutputIt { for (Size i = 0; i < count; ++i) { if (kblib::invoke(pred, *first)) { *out++ = *first; } ++first; } return out; } /** * @brief Copies an input range, but every element for which pred is true is * replaced by new_value. It also allows for a sentinel end iterator. * * @remark This function is `constexpr` in C++14. * * @param first The beginning of the input range. * @param last The end of the input range. * @param out The beginning of the output range. * @param pred The predicate to apply. * @param new_value The value to replace those elements for which pred is true * with. * @return An iterator to past the last element written. */ template constexpr auto replace_copy_if(InputIt first, EndIt last, OutputIt out, UnaryPredicate pred, const T& new_value) -> OutputIt { while (first != last) { if (kblib::invoke(pred, *first)) { *out = *first; } else { *out = new_value; } ++first; ++out; } return out; } /** * @brief Copies an input range, but every element for which pred is true is * replaced by new_value. * * @remark This function is `constexpr` in C++14. * * @param first The beginning of the input range. * @param count The number of elements to copy. * @param out The beginning of the output range. * @param pred The predicate to apply. * @param new_value The value to replace those elements for which pred is true * with. * @return OutputIt An iterator to past the last element written. */ template constexpr auto replace_copy_n_if(InputIt first, Size count, OutputIt out, UnaryPredicate pred, const T& new_value) -> OutputIt { for (Size i = 0; i < count; ++i) { if (kblib::invoke(pred, *first)) { *out = *first; } else { *out = new_value; } ++first; ++out; } return out; } // TODO(killerbee13): Debug and test search_replace_copy template > constexpr auto search_replace_copy(ForwardIt1 h_begin, ForwardIt1 h_end, ForwardIt2 n_begin, ForwardIt2 n_end, ForwardIt3 r_begin, ForwardIt3 r_end, OutputIt d_begin, BinaryPredicate Compare = {}) -> OutputIt { if (n_begin == n_end) { return copy(h_begin, h_end, d_begin); } else { const auto needle_length = std::distance(n_begin, n_end); while (h_begin != h_end) { const auto found = std::search(h_begin, h_end, n_begin, n_end, Compare); d_begin = kblib::copy(h_begin, found, d_begin); h_begin = found; if (h_begin != h_end) { d_begin = copy(r_begin, r_end, d_begin); std::advance(h_begin, needle_length); } } return d_begin; } } template > constexpr auto search_replace_copy(Haystack&& haystack, Needle&& needle, Replacement&& replacement, OutputIt d_begin, BinaryPredicate compare = {}) { using std::begin; using std::end; return search_replace_copy(begin(haystack), end(haystack), // begin(needle), end(needle), // begin(replacement), end(replacement), // d_begin, // compare); } /** * @brief Rotates the input range. This is just a constexpr-in-C++14 version of * std::rotate. * * @param first * @param n_first * @param last * @return ForwardIt */ template constexpr auto rotate(ForwardIt first, ForwardIt n_first, ForwardIt last) noexcept(noexcept(swap(*first, *first))) -> ForwardIt { if (first == n_first) return last; if (n_first == last) return first; ForwardIt read = n_first; ForwardIt write = first; ForwardIt next_read = first; // read position for when "read" hits "last" while (read != last) { if (write == next_read) next_read = read; // track where "first" went // iter_swap is not constexpr // std::iter_swap(write++, read++); swap(*(write++), *(read++)); } // rotate the remaining sequence into place kblib::rotate(write, next_read, last); return write; } /** * @brief Like std::generate except that it returns the output iterator at the * end. It also allows for a sentinel end iterator. * * @param first The beginning of the ouput range. * @param last The end of the output range. * @param g A generator to repeatedly call and assign the return values to the * elements of the output range. * @return ForwardIt The iterator pointing past the last element written. */ template constexpr auto generate(OutputIt first, EndIt last, Generator g) noexcept(noexcept(*++first = g())) -> OutputIt { while (first != last) { *first = g(); ++first; } return first; } /** * @brief Like std::generate_n except that it is constexpr. * * @param first The beginning of the ouput range. * @param count The number of elements to generate. * @param g A generator to repeatedly call and assign the return values to the * elements of the output range. * @return ForwardIt The iterator pointing past the last element written. */ template constexpr auto generate_n(OutputIt first, Size count, Generator g) noexcept(noexcept(*first++ = g())) -> OutputIt { for (Size i = 0; i < count; i++) { *first++ = g(); } return first; } template constexpr auto iota(ForwardIt first, ForwardIt last, T value) noexcept( noexcept(*first++ = value) and noexcept(++value)) -> void { while (first != last) { *first++ = value; ++value; } } // For some reason these long noexcept specifications really trip // up clang-format // clang-format off template constexpr auto iota( ForwardIt first, ForwardIt last, T value, UnaryOperation unary_op ) noexcept(noexcept(*first++ = value) and noexcept(kblib::invoke(unary_op, std::move(value)))) -> void { // clang-format on while (first != last) { *first++ = value; value = kblib::invoke(unary_op, std::move(value)); } } // clang-format off template constexpr auto call_each( InputIt first, EndIt last, Params&&... params ) noexcept(noexcept(kblib::invoke(*first++, std::forward(params)...))) -> InputIt { // clang-format on while (first != last) { kblib::invoke(*first++, std::forward(params)...); } return first; } /** * @brief transform applies the given function to a range and stores the result * in another range, beginning at d_first. The unary operation unary_op is * applied to the range defined by [first1, last1). It also allows for a * sentinel end iterator. * * @remark The expression `*d_first = kblib::invoke(unary_op, *first)` must be * valid and must not modify `*first`. * * @param first The beginning of the input range * @param last The end of the input range * @param d_first The beginning of the output range * @param unary_op The operation to apply * @return OutputIt An iterator to past the last element written */ template constexpr auto transform(InputIt first, EndIt last, OutputIt d_first, UnaryOperation unary_op) -> OutputIt { while (first != last) { *d_first++ = kblib::invoke(unary_op, *first); ++first; } return d_first; } /** * @brief transform applies the given function to a range and stores the result * in another range, beginning at d_first. The unary operation unary_op is * applied to the range defined by [first1, last1). It also allows for a * sentinel end iterator. * * @remark The expression `*d_first = kblib::invoke(binary_op, *first, *first2)` * must be valid and must not modify `*first` or `*first2`. * * @param first The beginning of the first input range * @param last The end of the first input range * @param first2 The beginning of the second input range * @param d_first The beginning of the output range * @param binary_op The operation to apply * @return OutputIt An iterator to past the last element written */ template constexpr auto transform(InputIt first, EndIt last, InputIt first2, OutputIt d_first, BinaryOperation binary_op) -> OutputIt { while (first != last) { *d_first++ = kblib::invoke(binary_op, *first, *first2); ++first; ++first2; } return d_first; } /** * @brief transform applies the given function to a range and stores the result * in another range, beginning at d_first. The unary operation unary_op is * applied to the range defined by [first1, last1). It also allows for a * sentinel end iterator. * * @remark The expression `kblib::invoke(pred, *first)` must be valid and must * return a type convertible to `bool`, and must not modify `*first` * @remark The expression `*d_first = kblib::invoke(unary_op, *first)` must be * valid and must not modify `*first`. * * @param first The beginning of the input range * @param last The end of the input range * @param d_first The beginning of the output range * @param pred The predicate to apply * @param unary_op The operation to apply * @return OutputIt An iterator to past the last element written */ template constexpr auto transform_if(InputIt first, EndIt last, OutputIt d_first, UnaryPredicate pred, UnaryOperation unary_op) -> OutputIt { while (first != last) { if (kblib::invoke(pred, *first)) { *d_first++ = kblib::invoke(unary_op, *first); } ++first; } return d_first; } namespace detail_algorithm { /** * @brief Implementation function for insertion_sort_copy. Like * std::move(begin, end, d_begin) but using the interface of rotate and * supporting backward overlapping, but not forward overlapping. * * @param first Start of range to assign to * @param n_first Start of range to read from * @param last End of range to read from */ template constexpr auto shift_backward( ForwardIt first, ForwardIt n_first, ForwardIt last) noexcept(noexcept(*first = std::move(*first))) -> void { if (first == n_first or n_first == last) return; ForwardIt read = n_first; ForwardIt write = first; while (read != last) { *(write++) = std::move(*(read++)); } return; } } // namespace detail_algorithm } // namespace KBLIB_NS #endif // ALGORITHM_H