static bool pruneForwardUseless(NGHolder &h, const nfag_t &g, NFAVertex s, vector<default_color_type> &vertexColor) { // Begin with all vertices set to white, as DFV only marks visited // vertices. fill(vertexColor.begin(), vertexColor.end(), boost::white_color); auto index_map = get(&NFAGraphVertexProps::index, g); depth_first_visit(g, s, make_dfs_visitor(boost::null_visitor()), make_iterator_property_map(vertexColor.begin(), index_map)); vector<NFAVertex> dead; // All non-special vertices that are still white can be removed. for (auto v : vertices_range(g)) { u32 idx = g[v].index; if (!is_special(v, g) && vertexColor[idx] == boost::white_color) { DEBUG_PRINTF("vertex %u is unreachable from %u\n", g[v].index, g[s].index); dead.push_back(v); } } if (dead.empty()) { return false; } DEBUG_PRINTF("removing %zu vertices\n", dead.size()); remove_vertices(dead, h, false); return true; }
// Remove boundary vertices (and faces that touch them) void erode(TriMesh *mesh) { int nv = mesh->vertices.size(); vector<bool> bdy(nv); for (int i = 0; i < nv; i++) bdy[i] = mesh->is_bdy(i); remove_vertices(mesh, bdy); }
// Clip mesh to the given bounding box void clip(TriMesh *mesh, const box &b) { int nv = mesh->vertices.size(); vector<bool> toremove(nv, false); for (int i = 0; i < nv; i++) if (mesh->vertices[i][0] < b.min[0] || mesh->vertices[i][0] > b.max[0] || mesh->vertices[i][1] < b.min[1] || mesh->vertices[i][1] > b.max[1] || mesh->vertices[i][2] < b.min[2] || mesh->vertices[i][2] > b.max[2]) toremove[i] = true; remove_vertices(mesh, toremove); }
/** Remove any vertices that can't be reached by traversing the graph in * reverse from acceptEod. */ void pruneUnreachable(NGHolder &g) { deque<NFAVertex> dead; if (!hasGreaterInDegree(1, g.acceptEod, g) && !hasGreaterInDegree(0, g.accept, g) && edge(g.accept, g.acceptEod, g).second) { // Trivial case: there are no in-edges to our accepts (other than // accept->acceptEod), so all non-specials are unreachable. for (auto v : vertices_range(g)) { if (!is_special(v, g)) { dead.push_back(v); } } } else { // Walk a reverse graph from acceptEod with Boost's depth_first_visit // call. typedef reverse_graph<NFAGraph, NFAGraph&> RevNFAGraph; RevNFAGraph revg(g.g); map<NFAVertex, default_color_type> colours; depth_first_visit(revg, g.acceptEod, make_dfs_visitor(boost::null_visitor()), make_assoc_property_map(colours)); DEBUG_PRINTF("color map has %zu entries after DFV\n", colours.size()); // All non-special vertices that aren't in the colour map (because they // weren't reached) can be removed. for (auto v : vertices_range(revg)) { if (is_special(v, revg)) { continue; } if (!contains(colours, v)) { dead.push_back(v); } } } if (dead.empty()) { DEBUG_PRINTF("no unreachable vertices\n"); return; } remove_vertices(dead, g, false); DEBUG_PRINTF("removed %zu unreachable vertices\n", dead.size()); }
/** This code removes any vertices which do not accept any symbols. Any * vertices which no longer lie on a path from a start to an accept are also * pruned. */ void pruneEmptyVertices(NGHolder &g) { DEBUG_PRINTF("pruning empty vertices\n"); vector<NFAVertex> dead; for (auto v : vertices_range(g)) { if (is_special(v, g)) { continue; } const CharReach &cr = g[v].char_reach; if (cr.none()) { DEBUG_PRINTF("empty: %u\n", g[v].index); dead.push_back(v); } } if (dead.empty()) { return; } remove_vertices(dead, g); pruneUseless(g); }
// walk through vertices of an equivalence class and replace them with a single // vertex (or, in rare cases for left equiv, a pair if we cannot satisfy the // report behaviour with a single vertex). static bool mergeEquivalentClasses(vector<VertexInfoSet> &classes, ptr_vector<VertexInfo> &infos, NGHolder &g) { bool merged = false; set<NFAVertex> toRemove; // go through all classes and merge classes with more than one vertex for (unsigned eq_class = 0; eq_class < classes.size(); eq_class++) { // get all vertices in current equivalence class VertexInfoSet &cur_class_vertices = classes[eq_class]; // we don't care for single-vertex classes if (cur_class_vertices.size() > 1) { merged = true; mergeClass(infos, g, eq_class, cur_class_vertices, &toRemove); } } // remove all dead vertices DEBUG_PRINTF("removing %zd vertices.\n", toRemove.size()); remove_vertices(toRemove, g); return merged; }
static u32 findMaxInfixMatches(const NGHolder &h, const set<ue2_literal> &lits) { DEBUG_PRINTF("h=%p, %zu literals\n", &h, lits.size()); //dumpGraph("infix.dot", h.g); if (!onlyOneTop(h)) { DEBUG_PRINTF("more than one top!n"); return NO_MATCH_LIMIT; } // Indices of vertices that could terminate any of the literals in 'lits'. set<u32> terms; for (const auto &s : lits) { DEBUG_PRINTF("lit s='%s'\n", escapeString(s).c_str()); if (s.empty()) { // Likely an anchored case, be conservative here. return NO_MATCH_LIMIT; } for (auto v : vertices_range(h)) { if (is_special(v, h)) { continue; } if (couldEndLiteral(s, v, h)) { u32 idx = h[v].index; DEBUG_PRINTF("vertex %u could terminate lit\n", idx); terms.insert(idx); } } } if (terms.empty()) { DEBUG_PRINTF("literals cannot match inside infix\n"); return 0; } NGHolder g; cloneHolder(g, h); vector<NFAVertex> dead; // The set of all edges in the graph is used for existence checks in contractVertex. ue2::unordered_set<pair<NFAVertex, NFAVertex>> all_edges; for (const auto &e : edges_range(g)) { all_edges.emplace(source(e, g), target(e, g)); } for (auto v : vertices_range(g)) { if (is_special(v, g)) { continue; } if (contains(terms, g[v].index)) { continue; } contractVertex(g, v, all_edges); dead.push_back(v); } remove_vertices(dead, g); //dumpGraph("relaxed.dot", g.g); depth maxWidth = findMaxWidth(g); DEBUG_PRINTF("maxWidth=%s\n", maxWidth.str().c_str()); assert(maxWidth.is_reachable()); if (maxWidth.is_infinite()) { // Cycle detected, so we can likely squeeze an unlimited number of // matches into this graph. return NO_MATCH_LIMIT; } assert(terms.size() >= maxWidth); return maxWidth; }