C++ Library Extensions 2022.12.09
To help learn modern C++ programming
tpf_graph.hpp
Go to the documentation of this file.
1
11#ifndef _TPF_GRAPH_HPP
12#define _TPF_GRAPH_HPP
13
14#ifndef NOMINMAX
15#define NOMINMAX
16#endif
17
18#ifdef _MSVC_LANG
19 #if _MSVC_LANG < 201703L
20 #error This libary requires C++17 Standard (Visual Studio 2017).
21 #endif
22#else
23
24 #if __cplusplus < 201703
25 #error This library requires C++17 Standard (GNU g++ version 8.0 or clang++ version 8.0 above)
26 #endif // end of __cplusplus
27
28#endif // end of _MSVC_LANG
29
30#include <tpf_set.hpp>
31
32namespace tpf
33{
34 namespace graph
35 {
37 {
38 static constexpr int undirected = 0; // 0000 0000
39 static constexpr int undirected_unweighted = 0; // 0000 0000
40 static constexpr int unweighted_undirected = 0; // 0000 0000
41
42 static constexpr int directed = 1; // 0000 0001
43 static constexpr int directed_unweighted = 1; // 0000 0001
44 static constexpr int unweighted_directed = 1; // 0000 0001
45
46 static constexpr int weighted = 2; // 0000 0010
47 static constexpr int undirected_weighted = 2; // 0000 0010
48 static constexpr int weighted_undirected = 2; // 0000 0010
49
50 static constexpr int directed_weighted = 3; // 0000 0011
51 static constexpr int weighted_directed = 3; // 0000 0011
52 };
53
54 constexpr bool is_directed(int direction)
55 {
56 return (direction & 1) != 0;
57 }
58
59 constexpr bool is_weighted(int weighted)
60 {
61 return (weighted & 2) != 0;
62 }
63
65 {
66 static constexpr int single_edged = 0;
67 static constexpr int plural_edged = 1;
68 static constexpr int weighted_edge = 1;
69 };
70
71 template<int DirectedEdge, typename NodeIndexType, typename... Types> class edge;
72
73 // undirected, directed, single edged (no weight)
74 template<int DirectedEdge, typename EdgeNodeIndexType>
75 class edge<DirectedEdge, EdgeNodeIndexType>
76 {
77 template<int, int, typename, typename, typename, template<typename, typename...> class,
78 template<typename, typename...> class, template<typename, typename...> class> friend class graph;
79
80 public:
81
82 using node_index_t = EdgeNodeIndexType;
83
84 static constexpr int directed_edge = DirectedEdge;
85 static constexpr node_index_t invalid_node_index = tpf::type_max_v<node_index_t>;
86
87 enum edge_index: size_t{ index_node_1, index_node_2 };
88
89 using edge_tuple_t = std::tuple<node_index_t, node_index_t>;
90
91 private:
92 edge_tuple_t m_edge;
93
94 public:
95
96 edge() = default;
97
98 template<typename IndexType_1, typename IndexType_2>
99 edge(IndexType_1 index_1, IndexType_2 index_2, bool bForceDirection = false) noexcept
100 {
101 set(index_1, index_2, bForceDirection);
102 }
103
104 node_index_t& node_1() noexcept{ return std::get<edge_index::index_node_1>(this->m_edge); }
105 const node_index_t& node_1() const noexcept{ return std::get<edge_index::index_node_1>(this->m_edge); }
106
107 node_index_t& node_2() noexcept { return std::get<edge_index::index_node_2>(this->m_edge); }
108 const node_index_t& node_2() const noexcept { return std::get<edge_index::index_node_2>(this->m_edge); }
109
110 template<typename IndexType_1, typename IndexType_2>
111 edge& set(IndexType_1 index_1, IndexType_2 index_2, bool bForceDirection = false) noexcept
112 {
113 if(bForceDirection || is_directed(directed_edge))
114 {
115 this->node_1() = (node_index_t) index_1;
116 this->node_2() = (node_index_t) index_2;
117 }
118 else
119 {
120 this->node_1() = (node_index_t) tpf::minimum(index_1, index_2);
121 this->node_2() = (node_index_t) tpf::maximum(index_1, index_2);
122 }
123
124 return *this;
125 }
126
127 template<typename IndexType>
128 node_index_t other_node(IndexType node_index) const noexcept
129 {
130 if(this->node_1() == (node_index_t)node_index)
131 return this->node_2();
132 else if(this->node_2() == (node_index_t)node_index)
133 return this->node_1();
134 else
135 return invalid_node_index;
136 }
137
138 static int compare_edges(const edge& lhs, const edge& rhs)
139 {
140 if(lhs.node_1() == rhs.node_1())
141 {
142 if(lhs.node_2() == rhs.node_2())
143 return 0;
144 else if(lhs.node_2() < rhs.node_2())
145 return -1;
146 else // this->node_2() > rhs.node_2()
147 return 1;
148 }
149 else if(lhs.node_1() < rhs.node_1() )
150 return -1;
151 else // this->node_1() > rhs.node_1()
152 return 1;
153 }
154
155 struct weak_compare
156 {
157 bool operator()(const edge& lhs, const edge& rhs) const
158 {
159 return compare_edges(lhs, rhs) < 0;
160 }
161 };
162
163 struct strong_compare
164 {
165 bool operator()(const edge& lhs, const edge& rhs) const
166 {
167 return compare_edges(lhs, rhs) < 0;
168 }
169 };
170
171 inline static constexpr weak_compare weak_comparator{};
172 inline static constexpr strong_compare strong_comparator{};
173
174 friend bool operator < (const edge& lhs, const edge& rhs)
175 {
176 return compare_edges(lhs, rhs) < 0;
177 }
178
179 friend bool operator > (const edge& lhs, const edge& rhs)
180 {
181 return compare_edges(lhs, rhs) > 0;
182 }
183
184 friend bool operator == (const edge& lhs, const edge& rhs)
185 {
186 return compare_edges(lhs, rhs) == 0;
187 }
188
189 friend bool operator != (const edge& lhs, const edge& rhs)
190 {
191 return compare_edges(lhs, rhs) != 0;
192 }
193
194 void swap(edge& rhs) noexcept
195 {
196 if(this != std::addressof(rhs))
197 {
198 this->m_edge.swap(rhs.m_edge);
199 }
200 }
201
202 friend void swap(edge& lhs, edge& rhs) noexcept
203 {
204 lhs.swap(rhs);
205 }
206
207 edge(const edge&) = default;
208 edge& operator = (const edge&) = default;
209
210 edge_tuple_t& get() noexcept { return this->m_edge; }
211
212 const edge_tuple_t& get() const noexcept { return this->m_edge; }
213
215 {
216 os << "( "<<e.node_1()<< ", "<<e.node_2() << " )";
217
218 return os;
219 }
220 };
221
222 // directed, undirected, singled edged, plural edged (weighted)
223 template<int DirectedEdge, typename EdgeNodeIndexType, typename EdgeWeightType>
224 class edge<DirectedEdge, EdgeNodeIndexType, EdgeWeightType> // Plural Edged, Weighted
225 {
226 template<int, int, typename, typename, typename, template<typename, typename...> class,
227 template<typename, typename...> class, template<typename, typename...> class> friend class graph;
228
229 public:
230
231 using node_index_t = EdgeNodeIndexType;
232 using weight_t = EdgeWeightType;
233
234 static constexpr int directed_edge = DirectedEdge;
235 static constexpr node_index_t invalid_node_index = tpf::type_max_v<node_index_t>;
236
237 enum edge_index: size_t{ index_node_1, index_node_2, index_weight };
238
239 using edge_tuple_t = std::tuple<node_index_t, node_index_t, weight_t>;
240
241 private:
242 edge_tuple_t m_edge;
243
244 public:
245
246 edge() = default;
247
248 template<typename IndexType_1, typename IndexType_2, typename WeightType>
249 edge(IndexType_1 index_1, IndexType_2 index_2, WeightType weight, bool bForceDirection = false) noexcept
250 {
251 set(index_1, index_2, weight, bForceDirection);
252 }
253
254 weight_t& weight() noexcept { return std::get<edge_index::index_weight>(this->m_edge); };
255 const weight_t& weight() const noexcept { return std::get<edge_index::index_weight>(this->m_edge); };
256
257 node_index_t& node_1() noexcept{ return std::get<edge_index::index_node_1>(this->m_edge); }
258 const node_index_t& node_1() const noexcept{ return std::get<edge_index::index_node_1>(this->m_edge); }
259
260 node_index_t& node_2() noexcept { return std::get<edge_index::index_node_2>(this->m_edge); }
261 const node_index_t& node_2() const noexcept { return std::get<edge_index::index_node_2>(this->m_edge); }
262
263 template<typename IndexType_1, typename IndexType_2, typename WeightType>
264 edge& set(IndexType_1 index_1, IndexType_2 index_2, WeightType weight, bool bForceDirection = false) noexcept
265 {
266 if(bForceDirection || is_directed(directed_edge))
267 {
268 this->node_1() = (node_index_t)index_1;
269 this->node_2() = (node_index_t)index_2;
270 }
271 else // direction ignored
272 {
273 this->node_1() = (node_index_t) tpf::minimum(index_1, index_2);
274 this->node_2() = (node_index_t) tpf::maximum(index_1, index_2);
275 }
276
277 this->weight() = (weight_t)weight;
278 return *this;
279 }
280
281 static int weak_compare_edges(const edge& lhs, const edge& rhs)
282 {
283 if(lhs.node_1() == rhs.node_1())
284 {
285 if(lhs.node_2() == rhs.node_2())
286 return 0;
287 else if(lhs.node_2() < rhs.node_2())
288 return -1;
289 else // this->node_2() > rhs.node_2()
290 return 1;
291 }
292 else if(lhs.node_1() < rhs.node_1() )
293 return -1;
294 else // this->node_1() > rhs.node_1()
295 return 1;
296 }
297
298 static int strong_compare_edges(const edge& lhs, const edge& rhs)
299 {
300 auto weak_compare = weak_compare_edges(lhs, rhs);
301
302 if(weak_compare!=0)
303 return weak_compare;
304 else
305 {
306 auto diff = lhs.weight() - rhs.weight();
307 return (diff == 0 ? 0: (diff < 0 ? -1: 1));
308 }
309 }
310
311 struct weak_compare
312 {
313 bool operator()(const edge& lhs, const edge& rhs) const
314 {
315 return weak_compare_edges(lhs, rhs) < 0;
316 }
317 };
318
319 struct strong_compare
320 {
321 bool operator()(const edge& lhs, const edge& rhs) const
322 {
323 return strong_compare_edges(lhs, rhs) < 0;
324 }
325 };
326
327 inline static constexpr weak_compare weak_comparator{};
328 inline static constexpr strong_compare strong_comparator{};
329
330 template<typename IndexType>
331 node_index_t other_node(IndexType node_index) const noexcept
332 {
333 if(this->node_1() == (node_index_t)node_index)
334 return this->node_2();
335 else if(this->node_2() == (node_index_t)node_index)
336 return this->node_1();
337 else
338 return invalid_node_index;
339 }
340
341 friend bool operator < (const edge& lhs, const edge& rhs)
342 {
343 return strong_compare_edges(lhs, rhs) < 0;
344 }
345
346 friend bool operator > (const edge& lhs, const edge& rhs)
347 {
348 return strong_compare_edges(lhs, rhs) > 0;
349 }
350
351 // partial order
352 friend bool operator == (const edge& lhs, const edge& rhs)
353 {
354 return strong_compare_edges(lhs, rhs) == 0;
355 }
356
357 friend bool operator != (const edge& lhs, const edge& rhs)
358 {
359 return strong_compare_edges(lhs, rhs) != 0;
360 }
361
362 void swap(edge& rhs) noexcept
363 {
364 if(this != std::addressof(rhs))
365 this->m_edge.swap(rhs.m_edge);
366 }
367
368 friend void swap(edge& lhs, edge& rhs) noexcept
369 {
370 lhs.swap(rhs);
371 }
372
373 edge(const edge&) = default;
374 edge& operator = (const edge&) = default;
375
376 edge_tuple_t& get() noexcept { return this->m_edge; }
377 const edge_tuple_t& get() const noexcept { return this->m_edge; }
378
380 {
381 os << "( "<<e.node_1()<< ", "<<e.node_2() << " | "<<e.weight() <<" )";
382 return os;
383 }
384 };
385
386 template<int EdgeDirected, typename EdgeIndexType, typename... EdgeTypes,
387 template<typename, typename...> class ContainerType, typename... Types>
388 tpf::sstream& operator <<
389 (tpf::sstream& os, const ContainerType<edge<EdgeDirected, EdgeIndexType, EdgeTypes...>, Types...>& edges)
390 {
391 if(edges.empty())
392 {
393 os << "{ }"; return os;
394 }
395 else
396 {
397 auto last_edge = edges.cend();
398 std::advance(last_edge, -1);
399
400 for(auto itr = edges.cbegin(); itr != last_edge; ++itr)
401 os << *itr <<", ";
402
403 os << *last_edge;
404
405 return os;
406 }
407 }
408
409 template<typename EdgeType, typename IndexType_1, typename IndexType_2, typename WeightType>
410 EdgeType make_edge(IndexType_1 index_1, IndexType_2 index_2, WeightType weight, bool bForceDirection = false)
411 {
412 return EdgeType{ index_1, index_2, weight, bForceDirection};
413 }
414
415 template<typename EdgeType, typename IndexType_1, typename IndexType_2>
416 EdgeType make_edge(IndexType_1 index_1, IndexType_2 index_2, bool bForceDirection = false)
417 {
418 return EdgeType{ index_1, index_2, bForceDirection};
419 }
420
421 template<int DirectedEdge, int EdgePlurality,
422 typename ElementType, typename NodeIndexType, typename EdgeWeightType,
423 template<typename, typename...> class NodeContainerType,
424 template<typename, typename...> class IndexContainerType,
425 template<typename, typename...> class EdgeContainerType>
426 class graph
427 {
428 template<int, typename, typename...> friend class edge;
429
430 public:
431 using element_t = ElementType;
432 using index_t = NodeIndexType;
433
434 using indices_t = std::vector<index_t>;
435 using node_names_t = std::vector<ElementType>;
436
437 using node_index_t = NodeIndexType;
438 using edge_weight_t = EdgeWeightType;
439
440 static constexpr int directed_edge = DirectedEdge;
441 static constexpr int edge_multiplicity = EdgePlurality;
442
444
445 using edge_container_t = EdgeContainerType<edge_type_t>;
446
447 using index_container_t = IndexContainerType<index_t>; // container type for node indices
448 using node_container_t = NodeContainerType<index_container_t>; // container type for nodes
449 using element_container_t = NodeContainerType<element_t>; // container type for node values
450
451 using ref_element_container_t = NodeContainerType<std::reference_wrapper<element_t>>;
452 using const_ref_element_container_t = NodeContainerType<std::reference_wrapper<const element_t>>;
453
454 using node_ref_t = std::tuple<ElementType&, index_container_t&>;
455 using const_node_ref_t = std::tuple<const ElementType&, const index_container_t&>;
456
457 using node_info_t = std::tuple<const ElementType&, const_ref_element_container_t>;
458
459 using distances_t = std::vector<edge_weight_t>;
460
462
463 using shortest_path_t = std::tuple<indices_t, distances_t, indices_t, edge_weight_t>;
464
465 enum class visit_mode: int { graph, visit_bfs, visit_dfs};
466
467 private:
468
469 node_container_t m_nodes; // container for node list
470 element_container_t m_values; // container for each node's value
471 edge_container_t m_edges; // container for edges
472
473
474 public:
475
477 {
478 return this->m_values;
479 }
480
481 decltype(auto) get_node_indices(index_t node_index) const
482 {
483 return tpf::get_element<const node_container_t&>(this->m_nodes, node_index);
484 }
485
486 decltype(auto) get_node_indices(index_t node_index)
487 {
488 return tpf::get_element<node_container_t&>(this->m_nodes, node_index);
489 }
490
491 decltype(auto) get_edge(index_t index)
492 {
493 return tpf::get_element<edge_container_t&>(this->m_edges, (size_t)index);
494 }
495
496 decltype(auto) get_edge(index_t index) const
497 {
498 return tpf::get_element<const edge_container_t&>(this->m_edges, (size_t)index);
499 }
500
502 {
503 indices_t edge_indices;
504
505 auto node_indices = this->get_node_indices(node_index);
506
508 auto endl = tpf::endl;
509
510 for(auto& end_node: node_indices)
511 {
512 auto e = make_edge<edge_type_t>(node_index, end_node, edge_weight_t{});
513
514 auto index = tpf::binary_find_index(this->m_edges, e, edge_type_t::weak_comparator);
515
516 if(index < this->m_edges.size())
517 {
518 edge_indices.emplace_back((index_t)index);
519 }
520 }
521
522 return edge_indices;
523 }
524
525 indices_t get_edge_indices(index_t node_index, const indices_t& visited) const
526 {
527 indices_t edge_indices;
528
529 auto node_indices = this->get_node_indices(node_index);
530
531 for(auto& end_node: node_indices)
532 {
533 if(set::is_in_container((index_t)end_node, visited)) continue;
534
535 auto e = make_edge<edge_type_t>(node_index, end_node, edge_weight_t{});
536
537 auto index = tpf::binary_find_index(this->m_edges, e, edge_type_t::weak_comparator);
538
539 if(index < this->m_edges.size())
540 {
541 edge_indices.emplace_back((index_t)index);
542 }
543 }
544
545 return edge_indices;
546 }
547
548 auto begin() { return this->m_nodes.begin(); }
549 auto end() { return this->m_nodes.end(); }
550
551 auto cbegin() { return this->m_nodes.cbegin(); }
552 auto cend() { return this->m_nodes.cend(); }
553
554 bool empty() const { return this->m_nodes.empty(); }
555
556 decltype(auto) adjacency_node_list(size_t node_index)
557 {
558 return tpf::get_element<node_container_t&>(m_nodes, node_index);
559 }
560
561 decltype(auto) adjacency_node_list(size_t node_index) const
562 {
563 return tpf::get_element<const node_container_t&>(m_nodes, node_index);
564 }
565
566 const node_container_t& node_lists() const { return this->m_nodes; }
567 const element_container_t& values() const { return this->m_values; }
568
569 auto adjacent_node_values(size_t node_index)
570 {
572 auto indices = tpf::get_element<node_container_t&>(m_nodes, node_index);
573
574 for(auto& idx: indices)
575 {
576 auto& value = tpf::get_element<element_container_t&>(m_values, idx);
577 elements.emplace_back(std::ref(value));
578 }
579
580 return elements;
581 }
582
583 auto adjacent_node_values(size_t node_index) const
584 {
586 auto indices = tpf::get_element<const node_container_t&>(m_nodes, node_index);
587
588 for(auto& idx: indices)
589 {
590 auto& value = tpf::get_element<const element_container_t&>(m_values, idx);
591 elements.emplace_back(std::cref(value));
592 }
593
594 return elements;
595 }
596
597 decltype(auto) node_value(size_t node_index)
598 {
599 return tpf::get_element<element_container_t&>(m_values, node_index);
600 }
601
602 decltype(auto) node_value(size_t node_index) const
603 {
604 return tpf::get_element<const element_container_t&>(m_values, node_index);
605 }
606
607 template<typename IndexType_1, typename IndexType_2>
608 auto get_edge_range_iterators(IndexType_1 index_1, IndexType_2 index_2) const
609 {
610 auto e = make_edge<edge_type_t>(index_1, index_2, edge_weight_t{});
611 return tpf::find_range_iterators(this->m_edges, e, edge_type_t::weak_comparator);
612 }
613
614 template<typename IndexType_1, typename IndexType_2, typename WeightType>
615 auto get_edge_range_iterators(IndexType_1 index_1, IndexType_2 index_2, WeightType weight) const
616 {
617 auto e = make_edge<edge_type_t>(index_1, index_2, weight);
618 return tpf::find_range_iterators(this->m_edges, e, edge_type_t::strong_comparator);
619 }
620
621 auto get_edge_range_iterators(const edge_type_t& e, bool bStrongCompare = false) const
622 {
623 if(bStrongCompare)
624 return tpf::find_range_iterators(this->m_edges, e, edge_type_t::strong_comparator);
625 else
626 return tpf::find_range_iterators(this->m_edges, e, edge_type_t::weak_comparator);
627 }
628
629 node_info_t node_info(size_t node_index) const
630 {
632 auto indices = tpf::get_element<const node_container_t&>(m_nodes, node_index);
633
634 for(auto& idx: indices)
635 {
636 auto& value = tpf::get_element<const element_container_t&>(m_values, idx);
637 elements.emplace_back(std::cref(value));
638 }
639
640 return { node_value(node_index), std::move(elements) };
641 }
642
643 tpf::sstream& get_node_name(tpf::sstream& os, size_t index) const
644 {
645 os << "node_" << index; return os;
646 }
647
649 node_index_t index_1, node_index_t index_2,
650 bool bProportional = false, const char* color="black") const
651 {
652 auto e = make_edge<edge_type_t>(index_1, index_2, edge_weight_t{});
653
654 auto [success, iterator] =
655 tpf::binary_find_bool_iterator_pair(this->m_edges, e, edge_type_t::weak_comparator);
656
657 if(success)
658 {
659 os << " [ dir = none, color = \""<< color << "\", label = \" "
660 << iterator->weight() << " \"] ;";
661 }
662
663 return os;
664 }
665
667 {
668 auto& value = this->node_value(index);
669
670 get_node_name(os, index) << " [ label =\" "
671 << value << " \"];";
672
673 return os;
674 }
675
677 visit_mode vmode = visit_mode::graph, bool bProportional = false) const
678 {
679 size_t size = this->m_nodes.size();
680
681 for(size_t i=0; i <size; ++i)
682 {
683 get_node_definition(os, i) << "\n";
684 }
685
686 os << "\n";
687
688 using set_t = std::vector<index_t>;
689 using sets_t = std::vector<set_t>;
690
691 sets_t sets;
692
693 for(size_t i = 0; i < size; ++i)
694 {
695 auto nodes = this->adjacency_node_list(i);
696 for(const auto& v: nodes)
697 {
698 set_t s{(index_t)i};
699 s.emplace_back(v);
700 sets.push_back(s);
701 }
702 }
703
705
706 for(auto& s: sets)
707 {
708 get_node_name(os, s[0]);
709
710 if(vmode == visit_mode::graph)
711 os << " -- ";
712 else
713 os << " -> ";
714
715 get_node_name(os, s[1]);
716
717 if(vmode != visit_mode::graph)
718 {
720 os << "\n";
721 }
722 else
723 os << " ; \n";
724 }
725
726 if(vmode != visit_mode::graph)
727 {
728 std::vector<index_t> visit_order;
729
730 if(vmode == visit_mode::visit_bfs)
731 visit_order = traverse_bfs();
732 else
733 visit_order = traverse_dfs();
734
735 if(visit_order.size() > 1)
736 {
737 os << "\n";
738
739 for(size_t i = 1; i < visit_order.size(); ++i )
740 {
741 get_node_name(os, (size_t)visit_order[i-1]);
742 os << " -> ";
743 get_node_name(os, (size_t)visit_order[i]);
744
745 os << " [ color = blue ] ; \n";
746 }
747 }
748 }
749 return os;
750 }
751
753 {
754 tpf::sstream os;
755
756 os << "digraph G {\n";
757
758 if(vmode == visit_mode::visit_bfs)
759 os << "label=\"Breadth First Search\"\n";
760 else
761 os << "label=\"Depth First Search\"\n";
762
763 build_graph_edges(os, vmode);
764
765 os << "}";
766
767 return os.str();
768
769 }
770
772 {
773 size_t size = this->m_nodes.size();
774
775 for(size_t i=0; i <size; ++i)
776 {
777 get_node_definition(os, i) << "\n";
778 }
779
780 os << "\n";
781
782 for(const auto& edge: this->m_edges)
783 {
784 get_node_name(os, edge.node_1());
785 os << " -> ";
786 get_node_name(os, edge.node_2());
787
788 os << " [ ";
789
791 os << "dir = none, ";
792
793 os << "label = \" " << edge.weight() << " \"];";
794
795 os << "\n";
796
797 }
798
799 return os;
800 }
801
802 std::string draw_graph(
803 const std::string& graph_title = "Plain Graph",
804 const std::string& graph_style = "",
805 const std::string& best_viewed = "dot") const
806 {
807 tpf::sstream os;
808
809 os << "digraph G {\n\n";
810
811 if(graph_style != "")
812 os << graph_style << "\n";
813
814 os << "label=\"";
815 os << graph_title <<"\\nBest Viewed: ";
816 os << best_viewed << "\";\n\n";
817
819
820 os << "}";
821
822 return os.str();
823
824 }
825
826 std::string build_graph(visit_mode vmode = visit_mode::graph) const
827 {
828 if(vmode == visit_mode::graph)
829 {
830 tpf::sstream os;
831
832 os << "digraph G {\n";
833 os << "label=\"Plain Graph\"\n";
834
836
837 os << "}";
838
839 return os.str();
840 }
841 else
842 {
843 tpf::sstream os;
844
845 os << "digraph G {\n";
846
847 if(vmode == visit_mode::visit_bfs)
848 os << "label=\"Breadth First Search\"\n";
849 else
850 os << "label=\"Depth First Search\"\n";
851
852 build_graph_edges(os, vmode);
853
854 os << "}";
855
856 return os.str();
857 }
858 }
859
860 graph() = default;
861
862 size_t size() { return this->m_nodes.size(); }
863
864 // throws exception if fails
866 {
867 size_t size = this->m_nodes.size();
868 for(const auto& node: this->m_nodes)
869 {
870 for(const auto& e: node)
871 {
872 if(e >= size)
873 {
874 tpf::sstream msg;
875 msg << "node " << node << " has an adjacent vertex " << e
876 <<" (max node index " << (size - 1)
877 << ") which does not exist in the node list ";
878
880 }
881 }
882 }
883 }
884
885 template<typename EleType, typename Type, typename... Types>
886 void emplace_back_node(EleType&& value, Type&& index0, Types&&... indices)
887 {
888 auto v = tpf::make_random_access_container<IndexContainerType>((NodeIndexType)std::forward<Type>(index0),
889 (NodeIndexType)std::forward<Types>(indices)...);
891
892 this->m_nodes.emplace_back(std::move(v));
893 this->m_values.emplace_back(std::forward<EleType>(value));
894 }
895
896 auto is_in_edge_list(const edge_type_t& e, bool bStrongCompare = false) const noexcept
897 {
898 if(bStrongCompare)
899 return tpf::binary_find_bool_iterator_pair(this->m_edges, e, edge_type_t::strong_comparator);
900 else
901 return tpf::binary_find_bool_iterator_pair(this->m_edges, e, edge_type_t::weak_comparator);
902 }
903
904 edge_container_t& edges() noexcept { return this->m_edges; }
905 const edge_container_t& edges() const noexcept { return this->m_edges; }
906
907
908 template<typename IndexType_1, typename IndexType_2, typename... WeightType>
909 void emplace_back_edge(IndexType_1 index_1, IndexType_2 index_2, WeightType... weight)
910 {
911 size_t size = this->m_nodes.size();
912
913 if( (size_t)index_1 >= size)
914 {
915 tpf::sstream msg;
916
917 msg <<"The edge (" << index_1 << ", " << index_2
918 <<") is not VALID, its first vertex index "<< index_1
919 << " is out of range (max node index " << (size-1) <<").";
920
922 }
923
924 if( (size_t)index_2 >= size)
925 {
926 tpf::sstream msg;
927
928 msg <<"The edge (" << index_1 << ", " << index_2
929 <<") is not VALID, its second vertex index "<< index_2
930 << " is out of range (max node index " << (size-1) <<").";
931
933 }
934
935 if constexpr(is_weighted(directed_edge))
936 {
937 static_assert(sizeof...(WeightType) == 1, "Weight of edge should be specified");
938
939 auto e = make_edge<edge_type_t>(index_1, index_2, weight...);
940
942 {
943 // false for "weak compare, ignore weight."
944 auto [already_exists, iterator] = is_in_edge_list(e, false);
945 if(already_exists)
946 {
947 // strong comparison
948 if(e == *iterator) return; // ignore
949
950 tpf::sstream msg;
951 msg << "new edge " << e << " is different from the existing edge "
952 << *iterator <<".\nMultiple different edges incident on the same node pairs are not allowed in a signle-edged graph.";
953
955 }
956 }
957 else
958 {
959 // true for "strong compare, respect weight."
960 auto [already_exists, iterator] = is_in_edge_list(e, true);
961 if(already_exists) return; // ignore exact match
962 // {
963 // tpf::sstream msg;
964 // msg << "new edge " << e << " is different from the existing edge "
965 // << *iterator <<". Multiple different edges incident on the same node pairs are not allowed";
966
967 // Tpf_ThrowDebugException(msg);
968 // }
969 }
970
971 this->m_edges.push_back(std::move(e));
972 set::sort_in_place(this->m_edges);
973 }
974 else
975 {
976 static_assert(sizeof...(WeightType) == 0, "Weight of edge is not allowed");
977
978 auto e = make_edge<edge_type_t>(index_1, index_2);
979
980 auto [already_exists, iterator] = is_in_edge_list(e);
981 if(already_exists) return;
982
983 this->m_edges.push_back(std::move(e));
984 set::sort_in_place(this->m_edges);
985 }
986 }
987
988 auto operator[](size_t node_index)
989 {
990 return node_ref_t{ tpf::get_element<element_container_t&>(this->m_values, node_index),
991 tpf::get_element<node_container_t&>(this->m_nodes, node_index) };
992 }
993
994 auto operator[](size_t node_index) const
995 {
996 return const_node_ref_t{ tpf::get_element<const element_container_t&>(this->m_values, node_index),
997 tpf::get_element<const node_container_t&>(this->m_nodes, node_index) };
998 }
999
1000 std::vector<index_t> traverse_bfs(index_t start = 0) const
1001 {
1003 auto endl = tpf::endl;
1004
1005 std::deque<index_t> queue;
1006 std::vector<char> visited(m_nodes.size());
1007 std::vector<index_t> traversal;
1008
1009 for(auto& v: visited) v = 0;
1010 queue.push_back(start);
1011
1012 visited[(size_t)start] = 1;
1013
1014 while(!queue.empty())
1015 {
1016 auto p = queue.front();
1017 queue.pop_front();
1018
1019 traversal.push_back(p);
1020
1021 auto node_list = this->adjacency_node_list((size_t)p);
1022 for(auto& node: node_list)
1023 {
1024 if(visited[(size_t)node] == 0)
1025 {
1026 queue.push_back(node);
1027 visited[(size_t)node] = 1;
1028
1029 }
1030 }
1031 }
1032
1033 return traversal;
1034 }
1035
1036 void push_stack(std::deque<index_t>& stack,
1037 std::vector<char>& visited,
1038 size_t node_index) const
1039 {
1040 const index_container_t& node_list
1041 = this->adjacency_node_list(node_index);
1042
1043 for(const auto& node: node_list)
1044 {
1045 if(visited[(size_t)node] != 1)
1046 {
1047 stack.push_front(node);
1048 visited[(size_t)node] = 1;
1049 }
1050 }
1051 }
1052
1053 void move_vertex(indices_t& visited, indices_t& unvisited, index_t index) const
1054 {
1055 visited.push_back(index);
1056
1057 auto itr = std::find(unvisited.cbegin(), unvisited.cend(), index);
1058
1059 if(itr != unvisited.end()) unvisited.erase(itr);
1060 }
1061
1063 {
1064 size_t size = this->m_values.size();
1065
1066 if((size_t)index < this->m_values.size())
1067 return tpf::get_element(this->m_values, (size_t)index);
1068 else
1069 return "Unvisited";
1070 }
1071
1072 node_names_t translate(const indices_t& indices) const
1073 {
1074 size_t size = indices.size();
1075 node_names_t names; names.reserve(size);
1076
1077 for(const auto& index: indices)
1078 {
1079 if((size_t)index < this->m_values.size())
1080 { auto e = tpf::get_element(this->m_values, (size_t)index);
1081 names.emplace_back(e);
1082 }
1083 else
1084 {
1085 names.emplace_back("Unvisited");
1086 }
1087
1088 }
1089
1090 return names;
1091 }
1092
1093 shortest_path_t shortest_path(index_t start_node = 0, index_t end_node=0) const
1094 {
1096 auto endl = tpf::endl;
1097
1098 size_t size = this->m_nodes.size();
1099
1100 indices_t unvisited(size);
1101 std::iota(unvisited.begin(), unvisited.end(), 0);
1102
1103 indices_t visited; visited.reserve(size);
1104
1105 indices_t previous_vertices;
1106 distances_t shortest_distances;
1107
1108 auto max_weight = tpf::type_max_v<edge_weight_t>;
1109 auto max_index = tpf::type_max_v<index_t>;
1110
1111 for(size_t i=0; i < size; ++i)
1112 {
1113 if(i != (size_t)start_node)
1114 {
1115 shortest_distances.emplace_back(max_weight);
1116 previous_vertices.emplace_back(max_index);
1117 }
1118 else
1119 {
1120 shortest_distances.emplace_back(edge_weight_t{});
1121 previous_vertices.emplace_back(start_node);
1122 }
1123 }
1124
1125 auto edge_indices = get_edge_indices(start_node);
1126
1127 for(auto edge_index: edge_indices)
1128 {
1129 auto edge = get_edge(edge_index);
1130 auto other_node = edge.other_node((node_index_t)start_node);
1131
1132 shortest_distances[(size_t)other_node] = edge.weight();
1133
1134 previous_vertices[(size_t)other_node] = (index_t)start_node;
1135 }
1136
1137 this->move_vertex(visited, unvisited, start_node);
1138
1139 while(!unvisited.empty())
1140 {
1141 start_node = (index_t)set::minimum_value_index(shortest_distances, visited);
1142 edge_weight_t start_weight = shortest_distances[(size_t)start_node];
1143
1144 edge_indices = this->get_edge_indices(start_node, visited);
1145
1146 for(auto edge_index: edge_indices)
1147 {
1148 auto edge = get_edge(edge_index);
1149 auto other_node = edge.other_node((node_index_t)start_node);
1150
1151 auto new_weight = start_weight + edge.weight();
1152
1153 if( new_weight < shortest_distances[(size_t)other_node])
1154 {
1155 shortest_distances[(size_t)other_node] = new_weight;
1156 previous_vertices[(size_t)other_node] = (index_t)start_node;
1157 }
1158 }
1159
1160 this->move_vertex(visited, unvisited, start_node);
1161 }
1162
1163 return {visited, shortest_distances,
1164 previous_vertices, shortest_distances[(size_t)visited.back()]};
1165 }
1166
1168 const indices_t& visited, const distances_t& distances,
1169 const indices_t& previous_node, index_t start_node, index_t end_node) const
1170 {
1171 std::deque<edge_type_t> itinerary;
1172
1173 while(start_node != end_node)
1174 {
1175 auto distance = distances[(size_t)end_node];
1176
1177 auto prev_idx = previous_node[end_node];
1178
1179 auto e = make_edge<edge_type_t>(prev_idx, end_node, distance, true);
1180
1181 itinerary.push_front(e);
1182
1183 end_node = prev_idx;
1184 }
1185
1186 for(const auto& edge: itinerary)
1187 {
1188 get_node_name(os, edge.node_1());
1189 os << " -> ";
1190 get_node_name(os, edge.node_2());
1191
1192 os << " [ style = dashed, color= blue, fontcolor = blue, ";
1193
1194 os << "label = \" " << edge.weight() << " \"];";
1195
1196 os << "\n";
1197
1198 }
1199
1200 return os;
1201 }
1202
1204 const indices_t& visited, const distances_t& distances,
1205 const indices_t& previous_node, index_t start_node, index_t end_node) const
1206 {
1207 const std::string& graph_title = "Shortest Path";
1208 const std::string& graph_style = "pack=100;\npackmode=array;\nedge [len=2];";
1209 const std::string& best_viewed = "neato or osage";
1210
1211 os << "digraph G {\n\n";
1212
1213 if(graph_style != "")
1214 os << graph_style << "\n";
1215
1216 os << "label=\"";
1217 os << graph_title <<"\\nBest Viewed: ";
1218 os << best_viewed << "\";\n\n";
1219
1220 draw_graph_edges(os);
1221
1222 os << "\n";
1223
1224 draw_edge_list(os, visited, distances, previous_node, start_node, end_node);
1225
1226 os << "\n";
1227
1228 os << "}";
1229 }
1230
1231 std::string shortest_path_report(element_t start_pos, element_t end_pos) const
1232 {
1234
1235 auto endl = "\n";
1236 auto endlL = "\n\n";
1237
1238 auto start_itr = std::find(this->m_values.cbegin(), this->m_values.cend(), start_pos);
1239 auto end_itr = std::find(this->m_values.cbegin(), this->m_values.cend(), end_pos);
1240
1241 if(start_itr == this->m_values.cend())
1242 {
1243 stream << start_pos << " is not in the node list";
1245 }
1246
1247 if(end_itr == this->m_values.cend())
1248 {
1249 stream << end_pos << " is not in the node list";
1251 }
1252
1253 index_t start_node = std::distance(this->m_values.cbegin(), start_itr);
1254 index_t end_node = std::distance(this->m_values.cbegin(), end_itr);
1255
1256 stream <<"Start: " << start_node << endl;
1257 stream <<"Start: " << end_node << endl;
1258
1259 auto [route, distances, previous_node, shortest] =
1260 this->shortest_path(start_node, end_node);
1261
1262 stream <<"Shortest Path: " << this->translate(route) << endL;
1263 stream <<"Distances: " << this->get_node_values() << endl;
1264 stream <<"from "<< this->translate(start_node) <<" : " << distances << endl;
1265 stream <<"Previous node: " << this->translate(previous_node) << endL;
1266
1267 stream <<"Shortest distance from " << this->translate(start_node) <<" to "
1268 << this->translate(end_node) << " is " << distances[(size_t)end_node];
1269
1270 stream << "\n\n";
1271
1272 draw_shortest_path(stream, route, distances, previous_node, start_node, end_node);
1273
1274 return stream.str();
1275 }
1276
1277 std::vector<index_t> traverse_dfs(index_t start=0) const
1278 {
1280 auto endl = tpf::endl;
1281
1282 std::deque<index_t> stack;
1283 std::vector<char> visited(this->m_nodes.size());
1284 std::vector<index_t> traversal;
1285
1286 for(auto& v: visited) v = 0;
1287
1288 push_stack(stack, visited, (size_t)start);
1289
1290 while(!stack.empty())
1291 {
1292 traversal.push_back(stack.front());
1293 stack.pop_front();
1294 push_stack(stack, visited, (size_t)traversal.back());
1295 }
1296
1297 return traversal;
1298 }
1299
1300
1302 {
1303 if(g.m_nodes.empty())
1304 {
1305 os << "--------\n";
1306 os << " empty \n";
1307 os << "--------\n";
1308
1309 return os;
1310 }
1311
1312 auto last_itr = g.m_nodes.cend();
1313
1314 os << "--------\n";
1315
1317
1318 size_t index = 0;
1319
1320 for(auto itr = g.m_nodes.cbegin(); itr != g.m_nodes.cend(); ++itr)
1321 os << std::setw(3) << index << " - ("
1322 << tpf::get_element<const element_container_t&>(g.m_values, index++)<<") " << *itr << "\n";
1323
1324 os << "--------\n";
1325
1326 os << g.m_edges << "\n";
1327
1328 os << "--------";
1329
1330 return os;
1331 }
1332 };
1333
1334 template<int DirectedEdge, int EdgePlurality = edge_plurality::single_edged,
1335 typename ElementType = const char*, typename NodeIndexType = int,
1336 typename EdgeWeightType = float,
1337 template<typename, typename...> class NodeContainerType = std::list,
1338 template<typename, typename...> class IndexContainerType = std::vector,
1339 template<typename, typename...> class EdgeContainerType = std::vector>
1340 using graph_t = graph<DirectedEdge, EdgePlurality, ElementType, NodeIndexType, EdgeWeightType,
1341 NodeContainerType, IndexContainerType, EdgeContainerType>;
1342
1343 } // end of namespace graph
1344
1345} // end of namespace tpf
1346
1347#endif // end of file _TPF_GRAPH_HPP
reference_wrapper< Type > ref(Type &val) noexcept
tpf::sstream stream
edge & set(IndexType_1 index_1, IndexType_2 index_2, WeightType weight, bool bForceDirection=false) noexcept
Definition: tpf_graph.hpp:264
std::tuple< node_index_t, node_index_t, weight_t > edge_tuple_t
Definition: tpf_graph.hpp:239
node_index_t other_node(IndexType node_index) const noexcept
Definition: tpf_graph.hpp:331
friend tpf::sstream & operator<<(tpf::sstream &os, const edge &e)
Definition: tpf_graph.hpp:379
static int weak_compare_edges(const edge &lhs, const edge &rhs)
Definition: tpf_graph.hpp:281
edge(IndexType_1 index_1, IndexType_2 index_2, WeightType weight, bool bForceDirection=false) noexcept
Definition: tpf_graph.hpp:249
static int strong_compare_edges(const edge &lhs, const edge &rhs)
Definition: tpf_graph.hpp:298
static int compare_edges(const edge &lhs, const edge &rhs)
Definition: tpf_graph.hpp:138
node_index_t other_node(IndexType node_index) const noexcept
Definition: tpf_graph.hpp:128
edge & set(IndexType_1 index_1, IndexType_2 index_2, bool bForceDirection=false) noexcept
Definition: tpf_graph.hpp:111
const edge_tuple_t & get() const noexcept
Definition: tpf_graph.hpp:212
std::tuple< node_index_t, node_index_t > edge_tuple_t
Definition: tpf_graph.hpp:89
const node_index_t & node_1() const noexcept
Definition: tpf_graph.hpp:105
edge(IndexType_1 index_1, IndexType_2 index_2, bool bForceDirection=false) noexcept
Definition: tpf_graph.hpp:99
friend void swap(edge &lhs, edge &rhs) noexcept
Definition: tpf_graph.hpp:202
const node_index_t & node_2() const noexcept
Definition: tpf_graph.hpp:108
std::tuple< const ElementType &, const index_container_t & > const_node_ref_t
Definition: tpf_graph.hpp:455
void draw_shortest_path(tpf::sstream &os, const indices_t &visited, const distances_t &distances, const indices_t &previous_node, index_t start_node, index_t end_node) const
Definition: tpf_graph.hpp:1203
auto adjacent_node_values(size_t node_index) const
Definition: tpf_graph.hpp:583
shortest_path_t shortest_path(index_t start_node=0, index_t end_node=0) const
Definition: tpf_graph.hpp:1093
const node_container_t & node_lists() const
Definition: tpf_graph.hpp:566
NodeContainerType< index_container_t > node_container_t
Definition: tpf_graph.hpp:448
decltype(auto) get_node_indices(index_t node_index)
Definition: tpf_graph.hpp:486
void integrity_check_nodes_throws() const
Definition: tpf_graph.hpp:865
tpf::sstream & get_node_name(tpf::sstream &os, size_t index) const
Definition: tpf_graph.hpp:643
void push_stack(std::deque< index_t > &stack, std::vector< char > &visited, size_t node_index) const
Definition: tpf_graph.hpp:1036
auto operator[](size_t node_index)
Definition: tpf_graph.hpp:988
auto get_edge_range_iterators(IndexType_1 index_1, IndexType_2 index_2) const
Definition: tpf_graph.hpp:608
NodeContainerType< element_t > element_container_t
Definition: tpf_graph.hpp:449
static constexpr int edge_multiplicity
Definition: tpf_graph.hpp:441
auto get_edge_range_iterators(IndexType_1 index_1, IndexType_2 index_2, WeightType weight) const
Definition: tpf_graph.hpp:615
const edge_container_t & edges() const noexcept
Definition: tpf_graph.hpp:905
tpf::sstream & get_edge_definition(tpf::sstream &os, node_index_t index_1, node_index_t index_2, bool bProportional=false, const char *color="black") const
Definition: tpf_graph.hpp:648
std::vector< edge_weight_t > distances_t
Definition: tpf_graph.hpp:459
decltype(auto) get_edge(index_t index) const
Definition: tpf_graph.hpp:496
tpf::sstream & draw_edge_list(tpf::sstream &os, const indices_t &visited, const distances_t &distances, const indices_t &previous_node, index_t start_node, index_t end_node) const
Definition: tpf_graph.hpp:1167
auto adjacent_node_values(size_t node_index)
Definition: tpf_graph.hpp:569
std::string draw_graph_traversal(visit_mode vmode=visit_mode::visit_dfs) const
Definition: tpf_graph.hpp:752
bool empty() const
Definition: tpf_graph.hpp:554
std::vector< index_t > indices_t
Definition: tpf_graph.hpp:434
indices_t get_edge_indices(index_t node_index, const indices_t &visited) const
Definition: tpf_graph.hpp:525
node_info_t node_info(size_t node_index) const
Definition: tpf_graph.hpp:629
void emplace_back_node(EleType &&value, Type &&index0, Types &&... indices)
Definition: tpf_graph.hpp:886
std::vector< index_t > traverse_dfs(index_t start=0) const
Definition: tpf_graph.hpp:1277
NodeIndexType index_t
Definition: tpf_graph.hpp:432
decltype(auto) get_node_indices(index_t node_index) const
Definition: tpf_graph.hpp:481
auto is_in_edge_list(const edge_type_t &e, bool bStrongCompare=false) const noexcept
Definition: tpf_graph.hpp:896
std::string draw_graph(const std::string &graph_title="Plain Graph", const std::string &graph_style="", const std::string &best_viewed="dot") const
Definition: tpf_graph.hpp:802
tpf::sstream & get_node_definition(tpf::sstream &os, size_t index) const
Definition: tpf_graph.hpp:666
friend class edge
Definition: tpf_graph.hpp:428
NodeContainerType< std::reference_wrapper< element_t > > ref_element_container_t
Definition: tpf_graph.hpp:451
std::tuple< ElementType &, index_container_t & > node_ref_t
Definition: tpf_graph.hpp:454
std::string build_graph(visit_mode vmode=visit_mode::graph) const
Definition: tpf_graph.hpp:826
tpf::sstream & draw_graph_edges(tpf::sstream &os) const
Definition: tpf_graph.hpp:771
decltype(auto) adjacency_node_list(size_t node_index) const
Definition: tpf_graph.hpp:561
decltype(auto) get_edge(index_t index)
Definition: tpf_graph.hpp:491
friend tpf::sstream & operator<<(tpf::sstream &os, const graph &g)
Definition: tpf_graph.hpp:1301
void emplace_back_edge(IndexType_1 index_1, IndexType_2 index_2, WeightType... weight)
Definition: tpf_graph.hpp:909
std::string shortest_path_report(element_t start_pos, element_t end_pos) const
Definition: tpf_graph.hpp:1231
const element_container_t & values() const
Definition: tpf_graph.hpp:567
IndexContainerType< index_t > index_container_t
Definition: tpf_graph.hpp:447
EdgeContainerType< edge_type_t > edge_container_t
Definition: tpf_graph.hpp:445
NodeContainerType< std::reference_wrapper< const element_t > > const_ref_element_container_t
Definition: tpf_graph.hpp:452
node_names_t translate(const indices_t &indices) const
Definition: tpf_graph.hpp:1072
std::vector< index_t > traverse_bfs(index_t start=0) const
Definition: tpf_graph.hpp:1000
NodeIndexType node_index_t
Definition: tpf_graph.hpp:437
EdgeWeightType edge_weight_t
Definition: tpf_graph.hpp:438
std::tuple< const ElementType &, const_ref_element_container_t > node_info_t
Definition: tpf_graph.hpp:457
decltype(auto) adjacency_node_list(size_t node_index)
Definition: tpf_graph.hpp:556
indices_t get_edge_indices(index_t node_index) const
Definition: tpf_graph.hpp:501
decltype(auto) node_value(size_t node_index)
Definition: tpf_graph.hpp:597
auto operator[](size_t node_index) const
Definition: tpf_graph.hpp:994
edge_container_t & edges() noexcept
Definition: tpf_graph.hpp:904
tpf::sstream & build_graph_edges(tpf::sstream &os, visit_mode vmode=visit_mode::graph, bool bProportional=false) const
Definition: tpf_graph.hpp:676
decltype(auto) node_value(size_t node_index) const
Definition: tpf_graph.hpp:602
std::tuple< indices_t, distances_t, indices_t, edge_weight_t > shortest_path_t
Definition: tpf_graph.hpp:463
auto get_edge_range_iterators(const edge_type_t &e, bool bStrongCompare=false) const
Definition: tpf_graph.hpp:621
const element_container_t & get_node_values() const
Definition: tpf_graph.hpp:476
static constexpr int directed_edge
Definition: tpf_graph.hpp:440
std::vector< ElementType > node_names_t
Definition: tpf_graph.hpp:435
void move_vertex(indices_t &visited, indices_t &unvisited, index_t index) const
Definition: tpf_graph.hpp:1053
element_t translate(index_t index) const
Definition: tpf_graph.hpp:1062
friend tpf::sstream & operator<<(tpf::sstream &os, const graph &g)
std::string str() const
Definition: tpf_output.hpp:951
bool operator==(rational< L > const &lhs, rational< R > const &rhs)
constexpr bool is_weighted(int weighted)
Definition: tpf_graph.hpp:59
EdgeType make_edge(IndexType_1 index_1, IndexType_2 index_2, WeightType weight, bool bForceDirection=false)
Definition: tpf_graph.hpp:410
constexpr bool is_directed(int direction)
Definition: tpf_graph.hpp:54
string_stream & operator>(string_stream &os, Type &&arg)
string_stream & operator<(string_stream &os, Type &&arg)
Implements set operations.
Definition: tpf_set.hpp:52
types::enable_if_container_type_t< ContainerType< EleType, Types... >, size_t > minimum_value_index(const ContainerType< EleType, Types... > &container)
Definition: tpf_set.hpp:1335
types::enable_if_container_type_t< ContainerType< EleType, Types... >, bool > is_in_container(const EleType &ele, const ContainerType< EleType, Types... > &container)
Definition: tpf_set.hpp:288
types::enable_if_container_type_t< ContainerType< EleType, Types... >, bool > operator!=(const ContainerType< EleType, Types... > &left, const ContainerType< EleType, Types... > &right)
Definition: tpf_set.hpp:197
types::enable_if_container_type_t< ContainerType< EleType, Types... >, bool > sort_in_place(ContainerType< EleType, Types... > &container, sort_order order=sort_order::ascending, sort_method method=sort_method::size)
Definition: tpf_set.hpp:376
Includes subnamespace conversion.
Definition: 31-visit.cpp:7
auto find_range_iterators(ForwardIterator first, ForwardIterator last, const EleType &value, CompareCallbackType &&compare_callback=CompareCallbackType{})
Definition: tpf_types.hpp:1519
auto minimum(Type_1 a, Type_2 b)
Definition: tpf_types.hpp:196
typename SetTagType::set_t set_t
Definition: tpf_types.hpp:1966
auto binary_find_index(ForwardIterator first, ForwardIterator last, const EleType &value, CompareCallbackType &&compare_callback=CompareCallbackType{})
Definition: tpf_types.hpp:1565
constexpr auto endL
Definition: tpf_output.hpp:974
auto binary_find_bool_iterator_pair(ForwardIterator first, ForwardIterator last, const EleType &value, CompareCallbackType &&compare_callback=CompareCallbackType{})
Definition: tpf_types.hpp:1586
auto maximum(Type_1 a, Type_2 b)
Definition: tpf_types.hpp:181
constexpr auto endl
Definition: tpf_output.hpp:973
decltype(auto) get_element(ContainerType container, IndexType index)
Definition: tpf_types.hpp:7899
typename SetTagType::sets_t sets_t
Definition: tpf_types.hpp:1969
bool operator()(const edge &lhs, const edge &rhs) const
Definition: tpf_graph.hpp:165
bool operator()(const edge &lhs, const edge &rhs) const
Definition: tpf_graph.hpp:157
static constexpr int undirected
Definition: tpf_graph.hpp:38
static constexpr int directed
Definition: tpf_graph.hpp:42
static constexpr int undirected_unweighted
Definition: tpf_graph.hpp:39
static constexpr int weighted_undirected
Definition: tpf_graph.hpp:48
static constexpr int directed_weighted
Definition: tpf_graph.hpp:50
static constexpr int undirected_weighted
Definition: tpf_graph.hpp:47
static constexpr int weighted_directed
Definition: tpf_graph.hpp:51
static constexpr int unweighted_undirected
Definition: tpf_graph.hpp:40
static constexpr int directed_unweighted
Definition: tpf_graph.hpp:43
static constexpr int weighted
Definition: tpf_graph.hpp:46
static constexpr int unweighted_directed
Definition: tpf_graph.hpp:44
static constexpr int single_edged
Definition: tpf_graph.hpp:66
static constexpr int plural_edged
Definition: tpf_graph.hpp:67
static constexpr int weighted_edge
Definition: tpf_graph.hpp:68
Implements set operations.
#define Tpf_ThrowDebugException(debug_message)
Throw a debug_exception with message as argument.
Definition: tpf_types.hpp:1416