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 #include <doctest/doctest.h>
8 #include <fplus/fplus.hpp>
9 
10 namespace {
11     typedef std::vector<int> IntVector;
is_odd_int(int x)12     bool is_odd_int(int x)
13     {
14         return (x % 2 != 0);
15     }
16 
is_even_int(int x)17     bool is_even_int(int x)
18     {
19         return (x % 2 == 0);
20     }
21 
times_3(int x)22     int times_3(int x)
23     {
24         return 3 * x;
25     }
26 
as_string_length(int i)27     int as_string_length(int i)
28     {
29         return static_cast<int>(std::to_string(i).size());
30     }
31 
__anonaf692af60202(int x)32     const auto times_3_lambda = [](int x){return times_3(x);};
__anonaf692af60302(int x)33     const auto is_odd_int_lambda = [](int x){return is_odd_int(x);};
__anonaf692af60402(int x)34     const auto as_string_length_lambda = [](int x){return as_string_length(x);};
35 
36     int (*times_3_fn_ptr)(int) = &times_3;
37 
38     struct times_3_struct {
operator ()__anonaf692af60111::times_3_struct39         int operator() (const int x)
40         { return times_3(x); }
sttcMemF__anonaf692af60111::times_3_struct41         static int sttcMemF(int x)
42         { return times_3(x); }
43     };
44 
45     std::function<int(int)> times_3_std_function = times_3_lambda;
46 }
47 
48 TEST_CASE("fwd_test - apply")
49 {
50     using namespace fplus;
51 
52     const auto result_old_style =
53         sum(
54         transform(as_string_length,
55         drop_if(is_odd_int,
56         transform(times_3,
57         numbers(0, 10)))));
58 
59     const auto result_new_style = fwd::apply(
60         numbers(0, 10)
61         , fwd::transform(times_3)
62         , fwd::drop_if(is_odd_int)
63         , fwd::transform(as_string_length)
64         , fwd::sum());
65 
66     REQUIRE_EQ(result_old_style, result_new_style);
67 }
68 
69 TEST_CASE("fwd_test - compose")
70 {
71     using namespace fplus;
72 
73     const auto function_chain_old_style = compose(
74         bind_1st_of_2(transform<decltype(times_3), const std::vector<int>&, std::vector<int>>, times_3),
75         bind_1st_of_2(drop_if<decltype(is_odd_int), const std::vector<int>&>, is_odd_int),
76         bind_1st_of_2(transform<decltype(as_string_length_lambda), const std::vector<int>&>, as_string_length_lambda),
77         sum<std::vector<int>>);
78 
79     const auto function_chain_new_style = fwd::compose(
80         fwd::transform(times_3),
81         fwd::drop_if(is_odd_int_lambda),
82         fwd::transform(as_string_length),
83         fwd::sum());
84 
85     const auto xs = numbers(0, 10);
86     REQUIRE_EQ(function_chain_old_style(xs), function_chain_new_style(xs));
87 }
88 
89 TEST_CASE("fwd_test - and_then_maybe")
90 {
91     using namespace fplus;
92     const auto sqrtToMaybeInt = [](int x) -> fplus::maybe<int>
__anonaf692af60502(int x) 93     {
94         return x < 0 ? fplus::nothing<int>() :
95                 fplus::just(fplus::round(sqrt(static_cast<float>(x))));
96     };
97     REQUIRE_EQ(
98         fwd::apply(just(4)
99             , fwd::and_then_maybe(sqrtToMaybeInt))
100         , just(2));
101 }
102 
103 TEST_CASE("fwd_test - fold_left")
104 {
105     using namespace fplus;
106 
107     const auto fold_result_old_style =
108         fold_left(std::plus<int>(), 0, numbers(0, 10));
109 
110     const auto fold_result_new_style = fwd::apply(
111         numbers(0, 10)
112         , fwd::fold_left(std::plus<int>(), 0));
113 
114     REQUIRE_EQ(fold_result_old_style, fold_result_new_style);
115 }
116 
117 TEST_CASE("fwd_test - transform_nested")
118 {
119     using namespace fplus;
120 
121     typedef std::vector<int> ints;
122     const std::vector<ints> nested_ints = {{1,2,3},{4,5,6}};
123 
124     const auto nested_transformed_old_style = transform(
125         bind_1st_of_2(transform<decltype(times_3), const std::vector<int>&, std::vector<int>>, times_3),
126         nested_ints);
127 
128     const auto nested_transformed_new_style = fwd::apply(
129         nested_ints
130         , fwd::transform(fwd::transform(times_3_lambda)));
131 
132     REQUIRE_EQ(nested_transformed_old_style, nested_transformed_new_style);
133 }
134 
135 TEST_CASE("fwd_test - different_function_types_apply")
136 {
137     using namespace fplus;
138 
139     const std::vector<int> xs = {1,2,3};
140     const auto result = transform(times_3, xs);
141 
142     REQUIRE_EQ(fwd::apply(xs, fwd::transform(times_3)), result);
143     REQUIRE_EQ(fwd::apply(xs, fwd::transform(times_3_lambda)), result);
144     REQUIRE_EQ(fwd::apply(xs, fwd::transform(times_3_std_function)), result);
145     REQUIRE_EQ(fwd::apply(xs, fwd::transform(times_3_fn_ptr)), result);
146     REQUIRE_EQ(fwd::apply(xs, fwd::transform(&times_3_struct::sttcMemF)), result);
147     REQUIRE_EQ(fwd::apply(xs, fwd::transform(times_3_struct())), result);
148 }
149 
150 TEST_CASE("fwd_test - different_function_types_compose")
151 {
152     using namespace fplus;
153 
154     const std::vector<int> xs = {1,2,3};
155     const auto result = transform(times_3, transform(times_3, xs));
156 
157     REQUIRE_EQ(fwd::transform(fwd::compose(times_3, times_3))(xs), result);
158     REQUIRE_EQ(fwd::transform(fwd::compose(times_3_lambda, times_3_lambda))(xs), result);
159     REQUIRE_EQ(fwd::transform(fwd::compose(times_3_std_function, times_3_std_function))(xs), result);
160     REQUIRE_EQ(fwd::transform(fwd::compose(&times_3_struct::sttcMemF, &times_3_struct::sttcMemF))(xs), result);
161     REQUIRE_EQ(fwd::transform(fwd::compose(times_3_fn_ptr, times_3_fn_ptr))(xs), result);
162 }
163 
collatz_seq(int x)164 std::list<int> collatz_seq(int x)
165 {
166     std::list<int> result;
167     while (x > 1)
168     {
169         result.push_back(x);
170         if (x % 2 == 0)
171             x = x / 2;
172         else
173             x = 3 * x + 1;
174     }
175     result.push_back(x);
176     return result;
177 }
178 
179 TEST_CASE("fwd_test - collatz")
180 {
181     using namespace fplus;
182 
183     auto collatz_dict = fwd::apply(
184         fplus::numbers<int>(0, 20)
185         , fwd::create_map_with(fwd::compose(
186             collatz_seq,
187             fwd::show_cont_with(" => ")))
188         );
189 }
190 
191 TEST_CASE("fwd_test - fwd_flip")
192 {
193     using namespace fplus;
194     std::vector<std::vector<std::size_t>> idxs = {{0,1,2}, {2,0}};
195     const std::vector<int> xs = {0,10,20};
196     const std::vector<int> ys = fwd::transform_and_concat(fwd::flip::elems_at_idxs(xs))(idxs);
197     const std::vector<int> result = {0,10,20,20,0};
198     REQUIRE_EQ(ys, result);
199 }
200 
201 TEST_CASE("fwd_test - keep_if")
202 {
203     const std::vector<int> v = { 1, 2, 3, 2, 4, 5 };
204     auto result = fplus::fwd::keep_if(is_even_int)(v);
205     REQUIRE_EQ(result, std::vector<int>({2, 2, 4}));
206 }
207 
208 TEST_CASE("fwd_test - keep_if_r_value")
209 {
210     auto result = fplus::fwd::keep_if(is_even_int)(std::vector<int>({1,2,3,2,4,5}));
211     REQUIRE_EQ(result, std::vector<int>({2, 2, 4}));
212 }
213 
214 TEST_CASE("fwd_test - zip_with")
215 {
216     using namespace fplus;
__anonaf692af60602(int x, int y) 217     const auto multiply_int = [](int x, int y) -> int { return x * y; };
__anonaf692af60702(auto x, auto y)218     const auto multiply_generic = [](auto x, auto y){ return x * y; };
219 
220     IntVector xs = {1,2,3,4,2};
221     IntVector ys = {2,2,3,1};
222     IntVector xs_mult_ys = {2,4,9,4};
223 
224     REQUIRE_EQ(fwd::zip_with(multiply_int, ys)(xs), xs_mult_ys);
225     REQUIRE_EQ(fwd::zip_with(multiply_generic, ys)(xs), xs_mult_ys);
226 }
227 
228 TEST_CASE("fwd_test - append")
229 {
230     using namespace fplus;
231 
232     IntVector xs = {1,2,3,4,2};
233     IntVector ys = {2,2,3,1};
234     IntVector xs_append_ys = {1,2,3,4,2,2,2,3,1};
235 
236     REQUIRE_EQ(fwd::append(xs)(ys), xs_append_ys);
237 }
238