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