Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
graph_visualization_test.cc
Go to the documentation of this file.
1
2/*
3 Aleph_w
4
5 Data structures & Algorithms
6 version 2.0.0b
7 https://github.com/lrleon/Aleph-w
8
9 This file is part of Aleph-w library
10
11 Copyright (c) 2002-2026 Leandro Rabindranath Leon
12
13 Permission is hereby granted, free of charge, to any person obtaining a copy
14 of this software and associated documentation files (the "Software"), to deal
15 in the Software without restriction, including without limitation the rights
16 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17 copies of the Software, and to permit persons to whom the Software is
18 furnished to do so, subject to the following conditions:
19
20 The above copyright notice and this permission notice shall be included in all
21 copies or substantial portions of the Software.
22
23 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29 SOFTWARE.
30*/
31
32
43#include <gtest/gtest.h>
44#include <sstream>
45#include <string>
46
47#include <tpl_graph.H>
48#include <tpl_graph_utils.H>
49#include <generate_graph.H>
50#include <graph_to_tree.H>
52#include <generate_tree.H>
53#include <tpl_tree_node.H>
54
55using namespace std;
56using namespace testing;
57using namespace Aleph;
58
59// ============================================================================
60// Test Fixtures
61// ============================================================================
62
64class SimpleGraphTest : public Test
65{
66protected:
69 using Arc = Graph::Arc;
70
75
76 void SetUp() override
77 {
78 n1 = g.insert_node(1);
79 n2 = g.insert_node(2);
80 n3 = g.insert_node(3);
81 g.insert_arc(n1, n2, 10);
82 g.insert_arc(n2, n3, 20);
83 g.insert_arc(n1, n3, 30);
84 }
85};
86
88class SimpleDigraphTest : public Test
89{
90protected:
94
99
100 void SetUp() override
101 {
102 n1 = g.insert_node(1);
103 n2 = g.insert_node(2);
104 n3 = g.insert_node(3);
105 g.insert_arc(n1, n2, 10);
106 g.insert_arc(n2, n3, 20);
107 g.insert_arc(n1, n3, 30);
108 }
109};
110
112class TreeGraphTest : public Test
113{
114protected:
118
124
125 void SetUp() override
126 {
127 // Create a simple tree:
128 // root(1)
129 // / |
130 // child1(2) child2(3)
131 // |
132 // grandchild(4)
133 root = tree.insert_node(1);
137
141 }
142};
143
144// ============================================================================
145// generate_graph.H Tests - Graphviz DOT generation
146// ============================================================================
147
149{
150 ostringstream out;
152 string result = out.str();
153
154 // Should contain "graph {" for undirected graphs
155 EXPECT_NE(result.find("graph {"), string::npos);
156 // Should NOT contain "digraph"
157 EXPECT_EQ(result.find("digraph {"), string::npos);
158}
159
161{
162 ostringstream out;
164 string result = out.str();
165
166 // Should contain "digraph {" for directed graphs
167 EXPECT_NE(result.find("digraph {"), string::npos);
168}
169
171{
172 ostringstream out;
174 string result = out.str();
175
176 // Should contain labels for all nodes
177 EXPECT_NE(result.find("label = \"1\""), string::npos);
178 EXPECT_NE(result.find("label = \"2\""), string::npos);
179 EXPECT_NE(result.find("label = \"3\""), string::npos);
180}
181
183{
184 ostringstream out;
186 string result = out.str();
187
188 // Should contain "--" for undirected graph arcs
189 EXPECT_NE(result.find("--"), string::npos);
190}
191
193{
194 ostringstream out;
196 string result = out.str();
197
198 // Should contain "->" for directed graph arcs
199 EXPECT_NE(result.find("->"), string::npos);
200}
201
203{
204 ostringstream out;
206 string result = out.str();
207
208 EXPECT_NE(result.find("rankdir = TB"), string::npos);
209}
210
212{
213 ostringstream out;
215 string result = out.str();
216
217 // Should end with closing brace
218 size_t lastBrace = result.rfind('}');
219 EXPECT_NE(lastBrace, string::npos);
220}
221
222// Test default attribute functors
224{
225 ostringstream out;
226 Dft_Node_Attr<Graph>()(g, n1, out);
227 string result = out.str();
228
229 EXPECT_NE(result.find("label"), string::npos);
230 EXPECT_NE(result.find("1"), string::npos);
231}
232
234{
235 auto arc = g.get_first_arc();
236 ostringstream out;
237 Dft_Arc_Attr<Graph>()(g, arc, out);
238 string result = out.str();
239
240 EXPECT_NE(result.find("label"), string::npos);
241}
242
243// Test Dummy_Attr always returns false
250
251// ============================================================================
252// generate_spanning_tree_picture.H Tests
253// ============================================================================
254
261
263{
264 NODE_COOKIE(n1) = n2; // non-null cookie
266 EXPECT_EQ(shader(n1), "SHADOW-NODE");
267}
268
270{
271 auto arc = g.get_first_arc();
272 ARC_COOKIE(arc) = nullptr;
274 EXPECT_EQ(shader(arc), "ARC");
275}
276
278{
279 auto arc = g.get_first_arc();
280 ARC_COOKIE(arc) = n1; // non-null cookie
282 EXPECT_EQ(shader(arc), "SHADOW-ARC");
283}
284
285// ============================================================================
286// graph_to_tree.H Tests
287// ============================================================================
288
291{
294 {
295 tnode->get_key() = gnode->get_info();
296 }
297};
298
309
311{
314
315 // Root should have children
317 ASSERT_NE(firstChild, nullptr);
318
319 // Count children
320 int childCount = 0;
321 for (auto* child = firstChild; child != nullptr;
322 child = child->get_right_sibling())
323 childCount++;
324
325 EXPECT_EQ(childCount, 2); // child1 and child2
326
328}
329
331{
334
335 // Find all node values
336 set<int> values;
338 if (node == nullptr) return;
339 values.insert(node->get_key());
340 for (auto* child = node->get_left_child(); child != nullptr;
341 child = child->get_right_sibling())
342 collectValues(child);
343 };
344
346
347 // Should contain all original values
348 EXPECT_TRUE(values.count(1) > 0);
349 EXPECT_TRUE(values.count(2) > 0);
350 EXPECT_TRUE(values.count(3) > 0);
351 EXPECT_TRUE(values.count(4) > 0);
352 EXPECT_EQ(values.size(), 4u);
353
355}
356
358{
359 // Add an arc to create a cycle
360 tree.insert_arc(grandchild, root, 0);
361
363 EXPECT_THROW(Converter()(tree, root), std::domain_error);
364}
365
366// ============================================================================
367// generate_tree.H Tests (additional to existing tests)
368// ============================================================================
369
371{
374
375 ostringstream out;
377 string result = out.str();
378
379 // Should start with Root
380 EXPECT_EQ(result.find("Root"), 0u);
381
383}
384
386{
389
390 ostringstream out;
392 string result = out.str();
393
394 // Should contain Node entries with Dewey notation
395 EXPECT_NE(result.find("Node "), string::npos);
396
398}
399
401{
402 struct RawWriter
403 {
404 string operator()(Tree_Node<string> * p) { return p->get_key(); }
405 };
406
407 auto * root = new Tree_Node<string>;
408 root->get_key() = "say \"hi\"\npath\\leaf";
409
410 ostringstream out;
412 const string result = out.str();
413
414 EXPECT_NE(result.find("label=\"say \\\"hi\\\"\\npath\\\\leaf\""), string::npos);
415 EXPECT_EQ(result.find("label=\"say \"hi\""), string::npos);
416
417 delete root;
418}
419
420// ============================================================================
421// Integration Tests
422// ============================================================================
423
425{
426 // Convert graph to tree
429
430 // Generate tree output
431 ostringstream out;
433 string result = out.str();
434
435 // Verify output is non-empty and has expected structure
436 EXPECT_FALSE(result.empty());
437 EXPECT_NE(result.find("Root"), string::npos);
438 EXPECT_NE(result.find("\"1\""), string::npos); // Root value
439
441}
442
443// ============================================================================
444// Edge Cases
445// ============================================================================
446
448{
450
451 ostringstream out;
453 string result = out.str();
454
455 // Should still produce valid DOT structure
456 EXPECT_NE(result.find("graph {"), string::npos);
457 EXPECT_NE(result.find("}"), string::npos);
458}
459
461{
463 g.insert_node(42);
464
465 ostringstream out;
467 string result = out.str();
468
469 EXPECT_NE(result.find("42"), string::npos);
470}
471
473{
475 auto* node = g.insert_node(42);
476
477 struct Conv {
478 void operator()(decltype(node) gn, Tree_Node<int>* tn) {
479 tn->get_key() = gn->get_info();
480 }
481 };
482
485
486 ASSERT_NE(treeRoot, nullptr);
487 EXPECT_EQ(treeRoot->get_key(), 42);
488 EXPECT_EQ(treeRoot->get_left_child(), nullptr);
489
491}
492
493// ============================================================================
494// Type Traits Tests
495// ============================================================================
496
498{
499 Tree_Node<int> node;
500 node.get_key() = 123;
501
503 string result = writer(&node);
504
505 EXPECT_EQ(result, "123");
506}
507
508// ============================================================================
509// Additional generate_graph.H Tests
510// ============================================================================
511
512// Test digraph_graphviz() - always outputs digraph format
514{
515 // Custom functors for digraph_graphviz
516 struct NodeAttr {
517 void operator()(const Graph&, Node* n, ostream& out) {
518 out << "label=\"" << n->get_info() << "\"";
519 }
520 };
521 struct ArcAttr {
522 void operator()(const Graph&, Arc* a, ostream& out) {
523 out << "label=\"" << a->get_info() << "\"";
524 }
525 };
526
527 ostringstream out;
529 (g, out);
530 string result = out.str();
531
532 // Should contain "digraph {" even for undirected graph
533 EXPECT_NE(result.find("digraph {"), string::npos);
534 EXPECT_NE(result.find("->"), string::npos);
535}
536
537// Test Generate_Graphviz struct
539{
540 struct WriteNode {
541 string operator()(Node* n) const { return to_string(n->get_info()); }
542 };
543 struct WriteArc {
544 string operator()(Arc* a) const { return to_string(a->get_info()); }
545 };
546
547 ostringstream out;
549 string result = out.str();
550
551 EXPECT_NE(result.find("graph {"), string::npos);
552 EXPECT_NE(result.find("rankdir = TB"), string::npos);
553}
554
555// Test custom rankdir values
557{
558 const vector<string> rankdirs = {"TB", "BT", "LR", "RL"};
559
560 for (const auto& dir : rankdirs) {
561 ostringstream out;
563 string result = out.str();
564 EXPECT_NE(result.find("rankdir = " + dir), string::npos)
565 << "Failed for rankdir: " << dir;
566 }
567}
568
569// Test with string node info
571{
573 SGraph g;
574 auto* a = g.insert_node("Alpha");
575 auto* b = g.insert_node("Beta");
576 g.insert_arc(a, b, 1);
577
578 ostringstream out;
580 string result = out.str();
581
582 EXPECT_NE(result.find("Alpha"), string::npos);
583 EXPECT_NE(result.find("Beta"), string::npos);
584}
585
586// Test with double arc weights
588{
590 DGraph g;
591 auto* a = g.insert_node(1);
592 auto* b = g.insert_node(2);
593 g.insert_arc(a, b, 3.14159);
594
595 ostringstream out;
597 string result = out.str();
598
599 EXPECT_NE(result.find("3.14"), string::npos);
600}
601
602// Test DAG with rank_graphviz
604{
606 DAG g;
607
608 // Create a simple DAG: 1 -> 2 -> 3
609 auto* n1 = g.insert_node(1);
610 auto* n2 = g.insert_node(2);
611 auto* n3 = g.insert_node(3);
612 g.insert_arc(n1, n2, 0);
613 g.insert_arc(n2, n3, 0);
614
615 struct NodeAttr {
616 void operator()(const DAG&, DAG::Node* n, ostream& out) {
617 out << "label=\"" << n->get_info() << "\"";
618 }
619 };
620 struct ArcAttr {
621 void operator()(const DAG&, DAG::Arc*, ostream& out) {
622 out << "";
623 }
624 };
625
626 ostringstream out;
629
630 string result = out.str();
631
632 // Should have multiple ranks
633 EXPECT_GE(numRanks, 1u);
634 // Should contain subgraph declarations
635 EXPECT_NE(result.find("subgraph"), string::npos);
636 EXPECT_NE(result.find("rank"), string::npos);
637}
638
639// ============================================================================
640// Additional generate_tree.H Tests
641// ============================================================================
642
643// Test generate_forest with multiple trees
645{
646 // Create a forest of 3 single-node trees as siblings
650
651 tree1->get_key() = 100;
652 tree2->get_key() = 200;
653 tree3->get_key() = 300;
654
655 // Link as siblings
656 tree1->insert_right_sibling(tree2);
657 tree2->insert_right_sibling(tree3);
658
659 ostringstream out;
661 string result = out.str();
662
663 // Should contain all three roots
664 EXPECT_NE(result.find("100"), string::npos);
665 EXPECT_NE(result.find("200"), string::npos);
666 EXPECT_NE(result.find("300"), string::npos);
667
668 // Should have multiple "Root" entries
669 size_t firstRoot = result.find("Root");
670 size_t secondRoot = result.find("Root", firstRoot + 1);
671 size_t thirdRoot = result.find("Root", secondRoot + 1);
672
673 EXPECT_NE(firstRoot, string::npos);
674 EXPECT_NE(secondRoot, string::npos);
675 EXPECT_NE(thirdRoot, string::npos);
676
677 // Clean up - siblings are not automatically deleted
678 delete tree1;
679 delete tree2;
680 delete tree3;
681}
682
683// Test custom Write functor
685{
686 struct HexWriter {
687 string operator()(Tree_Node<int>* p) {
688 ostringstream ss;
689 ss << "0x" << hex << p->get_key();
690 return ss.str();
691 }
692 };
693
695 root->get_key() = 255;
696
697 ostringstream out;
699 string result = out.str();
700
701 EXPECT_NE(result.find("0xff"), string::npos);
702
703 delete root;
704}
705
706// Test deep tree (many levels)
708{
709 const int DEPTH = 10;
711 root->get_key() = 0;
712
713 Tree_Node<int>* current = root;
714 for (int i = 1; i < DEPTH; ++i) {
715 Tree_Node<int>* child = new Tree_Node<int>;
716 child->get_key() = i;
717 current->insert_leftmost_child(child);
718 current = child;
719 }
720
721 ostringstream out;
723 string result = out.str();
724
725 // Should contain all nodes
726 for (int i = 0; i < DEPTH; ++i) {
727 EXPECT_NE(result.find("\"" + to_string(i) + "\""), string::npos)
728 << "Missing node " << i;
729 }
730
731 // Should have deep Dewey notation like "0.0.0.0..."
732 // The deepest node would have 9 dots in its Dewey number
733 EXPECT_NE(result.find("Node 0.0.0.0"), string::npos);
734
736}
737
738// Test wide tree (many siblings)
740{
741 const int WIDTH = 10;
743 root->get_key() = 0;
744
745 for (int i = 1; i <= WIDTH; ++i) {
746 Tree_Node<int>* child = new Tree_Node<int>;
747 child->get_key() = i;
748 root->insert_rightmost_child(child);
749 }
750
751 ostringstream out;
753 string result = out.str();
754
755 // Should contain all children
756 for (int i = 1; i <= WIDTH; ++i) {
757 EXPECT_NE(result.find("\"" + to_string(i) + "\""), string::npos)
758 << "Missing child " << i;
759 }
760
762}
763
764// ============================================================================
765// graph_to_tree.H Additional Tests
766// ============================================================================
767
768// Test free function graph_to_tree_node (not just the class)
779
780// Test with different starting nodes
782{
783 // Start from child1 instead of root
786
787 ASSERT_NE(treeRoot, nullptr);
788 EXPECT_EQ(treeRoot->get_key(), 2); // child1's value
789
790 // Should have grandchild as child (and root as parent via traversal)
791 auto* firstChild = treeRoot->get_left_child();
792 ASSERT_NE(firstChild, nullptr);
793
795}
796
797// Test converter that transforms data
799{
800 struct DoubleConvert {
802 void operator()(GNode* gnode, Tree_Node<int>* tnode) {
803 tnode->get_key() = gnode->get_info() * 2; // Double the value
804 }
805 };
806
809
810 EXPECT_EQ(treeRoot->get_key(), 2); // 1 * 2 = 2
811
813}
814
815// Test with string keys in tree
817{
818 struct StringConvert {
820 void operator()(GNode* gnode, Tree_Node<string>* tnode) {
821 tnode->get_key() = "Node_" + to_string(gnode->get_info());
822 }
823 };
824
827
828 EXPECT_EQ(treeRoot->get_key(), "Node_1");
829
831}
832
833// ============================================================================
834// Custom Filter Tests
835// ============================================================================
836
837// Custom arc filter that only shows arcs with weight > threshold
838template <typename GT>
840{
841 int threshold = 15;
842
843 bool operator()(typename GT::Arc* a) const {
844 return a->get_info() > threshold;
845 }
846};
847
849{
850 // Our graph has arcs with weights 10, 20, 30
851 // Filter should only show arcs > 15 (so 20 and 30)
852
853 struct NodeAttr {
854 void operator()(const Graph&, Node* n, ostream& out) {
855 out << "label=\"" << n->get_info() << "\"";
856 }
857 };
858 struct ArcAttr {
859 void operator()(const Graph&, Arc* a, ostream& out) {
860 out << "label=\"" << a->get_info() << "\"";
861 }
862 };
863
864 ostringstream out;
866 (g, out, NodeAttr(), ArcAttr(), "LR");
867 string result = out.str();
868
869 // Should contain arcs with weight 20 and 30
870 EXPECT_NE(result.find("20"), string::npos);
871 EXPECT_NE(result.find("30"), string::npos);
872 // Should NOT contain arc with weight 10
873 EXPECT_EQ(result.find("\"10\""), string::npos);
874}
875
876// ============================================================================
877// Stress Tests
878// ============================================================================
879
881{
883 LGraph g;
884
885 const int NUM_NODES = 100;
887
888 // Create nodes
889 for (int i = 0; i < NUM_NODES; ++i) {
890 nodes.push_back(g.insert_node(i));
891 }
892
893 // Create a path
894 for (int i = 0; i < NUM_NODES - 1; ++i) {
895 g.insert_arc(nodes[i], nodes[i + 1], i);
896 }
897
898 ostringstream out;
900 string result = out.str();
901
902 // Should produce valid output
903 EXPECT_NE(result.find("graph {"), string::npos);
904 EXPECT_NE(result.find("}"), string::npos);
905
906 // Should contain first and last nodes
907 EXPECT_NE(result.find("\"0\""), string::npos);
908 EXPECT_NE(result.find("\"99\""), string::npos);
909}
910
912{
914 LGraph tree;
915
916 const int NUM_NODES = 100;
918
919 // Create nodes
920 for (int i = 0; i < NUM_NODES; ++i) {
921 nodes.push_back(tree.insert_node(i));
922 }
923
924 // Create a binary-ish tree structure
925 for (int i = 1; i < NUM_NODES; ++i) {
926 int parent = (i - 1) / 2;
927 tree.insert_arc(nodes[parent], nodes[i], 0);
928 }
929
930 struct Conv {
931 void operator()(LGraph::Node* gn, Tree_Node<int>* tn) {
932 tn->get_key() = gn->get_info();
933 }
934 };
935
938
939 ASSERT_NE(treeRoot, nullptr);
940 EXPECT_EQ(treeRoot->get_key(), 0);
941
942 // Count all nodes
943 int count = 0;
945 if (node == nullptr) return;
946 count++;
947 for (auto* child = node->get_left_child(); child != nullptr;
948 child = child->get_right_sibling())
949 countNodes(child);
950 };
952
953 EXPECT_EQ(count, NUM_NODES);
954
956}
WeightedDigraph::Node Node
WeightedDigraph::Arc Arc
Generic directed graph (digraph) wrapper template.
Definition graph-dry.H:3854
typename BaseGraph::Arc Arc
Definition graph-dry.H:3858
typename BaseGraph::Node Node
Definition graph-dry.H:3857
Functor class to convert a tree graph to Tree_Node structure.
virtual Node * insert_node(Node *node) noexcept
Insertion of a node already allocated.
Definition tpl_graph.H:524
Graph_Arc< int > Arc
The node class type.
Definition tpl_graph.H:433
Arc * insert_arc(Node *src_node, Node *tgt_node, void *a)
Definition tpl_graph.H:604
Graph class implemented with singly-linked adjacency lists.
Definition tpl_sgraph.H:274
Arc * insert_arc(Node *src, Node *tgt, void *a)
Definition tpl_sgraph.H:497
virtual Node * insert_node(Node *p)
Insertion of a node whose memory has already been allocated.
Definition tpl_sgraph.H:487
Generic m-ary trees.
Tree_Node * get_left_child() const noexcept
Returns the leftmost child of this.
void insert_leftmost_child(Tree_Node *p) noexcept
Inserts p as the leftmost child of this.
T & get_key() noexcept
Returns a modifiable reference to the node contents.
ArcInfo & get_info() noexcept
Return a modifiable reference to the arc data.
Definition graph-dry.H:595
Simple digraph for testing.
Simple graph for testing.
Tree graph for graph_to_tree conversion testing.
#define TEST(name)
Grafo::Node GNode
Definition floyd.cc:48
Graph visualization and output generation utilities.
Spanning tree visualization with highlighted tree/non-tree edges.
Tree visualization and output generation.
__gmp_expr< T, __gmp_binary_expr< __gmp_expr< T, U >, unsigned long int, __gmp_root_function > > root(const __gmp_expr< T, U > &expr, unsigned long int l)
Definition gmpfrxx.h:4060
Convert spanning tree graphs to Tree_Node structures.
TEST_F(SimpleGraphTest, GenerateGraphvizContainsGraphKeyword)
DynArray< Graph::Node * > nodes
Definition graphpic.C:406
size_t rank_graphviz(const GT &g, std::ostream &out, Node_Attr node_attr=Node_Attr(), Arc_Attr arc_attr=Arc_Attr(), const std::string &rankdir="LR")
Generate Graphviz DOT output with topological ranking.
#define ARC_COOKIE(p)
Return the arc cookie
#define NODE_COOKIE(p)
Return the node cookie
void generate_forest(Node *root, std::ostream &out)
Generate a forest specification for the ntreepic drawing tool.
void generate_tree(Node *root, std::ostream &out, const int &tree_number=0)
Generate a tree specification for the ntreepic drawing tool.
void destroy_tree(Node *root)
Destroys (frees memory) the tree whose root is root.
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
Divide_Conquer_DP_Result< Cost > divide_and_conquer_partition_dp(const size_t groups, const size_t n, Transition_Cost_Fn transition_cost, const Cost inf=dp_optimization_detail::default_inf< Cost >())
Optimize partition DP using divide-and-conquer optimization.
std::string to_string(const time_t t, const std::string &format)
Format a time_t value into a string using format.
Definition ah-date.H:140
Itor::difference_type count(const Itor &beg, const Itor &end, const T &value)
Count elements equal to a value.
Definition ahAlgo.H:127
STL namespace.
#define WIDTH(p)
Definition ntreepic.C:362
Default filter for filtered iterators on arcs.
Definition tpl_graph.H:1000
Default filter for the graph nodes.
Definition tpl_graph.H:1192
Arc of graph implemented with double-linked adjacency lists.
Definition tpl_graph.H:222
Functor class for generating Graphviz DOT specifications.
Converter from Graph::Node to int for Tree_Node.
List_Graph< Graph_Node< int >, Graph_Arc< int > >::Node GraphNode
void operator()(GraphNode *gnode, Tree_Node< int > *tnode)
Shading functor for spanning tree arcs.
Shading functor for spanning tree nodes.
bool operator()(typename GT::Arc *a) const
Functor to convert tree node to string for output.
string operator()(Tree_Node< int > *p)
Dnode< int > Test
Definition testDnode.C:37
Generic graph and digraph implementations.
Utility algorithms and operations for graphs.
General tree (n-ary tree) node.