// Copyright (C) 2007 Davis E. King (davis@dlib.net) // License: Boost Software License See LICENSE.txt for the full license. #include <sstream> #include <string> #include <cstdlib> #include <ctime> #include <dlib/graph.h> #include <dlib/graph_utils.h> #include <dlib/set.h> #include "tester.h" // This is called an unnamed-namespace and it has the effect of making everything inside this file "private" // so that everything you declare will have static linkage. Thus we won't have any multiply // defined symbol errors coming out of the linker when we try to compile the test suite. namespace { using namespace test; using namespace dlib; using namespace std; // Declare the logger we will use in this test. The name of the tester // should start with "test." logger dlog("test.graph"); template < typename graph > void graph_test ( ) /*! requires - graph is an implementation of graph/graph_kernel_abstract.h is instantiated with int ensures - runs tests on graph for compliance with the specs !*/ { print_spinner(); COMPILE_TIME_ASSERT(is_graph<graph>::value); graph a, b; dlib::set<unsigned long>::compare_1b_c s; DLIB_TEST(graph_contains_length_one_cycle(a) == false); DLIB_TEST(graph_contains_undirected_cycle(a) == false); DLIB_TEST(a.number_of_nodes() == 0); a.set_number_of_nodes(5); DLIB_TEST(graph_is_connected(a) == false); DLIB_TEST(graph_contains_undirected_cycle(a) == false); DLIB_TEST(a.number_of_nodes() == 5); DLIB_TEST(graph_contains_length_one_cycle(a) == false); for (int i = 0; i < 5; ++i) { a.node(i).data = i; DLIB_TEST(a.node(i).index() == (unsigned int)i); } a.remove_node(1); DLIB_TEST(a.number_of_nodes() == 4); // make sure that only the number with data == 1 was removed int count = 0; for (int i = 0; i < 4; ++i) { count += a.node(i).data; DLIB_TEST(a.node(i).number_of_neighbors() == 0); DLIB_TEST(a.node(i).index() == (unsigned int)i); } DLIB_TEST(count == 9); a.add_edge(1,1); DLIB_TEST(graph_contains_length_one_cycle(a) == true); DLIB_TEST(graph_contains_undirected_cycle(a) == true); DLIB_TEST(a.has_edge(1,1)); DLIB_TEST(a.node(1).number_of_neighbors() == 1); a.add_edge(1,3); DLIB_TEST(a.node(1).number_of_neighbors() == 2); DLIB_TEST(a.node(2).number_of_neighbors() == 0); DLIB_TEST(a.node(3).number_of_neighbors() == 1); DLIB_TEST(a.has_edge(1,1)); DLIB_TEST(a.has_edge(1,3)); DLIB_TEST(a.has_edge(3,1)); DLIB_TEST(graph_contains_undirected_cycle(a) == true); a.remove_edge(1,1); DLIB_TEST(graph_contains_length_one_cycle(a) == false); DLIB_TEST(graph_contains_undirected_cycle(a) == false); DLIB_TEST(a.node(1).number_of_neighbors() == 1); DLIB_TEST(a.node(2).number_of_neighbors() == 0); DLIB_TEST(a.node(3).number_of_neighbors() == 1); DLIB_TEST(a.has_edge(1,1) == false); DLIB_TEST(a.has_edge(1,3)); DLIB_TEST(a.has_edge(3,1)); DLIB_TEST(graph_contains_undirected_cycle(a) == false); swap(a,b); DLIB_TEST(graph_contains_undirected_cycle(b) == false); DLIB_TEST(b.node(1).number_of_neighbors() == 1); DLIB_TEST(b.node(2).number_of_neighbors() == 0); DLIB_TEST(b.node(3).number_of_neighbors() == 1); DLIB_TEST(b.has_edge(1,1) == false); DLIB_TEST(b.has_edge(1,3)); DLIB_TEST(b.has_edge(3,1)); DLIB_TEST(graph_contains_undirected_cycle(b) == false); DLIB_TEST(a.number_of_nodes() == 0); DLIB_TEST(b.number_of_nodes() == 4); copy_graph_structure(b,b); DLIB_TEST(b.number_of_nodes() == 4); b.add_edge(1,2); DLIB_TEST(graph_contains_undirected_cycle(b) == false); DLIB_TEST(graph_contains_undirected_cycle(b) == false); b.add_edge(3,2); DLIB_TEST(graph_contains_undirected_cycle(b) == true); b.add_edge(1,1); DLIB_TEST(graph_is_connected(b) == false); b.add_edge(0,2); DLIB_TEST(graph_is_connected(b) == true); DLIB_TEST(graph_contains_undirected_cycle(b) == true); DLIB_TEST(a.number_of_nodes() == 0); for (unsigned long i = 0; i < b.number_of_nodes(); ++i) { for (unsigned long j = 0; j < b.node(i).number_of_neighbors(); ++j) { b.node(i).edge(j) = 'c'; } } b.node(1).edge(0) = 'a'; const unsigned long e1 = b.node(1).neighbor(0).index(); b.node(0).edge(0) = 'n'; const unsigned long e2 = b.node(0).neighbor(0).index(); ostringstream sout; serialize(b, sout); istringstream sin(sout.str()); DLIB_TEST(graph_contains_undirected_cycle(a) == false); a.set_number_of_nodes(10); deserialize(a, sin); DLIB_TEST(graph_contains_undirected_cycle(a) == true); for (unsigned long i = 0; i < a.number_of_nodes(); ++i) { for (unsigned long j = 0; j < a.node(i).number_of_neighbors(); ++j) { if ((i == 0 && a.node(i).neighbor(j).index() == e2) || (i == e2 && a.node(i).neighbor(j).index() == 0) ) { DLIB_TEST(a.node(i).edge(j) == 'n'); } else if ((i == 1 && a.node(i).neighbor(j).index() == e1) || (i == e1 && a.node(i).neighbor(j).index() == 1)) { DLIB_TEST(a.node(i).edge(j) == 'a'); } else { DLIB_TEST(i != 0 || a.node(i).neighbor(j).index() != e2); DLIB_TEST_MSG(a.node(i).edge(j) == 'c',a.node(i).edge(j)); } } } DLIB_TEST(a.number_of_nodes() == 4); DLIB_TEST(a.has_edge(1,2) == true); DLIB_TEST(a.has_edge(3,2) == true); DLIB_TEST(a.has_edge(1,1) == true); DLIB_TEST(a.has_edge(0,2) == true); DLIB_TEST(a.has_edge(1,3) == true); DLIB_TEST(a.has_edge(0,1) == false); DLIB_TEST(a.has_edge(0,3) == false); DLIB_TEST(a.has_edge(0,0) == false); DLIB_TEST(a.has_edge(1,0) == false); DLIB_TEST(a.has_edge(3,0) == false); for (unsigned long i = 0; i < a.number_of_nodes(); ++i) { a.node(i).data = static_cast<int>(i); } a.remove_node(2); DLIB_TEST(a.number_of_nodes() == 3); DLIB_TEST(graph_contains_undirected_cycle(a) == true); count = 0; for (unsigned long i = 0; i < a.number_of_nodes(); ++i) { if (a.node(i).data == 0) { DLIB_TEST(a.node(i).number_of_neighbors() == 0); } else if (a.node(i).data == 1) { DLIB_TEST(a.node(i).number_of_neighbors() == 2); } else if (a.node(i).data == 3) { DLIB_TEST(a.node(i).number_of_neighbors() == 1); } else { DLIB_TEST_MSG(false,"this is impossible"); } for (unsigned long j = 0; j < a.number_of_nodes(); ++j) { if ((a.node(i).data == 1 && a.node(j).data == 1) || (a.node(i).data == 1 && a.node(j).data == 3) || (a.node(i).data == 3 && a.node(j).data == 1)) { DLIB_TEST(a.has_edge(i,j) == true); ++count; } else { DLIB_TEST(a.has_edge(i,j) == false); } } } DLIB_TEST_MSG(count == 3,count); DLIB_TEST(graph_contains_undirected_cycle(a) == true); a.remove_edge(1,1); DLIB_TEST(graph_contains_undirected_cycle(a) == false); DLIB_TEST(b.number_of_nodes() == 4); b.clear(); DLIB_TEST(b.number_of_nodes() == 0); a.clear(); /* 1 7 | / \ 2 6 0 \ / | 3 / / \ / 4 5 */ a.set_number_of_nodes(8); a.add_edge(1,2); a.add_edge(2,3); a.add_edge(3,4); a.add_edge(3,5); a.add_edge(3,6); a.add_edge(6,7); a.add_edge(7,0); a.add_edge(0,5); DLIB_TEST(graph_is_connected(a)); dlib::set<dlib::set<unsigned long>::compare_1b_c>::kernel_1b_c sos; dlib::graph<dlib::set<unsigned long>::compare_1b_c, dlib::set<unsigned long>::compare_1b_c>::kernel_1a_c join_tree; unsigned long temp; triangulate_graph_and_find_cliques(a,sos); DLIB_TEST(a.number_of_nodes() == 8); create_join_tree(a, join_tree); DLIB_TEST(join_tree.number_of_nodes() == 6); DLIB_TEST(graph_is_connected(join_tree) == true); DLIB_TEST(graph_contains_undirected_cycle(join_tree) == false); DLIB_TEST(is_join_tree(a, join_tree)); // check old edges DLIB_TEST(a.has_edge(1,2)); DLIB_TEST(a.has_edge(2,3)); DLIB_TEST(a.has_edge(3,4)); DLIB_TEST(a.has_edge(3,5)); DLIB_TEST(a.has_edge(3,6)); DLIB_TEST(a.has_edge(6,7)); DLIB_TEST(a.has_edge(7,0)); DLIB_TEST(a.has_edge(0,5)); DLIB_TEST(graph_is_connected(a)); DLIB_TEST(sos.size() == 6); temp = 1; s.add(temp); temp = 2; s.add(temp); DLIB_TEST(sos.is_member(s)); s.clear(); temp = 2; s.add(temp); temp = 3; s.add(temp); DLIB_TEST(sos.is_member(s)); s.clear(); temp = 4; s.add(temp); temp = 3; s.add(temp); DLIB_TEST(sos.is_member(s)); sos.reset(); while (sos.move_next()) { DLIB_TEST(is_clique(a, sos.element())); DLIB_TEST(is_maximal_clique(a, sos.element())); } } void test_copy() { { graph<int,int>::kernel_1a_c a,b; a.set_number_of_nodes(3); a.node(0).data = 1; a.node(1).data = 2; a.node(2).data = 3; a.add_edge(0,1); a.add_edge(0,2); edge(a,0,1) = 4; edge(a,0,2) = 5; a.add_edge(0,0); edge(a,0,0) = 9; copy_graph(a, b); DLIB_TEST(b.number_of_nodes() == 3); DLIB_TEST(b.node(0).data == 1); DLIB_TEST(b.node(1).data == 2); DLIB_TEST(b.node(2).data == 3); DLIB_TEST(edge(b,0,1) == 4); DLIB_TEST(edge(b,0,2) == 5); DLIB_TEST(edge(b,0,0) == 9); } { graph<int,int>::kernel_1a_c a,b; a.set_number_of_nodes(4); a.node(0).data = 1; a.node(1).data = 2; a.node(2).data = 3; a.node(3).data = 8; a.add_edge(0,1); a.add_edge(0,2); a.add_edge(2,3); edge(a,0,1) = 4; edge(a,0,2) = 5; edge(a,2,3) = 6; copy_graph(a, b); DLIB_TEST(b.number_of_nodes() == 4); DLIB_TEST(b.node(0).data == 1); DLIB_TEST(b.node(1).data == 2); DLIB_TEST(b.node(2).data == 3); DLIB_TEST(b.node(3).data == 8); DLIB_TEST(edge(b,0,1) == 4); DLIB_TEST(edge(b,0,2) == 5); DLIB_TEST(edge(b,2,3) == 6); } } class graph_tester : public tester { /*! WHAT THIS OBJECT REPRESENTS This object represents a test for the graph object. When it is constructed it adds itself into the testing framework. The command line switch is specified as test_directed_graph by passing that string to the tester constructor. !*/ public: graph_tester ( ) : tester ("test_graph", "Runs tests on the graph component.") {} void perform_test ( ) { dlog << LINFO << "testing kernel_1a_c"; graph_test<graph<int>::kernel_1a_c>(); dlog << LINFO << "testing kernel_1a"; graph_test<graph<int>::kernel_1a>(); test_copy(); } } a; }