/**
 * @file onepass_plus.hpp
 * @author Leonardo Arcari (leonardo1.arcari@gmail.com)
 * @version 1.0.0
 * @date 2018-10-28
 *
 * @copyright Copyright (c) 2018 Leonardo Arcari
 *
 * MIT Licence
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */

#ifndef BOOST_ONEPASS_PLUS_HPP
#define BOOST_ONEPASS_PLUS_HPP

#include <boost/graph/graph_concepts.hpp>
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/properties.hpp>

#include <arlib/terminators.hpp>
#include <arlib/type_traits.hpp>

#include <arlib/details/onepass_plus_impl.hpp>

#include <cassert>
#include <iostream>
#include <memory>
#include <queue>
#include <unordered_map>
#include <vector>

/**
 * An Alternative-Routing library for Boost.Graph
 */
namespace arlib {
/**
 * An implementation of OnePass+ k-shortest path with limited overlap for
 * Boost::Graph.
 *
 * This implementation refers to the following publication:
 * Theodoros Chondrogiannis, Panagiotis Bouros, Johann Gamper and Ulf Leser,
 * Exact and Approximate Algorithms for Finding k-Shortest Paths with Limited
 * Overlap , In Proc. of the 20th Int. Conf. on Extending Database Technology
 * (EDBT) (2017)
 *
 * @tparam Graph A Boost::VertexAndEdgeListGraph
 * @tparam WeightMap The weight or "length" of each edge in the graph. The
 *         weights must all be non-negative, and the algorithm will throw a
 *         negative_edge exception is one of the edges is negative. The type
 *         WeightMap must be a model of Readable Property Map. The edge
 *         descriptor type of the graph needs to be usable as the key type for
 *         the weight map. The value type for this map must be the same as the
 *         value type of the distance map.
 * @tparam MultiPredecessorMap The multi predecessor map records the edges in
 *         the alternative paths from @p s to @p t. Upon completion of the
 *         algorithm, for any vertex `v` on any alternative path `p`, multi
 *         predecessor map stores the predecessor of node `v` on path `p`. If no
 *         predecessor for a node `v'` is reported, then `v'` is not part of any
 *         alternative path or it is the source node @p s. The type of
 *         `MultiPredecessorMap` must be a model of `Read Property Map`. The
 *         vertex descriptor of the input graph @p G must be usable as a key
 *         type for the multi predecessor map. Whereas the value type must
 *         satisfy the `UnorderedAssociativeContainer` concept, where its key is
 *         an `int`, the index/number of alternative path for which it exists a
 *         predecessor of `v`, and where the value type is a vertex descriptor
 *         of input graph @p G.
 * @tparam Vertex The vertex descriptor.
 * @param G The input graph.
 * @param weight The weight map of @p G.
 * @param predecessors The multi predecessor map of @p G.
 * @param s The source node.
 * @param t The target node.
 * @param k The number of alternative paths to compute.
 * @param theta The similarity threshold.
 *
 */
template <typename Graph, typename WeightMap, typename MultiPredecessorMap,
          typename Terminator = arlib::always_continue,
          typename Vertex = vertex_of_t<Graph>>
void onepass_plus(const Graph &G, WeightMap weight,
                  MultiPredecessorMap &predecessors, Vertex s, Vertex t, int k,
                  double theta, Terminator &&terminator = Terminator{}) {
  using namespace boost;
  using Edge = typename graph_traits<Graph>::edge_descriptor;
  using Length = typename boost::property_traits<WeightMap>::value_type;

  BOOST_CONCEPT_ASSERT((VertexAndEdgeListGraphConcept<Graph>));
  BOOST_CONCEPT_ASSERT((LvaluePropertyMapConcept<WeightMap, Edge>));
  BOOST_CONCEPT_ASSERT(
      (ReadablePropertyMapConcept<MultiPredecessorMap, Vertex>));

  auto resPathsEdges = std::vector<std::vector<Edge>>{};

  // resEdges keeps track of the edges that make the paths in resPaths and which
  // path includes it.
  using resPathIndex = typename decltype(resPathsEdges)::size_type;
  auto resEdges =
      std::unordered_map<Edge, std::vector<resPathIndex>, boost::hash<Edge>>{};

  // Min-priority queue
  using Label = details::OnePassLabel<Graph, Length>;
  using LabelPtr = std::unique_ptr<Label>;
  auto Q =
      std::priority_queue<Label *, std::vector<Label *>,
                          details::OnePassPlusASComparator<Graph, Length>>{};
  auto created_labels = std::vector<LabelPtr>{};

  // Skyline for dominance checkind (Lemma 2)
  auto skyline = details::SkylineContainer<Graph, Length>{};

  // Compute lower bounds for AStar
  auto lower_bounds = details::distance_from_target<Length>(G, t);

  // Compute shortest path from s to t
  auto sp_path = details::compute_shortest_path(G, weight, s, t);
  if (!sp_path) {
    auto oss = std::ostringstream{};
    oss << "Vertex " << t << " is unreachable from " << s;
    throw details::target_not_found{oss.str()};
  }

  // P_LO <-- {shortest path p_0(s, t)};
  resPathsEdges.push_back(*sp_path);
  resPathIndex paths_count = 1;

  // If we need the shortest path only
  if (k == 1) {
    details::fill_multi_predecessor(resPathsEdges.begin(), resPathsEdges.end(),
                                    G, predecessors);
    return;
  }

  // For each edge in the candidate path, we check if it's already in any of the
  // resPaths. If not, we add it to resEdges. If yes, we keep track of which
  // path includes it.
  details::update_res_edges(*sp_path, resEdges, paths_count);

  // Initialize min-priority queue Q with <s, empty_set>
  auto init_label =
      std::make_unique<Label>(s, 0, lower_bounds[s], k, paths_count - 1);
  Q.push(init_label.get());
  created_labels.push_back(std::move(init_label));

  // While Q is not empty
  while (!Q.empty()) {
    // The remainder code is the hot part of the algorithm. So we check here
    // if the algorithm should terminate
    if (terminator.should_stop()) {
      throw terminator_stop_error{
          "OnePass+ terminated before completing due to a Terminator. Please "
          "discard partial output."};
    }

    // Current path
    auto label = Q.top();
    Q.pop();

    // Perform lazy update of the similairty vector of 'label', since new paths
    // might have been added to P_LO from the time this 'label' was pushed into
    // priority queue.
    if (label->is_outdated(paths_count - 1)) {
      bool below_sim_threshold = details::update_label_similarity(
          *label, G, resEdges, resPathsEdges, weight, theta, paths_count);

      label->set_last_check(paths_count - 1); // Update last check time step
      if (!below_sim_threshold) {
        continue; // Skip candidate path
      }
    }

    // If we found the target node
    if (label->get_node() == t) {
      // Build the new k-th shortest path
      resPathsEdges.push_back(label->get_path(G));

      auto &tmpPath = resPathsEdges.back();
      ++paths_count;

      if (static_cast<int>(paths_count) == k) { // we found k paths. End.
        // Add computed alternatives to resPaths
        details::fill_multi_predecessor(resPathsEdges.begin(),
                                        resPathsEdges.end(), G, predecessors);
        break;
      }

      // For each edge in the candidate path see if it's already in any of the
      // P_LO paths. If not, add it to the resEdges. If so, keep track of which
      // path includes it
      details::update_res_edges(tmpPath, resEdges, paths_count);

    } else { // Expand Search
      if (skyline.dominates(*label)) {
        continue; // Prune path by Lemma 2
      }

      skyline.insert(label);
      auto node_n = label->get_node();
      // For each outgoing edge
      for (auto adj_it = adjacent_vertices(node_n, G).first;
           adj_it != adjacent_vertices(node_n, G).second; ++adj_it) {
        // Expand path
        auto c_edge = edge(node_n, *adj_it, G).first;
        auto c_label =
            details::expand_path(label, *adj_it, lower_bounds[*adj_it],
                                 weight[c_edge], paths_count - 1);

        // Check for acyclicity
        bool acyclic = label->is_path_acyclic(*adj_it);

        if (acyclic) {
          auto c_similarity_map = label->get_similarity_map();

          // Check Lemma 1 for similarity thresholding
          bool below_sim_threshold = details::is_below_sim_threshold(
              c_edge, c_similarity_map, theta, resEdges, resPathsEdges, weight);

          if (below_sim_threshold) {
            c_label->set_similarities(std::begin(c_similarity_map),
                                      std::end(c_similarity_map));
            Q.push(c_label.get());
            created_labels.push_back(std::move(c_label));
          }
        }
      }
    }
  }

  if (static_cast<int>(paths_count) != k) {
    // Add computed alternatives to resPaths
    details::fill_multi_predecessor(resPathsEdges.begin(), resPathsEdges.end(),
                                    G, predecessors);
  }
}

/**
 * An implementation of OnePass+ k-shortest path with limited overlap for
 * Boost::Graph.
 *
 * This overload takes an input graph modeling `PropertyGraph` concept having at
 * least one edge property with tag `boost::edge_weight_t`. Moreover it does not
 * require an explicit `WeightMap` parameter, because it is directly gathered
 * from the `PropertyGraph`.
 *
 * @see onepass_plus(const Graph &G, WeightMap weight,
 *                   MultiPredecessorMap &predecessors, Vertex s, Vertex t, int
 *                   k, double theta)
 *
 * @tparam PropertyGraph A Boost::PropertyGraph having at least one edge
 *         property with tag boost::edge_weight_t.
 *
 */
template <typename PropertyGraph, typename MultiPredecessorMap,
          typename Terminator = arlib::always_continue,
          typename Vertex = vertex_of_t<PropertyGraph>>
void onepass_plus(const PropertyGraph &G, MultiPredecessorMap &predecessors,
                  Vertex s, Vertex t, int k, double theta,
                  Terminator &&terminator = Terminator{}) {
  using namespace boost;
  using Edge = typename graph_traits<PropertyGraph>::edge_descriptor;

  BOOST_CONCEPT_ASSERT(
      (PropertyGraphConcept<PropertyGraph, Edge, edge_weight_t>));

  auto weight = get(edge_weight, G);
  onepass_plus(G, weight, predecessors, s, t, k, theta,
               std::forward<Terminator>(terminator));
}
} // namespace arlib

#endif


/*
 * morpho.cxx
 *
 *  Created on: Aug 21, 2013
 *      Author: Thomas Walter
 */

// define PY_ARRAY_UNIQUE_SYMBOL (required by the numpy C-API)
#define PY_ARRAY_UNIQUE_SYMBOL vigranumpymorpho_PyArray_API

// include the vigranumpy C++ API
#include <Python.h>
#include <boost/python.hpp>
#include <boost/python/list.hpp>
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>

#include <vigra/numpy_array.hxx>
#include <vigra/numpy_array_converters.hxx>

// specific includes
#include <vigra/morpho_basic.hxx>
#include <vigra/morpho_utilities.hxx>
#include <vigra/morpho_geodesy.hxx>
#include <vigra/morpho_criteria.hxx>
#include <vigra/morpho_watershed.hxx>
#include <vigra/morpho_dynamic.hxx>

// implementation of your wrapper functions and classes
//template <class PixelType>
//NumpyAnyArray
//pythonConvolveImage(NumpyArray<3, Multiband<PixelType> > image,
//                    TwoDKernel const & kernel,
//                    NumpyArray<3, Multiband<PixelType> > res = python::object())
//{
//    res.reshapeIfEmpty(image.taggedShape(),
//            "convolve(): Output array has wrong shape.");
//
//    {
//        PyAllowThreads _pythread;
//        for(int k=0;k<image.shape(2);++k)
//        {
//            MultiArrayView<2, PixelType, StridedArrayTag> bimage = image.bindOuter(k);
//            MultiArrayView<2, PixelType, StridedArrayTag> bres = res.bindOuter(k);
//            convolveImage(srcImageRange(bimage), destImage(bres),
//                          kernel2d(kernel));
//        }
//    }
//    return res;
//}

namespace python = boost::python;

namespace vigra
{

//template<class T>
//void pythonInitExplicitlyKernel1D(Kernel1D<T> &k, int left, int right, NumpyArray<1,T> contents)
//{
//    vigra_precondition(contents.size() == 1 || right-left+1 == contents.shape(0),
//              "Kernel1D::initExplicitly(): 'contents' must contain as many elements as the kernel (or just one element).");
//
//    k.initExplicitly(left,right);
//    for(int i=left; i<=right; ++i)
//    {
//        k[i] = (contents.size() == 1)
//                     ? contents(0)
//                     : contents(i-left);
//    }
//}

//NumpyArray<2, Singleband<PixelType> > image,

class structuringElement2D_Conv: public morpho::structuringElement2D
{
    public:
        structuringElement2D_Conv(): structuringElement2D() {}
        structuringElement2D_Conv(boost::python::list a, int s=1) {
            size = s;
            boost::python::ssize_t len = boost::python::len(a);
            for(int i=0; i<len;i++){
                Diff2D from_python((int)boost::python::extract<int>(a[i][0]),
                                   (int)boost::python::extract<int>(a[i][1]));
                support.push_back(from_python);

            }
            CalculateExtension();
        }
};

template <class PixelType>
NumpyAnyArray
pythonMorphoReconstructionByDilation(NumpyArray<2, Singleband<PixelType> > marker,
                                     NumpyArray<2, Singleband<PixelType> > mask,
                                     int neighborhood_graph
                                   )
{
    PyAllowThreads _pythread;

    Diff2D image_size = Diff2D(marker.width(), marker.height());

    switch(neighborhood_graph) {
        case 8:
        {
            morpho::neighborhood2D nb(morpho::WITHOUTCENTER8, image_size);
            morpho::morphoReconstructionByDilation(destImageRange(marker), srcImage(mask), nb);
            break;
        }
        case 4:
        {
            morpho::neighborhood2D nb(morpho::WITHOUTCENTER4, image_size);
            morpho::morphoReconstructionByDilation(destImageRange(marker), srcImage(mask), nb);
            break;
        }
        default:
        {
            vigra_precondition(false,
                         "pythonMorphoOpeningByReconstruction(): Unknown neighborhood graph.\n"
                         "This function is only defined for 4- and 8-neighborhoods.");
            break;
        }
    };

    return marker;
}

template <class PixelType>
NumpyAnyArray
pythonMorphoReconstructionByErosion(NumpyArray<2, Singleband<PixelType> > marker,
                                     NumpyArray<2, Singleband<PixelType> > mask,
                                     int neighborhood_graph
                                   )
{
    PyAllowThreads _pythread;

    Diff2D image_size = Diff2D(marker.width(), marker.height());

    switch(neighborhood_graph) {
        case 8:
        {
            morpho::neighborhood2D nb(morpho::WITHOUTCENTER8, image_size);
            morpho::morphoReconstructionByErosion(destImageRange(marker), srcImage(mask), nb);
            break;
        }
        case 4:
        {
            morpho::neighborhood2D nb(morpho::WITHOUTCENTER4, image_size);
            morpho::morphoReconstructionByErosion(destImageRange(marker), srcImage(mask), nb);
            break;
        }
        default:
        {
            vigra_precondition(false,
                         "pythonMorphoOpeningByReconstruction(): Unknown neighborhood graph.\n"
                         "This function is only defined for 4- and 8-neighborhoods.");
            break;
        }
    };

    return marker;
}

template <class PixelType>
NumpyAnyArray
pythonMorphoOpeningByReconstruction(NumpyArray<2, Singleband<PixelType> > image,
                                    structuringElement2D_Conv se,
                                    int neighborhood_graph,
                                    NumpyArray<2, Singleband<PixelType> > res
                                   )
{
    res.reshapeIfEmpty(image.taggedShape(),
            "morphoOpeningByReconstruction(): Output array has wrong shape.");
    PyAllowThreads _pythread;

    Diff2D image_size = Diff2D(image.width(), image.height());

    switch(neighborhood_graph) {
        case 8:
        {
            morpho::neighborhood2D nb(morpho::WITHOUTCENTER8, image_size);
            morpho::morphoOpeningByReconstruction(image, res, se, nb);
            break;
        }
        case 4:
        {
            morpho::neighborhood2D nb(morpho::WITHOUTCENTER4, image_size);
            morpho::morphoOpeningByReconstruction(image, res, se, nb);
            break;
        }
        default:
        {
            vigra_precondition(false,
                         "pythonMorphoOpeningByReconstruction(): Unknown neighborhood graph.\n"
                         "This function is only defined for 4- and 8-neighborhoods.");
            //std::cout << "This neighborhood is not defined." << std::endl;
            break;
        }
    };

    //morpho::neighborhood2D nb(morpho::WITHOUTCENTER8, image_size);
    //morpho::morphoOpeningByReconstruction(image, res, se, nb);

    return res;
}

template <class PixelType>
NumpyAnyArray
pythonMorphoDiameterOpening(NumpyArray<2, Singleband<PixelType> > image,
                            int diameter,
                            int neighborhood_graph,
                            NumpyArray<2, Singleband<PixelType> > res
                            )
{
    res.reshapeIfEmpty(image.taggedShape(),
            "morphoDiameterOpening(): Output array has wrong shape.");
    PyAllowThreads _pythread;

    Diff2D image_size = Diff2D(image.width(), image.height());

    switch(neighborhood_graph) {
        case 8:
        {
            morpho::neighborhood2D nb(morpho::WITHOUTCENTER8, image_size);
            morpho::morphoDiameterOpening(image, res, diameter, nb);
            break;
        }
        case 4:
        {
            morpho::neighborhood2D nb(morpho::WITHOUTCENTER4, image_size);
            morpho::morphoDiameterOpening(image, res, diameter, nb);
            break;
        }
        default:
        {
            vigra_precondition(false,
                         "pythonMorphoDiameterOpening(): Unknown neighborhood graph.\n"
                         "This function is only defined for 4- and 8-neighborhoods.");
            break;
        }
    };

    return res;
}

template <class PixelType, class LabelType>
NumpyAnyArray
pythonMorphoWatershed(NumpyArray<2, Singleband<PixelType> > image,
                      int neighborhood_graph,
                      NumpyArray<2, Singleband<LabelType> > res)
{
    res.reshapeIfEmpty(image.taggedShape(),
            "morphoWatershed(): Output array has wrong shape.");
    PyAllowThreads _pythread;

    Diff2D image_size = Diff2D(image.width(), image.height());

    switch(neighborhood_graph) {
        case 8:
        {
            morpho::neighborhood2D nb(morpho::WITHOUTCENTER8, image_size);
            morpho::morphoWatershed(image, res, nb);
            break;
        }
        case 4:
        {
            morpho::neighborhood2D nb(morpho::WITHOUTCENTER4, image_size);
            morpho::morphoWatershed(image, res, nb);
            break;
        }
        default:
        {
            vigra_precondition(false,
                         "pythonMorphoWatershed(): Unknown neighborhood graph.\n"
                         "This function is only defined for 4- and 8-neighborhoods.");
            break;
        }
    };

    return res;
}

template <class PixelType, class LabelType>
NumpyAnyArray
pythonMorphoSelectiveWatershed(NumpyArray<2, Singleband<PixelType> > image,
                               int dyn_thresh,
                               int neighborhood_graph,
                               NumpyArray<2, Singleband<LabelType> > res)
{
    res.reshapeIfEmpty(image.taggedShape(),
            "morphoSelectiveWatershed(): Output array has wrong shape.");
    PyAllowThreads _pythread;

    Diff2D image_size = Diff2D(image.width(), image.height());

    switch(neighborhood_graph) {
        case 8:
        {
            morpho::neighborhood2D nb(morpho::WITHOUTCENTER8, image_size);
            morpho::morphoSelectiveWatershed(image, res, dyn_thresh, nb);
            break;
        }
        case 4:
        {
            morpho::neighborhood2D nb(morpho::WITHOUTCENTER4, image_size);
            morpho::morphoSelectiveWatershed(image, res, dyn_thresh, nb);
            break;
        }
        default:
        {
            vigra_precondition(false,
                         "pythonMorphoSelectiveWatershed(): Unknown neighborhood graph.\n"
                         "This function is only defined for 4- and 8-neighborhoods.");
            break;
        }
    };

    return res;
}

template <class PixelType>
NumpyAnyArray
pythonMorphoDynMinima(NumpyArray<2, Singleband<PixelType> > image,
                      int neighborhood_graph,
                      NumpyArray<2, Singleband<PixelType> > res)
{
    res.reshapeIfEmpty(image.taggedShape(),
                       "pythonMorphoDynMinima(): Output array has wrong shape.");
    PyAllowThreads _pythread;

    Diff2D image_size = Diff2D(image.width(), image.height());
//
//    def("morphoDynMinima", registerConverters(&pythonMorphoDynMinima<UInt8>),
//        (arg("image"), arg("neighborhood_graph")=8, arg("res")=object()),
//        "takes an image and (optionally) a constant (4 or 8) indicating the neighborhood graph"
//        "(default is 8 neighborhood) as inputs. "
//        "The function calculates the morphological dynamics for each minimum"
//        "and returns an image which is only non-zero on the local minima and ");
//    def("morphoDynMinima", registerConverters(&pythonMorphoDynMinima<UInt16>),
//        (arg("image"), arg("neighborhood_graph")=8, arg("res")=object()),
//        "takes an image and (optionally) a constant (4 or 8) indicating the neighborhood graph"
//        "(default is 8 neighborhood) as inputs. "
//        "The function calculates the morphological dynamics for each minimum"
//        "and returns an image which is only non-zero on the local minima and ");

    //std::vector<PixelType> dynamics;

    switch(neighborhood_graph) {
        case 8:
        {
            morpho::neighborhood2D nb(morpho::WITHOUTCENTER8, image_size);
            morpho::morphoDynMinima(image, res, nb);
            //vigra::NumpyArray<2, vigra::Singleband<unsigned short>, vigra::StridedArrayTag>::value_type
            break;
        }
        case 4:
        {
            morpho::neighborhood2D nb(morpho::WITHOUTCENTER4, image_size);
            //morpho::morphoDynMinima(srcImageRange(image), destImageRange(res), nb);
            morpho::morphoDynMinima(image, res, nb);
            break;
        }
        default:
        {
            vigra_precondition(false,
                         "pythonMorphoDynMinima(): Unknown neighborhood graph.\n"
                         "This function is only defined for 4- and 8-neighborhoods.");
            break;
        }
    };

    return res;
}

template <class PixelType, class LabelType>
NumpyAnyArray
pythonMorphoBifurcationPoints(NumpyArray<2, Singleband<PixelType> > image,
                              int neighborhood_graph,
                              NumpyArray<2, Singleband<LabelType> > res)
{
    res.reshapeIfEmpty(image.taggedShape(),
                       "morphoBifurcationPoints(): Output array has wrong shape.");
    PyAllowThreads _pythread;

    Diff2D image_size = Diff2D(image.width(), image.height());

    switch(neighborhood_graph) {
        case 8:
        {
            morpho::neighborhood2D nb(morpho::WITHOUTCENTER8, image_size);
            morpho::morphoBifurcationPoints(image, res, nb);
            break;
        }
        case 4:
        {
            morpho::neighborhood2D nb(morpho::WITHOUTCENTER4, image_size);
            morpho::morphoBifurcationPoints(image, res, nb);
            break;
        }
        default:
        {
            vigra_precondition(false,
                         "pythonMorphoBifurcationPoints(): Unknown neighborhood graph.\n"
                         "This function is only defined for 4- and 8-neighborhoods.");
            break;
        }
    };

    return res;
}

template <class PixelType, class MarkerType, class LabelType>
NumpyAnyArray
pythonMorphoConstrainedWatershed(NumpyArray<2, Singleband<PixelType> > image,
                                 NumpyArray<2, Singleband<MarkerType> > marker_image,
                                 int neighborhood_graph,
                                 NumpyArray<2, Singleband<LabelType> > res)
{
    res.reshapeIfEmpty(image.taggedShape(),
            "morphoConstrainedWatershed(): Output array has wrong shape.");

    PyAllowThreads _pythread;

    Diff2D image_size = Diff2D(image.width(), image.height());

    switch(neighborhood_graph) {
        case 8:
        {
            morpho::neighborhood2D nb(morpho::WITHOUTCENTER8, image_size);
            morpho::morphoConstrainedWatershed(image, marker_image, res, nb);
            break;
        }
        case 4:
        {
            morpho::neighborhood2D nb(morpho::WITHOUTCENTER4, image_size);
            morpho::morphoConstrainedWatershed(image, marker_image, res, nb);
            break;
        }
        default:
        {
            vigra_precondition(false,
                         "pythonMorphoConstrainedWatershed(): Unknown neighborhood graph.\n"
                         "This function is only defined for 4- and 8-neighborhoods.");
            break;
        }
    };

    return res;
}

template <class PixelType>
NumpyAnyArray
pythonMorphoDiameterClosing(NumpyArray<2, Singleband<PixelType> > image,
                            int diameter,
                            int neighborhood_graph,
                            NumpyArray<2, Singleband<PixelType> > res
                            )
{
    res.reshapeIfEmpty(image.taggedShape(),
            "morphoDiameterClosing(): Output array has wrong shape.");
    PyAllowThreads _pythread;

    Diff2D image_size = Diff2D(image.width(), image.height());

    switch(neighborhood_graph) {
        case 8:
        {
            morpho::neighborhood2D nb(morpho::WITHOUTCENTER8, image_size);
            morpho::morphoDiameterClosing(image, res, diameter, nb);
            break;
        }
        case 4:
        {
            morpho::neighborhood2D nb(morpho::WITHOUTCENTER4, image_size);
            morpho::morphoDiameterClosing(image, res, diameter, nb);
            break;
        }
        default:
        {
            vigra_precondition(false,
                         "pythonMorphoDiameterClosing(): Unknown neighborhood graph.\n"
                         "This function is only defined for 4- and 8-neighborhoods.");
            break;
        }
    };

    return res;
}

template <class PixelType>
NumpyAnyArray
pythonMorphoAreaOpening(NumpyArray<2, Singleband<PixelType> > image,
                                    int area,
                                    int neighborhood_graph,
                                    NumpyArray<2, Singleband<PixelType> > res
                                   )
{
    res.reshapeIfEmpty(image.taggedShape(),
            "morphoAreaOpening(): Output array has wrong shape.");
    PyAllowThreads _pythread;

    Diff2D image_size = Diff2D(image.width(), image.height());

    switch(neighborhood_graph) {
        case 8:
        {
            morpho::neighborhood2D nb(morpho::WITHOUTCENTER8, image_size);
            morpho::morphoAreaOpening(image, res, area, nb);
            break;
        }
        case 4:
        {
            morpho::neighborhood2D nb(morpho::WITHOUTCENTER4, image_size);
            morpho::morphoAreaOpening(image, res, area, nb);
            break;
        }
        default:
        {
            vigra_precondition(false,
                         "pythonMorphoAreaOpening(): Unknown neighborhood graph.\n"
                         "This function is only defined for 4- and 8-neighborhoods.");
            //std::cout << "This neighborhood is not defined." << std::endl;
            break;
        }
    };

    return res;
}

template <class PixelType>
NumpyAnyArray
pythonMorphoAreaClosing(NumpyArray<2, Singleband<PixelType> > image,
                                    int area,
                                    int neighborhood_graph,
                                    NumpyArray<2, Singleband<PixelType> > res
                                   )
{
    res.reshapeIfEmpty(image.taggedShape(),
            "morphoAreaClosing(): Output array has wrong shape.");
    PyAllowThreads _pythread;

    Diff2D image_size = Diff2D(image.width(), image.height());

    switch(neighborhood_graph) {
        case 8:
        {
            morpho::neighborhood2D nb(morpho::WITHOUTCENTER8, image_size);
            morpho::morphoAreaClosing(image, res, area, nb);
            break;
        }
        case 4:
        {
            morpho::neighborhood2D nb(morpho::WITHOUTCENTER4, image_size);
            morpho::morphoAreaClosing(image, res, area, nb);
            break;
        }
        default:
        {
            vigra_precondition(false,
                         "pythonMorphoAreaClosing(): Unknown neighborhood graph.\n"
                         "This function is only defined for 4- and 8-neighborhoods.");
            //std::cout << "This neighborhood is not defined." << std::endl;
            break;
        }
    };

    return res;
}

template <class PixelType>
NumpyAnyArray
pythonMorphoClosingByReconstruction(NumpyArray<2, Singleband<PixelType> > image,
                                    structuringElement2D_Conv se,
                                    int neighborhood_graph,
                                    NumpyArray<2, Singleband<PixelType> > res
                                   )
{
    res.reshapeIfEmpty(image.taggedShape(),
            "morphoClosingByReconstruction(): Output array has wrong shape.");
    PyAllowThreads _pythread;

    Diff2D image_size = Diff2D(image.width(), image.height());

    switch(neighborhood_graph) {
        case 8:
        {
            morpho::neighborhood2D nb(morpho::WITHOUTCENTER8, image_size);
            morpho::morphoClosingByReconstruction(image, res, se, nb);
            break;
        }
        case 4:
        {
            morpho::neighborhood2D nb(morpho::WITHOUTCENTER4, image_size);
            morpho::morphoClosingByReconstruction(image, res, se, nb);
            break;
        }
        default:
        {
            vigra_precondition(false,
                         "pythonMorphoClosingByReconstruction(): Unknown neighborhood graph.\n"
                         "This function is only defined for 4- and 8-neighborhoods.");
            break;
        }
    };

    return res;
}

template <class PixelType>
NumpyAnyArray
pythonMorphoOpeningByDynamics(NumpyArray<2, Singleband<PixelType> > image,
                              int h,
                              int neighborhood_graph,
                              NumpyArray<2, Singleband<PixelType> > res
)
{
    res.reshapeIfEmpty(image.taggedShape(),
            "morphoClosingByReconstruction(): Output array has wrong shape.");
    PyAllowThreads _pythread;

    Diff2D image_size = Diff2D(image.width(), image.height());

    switch(neighborhood_graph) {
        case 8:
        {
            morpho::neighborhood2D nb(morpho::WITHOUTCENTER8, image_size);
            morpho::morphoOpeningByDynamics(image, res, h, nb);
            break;
        }
        case 4:
        {
            morpho::neighborhood2D nb(morpho::WITHOUTCENTER4, image_size);
            morpho::morphoOpeningByDynamics(image, res, h, nb);
            break;
        }
        default:
        {
            vigra_precondition(false,
                         "pythonMorphoOpeningByDynamics(): Unknown neighborhood graph.\n"
                         "This function is only defined for 4- and 8-neighborhoods.");
            break;
        }
    };

    return res;
}

template <class PixelType>
NumpyAnyArray
pythonMorphoClosingByDynamics(NumpyArray<2, Singleband<PixelType> > image,
                              int h,
                              int neighborhood_graph,
                              NumpyArray<2, Singleband<PixelType> > res)
{
    res.reshapeIfEmpty(image.taggedShape(),
            "morphoClosingByReconstruction(): Output array has wrong shape.");
    PyAllowThreads _pythread;

    Diff2D image_size = Diff2D(image.width(), image.height());

    switch(neighborhood_graph) {
        case 8:
        {
            morpho::neighborhood2D nb(morpho::WITHOUTCENTER8, image_size);
            morpho::morphoClosingByDynamics(image, res, h, nb);
            break;
        }
        case 4:
        {
            morpho::neighborhood2D nb(morpho::WITHOUTCENTER4, image_size);
            morpho::morphoClosingByDynamics(image, res, h, nb);
            break;
        }
        default:
        {
            vigra_precondition(false,
                         "pythonMorphoClosingByDynamics(): Unknown neighborhood graph.\n"
                         "This function is only defined for 4- and 8-neighborhoods.");
            break;
        }
    };

    return res;
}

template <class PixelType>
NumpyAnyArray
pythonMorphoDilation(NumpyArray<2, Singleband<PixelType> > image,
                    structuringElement2D_Conv se,
                    NumpyArray<2, Singleband<PixelType> > res // = python::object(),
                    )
{

    res.reshapeIfEmpty(image.taggedShape(),
            "morphoErosion(): Output array has wrong shape.");
    PyAllowThreads _pythread;

    morpho::morphoDilation(srcImageRange(image), destImageRange(res), se);
    return res;
}

template <class PixelType>
NumpyAnyArray
pythonMorphoOpening(NumpyArray<2, Singleband<PixelType> > image,
                    structuringElement2D_Conv se,
                    NumpyArray<2, Singleband<PixelType> > res // = python::object(),
                    )
{

    res.reshapeIfEmpty(image.taggedShape(),
            "morphoErosion(): Output array has wrong shape.");
    PyAllowThreads _pythread;

    morpho::morphoOpening(srcImageRange(image), destImageRange(res), se);
    return res;
}

template <class PixelType>
NumpyAnyArray
pythonMorphoClosing(NumpyArray<2, Singleband<PixelType> > image,
                    structuringElement2D_Conv se,
                    NumpyArray<2, Singleband<PixelType> > res // = python::object(),
                    )
{

    res.reshapeIfEmpty(image.taggedShape(),
            "morphoErosion(): Output array has wrong shape.");
    PyAllowThreads _pythread;

    morpho::morphoClosing(srcImageRange(image), destImageRange(res), se);
    return res;
}

template <class PixelType>
NumpyAnyArray
pythonMorphoInternalGradient(NumpyArray<2, Singleband<PixelType> > image,
                             structuringElement2D_Conv se,
                             NumpyArray<2, Singleband<PixelType> > res // = python::object(),
)
{

    res.reshapeIfEmpty(image.taggedShape(),
            "morphoErosion(): Output array has wrong shape.");
    PyAllowThreads _pythread;

    morpho::morphoInternalGradient(srcImageRange(image), destImageRange(res), se);
    return res;
}

template <class PixelType>
NumpyAnyArray
pythonMorphoExternalGradient(NumpyArray<2, Singleband<PixelType> > image,
                             structuringElement2D_Conv se,
                    NumpyArray<2, Singleband<PixelType> > res // = python::object(),
                    )
{

    res.reshapeIfEmpty(image.taggedShape(),
            "morphoErosion(): Output array has wrong shape.");
    PyAllowThreads _pythread;

    morpho::morphoExternalGradient(srcImageRange(image), destImageRange(res), se);
    return res;
}

template <class PixelType>
NumpyAnyArray
pythonMorphoGradient(NumpyArray<2, Singleband<PixelType> > image,
                     structuringElement2D_Conv se,
                     NumpyArray<2, Singleband<PixelType> > res
                    )
{

    res.reshapeIfEmpty(image.taggedShape(),
            "morphoErosion(): Output array has wrong shape.");
    PyAllowThreads _pythread;

    morpho::morphoGradient(srcImageRange(image), destImageRange(res), se);
    return res;
}

template <class PixelType>
NumpyAnyArray
pythonMorphoErosion(NumpyArray<2, Singleband<PixelType> > image,
                    structuringElement2D_Conv se,
                    NumpyArray<2, Singleband<PixelType> > res // = python::object(),
                    )
{
    res.reshapeIfEmpty(image.taggedShape(),
            "morphoErosion(): Output array has wrong shape.");
    PyAllowThreads _pythread;

    morpho::morphoErosion(srcImageRange(image), destImageRange(res), se);
    return res;
}

//struct Diff2Dlist_from_python_pointlist
//{
//
//  // Determine if obj_ptr can be converted in a QString
//  static void* convertible(PyObject* obj_ptr)
//    {
//      //if (!PyString_Check(obj_ptr)) return 0;
//      //return obj_ptr;
//    }
//
//  // Convert obj_ptr into a list of points
//   static void construct(
//     PyObject* obj_ptr,
//     boost::python::converter::rvalue_from_python_stage1_data* data)
//     {
//       // Extract the character data from the python string
//       const char* value = PyString_AsString(obj_ptr);
//
//       // Verify that obj_ptr is a string (should be ensured by convertible())
//       assert(value);
//
//       // Grab pointer to memory into which to construct the new QString
//       void* storage = (
//         (boost::python::converter::rvalue_from_python_storage<QString>*)
//         data)->storage.bytes;
//
//       // in-place construct the new QString using the character data
//       // extraced from the python object
//       new (storage) QString(value);
//
//       // Stash the memory chunk pointer for later use by boost.python
//       data->convertible = storage;
//     }
//
//};

//class VecTest {
//
//    protected:
//        std::vector<int> myvec;
//
//    public:
//        //VecTest(std::vector<int> a): myvec(a) {}
//        VecTest(boost::python::list a)
//        {
//
//            boost::python::ssize_t len = boost::python::len(a);
//            for(int i=0; i<len;i++){
//                myvec.push_back((int)boost::python::extract<int>(a[i]));
//            }
//        }
//
//        void output() {
//            for(std::vector<int>::iterator iter = myvec.begin();
//              iter != myvec.end();
//              ++iter)
//              {
//                std::cout << (*iter) << std::endl;
//              }
//            std::cout << std::endl;
//        };
//};
//
//class PointList {
//
//    protected:
//        std::vector<int> x;
//        std::vector<int> y;
//
//    public:
//        //VecTest(std::vector<int> a): myvec(a) {}
//        PointList(boost::python::list a)
//        {
//
//            boost::python::ssize_t len = boost::python::len(a);
//            std::cout << "length of list: " << len << std::endl;
//            for(int i=0; i<len;i++){
//                x.push_back((int)boost::python::extract<int>(a[i][0]));
//            }
//        }
//
//        void output() {
//            for(std::vector<int>::iterator iter = x.begin();
//              iter != x.end();
//              ++iter)
//              {
//                std::cout << (*iter) << std::endl;
//              }
//            std::cout << std::endl;
//        };
//};
//

using namespace boost::python;

// the argument of the init macro must be the module name
BOOST_PYTHON_MODULE_INIT(morpho)
{
    // initialize numpy and vigranumpy
    vigra::import_vigranumpy();

    // Structuring elements
    //class_<morpho::structuringElement2D>("structuringElement2D", init<std::vector<vigra::Diff2D> >())
    //    .def("output", &morpho::structuringElement2D::output);
//    class_<std::vector<int> >("VectorOfInt")
//            .def(vector_indexing_suite<std::vector<int> >() )
//        ;

    //typedef std::vector<int> VectorOfInt;

//    class_<VecTest>("VecTest", init<boost::python::list>())
//        .def("output", &VecTest::output)
//        ;
//
//    class_<PointList>("PointList", init<boost::python::list>())
//        .def("output", &PointList::output)
//        ;

//    class_<TCurrency>( "TCurrency" )
//        .def( init<long> )
//        .def( init<const std::string&> )
//        ...
//        ;
//
    class_<structuringElement2D_Conv>("structuringElement2D",
                                      "\nA Structuring Element in two dimensions for morphological operations. "
                                      "\nThe constructor takes a a list of points (python list of lists)."
                                      "\nThe structuring element is needed for morphological operations such as erosion, dilation"
                                      "opening, closing. "
                                      "\n\nExample: simple 4 neighborhood structuring element:"
                                      "\n>>> se = vigra.morpho.structuringElement2D([[0,0], [1,0], [0,1], [-1, 0], [0, -1]])"
                                      "\n\nExample: simple 8 neighborhood structuring element:"
                                      "\n>>> se = vigra.morpho.structuringElement2D([[0,0], [1,0], [0,1], [-1, 0], [0, -1], [1, 1], [1, -1], [-1, -1], [-1, 1]])"
                                      )
        .def(init<boost::python::list>())//("structuringElement2D", init<boost::python::list>())
        .def(init<boost::python::list, int>())
        .def("output", &structuringElement2D_Conv::output)
        .def("numberOfPixels", &structuringElement2D_Conv::numberOfPixels)
        .def_readwrite("size", &structuringElement2D_Conv::size)
        .def("transpose", &structuringElement2D_Conv::transpose)
        ;

    // export morphological functions
//    def("morphoDilation", registerConverters(&pythonMorphoDilation<UInt8>),
//        (arg("image"), arg("size"), arg("res")=object()),
//        "Morphological Dilation of the image with a structuring element."
//        "(for other images, you can use img_mod = img.astype(np.dtype('uint16')) for conversion)"
//        "At the moment we only accept size as a parameter, and the structuring"
//        "element is a square with radius of this size "
//        "(i.e. with each side of length 2*size + 1)");

    def("morphoDilation", registerConverters(&pythonMorphoDilation<UInt8>),
        (arg("image"), arg("se"), arg("res")=object()),
        "Morphological Dilation of the image with a structuring element."
        "(for other images, you can use img_mod = img.astype(np.dtype('uint16')) for conversion)"
        "The structuring element is of type vigra.morpho.structuringElement2D");
    def("morphoDilation", registerConverters(&pythonMorphoDilation<UInt16>),
        (arg("image"), arg("se"), arg("res")=object()),
        "Morphological Dilation of the image with a structuring element."
        "The structuring element is of type vigra.morpho.structuringElement2D");

    def("morphoErosion", registerConverters(&pythonMorphoErosion<UInt8>),
        (arg("image"), arg("se"), arg("res")=object()),
        "Morphological Erosion of the image with a structuring element."
        "The structuring element is of type vigra.morpho.structuringElement2D");
    def("morphoErosion", registerConverters(&pythonMorphoErosion<UInt16>),
        (arg("image"), arg("se"), arg("res")=object()),
        "Morphological Erosion of the image with a structuring element."
        "The structuring element is of type vigra.morpho.structuringElement2D");

    def("morphoOpening", registerConverters(&pythonMorphoOpening<UInt8>),
        (arg("image"), arg("se"), arg("res")=object()),
        "Morphological Opening of the image with a structuring element."
        "The structuring element is of type vigra.morpho.structuringElement2D");
    def("morphoOpening", registerConverters(&pythonMorphoOpening<UInt16>),
        (arg("image"), arg("se"), arg("res")=object()),
        "Morphological Opening of the image with a structuring element."
        "The structuring element is of type vigra.morpho.structuringElement2D");

    def("morphoClosing", registerConverters(&pythonMorphoClosing<UInt8>),
        (arg("image"), arg("se"), arg("res")=object()),
        "Morphological Closing of the image with a structuring element."
        "The structuring element is of type vigra.morpho.structuringElement2D");
    def("morphoClosing", registerConverters(&pythonMorphoClosing<UInt16>),
        (arg("image"), arg("se"), arg("res")=object()),
        "Morphological Closing of the image with a structuring element."
        "The structuring element is of type vigra.morpho.structuringElement2D");

    def("morphoInternalGradient", registerConverters(&pythonMorphoInternalGradient<UInt8>),
        (arg("image"), arg("se"), arg("res")=object()),
        "Morphological Internal gradient (f - ero(f)) of the image with a structuring element."
        "The structuring element is of type vigra.morpho.structuringElement2D");
    def("morphoInternalGradient", registerConverters(&pythonMorphoInternalGradient<UInt16>),
        (arg("image"), arg("se"), arg("res")=object()),
        "Morphological Internal gradient (f - ero(f)) of the image with a structuring element."
        "The structuring element is of type vigra.morpho.structuringElement2D");

    def("morphoExternalGradient", registerConverters(&pythonMorphoExternalGradient<UInt8>),
        (arg("image"), arg("se"), arg("res")=object()),
        "Morphological external gradient (dil(f) - f) of the image with a structuring element."
        "The structuring element is of type vigra.morpho.structuringElement2D");
    def("morphoExternalGradient", registerConverters(&pythonMorphoExternalGradient<UInt16>),
        (arg("image"), arg("se"), arg("res")=object()),
        "Morphological external gradient (dil(f) - f) of the image with a structuring element."
        "The structuring element is of type vigra.morpho.structuringElement2D");

    def("morphoOpeningByReconstruction", registerConverters(&pythonMorphoOpeningByReconstruction<UInt8>),
        (arg("image"), arg("se"), arg("neighborhood_graph")=8, arg("res")=object()),
        "Morphological opening, followed by a reconstruction of the opened image under the original image."
        "This function is only implemented for Uint8 and Uint16 images"
        "(for other images, you can use img_mod = img.astype(np.dtype('uint16')) for conversion)"
        "The structuring element is of type vigra.morpho.structuringElement2D");
    def("morphoOpeningByReconstruction", registerConverters(&pythonMorphoOpeningByReconstruction<UInt16>),
        (arg("image"), arg("se"), arg("neighborhood_graph")=8, arg("res")=object()),
        "Morphological opening, followed by a reconstruction of the opened image under the original image."
        "This function is only implemented for Uint8 and Uint16 images"
        "(for other images, you can use img_mod = img.astype(np.dtype('uint16')) for conversion)"
        "The structuring element is of type vigra.morpho.structuringElement2D");

    def("morphoClosingByReconstruction", registerConverters(&pythonMorphoClosingByReconstruction<UInt8>),
        (arg("image"), arg("se"), arg("neighborhood_graph")=8, arg("res")=object()),
        "Morphological closing, followed by a reconstruction of the closed image over the original image."
        "This function is only implemented for Uint8 and Uint16 images"
        "(for other images, you can use img_mod = img.astype(np.dtype('uint16')) for conversion)"
        "The structuring element is of type vigra.morpho.structuringElement2D");
    def("morphoClosingByReconstruction", registerConverters(&pythonMorphoClosingByReconstruction<UInt16>),
        (arg("image"), arg("se"), arg("neighborhood_graph")=8, arg("res")=object()),
        "Morphological closing, followed by a reconstruction of the closed image over the original image."
        "This function is only implemented for Uint8 and Uint16 images"
        "(for other images, you can use img_mod = img.astype(np.dtype('uint16')) for conversion)"
        "The structuring element is of type vigra.morpho.structuringElement2D");

    def("morphoClosingByDynamics", registerConverters(&pythonMorphoClosingByDynamics<UInt8>),
        (arg("image"), arg("h"), arg("neighborhood_graph")=8, arg("res")=object()),
        "Let f be an image and h a positive constant. This function calculates then the "
        "morphological reconstruction of f + h over f."
        "This function is only implemented for Uint8 and Uint16 images"
        "(for other images, you can use img_mod = img.astype(np.dtype('uint16')) for conversion)");
    def("morphoClosingByDynamics", registerConverters(&pythonMorphoClosingByDynamics<UInt16>),
        (arg("image"), arg("h"), arg("neighborhood_graph")=8, arg("res")=object()),
        "Let f be an image and h a positive constant. This function calculates then the "
        "morphological reconstruction of f + h over f."
        "This function is only implemented for Uint8 and Uint16 images"
        "(for other images, you can use img_mod = img.astype(np.dtype('uint16')) for conversion)");

    def("morphoOpeningByDynamics", registerConverters(&pythonMorphoOpeningByDynamics<UInt8>),
        (arg("image"), arg("h"), arg("neighborhood_graph")=8, arg("res")=object()),
        "Let f be an image and h a positive constant. This function calculates then the "
        "morphological reconstruction of f - h under f."
        "This function is only implemented for Uint8 and Uint16 images"
        "(for other images, you can use img_mod = img.astype(np.dtype('uint16')) for conversion)");
    def("morphoOpeningByDynamics", registerConverters(&pythonMorphoOpeningByDynamics<UInt16>),
        (arg("image"), arg("h"), arg("neighborhood_graph")=8, arg("res")=object()),
        "Let f be an image and h a positive constant. This function calculates then the "
        "morphological reconstruction of f - h under f."
        "This function is only implemented for Uint8 and Uint16 images"
        "(for other images, you can use img_mod = img.astype(np.dtype('uint16')) for conversion)");

    def("morphoReconstructionByDilation", registerConverters(&pythonMorphoReconstructionByDilation<UInt8>),
        (arg("marker"), arg("mask"), arg("neighborhood_graph")=8),
        "Takes two images and calculates the morphological reconstruction of the"
        "first under the second. Typically the first image is a marker image and the second is the original"
        "image to be analyzed.");
    def("morphoReconstructionByDilation", registerConverters(&pythonMorphoReconstructionByDilation<UInt16>),
        (arg("marker"), arg("mask"), arg("neighborhood_graph")=8),
        "Takes two images and calculates the morphological reconstruction of the"
        "first under the second. Typically the first image is a marker image and the second is the original"
        "image to be analyzed.");

    def("morphoReconstructionByErosion", registerConverters(&pythonMorphoReconstructionByErosion<UInt8>),
        (arg("marker"), arg("mask"), arg("neighborhood_graph")=8),
        "Takes two images and calculates the morphological reconstruction of the"
        "first over the second. For this, the first image has to be greater than the second (pointwise)."
        "Typically the first image is a marker image and the second is the original"
        "image to be analyzed.");
    def("morphoReconstructionByErosion", registerConverters(&pythonMorphoReconstructionByErosion<UInt16>),
        (arg("marker"), arg("mask"), arg("neighborhood_graph")=8),
        "Takes two images and calculates the morphological reconstruction of the"
        "first over the second. For this, the first image has to be greater than the second (pointwise)."
        "Typically the first image is a marker image and the second is the original"
        "image to be analyzed.");

    def("morphoAreaOpening", registerConverters(&pythonMorphoAreaOpening<UInt8>),
        (arg("image"), arg("area"), arg("neighborhood_graph")=8, arg("res")=object()),
        "takes an image, an area parameter and (optionally) a constant (4 or 8) indicating the neighborhood graph"
        "(default is a 8 neighborhood) as inputs. "
        "The function calculates the area opening of the input image, i.e."
        "the largest output image smaller than the input image for which any "
        "connected component resulting from a threshold operation would have at least <area> pixels.");
    def("morphoAreaOpening", registerConverters(&pythonMorphoAreaOpening<UInt16>),
        (arg("image"), arg("area"), arg("neighborhood_graph")=8, arg("res")=object()),
        "takes an image, an area parameter and (optionally) a constant (4 or 8) indicating the neighborhood graph"
        "(default is a 8 neighborhood) as inputs. "
        "The function calculates the area opening of the input image, i.e."
        "the largest output image smaller than the input image for which any "
        "connected component resulting from a threshold operation would have at least <area> pixels.");

    def("morphoAreaClosing", registerConverters(&pythonMorphoAreaClosing<UInt8>),
        (arg("image"), arg("area"), arg("neighborhood_graph")=8, arg("res")=object()),
        "takes an image, an area parameter and (optionally) a constant (4 or 8) indicating the neighborhood graph"
        "(default is a 8 neighborhood) as inputs. "
        "The function calculates the area closing of the input image, i.e."
        "the smallest output image larger than the input image for which any "
        "connected component resulting from a threshold operation would have at least <area> pixels.");
    def("morphoAreaClosing", registerConverters(&pythonMorphoAreaClosing<UInt16>),
        (arg("image"), arg("area"), arg("neighborhood_graph")=8, arg("res")=object()),
        "takes an image, an area parameter and (optionally) a constant (4 or 8) indicating the neighborhood graph"
        "(default is a 8 neighborhood) as inputs. "
        "The function calculates the area closing of the input image, i.e."
        "the smallest output image larger than the input image for which any "
        "connected component resulting from a threshold operation would have at least <area> pixels.");

    def("morphoWatershed", registerConverters(&pythonMorphoWatershed<UInt8, UInt16>),
        (arg("image"), arg("neighborhood_graph")=8, arg("res")=object()),
        "takes an image and (optionally) a constant (4 or 8) indicating the neighborhood graph"
        "(default is 8 neighborhood) as inputs. "
        "The function calculates a watershed from the local minima.");
    def("morphoWatershed", registerConverters(&pythonMorphoWatershed<UInt16, UInt16>),
        (arg("image"), arg("neighborhood_graph")=8, arg("res")=object()),
        "takes an image and (optionally) a constant (4 or 8) indicating the neighborhood graph"
        "(default is 8 neighborhood) as inputs. "
        "The function calculates a watershed from the local minima.");

    def("morphoSelectiveWatershed", registerConverters(&pythonMorphoSelectiveWatershed<UInt8, UInt16>),
        (arg("image"), arg("dyn_thresh"), arg("neighborhood_graph")=8, arg("res")=object()),
        "takes an image a dynamic threshold and (optionally) a constant (4 or 8) indicating the neighborhood graph"
        "(default is 8 neighborhood) as inputs. "
        "The function calculates a watershed from the local minima.");
    def("morphoSelectiveWatershed", registerConverters(&pythonMorphoSelectiveWatershed<UInt16, UInt16>),
        (arg("image"), arg("dyn_thresh"), arg("neighborhood_graph")=8, arg("res")=object()),
        "takes an image a dynamic threshold and (optionally) a constant (4 or 8) indicating the neighborhood graph"
        "(default is 8 neighborhood) as inputs. "
        "The function calculates a watershed from the local minima.");

    def("morphoDynMinima", registerConverters(&pythonMorphoDynMinima<UInt8>),
        (arg("image"), arg("neighborhood_graph")=8, arg("res")=object()),
        "takes an image and (optionally) a constant (4 or 8) indicating the neighborhood graph"
        "(default is 8 neighborhood) as inputs. "
        "The function calculates the morphological dynamics for each minimum"
        "and returns an image which is only non-zero on the local minima and ");
    def("morphoDynMinima", registerConverters(&pythonMorphoDynMinima<UInt16>),
        (arg("image"), arg("neighborhood_graph")=8, arg("res")=object()),
        "takes an image and (optionally) a constant (4 or 8) indicating the neighborhood graph"
        "(default is 8 neighborhood) as inputs. "
        "The function calculates the morphological dynamics for each minimum"
        "and returns an image which is only non-zero on the local minima and ");

    def("morphoBifurcationPoints", registerConverters(&pythonMorphoBifurcationPoints<UInt8, UInt8>),
        (arg("image"), arg("neighborhood_graph")=4, arg("res")=object()),
        "takes an image and (optionally) a constant (4 or 8) indicating the neighborhood graph"
        "(default is 8 neighborhood) as inputs. "
        "The input image is supposed to contain a skeleton, where all non-zero pixels are taken as"
        "foreground (i.e. belonging to the pixel)."
        "The function finds the bifurcation points (all points with more than 2 neighbors).");
    def("morphoBifurcationPoints", registerConverters(&pythonMorphoBifurcationPoints<UInt16, UInt16>),
        (arg("image"), arg("neighborhood_graph")=4, arg("res")=object()),
        "takes an image and (optionally) a constant (4 or 8) indicating the neighborhood graph"
        "(default is 8 neighborhood) as inputs. "
        "The input image is supposed to contain a skeleton, where all non-zero pixels are taken as"
        "foreground (i.e. belonging to the pixel)."
        "The function finds the bifurcation points (all points with more than 2 neighbors).");
    def("morphoBifurcationPoints", registerConverters(&pythonMorphoBifurcationPoints<float, float>),
        (arg("image"), arg("neighborhood_graph")=4, arg("res")=object()),
        "takes an image and (optionally) a constant (4 or 8) indicating the neighborhood graph"
        "(default is 8 neighborhood) as inputs. "
        "The input image is supposed to contain a skeleton, where all non-zero pixels are taken as"
        "foreground (i.e. belonging to the pixel)."
        "The function finds the bifurcation points (all points with more than 2 neighbors).");

    def("morphoConstrainedWatershed", registerConverters(&pythonMorphoConstrainedWatershed<UInt8, UInt8, UInt16>),
        (arg("image"), arg("neighborhood_graph")=8, arg("res")=object()),
        "takes an image, a marker image and (optionally) a constant (4 or 8) indicating the neighborhood graph"
        "(default is 8 neighborhood) as inputs. "
        "The function calculates calculates a constrained watershed from the markers.");
    def("morphoConstrainedWatershed", registerConverters(&pythonMorphoConstrainedWatershed<UInt16, UInt16, UInt16>),
        (arg("image"), arg("neighborhood_graph")=8, arg("res")=object()),
        "takes an image, a marker image and (optionally) a constant (4 or 8) indicating the neighborhood graph"
        "(default is 8 neighborhood) as inputs. "
        "The function calculates calculates a constrained watershed from the markers.");

    def("morphoDiameterOpening", registerConverters(&pythonMorphoDiameterOpening<UInt8>),
        (arg("image"), arg("diameter"), arg("neighborhood_graph")=8, arg("res")=object()),
        "takes an image, a diameter parameter and (optionally) a constant (4 or 8) indicating the neighborhood graph"
        "(default is a 8 neighborhood) as inputs. "
        "The function calculates the diameter opening of the input image, i.e."
        "the largest output image smaller than the input image for which any "
        "connected component resulting from a threshold operation would have at least a diameter of <diameter>");
    def("morphoDiameterOpening", registerConverters(&pythonMorphoDiameterOpening<UInt16>),
        (arg("image"), arg("diameter"), arg("neighborhood_graph")=8, arg("res")=object()),
        "takes an image, a diameter parameter and (optionally) a constant (4 or 8) indicating the neighborhood graph"
        "(default is a 8 neighborhood) as inputs. "
        "The function calculates the diameter opening of the input image, i.e."
        "the largest output image smaller than the input image for which any "
        "connected component resulting from a threshold operation would have at least a diameter of <diameter>");

    def("morphoDiameterClosing", registerConverters(&pythonMorphoDiameterClosing<UInt8>),
        (arg("image"), arg("diameter"), arg("neighborhood_graph")=8, arg("res")=object()),
        "takes an image, a diameter parameter and (optionally) a constant (4 or 8) indicating the neighborhood graph"
        "(default is a 8 neighborhood) as inputs. "
        "The function calculates the diameter closing of the input image, i.e."
        "the smallest output image larger than the input image for which any "
        "connected component resulting from a threshold operation would have at least a diameter of <diameter>");
    def("morphoDiameterClosing", registerConverters(&pythonMorphoDiameterClosing<UInt16>),
        (arg("image"), arg("diameter"), arg("neighborhood_graph")=8, arg("res")=object()),
        "takes an image, a diameter parameter and (optionally) a constant (4 or 8) indicating the neighborhood graph"
        "(default is a 8 neighborhood) as inputs. "
        "The function calculates the diameter closing of the input image, i.e."
        "the smallest output image larger than the input image for which any "
        "connected component resulting from a threshold operation would have at least a diameter of <diameter>");

}
} // end of namespace vigra


/*
 * This is part of the fl library, a C++ Bayesian filtering library
 * (https://github.com/filtering-library)
 *
 * Copyright (c) 2015 Max Planck Society,
 * 				 Autonomous Motion Department,
 * 			     Institute for Intelligent Systems
 *
 * This Source Code Form is subject to the terms of the MIT License (MIT).
 * A copy of the license can be found in the LICENSE file distributed with this
 * source code.
 */
/**
 * \file not_adaptive.hpp
 * \date Febuary 2015
 * \author Jan Issac (jan.issac@gmail.com)
 */

#pragma once

#include <type_traits>

#include <Eigen/Dense>

#include <fl/util/traits.hpp>
#include <fl/util/meta.hpp>
#include <fl/model/adaptive_model.hpp>
#include <fl/model/transition/interface/transition_function.hpp>
#include <fl/model/sensor/interface/sensor_function.hpp>

namespace fl
{

/**
 * \ingroup meta
 *
 * Not-Adaptive operator
 */
template <typename... Model> class NotAdaptive;

/**
 * \ingroup meta
 *
 * Traits of Non-Adaptive model
 */
template <typename Model>
struct Traits<NotAdaptive<Model>>
    : Traits<Model>
{
    typedef typename Traits<Model>::Scalar Scalar;

    enum : signed int { ParamDim = 0 };
    typedef Eigen::Matrix<Scalar, ParamDim, 1> Param;
    typedef AdaptiveModel<Param> AdaptiveModelBase;
};

/**
 * \ingroup meta
 *
 * \brief The NotAdaptive operator decorates any model such that it represents a
 * non-adaptive model. This operator can be applied to any model inparticular
 * to models which do not implement the AdaptiveModel interface.
 *
 * The way this operates is simply by overriding the AdaptiveModel interface
 * and deactivating it. If the model does not implement the AdaptiveModel
 * interface, then the NotAdaptive injects the interface with a deactivated
 * adaptivity.
 *
 * \cond internal
 * NotAdaptive operator specialization forwarding to the according ModelType
 * (i.e. either internal::SensorType or internal::TransitionType).
 * \endcond
 */
template <typename Model>
class NotAdaptive<Model>
  : public NotAdaptive<
               Model,
               typename Model::ModelType,
               Options<
                   std::is_base_of<internal::AdaptiveModelType, Model>::value
                >
            >
{
public:
    typedef NotAdaptive<
                Model,
                typename Model::ModelType,
                Options<
                    std::is_base_of<internal::AdaptiveModelType, Model>::value
                >
            > Base;

    /**
     * Conversion constructor. This converts the actual model instance into
     * a NotAdaptive instances
     *
     * \param model     Actual model instance
     */
    template <typename ... M>
    NotAdaptive(const M& ... model) : Base(model...) { }
};

/**
 * \internal
 * \ingroup meta
 *
 * Traits of Non-Adaptive observation models
 */
template <typename Model, int Adaptivity>
struct Traits<
           NotAdaptive<Model, internal::SensorType, Options<Adaptivity>>
       >
    : Traits<NotAdaptive<Model>>
{ };

/**
 * \internal
 * \ingroup meta
 *
 * Traits of Non-Adaptive process model
 */
template <typename Model, int Adaptivity>
struct Traits<
           NotAdaptive<Model, internal::TransitionType, Options<Adaptivity>>
        >
    : Traits<NotAdaptive<Model>>
{ };

/**
 * \internal
 * \ingroup meta
 *
 * NotAdaptive operator implementation of the observation model
 */
template <typename Model>
class NotAdaptive<Model, internal::SensorType, Options<IsNotAdaptive>>
  : public Model,
    public Traits<
               NotAdaptive<
                   Model,
                   internal::SensorType,
                   Options<IsNotAdaptive>
               >
           >::AdaptiveModelBase
{
public:
    typedef NotAdaptive<
                Model,
                internal::SensorType,
                Options<IsNotAdaptive>
            > This;

    typedef typename Traits<This>::Obsrv Obsrv;
    typedef typename Traits<This>::State State;
    typedef typename Traits<This>::Noise Noise;
    typedef typename Traits<This>::Param Param;

    /**
     * Conversion constructor. This converts the actual model instance into
     * a NotAdaptive instances
     *
     * \param model     Actual model instance
     */
    template <typename ... M>
    NotAdaptive(const M& ... model) : Model(model...) { }

    virtual Obsrv predict_obsrv(const State& state,
                                const Noise& noise,
                                double delta_time)
    {
        assert(state.rows() == state_dimension());

        return Model::predict_obsrv(state.topRows(Model::state_dimension()),
                                    noise, delta_time);
    }

    virtual int state_dimension() const
    {
        return Model::state_dimension() + param_dimension();
    }

    virtual void param(Param) {  }
    virtual const Param& param() const { return param_; }
    virtual int param_dimension() const { return 0; }

protected:
    Param param_;
};

/**
 * \internal
 * \ingroup meta
 *
 * NotAdaptive operator implementation of the observation model
 */
template <typename Model>
class NotAdaptive<Model, internal::SensorType, Options<IsAdaptive>>
  : public Model
{
public:
    typedef NotAdaptive<
                Model,
                internal::SensorType,
                Options<IsAdaptive>
            > This;

    typedef typename Traits<This>::Obsrv Obsrv;
    typedef typename Traits<This>::State State;
    typedef typename Traits<This>::Noise Noise;
    typedef typename Traits<This>::Param Param;

    /**
     * Conversion constructor. This converts the actual model instance into
     * a NotAdaptive instances
     *
     * \param model     Actual model instance
     */
    template <typename ... M>
    NotAdaptive(const M& ... model) : Model(model...) { }

    virtual Obsrv predict_obsrv(const State& state,
                                const Noise& noise,
                                double delta_time)
    {
        assert(state.rows() == state_dimension());

        return Model::predict_obsrv(state.topRows(Model::state_dimension()),
                                    noise, delta_time);
    }

    virtual int state_dimension() const
    {
        return Model::state_dimension() + param_dimension();
    }

    virtual void param(Param) {  }
//    virtual const Param& param() const { return param_; }
    virtual int param_dimension() const { return 0; }

protected:
    Param param_;
};

/**
 * \internal
 * \ingroup meta
 *
 * NotAdaptive operator implementation of the process model
 */
template <typename Model>
class NotAdaptive<Model, internal::TransitionType, Options<IsNotAdaptive>>
  : public Model,
    public Traits<
               NotAdaptive<
                   Model,
                   internal::TransitionType,
                   Options<IsNotAdaptive>>
           >::AdaptiveModelBase
{
public:
    typedef NotAdaptive<
                Model,
                internal::TransitionType,
                Options<IsNotAdaptive>
            > This;

    typedef typename Traits<This>::Input Input;
    typedef typename Traits<This>::State State;
    typedef typename Traits<This>::Noise Noise;
    typedef typename Traits<This>::Param Param;

    /**
     * Conversion constructor. This converts the actual model instance into
     * a NotAdaptive instances
     *
     * \param model     Actual model instance
     */
    template <typename ... M>
    NotAdaptive(const M& ... model) : Model(model...) { }

    virtual State predict_state(double delta_time,
                                const State& state,
                                const Noise& noise,
                                const Input& input)
    {
        assert(state.rows() == state_dimension());

        return Model::predict_state(
                    delta_time,
                    state.topRows(Model::state_dimension()),
                    noise,
                    input);
    }

    virtual int state_dimension() const
    {
        return Model::state_dimension() + param_dimension();
    }

    virtual void param(Param) {  }
    virtual const Param& param() const { return param_; }
    virtual int param_dimension() const { return 0; }

protected:
    Param param_;
};

/**
 * \internal
 * \ingroup meta
 *
 * NotAdaptive operator implementation of the process model
 */
template <typename Model>
class NotAdaptive<Model, internal::TransitionType, Options<IsAdaptive>>
  : public Model
{
public:
    typedef NotAdaptive<
                Model,
                internal::TransitionType,
                Options<IsAdaptive>
            > This;

    typedef typename Traits<This>::Input Input;
    typedef typename Traits<This>::State State;
    typedef typename Traits<This>::Noise Noise;
    typedef typename Traits<This>::Param Param;

    /**
     * Conversion constructor. This converts the actual model instance into
     * a NotAdaptive instances
     *
     * \param model     Actual model instance
     */
    template <typename ... M>
    NotAdaptive(const M& ... model) : Model(model...) { }

    virtual State predict_state(double delta_time,
                                const State& state,
                                const Noise& noise,
                                const Input& input)
    {
        assert(state.rows() == state_dimension());

        return Model::predict_state(
                    delta_time,
                    state.topRows(Model::state_dimension()),
                    noise,
                    input);
    }

    virtual int state_dimension() const
    {
        return Model::state_dimension() + param_dimension();
    }

    virtual void param(Param) {  }
//    virtual const Param& param() const { return param_; }
    virtual int param_dimension() const { return 0; }

protected:
    Param param_;
};

}


//---------------------------------------------------------------------------
//
//    FCST: Fuel Cell Simulation Toolbox
//
//    Copyright (C) 2009-13 by Energy Systems Design Laboratory, University of Alberta
//
//    This software is distributed under the MIT License.
//    For more information, see the README file in /doc/LICENSE
//
//    - Class: picard.cc
//    - Description: Picard solver
//    - Developers: Mayank Sabharwal
//
//---------------------------------------------------------------------------

#include <solvers/picard.h>
#include <deal.II/base/data_out_base.h>
#include <deal.II/lac/block_vector.h>

#include <iomanip>
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <cmath>

using namespace FuelCell::ApplicationCore;

//---------------------------------------------------------------------------
Picard::Picard(ApplicationBase& app)
    : PicardBase(app),
    underrelaxation(false)
{
  FcstUtilities::log << "->Picard";
}

//---------------------------------------------------------------------------
void
Picard::declare_parameters(ParameterHandler& param)
{
    PicardBase::declare_parameters(param);
    param.enter_subsection("Picard");
    {
        param.declare_entry("Under-relaxation",
                            "false",
                            Patterns::Bool(),
                            "Use adaptive under-relaxation");
        param.declare_entry("Alpha",
                            "4",
                            Patterns::Double(),
                            "Alpha value for underrelaxation;Range of 2-6 works best");
        param.declare_entry("Gamma min",
                            "0.6",
                            Patterns::Double(),
                            "Gamma min value for underrelaxation;Range of 0.1-0.6 works best");
    }
    param.leave_subsection();
}

//---------------------------------------------------------------------------
void
Picard::initialize (ParameterHandler& param)
{
    PicardBase::initialize(param);
    param.enter_subsection("Picard");
    {
        underrelaxation = param.get_bool("Under-relaxation");
        alpha = param.get_double("Alpha");
        gamma_min = param.get_double("Gamma min");
    }
    param.leave_subsection();
}

//---------------------------------------------------------------------------
void
Picard::solve (FuelCell::ApplicationCore::FEVector& u, const FuelCell::ApplicationCore::FEVectors& in_vectors)
{
    this->step = 0;

    if (debug>2)
        FcstUtilities::log << "u: " << u.l2_norm() << std::endl;

    FEVector u_n;
    FEVector res;

    res.reinit(u);
    u_n.reinit(u);
    FEVectors src1;
    FEVectors src2;
    src1.add_vector(u, "Solution");
    src1.merge(in_vectors);
    src2.add_vector(res, "residual");
    src2.merge(src1);

    double residual = app->residual(res, src1);
    double old_residual = residual;

    double abs_error = 1.e6;
    double rel_error = 1.e6;

    this->debug_output(u, u_n, res);
    app->notify(Event::assign("Picard"));
    double old_error = 1e5;

    //
    while ((abs_error > this->abs_tolerance) && (rel_error > this->rel_tolerance) && (this->step < this->maxsteps))
    {
        u_n.reinit(u);
        app->solve (u_n, src2);

        abs_error = 0;
        rel_error = 0;
        FEVector error;
        error.reinit(u);

        double gamma, delta=0, gamma_min=this->gamma_min, alpha= this->alpha, epsilon=this->abs_tolerance;

        this->compute_errors(u,u_n,error,abs_error,rel_error,delta);

        // Adaptive under-relaxation scheme
        if (this->underrelaxation)
        {

            FcstUtilities::log<<"Delta: "<<delta<<std::endl;

            double rho=0.95;    //damping factor if solution starts to diverge

            // If solution diverges then gamma and alpha are further damped
            while(old_error<abs_error && rho>0 && this->step>1)
            {
                FcstUtilities::log<<"New solution diverging! Damping gamma and alpha"<<std::endl;
                gamma_min*=rho;

                alpha = - log((gamma*rho - gamma_min)/(1-gamma_min))/(delta-epsilon);
                gamma = gamma_min+(1-gamma_min)*exp (-alpha*(delta-epsilon));

                for(unsigned int i=0; i<u_n.size();i++)
                    u(i)=u(i)+gamma*(u_n(i)-u(i));
                residual = app->residual(res, src1);
                app->notify(Event::assign("Picard"));
                u_n.reinit(u);
                app->solve(u_n,src2);
                abs_error = 0;
                rel_error = 0;

                FcstUtilities::log<<"Gamma: "<<gamma<<std::endl;
                double new_delta = 0;
                this->compute_errors(u,u_n,error,abs_error,rel_error,new_delta);

                FcstUtilities::log<<"Delta: "<<new_delta<<std::endl;
                if (old_error>abs_error)
                    delta = new_delta;
                rho-=0.1;
            }
            if (delta>epsilon)
                gamma = gamma_min+(1-gamma_min)*exp (-alpha*(delta-epsilon));
            else
                gamma = 1;
            FcstUtilities::log<<"Gamma: "<<gamma<<std::endl;
            for(unsigned int i=0; i<u_n.size();i++)
                u(i)=u(i)+gamma*(u_n(i)-u(i));
        }
        //Pure Picard scheme
        else
            u=u_n;

        // Changing type of solver in order to assemble the cell residual using the assemble_cell_residual function of equation class
        FEVector temp_res;
        FEVectors temp_src;
        temp_src.merge(src1);
        this->data->set_nonlinear_solver("NewtonLineSearch");
        double new_residual = app->residual(temp_res,temp_src,false);

        // Reset the non-linear solver to Picard
        this->data->set_nonlinear_solver("Picard");
        old_residual = residual;
        residual = app->residual(res, src1);

        // Output the global residual and the equation specific residual:
        FcstUtilities::log << "Overall absolute error at iteration "<<this->step<<" = " << abs_error << std::endl;
        FcstUtilities::log << "Overall relative error at iteration "<<this->step<<" = " << rel_error << std::endl;

        for (unsigned int i = 0; i<res.n_blocks(); i++)
            FcstUtilities::log << "Residual for equation "<<i<<" is: "<<temp_res.block(i).l2_norm() << std::endl;

        // Debug output options:
        this->debug_output(u, u_n, res);
        old_error=abs_error;
        this->step+=1;
    }

    AssertThrow((abs_error< this->abs_tolerance)||(rel_error< this->rel_tolerance),SolverControl::NoConvergence (this->step,abs_error));

}

void
Picard::compute_errors ( FEVector &u, FEVector &u_n, FEVector &error, double &abs_error, double &rel_error, double &delta)
{
    int flag =0;
    double dofs=u.size();
    for(unsigned int i=0; i<u_n.size();i++)
    {
        if (u_n(i)<0)
        {
            flag=1;
            u_n(i)=0;
        }
        abs_error+=pow((u(i)-u_n(i)),2);
        error(i)=u(i)-u_n(i);
        if (fabs(u(i)-u_n(i))>delta)
            delta = fabs(u(i)-u_n(i));
    }
    abs_error = sqrt(abs_error)/dofs;
    rel_error = error.l2_norm()/u_n.l2_norm();
    if (flag)
        FcstUtilities::log<<"Negative values in solution were set to zero!!!"<<std::endl;
}