commit 7f277b1768421ad25f82dd0ddd967dfb08c38e2c Author: Richard Wong Date: Sat Sep 9 20:41:49 2023 +0900 Feat: deferred planarity test implementation from https://code.google.com/archive/p/planarity-algorithms/ diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..aedf8ed --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.vscode +deferred_planarity_test/bin diff --git a/deferred_planarity_test/main.cpp b/deferred_planarity_test/main.cpp new file mode 100644 index 0000000..f656e39 --- /dev/null +++ b/deferred_planarity_test/main.cpp @@ -0,0 +1,70 @@ +//----------------------------------------------------------------------------------- +// A simple code that test the MPS algorighm. +//----------------------------------------------------------------------------------- + +#include +#include +#include +#include +#include "mps.h" +using namespace std; + +void find_mps(ifstream*, ofstream*); + +//n nodes, with the probability of existence of each edge being p. +void random_graph_generator(int n, double p, ofstream* out) { + (*out) << n << endl; + for (int i = 0; i < n; ++i) { + for (int j = i+1; j < n; ++j) { + if ((double)rand()/RAND_MAX <= p) (*out) << i << " " << j << endl; + } + } +} + +//Complete graph K_n. +void complete_graph_generator(int n, ofstream* out) { + (*out) << n << endl; + for (int i = 0; i < n; ++i) { + for (int j = i+1; j < n; ++j) { + (*out) << i << " " << j << endl; + } + } +} + +//----------------------------------------------------------------------------------- +// Main function. +//----------------------------------------------------------------------------------- + + +int main(int argc, char* argv[]) { + if (argc == 1) { + cout << "Usages:" << endl; + cout << "======================" << endl; + cout << "mps_testing -mps " << endl; + cout << " Process the infile, and output the resulting maximal" << endl + << " planar subgraph in the outfile." << endl; + cout << "mps_testing -gen " << endl; + cout << " Generate a random graph as the spec given in the infile." << endl; + while (true) { + int x = 0; + cin >> x; + } + } + ifstream in; + in.open(argv[2]); + ofstream out; + out.open(argv[3]); + if (!in.is_open() || !out.is_open()) { + cout << "An error occurs when opening file." << endl; + return 0; + } + if (argv[1][1] == 'm') find_mps(&in, &out); + else if (argv[1][1] == 'g') { + int n; + double p; + in >> n; + in >> p; + random_graph_generator(n, p, &out); + } + return 0; +} diff --git a/deferred_planarity_test/mps.cpp b/deferred_planarity_test/mps.cpp new file mode 100644 index 0000000..e5cbb20 --- /dev/null +++ b/deferred_planarity_test/mps.cpp @@ -0,0 +1,728 @@ +//----------------------------------------------------------------------------------- +// Implementation of a MPS algorithm via PC-tree. +//----------------------------------------------------------------------------------- + +#include "mps.h" + +//Empty constructor +maximal_planar_subgraph_finder::maximal_planar_subgraph_finder() {} + +//Destructor +maximal_planar_subgraph_finder::~maximal_planar_subgraph_finder() { + for (int i = 0; i < _node_list.size(); ++i) delete _node_list[i]; + for (int i = 0; i < _new_node_list.size(); ++i) delete _new_node_list[i]; +} + +node* +maximal_planar_subgraph_finder::get_new_node(node_type t) { + _new_node_list.push_back(new node(t)); + return _new_node_list[_new_node_list.size()-1]; +} + +//Determine the post-order-list by a DFS-traversal. +void +maximal_planar_subgraph_finder::postOrderTraversal() { + node::init_mark(); + int postOrderID = 0; + for (int i = 0; i < _node_list.size(); ++i) { + if (!_node_list[i]->is_marked()) { + _node_list[i]->DFS_visit(_post_order_list, postOrderID); + } + } +} + +//Sort the adj-list of every node increasingly according to post-order-index. +void +maximal_planar_subgraph_finder::sort_adj_list() { + vector > vecList; + vecList.resize(_post_order_list.size()); + for (int i = 0; i < _post_order_list.size(); ++i) { + for (int j = 0; j < _post_order_list[i]->degree(); ++j) { + vecList[_post_order_list[i]->adj(j)->post_order_index()].push_back(_post_order_list[i]); + } + } + for (int i = 0; i < _post_order_list.size(); ++i) { + _post_order_list[i]->set_adj_list(vecList[i]); + } +} + +//Determine edge-list, and back-edge-list. +//Order the edges properly. +void +maximal_planar_subgraph_finder::determine_edges() { + for (int i = 0; i < _post_order_list.size(); ++i) { + if (_post_order_list[i]->parent() == 0) continue; + _post_order_list[i]->set_1st_label(_post_order_list[i]->parent()->post_order_index()); + _edge_list.push_back(pair (_post_order_list[i]->parent(), _post_order_list[i])); + } + for (int i = 0; i < _post_order_list.size(); ++i) { + for (int j = 0; j < _post_order_list[i]->degree(); ++j) { + if (_post_order_list[i]->adj(j)->post_order_index() > i) break; + if (_post_order_list[i]->adj(j)->get_1st_label() == i) continue; + _back_edge_list.push_back(pair (_post_order_list[i], _post_order_list[i]->adj(j))); + _is_back_edge_eliminate.push_back(false); + } + } + for (int i = 0; i < _post_order_list.size(); ++i) { + _post_order_list[i]->set_1st_label(INT_MAX); + } +} + +//The main part of the whole algorithm: Back-edge-traversal +void +maximal_planar_subgraph_finder::back_edge_traversal() { + node* i_node = 0; + node* current_node = 0; + for (int i = 0; i < _back_edge_list.size(); ++i) { + current_node = _back_edge_list[i].second; + i_node = _back_edge_list[i].first; + if (!back_edge_traversal(current_node, i_node->post_order_index())) _is_back_edge_eliminate[i] = true; + } +} + +//sub-function for the for-loop of back_edge_traversal(). +bool maximal_planar_subgraph_finder::back_edge_traversal(node* traverse_node, int index) { + node* parent_node; //The next node to traverse. + //If the node has been deleted. + if (traverse_node == 0 || traverse_node->get_2nd_label() == DELETED) { + return false; + } + //We have reached the i-node, stop. + if (traverse_node->post_order_index() == index) { + return true; + } + //Case 1 + if (traverse_node->get_2nd_label() == NOT_VISITED) { + //1.1 + if (traverse_node->get_1st_label() == INT_MAX) { + traverse_node->set_1st_label(index); + parent_node = traverse_node->parent(); + } + //1.2 + else if (traverse_node->get_1st_label() == index) { + return true; + } + //1.3 + else if (traverse_node->get_1st_label() < index) { + parent_node = construct(traverse_node); + traverse_node->set_1st_label(index); + } + } + //Case 2: Find the top-tier c-node. + else { + node* my_c_node = find(traverse_node); + make_essential(traverse_node, my_c_node); + //2.1 + if (my_c_node->get_1st_label() == index) { + parent_node = my_c_node; + } + //2.2 + else if (my_c_node->get_1st_label() < index) { + node* my_c_node_2 = construct(my_c_node, traverse_node); + parent_node = my_c_node_2; + } + traverse_node->set_1st_label(index); + traverse_node->set_2nd_label(NOT_VISITED); + } + if (back_edge_traversal(parent_node, index)) { + if (parent_node != _post_order_list[index]) parent_node->add_child(traverse_node); + return true; + } + else { + eliminate(traverse_node); + return false; + } +} + +//The p_node is originally a normal node in c_node's boundary cycle. +//Now we transfer it to be an essential node by the following steps: +//1. Create a replica-node of p_node to be representative of p_node in c_node. +//2. Take out the p_node from c_node, and then set the parent of p_node to be c_node. +//Note: We are not adding p_node to the c_node's children-list. +void +maximal_planar_subgraph_finder::make_essential(node* p_node, node* c_node) { + node* sentinel = get_new_node(REPLICA_NODE); + node* n0 = p_node->neighbor(0); + node* n1 = p_node->neighbor(1); + sentinel->init_replica(p_node, c_node); + c_node->add_essential(sentinel); + sentinel->set_to_boundary_path(n0, n1); + sentinel->inherit_AE(p_node); + n0->set_neighbor(n0->get_next(p_node), sentinel); + n1->set_neighbor(n1->get_next(p_node), sentinel); + p_node->set_neighbor((node*)0, (node*)0); + p_node->set_parent(c_node); +} + +//Find the top-tier c-node of the input node. +//Note: We don't set the input node to be essential node of the top-tier c-node. +//When terminated, the input node will be in the boundary cycle of top-tier c-node. +node* maximal_planar_subgraph_finder::find(node* n) { + pair, pair > boundary; + node* c_node_new = 0; + int c_node_size = 0; + node* return_node = 0; + if (n->parent() == 0) { + //If n is already a node in boundary cycle. + //Note: n must not be an essential node, otherwise it will never enter the function. + //Find the first(nearest to n) essential node. + boundary.first = parallel_search_sentinel(n, c_node_new); + } + else { + //If n is not a node in boundary cycle. + //It is in an Artificial edge. + //Trim it. + boundary = trim(n); + //Find the first(nearest to n) essential node. + boundary.first = parallel_search_sentinel(boundary.first.first, boundary.first.second, boundary.second.first, boundary.second.second, c_node_new); + } + //Find the c-node in the current hierachy . + //If it is top-tier, return it. + if (c_node_new != 0) return c_node_new; + c_node_new = (boundary.first).first->get_c_node(); + + //If not, find the two nearest essential node, eliminate the rest nodes. + c_node_size = c_node_new->c_node_size(); + boundary.second = count_sentinel_elimination(boundary.first, c_node_size); + //Go to the higher hierachy, and continue to find. + if (c_node_new->get_2nd_label() == ARTIFICIAL_EDGE) { + //A peculiar technic: + //Remove all the children of c_node_new but the one that should remains(Let it be u). + //Remove all other essential nodes, pretend to be a c-node of size equals 2. + //Call find(u). + node* u = 0; + for (int i = 0; i < c_node_new->child_num(); ++i) { + if (node::is_same(boundary.first.first, c_node_new->child(i)) || node::is_same(boundary.second.first, c_node_new->child(i))) { + u = c_node_new->child(i); + } + else eliminate(c_node_new->child(i)); + } + c_node_new->clear_children(); + c_node_new->add_child(u); + c_node_new->clear_essential(); + c_node_new->add_essential(boundary.first.first); + c_node_new->add_essential(boundary.second.first); + return_node = find(u); + } + else return_node = find(c_node_new); + //Merge the part of boundary cycle remains in current hierachy to the top-tier c-node. + merge(boundary, c_node_new); + return return_node; +} + +//The list_node is a c-node. +//The boundary indicates the part of the boundary cycle of c-node needs to be merge to the higher hierachy. +//Replace the list_node by boundary. +//Set list_node to be DELETED. +//Note: We do not eliminate anything in this function. +void +maximal_planar_subgraph_finder::merge(pair, pair > boundary, node* list_node) { + node* n0 = list_node->neighbor(0); + node* n1 = list_node->neighbor(1); + node* s0, * s0_prev; + node* s1, * s1_prev; + if (node::is_same(boundary.first.first, n0)) { + s0 = boundary.first.first; + s0_prev = boundary.first.second; + s1 = boundary.second.first; + s1_prev = boundary.second.second; + } + else { + s0 = boundary.second.first; + s0_prev = boundary.second.second; + s1 = boundary.first.first; + s1_prev = boundary.first.second; + } + if (s0_prev == s1 && s1_prev == s0) { + n0->set_neighbor(n0->get_next(list_node), n1); + n1->set_neighbor(n1->get_next(list_node), n0); + } + else { + n0->set_neighbor(n0->get_next(list_node), s0_prev); + n1->set_neighbor(n1->get_next(list_node), s1_prev); + s0_prev->set_neighbor(s0_prev->get_next(s0), n0); + s1_prev->set_neighbor(s1_prev->get_next(s1), n1); + } + //Inherit AE. + n0->inherit_AE(s0); + n1->inherit_AE(s1); + //Delete c-node + list_node->set_2nd_label(DELETED); +} + +//Set u and its subtree to be DELETED. +//If u has some AE, eliminate them. +//We don't do anything about u's parent, neighborhood.(Only children are affected.) +//If u is a p-node, we don't eliminate anything in the lower hierachy that corresponds to the same p-node. +//If u is a c-node, we eliminate all nodes in u's boundary cycle. +void +maximal_planar_subgraph_finder::eliminate(node* u) { + if (u->get_2nd_label() == DELETED) return; + u->set_2nd_label(DELETED); + if (u->type() == C_NODE) { + node* list_node = u->get_a_list_node(); + node* n0, * n0_prev;; + node* temp = 0; + n0 = list_node; + n0_prev = list_node->neighbor(0); + while (true) { + eliminate(n0); + temp = n0; + n0 = n0->get_next(n0_prev); + n0_prev = temp; + if (n0 == list_node) break; + } + } + else if (u->type() == P_NODE) { + for (int i = 0; i < u->degree(); ++i) { + if (u->adj(i)->post_order_index() < u->post_order_index() && u->adj(i)->get_1st_label() == INT_MAX) eliminate(u->adj(i)); + } + } + if (u->AE(0) != 0) eliminate(u->AE(0)); + if (u->AE(1) != 0) eliminate(u->AE(1)); + for (int i = 0; i < u->child_num(); ++i) { + eliminate(u->child(i)); + } +} + +//Eliminate the AE of(u,v)-link that points to u.(If exists) +void +maximal_planar_subgraph_finder::eliminate_AE(node* u, node* v) { + int v_index = v->post_order_index(); + if (u->AE(0) != 0 && u->AE(0)->get_1st_label() == v_index) { + eliminate (u->AE(0)); + u->set_AE(0, 0); + } + if (u->AE(1) != 0 && u->AE(1)->get_1st_label() == v_index) { + eliminate (u->AE(1)); + u->set_AE(1, 0); + } +} + +//The input node u must not be c-node. +//The traversed node is in the AE = (up <- down). +//The returned boundary = [up, up_prev ..., down_prev, down]. +//Direction: up it higher than down. +pair, pair > +maximal_planar_subgraph_finder::trim(node* u) { + node* up = 0; + node* down = 0; + //Since we may do c-node extension in the future, we need to memorize next in order to deduce prev. + node* up_next = 0; + node* down_next = 0; + node* new_AE_root = 0; + //The index from small to large indicates the path that we traversed, note that u = node_list[0]. + vector node_list; + node* curr = u; + node_list.push_back(u); + //Traverse upward. + while (true) { + curr = curr->parent(); + if (curr->type() == AE_VIRTUAL_ROOT) { + up = curr->parent(); + //case 1: We are in a newly created c-node. + //It has only one AE, and the two neighbor-pointer point to the same one. + if (up->neighbor(0) == up->neighbor(1)) { + down = up->neighbor(0); + up->set_neighbor(down, node_list[node_list.size()-1]); + down->set_neighbor(up, node_list[0]); + curr->remove_child(node_list[node_list.size()-1]); + //There's no other child, just delete the AE. + if (curr->child_num() == 0) { + up->set_AE(0, 0); + up->set_AE(1, 0); + } + } + //case 2: General case. + else { + if (up->neighbor(0)->post_order_index() == curr->get_1st_label()) down = up->neighbor(0); + else down = up->neighbor(1); + up->set_neighbor(up->get_next(down), node_list[node_list.size()-1]); + down->set_neighbor(down->get_next(up), node_list[0]); + curr->remove_child(node_list[node_list.size()-1]); + eliminate_AE(up, down); + } + break; + } + node_list.push_back(curr); + } + //Set the "downward" AE of node_list[0]. + new_AE_root = get_new_node(AE_VIRTUAL_ROOT); + new_AE_root->init_AE(node_list[0]); + //Eliminate the children other than the path. + for (int i = 1; i < node_list.size(); ++i) { + for (int j = 0; j < node_list[i]->child_num(); ++j) { + if (node_list[i]->child(j) != node_list[i-1]) eliminate(node_list[i]->child(j)); + } + } + //Set to the boundary path. + if (node_list.size() == 1) node_list[0]->set_to_boundary_path(up, down); + else { + node_list[0]->set_to_boundary_path(down, node_list[1]); + node_list[node_list.size()-1]->set_to_boundary_path(up, node_list[node_list.size()-2]); + for (int i = 1; i < node_list.size()-1; ++i) { + node_list[i]->set_to_boundary_path(node_list[i-1], node_list[i+1]); + } + } + //Set the next of up and down. + up_next = up->get_next(node_list[node_list.size()-1]); + down_next = down->get_next(node_list[0]); + //Unfold the c-nodes in the node_list. + for (int i = 0; i < node_list.size(); ++i) { + if (node_list[i]->type() == C_NODE) c_node_extension(node_list[i]); + } + //Return the new boundary. + return pair, pair > (pair(up, up->get_next(up_next)), pair(down, down->get_next(down_next))); +} + +//The trim's sub-function. +//The input c-node does not contain any children nor parent, but it has two neighbors, which is originally c-node's parent and one child. +//If c-node's size equals 2, then we don't need to unfold it. +//Otherwise, it must has size equals 3. +//And then we find that redundent essential node, and remove the nodes that need not remains. +//Merge the remain part to the higher hierachy. +void +maximal_planar_subgraph_finder::c_node_extension(node* c_node) { + //size == 2 + if (c_node->c_node_size() == 2) return; + //size == 3 + node* sentinel = 0; + for (int i = 0; i < c_node->c_node_size(); ++i) { + if (!node::is_same(c_node->essential(i), c_node->neighbor(0)) && !node::is_same(c_node->essential(i), c_node->neighbor(1))) { + sentinel = c_node->essential(i); + break; + } + } + eliminate(sentinel); + //The two other essential nodes and their subsequent neighbor. + pair sentinel_0; + pair sentinel_1; + node* n0, * n0_prev = sentinel; + node* n1, * n1_prev = sentinel; + node* temp = 0; + n0 = sentinel->neighbor(0); + n1 = sentinel->neighbor(1); + while (true) {//Toward the direction of n0. + if (n0->is_sentinel()) {//If we meet a essential node, stop, don't remove it. + sentinel_0 = pair (n0, n0->get_next(n0_prev)); + break; + } + eliminate(n0); + temp = n0; + n0 = n0->get_next(n0_prev); + n0_prev = temp; + } + while (true) {//Toward the direction of n0. + if (n1->is_sentinel()) {//If we meet a essential node, stop, don't remove it. + sentinel_1 = pair (n1, n1->get_next(n1_prev)); + break; + } + eliminate(n1); + temp = n1; + n1 = n1->get_next(n1_prev); + n1_prev = temp; + } + + //Remember to remove the AE toward two essential nodes that is in the delete region. + eliminate_AE(sentinel_0.first, sentinel_0.first->get_next(sentinel_0.second)); + eliminate_AE(sentinel_1.first, sentinel_1.first->get_next(sentinel_1.second)); + //Reset the neighborhood of two essential nodes. + sentinel_0.first->set_neighbor(sentinel_1.first, sentinel_0.second); + sentinel_1.first->set_neighbor(sentinel_0.first, sentinel_1.second); + //Merge to upper boundary cycle. + merge(pair, pair >(sentinel_0, sentinel_1), c_node); +} + +//u is a normal p-node. +//We'll do the work of elimination, and renewing of children-list. +void +maximal_planar_subgraph_finder::recursively_shaving(node* u) { + node* parent_node = 0; + node* node_x = 0; + pair new_two_child; + vector new_child_list; + //p-node + if (u->type() == P_NODE) { + for (int i = 0; i < u->child_num(); ++i) recursively_shaving(u->child(i)); + } + //c-node + else { + //We don't need to shave if u has only one child. + if (u->child_num() == 1) { + recursively_shaving(u->child(0)); + return; + } + //More than one child. + parent_node = u->parent(); + //Find node_x, and shave it. + for (int i = 0; i < u->c_node_size(); ++i) { + if (node::is_same(u->essential(i), parent_node)) { + node_x = u->essential(i); + new_two_child = shave(node_x); + break; + } + } + //Reset children-list and essential node. + for (int i = 0; i < u->child_num(); ++i) { + if (node::is_same(u->child(i), new_two_child.first) || node::is_same(u->child(i), new_two_child.second)) new_child_list.push_back(u->child(i)); + else eliminate(u->child(i)); + } + u->clear_children(); + u->clear_essential(); + u->add_essential(node_x); + u->add_essential(new_two_child.first); + u->add_essential(new_two_child.second); + u->add_child(new_child_list[0]); + u->add_child(new_child_list[1]); + for (int i = 0; i < u->child_num(); ++i) recursively_shaving(u->child(i)); + } +} + +//In this function, we only deal with inner part of c-node. +//x,y,z are essential nodes, let y,z be x's nearest essential nodes in w's boundary cycle. +//Anything outside [x,y], and[x,z] will be eliminated. +//Definition of y_prev, z_prev: ..., y, y_prev, ..., x, ..., z_prev, z, ... +//Return pair = (y,z). Note: What we return is the replica-node in the inner part of c-node. +//The work of deleting children will be done by recursively_shaving(). +pair +maximal_planar_subgraph_finder::shave(node* x) { + //c-node. + node* c_node = x->get_c_node(); + //No need to shave if child_num == 1. + if (c_node->child_num() == 1) return pair((node*)0, (node*)0); + //sentinel_1 = (y, y_prev). Note: At this time, c-node must has type equals ARTIFICIAL_EDGE, so no problem here. + pair sentinel_1 = parallel_search_sentinel(x, c_node); + //sentinel_2 = (z, z_prev). Same as above. + pair sentinel_2 = count_sentinel_elimination(sentinel_1, c_node->child_num()); + return pair(sentinel_1.first, sentinel_2.first); +} + +//Use parallel_search to find essential nodes. Return (essential nodes that we find, its prev). +//x is not in the searching region. +//If the c-node found is top-tier, then set all the nodes during searching a pointer to c-node, set c to be that c-node, and return pair be all null. +pair +maximal_planar_subgraph_finder::parallel_search_sentinel(node* x, node* &c) { + node* n0, * n0_prev = x; + node* n1, * n1_prev = x; + n0 = x->neighbor(0); + n1 = x->neighbor(1); + return parallel_search_sentinel(n0, n0_prev, n1, n1_prev, c); +} + +//Another version of parallel search: n0, n0_prev, ..., n1_prev, n1 +//searching region = (...n0] [n1...). Find the nearest essential node. +//return (essential nodes that we find, its prev). +pair +maximal_planar_subgraph_finder::parallel_search_sentinel(node* n0, node* n0_prev, node* n1, node* n1_prev, node* & c) { + node* temp = 0; + vector traversed; + while (true) { + //If c-node is top-tier. + //note: If c points to a c-node traversed in some previous iteration, then it must not be top-tier, so it'll not pass the if-condition. + if (n0->get_c_node() != 0 && n0->get_c_node()->get_2nd_label() == NOT_VISITED) { + c = n0->get_c_node(); + break; + } + if (n1->get_c_node() != 0 && n1->get_c_node()->get_2nd_label() == NOT_VISITED) { + c = n1->get_c_node(); + break; + } + //If an essential-node found. + if (n0->is_sentinel()) return pair(n0, n0_prev); + if (n1->is_sentinel()) return pair(n1, n1_prev); + //Just a normal node.. + traversed.push_back(n0); + traversed.push_back(n1); + temp = n0; + n0 = n0->get_next(n0_prev); + n0_prev = temp; + temp = n1; + n1 = n1->get_next(n1_prev); + n1_prev = temp; + } + + //If the c-node found is top-tier, then assign all the traversed node a pointer to c-node. + for (int i = 0; i < traversed.size(); ++i) traversed[i]->set_c_node(c); + return pair((node*)0, (node*)0); +} + +// sentinel_1= (y, y_prev) +// return pair = (z, z_prev) +// ..., y_prev, y,[ ...(contains num_sentinel-2 essential nodes)...], z, z_prev, ... : eliminate the [...] part. +// Which means, num_sentinel = Number of essential nodes in the region [y, z]. +// Note: y, z(Of course, and their prev,) will not be eliminated. +// Note: All the node that correspond to the same one as deleted node in higher hierachy will not be affected. +// The boundary cycle of c-node will be re-connected, AE be properly handled. +// Do nothing outside the c-node. +pair maximal_planar_subgraph_finder::count_sentinel_elimination(pair sentinel_1, int num_sentinel) { + pair sentinel_2; //(z, z_prev) + int count = 1;//Count the essential nodes traversed. + node* n0 = sentinel_1.first->get_next(sentinel_1.second), * n0_prev = sentinel_1.first;//Going one step further. + node* temp = 0; + while (true) { + if (n0->is_sentinel()) { + ++count;//counter + if (count == num_sentinel) {//We have reached y. Note: We will not eleminate y. + sentinel_2.first = n0; + sentinel_2.second = n0->get_next(n0_prev); + break; + } + } + eliminate(n0); + temp = n0; + n0 = n0->get_next(n0_prev); + n0_prev = temp; + } + //Remember to eliminate AE toward two essential nodes that is in the deleted region. + eliminate_AE(sentinel_2.first, sentinel_2.first->get_next(sentinel_2.second)); + eliminate_AE(sentinel_1.first, sentinel_1.first->get_next(sentinel_1.second)); + //Reset neighborhood of two essential nodes. + sentinel_2.first->set_neighbor(sentinel_1.first, sentinel_2.second); + sentinel_1.first->set_neighbor(sentinel_2.first, sentinel_1.second); + return sentinel_2; +} + +//Used when u has label equals , i c-node -> node_i). +//We don't create child-link here. +//Default label of newly contructed c-node is (INT_MAX, NOT_VISITED). +node* +maximal_planar_subgraph_finder::construct(node* u) { + //Basic works. + int i_label = u->get_1st_label(); + node* node_i = _post_order_list[u->get_1st_label()]; + parenting_labeling_shaving(u, node_i); + + //Get some new nodes. + node* i_sentinel = get_new_node(REPLICA_NODE); + node* u_sentinel = get_new_node(REPLICA_NODE); + node* new_c_node = get_new_node(C_NODE); + node* new_AE_root = get_new_node(AE_VIRTUAL_ROOT); + + //Setting of replica-nodes. + i_sentinel->init_replica(node_i, new_c_node); + u_sentinel->init_replica(u, new_c_node); + for (int i = 0; i < u->child_num(); ++i) { + u_sentinel->add_child(u->child(i)); + } + new_AE_root->init_AE(u_sentinel); + + //Neighborhood setting of replica-nodes in c-node. + i_sentinel->set_to_boundary_path(u_sentinel, u_sentinel); + u_sentinel->set_to_boundary_path(i_sentinel, i_sentinel); + + //Default label of c-node. + new_c_node->set_1st_label(INT_MAX); + new_c_node->set_2nd_label(NOT_VISITED); + + //Set essential node of c-node. + new_c_node->add_essential(i_sentinel); + new_c_node->add_essential(u_sentinel); + + //Parenting + new_c_node->set_parent(node_i); + u->set_parent(new_c_node); + + //Clear children-list of u_node. (which has benn transfered to AE inside c-node.) + u->clear_children(); + + return new_c_node; + } + +//The case when the first explored node is c-node (The input parameter c). +//The p-node that trigger c(The input parameter p), has p->c parent-link already, and p is essential(not essential before triggered). +//But we don't have c->p child-link yet. +//We are not going to establish that child-link in this function. (Will be done in BET's main loop.) +//Set c to be DELETED. +node* +maximal_planar_subgraph_finder::construct(node* c, node* p) { + //Basic works. + int i_label = c->get_1st_label(); + node* node_i = _post_order_list[c->get_1st_label()]; + parenting_labeling_shaving(p, node_i); + //note: Now, c must have exactly two children left, and c has a parent-link to p, p has achild link to c, too. + //Remember to handle them later. + + //Get some new nodes. + node* i_sentinel = get_new_node(REPLICA_NODE); + node* new_c_node = get_new_node(C_NODE); + i_sentinel->init_replica(node_i, new_c_node); + + //Strategy: Build thisboundary cycle first: (i, child(0), c, child(1), i). + //And then find the two replica-node corresponding to the two children in c, and merge. + node* ch0 = c->child(0); + node* ch1 = c->child(1); + node* AE_root_0 = get_new_node(AE_VIRTUAL_ROOT); + node* AE_root_1 = get_new_node(AE_VIRTUAL_ROOT); + AE_root_0->init_AE(ch0); + AE_root_1->init_AE(ch1); + i_sentinel->set_to_boundary_path(ch0, ch1); + ch0->set_to_boundary_path(i_sentinel, c); + ch1->set_to_boundary_path(i_sentinel, c); + c->set_to_boundary_path(ch0, ch1); + + //find the boundary in c, merge! + node* sent_0; + node* sent_1; + node* sent_p; + for (int i = 0; i < c->c_node_size(); ++i) { + if (node::is_same(c->essential(i), ch0)) sent_0 = c->essential(i); + else if (node::is_same(c->essential(i), ch1)) sent_1 = c->essential(i); + else if (node::is_same(c->essential(i), p)) sent_p = c->essential(i); + } + merge(pair, pair > (pair(sent_0, sent_0->get_next(sent_1)), pair(sent_1, sent_1->get_next(sent_0))), c); + + //Set essential-node of c-node. + new_c_node->add_essential(i_sentinel); + new_c_node->add_essential(sent_p); + + //Default label of c-node. + new_c_node->set_1st_label(INT_MAX); + new_c_node->set_2nd_label(NOT_VISITED); + + //Parenting. + new_c_node->set_parent(node_i); + + //p-node, p_sent. + sent_p->set_c_node(new_c_node); + p->clear_children(); + p->set_parent(new_c_node); + + //Delete c-node + c->set_2nd_label(DELETED); + + return new_c_node; +} + +//Some basic works in constructing c-node. +//u is the first explored node in the newly constructed c-node. +//In the case of newly constructed c-node itself is c-node, u will be the p-node that trigger the c-node. +//And in this case, p->c parent-link has been established, but c->p child-link not. +void +maximal_planar_subgraph_finder::parenting_labeling_shaving(node* u, node* node_i) { + //reverse parent-children relation in [u, node_i] as following. + //(u-> ... ->y->i) -> (u<- ... <-y , i). + vector u_i_path; + u_i_path.push_back(u); + while (true) { + u_i_path.push_back(u_i_path[u_i_path.size()-1]->parent()); + if (u_i_path[u_i_path.size()-1] == node_i) break; + } + for (int i = 0; i < u_i_path.size()-2; ++i) { + u_i_path[i]->add_child(u_i_path[i+1]); + u_i_path[i+1]->set_parent(u_i_path[i]); + } + for (int i = 0; i < u_i_path.size()-2; ++i) { + for (int j = 0; j < u_i_path[i+1]->child_num(); ++j) { + if (u_i_path[i+1]->child(j) == u_i_path[i]) { + u_i_path[i+1]->remove_child(j); + } + } + } + u_i_path[0]->set_parent(0); + + //BFS-traversal, all labeled to , and then shave the c-node. + u->recursively_labeling(); + recursively_shaving(u); +} diff --git a/deferred_planarity_test/mps.h b/deferred_planarity_test/mps.h new file mode 100644 index 0000000..1e9ac47 --- /dev/null +++ b/deferred_planarity_test/mps.h @@ -0,0 +1,179 @@ +//----------------------------------------------------------------------------------- +// Header for modules: mps.cpp, mps_test.cpp, node.cpp. +//----------------------------------------------------------------------------------- + +#ifndef _MPS_H +#define _MPS_H + +#include +#include +#include +#include +#include + +using namespace std; + +class node; +class maximal_planar_subgraph_finder; + +enum label { + NOT_VISITED = 0, + ARTIFICIAL_EDGE = 1, + BOUNDARY_PATH = 2, + DELETED = 3 +}; + +enum node_type { + P_NODE = 0, + C_NODE = 1, + REPLICA_NODE = 2, + AE_VIRTUAL_ROOT = 3 +}; + +class node +{ +public: + //CONSTRUCTOR + node(node_type t); + + //DESTRUCTOR + ~node() {} + + //TYPE, ID, INDEX + node_type type(); + int post_order_index(); + void set_id(int i); + void set_post_order_index(int i); + void recursively_labeling(); + int node_id(); + + //DFS-TREE + void add_adj(node* n); + int degree(); + node* adj(int i); + void set_adj_list(vector vec); + void DFS_visit(vector &dfsList, int &index); + + //PARENT-CHILDREN + void set_parent(node* n) ; + node* parent(); + int child_num(); + node* child(int i); + void add_child(node* n); + void clear_children(); + void remove_child(int i); + void remove_child(node* n); + vector* get_children_list(); + + //BOUNDARY_PATH + void set_to_boundary_path(node* n0, node* n1); + void set_neighbor(int i, node* n); + void set_neighbor(node* u, node* v); + node* neighbor(int i); + node* get_next(node* prev); + + //ARTIFICIAL EDGE + node* AE(int i); + void set_AE(int i, node* j); + void add_AE(node* j); + void inherit_AE(node* u); + void init_AE(node* u); + + //REPLICA + node* original_node(); + node* get_c_node(); + void set_c_node(node* c); + bool is_sentinel(); + static bool is_same(node* n1, node* n2); + void init_replica(node* u, node* c); + + //LABELING + void set_1st_label(int i); + void set_2nd_label(label i); + int get_1st_label(); + label get_2nd_label(); + + //C-NODE + node* get_a_list_node(); + int c_node_size(); + node* essential(int i); + void clear_essential(); + void add_essential(node* u); + + //MARK + void mark(); + static void init_mark(); + void un_mark(); + bool is_marked(); + +private: + //Basic information. + node_type _type; + pair _label; + + //Information about neighborhood. + node* _neighbor[2]; + node* _AE_root[2]; + + //Information about higher hierarchy. + node* _original_node; + node* _c_node; + + //Information about parent-children relation. + node* _parent; + vector _children; + + //Information about about p-nodes in DFS-tree + vector _adj_list; + int _post_order_index; + int _node_id; + + //List of essential nodes in c-node + vector _essential_list; + + //Mark + int _mark; + static int _ref_mark; +}; + +class maximal_planar_subgraph_finder +{ +public: + maximal_planar_subgraph_finder(); + ~maximal_planar_subgraph_finder(); + void find_mps(ifstream* in, ofstream* out); + node* get_new_node(node_type t); + void read_from_file(ifstream* in); + void output(ofstream* out); + void output_deleted_edges(ofstream* out); + void postOrderTraversal(); + void sort_adj_list(); + void determine_edges(); + void back_edge_traversal(); + bool back_edge_traversal(node* traverse_node, int index); + void make_essential(node* p_node, node* c_node); + node* find(node* n); + void merge(pair, pair > boundary, node* list_node); + void eliminate(node* u); + void eliminate_AE(node* u, node* v); + pair, pair > trim(node* u); + void c_node_extension(node* c_node); + void recursively_shaving(node* u); + pair shave(node* x); + pair parallel_search_sentinel(node* x, node* &c); + pair parallel_search_sentinel(node* n0, node* n0_prev, node* n1, node* n1_prev, node* & c); + pair count_sentinel_elimination(pair sentinel_1, int num_sentinel); + node* construct(node* u); + node* construct(node* c, node* p); + void parenting_labeling_shaving(node* u, node* node_i) ; + +private: + vector _node_list; //List of nodes input. + vector > _edge_list; // Edges in DFS-tree. These edges must be contained in the maximal planar subgraph that we found. + vector _post_order_list; //The sorted version (increasing with post-order-index) of _node_list. + vector > _back_edge_list; // Edges other than that in DFS-tree. (The first node's index is higher than the second's.) + vector _is_back_edge_eliminate; //Record that if the back-edge has been eliminated or not. + vector _new_node_list; //Newly added nodes. +}; + +#endif diff --git a/deferred_planarity_test/mps_test.cpp b/deferred_planarity_test/mps_test.cpp new file mode 100644 index 0000000..4b1a3f2 --- /dev/null +++ b/deferred_planarity_test/mps_test.cpp @@ -0,0 +1,64 @@ +//----------------------------------------------------------------------------------- +// Implementation of a MPS algorithm via PC-tree. +//----------------------------------------------------------------------------------- + +#include "mps.h" + +//----------------------------------------------------------------------------------- +// Finding MPS +//----------------------------------------------------------------------------------- +void find_mps(ifstream* in, ofstream* out) { + maximal_planar_subgraph_finder m; + m.find_mps(in, out); +} + +void maximal_planar_subgraph_finder::find_mps(ifstream* in, ofstream* out) { + read_from_file(in); + postOrderTraversal(); + sort_adj_list(); + determine_edges(); + back_edge_traversal(); + output(out); +} + +//----------------------------------------------------------------------------------- +// Imput, output +//----------------------------------------------------------------------------------- +//First line: a single integer indicates the number of nodes. +//The rest: a pair of integers (i, j) represents an edge (i, j) +//0 <= i, j < n-1, where n is number of nodes. +void maximal_planar_subgraph_finder::read_from_file(ifstream* in) { + int node_num, n1, n2; + //Number of nodes. + (*in) >> node_num; + //initialize all the nodes. + for (int i = 0; i < node_num; ++i) { + _node_list.push_back(new node(P_NODE)); + _node_list[i]->set_id(i); + } + //Set the adj-list. + while ((*in) >> n1 >> n2) { + _node_list[n1]->add_adj(_node_list[n2]); + _node_list[n2]->add_adj(_node_list[n1]); + } +} + +//Output a maximal planar subgraph in the same format as input. +void maximal_planar_subgraph_finder::output(ofstream* out) { + (*out) << _node_list.size() << endl; + for (int i = 0; i < _edge_list.size(); ++i) { + (*out) << _edge_list[i].first->node_id() << " " << _edge_list[i].second->node_id() << endl; + } + for (int i = 0; i < _back_edge_list.size(); ++i) { + if (_is_back_edge_eliminate[i]) continue; + (*out) << _back_edge_list[i].first->node_id() << " " << _back_edge_list[i].second->node_id() << endl; + } +} + +void maximal_planar_subgraph_finder::output_deleted_edges(ofstream* out) { + (*out) << _node_list.size() << endl; + for (int i = 0; i < _back_edge_list.size(); ++i) { + if (!_is_back_edge_eliminate[i]) continue; + (*out) << _back_edge_list[i].first->node_id() << " " << _back_edge_list[i].second->node_id() << endl; + } +} diff --git a/deferred_planarity_test/node.cpp b/deferred_planarity_test/node.cpp new file mode 100644 index 0000000..8e36f4f --- /dev/null +++ b/deferred_planarity_test/node.cpp @@ -0,0 +1,241 @@ +//----------------------------------------------------------------------------------- +// Implementation of a MPS algorithm via PC-tree. +//----------------------------------------------------------------------------------- + +#include "mps.h" + +//----------------------------------------------------------------------------------- +// CONSTRUCTOR +//----------------------------------------------------------------------------------- +node::node(node_type t) { + _type = t; + _label = pair(INT_MAX, NOT_VISITED); + _neighbor[0] = _neighbor[1] = 0; + _AE_root[0] = _AE_root[1] = 0; + _original_node = 0; + _c_node = 0; + _parent = 0; + _post_order_index = INT_MAX; + _node_id = INT_MAX; + _mark = 0; +} + +//----------------------------------------------------------------------------------- +// TYPE, ID, INDEX +//----------------------------------------------------------------------------------- +node_type node::type() {return _type;} + +int node::post_order_index() {return _post_order_index;} + +void node::set_id(int i) {_node_id = i;} + +void node::set_post_order_index(int i) {_post_order_index = i;} + +//Only used when consturcting c-node +//The first node calling this function would not be labeled. +void node::recursively_labeling() { + for (int i = 0; i < _children.size(); ++i) { + _children[i]->_label.second = ARTIFICIAL_EDGE; + _children[i]->recursively_labeling(); + } +} + +int node::node_id() {return _node_id;} + +//----------------------------------------------------------------------------------- +// DFS-TREE +//----------------------------------------------------------------------------------- +void node::add_adj(node* n) {_adj_list.push_back(n);} + +int node::degree() {return _adj_list.size();} + +node* node::adj(int i) {return _adj_list[i];} + +void node::set_adj_list(vector vec) {_adj_list = vec;} + +void node::DFS_visit(vector &dfsList, int &index) { + mark(); + for (int i = 0; i < _adj_list.size(); ++i) { + if (!_adj_list[i]->is_marked()) { + _adj_list[i]->_parent = this; + _adj_list[i]->DFS_visit(dfsList, index); + } + } + set_post_order_index(index); + dfsList.push_back(this); + ++index; +} + +//----------------------------------------------------------------------------------- +// PARENT-CHILDREN +//----------------------------------------------------------------------------------- +int node::child_num() {return _children.size();} + +node* node::child(int i) {return _children[i];} + +node* node::parent() {return _parent;} + +void node::clear_children() { + _children.clear(); +} + +void node::remove_child(int i) { + _children[i] = _children[_children.size()-1]; + _children.resize(_children.size()-1); +} + +void node::remove_child(node* n) { + for (int i = 0; i < _children.size(); ++i) { + if (_children[i] == n) { + _children[i] = _children[_children.size()-1]; + _children.resize(_children.size()-1); + } + } +} + +void node::add_child(node* n) { + _children.push_back(n); +} + +vector* node::get_children_list() { + vector* ptr = new vector(_children); + return ptr; +} + +void node::set_parent(node* n) { + _parent= n; +} + +//----------------------------------------------------------------------------------- +// BOUNDARY_PATH +//----------------------------------------------------------------------------------- +void node::set_to_boundary_path(node* n0, node* n1) { + _parent = 0; + _children.clear(); + _neighbor[0] = n0; + _neighbor[1] = n1; + set_2nd_label(BOUNDARY_PATH); +} + +node* node::get_next(node* prev) { + if (_neighbor[0] != prev) return _neighbor[0]; + else return _neighbor[1]; +} + +node* node::neighbor(int i) {return _neighbor[i];} + +void node::set_neighbor(int i, node* n) {_neighbor[i] = n;} + +void node::set_neighbor(node* u, node* v) { + _neighbor[0] = u; + _neighbor[1] = v; +} + +//----------------------------------------------------------------------------------- +// ARTIFICIAL EDGE +//----------------------------------------------------------------------------------- +node* node::AE(int i) {return _AE_root[i];} + +void node::set_AE(int i, node* j) { + _AE_root[i] = j; + if (j != 0) j->set_parent(this); +} + +void node::add_AE(node* j) { + if (j == 0) return; + if (_AE_root[0] == 0) set_AE(0, j); + else if (_AE_root[1] == 0) set_AE(1, j); +} + +//Inherit u's artificial edge. +void node::inherit_AE(node* u) { + if (u->_AE_root[0] != 0) add_AE(u->_AE_root[0]); + if (u->_AE_root[1] != 0) add_AE(u->_AE_root[1]); + u->_AE_root[0] = u->_AE_root[1] = 0; +} + +//Set itself to be an AE-root-node in u. +//Inherite u's chilren-list. +//Do nothing if u does not have any children. +void node::init_AE(node* u) { + if (u->child_num() == 0) return; + _children = u->_children; + u->clear_children(); + for (int i = 0; i < _children.size(); ++i) { + _children[i]->set_parent(this); + } + set_parent(u); + set_1st_label(_children[0]->get_1st_label()); + set_2nd_label(ARTIFICIAL_EDGE); + u->add_AE(this); +} + +//----------------------------------------------------------------------------------- +// REPLICA +//----------------------------------------------------------------------------------- +node* node::original_node() {return _original_node;} + +node* node::get_c_node() {return _c_node;} + +void node::set_c_node(node* c) {_c_node = c;} + +bool node::is_sentinel() {return type() == REPLICA_NODE;} + +//Check if n1 and n2 correspond to the same node +bool node::is_same(node* n1, node* n2) { + node* s1 = (n1->type() == REPLICA_NODE)? n1->original_node() : n1; + node* s2 = (n2->type() == REPLICA_NODE)? n2->original_node() : n2; + return s1 == s2; +} + +//Set itself to be a replica-node of u in c. +//Only inherit some basic setting, not including info about neighborhood. +void node::init_replica(node* u, node* c) { + set_post_order_index(u->post_order_index()); + set_2nd_label(BOUNDARY_PATH); + _original_node = (u->type() == REPLICA_NODE)? u->original_node() : u; + _c_node = c; +} + +//----------------------------------------------------------------------------------- +// LABELING +//----------------------------------------------------------------------------------- +void node::set_1st_label(int i) {_label.first = i;} + +void node::set_2nd_label(label i) {_label.second = i;} + +int node::get_1st_label() {return _label.first;} + +label node::get_2nd_label() {return _label.second;} + +//----------------------------------------------------------------------------------- +// C-NODE +//----------------------------------------------------------------------------------- +node* node::get_a_list_node() { + return _essential_list[0]; +} + +int node::c_node_size() { + return _essential_list.size(); +} + +node* node::essential(int i) { + return _essential_list[i]; +} + +void node::clear_essential() {_essential_list.clear();} + +void node::add_essential(node* u) {_essential_list.push_back(u);} + +//----------------------------------------------------------------------------------- +// MARK +//----------------------------------------------------------------------------------- +void node::mark() {_mark = _ref_mark;} + +void node::init_mark() {++_ref_mark;} + +void node::un_mark() {_mark = 0;} + +bool node::is_marked() {return _mark == _ref_mark;} + +int node::_ref_mark = 1;