diff --git a/CMakeLists.txt b/CMakeLists.txt index 5a6fd5671afe41e195fb074c0ec31046445c01f4..aa173322eca5f46bf10e4c90da678660977aa48c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ cmake_minimum_required(VERSION 3.1.3) # Project settings # ============================================================================ project(hglib - VERSION 1.2.1) + VERSION 1.3.0) # ============================================================================ diff --git a/include/hglib/hypergraph/Hypergraph.h b/include/hglib/hypergraph/Hypergraph.h index c25ddd87c8b62216a07d36b3f5a15a5a0ccdb5ea..63f29d3753ec51a1d29c02c4ae26921f1d4931d4 100644 --- a/include/hglib/hypergraph/Hypergraph.h +++ b/include/hglib/hypergraph/Hypergraph.h @@ -58,6 +58,10 @@ template <template <typename> typename VertexTemplate_t, typename HypergraphProperty = emptyProperty> class Hypergraph : virtual public HypergraphInterface<VertexTemplate_t, Hyperedge_t, VertexProperty, EdgeProperty, HypergraphProperty> { + // Declare any instantiation of this template class as friend + template <template <typename> class, typename, typename, typename, typename> + friend class Hypergraph; + public: /// Alias for the vertex type using Vertex_t = typename HypergraphInterface<VertexTemplate_t, Hyperedge_t, @@ -97,6 +101,13 @@ class Hypergraph : virtual public HypergraphInterface<VertexTemplate_t, explicit Hypergraph(bool allowMultiHyperedges = true); /// Hypergraph copy constructor explicit Hypergraph(const Hypergraph& rhs); + /// Conversion constructor + template <typename OtherVP, typename OtherEP, typename OtherGP> + explicit Hypergraph(const Hypergraph<VertexTemplate_t, + Hyperedge_t, + OtherVP, + OtherEP, + OtherGP>& other); /// Hypergraph destructor virtual ~Hypergraph(); diff --git a/include/hglib/hypergraph/Hypergraph.hpp b/include/hglib/hypergraph/Hypergraph.hpp index 4f9abbdc9ff590fa982ccaa9e403e050f20399b2..e38c56c24d5cdd34db91798d2da57aeb5f8b63b2 100644 --- a/include/hglib/hypergraph/Hypergraph.hpp +++ b/include/hglib/hypergraph/Hypergraph.hpp @@ -137,6 +137,72 @@ Hypergraph(const Hypergraph& other) : } } +/** + * \brief Conversion constructor + * + * \details + * Allows to convert a graph to another graph with the same Vertex and Edge + * types but another set of (Vertex-, Edge- and Graph-) Properties + * + * \param other Hypergraph to be copied + */ +template <template <typename> typename VertexTemplate_t, + typename Hyperedge_t, + typename VertexProperty, + typename EdgeProperty, + typename GraphProperty> +template <typename OtherVP, typename OtherEP, typename OtherGP> +Hypergraph<VertexTemplate_t, + Hyperedge_t, + VertexProperty, + EdgeProperty, + GraphProperty>:: +Hypergraph(const Hypergraph<VertexTemplate_t, + Hyperedge_t, + OtherVP, + OtherEP, + OtherGP>& other) : + vertexMaxId_(-1), + allowMultiHyperedges_(other.allowMultiHyperedges_) { + try { + // create vertices_ + vertices_ = create_filtered_vector<Vertex_t*>( + create_filtered_vector<Vertex_t*>(), + std::make_unique<NullptrFilter<Vertex_t*>>()); + // create vertexProperties_ + vertexProperties_ = create_filtered_vector<VertexProperty*>( + create_filtered_vector<VertexProperty*>(), + std::make_unique<NullptrFilter<VertexProperty*>>()); + + // copy vertices and initialize associated vertex properties + for (std::size_t i = 0; i < other.vertices_->size(); ++i) { + Vertex_t* vertex = other.vertices_->operator[](i); + ++vertexMaxId_; + // if vertex was deleted in directedHypergraph + if (vertex == nullptr) { + vertices_->push_back(nullptr); + vertexProperties_->push_back(nullptr); + } else { + // Copy vertex + vertices_->push_back(new Vertex_t(*vertex)); + // Init vertex property + vertexProperties_->push_back(new VertexProperty()); + } + } + // Init graph properties + hypergraphProperty_ = new GraphProperty(); + } catch (std::bad_alloc &e) { + for (auto& vertex : *(vertices_.get())) { + delete vertex; + } + for (auto& vertexProperty : *(vertexProperties_.get())) { + delete vertexProperty; + } + delete hypergraphProperty_; + throw; + } +} + /** * \brief Hypergraph destructor diff --git a/include/hglib/hypergraph/directed/DirectedHypergraph.h b/include/hglib/hypergraph/directed/DirectedHypergraph.h index 6c5a03496db109c1da8d92e606b9e4613e2796bd..88c1c4cf9b812987e4728b49de2d3c0025f269e2 100644 --- a/include/hglib/hypergraph/directed/DirectedHypergraph.h +++ b/include/hglib/hypergraph/directed/DirectedHypergraph.h @@ -127,6 +127,10 @@ class DirectedHypergraph : public DirectedHypergraphInterface< "derived from DirectedHyperedgeBase, e.g. use as second template " "parameter DirectedHyperedge or NamedDirectedHyperedge"); + // Declare any instantiation of this template class as friend + template <template <typename> class, typename, typename, typename, typename> + friend class DirectedHypergraph; + public: /// Alias for the directed vertex type @@ -189,6 +193,14 @@ class DirectedHypergraph : public DirectedHypergraphInterface< /// Copy constructor explicit DirectedHypergraph( const DirectedHypergraph& directedHypergraph); + /// Conversion constructor + template <typename OtherVP, typename OtherEP, typename OtherGP> + explicit DirectedHypergraph( + const DirectedHypergraph<VertexTemplate_t, + Hyperarc_t, + OtherVP, + OtherEP, + OtherGP>& other); /// DirectedHypergraph destructor virtual ~DirectedHypergraph(); diff --git a/include/hglib/hypergraph/directed/DirectedHypergraph.hpp b/include/hglib/hypergraph/directed/DirectedHypergraph.hpp index 2970228bc68533cf646f0d7088529aadd14d6bd9..b31c13e515441f62ce890c8e1436b4d72c4d0bd8 100644 --- a/include/hglib/hypergraph/directed/DirectedHypergraph.hpp +++ b/include/hglib/hypergraph/directed/DirectedHypergraph.hpp @@ -142,6 +142,92 @@ DirectedHypergraph(const DirectedHypergraph& other) : } } +/** + * \brief Conversion constructor + * + * \details + * Allows to convert a graph to another graph with the same Vertex and Edge + * types but another set of (Vertex-, Edge- and Graph-) Properties + * + * \param other Hypergraph to be copied + */ +template <template <typename> typename VertexTemplate_t, + typename Hyperarc_t, + typename VertexProperty, + typename HyperarcProperty, + typename HypergraphProperty> +template <typename OtherVP, typename OtherEP, typename OtherGP> +DirectedHypergraph< + VertexTemplate_t, + Hyperarc_t, + VertexProperty, + HyperarcProperty, + HypergraphProperty>:: +DirectedHypergraph(const DirectedHypergraph<VertexTemplate_t, + Hyperarc_t, + OtherVP, + OtherEP, + OtherGP>& other) : + DirectedHypergraphInterface<VertexTemplate_t, + Hyperarc_t, + VertexProperty, + HyperarcProperty, + HypergraphProperty>(), + Hypergraph<VertexTemplate_t, + Hyperarc_t, + VertexProperty, + HyperarcProperty, + HypergraphProperty>(other), + hyperarcMaxId_(-1) { + try { + // Initialise (empty) hyperarcs_ + hyperarcs_ = create_filtered_vector<Hyperarc_t*>( + create_filtered_vector<Hyperarc_t*>(), + std::make_unique<NullptrFilter<Hyperarc_t*>>()); + // Initialise (empty) hyperarcProperties_ + hyperarcProperties_ = + create_filtered_vector<HyperarcProperty_type*>( + create_filtered_vector<HyperarcProperty_type*>(), + std::make_unique<NullptrFilter<HyperarcProperty_type*>>()); + // Copy hyperarcs and init associated properties + for (std::size_t i = 0; i < other.hyperarcs_->size(); ++i) { + Hyperarc_t* hyperarc = other.hyperarcs_->operator[](i); + ++hyperarcMaxId_; + // if hyperarc was deleted in other + if (hyperarc == nullptr) { + hyperarcs_->push_back(nullptr); + hyperarcProperties_->push_back(nullptr); + } else { + Hyperarc_t* copy = new Hyperarc_t(*hyperarc); + // Add tails to hyperarc + for (const auto& vertex : *(hyperarc->tails_.get())) { + auto& tail = this->vertices_->operator[](vertex->id()); + copy->addTailVertex(tail); + tail->addOutHyperarc(copy); + } + // Add heads to hyperarc + for (const auto& vertex : *(hyperarc->heads_.get())) { + auto& head = this->vertices_->operator[](vertex->id()); + copy->addHeadVertex(head); + head->addInHyperarc(copy); + } + // Add hyperarc to hypergraph + hyperarcs_->push_back(copy); + // Init hyperarc property + hyperarcProperties_->push_back(new HyperarcProperty()); + } + } + } catch (std::bad_alloc& e) { + for (auto& hyperarc : *(hyperarcs_.get())) { + delete hyperarc; + } + for (auto& hyperarcProperty : *(hyperarcProperties_.get())) { + delete hyperarcProperty; + } + throw; + } +} + /** * \brief DirectedHypergraph destructor * diff --git a/include/hglib/hypergraph/undirected/UndirectedHypergraph.h b/include/hglib/hypergraph/undirected/UndirectedHypergraph.h index 08fd7fb8719cdd597711049c9052ce59f1aab949..7b2f2cb03e53eac16009c2b6299a1bea2912393a 100644 --- a/include/hglib/hypergraph/undirected/UndirectedHypergraph.h +++ b/include/hglib/hypergraph/undirected/UndirectedHypergraph.h @@ -125,6 +125,10 @@ class UndirectedHypergraph : public UndirectedHypergraphInterface< "from HyperedgeBase, e.g. use as second template parameter " "UndirectedHyperedge or NamedUndirectedHyperedge"); + // Declare any instantiation of this template class as friend + template <template <typename> class, typename, typename, typename, typename> + friend class UndirectedHypergraph; + public: /// Alias for the directed vertex type using Vertex_t = typename UndirectedHypergraphInterface<VertexTemplate_t, @@ -192,6 +196,14 @@ class UndirectedHypergraph : public UndirectedHypergraphInterface< /// UndirectedHypergraph copy constructor explicit UndirectedHypergraph( const UndirectedHypergraph& undirectedHypergraph); + /// Conversion constructor + template <typename OtherVP, typename OtherEP, typename OtherGP> + explicit UndirectedHypergraph( + const UndirectedHypergraph<VertexTemplate_t, + Hyperedge_t, + OtherVP, + OtherEP, + OtherGP>& other); /// Copy assignment operator UndirectedHypergraph& operator=( const UndirectedHypergraph& source); diff --git a/include/hglib/hypergraph/undirected/UndirectedHypergraph.hpp b/include/hglib/hypergraph/undirected/UndirectedHypergraph.hpp index be68d1ca05ec16ce2e884e49ecfafb107e88b281..0622defd19fd29e604af39e9bea1423a238bb1f9 100644 --- a/include/hglib/hypergraph/undirected/UndirectedHypergraph.hpp +++ b/include/hglib/hypergraph/undirected/UndirectedHypergraph.hpp @@ -131,6 +131,84 @@ UndirectedHypergraph( } } +/** + * \brief Conversion constructor + * + * \details + * Allows to convert a graph to another graph with the same Vertex and Edge + * types but another set of (Vertex-, Edge- and Graph-) Properties + * + * \param other Hypergraph to be copied + */ +template <template <typename> typename VertexTemplate_t, + typename Hyperedge_t, + typename VertexProperty, + typename HyperedgeProperty, + typename HypergraphProperty> +template <typename OtherVP, typename OtherEP, typename OtherGP> +UndirectedHypergraph<VertexTemplate_t, + Hyperedge_t, + VertexProperty, + HyperedgeProperty, + HypergraphProperty>:: +UndirectedHypergraph(const UndirectedHypergraph<VertexTemplate_t, + Hyperedge_t, + OtherVP, + OtherEP, + OtherGP>& other) : + UndirectedHypergraphInterface<VertexTemplate_t, + Hyperedge_t, + VertexProperty, + HyperedgeProperty, + HypergraphProperty>(), + Hypergraph<VertexTemplate_t, + Hyperedge_t, + VertexProperty, + HyperedgeProperty, + HypergraphProperty>(other), + hyperedgeMaxId_(-1) { + try { + // Initialise (empty) hyperedges_ + hyperedges_ = create_filtered_vector<Hyperedge_t*>( + create_filtered_vector<Hyperedge_t*>(), + std::make_unique<NullptrFilter<Hyperedge_t*>>()); + // Initialise (empty) hyperedgeProperties_ + hyperedgeProperties_ = create_filtered_vector<HyperedgeProperty*>( + create_filtered_vector<HyperedgeProperty*>(), + std::make_unique<NullptrFilter<HyperedgeProperty*>>()); + // Copy edges and init associated edge properties + for (std::size_t i = 0; i < other.hyperedges_->size(); ++i) { + Hyperedge_t* hyperedge = other.hyperedges_->operator[](i); + ++hyperedgeMaxId_; + // if hyperedge was deleted in other + if (hyperedge == nullptr) { + hyperedges_->push_back(nullptr); + hyperedgeProperties_->push_back(nullptr); + } else { + Hyperedge_t* copy = new Hyperedge_t(*hyperedge); + // Add vertices to hyperedge + for (const auto& vertex : *(hyperedge->vertices_.get())) { + auto& v = this->vertices_->operator[](vertex->id()); + copy->addVertex(v); + v->addHyperedge(copy); + } + // Add hyperedge to hypergraph + hyperedges_->push_back(copy); + // Init hyperedge property + hyperedgeProperties_->push_back(new HyperedgeProperty()); + } + } + } catch (std::bad_alloc& e) { + for (auto& hyperedge : *(hyperedges_.get())) { + delete hyperedge; + } + for (auto& hyperedgeProperty : *(hyperedgeProperties_.get())) { + delete hyperedgeProperty; + } + throw; + } +} + /** * \brief Copy assignment operator diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 634ab7f7cffc175dd6eac683fb3df5ece3a180ab..858acf75a2b94120986285e5f130e8099698a65c 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -37,6 +37,7 @@ set(UNIT_TESTS_WITH_VALGRIND # Run these with valgrind (if available) testUndirectedHypergraph testUndirectedSubHypergraph testAlgorithmsDHG + testGraphConversion ) set(UNIT_TESTS_WITHOUT_VALGRIND # Always run these without valgrind ) @@ -97,11 +98,6 @@ endforeach (UNIT_TEST) # ============================================================================ find_package(Cpplint) # sets CPPLINT_FOUND and CPPLINT_PROGRAM if (CPPLINT_FOUND) - # List unit test source files - foreach (UNIT_TEST IN LISTS UNIT_TESTS) - list(APPEND UNIT_TEST_SOURCE_FILES ${hglib_SOURCE_DIR}/test/${UNIT_TEST}.cpp) - endforeach (UNIT_TEST) - add_custom_target(run_cpplint COMMAND cpplint --extensions=h,hpp,cpp # filter runtime/explicit: requires explicit one argument constructor diff --git a/test/testGraphConversion.cpp b/test/testGraphConversion.cpp new file mode 100644 index 0000000000000000000000000000000000000000..514da2429091de6cbcc3a28259890b6bd16819ff --- /dev/null +++ b/test/testGraphConversion.cpp @@ -0,0 +1,301 @@ +// **************************************************************************** +// +// hglib - HyperGraph Library +// +// **************************************************************************** +// +// Copyright (C) 2018 Arnaud Mary, David Parsons, Martin Wannagat +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +// **************************************************************************** + +#include "hglib.h" + +#include <string> +#include <utility> +#include <vector> + +#include "gtest/gtest.h" + +using std::pair; +using std::string; +using std::vector; +using std::cout; +using std::endl; +using hglib::DirectedVertex; +using hglib::UndirectedVertex; +using hglib::DirectedHyperedge; +using hglib::UndirectedHyperedge; +using hglib::NamedDirectedHyperedge; +using hglib::NamedUndirectedHyperedge; + +class TestGraphConversion : public testing::Test { + protected: + virtual void SetUp() { + } + + virtual void TearDown() { + } + + vector<string> vertices {"A", "B", "Foo", "Bar", "", "6"}; + vector<pair<vector<string>, string>> hyperedges { + {{"A", "Foo"}, "edge1"}, + {{"A", "B", "Bar", ""}, "edge2"}, + {{"6", ""}, "edge3"} + }; + vector<pair<pair<vector<string>, vector<string>>, string>> hyperarcs { + {{{"A"}, {"Foo"}}, "edge1"}, + {{{"A", "B"}, {"Bar", ""}}, "edge2"}, + {{{"6"}, {""}}, "edge3"} + }; +}; + +// Create ad-hoc properties +enum class Colour {red, blue, green}; +struct VertexProperty { + Colour colour = Colour::green; +}; +struct EdgeProperty { + double weight = 0.0; +}; +struct GraphProperty { + std::string label; +}; + +TEST_F(TestGraphConversion, Test_DhgConversion) { + // Create a graph with no properties + hglib::DirectedHypergraph <DirectedVertex, + DirectedHyperedge> g1; + for (auto vertexName : vertices) { + g1.addVertex(vertexName); + } + for (auto hyperarc : hyperarcs) { + g1.addHyperarc(hglib::NAME, hyperarc.first); + } + + // Copy graph into graph with properties + hglib::DirectedHypergraph <DirectedVertex, + DirectedHyperedge, + VertexProperty, + EdgeProperty, + GraphProperty> g2(g1); + + // Copy back into graph with empty properties + hglib::DirectedHypergraph <DirectedVertex, + DirectedHyperedge, + hglib::emptyProperty, + hglib::emptyProperty, + hglib::emptyProperty> g3(g2); + + // Check nb of vertices and hyperarcs + ASSERT_EQ(g1.nbVertices(), g2.nbVertices()); + ASSERT_EQ(g1.nbVertices(), g3.nbVertices()); + ASSERT_EQ(g1.nbHyperarcs(), g2.nbHyperarcs()); + ASSERT_EQ(g1.nbHyperarcs(), g3.nbHyperarcs()); + // Check vertices + auto vertexIt = g1.verticesBegin(); + auto vertexEnd = g1.verticesEnd(); + for (; vertexIt != vertexEnd; ++vertexIt) { + const auto& g1Vertex = *vertexIt; + const auto& g2Vertex = g2.vertexByName(g1Vertex->name()); + const auto& g3Vertex = g3.vertexByName(g1Vertex->name()); + ASSERT_NE(g2Vertex, nullptr); + ASSERT_NE(g3Vertex, nullptr); + ASSERT_EQ(g1Vertex->id(), g2Vertex->id()); + ASSERT_EQ(g1Vertex->id(), g3Vertex->id()); + const auto& vertexId = g1Vertex->id(); + // compare in-hyperarcs + ASSERT_EQ(g1.inDegree(vertexId), g2.inDegree(vertexId)); + ASSERT_EQ(g1.inDegree(vertexId), g3.inDegree(vertexId)); + auto hyperarcIt = g1.inHyperarcsBegin(vertexId); + auto hyperarcEnd = g1.inHyperarcsEnd(vertexId); + auto g2HyperarcIt = g2.inHyperarcsBegin(vertexId); + auto g3HyperarcIt = g3.inHyperarcsBegin(vertexId); + for (; hyperarcIt != hyperarcEnd; ++hyperarcIt) { + const auto& hyperarc = *hyperarcIt; + ASSERT_EQ(hyperarc->id(), (*g2HyperarcIt)->id()); + ASSERT_EQ(hyperarc->id(), (*g3HyperarcIt)->id()); + ++g2HyperarcIt; + ++g3HyperarcIt; + } + // compare out-edges + ASSERT_EQ(g1.outDegree(vertexId), g2.outDegree(vertexId)); + ASSERT_EQ(g1.outDegree(vertexId), g3.outDegree(vertexId)); + hyperarcIt = g1.outHyperarcsBegin(vertexId); + hyperarcEnd = g1.outHyperarcsEnd(vertexId); + g2HyperarcIt = g2.outHyperarcsBegin(vertexId); + g3HyperarcIt = g3.outHyperarcsBegin(vertexId); + for (; hyperarcIt != hyperarcEnd; ++hyperarcIt) { + const auto& hyperarc = *hyperarcIt; + ASSERT_EQ(hyperarc->id(), (*g2HyperarcIt)->id()); + ASSERT_EQ(hyperarc->id(), (*g3HyperarcIt)->id()); + ++g2HyperarcIt; + ++g3HyperarcIt; + } + } + + // Check hyperarcs + auto itHyperarcs = g1.hyperarcsBegin(); + auto itHyperarcsEnd = g1.hyperarcsEnd(); + for (; itHyperarcs != itHyperarcsEnd; ++itHyperarcs) { + const auto& g1Hyperarc = *itHyperarcs; + const auto& g2Hyperarc = g2.hyperarcById(g1Hyperarc->id()); + const auto& g3Hyperarc = g3.hyperarcById(g1Hyperarc->id()); + ASSERT_NE(g2Hyperarc, nullptr); + ASSERT_NE(g3Hyperarc, nullptr); + ASSERT_EQ(g1Hyperarc->id(), g2Hyperarc->id()); + ASSERT_EQ(g1Hyperarc->id(), g3Hyperarc->id()); + const auto& g1HyperarcId = g1Hyperarc->id(); + // compare tails + ASSERT_EQ(g1.nbTailVertices(g1HyperarcId), g2.nbTailVertices(g1HyperarcId)); + ASSERT_EQ(g1.nbTailVertices(g1HyperarcId), g3.nbTailVertices(g1HyperarcId)); + auto g2vertexIt = g2.tailsBegin(g1HyperarcId); + auto g3vertexIt = g3.tailsBegin(g1HyperarcId); + auto it = g1.tailsBegin(g1HyperarcId); + auto end = g1.tailsEnd(g1HyperarcId); + for (; it != end; ++it) { + const auto& tail = *it; + ASSERT_EQ(tail->id(), (*g2vertexIt)->id()); + ASSERT_EQ(tail->id(), (*g3vertexIt)->id()); + ASSERT_EQ(tail->name().compare((*g2vertexIt)->name()), 0); + ASSERT_EQ(tail->name().compare((*g3vertexIt)->name()), 0); + ++g2vertexIt; + ++g3vertexIt; + } + // compare heads + ASSERT_EQ(g1.nbHeadVertices(g1HyperarcId), g2.nbHeadVertices(g1HyperarcId)); + ASSERT_EQ(g1.nbHeadVertices(g1HyperarcId), g3.nbHeadVertices(g1HyperarcId)); + g2vertexIt = g2.headsBegin(g1HyperarcId); + g3vertexIt = g3.headsBegin(g1HyperarcId); + + it = g1.headsBegin(g1HyperarcId); + end = g1.headsEnd(g1HyperarcId); + for (; it != end; ++it) { + const auto& head = *it; + ASSERT_EQ(head->id(), (*g2vertexIt)->id()); + ASSERT_EQ(head->id(), (*g3vertexIt)->id()); + ASSERT_EQ(head->name().compare((*g2vertexIt)->name()), 0); + ASSERT_EQ(head->name().compare((*g3vertexIt)->name()), 0); + ++g2vertexIt; + ++g3vertexIt; + } + } + + // Add a vertex and hyperarc to converted graphs to check their correct id + const auto& g2AddedVertex = g2.addVertex("Test"); + const auto& g3AddedVertex = g3.addVertex("Test"); + const auto& g2AddedHyperarc = g2.addHyperarc(hglib::NAME, {{"A"}, {"Test"}}); + const auto& g3AddedHyperarc = g3.addHyperarc(hglib::NAME, {{"A"}, {"Test"}}); + ASSERT_EQ(g2AddedVertex->id(), 6); + ASSERT_EQ(g3AddedVertex->id(), 6); + ASSERT_EQ(g2AddedHyperarc->id(), 3); + ASSERT_EQ(g3AddedHyperarc->id(), 3); +} + +TEST_F(TestGraphConversion, Test_UhgConversion) { + // Create a graph with no properties + hglib::UndirectedHypergraph<UndirectedVertex, + UndirectedHyperedge> g1; + for (auto vertexName : vertices) { + g1.addVertex(vertexName); + } + for (auto hyperedge : hyperedges) { + g1.addHyperedge(hglib::NAME, hyperedge.first); + } + + // Copy graph into graph with properties + hglib::UndirectedHypergraph<UndirectedVertex, + UndirectedHyperedge, + VertexProperty, + EdgeProperty, + GraphProperty> g2(g1); + + // Copy back into graph with empty properties + hglib::UndirectedHypergraph<UndirectedVertex, + UndirectedHyperedge, + hglib::emptyProperty, + hglib::emptyProperty, + hglib::emptyProperty> g3(g2); + // Check nb of vertices and hyperarcs + ASSERT_EQ(g1.nbVertices(), g2.nbVertices()); + ASSERT_EQ(g1.nbVertices(), g3.nbVertices()); + ASSERT_EQ(g1.nbHyperedges(), g2.nbHyperedges()); + ASSERT_EQ(g1.nbHyperedges(), g3.nbHyperedges()); + // Check vertices + for (const auto& vertex : g1.vertices()) { + const auto& g2Vertex = g2.vertexByName(vertex->name()); + const auto& g3Vertex = g3.vertexByName(vertex->name()); + ASSERT_NE(g2Vertex, nullptr); + ASSERT_NE(g3Vertex, nullptr); + ASSERT_EQ(vertex->id(), g2Vertex->id()); + ASSERT_EQ(vertex->id(), g3Vertex->id()); + const auto& vertexId = vertex->id(); + // Compare implied hyperedges + ASSERT_EQ(g1.nbHyperedges(vertexId), g2.nbHyperedges(vertexId)); + ASSERT_EQ(g1.nbHyperedges(vertexId), g3.nbHyperedges(vertexId)); + auto hyperedgeIt = g1.hyperedgesBegin(vertexId); + auto hyperedgeEnd = g1.hyperedgesEnd(vertexId); + auto g2HyperedgeIt = g2.hyperedgesBegin(vertexId); + auto g3HyperedgeIt = g3.hyperedgesBegin(vertexId); + for (; hyperedgeIt != hyperedgeEnd; ++hyperedgeIt) { + const auto& hyperedge = *hyperedgeIt; + ASSERT_EQ(hyperedge->id(), (*g2HyperedgeIt)->id()); + ASSERT_EQ(hyperedge->id(), (*g3HyperedgeIt)->id()); + ++g2HyperedgeIt; + ++g3HyperedgeIt; + } + } + + // Check hyperedges + for (const auto& hyperedge : g1.hyperedges()) { + const auto& g2Hyperarc = g2.hyperedgeById(hyperedge->id()); + const auto& g3Hyperarc = g3.hyperedgeById(hyperedge->id()); + ASSERT_NE(g2Hyperarc, nullptr); + ASSERT_NE(g3Hyperarc, nullptr); + ASSERT_EQ(hyperedge->id(), g2Hyperarc->id()); + ASSERT_EQ(hyperedge->id(), g3Hyperarc->id()); + const auto& hyperedgeId = hyperedge->id(); + // Compare impliedVertices + ASSERT_EQ(g1.nbImpliedVertices(hyperedgeId), + g2.nbImpliedVertices(hyperedgeId)); + ASSERT_EQ(g1.nbImpliedVertices(hyperedgeId), + g3.nbImpliedVertices(hyperedgeId)); + auto impliedVerticesIt = g1.impliedVerticesBegin(hyperedgeId); + auto impliedVerticesEnd = g1.impliedVerticesEnd(hyperedgeId); + auto g2VertexIt = g2.impliedVerticesBegin(hyperedgeId); + auto g3VertexIt = g3.impliedVerticesBegin(hyperedgeId); + for (; impliedVerticesIt != impliedVerticesEnd; ++impliedVerticesIt) { + const auto& v = *impliedVerticesIt; + ASSERT_EQ(v->id(), (*g2VertexIt)->id()); + ASSERT_EQ(v->id(), (*g3VertexIt)->id()); + ASSERT_EQ(v->name().compare((*g2VertexIt)->name()), 0); + ASSERT_EQ(v->name().compare((*g3VertexIt)->name()), 0); + ++g2VertexIt; + ++g3VertexIt; + } + } + + // Add a vertex and hyperedge to converted graphs to check their correct id + const auto& g2AddedVertex = g2.addVertex("Test"); + const auto& g3AddedVertex = g3.addVertex("Test"); + const auto& g2AddedHyperedge = g2.addHyperedge(hglib::NAME, + {"A", "Test"}); + const auto& g3AddedHyperedge = g3.addHyperedge(hglib::NAME, + {"A", "Test"}); + ASSERT_EQ(g2AddedVertex->id(), 6); + ASSERT_EQ(g3AddedVertex->id(), 6); + ASSERT_EQ(g3AddedHyperedge->id(), 3); + ASSERT_EQ(g2AddedHyperedge->id(), 3); +} \ No newline at end of file