Skip to content
Closed
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
142 changes: 142 additions & 0 deletions include/CXXGraph/Graph/Algorithm/Bridges_impl.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/***********************************************************/
/*** ______ ____ ______ _ ***/
/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/
/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/
/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/
/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/
/*** |_| ***/
/***********************************************************/
/*** Header-Only C++ Library for Graph ***/
/*** Representation and Algorithms ***/
/***********************************************************/
/*** Author: ZigRazor ***/
/*** E-Mail: [email protected] ***/
/***********************************************************/
/*** Collaboration: John Ronchetto ***/
/***********************************************************/
/*** License: MPL v2.0 ***/
/***********************************************************/

// NOT OPTIMIZED SOLUTION
// UNDIRECTED GRAPH ASSUMPTION

#ifndef __CXXGRAPH_BRIDGES_IMPL_H__
#define __CXXGRAPH_BRIDGES_IMPL_H__

#pragma once

#include <algorithm>
#include <functional>
#include <vector>

#include "CXXGraph/Graph/Graph_decl.h"



namespace CXXGraph {

// Returns list of edges that are bridges in the graph
template <typename T>
const std::vector<std::pair<Node<T>, Node<T>>> Graph<T>::bridges(
const Node<T> &start) const {

std::vector<std::pair<Node<T>, Node<T>>> bridge;

auto &nodeSet = Graph<T>::getNodeSet();

// Check that start node actually exists
auto startNodeIt = std::find_if(
nodeSet.begin(), nodeSet.end(),
[&start](auto &node) {return node->getUserId() == start.getUserId();});
// If start node not in graph, then return wmpty result
if (startNodeIt == nodeSet.end())
return bridge;

// Helper that runs dfs on node and skips a specific edge (skipU-SkipV)
std::function<void(const std::shared_ptr<AdjacencyList<T>>,
shared<const Node<T>>,
shared<const Node<T>>,
shared<const Node<T>>,
std::vector<Node<T>> &)>
dfs;

dfs = [&dfs](const std::shared_ptr<AdjacencyList<T>> adj,
shared<const Node<T>> node,
shared<const Node<T>> skipU,
shared<const Node<T>> skipV,
std::vector<Node<T>> &visited) -> void {

Check notice on line 68 in include/CXXGraph/Graph/Algorithm/Bridges_impl.hpp

View check run for this annotation

codefactor.io / CodeFactor

include/CXXGraph/Graph/Algorithm/Bridges_impl.hpp#L68

Redundant blank line at the start of a code block should be deleted. (whitespace/blank_line)
// Mark current node as visited
visited.push_back(*node);

// if node has no out edges, then we're done
if (adj->find(node) == adj->end()) {
return;
}

// Explore all nighbors of current node
for (const auto &x : adj->at(node)) {
auto next = x.first;

// Check whether the current edge (node-next) is
// the one we want to remove. Check both directions.

bool edgeRemoved =
(node->getUserId() == skipU->getUserId() &&
next->getUserId() == skipV->getUserId()) ||
(node->getUserId() == skipV->getUserId() &&
next->getUserId() == skipU->getUserId());

// Pretend that edge doesn't exist
if (edgeRemoved) {
continue;
}

// Standard dfs check using Node<T> value
if (std::find(visited.begin(), visited.end(), *next) ==
visited.end()) {
dfs(adj, next, skipU, skipV, visited);
}
}
};

// Iterate over every node in the graph
for (auto &uPtr : nodeSet) {
// cachedAdjListOut maps each node in the graph to its outgoing neighbors
auto adjIt = cachedAdjListOut->find(uPtr);

if (adjIt == cachedAdjListOut->end()) {
continue;
}

// Look at every neighbor
for (const auto &edge : adjIt->second) {
auto vPtr = edge.first;

// Run dfs form u and skipping edge u-v
std::vector<Node<T>> visited;
dfs(cachedAdjListOut, uPtr, uPtr, vPtr, visited);

// Check if v was reached
// if NOT, then u-v is a bridge
bool reached =
std::find(visited.begin(), visited.end(), *vPtr) !=
visited.end();

auto uId = uPtr->getUserId();
auto vId = vPtr->getUserId();

if (!reached) {
if (uId < vId) {
bridge.emplace_back(*uPtr, *vPtr);
}
}
}
}

return bridge;
}

} // namespace CXXGraph

#endif // __CXXGRAPH_BRIDGES_IMPL_H__
1 change: 1 addition & 0 deletions include/CXXGraph/Graph/Graph.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
#include "CXXGraph/Graph/Algorithm/TopologicalSort_impl.hpp"
#include "CXXGraph/Graph/Algorithm/TransitiveReduction_impl.hpp"
#include "CXXGraph/Graph/Algorithm/WelshPowellColoring_impl.hpp"
#include "CXXGraph/Graph/Algorithm/Bridges_impl.hpp"

// IO Operation
#include "CXXGraph/Graph/IO/IOUtility_impl.hpp"
Expand Down
4 changes: 4 additions & 0 deletions include/CXXGraph/Graph/Graph_decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@

#pragma once

#include <cstdint>

#include <cmath>
#include <cstring>
#include <deque>
Expand Down Expand Up @@ -114,6 +116,8 @@ class Graph {
const std::string &fileName,
bool readNodeFeatures = false,
bool readEdgeWeights = true);
const std::vector<std::pair<Node<T>, Node<T>>> bridges(
const Node<T> &start) const;

private:
T_EdgeSet<T> edgeSet = {};
Expand Down
2 changes: 2 additions & 0 deletions include/CXXGraph/Utility/SecureRandom.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
#ifndef STATUS_SUCCESS
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
#endif
#ifdef _MSC_VER
#pragma comment(lib, "bcrypt.lib")
#endif
#else
#include <fstream>
#endif
Expand Down
106 changes: 106 additions & 0 deletions test/BridgesTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
#include <memory>
#include <string>
#include <utility>
#include <vector>

#include "CXXGraph/CXXGraph.hpp"
#include "gtest/gtest.h"


// Smart pointers alias
template <typename T>
using unique = std::unique_ptr<T>;
template <typename T>
using shared = std::shared_ptr<T>;

using std::make_shared;
using std::make_unique;

// helper: does the bridges vector contain an edge between a and b?
static bool has_bridge(
const std::vector<std::pair<CXXGraph::Node<int>, CXXGraph::Node<int>>> &bridges,
const CXXGraph::Node<int> &a,
const CXXGraph::Node<int> &b) {

return std::any_of(
bridges.begin(), bridges.end(),
[&a, &b](const auto &e) {
return (e.first == a && e.second == b) ||
(e.first == b && e.second == a);
});
}

/*
Test 1: 1 -- 2 -- 3

All edges are bridges
*/
TEST(BridgesTest, Chain_AllEdgesAreBridges) {
using namespace CXXGraph;

// Nodes
Node<int> node1("1", 1);
Node<int> node2("2", 2);
Node<int> node3("3", 3);

// Undirected edges
UndirectedWeightedEdge<int> e1("e1", node1, node2, 1);
UndirectedWeightedEdge<int> e2("e2", node2, node3, 1);

T_EdgeSet<int> edgeSet;
edgeSet.insert(make_shared<UndirectedWeightedEdge<int>>(e1));
edgeSet.insert(make_shared<UndirectedWeightedEdge<int>>(e2));

Graph<int> graph(edgeSet);

auto bridge = graph.bridges(node1);

// Expected bridges
ASSERT_TRUE(has_bridge(bridge, node1, node2));
ASSERT_TRUE(has_bridge(bridge, node2, node3));
}

/*
Test 2:
1
/ \
2---3---4

(3,4) is the only bridge
*/
TEST(BridgesTest, TrianglePlusTail_OnlyTailIsBridge) {
using namespace CXXGraph;

// Nodes
Node<int> node1("1", 1);
Node<int> node2("2", 2);
Node<int> node3("3", 3);
Node<int> node4("4", 4);

// Undirected edges
UndirectedWeightedEdge<int> e1("e1", node1, node2, 1);
UndirectedWeightedEdge<int> e2("e2", node2, node3, 1);
UndirectedWeightedEdge<int> e3("e3", node1, node3, 1);
UndirectedWeightedEdge<int> e4("e4", node3, node4, 1);

T_EdgeSet<int> edgeSet;
edgeSet.insert(make_shared<UndirectedWeightedEdge<int>>(e1));
edgeSet.insert(make_shared<UndirectedWeightedEdge<int>>(e2));
edgeSet.insert(make_shared<UndirectedWeightedEdge<int>>(e3));
edgeSet.insert(make_shared<UndirectedWeightedEdge<int>>(e4));

Graph<int> graph(edgeSet);

auto bridge = graph.bridges(node1);

// Only (3,4) should be a bridge
ASSERT_EQ(bridge.size(), 1u);
ASSERT_TRUE(has_bridge(bridge, node3, node4));

// All other edges should NOT be bridges
ASSERT_FALSE(has_bridge(bridge, node1, node2));
ASSERT_FALSE(has_bridge(bridge, node2, node3));
ASSERT_FALSE(has_bridge(bridge, node1, node3));
}


5 changes: 5 additions & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ if(TEST)
file (GLOB TEST_FILES "*.cpp" "*.hpp" "*.h")
add_executable(test_exe ${TEST_FILES})

if (MINGW)
target_link_libraries(test_exe PRIVATE bcrypt)
endif()


target_compile_definitions(test_exe
PUBLIC WITH_COMPRESSION
)
Expand Down