Sequoia
Loading...
Searching...
No Matches
TreeTestingUtilities.hpp
Go to the documentation of this file.
1
2// Copyright Oliver J. Rosten 2021. //
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
8#pragma once
9
13
16
17namespace sequoia::testing
18{
19 template<maths::dynamic_tree Tree>
21 {
22 using tree_type = Tree;
23 using size_type = typename tree_type::size_type;
24 using node_weight_type = typename Tree::node_weight_type;
25 using graph_value_tester_base<tree_type>::test;
26
27 constexpr static maths::tree_link_direction link_dir{Tree::link_dir};
28
29 template<class CheckType, test_mode Mode>
30 static void test(CheckType flavour, test_logger<Mode>& logger, const tree_type& actual, const maths::tree_initializer<node_weight_type>& prediction)
31 {
32 check_node(flavour, logger, 0, tree_type::npos, actual, prediction);
33 }
34 private:
35 using edge_iterator = typename tree_type::const_edge_iterator;
36
37 template<class CheckType, test_mode Mode>
38 static size_type check_node(CheckType flavour, test_logger<Mode>& logger, size_type node, size_type parent, const tree_type& actual, const maths::tree_initializer<node_weight_type>& prediction)
39 {
40 if (node == tree_type::npos) return node;
41
42 if (testing::check("Insufficient nodes detected while checking node " + std::to_string(node), logger, node < actual.order()))
43 {
44 if constexpr (link_dir != maths::tree_link_direction::backward)
45 {
46 if constexpr(!std::is_empty_v<node_weight_type>)
47 check(flavour, "Node weight for node " + std::to_string(node), logger, actual.cbegin_node_weights()[node], prediction.node);
48
49 if (auto optIter{ check_num_edges(logger, node, parent, actual, prediction) })
50 {
51 for (const auto& child : prediction.children)
52 {
53 if (!check_edge(logger, (*optIter)++, ++node, actual))
54 return tree_type::npos;
55
56 node = check_node(flavour, logger, node, optIter->partition_index(), actual, child);
57
58 if (node == tree_type::npos) return tree_type::npos;
59 }
60
61 return node;
62 }
63 }
64 else
65 {
66 if (auto optIter{ check_num_edges(logger, node, parent, actual, prediction) })
67 {
68 if constexpr(!std::is_empty_v<node_weight_type>)
69 check(flavour, "Node weight for node " + std::to_string(node), logger, actual.cbegin_node_weights()[node], prediction.node);
70
71 for (const auto& child : prediction.children)
72 {
73 node = check_node(flavour, logger, ++node, optIter->partition_index(), actual, child);
74
75 if (node == tree_type::npos) return tree_type::npos;
76 }
77
78 if (const auto next{ node + 1 }; next < actual.order())
79 {
80 if (auto begin{ actual.cbegin_edges(next) }; begin != actual.cend_edges(next))
81 {
82 if (!testing::check("Extraneous nodes joined to node " + std::to_string(node), logger, begin->target_node() != optIter->partition_index()))
83 {
84 return tree_type::npos;
85 }
86 }
87 }
88
89 return node;
90 }
91 }
92 }
93
94 return tree_type::npos;
95 }
96
97 template<test_mode Mode>
98 static std::optional<edge_iterator> check_num_edges(test_logger<Mode>& logger, size_type node, [[maybe_unused]] size_type parent, const tree_type& actual, const maths::tree_initializer<node_weight_type>& prediction)
99 {
100 if constexpr (link_dir == maths::tree_link_direction::forward)
101 {
102 if (check(equality, "Number of children for node " + std::to_string(node), logger, static_cast<std::size_t>(std::ranges::distance(actual.cbegin_edges(node), actual.cend_edges(node))), prediction.children.size()))
103 return actual.cbegin_edges(node);
104 }
105 else
106 {
107 const auto begin{ actual.cbegin_edges(node) };
108 const auto dist{ static_cast<std::size_t>(std::ranges::distance(begin, actual.cend_edges(node))) };
109 using dist_t = decltype(dist);
110
111 const auto num{
112 [&logger,parent,dist,begin]() -> std::optional<dist_t> {
113 if (parent == tree_type::npos) return dist;
114
115 if (testing::check("Return edge detected", logger, dist > 0)
116 && check(equality, "Return edge target", logger, begin->target_node(), parent))
117 return dist - 1;
118
119 return std::nullopt;
120 }()
121 };
122
123 if (num.has_value())
124 {
125 if constexpr (link_dir == maths::tree_link_direction::symmetric)
126 {
127 if (check(equality, "Number of children for node " + std::to_string(node), logger, num.value(), prediction.children.size()))
128 return num < dist ? std::ranges::next(begin) : begin;
129 }
130 else
131 {
132 if (check(equality, "No reachable children for node " + std::to_string(node), logger, num.value(), size_type{}))
133 return num < dist ? std::ranges::next(begin) : begin;
134 }
135 }
136 }
137
138 return std::nullopt;
139 }
140
141 template<test_mode Mode, std::input_or_output_iterator EdgeIter>
142 static bool check_edge(test_logger<Mode>& logger, EdgeIter iter, size_type nodeCounter, const tree_type& actual)
143 {
144 using std::ranges::distance;
145
146 if constexpr (link_dir == maths::tree_link_direction::forward)
147 {
148 const auto parent{ iter.partition_index() };
149 const auto dist{ distance(actual.cbegin_edges(parent), iter) };
150 const auto mess{ std::string{"Index for child "}.append(std::to_string(dist)).append(" of node ").append(std::to_string(parent)) };
151
152 return check(equality, mess, logger, iter->target_node(), nodeCounter);
153 }
154 else if constexpr (link_dir == maths::tree_link_direction::backward)
155 {
156 static_assert(dependent_false<tree_type>::value, "Not yet implemented");
157 }
158 else if constexpr (link_dir == maths::tree_link_direction::symmetric)
159 {
160 const auto parent{ iter.partition_index() };
161 const auto dist{ distance(actual.cbegin_edges(parent), iter) };
162 const auto mess{ std::string{"Index for child "}.append(std::to_string(dist)).append(" of node ").append(std::to_string(parent)) };
163
164 return check(equality, mess, logger, iter->target_node(), nodeCounter);
165 }
166 else
167 {
169 }
170 }
171 };
172
173 template
174 <
175 maths::tree_link_direction link_dir,
176 class EdgeWeight,
177 class node_weight_type,
178 class EdgeStorageConfig,
179 class NodeWeightStorage
180 >
181 struct value_tester<maths::directed_tree<link_dir, EdgeWeight, node_weight_type, EdgeStorageConfig, NodeWeightStorage>>
182 : tree_tester<maths::directed_tree<link_dir, EdgeWeight, node_weight_type, EdgeStorageConfig, NodeWeightStorage>>
183 {};
184
185 template
186 <
187 maths::tree_link_direction link_dir,
188 class EdgeWeight,
189 class node_weight_type,
190 class EdgeMetaData,
191 class EdgeStorageConfig,
192 class NodeWeightStorage
193 >
194 struct value_tester<maths::undirected_tree<link_dir, EdgeWeight, node_weight_type, EdgeMetaData, EdgeStorageConfig, NodeWeightStorage>>
195 : tree_tester<maths::undirected_tree<link_dir, EdgeWeight, node_weight_type, EdgeMetaData, EdgeStorageConfig, NodeWeightStorage>>
196 {};
197}
Restriction of Dynamic Graphs to Trees.
bool check(CheckType flavour, std::string description, test_logger< Mode > &logger, Iter first, Sentinel last, PredictionIter predictionFirst, PredictionSentinel predictionLast, tutor< Advisor > advisor={})
The workhorse for comparing the contents of ranges.
Definition: FreeCheckers.hpp:377
Utilities for checking regular semantics.
Definition: DynamicTree.hpp:176
Definition: DynamicTree.hpp:210
Definition: TestLogger.hpp:183
Standard meta-programming utility.
Definition: TypeTraits.hpp:27
Definition: GraphPrimitive.hpp:84
Definition: GraphTestingUtilities.hpp:90
Definition: TreeTestingUtilities.hpp:21
class template, specializations of which implement various comparisons for the specified type.
Definition: FreeCheckers.hpp:78