/* * Copyright (c) 2015-2017, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Intel Corporation nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** \file * \brief Loose equality testing for NGHolder graphs. * * Loose equality check for holders' graph structure and vertex_index, * vertex_char_reach and (optionally reports). */ #include "ng_is_equal.h" #include "grey.h" #include "ng_holder.h" #include "ng_util.h" #include "ue2common.h" #include "util/container.h" #include "util/flat_containers.h" #include "util/graph_range.h" using namespace std; namespace ue2 { namespace { struct check_report { virtual ~check_report() {} virtual bool operator()(const flat_set &reports_a, const flat_set &reports_b) const = 0; }; struct full_check_report : public check_report { bool operator()(const flat_set &reports_a, const flat_set &reports_b) const override { return reports_a == reports_b; } }; struct equiv_check_report : public check_report { equiv_check_report(ReportID a_in, ReportID b_in) : a_rep(a_in), b_rep(b_in) {} bool operator()(const flat_set &reports_a, const flat_set &reports_b) const override { return contains(reports_a, a_rep) == contains(reports_b, b_rep); } private: ReportID a_rep; ReportID b_rep; }; /** Comparison functor used to sort by vertex_index. */ template struct VertexIndexOrdering { explicit VertexIndexOrdering(const Graph &g_in) : g(g_in) {} bool operator()(typename Graph::vertex_descriptor a, typename Graph::vertex_descriptor b) const { assert(a == b || g[a].index != g[b].index); return g[a].index < g[b].index; } private: const Graph &g; }; template static VertexIndexOrdering make_index_ordering(const Graph &g) { return VertexIndexOrdering(g); } } static bool is_equal_i(const NGHolder &a, const NGHolder &b, const check_report &check_rep) { assert(hasCorrectlyNumberedVertices(a)); assert(hasCorrectlyNumberedVertices(b)); size_t num_verts = num_vertices(a); if (num_verts != num_vertices(b)) { return false; } vector vert_a; vector vert_b; vector adj_a; vector adj_b; vert_a.reserve(num_verts); vert_b.reserve(num_verts); adj_a.reserve(num_verts); adj_b.reserve(num_verts); insert(&vert_a, vert_a.end(), vertices(a)); insert(&vert_b, vert_b.end(), vertices(b)); sort(vert_a.begin(), vert_a.end(), make_index_ordering(a)); sort(vert_b.begin(), vert_b.end(), make_index_ordering(b)); for (size_t i = 0; i < vert_a.size(); i++) { NFAVertex va = vert_a[i]; NFAVertex vb = vert_b[i]; DEBUG_PRINTF("vertex %zu\n", a[va].index); // Vertex index must be the same. if (a[va].index != b[vb].index) { DEBUG_PRINTF("bad index\n"); return false; } // Reach must be the same. if (a[va].char_reach != b[vb].char_reach) { DEBUG_PRINTF("bad reach\n"); return false; } if (!check_rep(a[va].reports, b[vb].reports)) { DEBUG_PRINTF("bad reports\n"); return false; } // Other vertex properties may vary. /* Check successors */ adj_a.clear(); adj_b.clear(); insert(&adj_a, adj_a.end(), adjacent_vertices(va, a)); insert(&adj_b, adj_b.end(), adjacent_vertices(vb, b)); if (adj_a.size() != adj_b.size()) { DEBUG_PRINTF("bad adj\n"); return false; } sort(adj_a.begin(), adj_a.end(), make_index_ordering(a)); sort(adj_b.begin(), adj_b.end(), make_index_ordering(b)); for (size_t j = 0; j < adj_a.size(); j++) { if (a[adj_a[j]].index != b[adj_b[j]].index) { DEBUG_PRINTF("bad adj\n"); return false; } } } /* check top for edges out of start */ vector>> top_a; vector>> top_b; for (const auto &e : out_edges_range(a.start, a)) { top_a.emplace_back(a[target(e, a)].index, a[e].tops); } for (const auto &e : out_edges_range(b.start, b)) { top_b.emplace_back(b[target(e, b)].index, b[e].tops); } sort(top_a.begin(), top_a.end()); sort(top_b.begin(), top_b.end()); if (top_a != top_b) { DEBUG_PRINTF("bad top\n"); return false; } DEBUG_PRINTF("good\n"); return true; } /** \brief loose hash of an NGHolder; equal if is_equal would return true. */ u64a hash_holder(const NGHolder &g) { size_t rv = 0; for (auto v : vertices_range(g)) { hash_combine(rv, g[v].index); hash_combine(rv, g[v].char_reach); for (auto w : adjacent_vertices_range(v, g)) { hash_combine(rv, g[w].index); } } return rv; } bool is_equal(const NGHolder &a, const NGHolder &b) { DEBUG_PRINTF("testing %p %p\n", &a, &b); if (&a == &b) { return true; } return is_equal_i(a, b, full_check_report()); } bool is_equal(const NGHolder &a, ReportID a_rep, const NGHolder &b, ReportID b_rep) { DEBUG_PRINTF("testing %p %p\n", &a, &b); if (&a == &b && a_rep == b_rep) { return true; } return is_equal_i(a, b, equiv_check_report(a_rep, b_rep)); } } // namespace ue2