You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

237 lines
8.5KB

  1. /// \file
  2. // Range v3 library
  3. //
  4. // Copyright Eric Niebler 2014-present
  5. //
  6. // Use, modification and distribution is subject to the
  7. // Boost Software License, Version 1.0. (See accompanying
  8. // file LICENSE_1_0.txt or copy at
  9. // http://www.boost.org/LICENSE_1_0.txt)
  10. //
  11. // Project home: https://github.com/ericniebler/range-v3
  12. //
  13. #ifndef RANGES_V3_ALGORITHM_FIND_END_HPP
  14. #define RANGES_V3_ALGORITHM_FIND_END_HPP
  15. #include <utility>
  16. #include <meta/meta.hpp>
  17. #include <range/v3/range_fwd.hpp>
  18. #include <range/v3/functional/comparisons.hpp>
  19. #include <range/v3/functional/identity.hpp>
  20. #include <range/v3/functional/invoke.hpp>
  21. #include <range/v3/iterator/concepts.hpp>
  22. #include <range/v3/iterator/operations.hpp>
  23. #include <range/v3/iterator/traits.hpp>
  24. #include <range/v3/range/access.hpp>
  25. #include <range/v3/range/concepts.hpp>
  26. #include <range/v3/range/traits.hpp>
  27. #include <range/v3/utility/static_const.hpp>
  28. #include <range/v3/view/subrange.hpp>
  29. namespace ranges
  30. {
  31. /// \cond
  32. namespace detail
  33. {
  34. template<typename I, typename S>
  35. auto next_to_if(I i, S s, std::true_type) -> CPP_ret(I)( //
  36. requires input_iterator<I> && sentinel_for<S, I>)
  37. {
  38. return ranges::next(i, s);
  39. }
  40. template<typename I, typename S>
  41. auto next_to_if(I, S s, std::false_type) -> CPP_ret(S)( //
  42. requires input_iterator<I> && sentinel_for<S, I>)
  43. {
  44. return s;
  45. }
  46. template<bool B, typename I, typename S>
  47. auto next_to_if(I i, S s) -> CPP_ret(meta::if_c<B, I, S>)( //
  48. requires input_iterator<I> && sentinel_for<S, I>)
  49. {
  50. return detail::next_to_if(std::move(i), std::move(s), meta::bool_<B>{});
  51. }
  52. template<typename I1, typename S1, typename I2, typename S2, typename R,
  53. typename P>
  54. subrange<I1> find_end_impl(I1 begin1, S1 end1, I2 begin2, S2 end2, R pred, P proj,
  55. detail::forward_iterator_tag_,
  56. detail::forward_iterator_tag_)
  57. {
  58. bool found = false;
  59. I1 res_begin, res_end;
  60. if(begin2 == end2)
  61. {
  62. auto e1 = ranges::next(begin1, end1);
  63. return {e1, e1};
  64. }
  65. while(true)
  66. {
  67. while(true)
  68. {
  69. if(begin1 == end1)
  70. return {(found ? res_begin : begin1), (found ? res_end : begin1)};
  71. if(invoke(pred, invoke(proj, *begin1), *begin2))
  72. break;
  73. ++begin1;
  74. }
  75. auto tmp1 = begin1;
  76. auto tmp2 = begin2;
  77. while(true)
  78. {
  79. if(++tmp2 == end2)
  80. {
  81. res_begin = begin1++;
  82. res_end = ++tmp1;
  83. found = true;
  84. break;
  85. }
  86. if(++tmp1 == end1)
  87. return {(found ? res_begin : tmp1), (found ? res_end : tmp1)};
  88. if(!invoke(pred, invoke(proj, *tmp1), *tmp2))
  89. {
  90. ++begin1;
  91. break;
  92. }
  93. }
  94. }
  95. }
  96. template<typename I1, typename I2, typename R, typename P>
  97. subrange<I1> find_end_impl(I1 begin1, I1 end1, I2 begin2, I2 end2, R pred, P proj,
  98. detail::bidirectional_iterator_tag_,
  99. detail::bidirectional_iterator_tag_)
  100. {
  101. // modeled after search algorithm (in reverse)
  102. if(begin2 == end2)
  103. return {end1, end1}; // Everything matches an empty sequence
  104. I1 l1 = end1;
  105. I2 l2 = end2;
  106. --l2;
  107. while(true)
  108. {
  109. // Find end element in sequence 1 that matches *(end2-1), with a mininum
  110. // of loop checks
  111. do
  112. // return {end1,end1} if no element matches *begin2
  113. if(begin1 == l1)
  114. return {end1, end1};
  115. while(!invoke(pred, invoke(proj, *--l1), *l2));
  116. // *l1 matches *l2, now match elements before here
  117. I1 m1 = l1;
  118. I2 m2 = l2;
  119. do
  120. // If pattern exhausted, {m1,++l1} is the answer
  121. // (works for 1 element pattern)
  122. if(m2 == begin2)
  123. return {m1, ++l1};
  124. // Otherwise if source exhausted, pattern not found
  125. else if(m1 == begin1)
  126. return {end1, end1};
  127. // if there is a mismatch, restart with a new l1
  128. // else there is a match, check next elements
  129. while(invoke(pred, invoke(proj, *--m1), *--m2));
  130. }
  131. }
  132. template<typename I1, typename I2, typename R, typename P>
  133. subrange<I1> find_end_impl(I1 begin1, I1 end1, I2 begin2, I2 end2, R pred, P proj,
  134. detail::random_access_iterator_tag_,
  135. detail::random_access_iterator_tag_)
  136. {
  137. // Take advantage of knowing source and pattern lengths. Stop short when
  138. // source is smaller than pattern
  139. auto len2 = end2 - begin2;
  140. if(len2 == 0)
  141. return {end1, end1};
  142. auto len1 = end1 - begin1;
  143. if(len1 < len2)
  144. return {end1, end1};
  145. I1 const start =
  146. begin1 + (len2 - 1); // End of pattern match can't go before here
  147. I1 l1 = end1;
  148. I2 l2 = end2;
  149. --l2;
  150. while(true)
  151. {
  152. do
  153. if(start == l1)
  154. return {end1, end1};
  155. while(!invoke(pred, invoke(proj, *--l1), *l2));
  156. I1 m1 = l1;
  157. I2 m2 = l2;
  158. do
  159. if(m2 == begin2)
  160. return {m1, ++l1};
  161. // no need to check range on m1 because s guarantees we have enough source
  162. while(invoke(pred, invoke(proj, *--m1), *--m2));
  163. }
  164. }
  165. } // namespace detail
  166. /// \endcond
  167. /// \addtogroup group-algorithms
  168. /// @{
  169. RANGES_BEGIN_NIEBLOID(find_end)
  170. /// \brief function template \c find_end
  171. template<typename I1,
  172. typename S1,
  173. typename I2,
  174. typename S2,
  175. typename R = equal_to,
  176. typename P = identity>
  177. auto RANGES_FUN_NIEBLOID(find_end)(
  178. I1 begin1, S1 end1, I2 begin2, S2 end2, R pred = R{}, P proj = P{}) //
  179. ->CPP_ret(subrange<I1>)( //
  180. requires forward_iterator<I1> && sentinel_for<S1, I1> &&
  181. forward_iterator<I2> && sentinel_for<S2, I2> &&
  182. indirect_relation<R, projected<I1, P>, I2>)
  183. {
  184. constexpr bool Bidi =
  185. bidirectional_iterator<I1> && bidirectional_iterator<I2>;
  186. return detail::find_end_impl(begin1,
  187. detail::next_to_if<Bidi>(begin1, end1),
  188. begin2,
  189. detail::next_to_if<Bidi>(begin2, end2),
  190. std::move(pred),
  191. std::move(proj),
  192. iterator_tag_of<I1>(),
  193. iterator_tag_of<I2>());
  194. }
  195. /// \overload
  196. template<typename Rng1,
  197. typename Rng2,
  198. typename R = equal_to,
  199. typename P = identity>
  200. auto RANGES_FUN_NIEBLOID(find_end)(
  201. Rng1 && rng1, Rng2 && rng2, R pred = R{}, P proj = P{}) //
  202. ->CPP_ret(safe_subrange_t<Rng1>)( //
  203. requires forward_range<Rng1> && forward_range<Rng2> &&
  204. indirect_relation<R, projected<iterator_t<Rng1>, P>, iterator_t<Rng2>>)
  205. {
  206. return (*this)(begin(rng1),
  207. end(rng1),
  208. begin(rng2),
  209. end(rng2),
  210. std::move(pred),
  211. std::move(proj));
  212. }
  213. RANGES_END_NIEBLOID(find_end)
  214. namespace cpp20
  215. {
  216. using ranges::find_end;
  217. }
  218. /// @}
  219. } // namespace ranges
  220. #endif // include guard