Skip to content

Commit

Permalink
feat: utils header containing "get_transposed_graph" function (#136)
Browse files Browse the repository at this point in the history
  • Loading branch information
toadkarter authored Oct 8, 2023
1 parent ffaa4d1 commit ae3e232
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 3 deletions.
19 changes: 19 additions & 0 deletions include/graaflib/algorithm/utils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#pragma once

#include <graaflib/graph.h>

namespace graaf {

/**
* Get transposed version of a given directed graph
*
* @param graph The directed graph that is to be transposed
* @return directed_graph<VERTEX_T, EDGE_T> The transposed graph
*/
template <typename VERTEX_T, typename EDGE_T>
directed_graph<VERTEX_T, EDGE_T> get_transposed_graph(
const directed_graph<VERTEX_T, EDGE_T>& graph);

} // namespace graaf

#include "utils.tpp"
32 changes: 32 additions & 0 deletions include/graaflib/algorithm/utils.tpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#pragma once

namespace graaf {

template <typename VERTEX_T, typename EDGE_T>
directed_graph<VERTEX_T, EDGE_T> get_transposed_graph(
const directed_graph<VERTEX_T, EDGE_T>& graph) {
directed_graph<VERTEX_T, EDGE_T> transposed_graph{};
for (auto [edge_vertices, edge_type] : graph.get_edges()) {
const auto [vertex_id_lhs, vertex_id_rhs] = edge_vertices;

auto vertex_value_lhs = graph.get_vertex(vertex_id_lhs);
auto vertex_value_rhs = graph.get_vertex(vertex_id_rhs);

if (!transposed_graph.has_vertex(vertex_id_lhs)) {
auto vertex_id = transposed_graph.add_vertex(std::move(vertex_value_lhs),
vertex_id_lhs);
}

if (!transposed_graph.has_vertex(vertex_id_rhs)) {
auto vertex_id = transposed_graph.add_vertex(std::move(vertex_value_rhs),
vertex_id_rhs);
}

transposed_graph.add_edge(vertex_id_rhs, vertex_id_lhs,
std::move(edge_type));
}

return transposed_graph;
}

} // namespace graaf
11 changes: 10 additions & 1 deletion include/graaflib/graph.h
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,16 @@ class graph {
*/
[[nodiscard]] vertex_id_t add_vertex(auto&& vertex);

/**
* Add a vertex to the graph with a specific ID
*
* @param vertex The vertex to be added
* @param id The requested ID for the new vertex
* @return vertices_id_t - The ID of the new vertex
* @throws id_taken exception - If the relevant ID is already in use
*/
vertex_id_t add_vertex(auto&& vertex, vertex_id_t id);

/**
* Remove a vertex from the graph and update all its neighbors
*
Expand Down Expand Up @@ -217,7 +227,6 @@ using directed_graph = graph<VERTEX_T, EDGE_T, graph_type::DIRECTED>;

template <typename VERTEX_T, typename EDGE_T>
using undirected_graph = graph<VERTEX_T, EDGE_T, graph_type::UNDIRECTED>;

} // namespace graaf

#include "graph.tpp"
18 changes: 16 additions & 2 deletions include/graaflib/graph.tpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,12 +129,26 @@ graph<VERTEX_T, EDGE_T, GRAPH_TYPE_V>::get_neighbors(

template <typename VERTEX_T, typename EDGE_T, graph_type GRAPH_TYPE_V>
vertex_id_t graph<VERTEX_T, EDGE_T, GRAPH_TYPE_V>::add_vertex(auto&& vertex) {
// TODO: check overflow
const auto vertex_id{vertex_id_supplier_++};
while (has_vertex(vertex_id_supplier_)) {
++vertex_id_supplier_;
}
const auto vertex_id{vertex_id_supplier_};
vertices_.emplace(vertex_id, std::forward<VERTEX_T>(vertex));
return vertex_id;
}

template <typename VERTEX_T, typename EDGE_T, graph_type GRAPH_TYPE_V>
vertex_id_t graph<VERTEX_T, EDGE_T, GRAPH_TYPE_V>::add_vertex(auto&& vertex,
vertex_id_t id) {
if (has_vertex(id)) {
throw std::invalid_argument{"Vertex already exists at ID [" +
std::to_string(id) + "]"};
}

vertices_.emplace(id, std::forward<VERTEX_T>(vertex));
return id;
}

template <typename VERTEX_T, typename EDGE_T, graph_type GRAPH_TYPE_V>
void graph<VERTEX_T, EDGE_T, GRAPH_TYPE_V>::remove_vertex(
vertex_id_t vertex_id) {
Expand Down
36 changes: 36 additions & 0 deletions test/graaflib/algorithm/utils_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#include <graaflib/algorithm/utils.h>
#include <gtest/gtest.h>

/**
* Tests which miscellaneous utility functions contained
* in utils.h should go here. Any test relating specifically
* to the public graph interface should instead be located in
* graph_test.cpp.
*/

namespace graaf {

TEST(UtilsTest, Transpose) {
// GIVEN
using graph_t = directed_graph<int, int>;
graph_t graph{};
const auto vertex_id_1 = graph.add_vertex(1);
const auto vertex_id_2 = graph.add_vertex(2);
const auto vertex_id_3 = graph.add_vertex(3);
graph.add_edge(vertex_id_1, vertex_id_2, 100);
graph.add_edge(vertex_id_2, vertex_id_3, 200);
graph.add_edge(vertex_id_3, vertex_id_1, 300);

// WHEN
graph_t transposed_graph = get_transposed_graph(graph);

// THEN
EXPECT_EQ(get_weight(transposed_graph.get_edge(vertex_id_2, vertex_id_1)),
100);
EXPECT_EQ(get_weight(transposed_graph.get_edge(vertex_id_3, vertex_id_2)),
200);
EXPECT_EQ(get_weight(transposed_graph.get_edge(vertex_id_1, vertex_id_3)),
300);
}

} // namespace graaf
25 changes: 25 additions & 0 deletions test/graaflib/graph_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ TYPED_TEST(GraphTest, VertexCount) {
ASSERT_EQ(graph.vertex_count(), 2);
ASSERT_TRUE(graph.has_vertex(vertex_id_2));
ASSERT_EQ(graph.get_vertex(vertex_id_2), 20);

// WHEN - THEN
constexpr int specific_id = 2;
const auto vertex_specific_id{graph.add_vertex(30, specific_id)};
ASSERT_EQ(graph.vertex_count(), 3);
ASSERT_TRUE(graph.has_vertex(specific_id));
ASSERT_EQ(graph.get_vertex(specific_id), 30);
}

TYPED_TEST(GraphTest, RemoveVertex) {
Expand Down Expand Up @@ -170,6 +177,24 @@ TYPED_TEST(GraphTest, VertexTests) {
EXPECT_FALSE(graph.has_vertex(nonExistingVertexId));
EXPECT_TRUE(graph.has_vertex(vertex_id_2));
EXPECT_EQ(graph.get_vertex(vertex_id_2), 20);

ASSERT_THROW(
{
try {
// Add vertex to ID that already exists
[[maybe_unused]] const auto duplicate_id{
graph.add_vertex(50, vertex_id_1)};
FAIL()
<< "Expected std::invalid_argument exception, but no exception "
"was thrown.";
} catch (const std::invalid_argument &ex) {
EXPECT_EQ(ex.what(), fmt::format("Vertex already exists at ID [{}]",
vertex_id_1));
throw;
}
},
std::invalid_argument);
EXPECT_EQ(graph.get_vertex(vertex_id_1), 1);
}

TYPED_TEST(GraphTest, GetEdgeNonExistingEdge) {
Expand Down

0 comments on commit ae3e232

Please sign in to comment.