|
| 1 | +#ifndef ALG_MERGE_H |
| 2 | +#define ALG_MERGE_H |
| 3 | + |
| 4 | +#include <algorithm> |
| 5 | +#include <cstdint> |
| 6 | +#include <functional> |
| 7 | +#include <ranges> |
| 8 | + |
| 9 | +namespace Alg |
| 10 | +{ |
| 11 | + |
| 12 | +// closest to std::ranges::merge, but: |
| 13 | +// - added new projection parameter to projection both ranges |
| 14 | +// - comparison is a three-way comparison which requires weak_order |
| 15 | +// on projected values |
| 16 | +// - added new transformer_1 and transformer_2 parameters to transform |
| 17 | +// the values at left and right side if no merging happens |
| 18 | +// - added need_merge parameter to decide if merging is necessary |
| 19 | +// of 2 equivalent values or just transforming all values |
| 20 | +// - added merger parameter to merge the values if merging happens |
| 21 | +// - output argument can be range which used as inserter_iterator |
| 22 | +// - arguments are passed as struct to avoid argument swapping |
| 23 | +// and used as named arguments |
| 24 | + |
| 25 | +namespace Merge |
| 26 | +{ |
| 27 | +struct get_first |
| 28 | +{ |
| 29 | + template <class T, class... Args> |
| 30 | + constexpr auto operator()(T &&t, const Args &...) const noexcept |
| 31 | + { |
| 32 | + return std::forward<T>(t); |
| 33 | + } |
| 34 | +}; |
| 35 | + |
| 36 | +struct always |
| 37 | +{ |
| 38 | + template <class... Ts> |
| 39 | + constexpr auto operator()(const Ts &...) const noexcept |
| 40 | + { |
| 41 | + return res; |
| 42 | + } |
| 43 | + bool res{true}; |
| 44 | +}; |
| 45 | + |
| 46 | +template <typename T> |
| 47 | +concept comparison_category = |
| 48 | + !std::is_void_v<std::common_comparison_category_t<T>>; |
| 49 | + |
| 50 | +template <typename T, typename Cat> |
| 51 | +concept compares_as = |
| 52 | + comparison_category<Cat> |
| 53 | + && std::same_as<std::common_comparison_category_t<T, Cat>, Cat>; |
| 54 | + |
| 55 | +template <typename R, |
| 56 | + typename T, |
| 57 | + typename U = T, |
| 58 | + typename Cat = std::weak_ordering> |
| 59 | +concept three_way_order = |
| 60 | + std::regular_invocable<R, T, U> |
| 61 | + && compares_as<std::invoke_result_t<R, T, U>, Cat>; |
| 62 | + |
| 63 | +template <typename R, |
| 64 | + typename Iter1, |
| 65 | + typename Iter2 = Iter1, |
| 66 | + typename Cat = std::weak_ordering> |
| 67 | +concept indirect_three_way_order = |
| 68 | + std::indirectly_readable<Iter1> && std::indirectly_readable<Iter2> |
| 69 | + && std::copy_constructible<R> |
| 70 | + && three_way_order<R &, |
| 71 | + std::iter_value_t<Iter1> &, |
| 72 | + std::iter_value_t<Iter2> &, |
| 73 | + Cat> |
| 74 | + && three_way_order<R &, |
| 75 | + std::iter_value_t<Iter1> &, |
| 76 | + std::iter_reference_t<Iter2>, |
| 77 | + Cat> |
| 78 | + && three_way_order<R &, |
| 79 | + std::iter_reference_t<Iter1>, |
| 80 | + std::iter_value_t<Iter2> &, |
| 81 | + Cat> |
| 82 | + && three_way_order<R &, |
| 83 | + std::iter_reference_t<Iter1>, |
| 84 | + std::iter_reference_t<Iter2>, |
| 85 | + Cat> |
| 86 | + && three_way_order<R &, |
| 87 | + std::iter_common_reference_t<Iter1>, |
| 88 | + std::iter_common_reference_t<Iter2>, |
| 89 | + Cat>; |
| 90 | + |
| 91 | +template <typename... Ts> |
| 92 | +concept common_references = |
| 93 | + (std::common_reference_with<std::common_reference_t<Ts...>, Ts> |
| 94 | + && ...); |
| 95 | + |
| 96 | +template <typename R, typename Iter1, typename Iter2 = Iter1> |
| 97 | +concept indirectly_binary_invocable = |
| 98 | + std::indirectly_readable<Iter1> && std::indirectly_readable<Iter2> |
| 99 | + && std::copy_constructible<R> |
| 100 | + && std::invocable<R &, |
| 101 | + std::iter_value_t<Iter1> &, |
| 102 | + std::iter_value_t<Iter2> &> |
| 103 | + && std::invocable<R &, |
| 104 | + std::iter_value_t<Iter1> &, |
| 105 | + std::iter_reference_t<Iter2>> |
| 106 | + && std::invocable<R &, |
| 107 | + std::iter_reference_t<Iter1>, |
| 108 | + std::iter_value_t<Iter2> &> |
| 109 | + && std::invocable<R &, |
| 110 | + std::iter_reference_t<Iter1>, |
| 111 | + std::iter_reference_t<Iter2>> |
| 112 | + && std::invocable<R &, |
| 113 | + std::iter_common_reference_t<Iter1>, |
| 114 | + std::iter_common_reference_t<Iter2>> |
| 115 | + && common_references<std::invoke_result_t<R &, |
| 116 | + std::iter_value_t<Iter1> &, |
| 117 | + std::iter_value_t<Iter2> &>, |
| 118 | + std::invoke_result_t<R &, |
| 119 | + std::iter_value_t<Iter1> &, |
| 120 | + std::iter_reference_t<Iter2>>, |
| 121 | + std::invoke_result_t<R &, |
| 122 | + std::iter_reference_t<Iter1>, |
| 123 | + std::iter_value_t<Iter2> &>, |
| 124 | + std::invoke_result_t<R &, |
| 125 | + std::iter_reference_t<Iter1>, |
| 126 | + std::iter_reference_t<Iter2>>>; |
| 127 | + |
| 128 | +template <typename R, typename... Its> |
| 129 | +concept indirectly_unary_invocable_all = |
| 130 | + (std::indirectly_unary_invocable<R, Its> && ...); |
| 131 | + |
| 132 | +template <class U, class T = std::remove_reference_t<U>> |
| 133 | +concept insertable_range_or_iterator = |
| 134 | + std::weakly_incrementable<std::remove_cv_t<T>> |
| 135 | + || (std::ranges::range<U> |
| 136 | + && std::weakly_incrementable<std::insert_iterator<T>> |
| 137 | + && requires(U &cont, |
| 138 | + std::ranges::iterator_t<U> i, |
| 139 | + const std::ranges::range_value_t<U> &v) { |
| 140 | + std::inserter(cont, std::ranges::end(cont)); |
| 141 | + i = cont.insert(i, v); |
| 142 | + ++i; |
| 143 | + }); |
| 144 | + |
| 145 | +template <class T> |
| 146 | +struct borrowed_inserter_or_self : |
| 147 | + std::conditional<std::ranges::borrowed_range<T>, |
| 148 | + std::insert_iterator<std::remove_reference_t<T>>, |
| 149 | + std::ranges::dangling> |
| 150 | +{}; |
| 151 | + |
| 152 | +template <class T> |
| 153 | + requires(std::weakly_incrementable<std::remove_cvref_t<T>>) |
| 154 | +struct borrowed_inserter_or_self<T> : |
| 155 | + std::type_identity<std::remove_cvref_t<T>> |
| 156 | +{}; |
| 157 | + |
| 158 | +template <class T> |
| 159 | +using borrowed_inserter_or_self_t = |
| 160 | + typename borrowed_inserter_or_self<T>::type; |
| 161 | + |
| 162 | +template <class Proj1 = std::identity, |
| 163 | + class Proj2 = std::identity, |
| 164 | + class Proj = std::identity, |
| 165 | + class Comp = decltype(std::weak_order), |
| 166 | + class Transform1 = std::identity, |
| 167 | + class Transform2 = std::identity, |
| 168 | + class NeedMerge = always, |
| 169 | + class Merge = get_first> |
| 170 | +struct merge_args |
| 171 | +{ |
| 172 | + [[no_unique_address]] Proj1 projection_1{}; |
| 173 | + [[no_unique_address]] Proj2 projection_2{}; |
| 174 | + [[no_unique_address]] Proj projection{}; |
| 175 | + [[no_unique_address]] Comp comparator{}; |
| 176 | + [[no_unique_address]] Transform1 transformer_1{}; |
| 177 | + [[no_unique_address]] Transform2 transformer_2{}; |
| 178 | + [[no_unique_address]] NeedMerge need_merge{}; |
| 179 | + [[no_unique_address]] Merge merger{}; |
| 180 | +}; |
| 181 | + |
| 182 | +template <std::input_iterator It1, |
| 183 | + std::sentinel_for<It1> End1, |
| 184 | + std::input_iterator It2, |
| 185 | + std::sentinel_for<It2> End2, |
| 186 | + std::weakly_incrementable Out, |
| 187 | + std::indirectly_unary_invocable<It1> Proj1, |
| 188 | + std::indirectly_unary_invocable<It2> Proj2, |
| 189 | + indirectly_unary_invocable_all<std::projected<It1, Proj1>, |
| 190 | + std::projected<It2, Proj2>> Proj, |
| 191 | + indirect_three_way_order< |
| 192 | + std::projected<std::projected<It1, Proj1>, Proj>, |
| 193 | + std::projected<std::projected<It2, Proj2>, Proj>> Comp, |
| 194 | + std::indirectly_unary_invocable<It1> Transform1, |
| 195 | + std::indirectly_unary_invocable<It2> Transform2, |
| 196 | + std::indirect_binary_predicate<It1, It2> NeedMerge, |
| 197 | + indirectly_binary_invocable<It1, It2> Merge> |
| 198 | +constexpr std::ranges::in_in_out_result<It1, It2, Out> merge( |
| 199 | + It1 first1, |
| 200 | + End1 last1, |
| 201 | + It2 first2, |
| 202 | + End2 last2, |
| 203 | + Out result, |
| 204 | + merge_args<Proj1, |
| 205 | + Proj2, |
| 206 | + Proj, |
| 207 | + Comp, |
| 208 | + Transform1, |
| 209 | + Transform2, |
| 210 | + NeedMerge, |
| 211 | + Merge> &&args = {}) |
| 212 | +{ |
| 213 | + while (first1 != last1 && first2 != last2) |
| 214 | + if (auto cmp = std::invoke(args.comparator, |
| 215 | + std::invoke(args.projection, |
| 216 | + std::invoke(args.projection_1, *first1)), |
| 217 | + std::invoke(args.projection, |
| 218 | + std::invoke(args.projection_2, *first2))); |
| 219 | + is_eq(cmp) |
| 220 | + && std::invoke(args.need_merge, *first1, *first2)) { |
| 221 | + *result = std::invoke(args.merger, *first1, *first2); |
| 222 | + ++first1; |
| 223 | + ++first2; |
| 224 | + ++result; |
| 225 | + } |
| 226 | + else { |
| 227 | + if (is_lteq(cmp)) { |
| 228 | + *result = std::invoke(args.transformer_1, *first1); |
| 229 | + ++first1; |
| 230 | + ++result; |
| 231 | + } |
| 232 | + if (is_gteq(cmp)) { |
| 233 | + *result = std::invoke(args.transformer_2, *first2); |
| 234 | + ++first2; |
| 235 | + ++result; |
| 236 | + } |
| 237 | + } |
| 238 | + auto copy1 = std::ranges::transform(std::move(first1), |
| 239 | + std::move(last1), |
| 240 | + std::move(result), |
| 241 | + args.transformer_1); |
| 242 | + auto copy2 = std::ranges::transform(std::move(first2), |
| 243 | + std::move(last2), |
| 244 | + std::move(copy1.out), |
| 245 | + args.transformer_2); |
| 246 | + return {std::move(copy1.in), |
| 247 | + std::move(copy2.in), |
| 248 | + std::move(copy2.out)}; |
| 249 | +} |
| 250 | + |
| 251 | +template <std::ranges::input_range R1, |
| 252 | + std::ranges::input_range R2, |
| 253 | + insertable_range_or_iterator Out, |
| 254 | + class... Args> |
| 255 | +constexpr std::ranges::in_in_out_result< |
| 256 | + std::ranges::borrowed_iterator_t<R1>, |
| 257 | + std::ranges::borrowed_iterator_t<R2>, |
| 258 | + borrowed_inserter_or_self_t<Out>> |
| 259 | +merge(R1 &&range1, |
| 260 | + R2 &&range2, |
| 261 | + Out &&result, |
| 262 | + merge_args<Args...> &&args = {}) |
| 263 | +{ |
| 264 | + if constexpr (std::weakly_incrementable< |
| 265 | + std::remove_cvref_t<Out>>) { |
| 266 | + return ::Alg::Merge::merge(std::ranges::begin(range1), |
| 267 | + std::ranges::end(range1), |
| 268 | + std::ranges::begin(range2), |
| 269 | + std::ranges::end(range2), |
| 270 | + result, |
| 271 | + std::move(args)); |
| 272 | + } |
| 273 | + else { |
| 274 | + return ::Alg::Merge::merge(std::ranges::begin(range1), |
| 275 | + std::ranges::end(range1), |
| 276 | + std::ranges::begin(range2), |
| 277 | + std::ranges::end(range2), |
| 278 | + std::inserter(result, std::ranges::end(result)), |
| 279 | + std::move(args)); |
| 280 | + } |
| 281 | +} |
| 282 | +} |
| 283 | + |
| 284 | +using Merge::merge; |
| 285 | +using Merge::merge_args; |
| 286 | + |
| 287 | +} |
| 288 | + |
| 289 | +#endif |
0 commit comments