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) = ×_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(×_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(×_3_struct::sttcMemF, ×_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