Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ add_executable(test_exe test/main.cpp
test/UndirectedWeightedEdgeTest.cpp
test/GraphTest.cpp
test/DijkstraTest.cpp
test/BellmanFordTest.cpp
test/BFSTest.cpp
test/DFSTest.cpp
test/CycleCheckTest.cpp
Expand Down
120 changes: 120 additions & 0 deletions include/Graph/Graph.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,20 @@ namespace CXXGRAPH
*/
virtual const DijkstraResult dijkstra(const Node<T> &source, const Node<T> &target) const;
/**
* @brief Function runs the bellman-ford algorithm for some source node and
* target node in the graph and returns the shortest distance of target
* from the source. It can also detect if a negative cycle exists in the graph.
* Note: No Thread Safe
*
* @param source source vertex
* @param target target vertex
*
* @return shortest distance if target is reachable from source else ERROR in
* case if target is not reachable from source. If there is no error then also
* returns if the graph contains a negative cycle.
*/
virtual const BellmanFordResult bellmanford(const Node<T> &source, const Node<T> &target) const;
/**
* \brief
* Function performs the breadth first search algorithm over the graph
* Note: No Thread Safe
Expand Down Expand Up @@ -945,6 +959,112 @@ namespace CXXGRAPH
return result;
}

template <typename T>
const BellmanFordResult Graph<T>::bellmanford(const Node<T> &source, const Node<T> &target) const
{
BellmanFordResult result;
result.success = false;
result.errorMessage = "";
result.result = INF_DOUBLE;
auto nodeSet = getNodeSet();
if (std::find(nodeSet.begin(), nodeSet.end(), &source) == nodeSet.end())
{
// check if source node exist in the graph
result.errorMessage = ERR_SOURCE_NODE_NOT_IN_GRAPH;
return result;
}
if (std::find(nodeSet.begin(), nodeSet.end(), &target) == nodeSet.end())
{
// check if target node exist in the graph
result.errorMessage = ERR_TARGET_NODE_NOT_IN_GRAPH;
return result;
}
// setting all the distances initially to INF_DOUBLE
std::map<const Node<T> *, double> dist, currentDist;
// n denotes the number of vertices in graph
auto n = nodeSet.size();
for (auto elem : nodeSet)
{
dist[elem] = INF_DOUBLE;
currentDist[elem] = INF_DOUBLE;
}

// marking the distance of source as 0
dist[&source] = 0;
// set if node distances in two consecutive
// iterations remain the same.
auto earlyStopping = false;
// outer loop for vertex relaxation
for (int i=0; i<n-1; i++)
{
auto edgeSet = this->getEdgeSet();
// inner loop for distance updates of
// each relaxation
for (auto edge : edgeSet)
{
auto elem = edge->getNodePair();
if (edge->isWeighted().has_value() && edge->isWeighted().value())
{
auto edge_weight = (dynamic_cast<const Weighted *>(edge))->getWeight();
if (dist[elem.first] + edge_weight < dist[elem.second])
dist[elem.second] = dist[elem.first] + edge_weight;
}
else
{
// No Weighted Edge
result.errorMessage = ERR_NO_WEIGHTED_EDGE;
return result;
}
}
auto flag = true;
for (const auto& [key, value] : dist) {
if (currentDist[key]!=value)
{
flag = false;
break;
}
}
for (const auto& [key, value] : dist) {
currentDist[key] = value; //update the current distance
}
if (flag)
{
earlyStopping = true;
break;
}
}

// check if there exists a negative cycle
if (!earlyStopping)
{
auto edgeSet = this->getEdgeSet();
for (auto edge : edgeSet)
{
auto elem = edge->getNodePair();
auto edge_weight = (dynamic_cast<const Weighted *>(edge))->getWeight();
if (dist[elem.first] + edge_weight < dist[elem.second])
{
result.success = true;
result.negativeCycle = true;
result.errorMessage = "";
return result;
}
}
}

if (dist[&target] != INF_DOUBLE)
{
result.success = true;
result.errorMessage = "";
result.negativeCycle = false;
result.result = dist[&target];
return result;
}
result.errorMessage = ERR_TARGET_NODE_NOT_REACHABLE;
result.result = -1;
return result;
}

template <typename T>
const std::vector<Node<T>> Graph<T>::breadth_first_search(const Node<T> &start) const
{
Expand Down
10 changes: 10 additions & 0 deletions include/Utility/Typedef.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,16 @@ namespace CXXGRAPH
};
typedef DijkstraResult_struct DijkstraResult;

/// Struct that contains the information about Dijsktra's Algorithm results
struct BellmanFordResult_struct
{
bool success; // TRUE if the function does not return error, FALSE otherwise
bool negativeCycle; // TRUE if graph contains a negative cycle, FALSE otherwise
std::string errorMessage; //message of error
double result; //result (valid only if success is TRUE & negativeCycle is false )
};
typedef BellmanFordResult_struct BellmanFordResult;

/// Struct that contains the information about Dijsktra's Algorithm results
struct DialResult_struct
{
Expand Down
111 changes: 111 additions & 0 deletions test/BellmanFordTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
#include "gtest/gtest.h"
#include "CXXGraph.hpp"

// check if algorithm works using a complicated test case
TEST(BellmanFordTest, test_1)
{
CXXGRAPH::Node<int> node0(0, 0);
CXXGRAPH::Node<int> node1(1, 1);
CXXGRAPH::Node<int> node2(2, 2);
CXXGRAPH::Node<int> node3(3, 3);
CXXGRAPH::Node<int> node4(4, 4);

CXXGRAPH::DirectedWeightedEdge<int> edge1(1, node0, node1, 6);
CXXGRAPH::DirectedWeightedEdge<int> edge2(1, node0, node2, 7);

CXXGRAPH::DirectedWeightedEdge<int> edge3(1, node1, node2, 8);
CXXGRAPH::DirectedWeightedEdge<int> edge4(1, node1, node3, -4);
CXXGRAPH::DirectedWeightedEdge<int> edge5(1, node1, node4, 5);


CXXGRAPH::DirectedWeightedEdge<int> edge6(1, node2, node4, -3);
CXXGRAPH::DirectedWeightedEdge<int> edge7(1, node2, node3, 9);

CXXGRAPH::DirectedWeightedEdge<int> edge8(1, node3, node4, 7);
CXXGRAPH::DirectedWeightedEdge<int> edge9(1, node3, node0, 2);

CXXGRAPH::DirectedWeightedEdge<int> edge10(1, node4, node1, -2);
std::list<const CXXGRAPH::Edge<int> *> edgeSet;
edgeSet.push_back(&edge1);
edgeSet.push_back(&edge2);
edgeSet.push_back(&edge3);
edgeSet.push_back(&edge4);
edgeSet.push_back(&edge5);
edgeSet.push_back(&edge6);
edgeSet.push_back(&edge7);
edgeSet.push_back(&edge8);
edgeSet.push_back(&edge9);
edgeSet.push_back(&edge10);

CXXGRAPH::Graph<int> graph(edgeSet);
CXXGRAPH::BellmanFordResult res = graph.bellmanford(node0, node3);
ASSERT_TRUE(res.success);
ASSERT_FALSE(res.negativeCycle);
ASSERT_EQ(res.errorMessage, "");
ASSERT_EQ(res.result, -2);
}

// a graph with negative cycle
TEST(BellmanFordTest, test_2)
{
CXXGRAPH::Node<int> node0(0, 0);
CXXGRAPH::Node<int> node1(1, 1);
CXXGRAPH::Node<int> node2(2, 2);
CXXGRAPH::DirectedWeightedEdge<int> edge1(1, node0, node1, 2);
CXXGRAPH::DirectedWeightedEdge<int> edge2(2, node1, node2, 3);
CXXGRAPH::DirectedWeightedEdge<int> edge3(3, node2, node0, -7);
std::list<const CXXGRAPH::Edge<int> *> edgeSet;
edgeSet.push_back(&edge1);
edgeSet.push_back(&edge2);
edgeSet.push_back(&edge3);
CXXGRAPH::Graph<int> graph(edgeSet);
CXXGRAPH::BellmanFordResult res = graph.bellmanford(node0, node2);
ASSERT_TRUE(res.success);
ASSERT_TRUE(res.negativeCycle);
ASSERT_EQ(res.errorMessage, "");
ASSERT_EQ(res.result, CXXGRAPH::INF_DOUBLE);
}

// UndirectedWeightedEdge
TEST(BellmanFordTest, test_3)
{
CXXGRAPH::Node<int> node1(1, 1);
CXXGRAPH::Node<int> node2(2, 2);
CXXGRAPH::Node<int> node3(3, 3);
std::pair<const CXXGRAPH::Node<int> *, const CXXGRAPH::Node<int> *> pairNode(&node1, &node2);
CXXGRAPH::DirectedWeightedEdge<int> edge1(1, pairNode, 1);
CXXGRAPH::DirectedWeightedEdge<int> edge2(2, node2, node3, 1);
CXXGRAPH::UndirectedWeightedEdge<int> edge3(3, node1, node3, 6);
std::list<const CXXGRAPH::Edge<int> *> edgeSet;
edgeSet.push_back(&edge1);
edgeSet.push_back(&edge2);
edgeSet.push_back(&edge3);
CXXGRAPH::Graph<int> graph(edgeSet);
CXXGRAPH::BellmanFordResult res = graph.bellmanford(node1, node3);
ASSERT_TRUE(res.success);
ASSERT_FALSE(res.negativeCycle);
ASSERT_EQ(res.errorMessage, "");
ASSERT_EQ(res.result, 2);
}

// No weighted edge
TEST(BellmanFordTest, test_4)
{
CXXGRAPH::Node<int> node1(1, 1);
CXXGRAPH::Node<int> node2(2, 2);
CXXGRAPH::Node<int> node3(3, 3);
std::pair<const CXXGRAPH::Node<int> *, const CXXGRAPH::Node<int> *> pairNode(&node1, &node2);
CXXGRAPH::DirectedWeightedEdge<int> edge1(1, pairNode, 1);
CXXGRAPH::DirectedWeightedEdge<int> edge2(2, node2, node3, 1);
CXXGRAPH::DirectedEdge<int> edge3(3, node1, node3);
std::list<const CXXGRAPH::Edge<int> *> edgeSet;
edgeSet.push_back(&edge1);
edgeSet.push_back(&edge2);
edgeSet.push_back(&edge3);
CXXGRAPH::Graph<int> graph(edgeSet);
CXXGRAPH::BellmanFordResult res = graph.bellmanford(node1, node3);
ASSERT_FALSE(res.success);
ASSERT_FALSE(res.negativeCycle);
ASSERT_EQ(res.errorMessage, CXXGRAPH::ERR_NO_WEIGHTED_EDGE);
ASSERT_EQ(res.result, CXXGRAPH::INF_DOUBLE);
}