1 // Copyright 2015, Tobias Hermann and the FunctionalPlus contributors.
2 // https://github.com/Dobiasd/FunctionalPlus
3 // Distributed under the Boost Software License, Version 1.0.
4 // (See accompanying file LICENSE_1_0.txt or copy at
5 // http://www.boost.org/LICENSE_1_0.txt)
6
7 #pragma once
8
9 #include <fplus/container_common.hpp>
10 #include <fplus/function_traits.hpp>
11 #include <fplus/generate.hpp>
12 #include <fplus/internal/invoke.hpp>
13 #include <fplus/internal/asserts/pairs.hpp>
14
15 #include <utility>
16
17 namespace fplus
18 {
19 // API search type: apply_to_pair : (((a, b) -> c), (a, b)) -> c
20 // fwd bind count: 1
21 // Apply binary function to parts of a pair.
22 template <typename F, typename FIn0, typename FIn1>
apply_to_pair(F f,const std::pair<FIn0,FIn1> & p)23 auto apply_to_pair(F f, const std::pair<FIn0, FIn1>& p)
24 {
25 internal::trigger_static_asserts<internal::apply_to_pair_tag, F, FIn0, FIn1>();
26 return internal::invoke(f, p.first, p.second);
27 }
28
29 // API search type: zip_with : (((a, b) -> c), [a], [b]) -> [c]
30 // fwd bind count: 2
31 // Zip two sequences using a binary function.
32 // zip_with((+), [1, 2, 3], [5, 6]) == [1+5, 2+6] == [6, 8]
33 template <typename ContainerIn1,
34 typename ContainerIn2,
35 typename F,
36 typename X = typename ContainerIn1::value_type,
37 typename Y = typename ContainerIn2::value_type,
38 typename TOut = std::decay_t<internal::invoke_result_t<F, X, Y>>,
39 typename ContainerOut = std::vector<TOut>>
zip_with(F f,const ContainerIn1 & xs,const ContainerIn2 & ys)40 ContainerOut zip_with(F f, const ContainerIn1& xs, const ContainerIn2& ys)
41 {
42 internal::trigger_static_asserts<internal::zip_with_tag, F, X, Y>();
43 ContainerOut result;
44 std::size_t resultSize = std::min(size_of_cont(xs), size_of_cont(ys));
45 internal::prepare_container(result, resultSize);
46 auto itResult = internal::get_back_inserter(result);
47 auto itXs = std::begin(xs);
48 auto itYs = std::begin(ys);
49 for (std::size_t i = 0; i < resultSize; ++i)
50 {
51 *itResult = internal::invoke(f, *itXs, *itYs);
52 ++itXs;
53 ++itYs;
54 }
55 return result;
56 }
57
58 // API search type: zip_with_3 : (((a, b, c) -> d), [a], [b], [c]) -> [c]
59 // fwd bind count: 3
60 // Zip three sequences using a ternary function.
61 // zip_with_3((+), [1, 2, 3], [5, 6], [1, 1]) == [7, 9]
62 template <
63 typename ContainerIn1,
64 typename ContainerIn2,
65 typename ContainerIn3,
66 typename F,
67 typename X = typename ContainerIn1::value_type,
68 typename Y = typename ContainerIn2::value_type,
69 typename Z = typename ContainerIn3::value_type,
70 typename TOut = std::decay_t<internal::invoke_result_t<F, X, Y, Z>>,
71 typename ContainerOut = typename std::vector<TOut>>
zip_with_3(F f,const ContainerIn1 & xs,const ContainerIn2 & ys,const ContainerIn3 & zs)72 ContainerOut zip_with_3(F f,
73 const ContainerIn1& xs,
74 const ContainerIn2& ys,
75 const ContainerIn3& zs)
76 {
77 internal::trigger_static_asserts<internal::zip_with_3_tag, F, X, Y, Z>();
78 static_assert(std::is_same<
79 typename internal::same_cont_new_t<ContainerIn1, void>::type,
80 typename internal::same_cont_new_t<ContainerIn2, void>::type>::value,
81 "All three Containers must be of same outer type.");
82 static_assert(std::is_same<
83 typename internal::same_cont_new_t<ContainerIn2, void>::type,
84 typename internal::same_cont_new_t<ContainerIn3, void>::type>::value,
85 "All three Containers must be of same outer type.");
86 ContainerOut result;
87 std::size_t resultSize = std::min(size_of_cont(xs), size_of_cont(ys));
88 internal::prepare_container(result, resultSize);
89 auto itResult = internal::get_back_inserter(result);
90 auto itXs = std::begin(xs);
91 auto itYs = std::begin(ys);
92 auto itZs = std::begin(zs);
93 for (std::size_t i = 0; i < resultSize; ++i)
94 {
95 *itResult = internal::invoke(f, *itXs, *itYs, *itZs);
96 ++itXs;
97 ++itYs;
98 ++itZs;
99 }
100 return result;
101 }
102
103 // API search type: zip_with_defaults : (((a, b) -> c), a, b, [a], [b]) -> [c]
104 // fwd bind count: 4
105 // Zip two sequences and using a binary function
106 // and extrapolate the shorter sequence with a default value.
107 // zip_with_defaults((+), 6, 7, [1,2,3], [1,2]) == [2,4,10]
108 // zip_with_defaults((+), 6, 7, [1,2], [1,2,3]) == [2,4,9]
109 template <
110 typename ContainerIn1,
111 typename ContainerIn2,
112 typename F,
113 typename X = typename ContainerIn1::value_type,
114 typename Y = typename ContainerIn2::value_type>
zip_with_defaults(F f,const X & default_x,const Y & default_y,const ContainerIn1 & xs,const ContainerIn2 & ys)115 auto zip_with_defaults(F f,
116 const X& default_x,
117 const Y& default_y,
118 const ContainerIn1& xs,
119 const ContainerIn2& ys)
120 {
121 internal::trigger_static_asserts<internal::zip_with_tag, F, X, Y>();
122 const auto size_xs = size_of_cont(xs);
123 const auto size_ys = size_of_cont(ys);
124 if (size_xs < size_ys)
125 {
126 const auto extended_xs = append(
127 xs,
128 replicate<X, ContainerIn1>(size_ys - size_xs, default_x));
129 return zip_with(f, extended_xs, ys);
130 }
131 else if (size_xs > size_ys)
132 {
133 const auto extended_ys = append(
134 ys,
135 replicate<Y, ContainerIn2>(size_xs - size_ys, default_y));
136 return zip_with(f, xs, extended_ys);
137 }
138 return zip_with(f, xs, ys);
139 }
140
141 // API search type: zip : ([a], [b]) -> [(a, b)]
142 // fwd bind count: 1
143 // Combine two sequences to one sequence of pairs.
144 // zip([1, 2, 3], [5, 6]) == [(1, 5), (2, 6)]
145 template <typename ContainerIn1, typename ContainerIn2,
146 typename X = typename ContainerIn1::value_type,
147 typename Y = typename ContainerIn2::value_type>
zip(const ContainerIn1 & xs,const ContainerIn2 & ys)148 auto zip(const ContainerIn1& xs, const ContainerIn2& ys)
149 {
150 auto MakePair = [](const X& x, const Y& y)
151 { return std::make_pair(x, y); };
152 return zip_with(MakePair, xs, ys);
153 }
154
155 // API search type: zip_repeat : ([a], [b]) -> [(a, b)]
156 // fwd bind count: 1
157 // Similar to zip but repeats the shorter sequence
158 // to align with the longer sequence.
159 // zip([1, 2, 3, 4], [5, 6]) == [(1, 5), (2, 6), (3, 5), (4, 6)]
160 template <typename ContainerIn1, typename ContainerIn2>
zip_repeat(const ContainerIn1 & xs,const ContainerIn2 & ys)161 auto zip_repeat(const ContainerIn1& xs, const ContainerIn2& ys)
162 {
163 auto nx = xs.size();
164 auto ny = ys.size();
165 auto qx = ny/nx + (ny % nx ? 1 : 0);
166 auto qy = nx/ny + (nx % ny ? 1 : 0);
167 return zip(qx > 1 ? repeat(qx, xs) : xs,
168 qy > 1 ? repeat(qy, ys) : ys);
169 }
170
171 // API search type: unzip : [(a, b)] -> ([a], [b])
172 // fwd bind count: 0
173 // Split a sequence of pairs into two sequences.
174 // unzip([(1, 5), (2, 6)]) == ([1, 2], [5, 6])
175 template <typename ContainerIn,
176 typename TIn = typename ContainerIn::value_type,
177 typename X = typename TIn::first_type,
178 typename Y = typename TIn::second_type,
179 typename ContainerOutX = typename internal::same_cont_new_t<ContainerIn, X>::type,
180 typename ContainerOutY = typename internal::same_cont_new_t<ContainerIn, Y>::type>
unzip(const ContainerIn & pairs)181 std::pair<ContainerOutX, ContainerOutY> unzip(const ContainerIn& pairs)
182 {
183 ContainerOutX firsts;
184 ContainerOutY seconds;
185 internal::prepare_container(firsts, size_of_cont(pairs));
186 internal::prepare_container(seconds, size_of_cont(pairs));
187 auto itFirsts = internal::get_back_inserter(firsts);
188 auto itSeconds = internal::get_back_inserter(seconds);
189 for (const auto& pair : pairs)
190 {
191 *itFirsts = pair.first;
192 *itSeconds = pair.second;
193 }
194 return std::make_pair(firsts, seconds);
195 }
196
197 // API search type: fst : ((a, b)) -> a
198 // fwd bind count: 0
199 // Return the first element of a pair.
200 // fst((0, 1)) == 0
201 template <typename X, typename Y>
202 X fst(const std::pair<X, Y>& pair)
203 {
204 return pair.first;
205 }
206
207 // API search type: snd : ((a, b)) -> b
208 // fwd bind count: 0
209 // Return the second element of a pair.
210 // snd((0, 1)) == 1
211 template <typename X, typename Y>
snd(const std::pair<X,Y> & pair)212 Y snd(const std::pair<X, Y>& pair)
213 {
214 return pair.second;
215 }
216
217 // API search type: transform_fst : ((a -> c), (a, b)) -> (c, b)
218 // fwd bind count: 1
219 // Apply a function to the first element of a pair.
220 // transform_fst(square, (4, 5)) == (16, 5)
221 template <typename X, typename Y, typename F,
222 typename ResultFirst = std::decay_t<internal::invoke_result_t<F, X>>>
transform_fst(F f,const std::pair<X,Y> & pair)223 std::pair<ResultFirst, Y> transform_fst(F f, const std::pair<X, Y>& pair)
224 {
225 internal::trigger_static_asserts<internal::transform_fst_tag, F, X>();
226 return std::make_pair(internal::invoke(f, pair.first), pair.second);
227 }
228
229 // API search type: transform_snd : ((b -> c), (a, b)) -> (a, c)
230 // fwd bind count: 1
231 // Apply a function to the second element of a pair.
232 // transform_snd(square, (4, 5)) == (4, 25)
233 template <typename X, typename Y, typename F,
234 typename ResultSecond = std::decay_t<internal::invoke_result_t<F, Y>>>
transform_snd(F f,const std::pair<X,Y> & pair)235 std::pair<X, ResultSecond> transform_snd(F f, const std::pair<X, Y>& pair)
236 {
237 internal::trigger_static_asserts<internal::transform_snd_tag, F, Y>();
238 return std::make_pair(pair.first, internal::invoke(f, pair.second));
239 }
240
241 // API search type: transform_pair : ((a -> c), (b -> d), (a, b)) -> (c, d)
242 // fwd bind count: 2
243 // Apply functions the both parts of a pair.
244 // transform_pair(square, square, (4, 5)) == (16, 25)
245 template <
246 typename X,
247 typename Y,
248 typename F,
249 typename G,
250 typename ResultFirst = std::decay_t<internal::invoke_result_t<F, X>>,
251 typename ResultSecond = std::decay_t<internal::invoke_result_t<G, Y>>>
transform_pair(F f,G g,const std::pair<X,Y> & pair)252 std::pair<ResultFirst, ResultSecond> transform_pair(F f,
253 G g,
254 const std::pair<X, Y>& pair)
255 {
256 internal::trigger_static_asserts<internal::transform_fst_tag, F, X>();
257 internal::trigger_static_asserts<internal::transform_snd_tag, G, Y>();
258 return std::make_pair(internal::invoke(f, pair.first),
259 internal::invoke(g, pair.second));
260 }
261
262 // API search type: swap_pair_elems : (a, b) -> (b, a)
263 // fwd bind count: 0
264 // Swap the first and the second element of a pair.
265 // swap_pair_elems((3,4)) == (4,3)
266 template <typename X, typename Y>
swap_pair_elems(const std::pair<X,Y> & pair)267 std::pair<Y, X> swap_pair_elems(const std::pair<X, Y>& pair)
268 {
269 return std::make_pair(pair.second, pair.first);
270 }
271
272 // API search type: swap_pairs_elems : [(a, b)] -> [(b, a)]
273 // fwd bind count: 0
274 // Swap the first and the second element of every pair in a sequence.
275 // swap_pairs_elems([(1,2), (3,4)]) == [(2,1), (4,3)]
276 template <typename ContainerIn,
277 typename X = typename ContainerIn::value_type::first_type,
278 typename Y = typename ContainerIn::value_type::second_type>
swap_pairs_elems(const ContainerIn & xs)279 auto swap_pairs_elems(const ContainerIn& xs)
280 {
281 return fplus::transform(swap_pair_elems<X, Y>, xs);
282 }
283
284 // API search type: adjacent_pairs : [a] -> [(a, a)]
285 // fwd bind count: 0
286 // Split a sequence into pairs of elements.
287 // adjacent_pairs([0,1,2,3,4]) == [(0,1), (2,3)]
288 // Also known as zip_with_next.
289 template <typename Container,
290 typename ContainerOut =
291 typename internal::same_cont_new_t<Container,
292 std::pair<
293 typename Container::value_type,
294 typename Container::value_type>>::type>
adjacent_pairs(const Container & xs)295 ContainerOut adjacent_pairs(const Container& xs)
296 {
297 typedef typename Container::value_type T;
298 static_assert(std::is_convertible<
299 std::pair<T, T>,
300 typename ContainerOut::value_type>::value,
301 "ContainerOut can not store pairs of elements from ContainerIn.");
302 ContainerOut result;
303 if (size_of_cont(xs) < 2)
304 return result;
305 const std::size_t out_size = size_of_cont(xs) / 2;
306 internal::prepare_container(result, out_size);
307 auto itOut = internal::get_back_inserter(result);
308 auto it1 = std::begin(xs);
309 auto it2 = it1;
310 internal::advance_iterator(it2, 1);
311 const auto it_source_end =
312 internal::add_to_iterator(std::begin(xs), out_size + out_size);
313 for (;;)
314 {
315 *itOut = std::make_pair(*it1, *it2);
316 internal::advance_iterator(it1, 2);
317 if (it1 == it_source_end)
318 break;
319 internal::advance_iterator(it2, 2);
320 }
321 return result;
322 }
323
324 // API search type: overlapping_pairs : [a] -> [(a, a)]
325 // fwd bind count: 0
326 // Zip a sequence with itself shifted one element.
327 // overlapping_pairs([0,1,2,3]) == [(0,1),(1,2),(2,3)]
328 template <typename Container,
329 typename ContainerOut =
330 typename internal::same_cont_new_t<Container,
331 std::pair<
332 typename Container::value_type,
333 typename Container::value_type>, -1>::type>
overlapping_pairs(const Container & xs)334 ContainerOut overlapping_pairs(const Container& xs)
335 {
336 typedef typename Container::value_type T;
337 static_assert(std::is_convertible<
338 std::pair<T, T>,
339 typename ContainerOut::value_type>::value,
340 "ContainerOut can not store pairs of elements from ContainerIn.");
341 ContainerOut result;
342 if (size_of_cont(xs) < 2)
343 return result;
344 internal::prepare_container(result, size_of_cont(xs) - 1);
345 auto itOut = internal::get_back_inserter(result);
346 auto it1 = std::begin(xs);
347 auto it2 = it1;
348 internal::advance_iterator(it2, 1);
349 for (; it2 != std::end(xs); ++it1, ++it2)
350 {
351 *itOut = std::make_pair(*it1, *it2);
352 }
353 return result;
354 }
355
356 // API search type: overlapping_pairs_cyclic : [a] -> [(a, a)]
357 // fwd bind count: 0
358 // Zip a sequence with itself shifted one element,
359 // finally zipping the last element with the first one.
360 // overlapping_pairs_cyclic([0,1,2,3]) == [(0,1),(1,2),(2,3),(3,0)]
361 template <typename Container,
362 typename ContainerOut =
363 typename internal::same_cont_new_t<Container,
364 std::pair<
365 typename Container::value_type,
366 typename Container::value_type>, 0>::type>
overlapping_pairs_cyclic(const Container & xs)367 ContainerOut overlapping_pairs_cyclic(const Container& xs)
368 {
369 typedef typename Container::value_type T;
370 static_assert(std::is_convertible<
371 std::pair<T, T>,
372 typename ContainerOut::value_type>::value,
373 "ContainerOut can not store pairs of elements from ContainerIn.");
374 ContainerOut result;
375 if (size_of_cont(xs) < 2)
376 return result;
377 internal::prepare_container(result, size_of_cont(xs));
378 auto itOut = internal::get_back_inserter(result);
379 auto it1 = std::begin(xs);
380 auto it2 = it1;
381 internal::advance_iterator(it2, 1);
382 for (; it2 != std::end(xs); ++it1, ++it2)
383 {
384 *itOut = std::make_pair(*it1, *it2);
385 }
386 *itOut = std::make_pair(*it1, xs.front());
387 return result;
388 }
389
390 // API search type: enumerate : [a] -> [(Int, a)]
391 // fwd bind count: 0
392 // Attach its index to every element of a sequence.
393 // enumerate([6,4,7,6]) == [(0, 6), (1, 4), (2, 7), (3, 6)]
394 template <typename Container>
enumerate(const Container & xs)395 auto enumerate(const Container& xs)
396 {
397 return zip(all_idxs(xs), xs);
398 }
399
400 // API search type: inner_product_with : (((a, a) -> b), ((b, b) -> b), b, [a], [a]) -> b
401 // fwd bind count: 4
402 // Calculate the inner product of two sequences using custom operations.
403 // inner_product_with((+), (*), [1, 2, 3], [4, 5, 6]) == [32]
404 template <
405 typename ContainerIn1,
406 typename ContainerIn2,
407 typename OP1,
408 typename OP2,
409 typename Acc,
410 typename X = typename ContainerIn1::value_type,
411 typename Y = typename ContainerIn2::value_type,
412 typename OP2Out = internal::invoke_result_t<OP2, X, Y>>
inner_product_with(OP1 op1,OP2 op2,const Acc & value,const ContainerIn1 & xs,const ContainerIn2 & ys)413 auto inner_product_with(OP1 op1,
414 OP2 op2,
415 const Acc& value,
416 const ContainerIn1& xs,
417 const ContainerIn2& ys)
418 {
419 internal::trigger_static_asserts<internal::inner_product_with_tag, OP2, X, Y>();
420 internal::trigger_static_asserts<internal::inner_product_with_tag, OP1, Acc, OP2Out>();
421 assert(size_of_cont(xs) == size_of_cont(ys));
422 return std::inner_product(
423 std::begin(xs), std::end(xs), std::begin(ys), value, op1, op2);
424 }
425
426 // API search type: inner_product : (a, [a], [a]) -> a
427 // fwd bind count: 2
428 // Calculate the inner product of two sequences.
429 // inner_product([1, 2, 3], [4, 5, 6]) == [32]
430 template <typename ContainerIn1, typename ContainerIn2,
431 typename Z>
inner_product(const Z & value,const ContainerIn1 & xs,const ContainerIn2 & ys)432 Z inner_product(const Z& value,
433 const ContainerIn1& xs, const ContainerIn2& ys)
434 {
435 assert(size_of_cont(xs) == size_of_cont(ys));
436
437 return std::inner_product(
438 std::begin(xs), std::end(xs), std::begin(ys), value);
439 }
440
441 // API search type: first_mismatch_idx_by : (((a, b) -> Bool), [a], [b]) -> Maybe Int
442 // fwd bind count: 2
443 // Find the first index at which the two sequences differ
444 // using a binary predicate.
445 // first_mismatch_idx_by((==), [1, 2, 3], [1, 4, 3]) == Just 1
446 // first_mismatch_idx_by((==), [1, 2, 3], [1, 4]) == Just 1
447 // first_mismatch_idx_by((==), [1, 2, 3], [1, 2]) == Nothing
448 // first_mismatch_idx_by((==), [], [1, 2]) == Nothing
449 template <typename ContainerIn1, typename ContainerIn2,
450 typename BinaryPredicate>
first_mismatch_idx_by(BinaryPredicate p,const ContainerIn1 & xs,const ContainerIn2 & ys)451 maybe<std::size_t> first_mismatch_idx_by(BinaryPredicate p,
452 const ContainerIn1& xs, const ContainerIn2& ys)
453 {
454 auto itXs = std::begin(xs);
455 auto itYs = std::begin(ys);
456 std::size_t minSize = std::min(size_of_cont(xs), size_of_cont(ys));
457 for (std::size_t i = 0; i < minSize; ++i)
458 {
459 if (!internal::invoke(p, *itXs, *itYs))
460 {
461 return just(i);
462 }
463 ++itXs;
464 ++itYs;
465 }
466 return nothing<std::size_t>();
467 }
468
469 // API search type: first_mismatch_by : (((a, b) -> Bool), [a], [b]) -> Maybe (a, b)
470 // fwd bind count: 2
471 // Find the first pair of elements differing in the two sequences
472 // using a binary predicate.
473 // first_mismatch_by((==), [1, 2, 3], [1, 4, 3]) == Just (2, 4)
474 // first_mismatch_by((==), [1, 2, 3], [1, 4]) == Just (2, 4)
475 // first_mismatch_by((==), [1, 2, 3], [1, 2]) == Nothing
476 // first_mismatch_by((==), [], [1, 2]) == Nothing
477 template <typename ContainerIn1, typename ContainerIn2,
478 typename BinaryPredicate,
479 typename X = typename ContainerIn1::value_type,
480 typename Y = typename ContainerIn2::value_type,
481 typename TOut = std::pair<X, Y>>
first_mismatch_by(BinaryPredicate p,const ContainerIn1 & xs,const ContainerIn2 & ys)482 maybe<TOut> first_mismatch_by(BinaryPredicate p,
483 const ContainerIn1& xs, const ContainerIn2& ys)
484 {
485 const auto maybe_idx = first_mismatch_idx_by(p, xs, ys);
486 if (is_nothing(maybe_idx))
487 {
488 return nothing<TOut>();
489 }
490 else
491 {
492 const auto idx = maybe_idx.unsafe_get_just();
493 return just(std::make_pair(
494 elem_at_idx(idx, xs),
495 elem_at_idx(idx, ys)));
496 }
497 }
498
499 // API search type: first_mismatch_idx_on : ((a -> b), [a], [a]) -> Maybe Int
500 // fwd bind count: 2
501 // Find the first index of elements differing in the two sequences
502 // using a transformer.
503 // first_mismatch_idx_on((mod 2), [1, 2, 3], [3, 5, 3]) == 1
504 // first_mismatch_idx_on((mod 2), [1, 2, 3], [1, 5]) == 1
505 // first_mismatch_idx_on((mod 2), [1, 2, 3], [1, 6]) == Nothing
506 // first_mismatch_idx_on((mod 2), [], [1, 2]) == Nothing
507 template <typename ContainerIn1, typename ContainerIn2,
508 typename F,
509 typename X = typename ContainerIn1::value_type,
510 typename Y = typename ContainerIn2::value_type,
511 typename TOut = std::pair<X, Y>>
first_mismatch_idx_on(F f,const ContainerIn1 & xs,const ContainerIn2 & ys)512 maybe<std::size_t> first_mismatch_idx_on(F f,
513 const ContainerIn1& xs, const ContainerIn2& ys)
514 {
515 static_assert(std::is_same<X, Y>::value,
516 "Both containers must have the same element type.");
517 return first_mismatch_idx_by(is_equal_by(f), xs, ys);
518 }
519
520 // API search type: first_mismatch_on : ((a -> b), [a], [a]) -> Maybe (a, a)
521 // fwd bind count: 2
522 // Find the first pair of elements differing in the two sequences
523 // using a transformer.
524 // first_mismatch_on((mod 2), [1, 2, 3], [3, 5, 3]) == Just (2, 5)
525 // first_mismatch_on((mod 2), [1, 2, 3], [1, 5]) == Just (2, 5)
526 // first_mismatch_on((mod 2), [1, 2, 3], [1, 6]) == Nothing
527 // first_mismatch_on((mod 2), [], [1, 2]) == Nothing
528 template <typename ContainerIn1, typename ContainerIn2,
529 typename F,
530 typename X = typename ContainerIn1::value_type,
531 typename Y = typename ContainerIn2::value_type,
532 typename TOut = std::pair<X, Y>>
first_mismatch_on(F f,const ContainerIn1 & xs,const ContainerIn2 & ys)533 maybe<TOut> first_mismatch_on(F f,
534 const ContainerIn1& xs, const ContainerIn2& ys)
535 {
536 static_assert(std::is_same<X, Y>::value,
537 "Both containers must have the same element type.");
538 return first_mismatch_by(is_equal_by(f), xs, ys);
539 }
540
541 // API search type: first_mismatch_idx : ([a], [a]) -> Maybe Int
542 // fwd bind count: 2
543 // Find the first index of elements differing in the two sequences.
544 // first_mismatch_idx((==), [1, 2, 3], [1, 4, 3]) == 1
545 // first_mismatch_idx((==), [1, 2, 3], [1, 4]) == 1
546 // first_mismatch_idx((==), [1, 2, 3], [1, 2]) == Nothing
547 // first_mismatch_idx((==), [], [1, 2]) == Nothing
548 template <typename ContainerIn1, typename ContainerIn2,
549 typename X = typename ContainerIn1::value_type,
550 typename Y = typename ContainerIn2::value_type>
first_mismatch_idx(const ContainerIn1 & xs,const ContainerIn2 & ys)551 maybe<std::size_t> first_mismatch_idx(
552 const ContainerIn1& xs, const ContainerIn2& ys)
553 {
554 static_assert(std::is_same<X, Y>::value,
555 "Both containers must have the same element type.");
556 return first_mismatch_idx_by(std::equal_to<X>(), xs, ys);
557 }
558
559 // API search type: first_mismatch : ([a], [a]) -> Maybe (a, a)
560 // fwd bind count: 2
561 // Find the first pair of elements differing in the two sequences
562 // first_mismatch((==), [1, 2, 3], [1, 4, 3]) == Just (2, 4)
563 // first_mismatch((==), [1, 2, 3], [1, 4]) == Just (2, 4)
564 // first_mismatch((==), [1, 2, 3], [1, 2]) == Nothing
565 // first_mismatch((==), [], [1, 2]) == Nothing
566 template <typename ContainerIn1, typename ContainerIn2,
567 typename X = typename ContainerIn1::value_type,
568 typename Y = typename ContainerIn2::value_type,
569 typename TOut = std::pair<X, Y>>
first_mismatch(const ContainerIn1 & xs,const ContainerIn2 & ys)570 maybe<TOut> first_mismatch(const ContainerIn1& xs, const ContainerIn2& ys)
571 {
572 static_assert(std::is_same<X, Y>::value,
573 "Both containers must have the same element type.");
574 return first_mismatch_by(std::equal_to<X>(), xs, ys);
575 }
576
577 // API search type: first_match_idx_by : (((a, b) -> Bool), [a], [b]) -> Maybe Int
578 // fwd bind count: 2
579 // Find the first index at which the two sequences equal
580 // using a binary predicate.
581 // first_match_idx_by((==), [1, 2, 3], [3, 2, 3]) == Just 1
582 // first_match_idx_by((==), [], [1, 2]) == Nothing
583 template <typename ContainerIn1, typename ContainerIn2,
584 typename F,
585 typename X = typename ContainerIn1::value_type,
586 typename Y = typename ContainerIn2::value_type>
first_match_idx_by(F f,const ContainerIn1 & xs,const ContainerIn2 & ys)587 maybe<std::size_t> first_match_idx_by(F f,
588 const ContainerIn1& xs, const ContainerIn2& ys)
589 {
590 auto itXs = std::begin(xs);
591 auto itYs = std::begin(ys);
592 std::size_t minSize = std::min(size_of_cont(xs), size_of_cont(ys));
593 for (std::size_t i = 0; i < minSize; ++i)
594 {
595 if (internal::invoke(f, *itXs, *itYs))
596 {
597 return just(i);
598 }
599 ++itXs;
600 ++itYs;
601 }
602 return nothing<std::size_t>();
603 }
604
605 // API search type: first_match_by : (((a, b) -> Bool), [a], [b]) -> Maybe (a, b)
606 // fwd bind count: 2
607 // Find the first pair of equal elements in the two sequences
608 // using a binary predicate.
609 // first_match_by((==), [1, 2, 3], [3, 2, 3]) == Just (2, 2)
610 // first_match_by((==), [], [1, 2]) == Nothing
611 template <typename ContainerIn1, typename ContainerIn2,
612 typename F,
613 typename X = typename ContainerIn1::value_type,
614 typename Y = typename ContainerIn2::value_type,
615 typename TOut = std::pair<X, Y>>
first_match_by(F f,const ContainerIn1 & xs,const ContainerIn2 & ys)616 maybe<TOut> first_match_by(F f, const ContainerIn1& xs, const ContainerIn2& ys)
617 {
618 const auto maybe_idx = first_match_idx_by(f, xs, ys);
619 if (is_nothing(maybe_idx))
620 {
621 return nothing<TOut>();
622 }
623 else
624 {
625 const auto idx = maybe_idx.unsafe_get_just();
626 return just(std::make_pair(
627 elem_at_idx(idx, xs),
628 elem_at_idx(idx, ys)));
629 }
630 }
631
632 // API search type: first_match_idx_on : ((a -> b), [a], [a]) -> Maybe Int
633 // fwd bind count: 2
634 // Find the first index of equal elements in the two sequences
635 // using a transformer.
636 // first_match_idx_on((mod 2), [1, 2, 3], [2, 4, 3]) == 1
637 // first_match_idx_on((mod 2), [], [1, 2]) == Nothing
638 template <typename ContainerIn1, typename ContainerIn2,
639 typename F,
640 typename X = typename ContainerIn1::value_type,
641 typename Y = typename ContainerIn2::value_type>
first_match_idx_on(F f,const ContainerIn1 & xs,const ContainerIn2 & ys)642 maybe<std::size_t> first_match_idx_on(F f,
643 const ContainerIn1& xs, const ContainerIn2& ys)
644 {
645 static_assert(std::is_same<X, Y>::value,
646 "Both containers must have the same element type.");
647 return first_match_idx_by(is_equal_by(f), xs, ys);
648 }
649
650 // API search type: first_match_on : ((a -> b), [a], [a]) -> Maybe (a, a)
651 // fwd bind count: 2
652 // Find the first pair of equal elements in the two sequences
653 // using a transformer.
654 // first_match_on((mod 2), [1, 2, 3], [2, 4, 3]) == Just (2, 4)
655 // first_match_on((mod 2), [], [1, 2]) == Nothing
656 template <typename ContainerIn1, typename ContainerIn2,
657 typename F,
658 typename X = typename ContainerIn1::value_type,
659 typename Y = typename ContainerIn2::value_type,
660 typename TOut = std::pair<X, Y>>
first_match_on(F f,const ContainerIn1 & xs,const ContainerIn2 & ys)661 maybe<TOut> first_match_on(F f, const ContainerIn1& xs, const ContainerIn2& ys)
662 {
663 static_assert(std::is_same<X, Y>::value,
664 "Both containers must have the same element type.");
665 return first_match_by(is_equal_by(f), xs, ys);
666 }
667
668 // API search type: first_match_idx : ([a], [a]) -> Maybe Int
669 // fwd bind count: 2
670 // Find the first index of equal elements in the two sequences.
671 // first_match_idx((==), [1, 2, 3], [5, 2, 3]) == 1
672 // first_match_idx((==), [], [1, 2]) == Nothing
673 template <typename ContainerIn1, typename ContainerIn2,
674 typename X = typename ContainerIn1::value_type,
675 typename Y = typename ContainerIn2::value_type>
first_match_idx(const ContainerIn1 & xs,const ContainerIn2 & ys)676 maybe<std::size_t> first_match_idx(
677 const ContainerIn1& xs, const ContainerIn2& ys)
678 {
679 static_assert(std::is_same<X, Y>::value,
680 "Both containers must have the same element type.");
681 return first_match_idx_by(std::equal_to<X>(), xs, ys);
682 }
683
684 // API search type: first_match : ([a], [a]) -> Maybe (a, a)
685 // fwd bind count: 2
686 // Find the first pair of equal elements in the two sequences.
687 // first_match((==), [1, 2, 3], [5, 2, 3]) == Just (2, 2)
688 // first_match((==), [], [1, 2]) == Nothing
689 template <typename ContainerIn1, typename ContainerIn2,
690 typename X = typename ContainerIn1::value_type,
691 typename Y = typename ContainerIn2::value_type,
692 typename TOut = std::pair<X, Y>>
first_match(const ContainerIn1 & xs,const ContainerIn2 & ys)693 maybe<TOut> first_match(const ContainerIn1& xs, const ContainerIn2& ys)
694 {
695 static_assert(std::is_same<X, Y>::value,
696 "Both containers must have the same element type.");
697 return first_match_by(std::equal_to<X>(), xs, ys);
698 }
699
700 } // namespace fplus
701