1//     __ _____ _____ _____
2//  __|  |   __|     |   | |  JSON for Modern C++ (supporting code)
3// |  |  |__   |  |  | | | |  version 3.11.2
4// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
5//
6// SPDX-FileCopyrightText: 2013-2022 Niels Lohmann <https://nlohmann.me>
7// SPDX-License-Identifier: MIT
8
9#include "doctest_compatibility.h"
10
11#include <nlohmann/json.hpp>
12using nlohmann::json;
13
14TEST_CASE("algorithms")
15{
16    json j_array = {13, 29, 3, {{"one", 1}, {"two", 2}}, true, false, {1, 2, 3}, "foo", "baz"};
17    json j_object = {{"one", 1}, {"two", 2}};
18
19    SECTION("non-modifying sequence operations")
20    {
21        SECTION("std::all_of")
22        {
23            CHECK(std::all_of(j_array.begin(), j_array.end(), [](const json & value)
24            {
25                return !value.empty();
26            }));
27            CHECK(std::all_of(j_object.begin(), j_object.end(), [](const json & value)
28            {
29                return value.type() == json::value_t::number_integer;
30            }));
31        }
32
33        SECTION("std::any_of")
34        {
35            CHECK(std::any_of(j_array.begin(), j_array.end(), [](const json & value)
36            {
37                return value.is_string() && value.get<std::string>() == "foo";
38            }));
39            CHECK(std::any_of(j_object.begin(), j_object.end(), [](const json & value)
40            {
41                return value.get<int>() > 1;
42            }));
43        }
44
45        SECTION("std::none_of")
46        {
47            CHECK(std::none_of(j_array.begin(), j_array.end(), [](const json & value)
48            {
49                return value.empty();
50            }));
51            CHECK(std::none_of(j_object.begin(), j_object.end(), [](const json & value)
52            {
53                return value.get<int>() <= 0;
54            }));
55        }
56
57        SECTION("std::for_each")
58        {
59            SECTION("reading")
60            {
61                int sum = 0;
62
63                std::for_each(j_array.cbegin(), j_array.cend(), [&sum](const json & value)
64                {
65                    if (value.is_number())
66                    {
67                        sum += static_cast<int>(value);
68                    }
69                });
70
71                CHECK(sum == 45);
72            }
73
74            SECTION("writing")
75            {
76                auto add17 = [](json & value)
77                {
78                    if (value.is_array())
79                    {
80                        value.push_back(17);
81                    }
82                };
83
84                std::for_each(j_array.begin(), j_array.end(), add17);
85
86                CHECK(j_array[6] == json({1, 2, 3, 17}));
87            }
88        }
89
90        SECTION("std::count")
91        {
92            CHECK(std::count(j_array.begin(), j_array.end(), json(true)) == 1);
93        }
94
95        SECTION("std::count_if")
96        {
97            CHECK(std::count_if(j_array.begin(), j_array.end(), [](const json & value)
98            {
99                return (value.is_number());
100            }) == 3);
101            CHECK(std::count_if(j_array.begin(), j_array.end(), [](const json&)
102            {
103                return true;
104            }) == 9);
105        }
106
107        SECTION("std::mismatch")
108        {
109            json j_array2 = {13, 29, 3, {{"one", 1}, {"two", 2}, {"three", 3}}, true, false, {1, 2, 3}, "foo", "baz"};
110            auto res = std::mismatch(j_array.begin(), j_array.end(), j_array2.begin());
111            CHECK(*res.first == json({{"one", 1}, {"two", 2}}));
112            CHECK(*res.second == json({{"one", 1}, {"two", 2}, {"three", 3}}));
113        }
114
115        SECTION("std::equal")
116        {
117            SECTION("using operator==")
118            {
119                CHECK(std::equal(j_array.begin(), j_array.end(), j_array.begin()));
120                CHECK(std::equal(j_object.begin(), j_object.end(), j_object.begin()));
121                CHECK(!std::equal(j_array.begin(), j_array.end(), j_object.begin()));
122            }
123
124            SECTION("using user-defined comparison")
125            {
126                // compare objects only by size of its elements
127                json j_array2 = {13, 29, 3, {"Hello", "World"}, true, false, {{"one", 1}, {"two", 2}, {"three", 3}}, "foo", "baz"};
128                CHECK(!std::equal(j_array.begin(), j_array.end(), j_array2.begin()));
129                CHECK(std::equal(j_array.begin(), j_array.end(), j_array2.begin(),
130                                 [](const json & a, const json & b)
131                {
132                    return (a.size() == b.size());
133                }));
134            }
135        }
136
137        SECTION("std::find")
138        {
139            auto it = std::find(j_array.begin(), j_array.end(), json(false));
140            CHECK(std::distance(j_array.begin(), it) == 5);
141        }
142
143        SECTION("std::find_if")
144        {
145            auto it = std::find_if(j_array.begin(), j_array.end(),
146                                   [](const json & value)
147            {
148                return value.is_boolean();
149            });
150            CHECK(std::distance(j_array.begin(), it) == 4);
151        }
152
153        SECTION("std::find_if_not")
154        {
155            auto it = std::find_if_not(j_array.begin(), j_array.end(),
156                                       [](const json & value)
157            {
158                return value.is_number();
159            });
160            CHECK(std::distance(j_array.begin(), it) == 3);
161        }
162
163        SECTION("std::adjacent_find")
164        {
165            CHECK(std::adjacent_find(j_array.begin(), j_array.end()) == j_array.end());
166            CHECK(std::adjacent_find(j_array.begin(), j_array.end(),
167                                     [](const json & v1, const json & v2)
168            {
169                return v1.type() == v2.type();
170            }) == j_array.begin());
171        }
172    }
173
174    SECTION("modifying sequence operations")
175    {
176        SECTION("std::reverse")
177        {
178            std::reverse(j_array.begin(), j_array.end());
179            CHECK(j_array == json({"baz", "foo", {1, 2, 3}, false, true, {{"one", 1}, {"two", 2}}, 3, 29, 13}));
180        }
181
182        SECTION("std::rotate")
183        {
184            std::rotate(j_array.begin(), j_array.begin() + 1, j_array.end());
185            CHECK(j_array == json({29, 3, {{"one", 1}, {"two", 2}}, true, false, {1, 2, 3}, "foo", "baz", 13}));
186        }
187
188        SECTION("std::partition")
189        {
190            auto it = std::partition(j_array.begin(), j_array.end(), [](const json & v)
191            {
192                return v.is_string();
193            });
194            CHECK(std::distance(j_array.begin(), it) == 2);
195            CHECK(!it[2].is_string());
196        }
197    }
198
199    SECTION("sorting operations")
200    {
201        SECTION("std::sort")
202        {
203            SECTION("with standard comparison")
204            {
205                json j = {13, 29, 3, {{"one", 1}, {"two", 2}}, true, false, {1, 2, 3}, "foo", "baz", nullptr};
206                std::sort(j.begin(), j.end());
207                CHECK(j == json({nullptr, false, true, 3, 13, 29, {{"one", 1}, {"two", 2}}, {1, 2, 3}, "baz", "foo"}));
208            }
209
210            SECTION("with user-defined comparison")
211            {
212                json j = {3, {{"one", 1}, {"two", 2}}, {1, 2, 3}, nullptr};
213                std::sort(j.begin(), j.end(), [](const json & a, const json & b)
214                {
215                    return a.size() < b.size();
216                });
217                CHECK(j == json({nullptr, 3, {{"one", 1}, {"two", 2}}, {1, 2, 3}}));
218            }
219
220            SECTION("sorting an object")
221            {
222                json j({{"one", 1}, {"two", 2}});
223                CHECK_THROWS_WITH_AS(std::sort(j.begin(), j.end()), "[json.exception.invalid_iterator.209] cannot use offsets with object iterators", json::invalid_iterator&);
224            }
225        }
226
227        SECTION("std::partial_sort")
228        {
229            json j = {13, 29, 3, {{"one", 1}, {"two", 2}}, true, false, {1, 2, 3}, "foo", "baz", nullptr};
230            std::partial_sort(j.begin(), j.begin() + 4, j.end());
231            CHECK(j == json({nullptr, false, true, 3, {{"one", 1}, {"two", 2}}, 29, {1, 2, 3}, "foo", "baz", 13}));
232        }
233    }
234
235    SECTION("set operations")
236    {
237        SECTION("std::merge")
238        {
239            {
240                json j1 = {2, 4, 6, 8};
241                json j2 = {1, 2, 3, 5, 7};
242                json j3;
243
244                std::merge(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3));
245                CHECK(j3 == json({1, 2, 2, 3, 4, 5, 6, 7, 8}));
246            }
247        }
248
249        SECTION("std::set_difference")
250        {
251            json j1 = {1, 2, 3, 4, 5, 6, 7, 8};
252            json j2 = {1, 2, 3, 5, 7};
253            json j3;
254
255            std::set_difference(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3));
256            CHECK(j3 == json({4, 6, 8}));
257        }
258
259        SECTION("std::set_intersection")
260        {
261            json j1 = {1, 2, 3, 4, 5, 6, 7, 8};
262            json j2 = {1, 2, 3, 5, 7};
263            json j3;
264
265            std::set_intersection(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3));
266            CHECK(j3 == json({1, 2, 3, 5, 7}));
267        }
268
269        SECTION("std::set_union")
270        {
271            json j1 = {2, 4, 6, 8};
272            json j2 = {1, 2, 3, 5, 7};
273            json j3;
274
275            std::set_union(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3));
276            CHECK(j3 == json({1, 2, 3, 4, 5, 6, 7, 8}));
277        }
278
279        SECTION("std::set_symmetric_difference")
280        {
281            json j1 = {2, 4, 6, 8};
282            json j2 = {1, 2, 3, 5, 7};
283            json j3;
284
285            std::set_symmetric_difference(j1.begin(), j1.end(), j2.begin(), j2.end(), std::back_inserter(j3));
286            CHECK(j3 == json({1, 3, 4, 5, 6, 7, 8}));
287        }
288    }
289
290    SECTION("heap operations")
291    {
292        std::make_heap(j_array.begin(), j_array.end());
293        CHECK(std::is_heap(j_array.begin(), j_array.end()));
294        std::sort_heap(j_array.begin(), j_array.end());
295        CHECK(j_array == json({false, true, 3, 13, 29, {{"one", 1}, {"two", 2}}, {1, 2, 3}, "baz", "foo"}));
296    }
297}
298