/* Copyright 2017-2018 PaGMO development team

This file is part of the PaGMO library.

The PaGMO library is free software; you can redistribute it and/or modify
it under the terms of either:

  * the GNU Lesser General Public License as published by the Free
    Software Foundation; either version 3 of the License, or (at your
    option) any later version.

or

  * 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.

or both in parallel, as here.

The PaGMO library 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 copies of the GNU General Public License and the
GNU Lesser General Public License along with the PaGMO library.  If not,
see https://www.gnu.org/licenses/. */

#ifndef PAGMO_UTILS_GENERIC_HPP
#define PAGMO_UTILS_GENERIC_HPP

#include <cassert>
#include <cmath>
#include <limits>
#include <numeric>
#include <random>
#include <stdexcept>
#include <string>
#include <utility>
#include <vector>

#include <boost/numeric/conversion/cast.hpp>

#include <pagmo/detail/custom_comparisons.hpp>
#include <pagmo/exceptions.hpp>
#include <pagmo/problem.hpp>
#include <pagmo/types.hpp>

namespace pagmo
{

namespace detail
{

// Checks that all elements of the problem bounds are not equal
inline bool some_bound_is_equal(const problem &prob)
{
    // Some variable renaming
    const auto &lb = prob.get_lb();
    const auto &ub = prob.get_ub();
    // Since the bounds are extracted from problem we can be sure they have equal length
    for (decltype(lb.size()) i = 0u; i < lb.size(); ++i) {
        if (lb[i] == ub[i]) {
            return true;
        }
    }
    return false;
}

// Check that the lower/upper bounds lb/ub are suitable for the
// generation of a real number. The boolean flags specify at
// compile time which checks to run.
template <bool FiniteCheck, bool LbUbCheck, bool RangeCheck>
inline void uniform_real_from_range_checks(double lb, double ub)
{
    // NOTE: see here for the requirements for floating-point RNGS:
    // http://en.cppreference.com/w/cpp/numeric/random/uniform_real_distribution/uniform_real_distribution

    // 0 - Forbid random generation when bounds are not finite.
    if (FiniteCheck) {
        if (!std::isfinite(lb) || !std::isfinite(ub)) {
            pagmo_throw(std::invalid_argument, "Cannot generate a random real if the bounds are not finite");
        }
    } else {
        assert(std::isfinite(lb) && std::isfinite(ub));
    }

    // 1 - Check that lb is <= ub
    if (LbUbCheck) {
        if (lb > ub) {
            pagmo_throw(std::invalid_argument,
                        "Cannot generate a random real if the lower bound is larger than the upper bound");
        }
    } else {
        assert(lb <= ub);
    }

    // 2 - Bounds cannot be too large
    if (RangeCheck) {
        const auto delta = ub - lb;
        if (!std::isfinite(delta) || delta > std::numeric_limits<double>::max()) {
            pagmo_throw(std::invalid_argument, "Cannot generate a random real within bounds that are too large");
        }
    } else {
        assert(std::isfinite(ub - lb) && (ub - lb) <= std::numeric_limits<double>::max());
    }
}

// Implementation of the uniform_real_from_range() function.
template <bool FiniteCheck, bool LbUbCheck, bool RangeCheck, typename Rng>
inline double uniform_real_from_range_impl(double lb, double ub, Rng &r_engine)
{
    // Run the checks on the bounds.
    uniform_real_from_range_checks<FiniteCheck, LbUbCheck, RangeCheck>(lb, ub);
    // If the bounds are equal we don't call the RNG, as that would be undefined behaviour.
    return (lb == ub) ? lb : std::uniform_real_distribution<double>(lb, ub)(r_engine);
}

// Check that the lower/upper bounds lb/ub are suitable for the
// generation of an integral number. The boolean flags specify at
// compile time which checks to run.
template <bool FiniteCheck, bool LbUbCheck, bool IntCheck>
inline void uniform_integral_from_range_checks(double lb, double ub)
{
    // 0 - Check for finite bounds.
    if (FiniteCheck) {
        if (!std::isfinite(lb) || !std::isfinite(ub)) {
            pagmo_throw(std::invalid_argument, "Cannot generate a random integer if the bounds are not finite");
        }
    } else {
        assert(std::isfinite(lb) && std::isfinite(ub));
    }

    // 1 - Check that lb is <= ub
    if (LbUbCheck) {
        if (lb > ub) {
            pagmo_throw(std::invalid_argument,
                        "Cannot generate a random integer if the lower bound is larger than the upper bound");
        }
    } else {
        assert(lb <= ub);
    }

    // 2 - Check that lb/ub are integral values.
    if (IntCheck) {
        if (std::trunc(lb) != lb || std::trunc(ub) != ub) {
            pagmo_throw(std::invalid_argument,
                        "Cannot generate a random integer if the lower/upper bounds are not integral values");
        }
    } else {
        assert(std::trunc(lb) == lb && std::trunc(ub) == ub);
    }
}

// Implementation of the uniform_integral_from_range() function.
template <bool FiniteCheck, bool LbUbCheck, bool IntCheck, typename Rng>
inline double uniform_integral_from_range_impl(double lb, double ub, Rng &r_engine)
{
    // Run the checks on the bounds.
    uniform_integral_from_range_checks<FiniteCheck, LbUbCheck, IntCheck>(lb, ub);
    // We will convert ub/lb to the widest signed integral type possible (long long),
    // do the generation using uniform_int_distribution, and finally convert back the
    // result to double precision.
    // NOTE: the use of numeric_cast ensures that the conversion to long long is checked
    // (in case of overflow, an exception will be thrown).
    long long l, u;
    try {
        l = boost::numeric_cast<long long>(lb);
        u = boost::numeric_cast<long long>(ub);
    } catch (...) {
        pagmo_throw(std::invalid_argument, "Cannot generate a random integer if the lower/upper bounds are not within "
                                           "the bounds of the long long type");
    }
    // NOTE: it should be safe here to do a raw cast, as the result
    // will be within the original bounds and thus representable by double.
    return static_cast<double>(std::uniform_int_distribution<long long>(l, u)(r_engine));
}

} // namespace detail

/// Generate a random integral number within some lower and upper bounds
/**
 * This function will create a random integral number within a closed range. If
 * both the lower and upper bounds are finite numbers, then the generated value
 * \f$ x \f$ will be such that \f$lb \le x \le ub\f$.
 *
 * Example:
 *
 * @code{.unparsed}
 * std::mt19937 r_engine(32u);
 * auto x = uniform_integral_from_range(3,5,r_engine); // one of [3, 4, 5].
 * auto x = uniform_integral_from_range(2,2,r_engine); // the value 2.
 * @endcode
 *
 * \verbatim embed:rst:leading-asterisk
 * .. note::
 *
 *    The return value is created internally via an integral random number
 *    generator based on the ``long long`` type, and then cast back to ``double``.
 *    Thus, if the absolute values of the lower/upper bounds are large enough, any of
 *    the following may happen:
 *
 *    * the conversion of the lower/upper bounds to ``long long`` may produce an overflow error,
 *    * the conversion of the randomly-generated ``long long`` integer back to ``double`` may yield an
 *      inexact result.
 *
 *    In pratice, on modern mainstream computer architectures, this function will produce uniformly-distributed
 *    integral values as long as the absolute values of the bounds do not exceed :math:`2^{53}`.
 *
 * \endverbatim
 *
 * @param lb lower bound
 * @param ub upper bound
 * @param r_engine a C++ random engine
 *
 * @throws std::invalid_argument if:
 * - the bounds are not finite,
 * - \f$ lb > ub \f$,
 * - \f$ lb \f$ and/or \f$ ub \f$ are not integral values,
 * - \f$ lb \f$ and/or \f$ ub \f$ are not within the bounds of the ``long long`` type.
 *
 * @returns a random integral value
 */
template <typename Rng>
inline double uniform_integral_from_range(double lb, double ub, Rng &r_engine)
{
    // Activate all checks on lb/ub.
    return detail::uniform_integral_from_range_impl<true, true, true>(lb, ub, r_engine);
}

template <typename Rng>
inline int uniform_integral_from_range_cm(int lb, int ub, Rng& r_engine)
{
	long long l, u;
	try {
		l = boost::numeric_cast<long long>(lb);
		u = boost::numeric_cast<long long>(ub);
	}
	catch (...) {
		pagmo_throw(std::invalid_argument, "Cannot generate a random integer if the lower/upper bounds are not within "
			"the bounds of the long long type");
	}
	return static_cast<int>(std::uniform_int_distribution<long long>(l, u)(r_engine));
}

/// Generate a random real number within some lower and upper bounds
/**
 * This function will create a random real number within a half-open range. If
 * both the lower and upper bounds are finite numbers, then the generated value
 * \f$ x \f$ will be such that \f$lb \le x < ub\f$. If \f$lb = ub\f$, then \f$lb\f$ is
 * returned.
 *
 * Example:
 *
 * @code{.unparsed}
 * std::mt19937 r_engine(32u);
 * auto x = uniform_real_from_range(3,5,r_engine); // a random real in the [3, 5) range.
 * auto x = uniform_real_from_range(2,2,r_engine); // the value 2.
 * @endcode
 *
 * @param lb lower bound
 * @param ub upper bound
 * @param r_engine a C++ random engine
 *
 * @throws std::invalid_argument if:
 * - the bounds are not finite,
 * - \f$ lb > ub \f$,
 * - \f$ ub - lb \f$ is larger than an implementation-defined value.
 *
 * @returns a random floating-point value
 */
template <typename Rng>
inline double uniform_real_from_range(double lb, double ub, Rng &r_engine)
{
    // Activate all checks on lb/ub.
    return detail::uniform_real_from_range_impl<true, true, true>(lb, ub, r_engine);
}

/// Generate a random decision vector compatible with a problem
/**
 * This function will generate a decision vector whose values
 * are randomly chosen with uniform probability within
 * the input problem's bounds.
 *
 * For the continuous part of the decision vector, the values will be
 * generated via pagmo::uniform_real_from_range().
 *
 * For the discrete part of the decision vector, the values will be generated
 * via pagmo::uniform_integral_from_range().
 *
 * @param prob the input pagmo::problem
 * @param r_engine a C++ random engine
 *
 * @throws unspecified any exception thrown by pagmo::uniform_real_from_range() or pagmo::uniform_integral_from_range().
 *
 * @returns a pagmo::vector_double containing a random decision vector
 */
template <typename Rng>
inline vector_double random_decision_vector(const problem &prob, Rng &r_engine)
{
    // Prepare the return value.
    vector_double out(prob.get_nx());

    // Fetch a few quantities from prob.
    const auto nx = prob.get_nx();
    const auto ncx = nx - prob.get_nix();
    const auto &lb = prob.get_lb();
    const auto &ub = prob.get_ub();

    // Continuous part.
    for (vector_double::size_type i = 0u; i < ncx; ++i) {
        // NOTE: the lb<=ub check is not needed, as it is ensured by the problem class.
        // Still need to check for finiteness and range.
        out[i] = detail::uniform_real_from_range_impl<true, false, true>(lb[i], ub[i], r_engine);
    }

    // Integer part.
    for (auto i = ncx; i < nx; ++i) {
        // NOTE: the lb<=ub check and the check that lb/ub are integral values are not needed,
        // as they are ensured by the problem class.
        // Still need to check for finiteness.
        out[i] = detail::uniform_integral_from_range_impl<true, false, false>(lb[i], ub[i], r_engine);
    }

    return out;
}

/// Generate a batch of random decision vectors compatible with a problem
/**
 * This function will generate \p n decision vectors whose values
 * are randomly chosen with uniform probability within
 * the input problem's bounds. The decision vectors are laid
 * out contiguously in the return value: for a problem with dimension \f$ d \f$,
 * the first decision vector in the return value occupies
 * the index range \f$ \left[0, d\right) \f$, the second decision vector occupies the range
 * \f$ \left[d, 2d\right) \f$, and so on.
 *
 * For the continuous parts of the decision vectors, the values will be
 * generated via pagmo::uniform_real_from_range().
 *
 * For the discrete parts of the decision vectors, the values will be generated
 * via pagmo::uniform_integral_from_range().
 *
 * @param prob the input pagmo::problem
 * @param n how many decision vectors will be generated
 * @param r_engine a C++ random engine
 *
 * @throws std::overflow_error in case of (unlikely) overflow errors.
 * @throws unspecified any exception thrown by pagmo::uniform_real_from_range() or pagmo::uniform_integral_from_range().
 *
 * @returns a pagmo::vector_double containing \p n random decision vectors
 */
template <typename Rng>
inline vector_double batch_random_decision_vector(const problem &prob, vector_double::size_type n, Rng &r_engine)
{
    // NOTE: it is possible in principle to do this in a parallel fashion, e.g., see code
    // at the commit 13d4182a41e4e71034c6e1085699c5138805d21c. The price to pay however is
    // the loss of determinism. We can reconsider in the future whether it's worth it
    // to add an option to this constructor, e.g., par_random, defaulting to false.

    // Fetch a few quantities from prob.
    const auto nx = prob.get_nx();
    const auto ncx = nx - prob.get_nix();
    const auto &lb = prob.get_lb();
    const auto &ub = prob.get_ub();

    // LCOV_EXCL_START
    if (n > std::numeric_limits<vector_double::size_type>::max() / nx) {
        pagmo_throw(std::overflow_error,
                    "Cannot generate " + std::to_string(n)
                        + " random decision vectors in batch mode, as that would result in an overflow error");
    }
    // LCOV_EXCL_STOP

    // Check the problem bounds.
    for (vector_double::size_type i = 0u; i < ncx; ++i) {
        // NOTE: the lb<=ub check is not needed, as it is ensured by the problem class.
        // Still need to check for finiteness and range.
        detail::uniform_real_from_range_checks<true, false, true>(lb[i], ub[i]);
    }
    for (auto i = ncx; i < nx; ++i) {
        // NOTE: the lb<=ub check and the check that lb/ub are integral values are not needed,
        // as they are ensured by the problem class.
        // Still need to check for finiteness.
        detail::uniform_integral_from_range_checks<true, false, false>(lb[i], ub[i]);
        try {
            // Check that the bounds can be converted safely to long long.
            boost::numeric_cast<long long>(lb[i]);
            boost::numeric_cast<long long>(ub[i]);
        } catch (...) {
            pagmo_throw(std::invalid_argument,
                        "Cannot generate a random integer if the lower/upper bounds are not within "
                        "the bounds of the long long type");
        }
    }

    // Prepare the return value.
    vector_double out(nx * n);

    // Proceed to the random number generation.
    std::uniform_real_distribution<double> rdist;
    std::uniform_int_distribution<long long> idist;
    for (vector_double::size_type i = 0; i < out.size(); i += nx) {
        for (vector_double::size_type j = 0; j < ncx; ++j) {
            out[i + j] = (lb[j] == ub[j])
                             ? lb[j]
                             : rdist(r_engine, std::uniform_real_distribution<double>::param_type(lb[j], ub[j]));
        }
        for (vector_double::size_type j = ncx; j < nx; ++j) {
            out[i + j] = static_cast<double>(
                idist(r_engine, std::uniform_int_distribution<long long>::param_type(static_cast<long long>(lb[j]),
                                                                                static_cast<long long>(ub[j]))));
        }
    }

    return out;
}

/// Binomial coefficient
/**
 * An implementation of the binomial coefficient using gamma functions
 * @param  n first parameter \f$n\f$
 * @param  k second paramater \f$k\f$
 * @return the binomial coefficient \f$ n \choose k \f$
 */
inline double binomial_coefficient(vector_double::size_type n, vector_double::size_type k)
{
    if (k <= n) {
        return std::round(std::exp(std::lgamma(static_cast<double>(n) + 1.) - std::lgamma(static_cast<double>(k) + 1.)
                                   - std::lgamma(static_cast<double>(n) - static_cast<double>(k) + 1.)));
    } else {
        pagmo_throw(std::invalid_argument, "The binomial coefficient is only defined for k<=n, you requested n="
                                               + std::to_string(n) + " and k=" + std::to_string(k));
    }
}

/// K-Nearest Neighbours
/**
 * Computes the indexes of the k nearest neighbours (euclidean distance) to each of the input points.
 * The algorithm complexity (naive implementation) is \f$ O(MN^2)\f$ where \f$N\f$ is the number of
 * points and \f$M\f$ their dimensionality
 *
 * Example:
 * @code{.unparsed}
 * auto res = kNN({{1, 1}, {2, 2}, {3.1, 3.1}, {5, 5}}, 2u);
 * @endcode
 *
 * @param points the \f$N\f$ points having dimension \f$M\f$
 * @param k number of neighbours to detect
 * @return An <tt>std::vector<std::vector<population::size_type> > </tt> containing the indexes of the k nearest
 * neighbours sorted by distance
 * @throws std::invalid_argument If the points do not all have the same dimension.
 */
inline std::vector<std::vector<vector_double::size_type>> kNN(const std::vector<vector_double> &points,
                                                              std::vector<vector_double>::size_type k)
{
    std::vector<std::vector<vector_double::size_type>> neigh_idxs;
    auto N = points.size();
    if (N == 0u) {
        return {};
    }
    auto M = points[0].size();
    if (!std::all_of(points.begin(), points.end(), [M](const vector_double &p) { return p.size() == M; })) {
        pagmo_throw(std::invalid_argument, "All points must have the same dimensionality for k-NN to be invoked");
    }
    // loop through the points
    for (decltype(N) i = 0u; i < N; ++i) {
        // We compute all the distances to all other points including the self
        vector_double distances;
        for (decltype(N) j = 0u; j < N; ++j) {
            double dist = 0.;
            for (decltype(M) l = 0u; l < M; ++l) {
                dist += (points[i][l] - points[j][l]) * (points[i][l] - points[j][l]);
            }
            distances.push_back(std::sqrt(dist));
        }
        // We sort the indexes with respect to the distance
        std::vector<vector_double::size_type> idxs(N);
        std::iota(idxs.begin(), idxs.end(), vector_double::size_type(0u));
        std::sort(idxs.begin(), idxs.end(), [&distances](vector_double::size_type idx1, vector_double::size_type idx2) {
            return detail::less_than_f(distances[idx1], distances[idx2]);
        });
        // We remove the first element containg the self-distance (0)
        idxs.erase(std::remove(idxs.begin(), idxs.end(), i), idxs.end());
        neigh_idxs.push_back(idxs);
    }
    // We trim to k the lists if needed
    if (k < N - 1u) {
        for (decltype(neigh_idxs.size()) i = 0u; i < neigh_idxs.size(); ++i) {
            neigh_idxs[i].erase(neigh_idxs[i].begin() + static_cast<int>(k),
                                neigh_idxs[i].end()); // TODO: remove the static_cast
        }
    }
    return neigh_idxs;
}

namespace detail
{
// modifies a chromosome so that it will be in the bounds. elements that are off are resampled at random in the bounds
template <typename Rng>
inline void force_bounds_random(vector_double &x, const vector_double &lb, const vector_double &ub, Rng &r_engine)
{
    assert(x.size() == lb.size());
    assert(x.size() == ub.size());
    for (decltype(x.size()) j = 0u; j < x.size(); ++j) {
        if ((x[j] < lb[j]) || (x[j] > ub[j])) {
            x[j] = pagmo::uniform_real_from_range(lb[j], ub[j], r_engine);
        }
    }
}
// modifies a chromosome so that it will be in the bounds. Elements that are off are reflected in the bounds
inline void force_bounds_reflection(vector_double &x, const vector_double &lb, const vector_double &ub)
{
    assert(x.size() == lb.size());
    assert(x.size() == ub.size());
    for (decltype(x.size()) j = 0u; j < x.size(); ++j) {
        while (x[j] < lb[j] || x[j] > ub[j]) {
            if (x[j] < lb[j]) {
                x[j] = 2 * lb[j] - x[j];
            }
            if (x[j] > ub[j]) {
                x[j] = 2 * ub[j] - x[j];
            }
        }
    }
}
// modifies a chromosome so that it will be in the bounds. Elements that are off are set on the bounds
inline void force_bounds_stick(vector_double &x, const vector_double &lb, const vector_double &ub)
{
    assert(x.size() == lb.size());
    assert(x.size() == ub.size());
    for (decltype(x.size()) j = 0u; j < x.size(); ++j) {
        if (x[j] < lb[j]) {
            x[j] = lb[j];
        }
        if (x[j] > ub[j]) {
            x[j] = ub[j];
        }
    }
}
} // namespace detail

} // namespace pagmo

#endif


//
//=======================================================================
// Copyright 1997, 1998, 1999, 2000 University of Notre Dame.
// Authors: Andrew Lumsdaine, Lie-Quan Lee, Jeremy G. Siek
//
// This file is part of the Boost Graph Library
//
// You should have received a copy of the License Agreement for the
// Boost Graph Library along with the software; see the file LICENSE.
// If not, contact Office of Research, University of Notre Dame, Notre
// Dame, IN 46556.
//
// Permission to modify the code and to distribute modified code is
// granted, provided the text of this NOTICE is retained, a notice that
// the code was modified is included with the above COPYRIGHT NOTICE and
// with the COPYRIGHT NOTICE in the LICENSE file, and that the LICENSE
// file is distributed with the modified code.
//
// LICENSOR MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED.
// By way of example, but not limitation, Licensor MAKES NO
// REPRESENTATIONS OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY
// PARTICULAR PURPOSE OR THAT THE USE OF THE LICENSED SOFTWARE COMPONENTS
// OR DOCUMENTATION WILL NOT INFRINGE ANY PATENTS, COPYRIGHTS, TRADEMARKS
// OR OTHER RIGHTS.
//=======================================================================
//
//
// Revision History:
//   04 April 2001: Added named parameter variant. (Jeremy Siek)
//   01 April 2001: Modified to use new <boost/limits.hpp> header. (JMaddock)
//
#ifndef BOOST_GRAPH_DIJKSTRA_HPP
#define BOOST_GRAPH_DIJKSTRA_HPP

#include <functional>
#include <boost/limits.hpp>
#include <boost/graph/named_function_params.hpp>
#include <boost/graph/breadth_first_search.hpp>
#include <boost/pending/mutable_queue.hpp>
#include <boost/graph/relax.hpp>
#include <boost/pending/indirect_cmp.hpp>
#include <boost/graph/exception.hpp>

namespace boost {

  template <class Visitor, class Graph>
  struct DijkstraVisitorConcept {
    void constraints() {
      function_requires< CopyConstructibleConcept<Visitor> >();
      vis.discover_vertex(u, g);
      vis.examine_vertex(u, g);
      vis.examine_edge(e, g);
      vis.edge_relaxed(e, g);
      vis.edge_not_relaxed(e, g);
      vis.finish_vertex(u, g);
    }
    Visitor vis;
    Graph g;
    typename graph_traits<Graph>::vertex_descriptor u;
    typename graph_traits<Graph>::edge_descriptor e;
  };

  template <class Visitors = null_visitor>
  class dijkstra_visitor : public bfs_visitor<Visitors> {
  public:
    dijkstra_visitor() { }
    dijkstra_visitor(Visitors vis)
      : bfs_visitor<Visitors>(vis) { }

    template <class Edge, class Graph>
    void edge_relaxed(Edge e, Graph& g) {
      invoke_visitors(this->m_vis, e, g, on_edge_relaxed());
    }
    template <class Edge, class Graph>
    void edge_not_relaxed(Edge e, Graph& g) {
      invoke_visitors(this->m_vis, e, g, on_edge_not_relaxed());
    }
  private:
    template <class Edge, class Graph>
    void tree_edge(Edge u, Graph& g) { }
  };
  template <class Visitors>
  dijkstra_visitor<Visitors>
  make_dijkstra_visitor(Visitors vis) {
    return dijkstra_visitor<Visitors>(vis);
  }
  typedef dijkstra_visitor<> default_dijkstra_visitor;

  namespace detail {

    template <class UniformCostVisitor, class UpdatableQueue,
      class WeightMap, class PredecessorMap, class DistanceMap,
      class BinaryFunction, class BinaryPredicate>
    struct dijkstra_bfs_visitor
    {
      typedef typename property_traits<DistanceMap>::value_type D;

      dijkstra_bfs_visitor(UniformCostVisitor vis, UpdatableQueue& Q,
                           WeightMap w, PredecessorMap p, DistanceMap d,
                           BinaryFunction combine, BinaryPredicate compare,
                           D zero)
        : m_vis(vis), m_Q(Q), m_weight(w), m_predecessor(p), m_distance(d),
          m_combine(combine), m_compare(compare), m_zero(zero)  { }

      template <class Edge, class Graph>
      void tree_edge(Edge e, Graph& g) {
        m_decreased = relax(e, g, m_weight, m_predecessor, m_distance,
                            m_combine, m_compare);
        if (m_decreased)
          m_vis.edge_relaxed(e, g);
        else
          m_vis.edge_not_relaxed(e, g);
      }
      template <class Edge, class Graph>
      void gray_target(Edge e, Graph& g) {
        m_decreased = relax(e, g, m_weight, m_predecessor, m_distance,
                            m_combine, m_compare);
        if (m_decreased) {
          m_Q.update(target(e, g));
          m_vis.edge_relaxed(e, g);
        } else
          m_vis.edge_not_relaxed(e, g);
      }

      template <class Vertex, class Graph>
      void initialize_vertex(Vertex u, Graph& g)
        { m_vis.initialize_vertex(u, g); }
      template <class Edge, class Graph>
      void non_tree_edge(Edge, Graph&) { }
      template <class Vertex, class Graph>
      void discover_vertex(Vertex u, Graph& g) { m_vis.discover_vertex(u, g); }
      template <class Vertex, class Graph>
      void examine_vertex(Vertex u, Graph& g) { m_vis.examine_vertex(u, g); }
      template <class Edge, class Graph>
      void examine_edge(Edge e, Graph& g) {
        if (m_compare(get(m_weight, e), m_zero))
          throw negative_edge();
        m_vis.examine_edge(e, g);
      }
      template <class Edge, class Graph>
      void black_target(Edge, Graph&) { }
      template <class Vertex, class Graph>
      void finish_vertex(Vertex u, Graph& g) { m_vis.finish_vertex(u, g); }

      UniformCostVisitor m_vis;
      UpdatableQueue& m_Q;
      WeightMap m_weight;
      PredecessorMap m_predecessor;
      DistanceMap m_distance;
      BinaryFunction m_combine;
      BinaryPredicate m_compare;
      bool m_decreased;
      D m_zero;
    };

  } // namespace detail

  // Initalize distances and call breadth first search
  template <class VertexListGraph, class DijkstraVisitor,
            class PredecessorMap, class DistanceMap,
            class WeightMap, class IndexMap, class Compare, class Combine,
            class DistZero>
  inline void
  dijkstra_shortest_paths_no_init
    (const VertexListGraph& g,
     typename graph_traits<VertexListGraph>::vertex_descriptor s,
     PredecessorMap predecessor, DistanceMap distance, WeightMap weight,
     IndexMap index_map,
     Compare compare, Combine combine, DistZero zero,
     DijkstraVisitor vis)
  {
    typedef indirect_cmp<DistanceMap, Compare> IndirectCmp;
    IndirectCmp icmp(distance, compare);

    typedef typename graph_traits<VertexListGraph>::vertex_descriptor Vertex;
    typedef mutable_queue<Vertex, std::vector<Vertex>, IndirectCmp, IndexMap>
      MutableQueue;

    MutableQueue Q(num_vertices(g), icmp, index_map);

    detail::dijkstra_bfs_visitor<DijkstraVisitor, MutableQueue, WeightMap,
      PredecessorMap, DistanceMap, Combine, Compare>
        bfs_vis(vis, Q, weight, predecessor, distance, combine, compare, zero);

    std::vector<default_color_type> color(num_vertices(g));
    default_color_type c = white_color;
    breadth_first_visit(g, s, Q, bfs_vis,
      make_iterator_property_map(&color[0], index_map, c));
  }

  // Initalize distances and call breadth first search
  template <class VertexListGraph, class DijkstraVisitor,
            class PredecessorMap, class DistanceMap,
            class WeightMap, class IndexMap, class Compare, class Combine,
            class DistInf, class DistZero>
  inline void
  dijkstra_shortest_paths
    (const VertexListGraph& g,
     typename graph_traits<VertexListGraph>::vertex_descriptor s,
     PredecessorMap predecessor, DistanceMap distance, WeightMap weight,
     IndexMap index_map,
     Compare compare, Combine combine, DistInf inf, DistZero zero,
     DijkstraVisitor vis)
  {
    typename graph_traits<VertexListGraph>::vertex_iterator ui, ui_end;
    for (tie(ui, ui_end) = vertices(g); ui != ui_end; ++ui) {
      put(distance, *ui, inf);
      put(predecessor, *ui, *ui);
    }
    put(distance, s, zero);

    dijkstra_shortest_paths_no_init(g, s, predecessor, distance, weight,
                            index_map, compare, combine, zero, vis);
  }

  namespace detail {

    // Handle defaults for PredecessorMap and
    // Distance Compare, Combine, Inf and Zero
    template <class VertexListGraph, class DistanceMap, class WeightMap,
              class IndexMap, class Params>
    inline void
    dijkstra_dispatch2
      (const VertexListGraph& g,
       typename graph_traits<VertexListGraph>::vertex_descriptor s,
       DistanceMap distance, WeightMap weight, IndexMap index_map,
       const Params& params)
    {
      // Default for predecessor map
      dummy_property_map p_map;

      typedef typename property_traits<DistanceMap>::value_type D;
      dijkstra_shortest_paths
        (g, s,
         choose_param(get_param(params, vertex_predecessor), p_map),
         distance, weight, index_map,
         choose_param(get_param(params, distance_compare_t()),
                      std::less<D>()),
         choose_param(get_param(params, distance_combine_t()),
                      closed_plus<D>()),
         choose_param(get_param(params, distance_inf_t()),
                      (std::numeric_limits<D>::max)()),
         choose_param(get_param(params, distance_zero_t()),
                      D()),
         choose_param(get_param(params, graph_visitor),
                      make_dijkstra_visitor(null_visitor())));
    }

    template <class VertexListGraph, class DistanceMap, class WeightMap,
              class IndexMap, class Params>
    inline void
    dijkstra_dispatch1
      (const VertexListGraph& g,
       typename graph_traits<VertexListGraph>::vertex_descriptor s,
       DistanceMap distance, WeightMap weight, IndexMap index_map,
       const Params& params)
    {
      // Default for distance map
      typedef typename property_traits<WeightMap>::value_type D;
      typename std::vector<D>::size_type
        n = is_default_param(distance) ? num_vertices(g) : 1;
      std::vector<D> distance_map(n);

      detail::dijkstra_dispatch2
        (g, s, choose_param(distance, make_iterator_property_map
                            (distance_map.begin(), index_map,
                             distance_map[0])),
         weight, index_map, params);
    }
  } // namespace detail

  // Named Parameter Variant
  template <class VertexListGraph, class Param, class Tag, class Rest>
  inline void
  dijkstra_shortest_paths
    (const VertexListGraph& g,
     typename graph_traits<VertexListGraph>::vertex_descriptor s,
     const bgl_named_params<Param,Tag,Rest>& params)
  {
    // Default for edge weight and vertex index map is to ask for them
    // from the graph.  Default for the visitor is null_visitor.
    detail::dijkstra_dispatch1
      (g, s,
       get_param(params, vertex_distance),
       choose_const_pmap(get_param(params, edge_weight), g, edge_weight),
       choose_const_pmap(get_param(params, vertex_index), g, vertex_index),
       params);
  }

} // namespace boost

#endif // BOOST_GRAPH_DIJKSTRA_HPP


// This file is part of the dune-gdt project:
//   https://github.com/dune-community/dune-gdt
// Copyright 2010-2017 dune-gdt developers and contributors. All rights reserved.
// License: Dual licensed as BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)
//      or  GPL-2.0+ (http://opensource.org/licenses/gpl-license)
//          with "runtime exception" (http://www.dune-project.org/license.html)
// Authors:
//   Felix Schindler (2014 - 2017)
//   Rene Milk       (2016 - 2017)
//   Tobias Leibner  (2014)

#ifndef DUNE_GDT_OPERATORS_DARCY_HH
#define DUNE_GDT_OPERATORS_DARCY_HH

#include <limits>

#include <boost/numeric/conversion/cast.hpp>

#include <dune/geometry/quadraturerules.hh>

#include <dune/xt/common/exceptions.hh>
#include <dune/xt/common/fvector.hh>
#include <dune/xt/common/fmatrix.hh>
#include <dune/xt/common/type_traits.hh>
#include <dune/xt/grid/type_traits.hh>
#include <dune/xt/functions/interfaces.hh>
#include <dune/xt/la/container.hh>
#include <dune/xt/la/solver.hh>

#include <dune/gdt/discretefunction/default.hh>
#include <dune/gdt/exceptions.hh>
#include <dune/gdt/spaces/cg/interface.hh>
#include <dune/gdt/spaces/rt/interface.hh>

#include "interfaces.hh"

namespace Dune {
namespace GDT {

// forward, to be used in the traits
template <class GridLayerImp, class FunctionImp>
class DarcyOperator;

namespace internal {

template <class GridLayerImp, class FunctionImp>
class DarcyOperatorTraits
{
  static_assert(XT::Functions::is_localizable_function<FunctionImp>::value,
                "FunctionImp has to be derived from XT::Functions::is_localizable_function!");
  static_assert(std::is_same<typename GridLayerImp::ctype, typename FunctionImp::DomainFieldType>::value,
                "Types do not match!");
  static_assert(GridLayerImp::dimension == FunctionImp::dimDomain, "Dimensions do not match!");
  static_assert(FunctionImp::dimRange == FunctionImp::dimRangeCols, "Dimensions do not match!");

public:
  typedef DarcyOperator<GridLayerImp, FunctionImp> derived_type;
  typedef GridLayerImp GridLayerType;
  typedef typename FunctionImp::RangeFieldType FieldType;
  typedef NoJacobian JacobianType;
}; // class DarcyOperatorTraits

} // namespace internal

/**
  * \note Only works for scalar valued function atm.
  * \todo add make_darcy_operator
  **/
template <class GridLayerImp, class FunctionImp>
class DarcyOperator : public OperatorInterface<internal::DarcyOperatorTraits<GridLayerImp, FunctionImp>>
{
  typedef OperatorInterface<internal::DarcyOperatorTraits<GridLayerImp, FunctionImp>> BaseType;

public:
  typedef internal::DarcyOperatorTraits<GridLayerImp, FunctionImp> Traits;
  typedef typename Traits::GridLayerType GridLayerType;
  typedef typename Traits::FieldType FieldType;
  typedef typename Traits::JacobianType JacobianType;
  using EntityType = XT::Grid::extract_entity_t<GridLayerType>;
  typedef typename GridLayerType::ctype DomainFieldType;
  static const size_t dimDomain = GridLayerType::dimension;

  DarcyOperator(const GridLayerType& grd_vw, const FunctionImp& function)
    : grid_layer_(grd_vw)
    , function_(function)
  {
  }

  /**
   * \brief Applies the operator.
   * \note  See redirect_apply for the implementation (depending on the type of the range space).
   * \sa    redirect_apply
   */
  template <class S, class V, size_t r, size_t rC>
  void
  apply(const XT::Functions::LocalizableFunctionInterface<EntityType, DomainFieldType, dimDomain, FieldType, r, rC>&
            source,
        DiscreteFunction<S, V>& range,
        const Dune::XT::Common::Parameter& param = {}) const
  {
    redirect_apply(range.space(), source, range, param);
  }

  template <class SourceType>
  JacobianType jacobian(const SourceType& /*source*/, const Dune::XT::Common::Parameter& /*param*/ = {}) const
  {
    DUNE_THROW(NotImplemented, "This operator does not provide a jacobian (yet)!");
    return JacobianType();
  }

  template <class SourceType>
  void
  jacobian(const SourceType& /*source*/, JacobianType& /*jac*/, const Dune::XT::Common::Parameter& /*param*/ = {}) const
  {
    DUNE_THROW(NotImplemented, "This operator does not provide a jacobian (yet)!");
  }

private:
  template <class R, size_t r, size_t rC>
  struct Helper
  {
    static_assert(AlwaysFalse<R>::value, "This should not happen (see check in Traits)!");
  };

  template <class R, size_t r>
  struct Helper<R, r, r>
  {
    typedef XT::Common::FieldMatrix<R, r, r> type;
  };

  template <class R>
  struct Helper<R, 1, 1>
  {
    typedef XT::Common::FieldVector<R, 1> type;
  };

  typedef typename Helper<typename FunctionImp::RangeFieldType, FunctionImp::dimRange, FunctionImp::dimRangeCols>::type
      ValueType;

  /**
   * \brief Does an L2 projection of '- function * \gradient source' onto range.
   */
  template <class T, class S, class V>
  void redirect_apply(
      const CgSpaceInterface<T, dimDomain, dimDomain, 1>& /*space*/,
      const XT::Functions::LocalizableFunctionInterface<EntityType, DomainFieldType, dimDomain, FieldType, 1, 1>&
          source,
      DiscreteFunction<S, V>& range,
      const XT::Common::Parameter param) const
  {
    typedef typename XT::LA::Container<FieldType, V::sparse_matrix_type>::MatrixType MatrixType;
    MatrixType lhs(
        range.space().mapper().size(), range.space().mapper().size(), range.space().compute_volume_pattern());
    V rhs(range.space().mapper().size());

    // walk the grid
    const auto entity_it_end = grid_layer_.template end<0>();
    for (auto entity_it = grid_layer_.template begin<0>(); entity_it != entity_it_end; ++entity_it) {
      const auto& entity = *entity_it;
      const auto local_function = function_.local_function(entity);
      const auto local_source = source.local_function(entity);
      const auto basis = range.space().base_function_set(entity);
      // do a volume quadrature
      const size_t integrand_order =
          std::max(local_function->order() + ssize_t(local_source->order()) - 1, basis.order()) + basis.order();
      const auto& quadrature =
          QuadratureRules<DomainFieldType, dimDomain>::rule(entity.type(), boost::numeric_cast<int>(integrand_order));
      const auto quadrature_it_end = quadrature.end();
      for (auto quadrature_it = quadrature.begin(); quadrature_it != quadrature_it_end; ++quadrature_it) {
        const auto xx = quadrature_it->position();
        const auto quadrature_weight = quadrature_it->weight();
        const auto integration_element = entity.geometry().integrationElement(xx);
        const ValueType function_value = local_function->evaluate(xx, param);
        const auto source_gradient = local_source->jacobian(xx, param);
        const auto basis_value = basis.evaluate(xx, param);
        for (size_t ii = 0; ii < basis.size(); ++ii) {
          const size_t global_ii = range.space().mapper().mapToGlobal(entity, ii);
          rhs.add_to_entry(global_ii,
                           integration_element * quadrature_weight * -1.0
                               * ((function_value * source_gradient[0]) * basis_value[ii]));
          for (size_t jj = 0; jj < basis.size(); ++jj) {
            const size_t global_jj = range.space().mapper().mapToGlobal(entity, jj);
            lhs.add_to_entry(
                global_ii, global_jj, integration_element * quadrature_weight * (basis_value[ii] * basis_value[jj]));
          }
        }
      } // do a volume quadrature
    } // walk the grid

    // solve
    try {
      XT::LA::Solver<MatrixType>(lhs).apply(rhs, range.vector());
    } catch (XT::LA::Exceptions::linear_solver_failed& ee) {
      DUNE_THROW(operator_error,
                 "Application of the Darcy operator failed because a matrix could not be inverted!\n\n"
                     << "This was the original error: "
                     << ee.what());
    }
  } // ... redirect_apply(...)

  template <class T, class S, class V>
  void redirect_apply(
      const RtSpaceInterface<T, dimDomain, dimDomain, 1>& /*space*/,
      const XT::Functions::LocalizableFunctionInterface<EntityType, DomainFieldType, dimDomain, FieldType, 1>& source,
      DiscreteFunction<S, V>& range,
      const XT::Common::Parameter param) const
  {
    static_assert(RtSpaceInterface<T, dimDomain, 1>::polOrder == 0, "Untested!");
    const auto& rtn0_space = range.space();
    auto& range_vector = range.vector();
    const auto infinity = std::numeric_limits<FieldType>::infinity();
    for (size_t ii = 0; ii < range_vector.size(); ++ii)
      range_vector[ii] = infinity;
    // walk the grid
    const auto entity_it_end = grid_layer_.template end<0>();
    for (auto entity_it = grid_layer_.template begin<0>(); entity_it != entity_it_end; ++entity_it) {
      const auto& entity = *entity_it;
      const auto local_DoF_indices = rtn0_space.local_DoF_indices(entity);
      const auto global_DoF_indices = rtn0_space.mapper().globalIndices(entity);
      assert(global_DoF_indices.size() == local_DoF_indices.size());
      const auto local_function = function_.local_function(entity);
      const auto local_source = source.local_function(entity);
      const auto local_basis = rtn0_space.base_function_set(entity);
      // walk the intersections
      const auto intersection_it_end = grid_layer_.iend(entity);
      for (auto intersection_it = grid_layer_.ibegin(entity); intersection_it != intersection_it_end;
           ++intersection_it) {
        const auto& intersection = *intersection_it;
        if (intersection.neighbor() && !intersection.boundary()) {
          const auto neighbor = intersection.outside();
          if (grid_layer_.indexSet().index(entity) < grid_layer_.indexSet().index(neighbor)) {
            const auto local_function_neighbor = function_.local_function(neighbor);
            const auto local_source_neighbor = source.local_function(neighbor);
            const size_t local_intersection_index = intersection.indexInInside();
            const size_t local_DoF_index = local_DoF_indices[local_intersection_index];
            // do a face quadrature
            FieldType lhs = 0;
            FieldType rhs = 0;
            const size_t integrand_order = local_function->order();
            const auto& quadrature = QuadratureRules<DomainFieldType, dimDomain - 1>::rule(
                intersection.type(), boost::numeric_cast<int>(integrand_order));
            const auto quadrature_it_end = quadrature.end();
            for (auto quadrature_it = quadrature.begin(); quadrature_it != quadrature_it_end; ++quadrature_it) {
              const auto xx_intersection = quadrature_it->position();
              const auto normal = intersection.unitOuterNormal(xx_intersection);
              const FieldType integration_factor = intersection.geometry().integrationElement(xx_intersection);
              const FieldType weight = quadrature_it->weight();
              const auto xx_entity = intersection.geometryInInside().global(xx_intersection);
              const auto xx_neighbor = intersection.geometryInOutside().global(xx_intersection);
              // evaluate
              ValueType function_value = local_function->evaluate(xx_entity, param);
              function_value *= 0.5;
              ValueType function_value_neighbor = local_function_neighbor->evaluate(xx_neighbor, param);
              function_value_neighbor *= 0.5;
              function_value += function_value_neighbor;
              auto source_gradient = local_source->jacobian(xx_entity, param)[0];
              source_gradient *= 0.5;
              auto source_gradient_neighbor = local_source_neighbor->jacobian(xx_neighbor, param)[0];
              source_gradient_neighbor *= 0.5;
              source_gradient += source_gradient_neighbor;
              const auto basis_values = local_basis.evaluate(xx_entity, param);
              const auto basis_value = basis_values[local_DoF_index];
              // compute integrals
              lhs += integration_factor * weight * (basis_value * normal);
              rhs += integration_factor * weight * -1.0 * compute_value(function_value, source_gradient, normal);
            } // do a face quadrature
            // set DoF
            const size_t global_DoF_index = global_DoF_indices[local_DoF_index];
            assert(!(range_vector[global_DoF_index] < infinity));
            range_vector[global_DoF_index] = rhs / lhs;
          }
        } else if (intersection.boundary() && !intersection.neighbor()) {
          const size_t local_intersection_index = intersection.indexInInside();
          const size_t local_DoF_index = local_DoF_indices[local_intersection_index];
          // do a face quadrature
          FieldType lhs = 0;
          FieldType rhs = 0;
          const size_t integrand_order = local_function->order();
          const auto& quadrature = QuadratureRules<DomainFieldType, dimDomain - 1>::rule(
              intersection.type(), boost::numeric_cast<int>(integrand_order));
          const auto quadrature_it_end = quadrature.end();
          for (auto quadrature_it = quadrature.begin(); quadrature_it != quadrature_it_end; ++quadrature_it) {
            const auto xx_intersection = quadrature_it->position();
            const auto normal = intersection.unitOuterNormal(xx_intersection);
            const auto integration_factor = intersection.geometry().integrationElement(xx_intersection);
            const auto weight = quadrature_it->weight();
            const auto xx_entity = intersection.geometryInInside().global(xx_intersection);
            // evalaute
            const ValueType function_value = local_function->evaluate(xx_entity, param);
            const auto source_gradient = local_source->jacobian(xx_entity, param)[0];
            const auto basis_values = local_basis.evaluate(xx_entity, param);
            const auto basis_value = basis_values[local_DoF_index];
            // compute integrals
            lhs += integration_factor * weight * (basis_value * normal);
            rhs += integration_factor * weight * -1.0 * compute_value(function_value, source_gradient, normal);
          } // do a face quadrature
          // set DoF
          const size_t global_DoF_index = global_DoF_indices[local_DoF_index];
          assert(!(range_vector[global_DoF_index] < infinity));
          range_vector[global_DoF_index] = rhs / lhs;
        } else
          DUNE_THROW(XT::Common::Exceptions::internal_error, "Unknown intersection type!");
      } // walk the intersections
    } // walk the grid
  } // ... redirect_apply(...)

  template <class R, int d>
  R compute_value(const FieldVector<R, 1>& function_value,
                  const FieldVector<R, d>& source_gradient,
                  const FieldVector<R, d>& normal) const
  {
    return function_value * (source_gradient * normal);
  }

  template <class R, int d>
  typename std::enable_if<(d > 1), R>::type compute_value(const XT::Common::FieldMatrix<R, d, d>& function_value,
                                                          const FieldVector<R, d>& source_gradient,
                                                          const FieldVector<R, d>& normal) const
  {
    return (function_value * source_gradient) * normal;
  }

  const GridLayerType& grid_layer_;
  const FunctionImp& function_;
}; // class DarcyOperator

template <class G, class F>
std::unique_ptr<DarcyOperator<G, F>> make_darcy(const G& grid_layer, const F& function)
{
  return std::unique_ptr<DarcyOperator<G, F>>(new DarcyOperator<G, F>(grid_layer, function));
}

} // namespace GDT
} // namespace Dune

#endif // DUNE_GDT_OPERATORS_DARCY_HH


/* __HEAD__
 * Copyright (c) 2011-2016, Matthieu FAESSEL and ARMINES
 * Copyright (c) 2017-2020, Centre de Morphologie Mathematique
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of Matthieu FAESSEL, or ARMINES nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Description :
 *   Porting GeoCuts module from Morph-M - This is the Watershed submodule
 *
 * History :
 *   - 20/03/2019 - by Jose-Marcio Martins da Cruz
 *     Just created it...
 *   - XX/XX/XXXX -
 *
 *
 * __HEAD__ - Stop here !
 */

#ifndef _D_GEOCUTS_WATERSHED_HPP_
#define _D_GEOCUTS_WATERSHED_HPP_

/*
 *
 *
 *
 */
#ifndef __BOOST_INCLUDED__
#define __BOOST_INCLUDED__

#include <boost/config.hpp>
// for boost::tie
#include <boost/utility.hpp>
// for boost::graph_traits
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/adjacency_list.hpp>
// JOE #include <boost/graph/graphviz.hpp>
// JOE #include <boost/graph/prim_minimum_spanning_tree.hpp>
// JOE #include <boost/graph/dijkstra_shortest_paths.hpp>
// JOE #include <boost/graph/johnson_all_pairs_shortest.hpp>

#include <boost/version.hpp>

#if 0
// FROM STAWIASKI JAN 2012
#include "../boost_ext/kolmogorov_max_flow_min_cost.hpp"
//#include "../boost_ext/maximum_spanning_tree.hpp"
//STAWIASKI JAN2012 commented, why?
//#include "../boost_ext/boost_compare.hpp"
#include <boost/graph/connected_components.hpp>
#endif

#endif //  __BOOST_INCLUDED__

#include <vector>

using namespace geocuts;

namespace smil
{
  /*
   *
   *
   *
   */
  template <class T1, class T2>
  RES_T geoCutsWatershed_MinCut(const Image<T1> &imIn,
                                const Image<T2> &imMarker, const double Power,
                                const StrElt &nl, Image<T2> &imOut)
  {
    std::cout << "Enter function Geo-Cuts Watershed" << std::endl;

    ASSERT_ALLOCATED(&imIn, &imMarker, &imOut);
    ASSERT_SAME_SIZE(&imIn, &imMarker, &imOut);

    typename Image<T1>::lineType bufIn     = imIn.getPixels();
    typename Image<T2>::lineType bufMarker = imMarker.getPixels();
    typename Image<T2>::lineType bufOut    = imOut.getPixels();

    double exposant = Power;

    // needed for max flow: capacit map, rev_capacity map, etc.
    typedef boost::adjacency_list_traits<boost::vecS, boost::vecS,
                                         boost::directedS>
        Traits_d;

    typedef boost::adjacency_list<
        boost::vecS, boost::vecS, boost::directedS,
        boost::property<boost::vertex_name_t, std::string>,
        boost::property<
            boost::edge_capacity_t, double,
            boost::property<boost::edge_residual_capacity_t, double,
                            boost::property<boost::edge_reverse_t,
                                            Traits_d::edge_descriptor>>>>
        Graph_d;

    Graph_d g;

    boost::property_map<Graph_d, boost::edge_capacity_t>::type capacity =
        boost::get(boost::edge_capacity, g);

    boost::property_map<Graph_d, boost::edge_reverse_t>::type rev =
        get(boost::edge_reverse, g);

    boost::property_map<Graph_d, boost::edge_residual_capacity_t>::type
        residual_capacity = get(boost::edge_residual_capacity, g);

    Graph_d::edge_descriptor e1, e2, e3, e4;
    Graph_d::vertex_descriptor vSource, vSink;
    T1 numVertex = maxVal(imIn);
    // T2 numLabels = maxVal(imMarker);

    std::cout << "build graph vertices" << std::endl;
    std::cout << "number of vertices : " << numVertex << std::endl;

    size_t pixelCount = imIn.getPixelCount();
    for (off_t i = 0; i < (off_t) pixelCount; i++) {
      boost::add_vertex(g);
      bufOut[i] = (T2) 1;
    }

    vSource = boost::add_vertex(g);
    vSink   = boost::add_vertex(g);

    int width  = imIn.getWidth();
    int height = imIn.getHeight();
    int depth  = imIn.getDepth();

    off_t strideY = width;
    off_t strideZ = width * height;
    for (int z = 0; z < depth; z++) {
      off_t p0Z = z * strideZ;
      for (int y = 0; y < height; y++) {
        off_t p0Y = y * strideY;
        for (int x = 0; x < width; x++) {
          off_t o1 = p0Z + p0Y + x;

          // iterators on the Structuring Element
          vector<IntPoint> pts = filterStrElt(nl);
          vector<IntPoint>::iterator itBegin, itEnd, it;
          itBegin = pts.begin();
          itEnd   = pts.end();

          T1 val1   = bufIn[o1];
          T2 marker = bufMarker[o1];

          bool hasEdge = false;
          if (marker == 2) {
            boost::tie(e4, hasEdge) = boost::add_edge(vSource, o1, g);
            boost::tie(e3, hasEdge) = boost::add_edge(o1, vSource, g);
            capacity[e4]            = (std::numeric_limits<double>::max)();
            capacity[e3]            = (std::numeric_limits<double>::max)();
            rev[e4]                 = e3;
            rev[e3]                 = e4;
          } else if (marker == 3) {
            boost::tie(e4, hasEdge) = boost::add_edge(o1, vSink, g);
            boost::tie(e3, hasEdge) = boost::add_edge(vSink, o1, g);
            capacity[e4]            = (std::numeric_limits<double>::max)();
            capacity[e3]            = (std::numeric_limits<double>::max)();
            rev[e4]                 = e3;
            rev[e3]                 = e4;
          }

          for (it = itBegin; it != itEnd; it++) {
            if (x + it->x > width - 1 || x + it->x < 0)
              continue;
            if (y + it->y > height - 1 || y + it->y < 0)
              continue;
            if (z + it->z > depth - 1 || z + it->z < 0)
              continue;

            off_t o2 = o1 + it->z * strideZ + it->y * strideY + it->x;
            if (o2 <= o1)
              continue;

            T2 val2       = bufIn[o2];
            double valeur = (255.0 / (std::abs((double) (val1 - val2)) + 1));
            double cost   = std::pow(valeur, exposant);

            bool hasEdge            = false;
            boost::tie(e4, hasEdge) = boost::add_edge(o1, o2, g);
            boost::tie(e3, hasEdge) = boost::add_edge(o2, o1, g);
            capacity[e4]            = cost;
            capacity[e3]            = cost;
            rev[e4]                 = e3;
            rev[e3]                 = e4;
          }
        }
      }
    }

    std::cout << "Compute Max flow" << std::endl;
    boost::property_map<Graph_d, boost::vertex_index_t>::type indexmap =
        boost::get(boost::vertex_index, g);
    std::vector<boost::default_color_type> color(boost::num_vertices(g));
#if BOOST_VERSION >= 104700
    double flow =
        boykov_kolmogorov_max_flow(g, capacity, residual_capacity, rev,
                                   &color[0], indexmap, vSource, vSink);
#else
    double flow = kolmogorov_max_flow(g, capacity, residual_capacity, rev,
                                      &color[0], indexmap, vSource, vSink);
#endif
    std::cout << "c  The total flow:" << std::endl;
    std::cout << "s " << flow << std::endl << std::endl;

    // for all pixels in imIn create a vertex and an edge
    for (off_t o1 = 0; o1 < (off_t) pixelCount; o1++) {
      if (color[o1] == color[vSource])
        bufOut[o1] = (T2) 2;
      else if (color[o1] == color[vSink])
        bufOut[o1] = (T2) 3;
      else if (color[o1] == 1)
        bufOut[o1] = (T2) 4;
    }

    return RES_OK;
  }

  /*
   *
   *
   *
   */
  template <class T1, class T2>
  RES_T geoCutsMultiway_Watershed(const Image<T1> &imIn,
                                  const Image<T2> &imMarker, const double Power,
                                  const StrElt &nl, Image<T2> &imOut)
  {
    std::cout << "Enter function Multi way watershed" << std::endl;
    ASSERT_ALLOCATED(&imIn, &imMarker, &imOut);
    ASSERT_SAME_SIZE(&imIn, &imMarker, &imOut);

    typename Image<T1>::lineType bufIn     = imIn.getPixels();
    typename Image<T2>::lineType bufMarker = imMarker.getPixels();
    typename Image<T2>::lineType bufOut    = imOut.getPixels();

    double exposant = Power;

    // needed for max flow: capacit map, rev_capacity map, etc.
    typedef boost::adjacency_list_traits<boost::vecS, boost::vecS,
                                         boost::directedS>
        Traits_d;

    typedef boost::adjacency_list<
        boost::vecS, boost::vecS, boost::directedS,
        boost::property<boost::vertex_name_t, std::string>,
        boost::property<
            boost::edge_capacity_t, double,
            boost::property<boost::edge_residual_capacity_t, double,
                            boost::property<boost::edge_reverse_t,
                                            Traits_d::edge_descriptor>>>>
        Graph_d;

    Graph_d g;

    boost::property_map<Graph_d, boost::edge_capacity_t>::type capacity =
        boost::get(boost::edge_capacity, g);

    boost::property_map<Graph_d, boost::edge_reverse_t>::type rev =
        get(boost::edge_reverse, g);

    boost::property_map<Graph_d, boost::edge_residual_capacity_t>::type
        residual_capacity = get(boost::edge_residual_capacity, g);

    Graph_d::edge_descriptor e1, e2, e3, e4;
    Graph_d::vertex_descriptor vSource, vSink;
    T1 numVertex = maxVal(imIn);
    T2 numLabels = maxVal(imMarker);

    std::cout << "build graph vertices" << std::endl;

    size_t pixelCount = imIn.getPixelCount();
    for (off_t i = 0; i < (off_t) pixelCount; i++) {
      boost::add_vertex(g);
      bufOut[i] = (T2) 1;
    }

    std::cout << "number of Labels: " << numLabels << std::endl;
    std::cout << "number of vertices: " << numVertex << std::endl;

    vSource = boost::add_vertex(g);
    vSink   = boost::add_vertex(g);

    std::cout << "build graph edges" << std::endl;

    int width  = imIn.getWidth();
    int height = imIn.getHeight();
    int depth  = imIn.getDepth();

    off_t strideY = width;
    off_t strideZ = width * height;
    for (int z = 0; z < depth; z++) {
      off_t p0Z = z * strideZ;
      for (int y = 0; y < height; y++) {
        off_t p0Y = y * strideY;
        for (int x = 0; x < width; x++) {
          off_t o1 = p0Z + p0Y + x;

          // iterators on the Structuring Element
          vector<IntPoint> pts = filterStrElt(nl);
          vector<IntPoint>::iterator itBegin, itEnd, it;
          itBegin = pts.begin();
          itEnd   = pts.end();

          T1 val1 = bufIn[o1];

          for (it = itBegin; it != itEnd; it++) {
            if (x + it->x > width - 1 || x + it->x < 0)
              continue;
            if (y + it->y > height - 1 || y + it->y < 0)
              continue;
            if (z + it->z > depth - 1 || z + it->z < 0)
              continue;

            off_t o2 = o1 + it->z * strideZ + it->y * strideY + it->x;
            if (o2 <= o1)
              continue;

            T2 val2       = bufIn[o2];
            double valeur = (255.0 / (std::abs((double) (val1 - val2)) + 1));
            double cost   = std::pow(valeur, exposant);

            bool hasEdge            = false;
            boost::tie(e4, hasEdge) = boost::add_edge(o1, o2, g);
            boost::tie(e3, hasEdge) = boost::add_edge(o2, o1, g);
            capacity[e4]            = cost;
            capacity[e3]            = cost;
            rev[e4]                 = e3;
            rev[e3]                 = e4;
          }
        }
      }
    }

    for (T2 nbk = 2; nbk <= numLabels; nbk++) {
      // for all pixels in imIn create a vertex
      for (off_t o0 = 0; o0 < (off_t) pixelCount; o0++) {
        T2 val1 = bufMarker[o0];
        T1 val2 = bufIn[o0];

        double cost = std::numeric_limits<double>::max();
        bool hasEdge;
        if (val1 == nbk) {
          boost::tie(e4, hasEdge) = boost::edge(vSource, o0, g);
          if (!hasEdge) {
            boost::tie(e4, hasEdge) = boost::add_edge(vSource, o0, g);
            boost::tie(e3, hasEdge) = boost::add_edge(o0, vSource, g);
            capacity[e4]            = cost;
            capacity[e3]            = cost;
            rev[e4]                 = e3;
            rev[e3]                 = e4;
          }
        } else if (val1 > 1 && val1 != nbk) {
          boost::tie(e4, hasEdge) = boost::edge(val2, vSink, g);
          if (!hasEdge) {
            boost::tie(e4, hasEdge) = boost::add_edge(o0, vSink, g);
            boost::tie(e3, hasEdge) = boost::add_edge(vSink, o0, g);
            capacity[e4]            = cost;
            capacity[e3]            = cost;
            rev[e4]                 = e3;
            rev[e3]                 = e4;
          }
        }
      }

      std::cout << "Compute Max flow" << std::endl;
      boost::property_map<Graph_d, boost::vertex_index_t>::type indexmap =
          boost::get(boost::vertex_index, g);
      std::vector<boost::default_color_type> color(boost::num_vertices(g));
#if BOOST_VERSION >= 104700
      double flow =
          boykov_kolmogorov_max_flow(g, capacity, residual_capacity, rev,
                                     &color[0], indexmap, vSource, vSink);
#else
      double flow = kolmogorov_max_flow(g, capacity, residual_capacity, rev,
                                        &color[0], indexmap, vSource, vSink);
#endif
      std::cout << "c  The total flow:" << std::endl;
      std::cout << "s " << flow << std::endl << std::endl;

      // for all pixels in imIn create a vertex and an edge
      for (off_t o1 = 0; o1 < (off_t) pixelCount; o1++) {
        T1 val1 = (int) bufIn[o1];
        T2 val2 = (int) bufOut[o1];
        T2 val3 = (int) bufMarker[o1];

        if (val2 == 1) {
          if (color[val1] == color[vSource])
            bufOut[o1] = (T2) nbk;
        }

        bool hasEdge;
        if (val3 == nbk) {
          boost::tie(e4, hasEdge) = boost::edge(vSource, o1, g);
          if (hasEdge) {
            boost::remove_edge(vSource, o1, g);
            boost::remove_edge(o1, vSource, g);
          }
        } else if (val3 > 1) {
          boost::tie(e4, hasEdge) = boost::edge(o1, vSink, g);
          if (hasEdge) {
            boost::remove_edge(o1, vSink, g);
            boost::remove_edge(vSink, o1, g);
          }
        }
      }
    }
    return RES_OK;
  }

} // namespace smil

#endif // _D_GEOCUTS_WATERSHED_HPP_


/*
 [auto_generated]
  boost/numeric/odeint/iterator/detail/times_iterator_impl.hpp

  [begin_description]
  tba.
  [end_description]

  Copyright 2009-2013 Karsten Ahnert
  Copyright 2009-2013 Mario Mulansky

  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)
*/

#ifndef BOOST_NUMERIC_ODEINT_ITERATOR_DETAIL_TIMES_ITERATOR_IMPL_HPP_DEFINED
#define BOOST_NUMERIC_ODEINT_ITERATOR_DETAIL_TIMES_ITERATOR_IMPL_HPP_DEFINED

#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/throw_exception.hpp>

#include <boost/numeric/odeint/util/unit_helper.hpp>
#include <boost/numeric/odeint/util/copy.hpp>
#include <boost/numeric/odeint/stepper/controlled_step_result.hpp>
#include <boost/numeric/odeint/iterator/detail/ode_iterator_base.hpp>

namespace boost {
namespace numeric {
namespace odeint {

    template< class Iterator , class Stepper , class System , class State , class TimeIterator ,
              typename Tag , typename StepperTag >
    class times_iterator_impl;

    /*
     * Specilization for basic steppers
     */
    /**
     * \brief ODE Iterator with constant step size.
     *
     * Implements an ODE iterator with observer calls at predefined times.
     * Uses controlled steppers. times_iterator is a model of single-pass iterator.
     *
     * The value type of this iterator is the state type of the stepper. Hence one can only access the state and not the current time.
     *
     * \tparam Stepper The stepper type which should be used during the iteration.
     * \tparam System The type of the system function (ODE) which should be solved.
     */
    template< class Iterator , class Stepper , class System , class State , class TimeIterator , typename Tag >
    class times_iterator_impl< Iterator , Stepper , System , State , TimeIterator , Tag , stepper_tag >
        : public detail::ode_iterator_base< Iterator , Stepper , System , State , Tag >
    {
    private:

        typedef Stepper stepper_type;
        typedef System system_type;
        typedef typename boost::numeric::odeint::unwrap_reference< stepper_type >::type unwrapped_stepper_type;
        typedef State state_type;
        typedef TimeIterator time_iterator_type;
        typedef typename traits::time_type< stepper_type >::type time_type;
        typedef typename traits::value_type< stepper_type >::type ode_value_type;
        #ifndef DOXYGEN_SKIP
        typedef detail::ode_iterator_base< Iterator , Stepper , System , State , Tag > base_type;
        #endif

    public:

        /**
         * \brief Constructs a times_iterator. This constructor should be used to construct the begin iterator.
         *
         * \param stepper The stepper to use during the iteration.
         * \param sys The system function (ODE) to solve.
         * \param s The initial state. adaptive_iterator stores a reference of s and changes its value during the iteration.
         * \param t_start Iterator to the begin of a sequence of time values.
         * \param t_end Iterator to the begin of a sequence of time values.
         * \param dt The (initial) time step.
         */
        times_iterator_impl( stepper_type stepper , system_type sys , state_type &s ,
                             time_iterator_type t_start , time_iterator_type t_end , time_type dt )
            : base_type( stepper , sys , *t_start , dt ) ,
              m_t_start( t_start ) , m_t_end( t_end ) , m_state( &s )
        {
            if( t_start == t_end )
                this->m_at_end = true;
        }

        /**
         * \brief Constructs an adaptive_iterator. This constructor should be used to construct the end iterator.
         *
         * \param stepper The stepper to use during the iteration.
         * \param sys The system function (ODE) to solve.
         * \param s The initial state. adaptive_iterator store a reference of s and changes its value during the iteration.
         */
        times_iterator_impl( stepper_type stepper , system_type sys , state_type &s )
            : base_type( stepper , sys ) , m_state( &s ) { }

    protected:

        friend class boost::iterator_core_access;

        void increment()
        {
            unwrapped_stepper_type &stepper = this->m_stepper;
            if( ++m_t_start != m_t_end )
            {
                while( detail::less_with_sign( this->m_t , static_cast<time_type>(*m_t_start) , this->m_dt ) )
                {
                    const time_type current_dt = detail::min_abs( this->m_dt , static_cast<time_type>(*m_t_start) - this->m_t );
                    stepper.do_step( this->m_system , *( this->m_state ) , this->m_t , current_dt );
                    this->m_t += current_dt;
                }

            } else {
                this->m_at_end = true;
            }
         }

    public:
        const state_type& get_state() const
        {
            return *m_state;
        }

    private:
        time_iterator_type m_t_start;
        time_iterator_type m_t_end;
        state_type* m_state;
    };

    /*
     * Specilization for controlled steppers
     */
    /**
     * \brief ODE Iterator with adaptive step size control. The value type of this iterator is the state type of the stepper.
     *
     * Implements an ODE iterator with observer calls at predefined times.
     * Uses controlled steppers. times_iterator is a model of single-pass iterator.
     *
     * The value type of this iterator is the state type of the stepper. Hence one can only access the state and not the current time.
     *
     * \tparam Stepper The stepper type which should be used during the iteration.
     * \tparam System The type of the system function (ODE) which should be solved.
     */
    template< class Iterator , class Stepper , class System , class State , class TimeIterator , typename Tag >
    class times_iterator_impl< Iterator , Stepper , System , State , TimeIterator , Tag , controlled_stepper_tag >
        : public detail::ode_iterator_base< Iterator , Stepper , System , State , Tag >
    {
    private:

        typedef Stepper stepper_type;
        typedef System system_type;
        typedef typename boost::numeric::odeint::unwrap_reference< stepper_type >::type unwrapped_stepper_type;
        typedef State state_type;
        typedef TimeIterator time_iterator_type;
        typedef typename traits::time_type< stepper_type >::type time_type;
        typedef typename traits::value_type< stepper_type >::type ode_value_type;
        #ifndef DOXYGEN_SKIP
        typedef detail::ode_iterator_base< Iterator , Stepper , System , State , Tag > base_type;
        #endif

    public:

        /**
         * \brief Constructs a times_iterator. This constructor should be used to construct the begin iterator.
         *
         * \param stepper The stepper to use during the iteration.
         * \param sys The system function (ODE) to solve.
         * \param s The initial state. adaptive_iterator stores a reference of s and changes its value during the iteration.
         * \param t_start Iterator to the begin of a sequence of time values.
         * \param t_end Iterator to the begin of a sequence of time values.
         * \param dt The (initial) time step.
         */
        times_iterator_impl( stepper_type stepper , system_type sys , state_type &s ,
                             time_iterator_type t_start , time_iterator_type t_end , time_type dt )
            : base_type( stepper , sys , *t_start , dt ) ,
              m_t_start( t_start ) , m_t_end( t_end ) , m_state( &s )
        {
            if( t_start == t_end )
                this->m_at_end = true;
        }

        /**
         * \brief Constructs an adaptive_iterator. This constructor should be used to construct the end iterator.
         *
         * \param stepper The stepper to use during the iteration.
         * \param sys The system function (ODE) to solve.
         * \param s The initial state. adaptive_iterator store a reference of s and changes its value during the iteration.
         */
        times_iterator_impl( stepper_type stepper , system_type sys , state_type &s )
            : base_type( stepper , sys ) , m_state( &s ) { }

    protected:

        friend class boost::iterator_core_access;

        void increment()
        {
            if( ++m_t_start != m_t_end )
            {
                while( detail::less_with_sign( this->m_t , static_cast<time_type>(*m_t_start) , this->m_dt ) )
                {
                    if( detail::less_with_sign( static_cast<time_type>(*m_t_start) - this->m_t , this->m_dt , this->m_dt ) )
                    {
                        // we want to end exactly at the time point
                        time_type current_dt = static_cast<time_type>(*m_t_start) - this->m_t;
                        step_loop( current_dt );
                    } else {
                        step_loop( this->m_dt );
                    }
                }

            } else {
                this->m_at_end = true;
            }
        }

    private:
        void step_loop( time_type &dt )
        {
            unwrapped_stepper_type &stepper = this->m_stepper;
            const size_t max_attempts = 1000;
            size_t trials = 0;
            controlled_step_result res = success;
            do
            {
                res = stepper.try_step( this->m_system , *( this->m_state ) , this->m_t , dt );
                ++trials;
            }
            while( ( res == fail ) && ( trials < max_attempts ) );
            if( trials == max_attempts )
            {
                BOOST_THROW_EXCEPTION( std::overflow_error( "Adaptive iterator : Maximal number of iterations reached. A step size could not be found." ) );
            }
        }

    public:
        const state_type& get_state() const
        {
            return *m_state;
        }

    private:
        time_iterator_type m_t_start;
        time_iterator_type m_t_end;
        state_type* m_state;
    };

    /*
     * Specilization for dense outputer steppers
     */
    /**
     * \brief ODE Iterator with step size control and dense output.
     * Implements an ODE iterator with adaptive step size control. Uses dense-output steppers.
     * times_iterator is a model of single-pass iterator.
     *
     * \tparam Stepper The stepper type which should be used during the iteration.
     * \tparam System The type of the system function (ODE) which should be solved.
     */
    template< class Iterator , class Stepper , class System , class State , class TimeIterator , typename Tag >
    class times_iterator_impl< Iterator , Stepper , System , State , TimeIterator , Tag , dense_output_stepper_tag >
        : public detail::ode_iterator_base< Iterator , Stepper , System , State , Tag >
    {
    private:

        typedef Stepper stepper_type;
        typedef System system_type;
        typedef typename boost::numeric::odeint::unwrap_reference< stepper_type >::type unwrapped_stepper_type;
        typedef State state_type;
        typedef TimeIterator time_iterator_type;
        typedef typename traits::time_type< stepper_type >::type time_type;
        typedef typename traits::value_type< stepper_type >::type ode_value_type;
        #ifndef DOXYGEN_SKIP
        typedef detail::ode_iterator_base< Iterator , Stepper , System , State , Tag > base_type;
        #endif

   public:

        /**
         * \brief Constructs a times_iterator. This constructor should be used to construct the begin iterator.
         *
         * \param stepper The stepper to use during the iteration.
         * \param sys The system function (ODE) to solve.
         * \param s The initial state.
         * \param t_start Iterator to the begin of a sequence of time values.
         * \param t_end Iterator to the begin of a sequence of time values.
         * \param dt The (initial) time step.
         */
        times_iterator_impl( stepper_type stepper , system_type sys , state_type &s ,
                             time_iterator_type t_start , time_iterator_type t_end , time_type dt )
            : base_type( stepper , sys , *t_start , dt ) ,
              m_t_start( t_start ) , m_t_end( t_end ) , m_final_time( *(t_end-1) ) ,
              m_state( &s )
        {
            if( t_start != t_end )
            {
                unwrapped_stepper_type &st = this->m_stepper;
                st.initialize( *( this->m_state ) , this->m_t , this->m_dt );
            } else {
                this->m_at_end = true;
            }
        }

        /**
         * \brief Constructs a times_iterator. This constructor should be used to construct the end iterator.
         *
         * \param stepper The stepper to use during the iteration.
         * \param sys The system function (ODE) to solve.
         * \param s The initial state.
         */
        times_iterator_impl( stepper_type stepper , system_type sys , state_type &s )
            : base_type( stepper , sys ) , m_state( &s ) { }

    protected:

        friend class boost::iterator_core_access;

        void increment()
        {
            unwrapped_stepper_type &st = this->m_stepper;
            if( ++m_t_start != m_t_end )
            {
                this->m_t = static_cast<time_type>(*m_t_start);
                while( detail::less_with_sign( st.current_time() , this->m_t , this->m_dt ) )
                {
                    // make sure we don't go beyond the last point
                    if( detail::less_with_sign( m_final_time-st.current_time() , st.current_time_step() , st.current_time_step() ) )
                    {
                        st.initialize( st.current_state() , st.current_time() , m_final_time-st.current_time() );
                    }
                    st.do_step( this->m_system );
                }
                st.calc_state( this->m_t , *( this->m_state ) );
            } else {
                this->m_at_end = true;
            }
        }

    public:
        const state_type& get_state() const
        {
            return *m_state;
        }

    private:
        time_iterator_type m_t_start;
        time_iterator_type m_t_end;
        time_type m_final_time;
        state_type* m_state;
    };

} // namespace odeint
} // namespace numeric
} // namespace boost

#endif // BOOST_NUMERIC_ODEINT_ITERATOR_DETAIL_TIMES_ITERATOR_IMPL_HPP_DEFINED


/***
第三节 类模板中的友元

(2)友元函数：函数模板可以被声明为友元函数
(2.1)让函数模板的某个实例成为友元函数
代码行_nmsp1::func(2, 3);，会实例化出 void func<int,int>(int,int){......}

(2.2)友元模板
将func函数模板（泛化版本）声明为Men类模板的友元模板之后，那么func函数模板的特化版本也会被看成是Men类模板的友元。
编译器会把全特化的func函数模板看待成一个实例化过的函数模板。
看待成了void func<int,double>(int val1, double val2)

(2.3)在类模板中定义友元函数
这种友元函数是能够被调用的，而且也只有在代码中调用了函数的时候，编译器才会实例化出这个函数。
之所以这样定义友元函数，一般都是因为在该友元函数中会 用到这个类模板。
 这种友元函数的调用与调用普通函数函数，大家就把他当成普通函数来看待即可。
func2在Men类模板被实例化时并不会被一并实例化出来，只有调用了func2的时候，才会被实例化出来。
因为func2在类模板Men中，所以调用func2时，如果func2中的代码特别简单，则func2会被当成内联函数来处理。
如果func2中的代码比较复杂，比如出现了for循环，那么func2很可能就不会被当做内联函数来处理。
func2(mymen2); 可以被实例化出  void func2(class Men<double>&);
func2(mymen3); 可以被实例化出  void func2(class Men<int>&);
func2其实是个全局函数。
***/

#include <iostream>

//#include <boost/type_index.hpp>
using namespace std;
//#pragma warning(disable : 4996)

namespace _nmsp1
{
	//函数模板func的声明
	//template <typename U, typename V> void func(U val1, V val2);

	//Men类模板
	template <typename Z>
	class Men
	{
		friend void func2(Men<Z>& tmpmen)
		{
			//for(int i= 0; i<1 ; ++i)
				tmpmen.funcmen();
		}

		//friend void func<int, int>(int, int);  //<int,int>是两个模板实参
		////friend void func<>(int, int);
		////friend void func<int>(int, int);

		////friend void func<float,int>(float, int);
		//friend void func<>(float, int);

		////friend void func<int, float>(int, float);
		//friend void func<>(int, float);

		//让函数模板func成为类模板Men的友元函数模板
		template <typename U, typename V> friend void func(U val1, V val2);

	private:
		void funcmen() const
		{
			cout << "Men::funcmen被调用了" << endl;
		}
	};

	template <typename U,typename V>
	void func(U val1, V val2)
	{
		/*cout << "val1 = " << val1 << endl;
		cout << "val2 = " << val2 << endl;*/
		//Men mymen;
		Men<int> mymen;
		mymen.funcmen();
	}

	//func全特化版本
	template <>
	void func(int val1, double val2)
	{
		Men<int> mymen;
		mymen.funcmen();
	}

}

int main()
{
	//调用func的方法很多
	//_nmsp1::func(2, 3);
	//_nmsp1::func<float>(4.6f, 5); //第一个模板参数指定，第二个模板参数编译器自己推断
	//_nmsp1::func<int, float>(4, 5.8f); //完全手工指定模板参数

	_nmsp1::Men<double> mymen2;
	func2(mymen2); //直接调用Men类模板中定义的友元函数func2

	_nmsp1::Men<int> mymen3;
	func2(mymen3);

	return 0;
}


/**
 * -*- c++ -*-
 *
 * \file iterator_type.hpp
 *
 * \brief Iterator to a given container type.
 *
 * Copyright (c) 2009, Marco Guazzone
 *
 * 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)
 *
 * \author Marco Guazzone, marco.guazzone@gmail.com
 */

#ifndef BOOST_NUMERIC_UBLAS_TRAITS_ITERATOR_TYPE_HPP
#define BOOST_NUMERIC_UBLAS_TRAITS_ITERATOR_TYPE_HPP

#include <boost/numeric/ublas/fwd.hpp>
#include <boost/numeric/ublas/traits.hpp>
#include <boost/numeric/ublas/tags.hpp>

namespace boost { namespace numeric { namespace ublas {

    namespace detail {

        /**
         * \brief Auxiliary class for retrieving the iterator to the given
         *  matrix expression according its orientation and to the given dimension tag.
         * \tparam MatrixT A model of MatrixExpression.
         * \tparam TagT A dimension tag type (e.g., tag::major).
         * \tparam OrientationT An orientation category type (e.g., row_major_tag).
         */
        template <typename MatrixT, typename TagT, typename OrientationT>
        struct iterator_type_impl;

        /// \brief Specialization of \c iterator_type_impl for row-major oriented
        ///  matrices and over the major dimension.
        template <typename MatrixT>
        struct iterator_type_impl<MatrixT,tag::major,row_major_tag>
        {
            typedef typename matrix_traits<MatrixT>::iterator1 type;
        };

        /// \brief Specialization of \c iterator_type_impl for column-major oriented
        ///  matrices and over the major dimension.
        template <typename MatrixT>
        struct iterator_type_impl<MatrixT,tag::major,column_major_tag>
        {
            typedef typename matrix_traits<MatrixT>::iterator2 type;
        };

        /// \brief Specialization of \c iterator_type_impl for row-major oriented
        ///  matrices and over the minor dimension.
        template <typename MatrixT>
        struct iterator_type_impl<MatrixT,tag::minor,row_major_tag>
        {
            typedef typename matrix_traits<MatrixT>::iterator2 type;
        };

        /// \brief Specialization of \c iterator_type_impl for column-major oriented
        ///  matrices and over the minor dimension.
        template <typename MatrixT>
        struct iterator_type_impl<MatrixT,tag::minor,column_major_tag>
        {
            typedef typename matrix_traits<MatrixT>::iterator1 type;
        };

    } // Namespace detail

    /**
     * \brief A iterator for the given container type over the given dimension.
     * \tparam ContainerT A container expression type.
     * \tparam TagT A dimension tag type (e.g., tag::major).
     */
    template <typename ContainerT, typename TagT=void>
    struct iterator_type;

    /**
     * \brief Specialization of \c iterator_type for vector expressions.
     * \tparam VectorT A model of VectorExpression type.
     */
    template <typename VectorT>
    struct iterator_type<VectorT, void>
    {
        typedef typename vector_traits<VectorT>::iterator type;
    };

    /**
     * \brief Specialization of \c iterator_type for matrix expressions and
     *  over the major dimension.
     * \tparam MatrixT A model of MatrixExpression type.
     */
    template <typename MatrixT>
    struct iterator_type<MatrixT,tag::major>
    {
        typedef typename detail::iterator_type_impl<MatrixT,tag::major,typename matrix_traits<MatrixT>::orientation_category>::type type;
    };

    /**
     * \brief Specialization of \c iterator_type for matrix expressions and
     *  over the minor dimension.
     * \tparam MatrixT A model of MatrixExpression type.
     */
    template <typename MatrixT>
    struct iterator_type<MatrixT,tag::minor>
    {
        typedef typename detail::iterator_type_impl<MatrixT,tag::minor,typename matrix_traits<MatrixT>::orientation_category>::type type;
    };

}}} // Namespace boost::numeric::ublas

#endif // BOOST_NUMERIC_UBLAS_TRAITS_ITERATOR_TYPE_HPP