2023-11-04 17:12:18 +01:00
|
|
|
#include "graph.hpp" // always include corresponding header first
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Note this included everything from the header similar to copy-pasting it here,
|
|
|
|
* including our two classes, the function declarations and all the includes.
|
|
|
|
* In this file we will actually implement the in- and output routines though,
|
|
|
|
* so we need to include the actual implementation of std::istream and std::ostream.
|
|
|
|
*/
|
|
|
|
#include <iostream>
|
|
|
|
|
|
|
|
/**
|
|
|
|
* We are also going to use stringstream in order to treat a line,
|
|
|
|
* which we have already read from the input, like an input stream.
|
|
|
|
*/
|
|
|
|
#include <sstream>
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The execption header is used to terminate our program
|
|
|
|
* in the case of unexpected input,
|
|
|
|
* which is often the best way to handle such input.
|
|
|
|
* More complex programs may want to catch exceptions in
|
|
|
|
* surrouding code and either try to recover or help debug them.
|
|
|
|
*/
|
|
|
|
#include <stdexcept>
|
|
|
|
|
|
|
|
// Anonymous name spaces may be used to show the reader
|
|
|
|
// that a function will only be used in the current file.
|
|
|
|
namespace
|
|
|
|
{
|
|
|
|
|
|
|
|
// Using a function for converting the DIMACS node ids to and from our node ids
|
|
|
|
// makes the in and output code more understandable.
|
|
|
|
ED::NodeId from_dimacs_id(ED::size_type dimacs_node_id)
|
|
|
|
{
|
|
|
|
if (dimacs_node_id <= 0)
|
|
|
|
{
|
|
|
|
throw std::runtime_error("Non-positive DIMACS node id can not be converted.");
|
|
|
|
}
|
|
|
|
return dimacs_node_id - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ED::size_type to_dimacs_id(ED::NodeId node_id)
|
|
|
|
{
|
|
|
|
return node_id + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns the first line which is not a comment, i.e. does not start with c.
|
|
|
|
std::string read_next_non_comment_line(std::istream & input)
|
|
|
|
{
|
|
|
|
std::string line;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
if (!std::getline(input, line))
|
|
|
|
{
|
|
|
|
throw std::runtime_error("Unexpected end of DIMACS stream.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
while (line[0] == 'c');
|
|
|
|
return line;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // end of anonymous namespace
|
|
|
|
|
|
|
|
namespace ED
|
|
|
|
{
|
|
|
|
/////////////////////////////////////////////
|
|
|
|
//! \c Node definitions
|
|
|
|
/////////////////////////////////////////////
|
|
|
|
|
|
|
|
void Node::add_neighbor(NodeId const id)
|
|
|
|
{
|
|
|
|
_neighbors.push_back(id);
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////
|
|
|
|
//! \c Graph definitions
|
|
|
|
/////////////////////////////////////////////
|
|
|
|
|
|
|
|
// Whenever reasonably possible you should prefer to use `:`
|
|
|
|
// to initalize the members of your class, instead of
|
|
|
|
// assigning values to them after they were default initialized.
|
|
|
|
// Note you should initialize them in the same order
|
|
|
|
// they were declare in back in the class body!
|
|
|
|
Graph::Graph(NodeId const num_nodes)
|
|
|
|
: _nodes(num_nodes)
|
|
|
|
, _num_edges(0)
|
|
|
|
{}
|
|
|
|
|
|
|
|
void Graph::add_edge(NodeId node1_id, NodeId node2_id)
|
|
|
|
{
|
|
|
|
// It is ok if your program crashes for garbage input,
|
|
|
|
// but it should be an explicit, deliberate choice, e.g. like this.
|
|
|
|
if (node1_id == node2_id)
|
|
|
|
{
|
|
|
|
throw std::runtime_error("ED::Graph class does not support loops!");
|
|
|
|
}
|
|
|
|
|
|
|
|
_nodes[node1_id].add_neighbor(node2_id);
|
|
|
|
_nodes[node2_id].add_neighbor(node1_id);
|
|
|
|
++_num_edges;
|
|
|
|
}
|
|
|
|
|
|
|
|
Graph Graph::read_dimacs(std::istream & input)
|
|
|
|
{
|
|
|
|
// Unfortunatley the common std input functions require us to first declare
|
|
|
|
// our variables and assign them the correct values only later.
|
|
|
|
// Because we want to avoid unitizalized variables, we use a new syntax
|
|
|
|
// added in c++17 to call the constuctor with no arguments,
|
|
|
|
// often called the default constructor: We write {} behind the variable name.
|
|
|
|
|
|
|
|
// When parsing the DIMACS format, there are some words we are not interested in.
|
|
|
|
// We read them into this variable and never use the afterwards.
|
|
|
|
std::string unused_word{};
|
|
|
|
|
|
|
|
// As we need to watch out for comments, we first need to read the input by line.
|
|
|
|
// In order to split non-comment lines into multiple variables we use a std::stringstream.
|
|
|
|
std::stringstream first_buffering_stream{};
|
|
|
|
|
|
|
|
// Note if you do not plan to modify a variable, always declare it as constant.
|
|
|
|
// This does not only prevent you from doing so accidently,
|
|
|
|
// but also helps anybody reading your code understand what you are doing,
|
|
|
|
// as there are less possiblities what can happen.
|
|
|
|
std::string const first_line = read_next_non_comment_line(input);
|
|
|
|
|
|
|
|
size_type num_nodes{};
|
|
|
|
size_type num_edges{};
|
|
|
|
first_buffering_stream << first_line;
|
|
|
|
first_buffering_stream >> unused_word >> unused_word >> num_nodes >> num_edges;
|
|
|
|
|
|
|
|
// Now we successively add edges to our graph;
|
|
|
|
Graph graph(num_nodes);
|
|
|
|
for (size_type i = 1; i <= num_edges; ++i)
|
|
|
|
{
|
|
|
|
// This works just as parsing the first line!
|
|
|
|
std::stringstream ith_buffering_stream{};
|
|
|
|
std::string const ith_line = read_next_non_comment_line(input);
|
|
|
|
size_type dimacs_node1{};
|
|
|
|
size_type dimacs_node2{};
|
|
|
|
ith_buffering_stream << ith_line;
|
|
|
|
ith_buffering_stream >> unused_word >> dimacs_node1 >> dimacs_node2;
|
|
|
|
graph.add_edge(from_dimacs_id(dimacs_node1), from_dimacs_id(dimacs_node2));
|
|
|
|
}
|
|
|
|
|
|
|
|
return graph;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::ostream & operator<<(std::ostream & output, Graph const & graph)
|
|
|
|
{
|
|
|
|
// We use std::endl to write new lines here.
|
|
|
|
// If you prefer the new line character, \n on linux, that one works fine, too.
|
|
|
|
output << "c Recall each line starting with c encodes a comment in DIMACS format!" << std::endl
|
|
|
|
<< "c The first non-comment line specifies the number of nodes and edges:" << std::endl
|
|
|
|
<< "p edge " << graph.num_nodes() << " " << graph.num_edges() << std::endl
|
|
|
|
<< "c Each of the remaining non-comment lines specifys an edge by two nodes:" << std::endl;
|
|
|
|
|
|
|
|
// We will need the id of the node we are at, so we write a plain old loop here.
|
|
|
|
for (NodeId node_id = 0; node_id < graph.num_nodes(); ++node_id)
|
|
|
|
{
|
|
|
|
Node const & node = graph.node(node_id);
|
|
|
|
// We do not need to keep track of the index neighbor_id has in node,
|
|
|
|
// so we can use this cool loop syntax introduced in c++11.
|
|
|
|
for (NodeId const & neighbor_id : node.neighbors())
|
|
|
|
{
|
|
|
|
// Note we iterate over each edge two times, so we use the following
|
|
|
|
// comparism to check if the edge was not yet written to str!
|
|
|
|
if (node_id < neighbor_id)
|
|
|
|
{
|
|
|
|
output << "e " << to_dimacs_id(node_id) << " " << to_dimacs_id(neighbor_id) << std::endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
output << "c If you use this graph class in your solution, you should probably remove this comments!" << std::endl;
|
|
|
|
|
|
|
|
// Streams sometimes buffer their output.
|
|
|
|
// Once one is done with some output routine, it can make sense to flush them,
|
|
|
|
// which clears the buffer and writes the remaining output.
|
|
|
|
output << std::flush;
|
|
|
|
return output;
|
|
|
|
}
|
|
|
|
|
2023-11-04 17:26:17 +01:00
|
|
|
bool Graph::is_outer(NodeId const id) const {
|
|
|
|
return matched_neighbor(id) == id or \
|
|
|
|
ear_or_root_neighbor(matched_neighbor(id)) != matched_neighbor(id);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Graph::is_inner(NodeId const id) const
|
|
|
|
{
|
|
|
|
return ear_or_root_neighbor(id) != id and \
|
|
|
|
ear_or_root_neighbor(matched_neighbor(id)) == matched_neighbor(id);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Graph::is_out_of_forest(const ED::NodeId id) const
|
|
|
|
{
|
|
|
|
return matched_neighbor(id) != id and \
|
|
|
|
ear_or_root_neighbor(id) == id and \
|
|
|
|
ear_or_root_neighbor(matched_neighbor(id)) == matched_neighbor(id);
|
|
|
|
}
|
|
|
|
|
2023-11-04 17:12:18 +01:00
|
|
|
} // namespace ED
|