Hypergraph ParBMAlgorithm::transversal (const Hypergraph& H) const {
        BOOST_LOG_TRIVIAL(debug) << "Starting BM with " << num_threads
                                 << " threads. Hypergraph has "
                                 << H.num_verts() << " vertices and "
                                 << H.num_edges() << " edges.";

        // Set up inputs
        Hypergraph Hmin = H.minimization();
        Hypergraph G (H.num_verts());
        Hypergraph::Edge V = Hmin.verts_covered();

        // Initialize using any HS we can find
        Hypergraph::Edge first_hs = FKAlgorithm::minimize_new_hs(Hmin, G, V);
        G.add_edge(first_hs);

        // Grow G until it covers all vertices
        bool G_has_full_coverage = false;
        while (not G_has_full_coverage) {
            Hypergraph::Edge new_hs = V - coverage_condition_check(H, G);
            if (new_hs.is_proper_subset_of(V)) {
                Hypergraph::Edge new_mhs = FKAlgorithm::minimize_new_hs(Hmin, G, new_hs);
                G.add_edge(new_mhs);
            } else {
                G_has_full_coverage = true;
            }
        }

        // Apply the BM algorithm repeatedly, generating new transversals
        // until duality is confirmed
        bool still_searching_for_transversals = true;
        Hypergraph::EdgeQueue new_hses, new_mhses;
#pragma omp parallel shared(Hmin, G, new_hses, new_mhses) num_threads(num_threads)
#pragma omp master
        while (still_searching_for_transversals) {
            find_new_hses(Hmin, G, Hmin.verts_covered(), new_hses);

            if (new_hses.size_approx() == 0) {
                still_searching_for_transversals = false;
            } else {
                minimize_new_hses(Hmin, G, new_hses, new_mhses);

                Hypergraph::Edge new_mhs;
                while (new_mhses.try_dequeue(new_mhs)) {
                    // The results will all be inclusion-minimal, but
                    // there may be some overlap. Thus, we try to add
                    // them...
                    try {
                        G.add_edge(new_mhs, true);
                    }
                    // But ignore any minimality_violated_exception
                    // that is thrown.
                    catch (minimality_violated_exception& e) {}
                }
                BOOST_LOG_TRIVIAL(debug) << "New |G|: " << G.num_edges();
            }
        }

        return G;
    }
    Hypergraph FKAlgorithmA::transversal (const Hypergraph& H) const {
        BOOST_LOG_TRIVIAL(debug) << "Starting FKA. Hypergraph has "
                                 << H.num_verts() << " vertices and "
                                 << H.num_edges() << " edges.";
        Hypergraph G (H.num_verts());

        Hypergraph Hmin = H.minimization();
        Hypergraph::Edge V = Hmin.verts_covered();

        bool still_searching_for_transversals = true;
        while (still_searching_for_transversals) {
            Hypergraph::Edge omit_set = find_omit_set(Hmin, G);

            if (omit_set.none() and G.num_edges() > 0) {
                BOOST_LOG_TRIVIAL(debug) << "Received empty omit_set, so we're done.";
                still_searching_for_transversals = false;
            } else {
                Hypergraph::Edge new_hs = V - omit_set;
                Hypergraph::Edge new_mhs = minimize_new_hs(H, G, new_hs);
                BOOST_LOG_TRIVIAL(trace) << "Received witness."
                                         << "\nomit_set:\t" << omit_set
                                         << "\nMHS:\t\t" << new_mhs;
                G.add_edge(new_mhs, true);
                BOOST_LOG_TRIVIAL(debug) << "New G size: " << G.num_edges();
            }
        }

        return G;
    }
    Hypergraph ParBMAlgorithm::l5_full_cover (const Hypergraph& H,
                                              const Hypergraph::Edge& base_transversal) {
        /**
           Find a full cover of the dual of H from the given base_transversal
           in accordance with lemma 5 of BM.
        **/
        Hypergraph C (H.num_verts());
        Hypergraph::Edge V = H.verts_covered();

        C.add_edge(base_transversal, false);

        Hypergraph::EdgeIndex i = base_transversal.find_first();
        while (i != Hypergraph::Edge::npos) {
            V.reset(i);
            C.add_edge(V, false);
            V.set(i);
            i = base_transversal.find_next(i);
        }

        return C;
    }
    Hypergraph ParBMAlgorithm::l4_full_cover (const Hypergraph& H,
                                              const Hypergraph::Edge& base_edge) {
        /**
           Find a full cover of the dual of H from the given base_edge
           in accordance with lemma 4 of BM.
        **/
        Hypergraph C (H.num_verts());
        Hypergraph::Edge V = H.verts_covered();

        for (auto& edge: H) {
            Hypergraph::Edge intersection = edge & base_edge;
            Hypergraph::EdgeIndex i = intersection.find_first();
            while (i != Hypergraph::Edge::npos) {
                Hypergraph::Edge new_edge = V - edge;
                new_edge.set(i);
                C.add_edge(new_edge, false);
                i = intersection.find_next(i);
            }
        }

        return C;
    }