Sequoia
Loading...
Searching...
No Matches
Suite.hpp
Go to the documentation of this file.
1
2// Copyright Oliver J. Rosten 2023. //
3// Distributed under the GNU GENERAL PUBLIC LICENSE, Version 3.0. //
4// (See accompanying file LICENSE.md or copy at //
5// https://www.gnu.org/licenses/gpl-3.0.en.html) //
7
12#pragma once
13
17
18#include <algorithm>
19#include <ranges>
20#include <string>
21#include <tuple>
22#include <variant>
23#include <vector>
24
25namespace sequoia::object
26{
27 template<class T>
28 struct is_suite : std::false_type {};
29
30 template<class T>
31 using is_suite_t = typename is_suite<T>::type;
32
33 template<class T>
34 inline constexpr bool is_suite_v{is_suite<T>::value};
35
36 template<class... Ts>
37 requires ((is_suite_v<Ts> && ...) || ((!is_suite_v<Ts>) && ...))
38 class suite;
39
40 template<class... Ts>
41 struct is_suite<suite<Ts...>> : std::true_type {};
42
43 template<class... Ts>
44 requires ((is_suite_v<Ts> && ...) || ((!is_suite_v<Ts>) && ...))
45 class suite
46 {
47 public:
48 std::string m_Name;
49 std::tuple<Ts...> m_Values;
50
51 suite(std::string name, Ts&&... ts)
52 : m_Name{std::move(name)}
53 , m_Values{std::forward<Ts>(ts)...} {}
54
55 [[nodiscard]]
56 const std::string& name() const noexcept { return m_Name; }
57
58 [[nodiscard]]
59 const std::tuple<Ts...>& values() const noexcept { return m_Values; }
60
61 [[nodiscard]]
62 std::tuple<Ts...>& values() noexcept { return m_Values; }
63 };
64
65
66 template<class... Ts>
68
69 template<class... Ts>
70 using leaf_extractor_t = typename leaf_extractor<Ts...>::type;
71
72 template<class... Ts>
73 struct leaf_extractor<std::tuple<Ts...>>
74 {
75 using type = std::tuple<Ts...>;
76 };
77
78 template<class... Ts>
79 requires ((!is_suite_v<Ts>) && ...)
80 struct leaf_extractor<suite<Ts...>>
81 {
82 using type = std::tuple<Ts...>;
83 };
84
85 template<class... Ts>
86 struct leaf_extractor<suite<Ts...>>
87 {
88 using type = leaf_extractor_t<leaf_extractor_t<Ts>...>;
89 };
90
91 template<class... Ts, class... Us>
92 struct leaf_extractor<std::tuple<Ts...>, std::tuple<Us...>>
93 {
94 using type = std::tuple<Ts..., Us...>;
95 };
96
97 template<class... Ts, class... Us>
98 struct leaf_extractor<suite<Ts...>, std::tuple<Us...>>
99 {
100 using type = std::tuple<leaf_extractor_t<suite<Ts...>>, Us...>;
101 };
102
103 template<class... Ts, class... Us>
104 struct leaf_extractor<std::tuple<Ts...>, suite<Us...>>
105 {
106 using type = std::tuple<Ts..., leaf_extractor_t<suite<Us...>>>;
107 };
108
109 template<class T, class U, class... Vs>
110 struct leaf_extractor<T, U, Vs...>
111 {
112 using type = leaf_extractor_t<leaf_extractor_t<T, U>, Vs...>;
113 };
114
115 template<class T, class Transform>
117
118 template<class T, class Transform>
119 using leaves_to_variant_or_unique_type_t = typename leaves_to_variant_or_unique_type<T, Transform>::type;
120
121 template<class... Ts, class Transform>
122 requires (std::invocable<Transform, Ts> && ...)
123 struct leaves_to_variant_or_unique_type<std::tuple<Ts...>, Transform>
124 {
125 using type = std::variant<std::remove_cvref_t<std::invoke_result_t<Transform, Ts>>...>;
126 };
127
128 template<class T, class... Ts, class Transform>
129 requires (std::is_same_v<std::remove_cvref_t<std::invoke_result_t<Transform, T>>, std::remove_cvref_t<std::invoke_result_t<Transform, Ts>>> && ...)
130 struct leaves_to_variant_or_unique_type<std::tuple<T, Ts...>, Transform>
131 {
132 using type = std::remove_cvref_t<std::invoke_result_t<Transform, T>>;
133 };
134
135 template<class... Ts, class Transform>
136 struct leaves_to_variant_or_unique_type<suite<Ts...>, Transform>
137 {
138 using type = typename leaves_to_variant_or_unique_type<leaf_extractor_t<suite<Ts...>>, Transform>::type;
139 };
140
141
142 template<class...>
144
145 template<class... Ts>
146 using faithful_variant_t = typename faithful_variant<Ts...>::type;
147
148 template<>
150 {};
151
152 template<class T>
154 {
155 using type = std::variant<T>;
156 };
157
158 template<class... Ts, class T>
159 requires (std::is_same_v<Ts, T> || ...)
160 struct faithful_variant<std::variant<Ts...>, T>
161 {
162 using type = std::variant<Ts...>;
163 };
164
165 template<class... Ts, class T>
166 struct faithful_variant<std::variant<Ts...>, T>
167 {
168 using type = std::variant<Ts..., T>;
169 };
170
171 template<class... Ts, class T, class... Us>
172 struct faithful_variant<std::variant<Ts...>, T, Us...>
173 {
174 using type = faithful_variant_t<faithful_variant_t<std::variant<Ts...>, T>, Us...>;
175 };
176
177 template<class T, class... Ts>
178 struct faithful_variant<T, Ts...>
179 {
180 using type = faithful_variant_t<std::variant<T>, Ts...>;
181 };
182
183
184 template<class T, std::invocable<T> Transform>
186 {
187 using type = std::invoke_result_t<Transform, T>;
188 };
189
190 template<class T, class Transform>
191 using to_variant_or_unique_type_t = typename to_variant_or_unique_type<T, Transform>::type;
192
193 template<class... Ts, std::invocable<suite<Ts...>> Transform>
194 struct to_variant_or_unique_type<suite<Ts...>, Transform>
195 {
196 using variant_type = faithful_variant_t<std::invoke_result_t<Transform, suite<Ts...>>, to_variant_or_unique_type_t<Ts, Transform>...>;
197 using type = std::conditional_t<std::variant_size_v<variant_type> == 1, std::variant_alternative_t<0, variant_type>, variant_type>;
198 };
199
200 namespace impl
201 {
202 template<class... Ts, class Fn>
203 void extract_leaves(suite<Ts...>& s, Fn fn)
204 {
205 [&] <std::size_t... Is> (std::index_sequence<Is...>) {
206 (fn(std::get<Is>(s.values())), ...);
207 }(std::make_index_sequence<sizeof...(Ts)>{});
208 }
209
210 template<class... Ts, class Filter, class Transform, class Container, class... PreviousSuites>
211 requires (is_suite_v<PreviousSuites> && ...) && ((!is_suite_v<Ts>) && ...)
212 Container&& extract_leaves(suite<Ts...>& s, Filter&& filter, Transform t, Container&& c, const PreviousSuites&... previous)
213 {
214 auto emplacer{
215 [&] <class V> (V&& val) {
216 if(filter(val, previous..., s)) c.emplace_back(t(std::move(val)));
217 }
218 };
219
220 extract_leaves(s, emplacer);
221 return std::forward<Container>(c);
222 }
223
224 template<class... Ts, class Filter, class Transform, class Container, class... PreviousSuites>
225 requires (is_suite_v<PreviousSuites> && ...) && ((is_suite_v<Ts>) && ...)
226 Container&& extract_leaves(suite<Ts...>& s, Filter&& filter, Transform t, Container&& c, const PreviousSuites&... previous)
227 {
228 auto extractor{
229 [&]<class V>(V&& val) { extract_leaves(std::forward<V>(val), std::forward<Filter>(filter), t, std::forward<Container>(c), previous..., s); }
230 };
231
232 extract_leaves(s, extractor);
233 return std::forward<Container>(c);
234 }
235
236 template<class... Ts,
237 class Transform,
238 class Tree,
239 std::integral SizeType = typename Tree::size_type,
240 class Fn>
241 requires maths::dynamic_tree<std::remove_cvref_t<Tree>>
242 Tree&& extract_tree(suite<Ts...>& s, Transform transform, Tree&& tree, const SizeType parentNode, Fn fn)
243 {
244 const auto node{tree.add_node(parentNode, transform(s))};
245
246 [node, fn, &s] <std::size_t... Is> (std::index_sequence<Is...>) {
247 (fn(node, std::get<Is>(s.values())), ...);
248 }(std::make_index_sequence<sizeof...(Ts)>{});
249
250 if(!std::ranges::distance(tree.cedges(node)))
251 tree.prune(node);
252
253 return std::forward<Tree>(tree);
254 }
255
256
257 template<class... Ts, class Filter, class Transform, class Tree, std::integral SizeType = typename Tree::size_type, class... PreviousSuites>
258 requires maths::dynamic_tree<std::remove_cvref_t<Tree>> && (is_suite_v<PreviousSuites> && ...) && ((!is_suite_v<Ts>) && ...)
259 Tree&& extract_tree(suite<Ts...>& s, Filter&& filter, Transform transform, Tree&& tree, const SizeType parentNode, const PreviousSuites&... previous)
260 {
261 auto emplacer{
262 [&](SizeType node, auto&& val) {
263 if(filter(val, previous..., s))
264 tree.add_node(node, transform(std::move(val)));
265 }
266 };
267
268 return impl::extract_tree(s, transform, std::forward<Tree>(tree), parentNode, emplacer);
269 }
270
271 template<class... Ts, class Filter, class Transform, class Tree, std::integral SizeType = typename Tree::size_type, class... PreviousSuites>
272 requires maths::dynamic_tree<std::remove_cvref_t<Tree>> && (is_suite_v<PreviousSuites> && ...) && ((is_suite_v<Ts>) && ...)
273 Tree&& extract_tree(suite<Ts...>& s, Filter&& filter, Transform transform, Tree&& tree, const SizeType parentNode, const PreviousSuites&... previous)
274 {
275 auto recurser{
276 [&] <class V> (SizeType node, V&& val) {
277 impl::extract_tree(std::forward<V>(val), std::forward<Filter>(filter), transform, std::forward<Tree>(tree), node, previous..., s);
278 }
279 };
280
281 return impl::extract_tree(s, transform, std::forward<Tree>(tree), parentNode, recurser);
282 }
283 }
284
285 template<class Suite,
286 class Filter,
287 class Transform = std::identity,
288 class Container = std::vector<leaves_to_variant_or_unique_type_t<Suite, Transform>>>
289 requires is_suite_v<Suite>
290 [[nodiscard]]
291 Container extract_leaves(Suite s, Filter&& filter, Transform t = {})
292 {
293 return impl::extract_leaves(s, std::forward<Filter>(filter), std::move(t), Container{});
294 }
295
296 template<class Suite,
297 class Filter,
298 class Transform,
299 maths::dynamic_tree Tree = maths::directed_tree<maths::tree_link_direction::forward, maths::null_weight, to_variant_or_unique_type_t<Suite, Transform>>>
300 requires is_suite_v<Suite>
301 [[nodiscard]]
302 Tree extract_tree(Suite s, Filter&& filter, Transform transform)
303 {
304 return impl::extract_tree(s, std::forward<Filter>(filter), std::move(transform), Tree{}, Tree::npos);
305 }
306
307 template<class Suite,
308 class Filter,
309 class Transform,
310 maths::dynamic_tree Tree>
311 requires is_suite_v<Suite>
312 Tree& extract_tree(Suite s, Filter&& filter, Transform transform, Tree& tree, typename Tree::size_type pos)
313 {
314 return impl::extract_tree(s, std::forward<Filter>(filter), std::move(transform), tree, pos);
315 }
316
318 {
319 template<class T>
320 requires has_intrinsic_nomenclator<T> || has_extrinsic_nomenclator<T>
321 [[nodiscard]]
322 std::string operator()(const T& t) const
323 {
324 return object::nomenclature(t);
325 }
326 };
327
328 template
329 <
330 class ItemKeyType,
331 class ItemCompare,
332 class ItemProjector,
333 class T
334 >
335 inline constexpr bool granular_filter_for{
336 requires (const ItemKeyType& val, ItemCompare comp, ItemProjector proj, const T& t) {
337 { comp(val, proj(t)) } -> std::same_as<bool>;
338 }
339 };
340
341 template
342 <
343 class ItemKeyType,
344 class ItemCompare,
345 class ItemProjector
346 >
348 {
349 public:
350 using items_key_type = ItemKeyType;
351 using suites_map_type = std::vector<std::pair<std::string, bool>>;
352 using items_map_type = std::vector<std::pair<ItemKeyType, bool>>;
353 using selected_suites_iterator = typename suites_map_type::const_iterator;
354 using selected_items_iterator = typename items_map_type::const_iterator;
355 using optional_suite_selection = std::optional<std::vector<std::string>>;
356 using optional_item_selection = std::optional<std::vector<items_key_type>>;
357
358 granular_filter(ItemCompare compare = {}, ItemProjector proj = {})
359 : m_Compare{std::move(compare)}
360 , m_Proj{std::move(proj)}
361 {}
362
363 granular_filter(optional_suite_selection selectedSuites, optional_item_selection selectedItems, ItemCompare compare = {}, ItemProjector proj = {})
364 : m_Compare{std::move(compare)}
365 , m_Proj{std::move(proj)}
366 , m_SelectedSuites{make(std::move(selectedSuites))}
367 , m_SelectedItems{make(std::move(selectedItems))}
368 {}
369
370 void add_selected_suite(std::string name)
371 {
372 add(m_SelectedSuites, std::move(name));
373 }
374
375 void add_selected_item(items_key_type key)
376 {
377 add(m_SelectedItems, std::move(key));
378 }
379
380 template<class T, class... Suites>
381 requires (is_suite_v<Suites> && ...) && granular_filter_for<ItemKeyType, ItemCompare, ItemProjector, T>
382 [[nodiscard]]
383 bool operator()(const T& val, const Suites&... suites)
384 {
385 if(!m_SelectedSuites && !m_SelectedItems) return true;
386
387 // Don't use logical short-circuit, otherwise the maps may not accurately update
388 std::array<bool, sizeof...(Suites) + 1> isFound{ find(m_SelectedItems, val, m_Proj, m_Compare), find(m_SelectedSuites, suites, item_to_name{}, std::ranges::equal_to{}) ... };
389
390 return std::ranges::any_of(isFound, [](bool b) { return b; });
391 }
392
393 [[nodiscard]]
394 std::optional<std::ranges::subrange<selected_suites_iterator>> selected_suites() const noexcept
395 {
396 return m_SelectedSuites ? std::optional<std::ranges::subrange<selected_suites_iterator>>{{m_SelectedSuites->begin(), m_SelectedSuites->end()}} : std::nullopt;
397 }
398
399 [[nodiscard]]
400 std::optional<std::ranges::subrange<selected_items_iterator>> selected_items() const noexcept
401 {
402 return m_SelectedItems ? std::optional<std::ranges::subrange<selected_items_iterator>>{{m_SelectedItems->begin(), m_SelectedItems->end()}} : std::nullopt;
403 }
404
405 [[nodiscard]]
406 friend bool operator==(const granular_filter&, const granular_filter&) noexcept = default;
407
408 [[nodiscard]]
409 operator bool() const noexcept
410 {
411 return m_SelectedSuites || m_SelectedItems;
412 }
413 private:
414 [[no_unique_address]] ItemCompare m_Compare{};
415 [[no_unique_address]] ItemProjector m_Proj{};
416 std::optional<suites_map_type> m_SelectedSuites{};
417 std::optional<items_map_type> m_SelectedItems{};
418
419 template<class Key>
420 using opt_map_type = std::optional<std::vector<std::pair<Key, bool>>>;
421
422 template<class Key>
423 static void add(opt_map_type<Key>& map, Key k)
424 {
425 using map_type = typename opt_map_type<Key>::value_type;
426 if(!map) map = map_type{};
427
428 map->emplace_back(std::move(k), false);
429 }
430
431 template<class Key>
432 [[nodiscard]]
433 static opt_map_type<Key> make(std::optional<std::vector<Key>> selected)
434 {
435 if(!selected) return std::nullopt;
436
437 std::vector<std::pair<Key, bool>> selection{};
438 for (auto& e : *selected)
439 selection.emplace_back(std::move(e), false);
440
441 return selection;
442 }
443
444 template<class Key, class U, class Projector, class Comp>
445 static bool find(opt_map_type<Key>& selected, const U& u, Projector proj, Comp compare)
446 {
447 if(selected)
448 {
449 auto found{std::ranges::find_if(*selected, [&proj, &compare, &u](const std::pair<Key, bool>& e) { return compare(e.first, proj(u)); })};
450
451 if(found != selected->end())
452 {
453 found->second = true;
454 return true;
455 }
456 }
457
458 return false;
459 }
460 };
461}
Restriction of Dynamic Graphs to Trees.
Traits and Concepts for graphs.
Utilities for associating types with names.
Definition: Suite.hpp:348
Definition: Suite.hpp:46
Definition: Suite.hpp:143
Definition: Suite.hpp:28
Definition: Suite.hpp:318
Definition: Suite.hpp:67