1 // This file is part of Heimer.
2 // Copyright (C) 2018 Jussi Lind <jussi.lind@iki.fi>
3 //
4 // Heimer is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 // Heimer is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 // GNU General Public License for more details.
12 //
13 // You should have received a copy of the GNU General Public License
14 // along with Heimer. If not, see <http://www.gnu.org/licenses/>.
15
16 #include "graph.hpp"
17 #include "node.hpp"
18 #include "test_mode.hpp"
19
20 #include "simple_logger.hpp"
21
22 #include <algorithm>
23 #include <cassert>
24 #include <cmath>
25 #include <stdexcept>
26 #include <string>
27
28 namespace {
getKey(int c0,int c1)29 int64_t getKey(int c0, int c1)
30 {
31 return (int64_t(c0) << 32) + c1;
32 }
33 } // namespace
34
Graph()35 Graph::Graph()
36 {
37 }
38
clear()39 void Graph::clear()
40 {
41 m_edges.clear();
42 m_nodes.clear();
43 }
44
addNode(NodePtr node)45 void Graph::addNode(NodePtr node)
46 {
47 if (node->index() == -1) {
48 node->setIndex(m_count++);
49 } else {
50 if (node->index() >= m_count) {
51 m_count = node->index() + 1;
52 }
53 }
54
55 m_nodes[node->index()] = node;
56 }
57
deleteEdge(int index0,int index1)58 EdgePtr Graph::deleteEdge(int index0, int index1)
59 {
60 EdgePtr deletedEdge;
61 if (const auto edgeIter = m_edges.find(getKey(index0, index1)); edgeIter != m_edges.end()) {
62 deletedEdge = (*edgeIter).second;
63 m_deletedEdges.push_back(deletedEdge);
64 m_edges.erase(edgeIter);
65 }
66 return deletedEdge;
67 }
68
deleteNode(int index)69 std::pair<NodePtr, Graph::EdgeVector> Graph::deleteNode(int index)
70 {
71 NodePtr deletedNode;
72 Graph::EdgeVector deletedEdges;
73 if (const auto iter = m_nodes.find(index); iter != m_nodes.end()) {
74 auto edgeIter = m_edges.begin();
75 while (edgeIter != m_edges.end()) {
76 if (const auto edgePair = *edgeIter; edgePair.second->sourceNode().index() == index || edgePair.second->targetNode().index() == index) {
77 deletedEdges.push_back(edgePair.second);
78 m_deletedEdges.push_back(edgePair.second);
79 edgeIter = m_edges.erase(edgeIter);
80 } else {
81 edgeIter++;
82 }
83 }
84 deletedNode = iter->second;
85 m_deletedNodes.push_back(deletedNode);
86 m_nodes.erase(iter);
87 }
88
89 return { deletedNode, deletedEdges };
90 }
91
addEdge(EdgePtr newEdge)92 void Graph::addEdge(EdgePtr newEdge)
93 {
94 // Add if such edge doesn't already exist
95 const auto c0 = newEdge->sourceNode().index();
96 const auto c1 = newEdge->targetNode().index();
97 if (!m_edges.count(getKey(c0, c1))) {
98 m_edges.insert({ getKey(c0, c1), newEdge });
99 }
100 }
101
areDirectlyConnected(NodePtr node0,NodePtr node1)102 bool Graph::areDirectlyConnected(NodePtr node0, NodePtr node1)
103 {
104 return areDirectlyConnected(node0->index(), node1->index());
105 }
106
areDirectlyConnected(int index0,int index1)107 bool Graph::areDirectlyConnected(int index0, int index1)
108 {
109 return m_edges.count(getKey(index0, index1)) || m_edges.count(getKey(index1, index0));
110 }
111
numNodes() const112 size_t Graph::numNodes() const
113 {
114 return m_nodes.size();
115 }
116
getEdges() const117 Graph::EdgeVector Graph::getEdges() const
118 {
119 EdgeVector edges;
120 edges.reserve(m_edges.size());
121 for (auto && edge : m_edges) {
122 edges.push_back(edge.second);
123 }
124 return edges;
125 }
126
getEdgesFromNode(NodePtr node)127 Graph::EdgeVector Graph::getEdgesFromNode(NodePtr node)
128 {
129 EdgeVector edges;
130 for (auto && edge : m_edges) {
131 if (edge.second->sourceNode().index() == node->index()) {
132 edges.push_back(edge.second);
133 }
134 }
135 return edges;
136 }
137
getEdgesToNode(NodePtr node)138 Graph::EdgeVector Graph::getEdgesToNode(NodePtr node)
139 {
140 Graph::EdgeVector edges;
141 for (auto && edge : m_edges) {
142 if (edge.second->targetNode().index() == node->index()) {
143 edges.push_back(edge.second);
144 }
145 }
146 return edges;
147 }
148
getNode(int index)149 NodePtr Graph::getNode(int index)
150 {
151 if (const auto iter = m_nodes.find(index); iter != m_nodes.end()) {
152 return iter->second;
153 }
154 throw std::runtime_error("Invalid node index: " + std::to_string(index));
155 }
156
getNodes() const157 Graph::NodeVector Graph::getNodes() const
158 {
159 NodeVector nodes;
160 nodes.reserve(m_nodes.size());
161 for (auto && node : m_nodes) {
162 nodes.push_back(node.second);
163 }
164 return nodes;
165 }
166
getNodesConnectedToNode(NodePtr node)167 Graph::NodeVector Graph::getNodesConnectedToNode(NodePtr node)
168 {
169 NodeVector result;
170
171 for (auto && edge : getEdgesToNode(node)) {
172 result.push_back(getNode(edge->sourceNode().index()));
173 }
174
175 for (auto && edge : getEdgesFromNode(node)) {
176 result.push_back(getNode(edge->targetNode().index()));
177 }
178
179 return result;
180 }
181
~Graph()182 Graph::~Graph()
183 {
184 // Ensure that edges are always deleted before nodes
185 clear();
186
187 juzzlin::L().debug() << "Graph deleted";
188 }
189