// graph-tool -- a general graph modification and manipulation thingy
//
// Copyright (C) 2006-2015 Tiago de Paula Peixoto <tiago@skewed.de>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

#define BOOST_PYTHON_MAX_ARITY 40
#include <boost/python.hpp>
#include <cmath>
#include <iostream>

#include "numpy_bind.hh"

#include <boost/python.hpp>
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>

#include "graph_filtering.hh"

#include "graph.hh"
#include "graph_selectors.hh"
#include "graph_properties.hh"
#include "graph_util.hh"

#include "random.hh"

#include "config.h"
#include "graph_blockmodel.hh"
#include "graph_blockmodel_overlap.hh"

using namespace boost;
using namespace graph_tool;

namespace graph_tool
{
template <class Eprop, class Vprop, class VEprop>
struct move_sweep_overlap_dispatch
{
    move_sweep_overlap_dispatch(Eprop eweight, Vprop vweight,
                                boost::any egroups, VEprop esrcpos,
                                VEprop etgtpos, Vprop label, vector<int>& vlist,
                                bool deg_corr, bool dense, bool multigraph,
                                bool parallel_edges, double beta,
                                bool sequential, bool parallel,
                                bool random_move, double c, bool node_coherent,
                                bool verbose, size_t max_edge_index,
                                size_t nmerges, size_t niter, Vprop merge_map,
                                overlap_stats_t& overlap_stats,
                                overlap_partition_stats_t& partition_stats,
                                rng_t& rng, double& S, size_t& nmoves,
                                GraphInterface& bgi)

        : eweight(eweight), vweight(vweight), oegroups(egroups), esrcpos(esrcpos),
          etgtpos(etgtpos), label(label), vlist(vlist),
          deg_corr(deg_corr), dense(dense), multigraph(multigraph),
          parallel_edges(parallel_edges), beta(beta), sequential(sequential),
          parallel(parallel), random_move(random_move), c(c),
          node_coherent(node_coherent), verbose(verbose),
          max_edge_index(max_edge_index), nmerges(nmerges),
          niter(niter), merge_map(merge_map), overlap_stats(overlap_stats),
          partition_stats(partition_stats), rng(rng), S(S), nmoves(nmoves), bgi(bgi)
    {}

    Eprop eweight;
    Vprop vweight;
    boost::any oegroups;
    VEprop esrcpos;
    VEprop etgtpos;
    Vprop label;
    size_t n;
    vector<int>& vlist;
    bool deg_corr;
    bool dense;
    bool multigraph;
    bool parallel_edges;
    double beta;
    bool sequential;
    bool parallel;
    bool random_move;
    double c;
    bool node_coherent;
    bool verbose;
    size_t max_edge_index;
    size_t nmerges;
    size_t niter;
    Vprop merge_map;
    overlap_stats_t& overlap_stats;
    overlap_partition_stats_t& partition_stats;
    rng_t& rng;
    double& S;
    size_t& nmoves;
    GraphInterface& bgi;

    template <class Graph>
    void operator()(Eprop mrs, Vprop mrp, Vprop mrm, Vprop wr, Vprop b,
                    Graph& g, boost::any& emat, boost::any sampler,
                    boost::any cavity_sampler, bool weighted) const
    {
        if (is_directed::apply<Graph>::type::value)
        {
            dispatch(mrs, mrp, mrm, wr, b, g, emat, sampler, cavity_sampler,
                     bgi.GetGraph(), weighted);
        }
        else
        {
            UndirectedAdaptor<GraphInterface::multigraph_t> ug(bgi.GetGraph());
            dispatch(mrs, mrp, mrm, wr, b, g, emat, sampler, cavity_sampler, ug,
                     weighted);
        }
    }

    template <class Graph, class BGraph>
    void dispatch(Eprop& mrs, Vprop& mrp, Vprop& mrm, Vprop& wr, Vprop& b,
                  Graph& g, boost::any& aemat, boost::any asampler,
                  boost::any& cavity_sampler, BGraph& bg, bool weighted) const
    {
        if (weighted)
        {
            typedef typename property_map_type::apply<DynamicSampler<std::tuple<typename graph_traits<Graph>::edge_descriptor, bool> >,
                                                      GraphInterface::vertex_index_map_t>::type vemap_t;
            vemap_t egroups = any_cast<vemap_t>(oegroups);
            dispatch(mrs, mrp, mrm, wr, b, g, aemat, asampler, cavity_sampler,
                     bg, egroups);
        }
        else
        {
            typedef typename property_map_type::apply<vector<std::tuple<typename graph_traits<Graph>::edge_descriptor, bool> >,
                                                      GraphInterface::vertex_index_map_t>::type vemap_t;
            vemap_t egroups = any_cast<vemap_t>(oegroups);
            dispatch(mrs, mrp, mrm, wr, b, g, aemat, asampler, cavity_sampler,
                     bg, egroups);
        }
    }

    template <class Graph, class BGraph, class Egroups>
    void dispatch(Eprop& mrs, Vprop& mrp, Vprop& mrm, Vprop& wr, Vprop& b,
                  Graph& g, boost::any& aemat, boost::any& asampler,
                  boost::any& acavity_sampler, BGraph& bg, Egroups& egroups) const
    {
        try
        {
            typedef typename get_emat_t::apply<BGraph>::type emat_t;
            emat_t& emat = any_cast<emat_t&>(aemat);
            size_t B = num_vertices(bg);
            size_t max_BE = is_directed::apply<Graph>::type::value ?
                B * B : (B * (B + 1)) / 2;
            dispatch(mrs.get_unchecked(max_BE), mrp, mrm, wr, b, g,
                     asampler, acavity_sampler, bg, egroups, emat);
        }
        catch (bad_any_cast&)
        {
            typedef typename get_ehash_t::apply<BGraph>::type emat_t;
            emat_t& emat = any_cast<emat_t&>(aemat);
            dispatch(mrs, mrp, mrm, wr, b, g, asampler, acavity_sampler,
                     bg, egroups, emat);
        }
    }

    template <class Graph, class BGraph, class Egroups, class Emat, class MEprop>
    void dispatch(MEprop mrs, Vprop& mrp, Vprop& mrm, Vprop& wr, Vprop& b,
                  Graph& g, boost::any& asampler, boost::any& acavity_sampler,
                  BGraph& bg, Egroups& egroups, Emat& emat) const
    {
        typedef typename graph_traits<Graph>::vertex_descriptor vertex_t;

        size_t eidx = random_move ? 1 : max_edge_index;

        typedef typename property_map<Graph, vertex_index_t>::type vindex_map_t;
        typedef typename property_map_type::apply<Sampler<vertex_t, boost::mpl::false_>,
                                                  vindex_map_t>::type::unchecked_t
            sampler_map_t;
        sampler_map_t sampler = any_cast<sampler_map_t>(asampler);
        sampler_map_t cavity_sampler = any_cast<sampler_map_t>(acavity_sampler);

        ConstantPropertyMap<int, typename graph_traits<Graph>::edge_descriptor> ce(0);
        ConstantPropertyMap<std::array<int, 1>, typename graph_traits<Graph>::vertex_descriptor> cv({-1});
        IdentityArrayPropertyMap<typename graph_traits<Graph>::vertex_descriptor> vmap;
        boost::typed_identity_property_map<int> identity;

        vector<size_t> free_blocks;
        auto state = make_block_state(g,
                                      eweight.get_unchecked(max_edge_index),
                                      vweight.get_unchecked(num_vertices(g)),
                                      b.get_unchecked(num_vertices(g)),
                                      bg, emat, mrs,
                                      mrp.get_unchecked(num_vertices(bg)),
                                      mrm.get_unchecked(num_vertices(bg)),
                                      wr.get_unchecked(num_vertices(bg)),
                                      egroups.get_unchecked(num_vertices(bg)),
                                      esrcpos.get_unchecked(eidx),
                                      etgtpos.get_unchecked(eidx),
                                      sampler, cavity_sampler,
                                      partition_stats, overlap_stats,
                                      identity, identity, free_blocks,
                                      false, false, true);

        vector<decltype(state)> states = {state};
        vector<SingleEntrySet<Graph>> m_entries(1);

        if (nmerges == 0)
        {
            if (!node_coherent)
            {
                move_sweep_overlap(states, m_entries, overlap_stats,
                                   wr.get_unchecked(num_vertices(bg)),
                                   b.get_unchecked(num_vertices(g)), cv,
                                   vmap, label.get_unchecked(num_vertices(bg)),
                                   vlist, deg_corr, dense, multigraph, beta,
                                   vweight.get_unchecked(num_vertices(g)), g,
                                   sequential, parallel, random_move, c, niter,
                                   num_vertices(bg), verbose, rng, S, nmoves);
            }
            else
            {
                vector<EntrySet<Graph>> m_entries(1, EntrySet<Graph>(num_vertices(bg)));
                coherent_move_sweep_overlap(states, m_entries, overlap_stats,
                                            wr.get_unchecked(num_vertices(bg)),
                                            b.get_unchecked(num_vertices(g)), cv,
                                            vmap, label.get_unchecked(num_vertices(bg)),
                                            vlist, deg_corr, dense, multigraph, beta,
                                            vweight.get_unchecked(num_vertices(g)), g,
                                            sequential,  random_move, c,
                                            false, niter,
                                            num_vertices(bg), rng, S, nmoves);
            }
        }
        else
        {
            merge_sweep_overlap(states, m_entries, overlap_stats,
                                wr.get_unchecked(num_vertices(bg)),
                                b.get_unchecked(num_vertices(g)), ce, cv, vmap,
                                label.get_unchecked(num_vertices(bg)), vlist,
                                deg_corr, dense, multigraph, g, random_move,
                                false, nmerges, niter,
                                num_vertices(bg), rng, S, nmoves);
        }
    }
};

boost::python::object
do_move_sweep_overlap(GraphInterface& gi, GraphInterface& bgi, boost::any& emat,
                      boost::any sampler, boost::any cavity_sampler,
                      boost::any omrs, boost::any omrp, boost::any omrm,
                      boost::any owr, boost::any ob, boost::any olabel,
                      vector<int>& vlist, bool deg_corr, bool dense,
                      bool multigraph, bool parallel_edges, boost::any oeweight,
                      boost::any ovweight, boost::any oegroups,
                      boost::any oesrcpos, boost::any oetgtpos, double beta,
                      bool sequential, bool parallel, bool random_move,
                      double c, bool node_coherent, bool weighted,
                      size_t nmerges, boost::any omerge_map, size_t niter,
                      overlap_stats_t& overlap_stats,
                      overlap_partition_stats_t& partition_stats, bool verbose,
                      rng_t& rng)
{
    typedef property_map_type::apply<int32_t,
                                     GraphInterface::vertex_index_map_t>::type
        vmap_t;
    typedef property_map_type::apply<int32_t,
                                     GraphInterface::edge_index_map_t>::type
        emap_t;
    typedef property_map_type::apply<int32_t,
                                     GraphInterface::edge_index_map_t>::type
        vemap_t;
    emap_t mrs = any_cast<emap_t>(omrs);
    vmap_t mrp = any_cast<vmap_t>(omrp);
    vmap_t mrm = any_cast<vmap_t>(omrm);
    vmap_t wr = any_cast<vmap_t>(owr);
    vmap_t b = any_cast<vmap_t>(ob);
    vmap_t label = any_cast<vmap_t>(olabel);
    emap_t eweight = any_cast<emap_t>(oeweight);
    vmap_t vweight = any_cast<vmap_t>(ovweight);

    vemap_t esrcpos = any_cast<vemap_t>(oesrcpos);
    vemap_t etgtpos = any_cast<vemap_t>(oetgtpos);

    double S = 0;
    size_t nmoves = 0;

    vmap_t merge_map = any_cast<vmap_t>(omerge_map);

    run_action<graph_tool::detail::all_graph_views, boost::mpl::true_>()
        (gi, std::bind(move_sweep_overlap_dispatch<emap_t, vmap_t, vemap_t>
                       (eweight, vweight, oegroups, esrcpos, etgtpos,
                        label, vlist, deg_corr, dense, multigraph, parallel_edges,
                        beta, sequential, parallel, random_move, c, node_coherent,
                        verbose, gi.GetMaxEdgeIndex(), nmerges,
                        niter, merge_map, overlap_stats, partition_stats, rng, S,
                        nmoves, bgi),
                       mrs, mrp, mrm, wr, b, placeholders::_1,
                       std::ref(emat), sampler, cavity_sampler, weighted))();
    return boost::python::make_tuple(S, nmoves);
}

struct get_overlap_stats
{
    template <class Graph, class Vprop, class VIprop, class VVprop>
    void operator()(Graph& g, Vprop b, VVprop half_edges, VIprop node_index,
                    size_t B, overlap_stats_t& overlap_stats) const
    {
        overlap_stats = overlap_stats_t(g, b.get_unchecked(num_vertices(g)),
                                        half_edges,
                                        node_index.get_unchecked(num_vertices(g)), B);
    }
};

overlap_stats_t
do_get_overlap_stats(GraphInterface& gi, boost::any ob,
                     boost::any ohalf_edges, boost::any onode_index,
                     size_t NN, size_t B)
{
    typedef property_map_type::apply<int32_t,
                                     GraphInterface::vertex_index_map_t>::type
        vmap_t;
    typedef property_map_type::apply<int64_t,
                                     GraphInterface::vertex_index_map_t>::type
        vimap_t;
    typedef property_map_type::apply<vector<int64_t>,
                                     GraphInterface::vertex_index_map_t>::type
        vvmap_t;

    overlap_stats_t overlap_stats;

    vmap_t b = any_cast<vmap_t>(ob);
    vvmap_t half_edges = any_cast<vvmap_t>(ohalf_edges);
    vimap_t node_index = any_cast<vimap_t>(onode_index);

    run_action<>()(gi, std::bind(get_overlap_stats(), placeholders::_1, b,
                                 half_edges.get_unchecked(NN), node_index, B,
                                 std::ref(overlap_stats)))();
    return overlap_stats;
}

struct get_overlap_partition_stats
{
    template <class Graph, class Vprop, class Eprop>
    void operator()(Graph& g, Vprop b, Eprop eweight, size_t N, size_t B,
                    bool edges_dl, overlap_stats_t& overlap_stats,
                    overlap_partition_stats_t& partition_stats) const
    {
        partition_stats = overlap_partition_stats_t(g, b, overlap_stats,
                                                    eweight, N, B, edges_dl);
    }
};

overlap_partition_stats_t
do_get_overlap_partition_stats(GraphInterface& gi, boost::any ob,
                               boost::any aeweight, size_t N, size_t B,
                               bool edges_dl, overlap_stats_t& overlap_stats)
{
    typedef property_map_type::apply<int32_t,
                                     GraphInterface::vertex_index_map_t>::type
        vmap_t;
    typedef property_map_type::apply<int32_t,
                                     GraphInterface::edge_index_map_t>::type
        emap_t;

    overlap_partition_stats_t partition_stats;

    vmap_t b = any_cast<vmap_t>(ob);
    emap_t eweight = any_cast<emap_t>(aeweight);

    run_action<>()(gi, std::bind(get_overlap_partition_stats(),
                                 placeholders::_1, b, eweight, N, B, edges_dl,
                                 std::ref(overlap_stats),
                                 std::ref(partition_stats)))();

    return partition_stats;
}

double do_get_overlap_parallel_entropy(GraphInterface& gi,
                                       overlap_stats_t& overlap_stats)
{
    double S = 0;
    run_action<>()
        (gi, std::bind(entropy_parallel_edges_overlap(),
                       placeholders::_1, std::ref(overlap_stats),
                       std::ref(S)))();
    return S;
}

struct get_eg_overlap
{
    template <class Graph, class EGraph, class EVprop, class VProp,
              class VIProp, class VVProp, class EProp>
    void operator()(Graph& g, EGraph& eg, EVprop be, VProp b, VIProp node_index,
                    VVProp half_edges, EProp egindex) const
    {
        auto eindex = get(edge_index, g);
        typedef typename graph_traits<Graph>::vertex_descriptor vertex_t;
        for (auto e : edges_range(g))
        {
            vertex_t s = get_source(e, g);
            vertex_t t = get_target(e, g);
            vertex_t u = add_vertex(eg);
            vertex_t v = add_vertex(eg);
            auto ne = add_edge(u, v, eg).first;
            egindex[ne] = eindex[e];
            if (be[e].size() != 2)
                throw GraphException("Edge block property map must have two values per edge");
            b[u] = be[e][0];
            b[v] = be[e][1];
            node_index[u] = s;
            node_index[v] = t;
            half_edges[s].push_back(u);
            half_edges[t].push_back(v);
        }
    }
};

void do_get_eg_overlap(GraphInterface& gi, GraphInterface& egi, boost::any obe,
                       boost::any ob, boost::any onode_index,
                       boost::any ohalf_edges, boost::any oeindex)
{
    typedef property_map_type::apply<int32_t,
                                     GraphInterface::vertex_index_map_t>::type
        vmap_t;
    typedef property_map_type::apply<int64_t,
                                     GraphInterface::vertex_index_map_t>::type
        vimap_t;
    typedef property_map_type::apply<vector<int64_t>,
                                     GraphInterface::vertex_index_map_t>::type
        vvmap_t;
    typedef property_map_type::apply<vector<int32_t>,
                                     GraphInterface::edge_index_map_t>::type
        evmap_t;
    typedef property_map_type::apply<int64_t,
                                     GraphInterface::edge_index_map_t>::type
        emap_t;

    vmap_t b = any_cast<vmap_t>(ob);
    evmap_t be = any_cast<evmap_t>(obe);
    vimap_t node_index = any_cast<vimap_t>(onode_index);
    vvmap_t half_edges = any_cast<vvmap_t>(ohalf_edges);
    emap_t eindex = any_cast<emap_t>(oeindex);

    run_action<>()(gi, std::bind(get_eg_overlap(), placeholders::_1,
                                 std::ref(egi.GetGraph()), be, b, node_index,
                                 half_edges, eindex))();
}

struct get_be_overlap
{
    template <class Graph, class EGraph, class EVprop, class VProp, class VIProp>
    void operator()(Graph& g, EGraph& eg, EVprop be, VProp b, VIProp node_index)
        const
    {
        for (auto ei : edges_range(eg))
        {
            auto u = source(ei, eg);
            auto v = target(ei, eg);

            auto s = vertex(node_index[u], g);
            auto t = vertex(node_index[v], g);

            for (auto e : out_edges_range(s, g))
            {
                if (!be[e].empty() || target(e, g) != t)
                    continue;
                if (is_directed::apply<Graph>::type::value || s < target(e, g))
                    be[e] = {b[u], b[v]};
                else
                    be[e] = {b[v], b[u]};
                break;
            }

            for (auto e : in_edges_range(t, g))
            {
                if (!be[e].empty() || source(e, g) != s)
                    continue;
                be[e] = {b[u], b[v]};
                break;
            }
        }
    }
};

void do_get_be_overlap(GraphInterface& gi, GraphInterface& egi, boost::any obe,
                       boost::any ob, boost::any onode_index)
{
    typedef property_map_type::apply<int32_t,
                                     GraphInterface::vertex_index_map_t>::type
        vmap_t;
    typedef property_map_type::apply<int64_t,
                                     GraphInterface::vertex_index_map_t>::type
        vimap_t;
    typedef property_map_type::apply<vector<int32_t>,
                                     GraphInterface::edge_index_map_t>::type
        evmap_t;

    vmap_t b = any_cast<vmap_t>(ob);
    evmap_t be = any_cast<evmap_t>(obe);
    vimap_t node_index = any_cast<vimap_t>(onode_index);

    run_action<>()(gi, std::bind(get_be_overlap(), placeholders::_1,
                                 std::ref(egi.GetGraph()), be, b,
                                 node_index))();
}

struct get_be_from_b_overlap
{
    template <class Graph, class EVprop, class VProp>
    void operator()(Graph& g, EVprop be, VProp b)
        const
    {
        typedef typename graph_traits<Graph>::vertex_descriptor vertex_t;
        typename graph_traits<Graph>::edge_iterator ei, ei_end;
        for (auto e : edges_range(g))
        {
            vertex_t s = get_source(e, g);
            vertex_t t = get_target(e, g);
            be[e] = {b[s], b[t]};
        }
    }
};

void do_get_be_from_b_overlap(GraphInterface& gi, boost::any obe, boost::any ob)
{
    typedef property_map_type::apply<int32_t,
                                     GraphInterface::vertex_index_map_t>::type
        vmap_t;
    typedef property_map_type::apply<vector<int32_t>,
                                     GraphInterface::edge_index_map_t>::type
        evmap_t;

    vmap_t b = any_cast<vmap_t>(ob);
    evmap_t be = any_cast<evmap_t>(obe);

    run_action<>()(gi, std::bind(get_be_from_b_overlap(), placeholders::_1,
                                 be, b))();
}

struct get_bv_overlap
{
    template <class Graph, class VProp, class VIProp, class VVProp>
    void operator()(Graph& g, VProp b, VIProp node_index, VVProp bv,
                    VVProp bc_in, VVProp bc_out, VVProp bc_total) const
    {
        typedef typename graph_traits<Graph>::vertex_descriptor vertex_t;

        typedef unordered_map<int, int> map_t;
        vector<map_t> hist_in;
        vector<map_t> hist_out;

        for (auto v : vertices_range(g))
        {
            if (out_degree(v, g) > 0)
            {
                vertex_t s = node_index[v];
                if (s >= hist_out.size())
                    hist_out.resize(s + 1);
                hist_out[s][b[v]]++;
            }

            if (in_degreeS()(v, g) > 0)
            {
                vertex_t t = node_index[v];
                if (t >= hist_in.size())
                    hist_in.resize(t + 1);
                hist_in[t][b[v]]++;
            }
        }

        size_t N = max(hist_in.size(), hist_out.size());
        hist_in.resize(N);
        hist_out.resize(N);

        set<size_t> rs;
        for (size_t i = 0; i < N; ++i)
        {
            rs.clear();
            for (auto iter = hist_out[i].begin(); iter != hist_out[i].end(); ++iter)
                rs.insert(iter->first);
            for (auto iter = hist_in[i].begin(); iter != hist_in[i].end(); ++iter)
                rs.insert(iter->first);
            // if (rs.empty())
            //     throw GraphException("Cannot have empty overlapping block membership!");
            for (size_t r : rs)
            {
                bv[i].push_back(r);

                auto iter_in = hist_in[i].find(r);
                if (iter_in != hist_in[i].end())
                    bc_in[i].push_back(iter_in->second);
                else
                    bc_in[i].push_back(0);

                auto iter_out = hist_out[i].find(r);
                if (iter_out != hist_out[i].end())
                    bc_out[i].push_back(iter_out->second);
                else
                    bc_out[i].push_back(0);

                bc_total[i].push_back(bc_in[i].back() +
                                      bc_out[i].back());
            }
        }
    }
};

void do_get_bv_overlap(GraphInterface& gi, boost::any ob,  boost::any onode_index,
                       boost::any obv, boost::any obc_in, boost::any obc_out,
                       boost::any obc_total)
{
    typedef property_map_type::apply<int64_t,
                                     GraphInterface::vertex_index_map_t>::type
        vimap_t;
    typedef property_map_type::apply<int32_t,
                                     GraphInterface::vertex_index_map_t>::type
        vmap_t;
    typedef property_map_type::apply<vector<int32_t>,
                                     GraphInterface::vertex_index_map_t>::type
        vvmap_t;

    vmap_t b = any_cast<vmap_t>(ob);
    vimap_t node_index = any_cast<vimap_t>(onode_index);
    vvmap_t bv = any_cast<vvmap_t>(obv);
    vvmap_t bc_in = any_cast<vvmap_t>(obc_in);
    vvmap_t bc_out = any_cast<vvmap_t>(obc_out);
    vvmap_t bc_total = any_cast<vvmap_t>(obc_total);

    run_action<>()(gi, std::bind(get_bv_overlap(), placeholders::_1, b,
                                 node_index, bv, bc_in, bc_out, bc_total))();
 }

struct get_wr_overlap
{
    template <class Graph, class VProp, class VVProp>
    void operator()(Graph& g, VVProp bv, VProp wr) const
    {
        for (auto v : vertices_range(g))
        {
            for (size_t i = 0; i < bv[v].size(); ++i)
                wr[bv[v][i]]++;
        }
    }
};

void do_get_wr_overlap(GraphInterface& gi, boost::any obv,
                       boost::any owr)
{
    typedef property_map_type::apply<vector<int32_t>,
                                     GraphInterface::vertex_index_map_t>::type
        vvmap_t;
    typedef property_map_type::apply<int32_t,
                                     GraphInterface::vertex_index_map_t>::type
        vmap_t;

    vvmap_t bv = any_cast<vvmap_t>(obv);
    vmap_t wr = any_cast<vmap_t>(owr);

    run_action<>()(gi, std::bind(get_wr_overlap(), placeholders::_1, bv, wr))();
}

struct get_nodeset_overlap
{
    template <class Graph, class VProp, class VVProp>
    void operator()(Graph& g, VProp node_index, VVProp half_edges)
        const
    {
        typedef typename graph_traits<Graph>::vertex_descriptor vertex_t;
        for (auto e : edges_range(g))
        {
            vertex_t s = get_source(e, g);
            vertex_t t = get_target(e, g);
            half_edges[node_index[s]].push_back(s);
            half_edges[node_index[t]].push_back(t);
        }
    }
};

void do_get_nodeset_overlap(GraphInterface& gi, boost::any onode_index,
                            boost::any ohalf_edges)
{
    typedef property_map_type::apply<int64_t,
                                     GraphInterface::vertex_index_map_t>::type
        vmap_t;
    typedef property_map_type::apply<vector<int64_t>,
                                     GraphInterface::vertex_index_map_t>::type
        vvmap_t;

    vmap_t node_index = any_cast<vmap_t>(onode_index);
    vvmap_t half_edges = any_cast<vvmap_t>(ohalf_edges);

    run_action<>()(gi, std::bind(get_nodeset_overlap(), placeholders::_1,
                                 node_index, half_edges))();
}

struct get_augmented_overlap
{
    template <class Graph, class VProp, class VIProp>
    void operator()(Graph& g, VProp b, VIProp node_index, VProp br_map,
                    vector<int32_t>& br_b, vector<int32_t>& br_ni) const
    {
        unordered_map<std::tuple<int, int>, size_t> idx_map;
        vector<std::tuple<int, int>> idx_rmap;
        size_t pos = 0;

        for (auto v : vertices_range(g))
        {
            size_t vi = node_index[v];
            auto br = std::make_tuple(b[v], vi);
            size_t idx;
            auto iter = idx_map.find(br);
            if (iter != idx_map.end())
            {
                idx = iter->second;
            }
            else
            {
                idx = pos;
                idx_map[br] = pos++;
                idx_rmap.push_back(br);
            }
            br_map[v] = idx;
        }

        for (size_t i = 0; i < idx_rmap.size(); ++i)
        {
            auto& br = idx_rmap[i];
            br_b.push_back(get<0>(br));
            br_ni.push_back(get<1>(br));
        }
    }
};

void do_get_augmented_overlap(GraphInterface& gi, boost::any ob,
                              boost::any onode_index, boost::any obr_map,
                              vector<int32_t>& br_b, vector<int32_t>& br_ni)
{
    typedef property_map_type::apply<int32_t,
                                     GraphInterface::vertex_index_map_t>::type
        vmap_t;
    typedef property_map_type::apply<int64_t,
                                     GraphInterface::vertex_index_map_t>::type
        vimap_t;

    vmap_t b = any_cast<vmap_t>(ob);
    vimap_t node_index = any_cast<vimap_t>(onode_index);
    vmap_t br_map = any_cast<vmap_t>(obr_map);

    run_action<>()(gi, std::bind(get_augmented_overlap(), placeholders::_1,
                                 b, node_index, br_map, std::ref(br_b),
                                 std::ref(br_ni)))();
}

struct get_overlap_split
{
    template <class Graph, class VVProp, class VProp>
    void operator()(Graph& g, VVProp bv, VProp b) const
    {
        unordered_map<vector<int>, size_t> bvset;

        for (auto v : vertices_range(g))
        {
            auto r = bv[v];
            auto iter = bvset.find(r);
            if (iter == bvset.end())
                iter = bvset.insert(make_pair(r, bvset.size())).first;
            b[v] = iter->second;
        }
    }
};

void do_get_overlap_split(GraphInterface& gi, boost::any obv, boost::any ob)
{
    typedef property_map_type::apply<int32_t,
                                     GraphInterface::vertex_index_map_t>::type
        vmap_t;
    typedef property_map_type::apply<vector<int32_t>,
                                     GraphInterface::vertex_index_map_t>::type
        vvmap_t;

    vvmap_t bv = any_cast<vvmap_t>(obv);
    vmap_t b = any_cast<vmap_t>(ob);

    run_action<>()(gi, std::bind(get_overlap_split(),
                                 placeholders::_1, bv, b))();
}

struct get_maj_overlap
{
    template <class Graph, class VProp, class VVProp>
    void operator()(Graph& g, VVProp bv, VVProp bc_total, VProp b) const
    {
        for (auto v : vertices_range(g))
        {
            if (bv[v].empty())
            {
                b[v] = numeric_limits<int32_t>::max();
                continue;
            }

            auto& c = bc_total[v];
            auto pos = std::max_element(c.begin(), c.end());
            auto r = *(bv[v].begin() + (pos - c.begin()));
            b[v] = r;
        }
    }
};

void do_get_maj_overlap(GraphInterface& gi, boost::any obv,
                        boost::any obc_total, boost::any ob)
{
    typedef property_map_type::apply<int32_t,
                                     GraphInterface::vertex_index_map_t>::type
        vmap_t;
    typedef property_map_type::apply<vector<int32_t>,
                                     GraphInterface::vertex_index_map_t>::type
        vvmap_t;

    vmap_t b = any_cast<vmap_t>(ob);
    vvmap_t bv = any_cast<vvmap_t>(obv);
    vvmap_t bc_total = any_cast<vvmap_t>(obc_total);

    run_action<>()(gi, std::bind(get_maj_overlap(), placeholders::_1, bv,
                                 bc_total, b))();
}

} // namespace graph_tool

void export_blockmodel_overlap()
{
    using namespace boost::python;

    class_<overlap_stats_t>("overlap_stats")
        .def("is_enabled", &overlap_stats_t::is_enabled);
    class_<overlap_partition_stats_t>("overlap_partition_stats")
        .def("is_enabled", &overlap_partition_stats_t::is_enabled)
        .def("get_partition_dl", &overlap_partition_stats_t::get_partition_dl)
        .def("get_deg_dl", &overlap_partition_stats_t::get_deg_dl);

    def("move_sweep_overlap", do_move_sweep_overlap);
    def("init_overlap_stats", do_get_overlap_stats);
    def("init_overlap_partition_stats", do_get_overlap_partition_stats);

    def("overlap_parallel_entropy", do_get_overlap_parallel_entropy);

    // def("get_overlap_proj", do_get_overlap_proj);

    def("get_eg_overlap", do_get_eg_overlap);
    def("get_be_overlap", do_get_be_overlap);
    def("get_be_from_b_overlap", do_get_be_from_b_overlap);
    def("get_bv_overlap", do_get_bv_overlap);
    def("get_wr_overlap", do_get_wr_overlap);
    def("get_nodeset_overlap", do_get_nodeset_overlap);
    def("get_augmented_overlap", do_get_augmented_overlap);
    def("get_overlap_split", do_get_overlap_split);
    def("get_maj_overlap", do_get_maj_overlap);
}


//=======================================================================
// Copyright (C) 2012 Flavio De Lorenzi (fdlorenzi@gmail.com)
// Copyright (C) 2013 Jakob Lykke Andersen, University of Southern Denmark (jlandersen@imada.sdu.dk)
//
// The algorithm implemented here is derived from original ideas by
// Pasquale Foggia and colaborators. For further information see
// e.g. Cordella et al. 2001, 2004.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================

// Revision History:
//   8 April 2013: Fixed a typo in vf2_print_callback. (Flavio De Lorenzi)

#ifndef BOOST_VF2_SUB_GRAPH_ISO_HPP
#define BOOST_VF2_SUB_GRAPH_ISO_HPP

#include <iostream>
#include <iomanip>
#include <iterator>
#include <vector>
#include <utility>

#include <boost/assert.hpp>
#include <boost/concept/assert.hpp>
#include <boost/concept_check.hpp>
#include <boost/graph/graph_utility.hpp>
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/mcgregor_common_subgraphs.hpp> // for always_equivalent
#include <boost/graph/named_function_params.hpp>
#include <boost/type_traits/has_less.hpp>
#include <boost/mpl/int.hpp>
#include <boost/range/algorithm/sort.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/utility/enable_if.hpp>

#ifndef BOOST_GRAPH_ITERATION_MACROS_HPP
#define BOOST_ISO_INCLUDED_ITER_MACROS // local macro, see bottom of file
#include <boost/graph/iteration_macros.hpp>
#endif

namespace boost {

  // Default print_callback
  template <typename Graph1,
            typename Graph2>
  struct vf2_print_callback {

    vf2_print_callback(const Graph1& graph1, const Graph2& graph2)
      : graph1_(graph1), graph2_(graph2) {}

    template <typename CorrespondenceMap1To2,
              typename CorrespondenceMap2To1>
    bool operator()(CorrespondenceMap1To2 f, CorrespondenceMap2To1) const {

      // Print (sub)graph isomorphism map
      BGL_FORALL_VERTICES_T(v, graph1_, Graph1)
        std::cout << '(' << get(vertex_index_t(), graph1_, v) << ", "
                  << get(vertex_index_t(), graph2_, get(f, v)) << ") ";

      std::cout << std::endl;

      return true;
    }

  private:
    const Graph1& graph1_;
    const Graph2& graph2_;
  };

  namespace detail {

    // State associated with a single graph (graph_this)
    template<typename GraphThis,
             typename GraphOther,
             typename IndexMapThis,
             typename IndexMapOther>
    class base_state {

      typedef typename graph_traits<GraphThis>::vertex_descriptor vertex_this_type;
      typedef typename graph_traits<GraphOther>::vertex_descriptor vertex_other_type;

      typedef typename graph_traits<GraphThis>::vertices_size_type size_type;

      const GraphThis& graph_this_;
      const GraphOther& graph_other_;

      IndexMapThis index_map_this_;
      IndexMapOther index_map_other_;

      std::vector<vertex_other_type> core_vec_;
      typedef iterator_property_map<typename std::vector<vertex_other_type>::iterator,
                                    IndexMapThis, vertex_other_type,
                                    vertex_other_type&> core_map_type;
      core_map_type core_;

      std::vector<size_type> in_vec_, out_vec_;
      typedef iterator_property_map<typename std::vector<size_type>::iterator,
                                    IndexMapThis, size_type, size_type&> in_out_map_type;
      in_out_map_type in_, out_;

      size_type term_in_count_, term_out_count_, term_both_count_, core_count_;

      // Forbidden
      base_state(const base_state&);
      base_state& operator=(const base_state&);

    public:

      base_state(const GraphThis& graph_this, const GraphOther& graph_other,
                 IndexMapThis index_map_this, IndexMapOther index_map_other)
        : graph_this_(graph_this), graph_other_(graph_other),
          index_map_this_(index_map_this), index_map_other_(index_map_other),
          term_in_count_(0), term_out_count_(0), term_both_count_(0), core_count_(0) {

        core_vec_.resize(num_vertices(graph_this_), graph_traits<GraphOther>::null_vertex());
        core_ = make_iterator_property_map(core_vec_.begin(), index_map_this_);

        in_vec_.resize(num_vertices(graph_this_), 0);
        in_ = make_iterator_property_map(in_vec_.begin(), index_map_this_);

        out_vec_.resize(num_vertices(graph_this_), 0);
        out_ = make_iterator_property_map(out_vec_.begin(), index_map_this_);
      }

      // Adds a vertex pair to the state of graph graph_this
      void push(const vertex_this_type& v_this, const vertex_other_type& v_other) {

        ++core_count_;

        put(core_, v_this, v_other);

        if (!get(in_, v_this)) {
          put(in_, v_this, core_count_);
          ++term_in_count_;
          if (get(out_, v_this))
            ++term_both_count_;
        }

        if (!get(out_, v_this)) {
          put(out_, v_this, core_count_);
          ++term_out_count_;
          if (get(in_, v_this))
            ++term_both_count_;
        }

        BGL_FORALL_INEDGES_T(v_this, e, graph_this_, GraphThis) {
          vertex_this_type w = source(e, graph_this_);
          if (!get(in_, w)) {
            put(in_, w, core_count_);
            ++term_in_count_;
            if (get(out_, w))
              ++term_both_count_;
          }
        }

        BGL_FORALL_OUTEDGES_T(v_this, e, graph_this_, GraphThis) {
          vertex_this_type w = target(e, graph_this_);
          if (!get(out_, w)) {
            put(out_, w, core_count_);
            ++term_out_count_;
            if (get(in_, w))
              ++term_both_count_;
          }
        }

      }

      // Removes vertex pair from state of graph_this
      void pop(const vertex_this_type& v_this, const vertex_other_type&) {

        if (!core_count_) return;

        if (get(in_, v_this) == core_count_) {
          put(in_, v_this, 0);
          --term_in_count_;
          if (get(out_, v_this))
            --term_both_count_;
        }

        BGL_FORALL_INEDGES_T(v_this, e, graph_this_, GraphThis) {
          vertex_this_type w = source(e, graph_this_);
          if (get(in_, w) == core_count_) {
            put(in_, w, 0);
            --term_in_count_;
            if (get(out_, w))
              --term_both_count_;
          }
        }

        if (get(out_, v_this) == core_count_) {
          put(out_, v_this, 0);
          --term_out_count_;
          if (get(in_, v_this))
            --term_both_count_;
        }

        BGL_FORALL_OUTEDGES_T(v_this, e, graph_this_, GraphThis) {
          vertex_this_type w = target(e, graph_this_);
          if (get(out_, w) == core_count_) {
            put(out_, w, 0);
            --term_out_count_;
            if (get(in_, w))
              --term_both_count_;
          }
        }
        put(core_, v_this, graph_traits<GraphOther>::null_vertex());

        --core_count_;

      }

      // Returns true if the in-terminal set is not empty
      bool term_in() const {
        return core_count_ < term_in_count_ ;
      }

      // Returns true if vertex belongs to the in-terminal set
      bool term_in(const vertex_this_type& v) const {
        return (get(in_, v) > 0) &&
               (get(core_, v) == graph_traits<GraphOther>::null_vertex());
      }

      // Returns true if the out-terminal set is not empty
      bool term_out() const {
        return core_count_ < term_out_count_;
      }

      // Returns true if vertex belongs to the out-terminal set
      bool term_out(const vertex_this_type& v) const {
        return (get(out_, v) > 0) &&
               (get(core_, v) == graph_traits<GraphOther>::null_vertex());
      }

      // Returns true of both (in- and out-terminal) sets are not empty
      bool term_both() const {
        return core_count_ < term_both_count_;
      }

      // Returns true if vertex belongs to both (in- and out-terminal) sets
      bool term_both(const vertex_this_type& v) const {
        return (get(in_, v) > 0) && (get(out_, v) > 0) &&
               (get(core_, v) == graph_traits<GraphOther>::null_vertex());
      }

      // Returns true if vertex belongs to the core map, i.e. it is in the
      // present mapping
      bool in_core(const vertex_this_type& v) const {
        return get(core_, v) != graph_traits<GraphOther>::null_vertex();
      }

      // Returns the number of vertices in the mapping
      size_type count() const {
        return core_count_;
      }

      // Returns the image (in graph_other) of vertex v (in graph_this)
      vertex_other_type core(const vertex_this_type& v) const {
        return get(core_, v);
      }

      // Returns the mapping
      core_map_type get_map() const {
        return core_;
      }

      // Returns the "time" (or depth) when vertex was added to the in-terminal set
      size_type in_depth(const vertex_this_type& v) const {
        return get(in_, v);
      }

      // Returns the "time" (or depth) when vertex was added to the out-terminal set
      size_type out_depth(const vertex_this_type& v) const {
        return get(out_, v);
      }

      // Returns the terminal set counts
      boost::tuple<size_type, size_type, size_type>
      term_set() const {
        return boost::make_tuple(term_in_count_, term_out_count_,
                                 term_both_count_);
      }

    };

    // Function object that checks whether a valid edge
    // exists. For multi-graphs matched edges are excluded
    template <typename Graph, typename Enable = void>
    struct equivalent_edge_exists {
      typedef typename boost::graph_traits<Graph>::edge_descriptor edge_type;

      BOOST_CONCEPT_ASSERT(( LessThanComparable<edge_type> ));

      template<typename EdgePredicate>
      bool operator()(typename graph_traits<Graph>::vertex_descriptor s,
                      typename graph_traits<Graph>::vertex_descriptor t,
                      EdgePredicate is_valid_edge, const Graph& g) {

        BGL_FORALL_OUTEDGES_T(s, e, g, Graph) {
          if ((target(e, g) == t) && is_valid_edge(e) &&
              (matched_edges_.find(e) == matched_edges_.end())) {
            matched_edges_.insert(e);
            return true;
          }
        }

        return false;
      }

    private:

      std::set<edge_type> matched_edges_;
    };

    template <typename Graph>
    struct equivalent_edge_exists<Graph, typename boost::disable_if<is_multigraph<Graph> >::type> {
      template<typename EdgePredicate>
      bool operator()(typename graph_traits<Graph>::vertex_descriptor s,
                      typename graph_traits<Graph>::vertex_descriptor t,
                      EdgePredicate is_valid_edge, const Graph& g) {

        typename graph_traits<Graph>::edge_descriptor e;
        bool found;
        boost::tie(e, found) = edge(s, t, g);
        if (!found)
          return false;
        else if (is_valid_edge(e))
          return true;

        return false;
      }

    };

    // Generates a predicate for edge e1 given  a binary predicate and a
    // fixed edge e2
    template <typename Graph1,
              typename Graph2,
              typename EdgeEquivalencePredicate>
    struct edge1_predicate {

      edge1_predicate(EdgeEquivalencePredicate edge_comp,
                      typename graph_traits<Graph2>::edge_descriptor e2)
        : edge_comp_(edge_comp), e2_(e2) {}

      bool operator()(typename graph_traits<Graph1>::edge_descriptor e1) {
        return edge_comp_(e1, e2_);
      }

      EdgeEquivalencePredicate edge_comp_;
      typename graph_traits<Graph2>::edge_descriptor e2_;
    };

    // Generates a predicate for edge e2 given given a binary predicate and a
    // fixed edge e1
    template <typename Graph1,
              typename Graph2,
              typename EdgeEquivalencePredicate>
    struct edge2_predicate {

      edge2_predicate(EdgeEquivalencePredicate edge_comp,
                      typename graph_traits<Graph1>::edge_descriptor e1)
        : edge_comp_(edge_comp), e1_(e1) {}

      bool operator()(typename graph_traits<Graph2>::edge_descriptor e2) {
        return edge_comp_(e1_, e2);
      }

      EdgeEquivalencePredicate edge_comp_;
      typename graph_traits<Graph1>::edge_descriptor e1_;
    };

    enum problem_selector {subgraph_mono, subgraph_iso, isomorphism };

    // The actual state associated with both graphs
    template<typename Graph1,
             typename Graph2,
             typename IndexMap1,
             typename IndexMap2,
             typename EdgeEquivalencePredicate,
             typename VertexEquivalencePredicate,
             typename SubGraphIsoMapCallback,
             problem_selector problem_selection>
    class state {

      typedef typename graph_traits<Graph1>::vertex_descriptor vertex1_type;
      typedef typename graph_traits<Graph2>::vertex_descriptor vertex2_type;

      typedef typename graph_traits<Graph1>::edge_descriptor edge1_type;
      typedef typename graph_traits<Graph2>::edge_descriptor edge2_type;

      typedef typename graph_traits<Graph1>::vertices_size_type graph1_size_type;
      typedef typename graph_traits<Graph2>::vertices_size_type graph2_size_type;

      const Graph1& graph1_;
      const Graph2& graph2_;

      IndexMap1 index_map1_;

      EdgeEquivalencePredicate edge_comp_;
      VertexEquivalencePredicate vertex_comp_;

      base_state<Graph1, Graph2, IndexMap1, IndexMap2> state1_;
      base_state<Graph2, Graph1, IndexMap2, IndexMap1> state2_;

      // Three helper functions used in Feasibility and Valid functions to test
      // terminal set counts when testing for:
      // - graph sub-graph monomorphism, or
      inline bool comp_term_sets(graph1_size_type a,
                                 graph2_size_type b,
                                 boost::mpl::int_<subgraph_mono>) const {
        return a <= b;
      }

      // - graph sub-graph isomorphism, or
      inline bool comp_term_sets(graph1_size_type a,
                                 graph2_size_type b,
                                 boost::mpl::int_<subgraph_iso>) const {
        return a <= b;
      }

      // - graph isomorphism
      inline bool comp_term_sets(graph1_size_type a,
                                 graph2_size_type b,
                                 boost::mpl::int_<isomorphism>) const {
        return a == b;
      }

      // Forbidden
      state(const state&);
      state& operator=(const state&);

    public:

      state(const Graph1& graph1, const Graph2& graph2,
            IndexMap1 index_map1, IndexMap2 index_map2,
            EdgeEquivalencePredicate edge_comp,
            VertexEquivalencePredicate vertex_comp)
        : graph1_(graph1), graph2_(graph2),
          index_map1_(index_map1),
          edge_comp_(edge_comp), vertex_comp_(vertex_comp),
          state1_(graph1, graph2, index_map1, index_map2),
          state2_(graph2, graph1, index_map2, index_map1) {}

            // Add vertex pair to the state
      void push(const vertex1_type& v, const vertex2_type& w) {
        state1_.push(v, w);
        state2_.push(w, v);
      }

      // Remove vertex pair from state
      void pop(const vertex1_type& v, const vertex2_type&) {
        vertex2_type w = state1_.core(v);
        state1_.pop(v, w);
        state2_.pop(w, v);
      }

      // Checks the feasibility of a new vertex pair
      bool feasible(const vertex1_type& v_new, const vertex2_type& w_new) {

        if (!vertex_comp_(v_new, w_new)) return false;

        // graph1
        graph1_size_type term_in1_count = 0, term_out1_count = 0, rest1_count = 0;

        {
          equivalent_edge_exists<Graph2> edge2_exists;

          BGL_FORALL_INEDGES_T(v_new, e1, graph1_, Graph1) {
            vertex1_type v = source(e1, graph1_);

            if (state1_.in_core(v) || (v == v_new)) {
              vertex2_type w = w_new;
              if (v != v_new)
                w = state1_.core(v);
              if (!edge2_exists(w, w_new,
                                edge2_predicate<Graph1, Graph2, EdgeEquivalencePredicate>(edge_comp_, e1),
                                graph2_))
                return false;

            } else {
              if (0 < state1_.in_depth(v))
                ++term_in1_count;
              if (0 < state1_.out_depth(v))
                ++term_out1_count;
              if ((state1_.in_depth(v) == 0) && (state1_.out_depth(v) == 0))
                ++rest1_count;
            }
          }
        }

        {
          equivalent_edge_exists<Graph2> edge2_exists;

          BGL_FORALL_OUTEDGES_T(v_new, e1, graph1_, Graph1) {
            vertex1_type v = target(e1, graph1_);
            if (state1_.in_core(v) || (v == v_new)) {
              vertex2_type w = w_new;
              if (v != v_new)
                w = state1_.core(v);

              if (!edge2_exists(w_new, w,
                                edge2_predicate<Graph1, Graph2, EdgeEquivalencePredicate>(edge_comp_, e1),
                                graph2_))
                return false;

            } else {
              if (0 < state1_.in_depth(v))
                ++term_in1_count;
              if (0 < state1_.out_depth(v))
                ++term_out1_count;
              if ((state1_.in_depth(v) == 0) && (state1_.out_depth(v) == 0))
                ++rest1_count;
            }
          }
        }

        // graph2
        graph2_size_type term_out2_count = 0, term_in2_count = 0, rest2_count = 0;

        {
          equivalent_edge_exists<Graph1> edge1_exists;

          BGL_FORALL_INEDGES_T(w_new, e2, graph2_, Graph2) {
            vertex2_type w = source(e2, graph2_);
            if (state2_.in_core(w) || (w == w_new)) {
              if (problem_selection != subgraph_mono) {
                vertex1_type v = v_new;
                if (w != w_new)
                  v = state2_.core(w);

                if (!edge1_exists(v, v_new,
                                  edge1_predicate<Graph1, Graph2, EdgeEquivalencePredicate>(edge_comp_, e2),
                                  graph1_))
                  return false;
              }
            } else {
              if (0 < state2_.in_depth(w))
                ++term_in2_count;
              if (0 < state2_.out_depth(w))
                ++term_out2_count;
              if ((state2_.in_depth(w) == 0) && (state2_.out_depth(w) == 0))
                ++rest2_count;
            }
          }
        }

        {
          equivalent_edge_exists<Graph1> edge1_exists;

          BGL_FORALL_OUTEDGES_T(w_new, e2, graph2_, Graph2) {
            vertex2_type w = target(e2, graph2_);
            if (state2_.in_core(w) || (w == w_new)) {
              if (problem_selection != subgraph_mono) {
                vertex1_type v = v_new;
                if (w != w_new)
                  v = state2_.core(w);

                if (!edge1_exists(v_new, v,
                                  edge1_predicate<Graph1, Graph2, EdgeEquivalencePredicate>(edge_comp_, e2),
                                  graph1_))
                  return false;
              }
            } else {
              if (0 < state2_.in_depth(w))
                ++term_in2_count;
              if (0 < state2_.out_depth(w))
                ++term_out2_count;
              if ((state2_.in_depth(w) == 0) && (state2_.out_depth(w) == 0))
                ++rest2_count;
            }
          }
        }

        if (problem_selection != subgraph_mono) { // subgraph_iso and isomorphism
          return comp_term_sets(term_in1_count, term_in2_count,
                                boost::mpl::int_<problem_selection>()) &&
                 comp_term_sets(term_out1_count, term_out2_count,
                                boost::mpl::int_<problem_selection>()) &&
                 comp_term_sets(rest1_count, rest2_count,
                                boost::mpl::int_<problem_selection>());
        } else { // subgraph_mono
          return comp_term_sets(term_in1_count, term_in2_count,
                                boost::mpl::int_<problem_selection>()) &&
                 comp_term_sets(term_out1_count, term_out2_count,
                                boost::mpl::int_<problem_selection>()) &&
                 comp_term_sets(term_in1_count + term_out1_count + rest1_count,
                                term_in2_count + term_out2_count + rest2_count,
                                boost::mpl::int_<problem_selection>());
        }
      }

      // Returns true if vertex v in graph1 is a possible candidate to
      // be added to the current state
      bool possible_candidate1(const vertex1_type& v) const {
        if (state1_.term_both() && state2_.term_both())
          return state1_.term_both(v);
        else if (state1_.term_out() && state2_.term_out())
          return state1_.term_out(v);
        else if (state1_.term_in() && state2_.term_in())
          return state1_.term_in(v);
        else
          return !state1_.in_core(v);
      }

      // Returns true if vertex w in graph2 is a possible candidate to
      // be added to the current state
      bool possible_candidate2(const vertex2_type& w) const {
        if (state1_.term_both() && state2_.term_both())
          return state2_.term_both(w);
        else if (state1_.term_out() && state2_.term_out())
          return state2_.term_out(w);
        else if (state1_.term_in() && state2_.term_in())
          return state2_.term_in(w);
        else
          return !state2_.in_core(w);
      }

      // Returns true if a mapping was found
      bool success() const {
        return state1_.count() == num_vertices(graph1_);
      }

      // Returns true if a state is valid
      bool valid() const {
        boost::tuple<graph1_size_type, graph1_size_type, graph1_size_type> term1;
        boost::tuple<graph2_size_type, graph2_size_type, graph2_size_type> term2;

        term1 = state1_.term_set();
        term2 = state2_.term_set();

        return comp_term_sets(boost::get<0>(term1), boost::get<0>(term2),
                              boost::mpl::int_<problem_selection>()) &&
               comp_term_sets(boost::get<1>(term1), boost::get<1>(term2),
                              boost::mpl::int_<problem_selection>()) &&
               comp_term_sets(boost::get<2>(term1), boost::get<2>(term2),
                              boost::mpl::int_<problem_selection>());
      }

      // Calls the user_callback with a graph (sub)graph mapping
      bool call_back(SubGraphIsoMapCallback user_callback) const {
        return user_callback(state1_.get_map(), state2_.get_map());
      }

    };

    // Data structure to keep info used for back tracking during
    // matching process
    template<typename Graph1,
             typename Graph2,
             typename VertexOrder1>
    struct vf2_match_continuation {
      typename VertexOrder1::const_iterator graph1_verts_iter;
      typename graph_traits<Graph2>::vertex_iterator graph2_verts_iter;
    };

    // Non-recursive method that explores state space using a depth-first
    // search strategy.  At each depth possible pairs candidate are compute
    // and tested for feasibility to extend the mapping. If a complete
    // mapping is found, the mapping is output to user_callback in the form
    // of a correspondence map (graph1 to graph2). Returning false from the
    // user_callback will terminate the search. Function match will return
    // true if the entire search space was explored.
    template<typename Graph1,
             typename Graph2,
             typename IndexMap1,
             typename IndexMap2,
             typename VertexOrder1,
             typename EdgeEquivalencePredicate,
             typename VertexEquivalencePredicate,
             typename SubGraphIsoMapCallback,
             problem_selector problem_selection>
    bool match(const Graph1& graph1, const Graph2& graph2,
               SubGraphIsoMapCallback user_callback, const VertexOrder1& vertex_order1,
               state<Graph1, Graph2, IndexMap1, IndexMap2,
               EdgeEquivalencePredicate, VertexEquivalencePredicate,
               SubGraphIsoMapCallback, problem_selection>& s) {

      typename VertexOrder1::const_iterator graph1_verts_iter;

      typedef typename graph_traits<Graph2>::vertex_iterator vertex2_iterator_type;
      vertex2_iterator_type graph2_verts_iter, graph2_verts_iter_end;

      typedef vf2_match_continuation<Graph1, Graph2, VertexOrder1> match_continuation_type;
      std::vector<match_continuation_type> k;

      recur:
      if (s.success()) {
        if (!s.call_back(user_callback))
          return false;

        goto back_track;
      }

      if (!s.valid())
        goto back_track;

      graph1_verts_iter = vertex_order1.begin();
      while (graph1_verts_iter != vertex_order1.end() &&
             !s.possible_candidate1(*graph1_verts_iter)) {
        ++graph1_verts_iter;
      }

      boost::tie(graph2_verts_iter, graph2_verts_iter_end) = vertices(graph2);
      while (graph2_verts_iter != graph2_verts_iter_end) {
        if (s.possible_candidate2(*graph2_verts_iter)) {
          if (s.feasible(*graph1_verts_iter, *graph2_verts_iter)) {
            match_continuation_type kk;
            kk.graph1_verts_iter = graph1_verts_iter;
            kk.graph2_verts_iter = graph2_verts_iter;
            k.push_back(kk);

            s.push(*graph1_verts_iter, *graph2_verts_iter);
            goto recur;
          }
        }
        graph2_loop: ++graph2_verts_iter;
      }

      back_track:
      if (k.empty())
        return true;

      const match_continuation_type kk = k.back();
      graph1_verts_iter = kk.graph1_verts_iter;
      graph2_verts_iter = kk.graph2_verts_iter;
      k.pop_back();

      s.pop(*graph1_verts_iter, *graph2_verts_iter);

      goto graph2_loop;
    }

    // Used to sort nodes by in/out degrees
    template<typename Graph>
    struct vertex_in_out_degree_cmp {
      typedef typename graph_traits<Graph>::vertex_descriptor vertex_type;

      vertex_in_out_degree_cmp(const Graph& graph)
        : graph_(graph) {}

      bool operator()(const vertex_type& v, const vertex_type& w) const {
        // lexicographical comparison
        return std::make_pair(in_degree(v, graph_), out_degree(v, graph_)) <
               std::make_pair(in_degree(w, graph_), out_degree(w, graph_));
      }

      const Graph& graph_;
    };

    // Used to sort nodes by multiplicity of in/out degrees
    template<typename Graph,
             typename FrequencyMap>
    struct vertex_frequency_degree_cmp {
      typedef typename graph_traits<Graph>::vertex_descriptor vertex_type;

      vertex_frequency_degree_cmp(const Graph& graph, FrequencyMap freq)
        : graph_(graph), freq_(freq) {}

      bool operator()(const vertex_type& v, const vertex_type& w) const {
        // lexicographical comparison
        return std::make_pair(freq_[v], in_degree(v, graph_)+out_degree(v, graph_)) <
               std::make_pair(freq_[w], in_degree(w, graph_)+out_degree(w, graph_));
      }

      const Graph& graph_;
      FrequencyMap freq_;
    };

    // Sorts vertices of a graph by multiplicity of in/out degrees
    template<typename Graph,
             typename IndexMap,
             typename VertexOrder>
    void sort_vertices(const Graph& graph, IndexMap index_map, VertexOrder& order) {
      typedef typename graph_traits<Graph>::vertices_size_type size_type;

      boost::range::sort(order, vertex_in_out_degree_cmp<Graph>(graph));

      std::vector<size_type> freq_vec(num_vertices(graph), 0);
      typedef iterator_property_map<typename std::vector<size_type>::iterator,
                                    IndexMap, size_type, size_type&> frequency_map_type;

      frequency_map_type freq = make_iterator_property_map(freq_vec.begin(), index_map);

      typedef typename VertexOrder::iterator order_iterator;

      for (order_iterator order_iter = order.begin(); order_iter != order.end(); ) {
        size_type count = 0;
        for (order_iterator count_iter = order_iter;
             (count_iter != order.end()) &&
             (in_degree(*order_iter, graph) == in_degree(*count_iter, graph)) &&
             (out_degree(*order_iter, graph) == out_degree(*count_iter, graph));
             ++count_iter)
          ++count;

        for (size_type i = 0; i < count; ++i) {
          freq[*order_iter] = count;
          ++order_iter;
        }
      }

      boost::range::sort(order, vertex_frequency_degree_cmp<Graph, frequency_map_type>(graph, freq));

    }

    // Enumerates all graph sub-graph mono-/iso-morphism mappings between graphs
    // graph_small and graph_large. Continues until user_callback returns true or the
    // search space has been fully explored.
    template <problem_selector problem_selection,
              typename GraphSmall,
              typename GraphLarge,
              typename IndexMapSmall,
              typename IndexMapLarge,
              typename VertexOrderSmall,
              typename EdgeEquivalencePredicate,
              typename VertexEquivalencePredicate,
              typename SubGraphIsoMapCallback>
    bool vf2_subgraph_morphism(const GraphSmall& graph_small, const GraphLarge& graph_large,
                          SubGraphIsoMapCallback user_callback,
                          IndexMapSmall index_map_small, IndexMapLarge index_map_large,
                          const VertexOrderSmall& vertex_order_small,
                          EdgeEquivalencePredicate edge_comp,
                          VertexEquivalencePredicate vertex_comp) {

      // Graph requirements
      BOOST_CONCEPT_ASSERT(( BidirectionalGraphConcept<GraphSmall> ));
      BOOST_CONCEPT_ASSERT(( VertexListGraphConcept<GraphSmall> ));
      BOOST_CONCEPT_ASSERT(( EdgeListGraphConcept<GraphSmall> ));
      BOOST_CONCEPT_ASSERT(( AdjacencyMatrixConcept<GraphSmall> ));

      BOOST_CONCEPT_ASSERT(( BidirectionalGraphConcept<GraphLarge> ));
      BOOST_CONCEPT_ASSERT(( VertexListGraphConcept<GraphLarge> ));
      BOOST_CONCEPT_ASSERT(( EdgeListGraphConcept<GraphLarge> ));
      BOOST_CONCEPT_ASSERT(( AdjacencyMatrixConcept<GraphLarge> ));

      typedef typename graph_traits<GraphSmall>::vertex_descriptor vertex_small_type;
      typedef typename graph_traits<GraphLarge>::vertex_descriptor vertex_large_type;

      typedef typename graph_traits<GraphSmall>::vertices_size_type size_type_small;
      typedef typename graph_traits<GraphLarge>::vertices_size_type size_type_large;

      // Property map requirements
      BOOST_CONCEPT_ASSERT(( ReadablePropertyMapConcept<IndexMapSmall, vertex_small_type> ));
      typedef typename property_traits<IndexMapSmall>::value_type IndexMapSmallValue;
      BOOST_STATIC_ASSERT(( is_convertible<IndexMapSmallValue, size_type_small>::value ));

      BOOST_CONCEPT_ASSERT(( ReadablePropertyMapConcept<IndexMapLarge, vertex_large_type> ));
      typedef typename property_traits<IndexMapLarge>::value_type IndexMapLargeValue;
      BOOST_STATIC_ASSERT(( is_convertible<IndexMapLargeValue, size_type_large>::value ));

      // Edge & vertex requirements
      typedef typename graph_traits<GraphSmall>::edge_descriptor edge_small_type;
      typedef typename graph_traits<GraphLarge>::edge_descriptor edge_large_type;

      BOOST_CONCEPT_ASSERT(( BinaryPredicateConcept<EdgeEquivalencePredicate,
                             edge_small_type, edge_large_type> ));

      BOOST_CONCEPT_ASSERT(( BinaryPredicateConcept<VertexEquivalencePredicate,
                             vertex_small_type, vertex_large_type> ));

      // Vertex order requirements
      BOOST_CONCEPT_ASSERT(( ContainerConcept<VertexOrderSmall> ));
      typedef typename VertexOrderSmall::value_type order_value_type;
      BOOST_STATIC_ASSERT(( is_same<vertex_small_type, order_value_type>::value ));
      BOOST_ASSERT( num_vertices(graph_small) == vertex_order_small.size() );

      if (num_vertices(graph_small) > num_vertices(graph_large))
        return false;

      typename graph_traits<GraphSmall>::edges_size_type num_edges_small = num_edges(graph_small);
      typename graph_traits<GraphLarge>::edges_size_type num_edges_large = num_edges(graph_large);

      // Double the number of edges for undirected graphs: each edge counts as
      // in-edge and out-edge
      if (is_undirected(graph_small)) num_edges_small *= 2;
      if (is_undirected(graph_large)) num_edges_large *= 2;
      if (num_edges_small > num_edges_large)
        return false;

      if ((num_vertices(graph_small) == 0) && (num_vertices(graph_large) == 0))
        return true;

      detail::state<GraphSmall, GraphLarge, IndexMapSmall, IndexMapLarge,
                    EdgeEquivalencePredicate, VertexEquivalencePredicate,
                    SubGraphIsoMapCallback, problem_selection>
        s(graph_small, graph_large, index_map_small, index_map_large, edge_comp, vertex_comp);

      return detail::match(graph_small, graph_large, user_callback, vertex_order_small, s);
    }

  } // namespace detail

  // Returns vertex order (vertices sorted by multiplicity of in/out degrees)
  template<typename Graph>
  std::vector<typename graph_traits<Graph>::vertex_descriptor>
    vertex_order_by_mult(const Graph& graph) {

    std::vector<typename graph_traits<Graph>::vertex_descriptor> vertex_order;
    std::copy(vertices(graph).first, vertices(graph).second, std::back_inserter(vertex_order));

    detail::sort_vertices(graph, get(vertex_index, graph), vertex_order);
    return vertex_order;
  }

  // Enumerates all graph sub-graph monomorphism mappings between graphs
  // graph_small and graph_large. Continues until user_callback returns true or the
  // search space has been fully explored.
  template <typename GraphSmall,
            typename GraphLarge,
            typename IndexMapSmall,
            typename IndexMapLarge,
            typename VertexOrderSmall,
            typename EdgeEquivalencePredicate,
            typename VertexEquivalencePredicate,
            typename SubGraphIsoMapCallback>
  bool vf2_subgraph_mono(const GraphSmall& graph_small, const GraphLarge& graph_large,
                         SubGraphIsoMapCallback user_callback,
                         IndexMapSmall index_map_small, IndexMapLarge index_map_large,
                         const VertexOrderSmall& vertex_order_small,
                         EdgeEquivalencePredicate edge_comp,
                         VertexEquivalencePredicate vertex_comp) {
    return detail::vf2_subgraph_morphism<detail::subgraph_mono>
                                        (graph_small, graph_large,
                                         user_callback,
                                         index_map_small, index_map_large,
                                         vertex_order_small,
                                         edge_comp,
                                         vertex_comp);
  }

  // All default interface for vf2_subgraph_iso
  template <typename GraphSmall,
            typename GraphLarge,
            typename SubGraphIsoMapCallback>
  bool vf2_subgraph_mono(const GraphSmall& graph_small, const GraphLarge& graph_large,
                         SubGraphIsoMapCallback user_callback) {
    return vf2_subgraph_mono(graph_small, graph_large, user_callback,
                             get(vertex_index, graph_small), get(vertex_index, graph_large),
                             vertex_order_by_mult(graph_small),
                             always_equivalent(), always_equivalent());
  }

  // Named parameter interface of vf2_subgraph_iso
  template <typename GraphSmall,
            typename GraphLarge,
            typename VertexOrderSmall,
            typename SubGraphIsoMapCallback,
            typename Param,
            typename Tag,
            typename Rest>
  bool vf2_subgraph_mono(const GraphSmall& graph_small, const GraphLarge& graph_large,
                         SubGraphIsoMapCallback user_callback,
                         const VertexOrderSmall& vertex_order_small,
                         const bgl_named_params<Param, Tag, Rest>& params) {
    return vf2_subgraph_mono(graph_small, graph_large, user_callback,
                             choose_const_pmap(get_param(params, vertex_index1),
                                               graph_small, vertex_index),
                             choose_const_pmap(get_param(params, vertex_index2),
                                               graph_large, vertex_index),
                             vertex_order_small,
                             choose_param(get_param(params, edges_equivalent_t()),
                                          always_equivalent()),
                             choose_param(get_param(params, vertices_equivalent_t()),
                                          always_equivalent())
                             );
  }

  // Enumerates all graph sub-graph isomorphism mappings between graphs
  // graph_small and graph_large. Continues until user_callback returns true or the
  // search space has been fully explored.
  template <typename GraphSmall,
            typename GraphLarge,
            typename IndexMapSmall,
            typename IndexMapLarge,
            typename VertexOrderSmall,
            typename EdgeEquivalencePredicate,
            typename VertexEquivalencePredicate,
            typename SubGraphIsoMapCallback>
  bool vf2_subgraph_iso(const GraphSmall& graph_small, const GraphLarge& graph_large,
                        SubGraphIsoMapCallback user_callback,
                        IndexMapSmall index_map_small, IndexMapLarge index_map_large,
                        const VertexOrderSmall& vertex_order_small,
                        EdgeEquivalencePredicate edge_comp,
                        VertexEquivalencePredicate vertex_comp) {
    return detail::vf2_subgraph_morphism<detail::subgraph_iso>
                                        (graph_small, graph_large,
                                         user_callback,
                                         index_map_small, index_map_large,
                                         vertex_order_small,
                                         edge_comp,
                                         vertex_comp);
  }

  // All default interface for vf2_subgraph_iso
  template <typename GraphSmall,
            typename GraphLarge,
            typename SubGraphIsoMapCallback>
  bool vf2_subgraph_iso(const GraphSmall& graph_small, const GraphLarge& graph_large,
                        SubGraphIsoMapCallback user_callback) {

    typedef typename graph_traits<GraphSmall>::vertex_descriptor vertex_small_type;

    return vf2_subgraph_iso(graph_small, graph_large, user_callback,
                            get(vertex_index, graph_small), get(vertex_index, graph_large),
                            vertex_order_by_mult(graph_small),
                            always_equivalent(), always_equivalent());
  }

  // Named parameter interface of vf2_subgraph_iso
  template <typename GraphSmall,
            typename GraphLarge,
            typename VertexOrderSmall,
            typename SubGraphIsoMapCallback,
            typename Param,
            typename Tag,
            typename Rest>
  bool vf2_subgraph_iso(const GraphSmall& graph_small, const GraphLarge& graph_large,
                        SubGraphIsoMapCallback user_callback,
                        const VertexOrderSmall& vertex_order_small,
                        const bgl_named_params<Param, Tag, Rest>& params) {

    return vf2_subgraph_iso(graph_small, graph_large, user_callback,
                            choose_const_pmap(get_param(params, vertex_index1),
                                              graph_small, vertex_index),
                            choose_const_pmap(get_param(params, vertex_index2),
                                              graph_large, vertex_index),
                            vertex_order_small,
                            choose_param(get_param(params, edges_equivalent_t()),
                                         always_equivalent()),
                            choose_param(get_param(params, vertices_equivalent_t()),
                                         always_equivalent())
                            );

  }

  // Enumerates all isomorphism mappings between graphs graph1_ and graph2_.
  // Continues until user_callback returns true or the search space has been
  // fully explored.
  template <typename Graph1,
            typename Graph2,
            typename IndexMap1,
            typename IndexMap2,
            typename VertexOrder1,
            typename EdgeEquivalencePredicate,
            typename VertexEquivalencePredicate,
            typename GraphIsoMapCallback>
  bool vf2_graph_iso(const Graph1& graph1, const Graph2& graph2,
                     GraphIsoMapCallback user_callback,
                     IndexMap1 index_map1, IndexMap2 index_map2,
                     const VertexOrder1& vertex_order1,
                     EdgeEquivalencePredicate edge_comp,
                     VertexEquivalencePredicate vertex_comp) {

    // Graph requirements
    BOOST_CONCEPT_ASSERT(( BidirectionalGraphConcept<Graph1> ));
    BOOST_CONCEPT_ASSERT(( VertexListGraphConcept<Graph1> ));
    BOOST_CONCEPT_ASSERT(( EdgeListGraphConcept<Graph1> ));
    BOOST_CONCEPT_ASSERT(( AdjacencyMatrixConcept<Graph1> ));

    BOOST_CONCEPT_ASSERT(( BidirectionalGraphConcept<Graph2> ));
    BOOST_CONCEPT_ASSERT(( VertexListGraphConcept<Graph2> ));
    BOOST_CONCEPT_ASSERT(( EdgeListGraphConcept<Graph2> ));
    BOOST_CONCEPT_ASSERT(( AdjacencyMatrixConcept<Graph2> ));

    typedef typename graph_traits<Graph1>::vertex_descriptor vertex1_type;
    typedef typename graph_traits<Graph2>::vertex_descriptor vertex2_type;

    typedef typename graph_traits<Graph1>::vertices_size_type size_type1;
    typedef typename graph_traits<Graph2>::vertices_size_type size_type2;

    // Property map requirements
    BOOST_CONCEPT_ASSERT(( ReadablePropertyMapConcept<IndexMap1, vertex1_type> ));
    typedef typename property_traits<IndexMap1>::value_type IndexMap1Value;
    BOOST_STATIC_ASSERT(( is_convertible<IndexMap1Value, size_type1>::value ));

    BOOST_CONCEPT_ASSERT(( ReadablePropertyMapConcept<IndexMap2, vertex2_type> ));
    typedef typename property_traits<IndexMap2>::value_type IndexMap2Value;
    BOOST_STATIC_ASSERT(( is_convertible<IndexMap2Value, size_type2>::value ));

    // Edge & vertex requirements
    typedef typename graph_traits<Graph1>::edge_descriptor edge1_type;
    typedef typename graph_traits<Graph2>::edge_descriptor edge2_type;

    BOOST_CONCEPT_ASSERT(( BinaryPredicateConcept<EdgeEquivalencePredicate,
                           edge1_type, edge2_type> ));

    BOOST_CONCEPT_ASSERT(( BinaryPredicateConcept<VertexEquivalencePredicate,
                           vertex1_type, vertex2_type> ));

    // Vertex order requirements
    BOOST_CONCEPT_ASSERT(( ContainerConcept<VertexOrder1> ));
    typedef typename VertexOrder1::value_type order_value_type;
    BOOST_STATIC_ASSERT(( is_same<vertex1_type, order_value_type>::value ));
    BOOST_ASSERT( num_vertices(graph1) == vertex_order1.size() );

    if (num_vertices(graph1) != num_vertices(graph2))
      return false;

    typename graph_traits<Graph1>::edges_size_type num_edges1 = num_edges(graph1);
    typename graph_traits<Graph2>::edges_size_type num_edges2 = num_edges(graph2);

    // Double the number of edges for undirected graphs: each edge counts as
    // in-edge and out-edge
    if (is_undirected(graph1)) num_edges1 *= 2;
    if (is_undirected(graph2)) num_edges2 *= 2;
    if (num_edges1 != num_edges2)
      return false;

    if ((num_vertices(graph1) == 0) && (num_vertices(graph2) == 0))
      return true;

    detail::state<Graph1, Graph2, IndexMap1, IndexMap2,
                  EdgeEquivalencePredicate, VertexEquivalencePredicate,
                  GraphIsoMapCallback, detail::isomorphism>
      s(graph1, graph2, index_map1, index_map2, edge_comp, vertex_comp);

    return detail::match(graph1, graph2, user_callback, vertex_order1, s);
  }

  // All default interface for vf2_graph_iso
  template <typename Graph1,
            typename Graph2,
            typename GraphIsoMapCallback>
  bool vf2_graph_iso(const Graph1& graph1, const Graph2& graph2,
                     GraphIsoMapCallback user_callback) {

    typedef typename graph_traits<Graph1>::vertex_descriptor vertex1_type;

    return vf2_graph_iso(graph1, graph2, user_callback,
                         get(vertex_index, graph1), get(vertex_index, graph2),
                         vertex_order_by_mult(graph1),
                         always_equivalent(), always_equivalent());
  }

  // Named parameter interface of vf2_graph_iso
  template <typename Graph1,
            typename Graph2,
            typename VertexOrder1,
            typename GraphIsoMapCallback,
            typename Param,
            typename Tag,
            typename Rest>
  bool vf2_graph_iso(const Graph1& graph1, const Graph2& graph2,
                     GraphIsoMapCallback user_callback,
                     const VertexOrder1& vertex_order1,
                     const bgl_named_params<Param, Tag, Rest>& params) {

    return vf2_graph_iso(graph1, graph2, user_callback,
                         choose_const_pmap(get_param(params, vertex_index1),
                                           graph1, vertex_index),
                         choose_const_pmap(get_param(params, vertex_index2),
                                           graph2, vertex_index),
                         vertex_order1,
                         choose_param(get_param(params, edges_equivalent_t()),
                                always_equivalent()),
                         choose_param(get_param(params, vertices_equivalent_t()),
                                      always_equivalent())
                         );

  }

  // Verifies a graph (sub)graph isomorphism map
  template<typename Graph1,
           typename Graph2,
           typename CorresponenceMap1To2,
           typename EdgeEquivalencePredicate,
           typename VertexEquivalencePredicate>
  inline bool verify_vf2_subgraph_iso(const Graph1& graph1, const Graph2& graph2,
                                      const CorresponenceMap1To2 f,
                                      EdgeEquivalencePredicate edge_comp,
                                      VertexEquivalencePredicate vertex_comp) {

    BOOST_CONCEPT_ASSERT(( EdgeListGraphConcept<Graph1> ));
    BOOST_CONCEPT_ASSERT(( AdjacencyMatrixConcept<Graph2> ));

    detail::equivalent_edge_exists<Graph2> edge2_exists;

    BGL_FORALL_EDGES_T(e1, graph1, Graph1) {
      typename graph_traits<Graph1>::vertex_descriptor s1, t1;
      typename graph_traits<Graph2>::vertex_descriptor s2, t2;

      s1 = source(e1, graph1); t1 = target(e1, graph1);
      s2 = get(f, s1); t2 = get(f, t1);

      if (!vertex_comp(s1, s2) || !vertex_comp(t1, t2))
        return false;

      typename graph_traits<Graph2>::edge_descriptor e2;

      if (!edge2_exists(s2, t2,
                        detail::edge2_predicate<Graph1, Graph2, EdgeEquivalencePredicate>(edge_comp, e1),
                        graph2))
        return false;

    }

    return true;
  }

  // Variant of verify_subgraph_iso with all default parameters
  template<typename Graph1,
           typename Graph2,
           typename CorresponenceMap1To2>
  inline bool verify_vf2_subgraph_iso(const Graph1& graph1, const Graph2& graph2,
                                      const CorresponenceMap1To2 f) {
    return verify_vf2_subgraph_iso(graph1, graph2, f,
                                   always_equivalent(), always_equivalent());
  }

} // namespace boost

#ifdef BOOST_ISO_INCLUDED_ITER_MACROS
#undef BOOST_ISO_INCLUDED_ITER_MACROS
#include <boost/graph/iteration_macros_undef.hpp>
#endif

#endif // BOOST_VF2_SUB_GRAPH_ISO_HPP


/*
  Author(s):      Robert Patterson and Markus Sander
  Project:        sweepc (population balance solver)
  Sourceforge:    http://sourceforge.net/projects/mopssuite

  Copyright (C) 2008 Matthew S Celnik.

  File purpose:
    Implementation of the PAHInception class declared in the
    swp_PAHInception.h header file.

  Licence:
    This file is part of "sweepc".

    sweepc is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public License
    as published by the Free Software Foundation; either version 2
    of the License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

  Contact:
    Prof Markus Kraft
    Dept of Chemical Engineering
    University of Cambridge
    New Museums Site
    Pembroke Street
    Cambridge
    CB2 3RA
    UK

    Email:       mk306@cam.ac.uk
    Website:     http://como.cheng.cam.ac.uk
*/

#include "swp_pah_inception.h"
#include "swp_mechanism.h"

#include "local_geometry1d.h"

#include <boost/random/uniform_01.hpp>

using namespace Sweep;
using namespace Sweep::Processes;
using namespace std;

// CONSTRUCTORS AND DESTRUCTORS.

// Default constructor (protected).
PAHInception::PAHInception(void)
: Inception()
{
    m_name = "PAHInception";
}

// Initialising constructor.
PAHInception::PAHInception(const Sweep::Mechanism &mech,
                          const EnvironmentInterface::PropertyIndex pah_index)
: Inception(mech)
, mPAHFormationIndex(pah_index)
{
    m_name = "PAHInception";
}

// Copy constructor.
PAHInception::PAHInception(const PAHInception &copy)
{
    *this = copy;
}

// Stream-reading constructor.
PAHInception::PAHInception(std::istream &in, const Sweep::Mechanism &mech)
{
    Deserialize(in, mech);
}

// Default destructor.
PAHInception::~PAHInception(void)
{
}

// OPERATOR OVERLOADS.

// Assignment operator.
PAHInception &PAHInception::operator =(const PAHInception &rhs)
{
    if (this != &rhs) {
        Inception::operator =(rhs);

    }
    return *this;
}

/*!
 * Create a new particle and add it to the ensemble with position uniformly
 * distributed over the grid cell, if it is of positive size.
 *
 * The iterm parameter is included because it will be needed for many process
 * types and this function is meant to have a general signature.
 *
 * \param[in]       t           Time
 * \param[in,out]   sys         System to update
 * \param[in]       local_geom  Details of local phsyical layout
 * \param[in]       iterm       Process term responsible for this event
 * \param[in,out]   rng         Random number generator
 *
 * \return      0 on success, otherwise negative.
 */
int PAHInception::Perform(const double t, Cell &sys,
                          const Geometry::LocalGeometry1d &local_geom,
                          const unsigned int iterm,
                          rng_type &rng) const {

	Particle *sp = NULL;

	//If using weighted PAHs...
	if (sys.ParticleModel()->Components(0)->WeightedPAHs()){
	//Check to see if a particle that matches that of the incepted PAHs already exists in the emsemble
		int j = sys.Particles().NumOfInceptedPAH(m_mech->AggModel(), 0);
		if (j > 0) {
			//There is already an inception PAH in the ensemble (should only be 1).
			//Just update it's statistical weight
			int Pindex = sys.Particles().IndexOfInceptedPAH(m_mech->AggModel(), 0);
			sp = sys.Particles().At(Pindex);
			int StatWeight = sp->getStatisticalWeight();
			sp->setStatisticalWeight(StatWeight + 1.0);
			sys.Particles().Update(Pindex);
			return 0;
		}
	}

	// Get the cell vertices
	fvector vertices = local_geom.cellVertices();

	// Sample a uniformly distributed position, note that this method
	// works whether the vertices come in increasing or decreasing order,
	// but 1d is assumed for now.
	double posn = vertices.front();

	const double width = vertices.back() - posn;

	if (width > 0) {
		boost::uniform_01<rng_type&, double> uniformGenerator(rng);
		// There is some double spatial detail
		posn += width * uniformGenerator();
		sp = m_mech->CreateParticle(t, posn);
	}
	else {
		// Ignore all questions of position
		sp = m_mech->CreateParticle(t, 0);
		//This means creating the first PAH in the inception list. Not tested gl413
	}

	sp->UpdateCache();

	// Add particle to main ensemble.
	if (sys.ParticleCount() < sys.Particles().Capacity()){
		sys.Particles().Add(*sp, rng);
	}
	else
	{
		std::cout << "No room in ensemble after PAH inception" << std::endl;
		sys.Particles().Add(*sp, rng);
	}

	// Update gas-phase chemistry of system.
	adjustGas(sys, sp->getStatisticalWeight());

    return 0;
}

/*!
 * Ensure the number of InceptedPAHs in the particle ensemble is consistent with that in the gas phase
 * If the number of InceptedPAHs (i.e. A1, A2 or A4) in the particle ensemble is less than that in the gas phase, new InceptedPAHs will be created (transfer from gas phase) and added to the ensemble. In contrast, if the number of InceptedPAHs in the particle ensemble are more than that in the gas phase, the redundant InceptedPAH will be removed accordingly.
 * This function is only used for PAH-PP model currently
 * The mass form gas phase will be transfered to particle ensemble by using this function
 *
 * \param[in]       i           number of pyrene supposed in the ensemble
 * \param[in]		k 			Index of PAH in inception list to incept
 * \param[in]       t           Time
 * \param[in,out]   sys         System to update
 * \param[in]       iterm       Process term responsible for this event
 * \param[in,out]   rng         Random number generator
 *
 * \return      0 on success, otherwise negative.
 */
int PAHInception::AddInceptedPAH(const int i, const int k, const double t, Cell &sys,rng_type &rng) const {

    Particle *sp = NULL;

    // return current number of pyrene in the emsemble
    int j = sys.Particles().NumOfInceptedPAH(m_mech->AggModel(), k);

	if (i > j)
	{
		if (sys.ParticleModel()->Components(0)->WeightedPAHs()){
			if (j > 0) {
				//There is already an inception PAH in the ensemble (should only be 1).
				//Just update it's statistical weight
				int Pindex = sys.Particles().IndexOfInceptedPAH(m_mech->AggModel(), k);
				sp = sys.Particles().At(Pindex);
				int StatWeight = sp->getStatisticalWeight();
				sp->setStatisticalWeight(StatWeight + i - j);
				sys.Particles().Update(Pindex);
			}
			else{
				sp = m_mech->CreateParticle(t, k);
				sp->setStatisticalWeight(i);
				sp->UpdateCache();
				// Add particle to main ensemble.
				sys.Particles().Add(*sp, rng);
			}
		}
		else{
			while (i > j)
			{

				// Ignore all questions of position
				sp = m_mech->CreateParticle(t, k);
				sp->UpdateCache();
				// Add particle to main ensemble.
				sys.Particles().Add(*sp, rng);

				j++;
			}
		}
    }
    else if (i<j){
		if (sys.ParticleModel()->Components(0)->WeightedPAHs()){
			int Pindex = sys.Particles().IndexOfInceptedPAH(m_mech->AggModel(), k);
			sp = sys.Particles().At(Pindex);
			int StatWeight = sp->getStatisticalWeight();
			sp->setStatisticalWeight(StatWeight + i - j);
			sys.Particles().Update(Pindex);
		}
		else{
			while (i < j)
			{
				int Pindex = sys.Particles().IndexOfInceptedPAH(m_mech->AggModel(), k);
				if (Pindex < 0)
					throw runtime_error("There are no InceptedPAH in the ensemble, and all the InceptedPAH molecules are consumed due to unknown reason(Mops, Sweep::PAHInception::Perform).");
				sys.Particles().Remove(Pindex);
				//std::cout<<"j-i is "<<j-i<<std::endl;
				j--;
			}
		}
    }
    //i==j do nothong
    else return 0;
    return 0;
}

// TOTAL RATE CALCULATIONS.

// Returns rate of the process for the given system.
double PAHInception::Rate(double t, const Cell &sys,
                        const Geometry::LocalGeometry1d &local_geom) const
{
    const double rate = NA * sys.GasPhase().PropertyValue(mPAHFormationIndex) * A();

    // PAHFormation rate may be negative which means no inception
    if(rate < 0.0)
        return 0.0;

    return rate *  sys.SampleVolume();
}

// RATE TERM CALCULATIONS.

// Returns the number of rate terms for this process (one).
unsigned int PAHInception::TermCount(void) const {return 1;}

// Calculates the rate terms given an iterator to a double vector. The
// iterator is advanced to the position after the last term for this
// process.  Returns the sum of all terms.
double PAHInception::RateTerms(const double t, const Cell &sys,
                             const Geometry::LocalGeometry1d &local_geom,
                             fvector::iterator &iterm) const
{
    // Calculate the single rate term and advance iterator.
    *iterm = Rate(t, sys, local_geom);
    return *(iterm++);
}

// READ/WRITE/COPY.

// Creates a copy of the inception.
PAHInception *const PAHInception::Clone(void) const {return new PAHInception(*this);}

// Returns the process type.  Used to identify different
// processes and for serialisation.
ProcessType PAHInception::ID(void) const {return PAH_Inception_ID;}

// Writes the object to a binary stream.
void PAHInception::Serialize(std::ostream &out) const
{
    if (out.good()) {
        // Output the version ID (=0 at the moment).
        const unsigned int version = 0;
        out.write((char*)&version, sizeof(version));

        // Serialize base class.
        Inception::Serialize(out);

        out.write(reinterpret_cast<const char*>(&mPAHFormationIndex), sizeof(mPAHFormationIndex));

    } else {
        throw invalid_argument("Output stream not ready "
                               "(Sweep, PAHInception::Serialize).");
    }
}

// Reads the object from a binary stream.
void PAHInception::Deserialize(std::istream &in, const Sweep::Mechanism &mech)
{
    if (in.good()) {
        // Read the output version.  Currently there is only one
        // output version, so we don't do anything with this variable.
        // Still needs to be read though.
        unsigned int version = 0;
        in.read(reinterpret_cast<char*>(&version), sizeof(version));

        switch (version) {
            case 0:
                // Deserialize base class.
                Inception::Deserialize(in, mech);

                in.read(reinterpret_cast<char*>(&mPAHFormationIndex), sizeof(mPAHFormationIndex));
                 break;
            default:
                throw runtime_error("Serialized version number is invalid "
                                    "(Sweep, PAHInception::Deserialize).");
        }
    } else {
        throw invalid_argument("Input stream not ready "
                               "(Sweep, PAHInception::Deserialize).");
    }
}