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>
12 using nlohmann::json;
13 
14 TEST_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