Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
ah_concepts_functional_test.cc
Go to the documentation of this file.
1#include <functional>
2#include <optional>
3
4#include <gtest/gtest.h>
5
6#include <ahFunctional.H>
7#include <htlist.H>
8#include <tpl_dynArray.H>
9#include <tpl_dynBinHeap.H>
10#include <tpl_graph.H>
11#include <tpl_sort_utils.H>
12
13// Positive/negative checks for the constraints added in Phase 2 of
14// aleph-concepts-plan.md: comparators on sorting and heaps, and callables on
15// the functional layer (ah-dry.H, ah-dry-mixin.H, graph-dry.H, ahFunctional.H).
16// Each constraint mirrors the call its function body makes, so the negative
17// cases are exactly the ones that used to fail deep inside the library.
18
19using namespace Aleph;
20
21namespace
22{
23 struct IntLess
24 {
25 bool operator()(const int & a, const int & b) const { return a < b; }
26 };
27
28 struct NonConstLess
29 {
30 bool operator()(const int & a, const int & b) { return a < b; }
31 };
32
33 struct IsPositive
34 {
35 bool operator()(int x) const { return x > 0; }
36 };
37
38 struct ReturnsVoid
39 {
40 void operator()(int) const {}
41 };
42
43 struct MutatesInPlace
44 {
45 void operator()(int & x) const { ++x; }
46 };
47
48 struct MaybePositive
49 {
50 std::optional<int> operator()(int x) const
51 {
52 return x > 0 ? std::optional<int>(x) : std::nullopt;
53 }
54 };
55
56 struct NeedsNothing
57 {
58 void operator()() const {}
59 };
60
61 struct Accumulate
62 {
63 int operator()(int acc, int x) const { return acc + x; }
64 };
65
66 struct NotAFold
67 {
68 void operator()(int, int) const {}
69 };
70
72
73 struct NodePred
74 {
75 bool operator()(Graph::Node *) const { return true; }
76 };
77
78 struct ArcPred
79 {
80 bool operator()(Graph::Arc *) const { return true; }
81 };
82} // namespace
83
84// --- Hub concepts -----------------------------------------------------------
85
88static_assert(not CallableWith<IsPositive &>);
89static_assert(not CallableWith<bool (std::optional<int>::*)() const, std::optional<int> &>);
90
93// Contextual (not implicit) conversion to bool, as `if (op(x))` needs:
95static_assert(not std::predicate<MaybePositive &, const int &>);
96
97// --- Sorting ----------------------------------------------------------------
98
99template <class Cmp>
100concept can_introsort = requires(int * a, Cmp cmp) { Aleph::introsort(a, 3, cmp); };
101
102static_assert(can_introsort<IntLess>);
103static_assert(can_introsort<std::greater<int>>);
104static_assert(can_introsort<bool (*)(const int &, const int &)>);
105static_assert(not can_introsort<NonConstLess>);
106static_assert(not can_introsort<ReturnsVoid>);
107
108// --- Heaps ------------------------------------------------------------------
109
110template <class Cmp>
111concept can_name_heap = requires { typename DynBinHeap<int, Cmp>; };
112
113static_assert(can_name_heap<IntLess>);
114static_assert(not can_name_heap<NonConstLess>);
115
116// --- Member functional layer (ah-dry.H) --------------------------------------
117
118template <class Op>
119concept can_filter = requires(const DynList<int> & l, Op op) { l.filter(op); };
120template <class Op>
121concept can_mutable_for_each = requires(DynList<int> & l, Op op) { l.mutable_for_each(op); };
122template <class Op>
123concept can_foldl = requires(const DynList<int> & l, Op op) { l.foldl(0, op); };
124template <class Op>
125concept can_partition = requires(const DynList<int> & l, Op op) { l.partition(op); };
126
127static_assert(can_filter<IsPositive>);
128static_assert(can_filter<MaybePositive>);
129static_assert(not can_filter<ReturnsVoid>);
131static_assert(can_foldl<Accumulate>);
132static_assert(not can_foldl<NotAFold>);
133static_assert(can_partition<IsPositive>);
134// An int count is viable: it now reaches partition(size_t). Before the
135// constraint, partition(Operation &&) won overload resolution with an exact
136// match (Operation = int) and failed inside calling `op(item)`.
137static_assert(can_partition<int>);
138
139// --- Free functional layer (ahFunctional.H) ---------------------------------
140
141template <class Op>
142concept can_count_if = requires(const DynList<int> & l, Op op) { Aleph::count_if(l, op); };
143template <class Op>
144concept can_each = requires(Op op) { Aleph::each(size_t(0), size_t(1), op); };
145
146static_assert(can_count_if<IsPositive>);
147static_assert(not can_count_if<ReturnsVoid>);
148static_assert(can_each<NeedsNothing>);
149static_assert(not can_each<IsPositive>);
150
151// --- Graph functional layer (graph-dry.H) -----------------------------------
152
153template <class Op>
154concept can_traverse_nodes = requires(const Graph & g, Op op) { g.traverse_nodes(op); };
155template <class Op>
156concept can_filter_arcs = requires(const Graph & g, Op op) { g.filter_arcs(op); };
157
158static_assert(can_traverse_nodes<NodePred>);
159static_assert(not can_traverse_nodes<ArcPred>);
160static_assert(can_filter_arcs<ArcPred>);
161static_assert(not can_filter_arcs<NodePred>);
162
163// Former std::function parameters: the constraint is what std::function's
164// constructor required (callable, result implicitly convertible to T).
165template <class Op>
166concept can_nodes_map = requires(const Graph & g, Op op) { g.nodes_map(op); };
167
168static_assert(can_nodes_map<NodePred>);
169static_assert(not can_nodes_map<ArcPred>);
170
172{
173 DynList<int> l = {3, -1, 4};
174 EXPECT_EQ(l.filter(IsPositive()).size(), 2u);
175 EXPECT_EQ(l.filter(MaybePositive()).size(), 2u);
176 EXPECT_EQ(l.foldl(0, Accumulate()), 6);
177 EXPECT_EQ(l.partition(1).first.size(), 1u);
178 EXPECT_EQ(Aleph::count_if(l, IsPositive()), 2u);
179
180 int a[] = {3, 1, 2};
181 Aleph::introsort(a, 3, IntLess());
182 EXPECT_EQ(a[0], 1);
183
184 Graph g;
185 auto * n1 = g.insert_node(1);
186 auto * n2 = g.insert_node(2);
187 g.insert_arc(n1, n2, 7);
188 EXPECT_TRUE(g.traverse_nodes(NodePred()));
189 EXPECT_EQ(g.filter_arcs(ArcPred()).size(), 1u);
190}
191
192namespace
193{
194 int node_info(Graph::Node * p) { return p->get_info(); }
195}
196
197// These member functions used to take std::function<T(...)> with T in a
198// deduced context, so passing a lambda without spelling T out did not compile
199// (deduction of T from a lambda fails before the default could apply).
201{
202 Graph g;
203 auto * n1 = g.insert_node(1);
204 auto * n2 = g.insert_node(2);
205 g.insert_arc(n1, n2, 7);
206
207 auto infos = g.nodes_map([](Graph::Node * p) { return p->get_info(); });
208 EXPECT_EQ(infos.size(), 2u);
209 EXPECT_EQ(g.nodes_map(&node_info).size(), 2u);
210 EXPECT_EQ(g.arcs_map([](Graph::Arc * a) { return a->get_info(); }).get_first(), 7);
211 EXPECT_EQ(g.arcs_map(n1, [](Graph::Arc * a) { return a->get_info(); }).get_first(), 7);
212 EXPECT_EQ(g.foldl_nodes(0, [](const int & acc, Graph::Node * p) { return acc + p->get_info(); }), 3);
213 EXPECT_EQ(g.foldl_arcs(0, [](const int & acc, Graph::Arc * a) { return acc + a->get_info(); }), 7);
214 EXPECT_EQ(g.foldl_arcs(n1, 0, [](const int & acc, Graph::Arc * a) { return acc + a->get_info(); }), 7);
215
216 // An explicit T still works and still converts the result.
217 auto halves = g.template nodes_map<double>([](Graph::Node * p) { return p->get_info() / 2.0; });
218 EXPECT_DOUBLE_EQ(halves.get_first() + halves.get_last(), 1.5);
219}
220
221// Uses an undirected List_Graph: a List_Digraph keeps each arc only in its
222// source's list, so in-arcs are not visible from the target (in_degree 0).
224{
225 Graph g;
226 auto * a = g.insert_node(1);
227 auto * b = g.insert_node(2);
228 g.insert_arc(a, b, 5);
229
230 auto arc_info = [](Graph::Arc * arc) { return arc->get_info(); };
231 auto add_info = [](const int & acc, Graph::Arc * arc) { return acc + arc->get_info(); };
232
233 EXPECT_EQ(g.template in_arcs_map<int>(b, arc_info).get_first(), 5);
234 EXPECT_EQ(g.out_arcs_map(a, arc_info).get_first(), 5);
235 EXPECT_EQ(g.foldl_in_arcs(b, 0, add_info), 5);
236 EXPECT_EQ(g.foldl_out_arcs(a, 0, add_info), 5);
237 EXPECT_TRUE(g.out_arcs_map(b, arc_info).is_empty());
238}
239
240// The 3-argument free overloads (g, op, filter) and (g, node, op) must stay
241// unambiguous now that the operation is a template parameter.
243{
244 Graph g;
245 auto * n1 = g.insert_node(1);
246 auto * n2 = g.insert_node(2);
247 g.insert_arc(n1, n2, 7);
248
249 int visits = 0;
251 Aleph::for_each_arc(g, n1, [&visits](Graph::Arc *) { ++visits; });
253 EXPECT_EQ(visits, 3);
254 EXPECT_TRUE(Aleph::forall_arc(g, ArcPred()));
255 EXPECT_EQ((Aleph::nodes_map<Graph, long>(g, [](Graph::Node * p) { return p->get_info(); }).size()), 2u);
256}
257
258// nodes_map/arcs_map/map_in_arcs/map_out_arcs only accept a generic Op, so T
259// (which appears only in the return type and the requires-clause, never in a
260// parameter) is not deducible: an actual std::function<T(...)> argument, which
261// the older std::function-parameter signature could deduce T from, used to
262// need T spelled out explicitly. The compatibility overloads restore that.
264{
265 Graph g;
266 auto * n1 = g.insert_node(1);
267 auto * n2 = g.insert_node(2);
268 auto * n3 = g.insert_node(3);
269 g.insert_arc(n1, n2, 10);
270 g.insert_arc(n1, n3, 20);
271 g.insert_arc(n2, n3, 30);
272
273 std::function<int(Graph::Node *)> nf = [](Graph::Node * p) { return p->get_info() * 100; };
274 std::function<int(Graph::Arc *)> af = [](Graph::Arc * a) { return a->get_info() + 1; };
275
278 EXPECT_EQ((Aleph::arcs_map<Graph>(g, n1, af).size()), 2u);
281
282 // The generic (lambda) and std::function overloads must both stay callable
283 // with T explicit, without becoming ambiguous with each other.
284 EXPECT_EQ((Aleph::nodes_map<Graph, int>(g, [](Graph::Node * p) { return p->get_info(); }).size()), 3u);
286}
Functional programming utilities for Aleph-w containers.
Dynamic heap of elements of type T ordered by a comparison functor.
Doubly-linked list (defined in tpl_dynList.H).
Definition htlist.H:1155
virtual Node * insert_node(Node *node) noexcept
Insertion of a node already allocated.
Definition tpl_graph.H:525
Node Node
The graph type.
Definition tpl_graph.H:433
Arc Arc
The node class type.
Definition tpl_graph.H:434
Arc * insert_arc(Node *src_node, Node *tgt_node, void *a)
Definition tpl_graph.H:605
Aleph::DynList< T > filter(Operation &operation) const
Filter the elements of a container according to a matching criterion.
Definition ah-dry.H:1437
__T foldl(const __T &init, Op &op) const
Fold the elements of the container to a specific result.
Definition ah-dry.H:1312
void mutable_for_each(Operation &operation)
Apply a mutable operation to each element of the container.
Definition ah-dry.H:953
std::pair< Aleph::DynList< T >, Aleph::DynList< T > > partition(Operation &op) const
Exclusive partition of container according to a filter criterion.
Definition ah-dry.H:1592
auto out_arcs_map(Node *p, Op op) const
Return a list of outcoming arcs of a node mapped to items of type given by transformation op.
Definition graph-dry.H:3682
bool traverse_nodes(Operation &op) const
Conditioned traversal of all the nodes of a graph.
Definition graph-dry.H:1353
T foldl_in_arcs(Node *p, const T &init, Op op) const
Fold the incoming arcs of a node.
Definition graph-dry.H:3544
T foldl_nodes(const T &init, Op op) const
Folding of nodes on a graph.
Definition graph-dry.H:1956
auto nodes_map(Op op) const
Map the nodes of a graph to a specific range.
Definition graph-dry.H:1810
T foldl_arcs(const T &init, Op op) const
Folding of arcs on a graph.
Definition graph-dry.H:1999
auto arcs_map(Op operation) const
Map the arcs of a graph to a specific range.
Definition graph-dry.H:1858
auto filter_arcs(Op &op) const
Filter the arcs of graph satisfying a condition.
Definition graph-dry.H:2116
T foldl_out_arcs(Node *p, const T &init, Op op) const
Fold-left over outcoming arcs of a node.
Definition graph-dry.H:3692
#define TEST(name)
int cmp(const __gmp_expr< T, U > &expr1, const __gmp_expr< V, W > &expr2)
Definition gmpfrxx.h:4129
void for_each_arc(const GT &g, Op operation, SA sa=SA())
Traverse all the arcs of graph filtering some ones according to a condition and executing an operatio...
Definition tpl_graph.H:1259
size_t blossom_maximum_cardinality_matching(const GT &g, DynDlist< typename GT::Arc * > &matching, SA sa=SA())
Alias of compute_maximum_cardinality_general_matching().
Definition Blossom.H:466
bool forall_arc(const GT &g, Op cond, SA sa=SA())
Return true if condition cond is met on every filtered arc of the graph.
Definition tpl_graph.H:1323
Singly linked list implementations with head-tail access.
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
void each(const size_t start, const size_t end, Op &op)
Execute an operation repeatedly over a range of indices.
size_t size(Node *root) noexcept
Itor::difference_type count_if(Itor beg, const Itor &end, Operation op)
Count elements satisfying a predicate.
Definition ahAlgo.H:100
void introsort(T *a, const long l, const long r, const Compare &cmp=Compare())
Sort an array using introsort (introspective sort).
STL namespace.
Arc of graph implemented with double-linked adjacency lists.
Definition tpl_graph.H:223
bool operator()(const int &a, const int &b) const
bool operator()(const int &a, const int &b)
Lazy and scalable dynamic array implementation.
Dynamic binary heap with node-based storage.
Generic graph and digraph implementations.
Comprehensive sorting algorithms and search utilities for Aleph-w.
DynList< int > l