2020-03-14 17:18:06 -07:00
|
|
|
#pragma once
|
|
|
|
#include <algorithm>
|
|
|
|
#include <cstddef>
|
|
|
|
#include <queue>
|
|
|
|
#include <set>
|
|
|
|
#include <string>
|
|
|
|
#include <map>
|
|
|
|
#include <memory>
|
|
|
|
#include <vector>
|
|
|
|
#include <iostream>
|
|
|
|
|
|
|
|
using function = std::string;
|
|
|
|
|
|
|
|
struct group {
|
|
|
|
std::set<function> members;
|
|
|
|
};
|
|
|
|
|
|
|
|
using group_ptr = std::unique_ptr<group>;
|
|
|
|
|
|
|
|
class function_graph {
|
|
|
|
using group_id = size_t;
|
|
|
|
|
|
|
|
struct group_data {
|
|
|
|
std::set<function> functions;
|
|
|
|
std::set<group_id> adjacency_list;
|
|
|
|
size_t indegree;
|
|
|
|
};
|
|
|
|
|
|
|
|
using data_ptr = std::shared_ptr<group_data>;
|
2020-03-14 21:04:13 -07:00
|
|
|
using edge = std::pair<function, function>;
|
2020-03-14 17:18:06 -07:00
|
|
|
using group_edge = std::pair<group_id, group_id>;
|
|
|
|
|
|
|
|
std::map<function, std::set<function>> adjacency_lists;
|
|
|
|
std::set<edge> edges;
|
|
|
|
|
|
|
|
std::set<edge> compute_transitive_edges();
|
|
|
|
void create_groups(
|
|
|
|
const std::set<edge>&,
|
|
|
|
std::map<function, group_id>&,
|
|
|
|
std::map<group_id, data_ptr>&);
|
|
|
|
void create_edges(
|
|
|
|
std::map<function, group_id>&,
|
|
|
|
std::map<group_id, data_ptr>&);
|
|
|
|
std::vector<group_ptr> generate_order(
|
|
|
|
std::map<function, group_id>&,
|
|
|
|
std::map<group_id, data_ptr>&);
|
|
|
|
|
|
|
|
public:
|
|
|
|
void add_edge(const function& from, const function& to);
|
|
|
|
std::vector<group_ptr> compute_order();
|
|
|
|
};
|
|
|
|
|
|
|
|
std::set<function_graph::edge> function_graph::compute_transitive_edges() {
|
|
|
|
std::set<edge> transitive_edges;
|
|
|
|
transitive_edges.insert(edges.begin(), edges.end());
|
|
|
|
for(auto& connector : adjacency_lists) {
|
|
|
|
for(auto& from : adjacency_lists) {
|
|
|
|
edge to_connector { from.first, connector.first };
|
|
|
|
for(auto& to : adjacency_lists) {
|
|
|
|
edge full_jump { from.first, to.first };
|
|
|
|
if(transitive_edges.find(full_jump) != transitive_edges.end()) continue;
|
|
|
|
|
|
|
|
edge from_connector { connector.first, to.first };
|
|
|
|
if(transitive_edges.find(to_connector) != transitive_edges.end() &&
|
|
|
|
transitive_edges.find(from_connector) != transitive_edges.end())
|
|
|
|
transitive_edges.insert(std::move(full_jump));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return transitive_edges;
|
|
|
|
}
|
|
|
|
|
|
|
|
void function_graph::create_groups(
|
|
|
|
const std::set<edge>& transitive_edges,
|
|
|
|
std::map<function, group_id>& group_ids,
|
|
|
|
std::map<group_id, data_ptr>& group_data_map) {
|
|
|
|
group_id id_counter = 0;
|
|
|
|
for(auto& vertex : adjacency_lists) {
|
|
|
|
if(group_ids.find(vertex.first) != group_ids.end())
|
|
|
|
continue;
|
|
|
|
data_ptr new_group(new group_data);
|
|
|
|
new_group->functions.insert(vertex.first);
|
|
|
|
group_data_map[id_counter] = new_group;
|
|
|
|
group_ids[vertex.first] = id_counter;
|
|
|
|
for(auto& other_vertex : adjacency_lists) {
|
|
|
|
if(transitive_edges.find({vertex.first, other_vertex.first}) != transitive_edges.end() &&
|
|
|
|
transitive_edges.find({other_vertex.first, vertex.first}) != transitive_edges.end()) {
|
|
|
|
group_ids[other_vertex.first] = id_counter;
|
|
|
|
new_group->functions.insert(other_vertex.first);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
id_counter++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void function_graph::create_edges(
|
|
|
|
std::map<function, group_id>& group_ids,
|
|
|
|
std::map<group_id, data_ptr>& group_data_map) {
|
|
|
|
std::set<std::pair<group_id, group_id>> group_edges;
|
|
|
|
for(auto& vertex : adjacency_lists) {
|
|
|
|
auto vertex_id = group_ids[vertex.first];
|
|
|
|
auto& vertex_data = group_data_map[vertex_id];
|
|
|
|
for(auto& other_vertex : vertex.second) {
|
|
|
|
auto other_id = group_ids[other_vertex];
|
|
|
|
if(vertex_id == other_id) continue;
|
|
|
|
if(group_edges.find({vertex_id, other_id}) != group_edges.end())
|
|
|
|
continue;
|
|
|
|
group_edges.insert({vertex_id, other_id});
|
|
|
|
vertex_data->adjacency_list.insert(other_id);
|
|
|
|
group_data_map[other_id]->indegree++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<group_ptr> function_graph::generate_order(
|
|
|
|
std::map<function, group_id>& group_ids,
|
|
|
|
std::map<group_id, data_ptr>& group_data_map) {
|
|
|
|
std::queue<group_id> id_queue;
|
|
|
|
std::vector<group_ptr> output;
|
|
|
|
for(auto& group : group_data_map) {
|
|
|
|
if(group.second->indegree == 0) id_queue.push(group.first);
|
|
|
|
}
|
|
|
|
|
|
|
|
while(!id_queue.empty()) {
|
|
|
|
auto new_id = id_queue.front();
|
|
|
|
auto& group_data = group_data_map[new_id];
|
|
|
|
group_ptr output_group(new group);
|
|
|
|
output_group->members = std::move(group_data->functions);
|
|
|
|
id_queue.pop();
|
|
|
|
|
|
|
|
for(auto& adjacent_group : group_data->adjacency_list) {
|
|
|
|
if(--group_data_map[adjacent_group]->indegree == 0)
|
|
|
|
id_queue.push(adjacent_group);
|
|
|
|
}
|
|
|
|
|
|
|
|
output.push_back(std::move(output_group));
|
|
|
|
}
|
|
|
|
|
|
|
|
return output;
|
|
|
|
}
|
|
|
|
|
|
|
|
void function_graph::add_edge(const function& from, const function& to) {
|
|
|
|
auto adjacency_list_it = adjacency_lists.find(from);
|
|
|
|
if(adjacency_list_it != adjacency_lists.end()) {
|
|
|
|
adjacency_list_it->second.insert(to);
|
|
|
|
} else {
|
|
|
|
adjacency_lists[from] = { to };
|
|
|
|
}
|
|
|
|
edges.insert({ from, to });
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<group_ptr> function_graph::compute_order() {
|
|
|
|
std::set<edge> transitive_edges = compute_transitive_edges();
|
|
|
|
std::map<function, group_id> group_ids;
|
|
|
|
std::map<group_id, data_ptr> group_data_map;
|
|
|
|
|
|
|
|
create_groups(transitive_edges, group_ids, group_data_map);
|
|
|
|
create_edges(group_ids, group_data_map);
|
|
|
|
return generate_order(group_ids, group_data_map);
|
|
|
|
}
|