/// \file // Range v3 library // // Copyright Eric Niebler 2014-present // // Use, modification and distribution is subject to the // Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // // Project home: https://github.com/ericniebler/range-v3 // #ifndef RANGES_V3_ALGORITHM_FIND_END_HPP #define RANGES_V3_ALGORITHM_FIND_END_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace ranges { /// \cond namespace detail { template auto next_to_if(I i, S s, std::true_type) -> CPP_ret(I)( // requires input_iterator && sentinel_for) { return ranges::next(i, s); } template auto next_to_if(I, S s, std::false_type) -> CPP_ret(S)( // requires input_iterator && sentinel_for) { return s; } template auto next_to_if(I i, S s) -> CPP_ret(meta::if_c)( // requires input_iterator && sentinel_for) { return detail::next_to_if(std::move(i), std::move(s), meta::bool_{}); } template subrange find_end_impl(I1 begin1, S1 end1, I2 begin2, S2 end2, R pred, P proj, detail::forward_iterator_tag_, detail::forward_iterator_tag_) { bool found = false; I1 res_begin, res_end; if(begin2 == end2) { auto e1 = ranges::next(begin1, end1); return {e1, e1}; } while(true) { while(true) { if(begin1 == end1) return {(found ? res_begin : begin1), (found ? res_end : begin1)}; if(invoke(pred, invoke(proj, *begin1), *begin2)) break; ++begin1; } auto tmp1 = begin1; auto tmp2 = begin2; while(true) { if(++tmp2 == end2) { res_begin = begin1++; res_end = ++tmp1; found = true; break; } if(++tmp1 == end1) return {(found ? res_begin : tmp1), (found ? res_end : tmp1)}; if(!invoke(pred, invoke(proj, *tmp1), *tmp2)) { ++begin1; break; } } } } template subrange find_end_impl(I1 begin1, I1 end1, I2 begin2, I2 end2, R pred, P proj, detail::bidirectional_iterator_tag_, detail::bidirectional_iterator_tag_) { // modeled after search algorithm (in reverse) if(begin2 == end2) return {end1, end1}; // Everything matches an empty sequence I1 l1 = end1; I2 l2 = end2; --l2; while(true) { // Find end element in sequence 1 that matches *(end2-1), with a mininum // of loop checks do // return {end1,end1} if no element matches *begin2 if(begin1 == l1) return {end1, end1}; while(!invoke(pred, invoke(proj, *--l1), *l2)); // *l1 matches *l2, now match elements before here I1 m1 = l1; I2 m2 = l2; do // If pattern exhausted, {m1,++l1} is the answer // (works for 1 element pattern) if(m2 == begin2) return {m1, ++l1}; // Otherwise if source exhausted, pattern not found else if(m1 == begin1) return {end1, end1}; // if there is a mismatch, restart with a new l1 // else there is a match, check next elements while(invoke(pred, invoke(proj, *--m1), *--m2)); } } template subrange find_end_impl(I1 begin1, I1 end1, I2 begin2, I2 end2, R pred, P proj, detail::random_access_iterator_tag_, detail::random_access_iterator_tag_) { // Take advantage of knowing source and pattern lengths. Stop short when // source is smaller than pattern auto len2 = end2 - begin2; if(len2 == 0) return {end1, end1}; auto len1 = end1 - begin1; if(len1 < len2) return {end1, end1}; I1 const start = begin1 + (len2 - 1); // End of pattern match can't go before here I1 l1 = end1; I2 l2 = end2; --l2; while(true) { do if(start == l1) return {end1, end1}; while(!invoke(pred, invoke(proj, *--l1), *l2)); I1 m1 = l1; I2 m2 = l2; do if(m2 == begin2) return {m1, ++l1}; // no need to check range on m1 because s guarantees we have enough source while(invoke(pred, invoke(proj, *--m1), *--m2)); } } } // namespace detail /// \endcond /// \addtogroup group-algorithms /// @{ RANGES_BEGIN_NIEBLOID(find_end) /// \brief function template \c find_end template auto RANGES_FUN_NIEBLOID(find_end)( I1 begin1, S1 end1, I2 begin2, S2 end2, R pred = R{}, P proj = P{}) // ->CPP_ret(subrange)( // requires forward_iterator && sentinel_for && forward_iterator && sentinel_for && indirect_relation, I2>) { constexpr bool Bidi = bidirectional_iterator && bidirectional_iterator; return detail::find_end_impl(begin1, detail::next_to_if(begin1, end1), begin2, detail::next_to_if(begin2, end2), std::move(pred), std::move(proj), iterator_tag_of(), iterator_tag_of()); } /// \overload template auto RANGES_FUN_NIEBLOID(find_end)( Rng1 && rng1, Rng2 && rng2, R pred = R{}, P proj = P{}) // ->CPP_ret(safe_subrange_t)( // requires forward_range && forward_range && indirect_relation, P>, iterator_t>) { return (*this)(begin(rng1), end(rng1), begin(rng2), end(rng2), std::move(pred), std::move(proj)); } RANGES_END_NIEBLOID(find_end) namespace cpp20 { using ranges::find_end; } /// @} } // namespace ranges #endif // include guard