/*
 * 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 Calculate dominator and post-dominator trees.
 *
 * A small wrapper around the BGL's lengauer_tarjan_dominator_tree algorithm.
 */
#include "ng_dominators.h"

#include "ue2common.h"
#include "ng_holder.h"
#include "ng_util.h"

#include <boost-patched/graph/dominator_tree.hpp> // locally patched version
#include <boost-patched/graph/reverse_graph.hpp>

using namespace std;
using boost::make_assoc_property_map;
using boost::make_iterator_property_map;

namespace ue2 {

template <class Graph>
unordered_map<NFAVertex, NFAVertex> calcDominators(const Graph &g,
                                typename Graph::vertex_descriptor source) {
    using Vertex = typename Graph::vertex_descriptor;
    const size_t num_verts = num_vertices(g);
    auto index_map = get(&NFAGraphVertexProps::index, g);

    vector<size_t> dfnum(num_verts, 0);
    vector<Vertex> parents(num_verts, Graph::null_vertex());

    auto dfnum_map = make_iterator_property_map(dfnum.begin(), index_map);
    auto parent_map = make_iterator_property_map(parents.begin(), index_map);
    vector<Vertex> vertices_by_dfnum(num_verts, Graph::null_vertex());

    // Output map.
    vector<Vertex> doms(num_verts, Graph::null_vertex());
    auto dom_map = make_iterator_property_map(doms.begin(), index_map);

    boost_ue2::lengauer_tarjan_dominator_tree(g, source, index_map, dfnum_map,
                                              parent_map, vertices_by_dfnum,
                                              dom_map);

    /* Translate back to an NFAVertex map */
    unordered_map<NFAVertex, NFAVertex> doms2;
    doms2.reserve(num_verts);
    for (auto v : vertices_range(g)) {
        auto dom_of_v = doms[g[v].index];
        if (dom_of_v) {
            doms2.emplace(v, dom_of_v);
        }
    }
    return doms2;
}

unordered_map<NFAVertex, NFAVertex> findDominators(const NGHolder &g) {
    assert(hasCorrectlyNumberedVertices(g));
    return calcDominators(g, g.start);
}

unordered_map<NFAVertex, NFAVertex> findPostDominators(const NGHolder &g) {
    assert(hasCorrectlyNumberedVertices(g));
    return calcDominators(boost::reverse_graph<NGHolder, const NGHolder &>(g),
                          g.acceptEod);
}

} // namespace ue2
