<tb@panthema.net>
<http://www.gnu.org/licenses/>
#ifndef BISPANNING_ALG_BISPANNING_HEADER
#define BISPANNING_ALG_BISPANNING_HEADER
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
#include "debug.h"
#include "graph.h"
template <typename IdType>
class UnionFind
{
protected:
using id_type = IdType;
struct Item
{
id_type parent;
id_type rank;
};
std::vector<Item> m_sets;
public:
explicit UnionFind(const id_type& num)
: m_sets(num)
{
clear();
}
void clear()
{
for (size_t v = 0; v < m_sets.size(); ++v)
{
m_sets[v].parent = v;
m_sets[v].rank = 0;
}
}
const id_type & find(const id_type& v)
{
if (m_sets[v].parent != v)
m_sets[v].parent = find(m_sets[v].parent);
return m_sets[v].parent;
}
const id_type & find(const id_type& v) const
{
if (m_sets[v].parent != v)
return find(m_sets[v].parent);
else
return m_sets[v].parent;
}
void unite(const id_type& v, const id_type& w)
{
int vr = find(v), wr = find(w);
if (m_sets[vr].rank < m_sets[wr].rank)
m_sets[vr].parent = wr;
else if (m_sets[vr].rank > m_sets[wr].rank)
m_sets[wr].parent = vr;
else
{
m_sets[wr].parent = vr;
m_sets[vr].rank++;
}
}
};
template <typename GraphType, bool debug = false>
struct AlgBispanning : public GraphDecorator<GraphType>
{
protected:
using id_type = typename GraphType::id_type;
using const_vertex_iterator = typename GraphType::const_vertex_iterator;
using const_edge_iterator = typename GraphType::const_edge_iterator;
using const_vertex_edge_iterator = typename GraphType::const_vertex_edge_iterator;
using color_type = unsigned char;
const GraphType& m_graph;
std::vector<color_type> m_color;
UnionFind<id_type> m_union1, m_union2;
std::vector<ssize_t> m_pred1, m_pred2;
std::vector<ssize_t> m_label;
unsigned int m_count[3];
void bfs_tree(color_type color, id_type root)
{
std::vector<ssize_t>& pred = (color == 1) ? m_pred1 : m_pred2;
for (size_t v = 0; v < pred.size(); ++v) {
pred[v] = -1;
}
std::queue<id_type> queue;
queue.push(root);
pred[root] = root;
while (!queue.empty())
{
id_type v = queue.front();
queue.pop();
for (const_vertex_edge_iterator ei : m_graph.vertex_edge_list(v))
{
if (m_color[ei.edge_id()] != color) continue;
id_type w = ei.other_id(v);
if (pred[w] >= 0)
continue;
queue.push(w);
pred[w] = ei.edge_id();
}
}
}
bool bfs_augmenting_path(const const_edge_iterator& e0)
{
std::queue<id_type> queue;
queue.push(e0.edge_id());
id_type e0_x = e0.head_id();
for (id_type e = 0; e < m_graph.total_edge(); ++e)
m_label[e] = -1;
while (!queue.empty())
{
id_type e = queue.front();
queue.pop();
color_type ti = (m_color[e] % 2) + 1;
std::vector<ssize_t>& pred = (ti == 1) ? m_pred1 : m_pred2;
UnionFind<id_type>& myunion = (ti == 1) ? m_union1 : m_union2;
id_type e_v = m_graph.edge(e).head_id();
id_type e_w = m_graph.edge(e).tail_id();
DBGG("Visiting " << m_graph.edge(e) << " with color " << int(ti) << "!");
DBGG("Ends of " << e << ": " << myunion.find(e_v) << " - " << myunion.find(e_w));
if (myunion.find(e_v) != myunion.find(e_w))
{
DBGG("Augmenting sequence!");
myunion.unite(e_v, e_w);
m_count[ti]++;
while (m_label[e] >= 0)
{
std::swap(m_color[e], ti);
DBGG("colored " << m_graph.edge(e) << " with " << int(m_color[e]));
e = m_label[e];
}
m_color[e] = ti;
DBGG("colored final " << m_graph.edge(e) << " with " << int(m_color[e]));
m_count[0]--;
return true;
}
DBGG("v: pred[" << e_v << "] = " << m_graph.edge(pred[e_v])
<< " label[" << pred[e_v] << "] = " << m_label[pred[e_v]]);
DBGG("w: pred[" << e_w << "] = " << m_graph.edge(pred[e_w])
<< " label[" << pred[e_w] << "] = " << m_label[pred[e_w]]);
assert(pred[e_v] >= 0 && pred[e_w] >= 0);
id_type e_u;
if (e_v != e0_x && m_label[pred[e_v]] < 0)
e_u = e_v;
else if (e_w != e0_x && m_label[pred[e_w]] < 0)
e_u = e_w;
else {
DBGG("Both ends of edge already in label tree.");
continue;
}
DBGG("u0 = " << e_u << ", pred = " << pred[e_u] << " -> " << m_graph.edge(pred[e_u])
<< ", label = " << m_label[pred[e_u]]);
std::stack<id_type> predpath;
while (e_u != e0_x && m_label[pred[e_u]] < 0)
{
const_edge_iterator en = m_graph.edge(pred[e_u]);
DBGG("push (e_u,pred[e_u]) = (" << e_u << "," << pred[e_u] << ") = " << en.edge_id() << " onto stack.");
predpath.push(en.edge_id());
e_u = en.other_id(e_u);
if (e_u != e0_x) {
DBGG("u = " << e_u << ", pred = " << pred[e_u] << " -> " << m_graph.edge(pred[e_u])
<< ", label = " << m_label[pred[e_u]]);
}
}
while (!predpath.empty())
{
id_type e_prime = predpath.top();
predpath.pop();
DBGG("label " << m_graph.edge(e_prime) << " with " << e << " and put " << e_prime << " on queue.");
assert(m_label[e_prime] < 0);
m_label[e_prime] = e;
queue.push(e_prime);
}
}
return false;
}
public:
explicit AlgBispanning(GraphType& g)
: m_graph(g),
m_color(g.total_edge(), 0),
m_union1(g.total_vertex()),
m_union2(g.total_vertex()),
m_pred1(g.total_vertex()),
m_pred2(g.total_vertex()),
m_label(g.total_edge())
{
m_count[0] = m_count[1] = m_count[2] = 0;
for (const_edge_iterator e0 : m_graph.edge_list())
{
id_type e0id = e0.edge_id();
id_type e0_x = e0.head_id(), e0_y = e0.tail_id();
m_color[e0id] = e0->color + 1;
if (m_color[e0id] == 1 && m_union1.find(e0_x) != m_union1.find(e0_y))
{
DBGG("Edge " << e0 << " directly readded to tree 1");
m_union1.unite(e0_x, e0_y);
m_count[1]++;
}
else if (m_color[e0id] == 2 && m_union2.find(e0_x) != m_union2.find(e0_y))
{
DBGG("Edge " << e0 << " directly readded to tree 2");
m_union2.unite(e0_x, e0_y);
m_count[2]++;
}
else
{
m_color[e0id] = 0;
m_count[0]++;
}
}
for (const_edge_iterator e0 : m_graph.edge_list())
{
id_type e0id = e0.edge_id();
id_type e0_x = e0.head_id(), e0_y = e0.tail_id();
DBGG("Processing new edge " << e0);
if (m_color[e0id] != 0)
{
DBGG("Edge already in a tree");
}
#if 1
else if (m_union1.find(e0_x) != m_union1.find(e0_y))
{
DBGG("Edge added directly to tree 1");
m_color[e0id] = 1;
m_union1.unite(e0_x, e0_y);
m_count[1]++;
m_count[0]--;
DBG(graphviz(g, *this));
}
else if (m_union2.find(e0_x) != m_union2.find(e0_y))
{
DBGG("Edge added directly to tree 2");
m_color[e0id] = 2;
m_union2.unite(e0_x, e0_y);
m_count[2]++;
m_count[0]--;
DBG(graphviz(g, *this));
}
#endif
else
{
DBGG("BFS root node x = " << e0_x);
bfs_tree(1, e0_x);
bfs_tree(2, e0_x);
DBG(graphviz(g, *this));
if (!bfs_augmenting_path(e0)) return;
}
id_type count[3] = { 0, 0, 0 };
for (size_t ei = 0; ei < m_graph.total_edge(); ++ei)
{
if (g.edge_deleted(ei)) continue;
count[m_color[ei]]++;
}
DBGG("number of colored edges: " << count[0] << " / " << count[1] << " / " << count[2]);
assert(m_count[0] == count[0]);
assert(m_count[1] == count[1]);
assert(m_count[2] == count[2]);
if (m_count[1] == g.num_vertex() - 1 &&
m_count[2] == g.num_vertex() - 1)
break;
}
DBGG("resulting number of colored edges: " << m_count[0] << " / " << m_count[1] << " / " << m_count[2]);
DBG(graphviz(g, *this));
}
bool good()
{
return (m_graph.num_edge() == 2 * m_graph.num_vertex() - 2 &&
m_count[1] == m_graph.num_vertex() - 1 &&
m_count[2] == m_graph.num_vertex() - 1);
}
void write(GraphType& g)
{
assert(g.total_edge() == m_color.size());
for (size_t ei = 0; ei < m_color.size(); ++ei)
{
if (g.edge_deleted(ei)) continue;
g.edge(ei)->color = (m_color[ei] - 1);
}
}
bool test()
{
bfs_tree(1, 0);
for (unsigned int v = 0; v < m_pred1.size(); ++v) {
if (m_pred1[v] < 0) {
std::cerr << "Vertex " << v << " is disconnected in tree 1.\n";
return false;
}
}
bfs_tree(2, 0);
for (unsigned int v = 0; v < m_pred2.size(); ++v) {
if (m_pred2[v] < 0) {
std::cerr << "Vertex " << v << " is disconnected in tree 2.\n";
return false;
}
}
return true;
}
void graph(std::ostream& os, const GraphType&) const
{
os << "splines=false,K=1.6";
}
void vertex(std::ostream& os, const const_vertex_iterator& v) const
{
os << "label=\"" << v.vertex_id()
<< "/" << m_union1.find(v.vertex_id())
<< "." << m_union2.find(v.vertex_id())
<< "/" << m_pred1[v.vertex_id()]
<< "." << m_pred2[v.vertex_id()]
<< "\"";
}
void edge(std::ostream& os, const const_edge_iterator& e) const
{
id_type eid = e.edge_id();
if (m_color[eid] == 1)
os << "color=\"blue\"";
else if (m_color[eid] == 2)
os << "color=\"red\"";
else
os << "color=\"black\"";
os << ",label=\"e" << e.edge_id() << "\"";
}
};
#endif