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