// This file is part of the dune-xt-grid project:
//   https://github.com/dune-community/dune-xt-grid
// Copyright 2009-2018 dune-xt-grid 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 (2017 - 2018)
//   René Fritze     (2018)
//   Tobias Leibner  (2017 - 2018)

#ifndef DUNE_XT_GRID_DD_GLUED_HH
#define DUNE_XT_GRID_DD_GLUED_HH

#include <array>
#include <algorithm>
#include <memory>
#include <map>

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

#include <dune/grid/common/rangegenerators.hh>

#if HAVE_DUNE_GRID_GLUE
#  include <dune/grid-glue/extractors/codim1extractor.hh>
#  include <dune/grid-glue/gridglue.hh>
#  include <dune/grid-glue/merging/contactmerge.hh>
#endif // HAVE_DUNE_GRID_GLUE

#include <dune/xt/common/exceptions.hh>
#include <dune/xt/common/float_cmp.hh>
#include <dune/xt/common/ranges.hh>
#include <dune/xt/common/timedlogging.hh>
#include <dune/xt/grid/intersection.hh>
#include <dune/xt/grid/layers.hh>
#include <dune/xt/grid/gridprovider/provider.hh>
#include <dune/xt/grid/gridprovider/cube.hh>
#include <dune/xt/grid/search.hh>
#include <dune/xt/grid/type_traits.hh>

namespace Dune {
namespace XT {
namespace Grid {
namespace DD {
namespace Exceptions {

class intersection_orientation_is_broken : public Dune::InvalidStateException
{};

} // namespace Exceptions

#if HAVE_DUNE_GRID_GLUE

template <typename P0, typename P1>
size_t check_for_broken_coupling_intersections(
    const GridGlue::GridGlue<P0, P1>& glue,
    const typename P0::ctype& tolerance = 10 * XT::Common::FloatCmp::DefaultEpsilon<typename P0::ctype>::value())
{
  const auto inside_grid_view = glue.template gridView<0>();
  size_t failures = 0;
  // walk the coupling
  const auto coupling_intersection_it_end = glue.template iend<0>();
  for (auto coupling_intersection_it = glue.template ibegin<0>();
       coupling_intersection_it != coupling_intersection_it_end;
       ++coupling_intersection_it) {
    const auto& coupling_intersection = *coupling_intersection_it;
    const auto coupling_intersection_normal = coupling_intersection.centerUnitOuterNormal();
    const auto local_entity = coupling_intersection.inside();
    typename std::remove_const<decltype(coupling_intersection_normal)>::type local_intersection_normal(0.);
    // find the intersection of the local inside entity that corresponds to the coupling intersection
    size_t found = 0;
    for (auto&& local_intersection : intersections(inside_grid_view, local_entity)) {
      // the coupling intersection may be smaller than the local intersection, so check if all of the corners of the
      // coupling intersection lie within this local intersection
      int corners_inside = 0;
      for (auto ii : XT::Common::value_range(coupling_intersection.geometry().corners()))
        if (XT::Grid::contains(local_intersection, coupling_intersection.geometry().corner(ii)))
          ++corners_inside;
      if (corners_inside == coupling_intersection.geometry().corners()) {
        // this is the one
        ++found;
        local_intersection_normal = local_intersection.centerUnitOuterNormal();
      }
    }
    if (found != 1)
      DUNE_THROW(InvalidStateException,
                 "This should not happen!\n"
                     << "There were " << found << " local intersections which contain the coupling intersection, "
                     << "and there must not be more than one!");
    // now the expected normal is local_intersection_normal
    // and we would like coupling_intersection_normal to point in the same direction
    // since they have unit length, they should be identical
    if ((local_intersection_normal - coupling_intersection_normal).infinity_norm() > tolerance)
      ++failures;
  }
  return failures;
} // ... check_for_broken_coupling_intersections(...)

// forward
template <class MacroGridType, class LocalGridType, Layers layer = Layers::level>
class GluedVTKWriter;

template <class MacroGridImp, class LocalGridImp, Layers layer = Layers::level>
class Glued
{
  template <class G, bool anything = true>
  struct allowed_macro_grid
  {
    static const bool value = true;
  };

  template <class G, bool anything = true>
  struct allowed_local_grid
  {
    static const bool value = true;
  };

#  if HAVE_DUNE_ALUGRID
  template <class Comm, bool anything>
  struct allowed_local_grid<ALUGrid<3, 3, simplex, conforming, Comm>, anything>
  {
    static const bool value = false;
  };

  template <class Comm, bool anything>
  struct allowed_local_grid<ALUGrid<3, 3, cube, conforming, Comm>, anything>
  {
    static const bool value = false;
  };
#  endif // HAVE_DUNE_ALUGRID

#  if HAVE_MPI && (HAVE_DUNE_UGGRID || HAVE_UG)
  // UGGrid does not support multiple parallel instances in parallel and we have no means yet to create multiple
  // sequential grids once MPI was found.
  template <bool anything>
  struct allowed_local_grid<UGGrid<2>, anything>
  {
    static const bool value = false;
  };

  template <bool anything>
  struct allowed_local_grid<UGGrid<3>, anything>
  {
    static const bool value = false;
  };
#  endif

  static_assert(allowed_macro_grid<MacroGridImp>::value,
                "This macro grid is known to fail, enable on your onw risk by disabling this check!");
  static_assert(allowed_local_grid<LocalGridImp>::value,
                "This local grid is known to fail, enable on your onw risk by disabling this check!");

public:
  typedef MacroGridImp MacroGridType;
  typedef XT::Grid::GridProvider<MacroGridType> MacroGridProviderType;
  typedef typename MacroGridProviderType::LeafGridViewType MacroGridViewType;
  typedef typename MacroGridType::template Codim<0>::Entity MacroEntityType;

  typedef LocalGridImp LocalGridType;
  typedef XT::Grid::GridProvider<LocalGridType> LocalGridProviderType;
  typedef typename LocalGridType::LevelGridView MicroGridViewType;
  using MicroEntityType = XT::Grid::extract_entity_t<MicroGridViewType>;

  typedef typename MacroGridType::ctype ctype;
  static const size_t dimDomain = MacroGridType::dimension;
  static const size_t dimWorld = MacroGridType::dimensionworld;

public:
  typedef typename Layer<LocalGridType, layer, Backends::view>::type LocalViewType;

private:
  typedef GridGlue::Codim1Extractor<LocalViewType> LocalExtractorType;

public:
  typedef GridGlue::GridGlue<LocalExtractorType, LocalExtractorType> GlueType;

private:
  template <class GridView, class MacroIntersectionType>
  class CouplingFaceDescriptor
  {
    using LocalEntityType = XT::Grid::extract_entity_t<GridView>;
    typedef typename GridView::ctype ctype;

  public:
    CouplingFaceDescriptor(const MacroIntersectionType& macro_intersection)
      : macro_intersection_(macro_intersection)
    {}

    bool operator()(const LocalEntityType& element, unsigned int face) const
    {
      const auto local_intersection_ptr = element.template subEntity<1>(face);
#  if DUNE_VERSION_GTE(DUNE_GRID, 2, 4)
      const auto& local_intersection = local_intersection_ptr;
#  else
      const auto& local_intersection = *local_intersection_ptr;
#  endif
      const auto& local_intersection_geometry = local_intersection.geometry();
      // Check if all corners of the local intersection lie within the macro intersection.
      for (auto ii : XT::Common::value_range(local_intersection_geometry.corners()))
        if (!XT::Grid::contains(macro_intersection_, local_intersection_geometry.corner(ii)))
          return false;
      return true;
    } // ... contains(...)

  private:
    const MacroIntersectionType& macro_intersection_;
  }; // CouplingFaceDescriptor

  template <class GV, class MI>
  static CouplingFaceDescriptor<GV, MI> create_descriptor(const GV& /*gv*/, const MI& mi)
  {
    return CouplingFaceDescriptor<GV, MI>(mi);
  }

public:
  Glued(MacroGridProviderType& macro_grid_provider,
        const size_t num_local_refinements = 0,
        const bool prepare_glues = false,
        const bool allow_for_broken_orientation_of_coupling_intersections = false,
        const ctype& allowed_overlap = 10 * XT::Common::FloatCmp::DefaultEpsilon<ctype>::value())
    : macro_grid_(macro_grid_provider)
    , allowed_overlap_(allowed_overlap)
    , macro_leaf_view_(macro_grid_.leaf_view())
    , macro_leaf_view_size_(macro_leaf_view_.indexSet().size(0))
    , local_grids_(macro_leaf_view_.indexSet().size(0), nullptr)
    , glues_(macro_leaf_view_.indexSet().size(0))
  {
    setup_local_grids();
    if (num_local_refinements > 0)
      for (auto& local_grid_provider : local_grids_) {
        assert(local_grid_provider);
        local_grid_provider->grid().globalRefine(boost::numeric_cast<int>(num_local_refinements));
      }
    if (prepare_glues)
      setup_glues(allow_for_broken_orientation_of_coupling_intersections);
  } // Glued(...)

  const MacroGridViewType& macro_grid_view() const
  {
    assert_macro_grid_state();
    return macro_leaf_view_;
  }

  size_t num_subdomains() const
  {
    assert_macro_grid_state();
    return macro_leaf_view_size_;
  }

  size_t subdomain(const MacroEntityType& macro_entity) const
  {
    assert_macro_grid_state();
    assert(macro_leaf_view_.indexSet().contains(macro_entity));
    return macro_leaf_view_.indexSet().index(macro_entity);
  }

  bool boundary(const MacroEntityType& macro_entity) const
  {
    assert_macro_grid_state();
    assert(macro_leaf_view_.indexSet().contains(macro_entity));
    return macro_entity.hasBoundaryIntersections();
  }

  MicroGridViewType global_grid_view()
  {
    auto logger = XT::Common::TimedLogger().get("grid-multiscale.glued.global_grid_view");
    logger.warn() << "Requiring inefficient access to global micro grid!" << std::endl;
    assert_macro_grid_state();
    prepare_global_grid();
    return global_grid_->level_view(global_grid_->grid().maxLevel());
  }

  const std::vector<std::vector<size_t>>& local_to_global_indices()
  {
    auto logger = XT::Common::TimedLogger().get("grid-multiscale.glued.local_to_global_indices");
    logger.warn() << "Requiring inefficient access to global micro grid!" << std::endl;
    assert_macro_grid_state();
    prepare_global_grid();
    return *local_to_global_indices_;
  }

  MicroEntityType local_to_global_entity(const size_t subd, const MicroEntityType& local_entity)
  {
    return local_to_global_entity(
        subd, extract_local_view<layer>()(*local_grids_[subd], max_local_level(subd)).indexSet().index(local_entity));
  }

  MicroEntityType local_to_global_entity(const size_t subd, const size_t local_entity_index)
  {
    auto logger = XT::Common::TimedLogger().get("grid-multiscale.glued.local_to_global_entity");
    logger.warn() << "Requiring inefficient access to global micro grid!" << std::endl;
    assert_macro_grid_state();
    prepare_global_grid();
    const size_t global_index_of_local_entity = local_to_global_indices_->operator[](subd)[local_entity_index];
    const auto global_micro_grid_view = global_grid_view();
    const auto entity_it_end = global_micro_grid_view.template end<0>();
    for (auto entity_it = global_micro_grid_view.template begin<0>(); entity_it != entity_it_end; ++entity_it) {
      const auto& entity = *entity_it;
      if (global_micro_grid_view.indexSet().index(entity) == global_index_of_local_entity)
        return entity;
    }
    DUNE_THROW(XT::Common::Exceptions::wrong_input_given,
               "subdomain: " << subd << "\n"
                             << "local_entity_index: " << local_entity_index << "\n"
                             << "global_index_of_local_entity: " << global_index_of_local_entity);
    return *global_micro_grid_view.template begin<0>();
  } // ... local_to_global_entity(...)

  MicroEntityType global_to_local_entity(const MicroEntityType& micro_entity)
  {
    assert_macro_grid_state();
    prepare_global_grid();
    return global_to_local_entity(global_grid_view().indexSet().index(micro_entity));
  }

  MicroEntityType global_to_local_entity(const size_t micro_entity_index)
  {
    auto logger = XT::Common::TimedLogger().get("grid-multiscale.glued.global_to_local_entity");
    logger.warn() << "Requiring inefficient access to global micro grid!" << std::endl;
    assert_macro_grid_state();
    prepare_global_grid();
    auto subdomain_and_local_entity_index = global_to_local_indices_->operator[](micro_entity_index);
    const auto subd = subdomain_and_local_entity_index.first;
    const auto local_index_of_global_entity = subdomain_and_local_entity_index.second;
    const auto local_grid_view = extract_local_view<layer>()(*local_grids_[subd], max_local_level(subd));
    const auto entity_it_end = local_grid_view.template end<0>();
    for (auto entity_it = local_grid_view.template begin<0>(); entity_it != entity_it_end; ++entity_it) {
      const auto& entity = *entity_it;
      if (local_grid_view.indexSet().index(entity) == local_index_of_global_entity)
        return entity;
    }
    DUNE_THROW(XT::Common::Exceptions::wrong_input_given,
               "micro_entity_index: " << micro_entity_index
                                      << "\n"
                                         "subdomain: "
                                      << subd << "\n"
                                      << "local_index_of_global_entity: " << local_index_of_global_entity);
    return *local_grid_view.template begin<0>();
  } // ... global_to_local_entity(...)

  const std::vector<std::pair<size_t, size_t>>& global_to_local_indices()
  {
    auto logger = XT::Common::TimedLogger().get("grid-multiscale.glued.global_to_local_indices");
    logger.warn() << "Requiring inefficient access to global micro grid!" << std::endl;
    assert_macro_grid_state();
    prepare_global_grid();
    assert(global_to_local_indices_);
    return *global_to_local_indices_;
  }

  const LocalGridProviderType& local_grid(const MacroEntityType& macro_entity) const
  {
    assert_macro_grid_state();
    assert(macro_leaf_view_.indexSet().contains(macro_entity));
    return local_grid(macro_leaf_view_.indexSet().index(macro_entity));
  }

  LocalGridProviderType& local_grid(const MacroEntityType& macro_entity)
  {
    assert_macro_grid_state();
    assert(macro_leaf_view_.indexSet().contains(macro_entity));
    return local_grid(macro_leaf_view_.indexSet().index(macro_entity));
  }

  LocalGridProviderType& local_grid(const size_t macro_entity_index)
  {
    assert_macro_grid_state();
    assert(macro_entity_index < macro_leaf_view_size_);
    return *(local_grids_.at(macro_entity_index));
  }

  const LocalGridProviderType& local_grid(const size_t macro_entity_index) const
  {
    assert_macro_grid_state();
    assert(macro_entity_index < macro_leaf_view_size_);
    return *(local_grids_.at(macro_entity_index));
  }

  /**
   * \brief Returns (and creates, if it does not exist) the coupling glue between the local grid view of level
   *        local_level_macro_entity on macro_entity and the local grid view of level local_level_macro_neighbor on
   * macro_neighbor.
   * \note  Access is not implemented efficiently. This could be improved by using std::map::find instead of
   *        std::map::operator[].
   */
  const GlueType& coupling(const MacroEntityType& macro_entity,
                           const int local_level_macro_entity,
                           const MacroEntityType& macro_neighbor,
                           const int local_level_macro_neighbor,
                           const bool allow_for_broken_orientation_of_coupling_intersections = false)
  {
    assert_macro_grid_state();
    const auto& macro_index_set = macro_leaf_view_.indexSet();
    assert(macro_index_set.contains(macro_entity));
    assert(macro_index_set.contains(macro_neighbor));
    if (local_level_macro_entity > max_local_level(macro_entity))
      DUNE_THROW(XT::Common::Exceptions::you_are_using_this_wrong,
                 "max_local_level(macro_entity): " << max_local_level(macro_entity) << "\n"
                                                   << "   local_level_macro_entity:      " << local_level_macro_entity);
    if (local_level_macro_neighbor > max_local_level(macro_neighbor))
      DUNE_THROW(XT::Common::Exceptions::you_are_using_this_wrong,
                 "max_local_level(macro_neighbor): " << max_local_level(macro_neighbor) << "\n"
                                                     << "   local_level_macro_neighbor:      "
                                                     << local_level_macro_neighbor);
    int local_entity_grid_level = local_level_macro_entity;
    int local_neighbor_grid_level = local_level_macro_neighbor;
    // in case of local level views, the local_level_macro... have to be non-negative
    if (layer == Layers::level) {
      if (local_level_macro_entity < 0)
        DUNE_THROW(XT::Common::Exceptions::you_are_using_this_wrong,
                   "local_level_macro_entity has to be non-negative (is " << local_level_macro_entity << ")!");
      if (local_level_macro_neighbor < 0)
        DUNE_THROW(XT::Common::Exceptions::you_are_using_this_wrong,
                   "local_level_macro_neighbor has to be non-negative (is " << local_level_macro_neighbor << ")!");
    } else if (layer == Layers::leaf) {
      // in case of local leaf views, it has to be -1
      if (local_level_macro_entity != -1)
        DUNE_THROW(XT::Common::Exceptions::you_are_using_this_wrong,
                   "local_level_macro_entity has to be -1 (is " << local_level_macro_entity << ")!");
      if (local_level_macro_neighbor != -1)
        DUNE_THROW(XT::Common::Exceptions::you_are_using_this_wrong,
                   "local_level_macro_neighbor has to be -1 (is " << local_level_macro_neighbor << ")!");
      // since the local leaf views may have been adapted, we use their respective sizes as keys for the glue map
      local_entity_grid_level *= local_grid(macro_entity).leaf_view().indexSet().size(0);
      local_neighbor_grid_level *= local_grid(macro_neighbor).leaf_view().indexSet().size(0);
    }
    const auto entity_index = macro_index_set.index(macro_entity);
    const auto neighbor_index = macro_index_set.index(macro_neighbor);
    auto& glues_for_this_entity_neighbor = glues_[entity_index][neighbor_index];
    if (glues_for_this_entity_neighbor[local_entity_grid_level][local_neighbor_grid_level] == nullptr) {
      if (layer == Layers::leaf) {
        // if the glue for the current leaf sizes does not exist, but for other leaf sizes, then those are not valid any
        // more and we can get rid of them
        if (glues_for_this_entity_neighbor.size() > 0)
          glues_for_this_entity_neighbor.clear();
      }
      // find the corresponding macro intersection ...
      const auto macro_intersection_it_end = macro_leaf_view_.iend(macro_entity);
      for (auto macro_intersection_it = macro_leaf_view_.ibegin(macro_entity);
           macro_intersection_it != macro_intersection_it_end;
           ++macro_intersection_it) {
        const auto& macro_intersection = *macro_intersection_it;
        if (macro_intersection.neighbor() && !macro_intersection.boundary()) {
          const auto real_neighbor_ptr = macro_intersection.outside();
#  if DUNE_VERSION_GTE(DUNE_GRID, 2, 4)
          const auto& real_neighbor = real_neighbor_ptr;
#  else
          const auto& real_neighbor = *real_neighbor_ptr;
#  endif
          if (macro_index_set.index(real_neighbor) == neighbor_index)
            glues_for_this_entity_neighbor[local_entity_grid_level][local_neighbor_grid_level] =
                create_glue(macro_entity,
                            macro_neighbor,
                            macro_intersection,
                            (layer == Layers::level) ? local_entity_grid_level : -1,
                            (layer == Layers::level) ? local_neighbor_grid_level : -1);
        }
      } // ... find the corresponding macro intersection
    }

    const auto& glue = *(glues_for_this_entity_neighbor[local_entity_grid_level][local_neighbor_grid_level]);
    if (!allow_for_broken_orientation_of_coupling_intersections) {
      const size_t brocken_intersections = check_for_broken_coupling_intersections(glue);
      if (brocken_intersections > 0)
        DUNE_THROW(Exceptions::intersection_orientation_is_broken,
                   "The coupling glue between the grid views of\n"
                       << "     level " << local_entity_grid_level << " on macro entity   "
                       << macro_leaf_view_.indexSet().index(macro_entity) << " and\n"
                       << "     level " << local_neighbor_grid_level << " on macro neighbor "
                       << macro_leaf_view_.indexSet().index(macro_neighbor) << "\n"
                       << "   contains\n"
                       << "     " << brocken_intersections << "/" << glue.size()
                       << " intersections with wrong orientation!");
    }
    return glue;
  } // ... coupling(...)

  const GlueType& coupling(const MacroEntityType& macro_entity,
                           const MacroEntityType& macro_neighbor,
                           const bool allow_for_broken_orientation_of_coupling_intersections = false)
  {
    return coupling(macro_entity,
                    max_local_level(macro_entity),
                    macro_neighbor,
                    max_local_level(macro_neighbor),
                    allow_for_broken_orientation_of_coupling_intersections);
  }

  int max_local_level(const MacroEntityType& macro_entity) const
  {
    assert_macro_grid_state();
    if (layer == Layers::leaf)
      return -1;
    else
      return local_grid(macro_entity).grid().maxLevel();
  }

  int max_local_level(const size_t macro_entity_index) const
  {
    assert_macro_grid_state();
    if (layer == Layers::leaf)
      return -1;
    else
      return local_grid(macro_entity_index).grid().maxLevel();
  }

  const std::vector<std::pair<MicroEntityType, std::vector<int>>>&
  local_boundary_entities(const MacroEntityType& macro_entity, const int local_level)
  {
    assert_macro_grid_state();
    assert(macro_leaf_view_.indexSet().contains(macro_entity));
    const size_t macro_entity_index = macro_leaf_view_.indexSet().index(macro_entity);
    if (local_level > max_local_level(macro_entity))
      DUNE_THROW(XT::Common::Exceptions::you_are_using_this_wrong,
                 "macro_entity_index: " << macro_entity_index << "\n"
                                        << "local_level: " << local_level << "\n"
                                        << "max_local_level(macro_entity): " << max_local_level(macro_entity));
    auto& local_level_to_boundary_entity_ptrs_with_local_intersections =
        macro_entity_to_local_level_to_boundary_entity_ptrs_with_local_intersections_[macro_entity_index];
    auto& boundary_entity_ptrs_with_local_intersections =
        local_level_to_boundary_entity_ptrs_with_local_intersections[local_level];
    //    logger.debug() << "macro_entity: " << macro_entity_index << std::endl;
    if (boundary(macro_entity) && boundary_entity_ptrs_with_local_intersections.empty()) {
      // create the container, therefore
      const auto local_leaf_view = local_grids_[macro_entity_index]->leaf_view();
      // * walk the local grid (manually, to have access to the entity pointer)
      const auto local_entity_it_end = local_leaf_view.template end<0>();
      for (auto local_entity_it = local_leaf_view.template begin<0>(); local_entity_it != local_entity_it_end;
           ++local_entity_it) {
        const auto& local_entity = *local_entity_it;
        //        logger.debug() << "local_entity: " << local_leaf_view.indexSet().index(local_entity) << " ";
        if (local_entity.hasBoundaryIntersections()) {
          //          logger.debug() << "(boundary entity)" << std::endl;
          std::vector<int> local_boundary_intersections;
          // This entity has intersections on the local grid boundary, those could either be the domain boundary (which
          // we are looking for) or a boundary to another local grid (which we are not looking for). To find out
          // * walk the intersections
          for (auto&& local_intersection : intersections(local_leaf_view, local_entity)) {
            if (local_intersection.boundary() && !local_intersection.neighbor()) {
              //              logger.debug() << "local_intersection: " << local_intersection.indexInInside() << ":" <<
              //              std::endl;
              const auto local_intersection_geometry = local_intersection.geometry();
              const size_t num_corners = boost::numeric_cast<size_t>(local_intersection_geometry.corners());
              // ** Check if all corners of the intersection lie on the domain boundary (aka the boundary intersection
              // of
              //    the macro entity this local grid belongs to. Therefore
              //    *** walk the intersections of the macro entity
              for (auto&& macro_intersection : intersections(macro_leaf_view_, macro_entity)) {
                if (macro_intersection.boundary() && !macro_intersection.neighbor()) {
                  // This macro intersection lies on the domain boundary, check if the local intersection is contained.
                  size_t corners_lie_on_boundary = 0;
                  for (size_t ii = 0; ii < num_corners; ++ii) {
                    if (XT::Grid::contains(macro_intersection,
                                           local_intersection_geometry.corner(boost::numeric_cast<int>(ii))))
                      ++corners_lie_on_boundary;
                  }
                  if (corners_lie_on_boundary == num_corners) {
                    // add the information to the container
                    local_boundary_intersections.push_back(local_intersection.indexInInside());
                  } // add the information to the container
                }
              } //    *** walk the intersections of the macro entity
            }
          } // * walk the intersections
          if (!local_boundary_intersections.empty()) {
            // add this local entity and its local intersections to the container
            boundary_entity_ptrs_with_local_intersections.emplace_back(local_entity, local_boundary_intersections);
          }
        } /*else
          logger.debug() << "(inner entity)" << std::endl;*/
      } // * walk the local grid
    } // create the container
    return boundary_entity_ptrs_with_local_intersections;
  } // ... local_boundary_entities(...)

  template <class... Args>
  void visualize(const std::string& filename = "grid.multiscale.glued", Args&&... args)
  {
    assert_macro_grid_state();
    auto logger = XT::Common::TimedLogger().get("grid-multiscale.glued.visualize");
    macro_grid_.visualize(filename + ".macro", std::forward<Args>(args)...);
    GluedVTKWriter<MacroGridType, LocalGridType, layer> vtk_writer(*this);
    const auto& macro_index_set = macro_leaf_view_.indexSet();
    std::vector<std::vector<double>> subdomain_visualization(macro_index_set.size(0));
    std::vector<std::vector<double>> boundary_visualization(macro_index_set.size(0));
    std::vector<std::vector<double>> inside_outside_coupling_visualization(macro_index_set.size(0));
    std::vector<std::vector<double>> outside_inside_coupling_visualization(macro_index_set.size(0));
    // walk the macro grid
    for (auto&& macro_entity : elements(macro_leaf_view_)) {
      const size_t macro_entity_index = macro_index_set.index(macro_entity);
      logger.debug() << "macro_entity: " << macro_entity_index << " ";
      const auto local_level = max_local_level(macro_entity);
      const auto local_grid_view = extract_local_view<layer>()(*local_grids_[macro_entity_index], local_level);
      subdomain_visualization[macro_entity_index] =
          std::vector<double>(local_grid_view.indexSet().size(0), macro_entity_index);
      boundary_visualization[macro_entity_index] = std::vector<double>(local_grid_view.indexSet().size(0), -1);
      if (inside_outside_coupling_visualization[macro_entity_index].empty())
        inside_outside_coupling_visualization[macro_entity_index] =
            std::vector<double>(local_grid_view.indexSet().size(0), -1);
      if (outside_inside_coupling_visualization[macro_entity_index].empty())
        outside_inside_coupling_visualization[macro_entity_index] =
            std::vector<double>(local_grid_view.indexSet().size(0), -1);
      // local boundary entities
      if (boundary(macro_entity)) {
        logger.debug() << "(boundary entity)" << std::endl;
        const auto& boundary_entities_with_local_intersections = local_boundary_entities(macro_entity, local_level);
        logger.debug() << "  " << boundary_entities_with_local_intersections.size() << "/"
                       << local_grid_view.indexSet().size(0) << " boundary entities" << std::endl;
        for (const auto& element : boundary_entities_with_local_intersections) {
          const auto& local_entity = element.first;
          const auto& local_intersections = element.second;
          if (!local_intersections.empty()) {
            const size_t local_entity_index = local_grid_view.indexSet().index(local_entity);
            boundary_visualization[macro_entity_index][local_entity_index] = macro_entity_index;
          }
        }
      } else
        logger.debug() << "(inner entity)" << std::endl;
      for (auto&& macro_intersection : intersections(macro_leaf_view_, macro_entity)) {
        if (!macro_intersection.boundary() && macro_intersection.neighbor()) {
          const auto macro_neighbor_ptr = macro_intersection.outside();
#  if DUNE_VERSION_GTE(DUNE_GRID, 2, 4)
          const auto& macro_neighbor = macro_neighbor_ptr;
#  else
          const auto& macro_neighbor = *macro_neighbor_ptr;
#  endif
          const size_t macro_neighbor_index = macro_leaf_view_.indexSet().index(macro_neighbor);
          const auto local_neighbor_level = max_local_level(macro_neighbor);
          const auto local_neighbor_grid_view =
              extract_local_view<layer>()(*local_grids_[macro_neighbor_index], local_neighbor_level);
          if (inside_outside_coupling_visualization[macro_neighbor_index].empty())
            inside_outside_coupling_visualization[macro_neighbor_index] =
                std::vector<double>(local_neighbor_grid_view.indexSet().size(0), -1);
          if (outside_inside_coupling_visualization[macro_neighbor_index].empty())
            outside_inside_coupling_visualization[macro_neighbor_index] =
                std::vector<double>(local_neighbor_grid_view.indexSet().size(0), -1);
          // walk the coupling, where this is the inside
          size_t num_coupling_intersections = 0;
          const auto& in_out_coupling_glue = coupling(macro_entity,
                                                      local_level,
                                                      macro_neighbor,
                                                      local_neighbor_level,
                                                      /*allow_for_broken_orientation_of_coupling_intersections=*/true);
          const auto in_out_coupling_intersection_it_end = in_out_coupling_glue.template iend<0>();
          for (auto in_out_coupling_intersection_it = in_out_coupling_glue.template ibegin<0>();
               in_out_coupling_intersection_it != in_out_coupling_intersection_it_end;
               ++in_out_coupling_intersection_it) {
            ++num_coupling_intersections;
            const auto& coupling_intersection = *in_out_coupling_intersection_it;
            const auto local_entity = coupling_intersection.inside();
            const size_t local_entity_index = local_grid_view.indexSet().index(local_entity);
            inside_outside_coupling_visualization[macro_entity_index][local_entity_index] = macro_entity_index;
            const auto local_neighbor = coupling_intersection.outside();
            const size_t local_neighbor_index = local_neighbor_grid_view.indexSet().index(local_neighbor);
            inside_outside_coupling_visualization[macro_neighbor_index][local_neighbor_index] = macro_neighbor_index;
          }
          // walk the coupling, where this is the outside
          size_t out_in_num_coupling_intersections = 0;
          const auto& out_in_coupling_glue = coupling(macro_neighbor,
                                                      local_neighbor_level,
                                                      macro_entity,
                                                      local_level,
                                                      /*allow_for_broken_orientation_of_coupling_intersections=*/true);
          const auto out_in_coupling_intersection_it_end = out_in_coupling_glue.template iend<0>();
          for (auto out_in_coupling_intersection_it = out_in_coupling_glue.template ibegin<0>();
               out_in_coupling_intersection_it != out_in_coupling_intersection_it_end;
               ++out_in_coupling_intersection_it) {
            ++out_in_num_coupling_intersections;
            const auto& coupling_intersection = *out_in_coupling_intersection_it;
            const auto local_entity = coupling_intersection.inside();
            const size_t local_entity_index = local_grid_view.indexSet().index(local_entity);
            outside_inside_coupling_visualization[macro_neighbor_index][local_entity_index] = macro_neighbor_index;
            const auto local_neighbor = coupling_intersection.outside();
            const size_t local_neighbor_index = local_neighbor_grid_view.indexSet().index(local_neighbor);
            outside_inside_coupling_visualization[macro_entity_index][local_neighbor_index] = macro_entity_index;
          }
          if (num_coupling_intersections != out_in_num_coupling_intersections)
            DUNE_THROW(XT::Common::Exceptions::internal_error,
                       "The coupling glue is broken!\n"
                           << "macro entity (local level):   " << macro_entity_index << " (" << local_level << ")\n"
                           << "macro neighbor (local level): " << macro_neighbor_index << " (" << local_neighbor_level
                           << ")");
          logger.debug() << "  " << num_coupling_intersections << " coupling intersections with neighbor "
                         << macro_neighbor_index << std::endl;
        }
      }
    } // walk the macro grid
    vtk_writer.addCellData(subdomain_visualization, "subdomains");
    vtk_writer.addCellData(boundary_visualization, "local boundary entities");
    vtk_writer.addCellData(inside_outside_coupling_visualization, "local coupling entities (inside/outside)");
    vtk_writer.addCellData(outside_inside_coupling_visualization, "local coupling entities (outside/inside)");
    vtk_writer.write(filename, VTK::appendedraw);
  } // ... visualize(...)

private:
  template <class MacroEntityType>
  static std::shared_ptr<LocalGridProviderType> create_grid_of_simplex(const MacroEntityType& macro_entity)
  {
    try {
      GridFactory<LocalGridType> subdomain_factory;
      const auto num_vertices = macro_entity.subEntities(dimDomain);
      std::vector<unsigned int> vertex_ids(num_vertices, 0);
      for (auto&& local_vertex_id : XT::Common::value_range(num_vertices)) {
        const auto vertex = macro_entity.template subEntity<dimDomain>(local_vertex_id).geometry().center();
        subdomain_factory.insertVertex(vertex);
        vertex_ids[local_vertex_id] = local_vertex_id;
      }
      subdomain_factory.insertElement(macro_entity.geometry().type(), vertex_ids);
      return std::make_shared<XT::Grid::GridProvider<LocalGridType>>(subdomain_factory.createGrid());
    } catch (GridError& ee) {
      DUNE_THROW(GridError,
                 "It was not possible to create a grid for this simplex with the given GridType!\n\n"
                     << "GridType: " << XT::Common::Typename<LocalGridType>::value() << "\n\n"
                     << "This was the original error: " << ee.what());
    }
  } // ... create_grid_of_simplex(...)

  template <class MacroEntityType>
  static std::shared_ptr<LocalGridProviderType> create_grid_of_cube(const MacroEntityType& macro_entity)
  {
    const auto num_vertices = macro_entity.subEntities(dimDomain);
    FieldVector<ctype, dimDomain> lower_left(std::numeric_limits<ctype>::max());
    FieldVector<ctype, dimDomain> upper_right(std::numeric_limits<ctype>::min());
    for (auto&& local_vertex_id : XT::Common::value_range(num_vertices)) {
      const auto vertex = macro_entity.template subEntity<dimDomain>(local_vertex_id).geometry().center();
      for (size_t dd = 0; dd < dimDomain; ++dd) {
        lower_left[dd] = std::min(lower_left[dd], vertex[dd]);
        upper_right[dd] = std::max(upper_right[dd], vertex[dd]);
      }
    }
    std::array<unsigned int, dimDomain> num_elements;
    std::fill(num_elements.begin(), num_elements.end(), 1);
    return std::make_shared<XT::Grid::GridProvider<LocalGridType>>(
        XT::Grid::make_cube_grid<LocalGridType>(lower_left, upper_right, num_elements));
  } // ... create_grid_of_cube(...)

  void setup_local_grids()
  {
    const auto& macro_index_set = macro_leaf_view_.indexSet();
    for (auto&& macro_entity : elements(macro_leaf_view_)) {
      auto macro_entity_index = macro_index_set.index(macro_entity);
      if (macro_entity.type().isSimplex())
        local_grids_[macro_entity_index] = create_grid_of_simplex(macro_entity);
      else if (macro_entity.type().isCube())
        local_grids_[macro_entity_index] = create_grid_of_cube(macro_entity);
      else
        DUNE_THROW(GridError, "Unknown entity.type() encountered: " << macro_entity.type());
    }
  } // ... setup_local_grids()

  template <Layers l, bool anything = true>
  struct extract_local_view
  {
    template <class G>
    auto operator()(G& g, int /*lv*/) -> decltype(g.leaf_view())
    {
      return g.leaf_view();
    }
  };

  template <bool anything>
  struct extract_local_view<Layers::level, anything>
  {
    template <class G>
    auto operator()(G& g, int lv) -> decltype(g.level_view(lv))
    {
      return g.level_view(lv);
    }
  };

  template <class MacroIntersectionType>
  std::shared_ptr<GlueType> create_glue(const MacroEntityType& macro_entity,
                                        const MacroEntityType& macro_neighbor,
                                        const MacroIntersectionType& macro_intersection,
                                        const int local_entity_level,
                                        const int local_neighbor_level) const
  {
    assert(local_entity_level >= -1);
    assert(local_neighbor_level >= -1);
    assert(local_entity_level <= max_local_level(macro_entity));
    assert(local_neighbor_level <= max_local_level(macro_neighbor));
    const auto& local_entity_grid = local_grid(macro_entity);
    const auto& local_neighbor_grid = local_grid(macro_neighbor);
    auto local_entity_view = extract_local_view<layer>()(local_entity_grid, local_entity_level);
    auto local_neighbor_view = extract_local_view<layer>()(local_neighbor_grid, local_neighbor_level);
    // create descriptors, these can be discarded after creating the extractors
    auto entity_descriptor = create_descriptor(local_entity_view, macro_intersection);
    auto neighbor_descriptor = create_descriptor(local_neighbor_view, macro_intersection);
    // create extractors and merger as shared_ptr, so glue will handle memory
    auto entity_extractor = std::make_shared<LocalExtractorType>(local_entity_view, entity_descriptor);
    auto neighbor_extractor = std::make_shared<LocalExtractorType>(local_neighbor_view, neighbor_descriptor);
    auto contact_merger = std::make_shared<GridGlue::ContactMerge<dimWorld, ctype>>(allowed_overlap_);
    // create glue
    auto glue = std::make_shared<GlueType>(entity_extractor, neighbor_extractor, contact_merger);
    glue->build();
    if (glue->size() == 0)
      DUNE_THROW(GridError,
                 "Something went wrong, the coupling glue is empty!\n"
                     << "   macro_entity " << macro_leaf_view_.indexSet().index(macro_entity) << "\n"
                     << "   local_entity_level " << local_entity_level << "\n"
                     << "   macro_neighbor " << macro_leaf_view_.indexSet().index(macro_neighbor) << "\n"
                     << "   local_neighbor_level " << local_neighbor_level << "\n");
    return glue;
  } // ... create_glue(...)

  void setup_glues(const bool allow_for_broken_orientation_of_coupling_intersections = false)
  {
    const auto& macro_index_set = macro_leaf_view_.indexSet();
    for (auto&& macro_entity : elements(macro_leaf_view_)) {
      const auto macro_entity_index = macro_index_set.index(macro_entity);
      auto& entity_glues = glues_[macro_entity_index];
      // walk the neighbors ...
      const auto macro_intersection_it_end = macro_leaf_view_.iend(macro_entity);
      for (auto macro_intersection_it = macro_leaf_view_.ibegin(macro_entity);
           macro_intersection_it != macro_intersection_it_end;
           ++macro_intersection_it) {
        const auto& macro_intersection = *macro_intersection_it;
        if (macro_intersection.neighbor() && !macro_intersection.boundary()) {
          const auto macro_neighbor_ptr = macro_intersection.outside();
#  if DUNE_VERSION_GTE(DUNE_GRID, 2, 4)
          const auto& macro_neighbor = macro_neighbor_ptr;
#  else
          const auto& macro_neighbor = *macro_neighbor_ptr;
#  endif
          const auto macro_neighbor_index = macro_index_set.index(macro_neighbor);
          for (auto local_entity_level :
               XT::Common::value_range(local_grids_[macro_entity_index]->grid().maxLevel() + 1))
            for (auto local_neighbor_level :
                 XT::Common::value_range(local_grids_[macro_neighbor_index]->grid().maxLevel() + 1)) {
              auto glue = create_glue(
                  macro_entity, macro_neighbor, macro_intersection, local_entity_level, local_neighbor_level);
              if (!allow_for_broken_orientation_of_coupling_intersections) {
                const size_t brocken_intersections = check_for_broken_coupling_intersections(*glue);
                if (brocken_intersections > 0)
                  DUNE_THROW(Exceptions::intersection_orientation_is_broken,
                             "The coupling glue between the grid views of\n"
                                 << "  level " << local_entity_level << " on macro entity   "
                                 << macro_leaf_view_.indexSet().index(macro_entity) << " and\n"
                                 << "  level " << local_neighbor_level << " on macro neighbor "
                                 << macro_leaf_view_.indexSet().index(macro_neighbor) << "\n"
                                 << "contains\n"
                                 << "  " << brocken_intersections << "/" << glue->size() << " intersections with wrong"
                                 << "orientation!");
              }
              entity_glues[macro_neighbor_index][local_entity_level][local_neighbor_level] = glue;
            }
        }
      } // ... walk the neighbors
    }
  } // ... setup_glues(...)

  void prepare_global_grid()
  {
    if (global_grid_)
      return;
    const auto& macro_index_set = macro_leaf_view_.indexSet();
    std::vector<FieldVector<ctype, dimDomain>> vertices;
    std::vector<std::vector<std::vector<unsigned int>>> entity_to_vertex_ids(local_grids_.size());
    std::vector<std::vector<GeometryType>> geometry_types(local_grids_.size());
    // walk the grid for the first time
    for (auto&& macro_entity : elements(macro_leaf_view_)) {
      const auto local_leaf_view = local_grid(macro_entity).leaf_view();
      const auto& local_index_set = local_leaf_view.indexSet();
      const size_t macro_index = macro_index_set.index(macro_entity);
      entity_to_vertex_ids[macro_index] = std::vector<std::vector<unsigned int>>(local_index_set.size(0));
      geometry_types[macro_index] = std::vector<GeometryType>(local_index_set.size(0));
      for (auto&& micro_entity : elements(local_leaf_view)) {
        const size_t micro_index = local_index_set.index(micro_entity);
        const auto num_vertices = micro_entity.
#  if DUNE_VERSION_GTE(DUNE_GRID, 2, 4)

                                  subEntities(dimDomain);
#  else
                                  template count<dimDomain>();
#  endif
        entity_to_vertex_ids[macro_index][micro_index] = std::vector<unsigned int>(num_vertices);
        geometry_types[macro_index][micro_index] = micro_entity.geometry().type();
        for (unsigned int local_vertex_id = 0; local_vertex_id < num_vertices; ++local_vertex_id) {
          const unsigned int global_vertex_id = find_insert_vertex(vertices,
                                                                   micro_entity
                                                                       .template subEntity<dimDomain>(local_vertex_id)
#  if DUNE_VERSION_GTE(DUNE_GRID, 2, 4)
                                                                       .
#  else
                                                                       ->
#  endif
                                                                   geometry()
                                                                       .center());
          entity_to_vertex_ids[macro_index][micro_index][local_vertex_id] = global_vertex_id;
        }
      } // walk the local grid
    } // walk the macro grid
    GridFactory<LocalGridType> global_factory;
    for (const auto& vertex : vertices)
      global_factory.insertVertex(vertex);
    size_t II = 0;
    size_t JJ = 0;
    try {
      for (size_t ii = 0; ii < local_grids_.size(); ++ii, II = ii)
        for (size_t jj = 0; jj < entity_to_vertex_ids[ii].size(); ++jj, JJ = jj)
          global_factory.insertElement(geometry_types[ii][jj], entity_to_vertex_ids[ii][jj]);
    } catch (GridError& ee) {
      DUNE_THROW(GridError,
                 "It was not possible to insert an element into the grid factory!\n\n"
                     << "GridType: " << XT::Common::Typename<LocalGridType>::value() << "\n"
                     << "GeometryType: " << geometry_types[II][JJ] << "\n"
                     << "\n"
                     << "This was the original error: " << ee.what());
    } // try
    global_grid_ = std::make_unique<XT::Grid::GridProvider<LocalGridType>>(global_factory.createGrid());

    // build maps of indices, relating the local to the global grid
    const auto global_view = global_grid_->leaf_view();
    local_to_global_indices_ = std::make_unique<std::vector<std::vector<size_t>>>(macro_leaf_view_.indexSet().size(0));
    auto& local_to_global_inds = *local_to_global_indices_;
    global_to_local_indices_ = std::make_unique<std::vector<std::pair<size_t, size_t>>>(global_view.indexSet().size(0));
    auto& global_to_local_inds = *global_to_local_indices_;
    // therefore
    // * create a search on the global view: if all is fine, we only need to walk that one once
    std::vector<FieldVector<ctype, dimDomain>> local_entity_center{FieldVector<ctype, dimDomain>(0.0)};
    auto global_search = XT::Grid::make_entity_in_level_search(global_view);
    // * walk the macro grid
    for (auto&& macro_entity : elements(macro_leaf_view_)) {
      const size_t subd = macro_leaf_view_.indexSet().index(macro_entity);
      const auto local_leaf_view = local_grid(macro_entity).leaf_view();
      const auto& local_index_set = local_leaf_view.indexSet();
      local_to_global_inds[subd] = std::vector<size_t>(local_index_set.size(0));
      // * walk the local grid
      for (auto&& local_entity : elements(local_leaf_view)) {
        const size_t local_entity_index = local_index_set.index(local_entity);
        local_entity_center[0] = local_entity.geometry().center();
        const auto global_entity_ptr_unique_ptrs = global_search(local_entity_center);
        // the search has to be successfull, since the global grid has been constructed to exactly contain each local
        // entity
        assert(global_entity_ptr_unique_ptrs.size() == 1);
        const auto& global_entity_ptr_unique_ptr = global_entity_ptr_unique_ptrs.at(0);
        assert(global_entity_ptr_unique_ptr);
        const auto& global_entity_ptr = *global_entity_ptr_unique_ptr;
        const auto& global_entity = *global_entity_ptr;
        const size_t global_entity_index = global_view.indexSet().index(global_entity);
        // store information
        local_to_global_inds[subd][local_entity_index] = global_entity_index;
        global_to_local_inds[global_entity_index] = {subd, local_entity_index};
      } // * walk the local grid
    } // * walk the macro grid
  } // ... prepare_global_grid(...)

  size_t find_insert_vertex(std::vector<FieldVector<ctype, dimDomain>>& vertices,
                            FieldVector<ctype, dimDomain>&& vertex) const
  {
    // check if vertex is already contained
    for (size_t ii = 0; ii < vertices.size(); ++ii)
      if (XT::Common::FloatCmp::eq(vertex, vertices[ii]))
        return ii;
    // if not, add it
    vertices.emplace_back(std::move(vertex));
    return vertices.size() - 1;
  } // ... find_insert_vertex(...)

  void assert_macro_grid_state() const
  {
    if (macro_leaf_view_.indexSet().size(0) != macro_leaf_view_size_)
      DUNE_THROW(InvalidStateException,
                 "The size of the macro leaf grid view has changed (from "
                     << macro_leaf_view_size_ << " to " << macro_leaf_view_.indexSet().size(0)
                     << "), which invalidates this object!\n"
                     << "Do not adapt the macro grid after instantiating this object!");
  } // void assert_macro_grid_state()

  MacroGridProviderType& macro_grid_;
  const ctype allowed_overlap_;
  MacroGridViewType macro_leaf_view_;
  const size_t macro_leaf_view_size_;
  std::vector<std::shared_ptr<LocalGridProviderType>> local_grids_;
  std::vector<std::map<size_t, std::map<int, std::map<int, std::shared_ptr<GlueType>>>>> glues_;
  std::map<size_t, std::map<int, std::vector<std::pair<MicroEntityType, std::vector<int>>>>>
      macro_entity_to_local_level_to_boundary_entity_ptrs_with_local_intersections_;
  std::unique_ptr<LocalGridProviderType> global_grid_;
  std::unique_ptr<std::vector<std::vector<size_t>>> local_to_global_indices_;
  std::unique_ptr<std::vector<std::pair<size_t, size_t>>> global_to_local_indices_;
}; // class Glued

template <class MacroGridType, class LocalGridType, Layers layer>
class GluedVTKWriter
{
  typedef typename Layer<LocalGridType, layer, Backends::view>::type LocalGridViewType;

  // we only need this class to access a protected pwrite method of VTKWriter
  class LocalVTKWriter : public VTKWriter<LocalGridViewType>
  {
    typedef VTKWriter<LocalGridViewType> BaseType;

  public:
    LocalVTKWriter(const LocalGridViewType& local_grid_view, const size_t subdomain, const size_t num_subdomains)
      : BaseType(local_grid_view)
      , commRank_(boost::numeric_cast<int>(subdomain))
      , commSize_(boost::numeric_cast<int>(num_subdomains))
    {}

    void write_locally(const std::string& name, VTK::OutputType ot)
    {
      BaseType::pwrite(name, "", "", ot, commRank_, commSize_);
    }

  private:
    const int commRank_;
    const int commSize_;
  }; // class LocalVTKWriter

public:
  typedef Glued<MacroGridType, LocalGridType, layer> GluedGridType;

  GluedVTKWriter(const GluedGridType& glued_grid, const int local_level = -1)
    : glued_grid_(glued_grid)
    , local_levels_(glued_grid_.num_subdomains(), -1)
  {
    // set each local level to its respective max
    if (local_level < 0)
      for (auto&& macro_entity : elements(glued_grid_.macro_grid_view()))
        local_levels_[glued_grid_.subdomain(macro_entity)] = glued_grid_.max_local_level(macro_entity);
    prepare_local_vtk_writers();
  } // GluedVTKWriter(...)

  GluedVTKWriter(const GluedGridType& glued_grid, const std::vector<int>& local_levels)
    : glued_grid_(glued_grid)
    , local_levels_(local_levels)
  {
    for (size_t ss = 0; ss < glued_grid_.num_subdomains(); ++ss)
      if (local_levels_[ss] < 0)
        local_levels_[ss] = glued_grid_.max_local_level(ss);
    prepare_local_vtk_writers();
  }

  template <class V>
  void addCellData(const size_t subdomain, const std::vector<V>& vector, const std::string& name, const int ncomps = 1)
  {
    DUNE_THROW_IF(subdomain > glued_grid_.num_subdomains(),
                  Common::Exceptions::index_out_of_range,
                  "subdomain = " << subdomain
                                 << "\n   glued_grid_.num_subdomains() = " << glued_grid_.num_subdomains());
    local_vtk_writers_[subdomain]->addCellData(vector, name, ncomps);
  } // ... addCellData(...)

  template <class V>
  void addCellData(const std::vector<std::vector<V>>& vectors, const std::string& name, const int ncomps = 1)
  {
    DUNE_THROW_IF(vectors.size() != glued_grid_.num_subdomains(),
                  Common::Exceptions::shapes_do_not_match,
                  "vectors.size() =  " << vectors.size()
                                       << "\n   glued_grid_.num_subdomains(): " << glued_grid_.num_subdomains());
    for (size_t ss = 0; ss < glued_grid_.num_subdomains(); ++ss)
      local_vtk_writers_[ss]->addCellData(vectors[ss], name, ncomps);
  } // ... addCellData(...)

  template <class VTKFunctionType>
  void addCellData(const size_t subdomain, const std::shared_ptr<VTKFunctionType>& function, const std::string& name)
  {
    DUNE_THROW_IF(subdomain > glued_grid_.num_subdomains(),
                  Common::Exceptions::index_out_of_range,
                  "subdomain = " << subdomain
                                 << "\n   glued_grid_.num_subdomains() = " << glued_grid_.num_subdomains());
    local_vtk_writers_[subdomain]->addCellData(function, name);
  } // ... addCellData(...)

  template <class VTKFunctionType>
  void addCellData(const std::vector<std::shared_ptr<VTKFunctionType>>& functions, const std::string& name)
  {
    DUNE_THROW_IF(functions.size() != glued_grid_.num_subdomains(),
                  Common::Exceptions::shapes_do_not_match,
                  "functions.size() =  " << functions.size()
                                         << "\n   glued_grid_.num_subdomains(): " << glued_grid_.num_subdomains());
    for (size_t ss = 0; ss < glued_grid_.num_subdomains(); ++ss)
      local_vtk_writers_[ss]->addCellData(functions[ss], name);
  } // ... addCellData(...)

  template <class V>
  void
  addVertexData(const size_t subdomain, const std::vector<V>& vector, const std::string& name, const int ncomps = 1)
  {
    DUNE_THROW_IF(subdomain > glued_grid_.num_subdomains(),
                  Common::Exceptions::index_out_of_range,
                  "subdomain = " << subdomain
                                 << "\n   glued_grid_.num_subdomains() = " << glued_grid_.num_subdomains());
    local_vtk_writers_[subdomain]->addVertexData(vector, name, ncomps);
  } // ... addVertexData(...)

  template <class V>
  void addVertexData(const std::vector<std::vector<V>>& vectors, const std::string& name, const int ncomps = 1)
  {
    DUNE_THROW_IF(vectors.size() != glued_grid_.num_subdomains(),
                  Common::Exceptions::shapes_do_not_match,
                  "vectors.size() =  " << vectors.size()
                                       << "\n   glued_grid_.num_subdomains(): " << glued_grid_.num_subdomains());
    for (size_t ss = 0; ss < glued_grid_.num_subdomains(); ++ss)
      local_vtk_writers_[ss]->addVertexData(vectors[ss], name, ncomps);
  } // ... addVertexData(...)

  template <class VTKFunctionType>
  void addVertexData(const size_t subdomain, const std::shared_ptr<VTKFunctionType>& function)
  {
    DUNE_THROW_IF(subdomain > glued_grid_.num_subdomains(),
                  Common::Exceptions::index_out_of_range,
                  "subdomain = " << subdomain
                                 << "\n   glued_grid_.num_subdomains() = " << glued_grid_.num_subdomains());
    local_vtk_writers_[subdomain]->addVertexData(function);
  } // ... addVertexData(...)

  template <class VTKFunctionType>
  void addVertexData(const std::vector<std::shared_ptr<VTKFunctionType>>& functions)
  {
    DUNE_THROW_IF(functions.size() != glued_grid_.num_subdomains(),
                  Common::Exceptions::shapes_do_not_match,
                  "functions.size() =  " << functions.size()
                                         << "\n   glued_grid_.num_subdomains(): " << glued_grid_.num_subdomains());
    for (size_t ss = 0; ss < glued_grid_.num_subdomains(); ++ss)
      local_vtk_writers_[ss]->addVertexData(functions[ss]);
  } // ... addVertexData(...)

  void clear()
  {
    for (auto& local_vtk_writer : local_vtk_writers_)
      local_vtk_writer->clear();
  }

  void write(const std::string& name, VTK::OutputType type = VTK::ascii)
  {
    for (size_t ss = 0; ss < glued_grid_.num_subdomains(); ++ss)
      local_vtk_writers_[ss]->write_locally(name, type);
  }

private:
  template <Layers l, bool anything = true>
  struct extract_local_view
  {
    template <class G>
    auto operator()(G& g, int /*lv*/) -> decltype(g.leaf_view())
    {
      return g.leaf_view();
    }
  };

  template <bool anything>
  struct extract_local_view<Layers::level, anything>
  {
    template <class G>
    auto operator()(G& g, int lv) -> decltype(g.level_view(lv))
    {
      return g.level_view(lv);
    }
  };

  void prepare_local_vtk_writers()
  {
    for (auto&& macro_entity : elements(glued_grid_.macro_grid_view())) {
      const size_t subdomain = glued_grid_.subdomain(macro_entity);
      local_vtk_writers_.emplace_back(new LocalVTKWriter(
          extract_local_view<layer>()(glued_grid_.local_grid(macro_entity), local_levels_[subdomain]),
          subdomain,
          glued_grid_.num_subdomains()));
    }
  } // ... prepare_local_vtk_writers(...)

  const GluedGridType& glued_grid_;
  std::vector<int> local_levels_;
  std::vector<std::unique_ptr<LocalVTKWriter>> local_vtk_writers_;
}; // class GluedVTKWriter

#else // HAVE_DUNE_GRID_GLUE

template <class MacroGridImp, class LocalGridImp, Layers layer = Layers::level>
class GluedVTKWriter
{
  static_assert(AlwaysFalse<MacroGridImp>::value, "You are missing dune-grid-glue!");
};

template <class MacroGridImp, class LocalGridImp, Layers layer = Layers::level>
class Glued
{
  static_assert(AlwaysFalse<MacroGridImp>::value, "You are missing dune-grid-glue!");
};

#endif // HAVE_DUNE_GRID_GLUE

} // namespace DD
} // namespace Grid
} // namespace XT
} // namespace Dune

#endif // DUNE_XT_GRID_DD_GLUED_HH


//=======================================================================
// Copyright 2007 Aaron Windsor
//
// 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 __MAKE_MAXIMAL_PLANAR_HPP__
#define __MAKE_MAXIMAL_PLANAR_HPP__

#include <boost/config.hpp>
#include <boost/tuple/tuple.hpp>   //for tie
#include <boost/graph/biconnected_components.hpp>
#include <boost/property_map/property_map.hpp>
#include <vector>
#include <iterator>
#include <algorithm>

#include <boost/graph/planar_face_traversal.hpp>
#include <boost/graph/planar_detail/add_edge_visitors.hpp>

namespace boost
{

  template <typename Graph, typename VertexIndexMap, typename AddEdgeVisitor>
  struct triangulation_visitor : public planar_face_traversal_visitor
  {

    typedef typename graph_traits<Graph>::vertex_descriptor vertex_t;
    typedef typename graph_traits<Graph>::edge_descriptor edge_t;
    typedef typename graph_traits<Graph>::vertices_size_type v_size_t;
    typedef typename graph_traits<Graph>::degree_size_type degree_size_t;
    typedef typename graph_traits<Graph>::edge_iterator edge_iterator_t;
    typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator_t;
    typedef typename graph_traits<Graph>::adjacency_iterator
      adjacency_iterator_t;
    typedef typename std::vector<vertex_t> vertex_vector_t;
    typedef typename std::vector<v_size_t> v_size_vector_t;
    typedef typename std::vector<degree_size_t> degree_size_vector_t;
    typedef iterator_property_map
      < typename v_size_vector_t::iterator, VertexIndexMap >
      vertex_to_v_size_map_t;
    typedef iterator_property_map
      < typename degree_size_vector_t::iterator, VertexIndexMap >
      vertex_to_degree_size_map_t;
    typedef typename vertex_vector_t::iterator face_iterator;

    triangulation_visitor(Graph& arg_g,
                          VertexIndexMap arg_vm,
                          AddEdgeVisitor arg_add_edge_visitor
                          ) :
      g(arg_g),
      vm(arg_vm),
      add_edge_visitor(arg_add_edge_visitor),
      timestamp(0),
      marked_vector(num_vertices(g), timestamp),
      degree_vector(num_vertices(g), 0),
      marked(marked_vector.begin(), vm),
      degree(degree_vector.begin(), vm)
    {
      vertex_iterator_t vi, vi_end;
      for(tie(vi,vi_end) = vertices(g); vi != vi_end; ++vi)
        put(degree, *vi, out_degree(*vi, g));
    }

    template <typename Vertex>
    void next_vertex(Vertex v)
    {
      // Self-loops will appear as consecutive vertices in the list of
      // vertices on a face. We want to skip these.
      if (!vertices_on_face.empty() &&
          (vertices_on_face.back() == v || vertices_on_face.front() == v)
          )
        return;

      vertices_on_face.push_back(v);
    }

    void end_face()
    {
      ++timestamp;

      if (vertices_on_face.size() <= 3)
        {
          // At most three vertices on this face - don't need to triangulate
          vertices_on_face.clear();
          return;
        }

      // Find vertex on face of minimum degree
      degree_size_t min_degree = num_vertices(g);
      typename vertex_vector_t::iterator min_degree_vertex_itr;
      face_iterator fi_end = vertices_on_face.end();
      for(face_iterator fi = vertices_on_face.begin(); fi != fi_end; ++fi)
        {
          degree_size_t deg = get(degree,*fi);
          if (deg < min_degree)
            {
              min_degree_vertex_itr = fi;
              min_degree = deg;
            }
        }

      // To simplify some of the manipulations, we'll re-arrange
      // vertices_on_face so that it still contains the same
      // (counter-clockwise) order of the vertices on this face, but now the
      // min_degree_vertex is the first element in vertices_on_face.
      vertex_vector_t temp_vector;
      std::copy(min_degree_vertex_itr, vertices_on_face.end(),
                std::back_inserter(temp_vector));
      std::copy(vertices_on_face.begin(), min_degree_vertex_itr,
                std::back_inserter(temp_vector));
      vertices_on_face.swap(temp_vector);

      // Mark all of the min degree vertex's neighbors
      adjacency_iterator_t ai, ai_end;
      for(tie(ai,ai_end) = adjacent_vertices(vertices_on_face.front(),g);
          ai != ai_end; ++ai
          )
        {
          put(marked, *ai, timestamp);
        }

      typename vertex_vector_t::iterator marked_neighbor
        = vertices_on_face.end();

      // The iterator manipulations on the next two lines are safe because
      // vertices_on_face.size() > 3 (from the first test in this function)
      fi_end = prior(vertices_on_face.end());
      for(face_iterator fi = boost::next(boost::next(vertices_on_face.begin()));
          fi != fi_end; ++fi
          )
        {
          if (get(marked, *fi) == timestamp)
            {
              marked_neighbor = fi;
              break;
            }
        }

      if (marked_neighbor == vertices_on_face.end())
        {
          add_edge_range(
                         vertices_on_face[0],
                         boost::next(boost::next(vertices_on_face.begin())),
                         prior(vertices_on_face.end())
                         );
        }
      else
        {
          add_edge_range(
                         vertices_on_face[1],
                         boost::next(marked_neighbor),
                         vertices_on_face.end()
                         );

          add_edge_range(
                         *boost::next(marked_neighbor),
                         boost::next(boost::next(vertices_on_face.begin())),
                         marked_neighbor
                         );
        }

      //reset for the next face
      vertices_on_face.clear();

    }

  private:

    void add_edge_range(vertex_t anchor,
                        face_iterator fi,
                        face_iterator fi_end
                        )
    {
      for (; fi != fi_end; ++fi)
        {
          vertex_t v(*fi);
          add_edge_visitor.visit_vertex_pair(anchor, v, g);
          put(degree, anchor, get(degree, anchor) + 1);
          put(degree, v, get(degree, v) + 1);
        }
    }

    Graph& g;
    VertexIndexMap vm;
    AddEdgeVisitor add_edge_visitor;
    v_size_t timestamp;
    vertex_vector_t vertices_on_face;
    v_size_vector_t marked_vector;
    degree_size_vector_t degree_vector;
    vertex_to_v_size_map_t marked;
    vertex_to_degree_size_map_t degree;

  };

  template <typename Graph,
            typename PlanarEmbedding,
            typename VertexIndexMap,
            typename EdgeIndexMap,
            typename AddEdgeVisitor
            >
  void make_maximal_planar(Graph& g,
                           PlanarEmbedding embedding,
                           VertexIndexMap vm,
                           EdgeIndexMap em,
                           AddEdgeVisitor& vis)
  {
    triangulation_visitor<Graph,VertexIndexMap,AddEdgeVisitor>
      visitor(g, vm, vis);
    planar_face_traversal(g, embedding, visitor, em);
  }

  template <typename Graph,
            typename PlanarEmbedding,
            typename VertexIndexMap,
            typename EdgeIndexMap
            >
  void make_maximal_planar(Graph& g,
                           PlanarEmbedding embedding,
                           VertexIndexMap vm,
                           EdgeIndexMap em
                           )
  {
    default_add_edge_visitor vis;
    make_maximal_planar(g, embedding, vm, em, vis);
  }

  template <typename Graph,
            typename PlanarEmbedding,
            typename VertexIndexMap
            >
  void make_maximal_planar(Graph& g,
                           PlanarEmbedding embedding,
                           VertexIndexMap vm
                           )
  {
    make_maximal_planar(g, embedding, vm, get(edge_index,g));
  }

  template <typename Graph,
            typename PlanarEmbedding
            >
  void make_maximal_planar(Graph& g,
                           PlanarEmbedding embedding
                           )
  {
    make_maximal_planar(g, embedding, get(vertex_index,g));
  }

} // namespace boost

#endif //__MAKE_MAXIMAL_PLANAR_HPP__


// Copyright Louis Dionne 2013

// Use, modification and distribution is subject to 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_GRAPH_HAWICK_CIRCUITS_HPP
#define BOOST_GRAPH_HAWICK_CIRCUITS_HPP

#include <algorithm>
#include <boost/assert.hpp>
#include <boost/foreach.hpp>
#include <boost/graph/graph_traits.hpp>
#include <boost/graph/one_bit_color_map.hpp>
#include <boost/graph/properties.hpp>
#include <boost/move/utility.hpp>
#include <boost/property_map/property_map.hpp>
#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>
#include <boost/range/iterator.hpp>
#include <boost/tuple/tuple.hpp> // for boost::tie
#include <boost/type_traits/remove_reference.hpp>
#include <boost/utility/result_of.hpp>
#include <set>
#include <utility> // for std::pair
#include <vector>

namespace boost
{
namespace hawick_circuits_detail
{
    //! @internal Functor returning all the vertices adjacent to a vertex.
    struct get_all_adjacent_vertices
    {
        template < typename Sig > struct result;

        template < typename This, typename Vertex, typename Graph >
        struct result< This(Vertex, Graph) >
        {
        private:
            typedef typename remove_reference< Graph >::type RawGraph;
            typedef graph_traits< RawGraph > Traits;
            typedef typename Traits::adjacency_iterator AdjacencyIterator;

        public:
            typedef std::pair< AdjacencyIterator, AdjacencyIterator > type;
        };

        template < typename Vertex, typename Graph >
        typename result< get_all_adjacent_vertices(
            BOOST_FWD_REF(Vertex), BOOST_FWD_REF(Graph)) >::type
        operator()(BOOST_FWD_REF(Vertex) v, BOOST_FWD_REF(Graph) g) const
        {
            return adjacent_vertices(
                boost::forward< Vertex >(v), boost::forward< Graph >(g));
        }
    };

    //! @internal Functor returning a set of the vertices adjacent to a vertex.
    struct get_unique_adjacent_vertices
    {
        template < typename Sig > struct result;

        template < typename This, typename Vertex, typename Graph >
        struct result< This(Vertex, Graph) >
        {
            typedef std::set< typename remove_reference< Vertex >::type > type;
        };

        template < typename Vertex, typename Graph >
        typename result< get_unique_adjacent_vertices(
            Vertex, Graph const&) >::type
        operator()(Vertex v, Graph const& g) const
        {
            typedef typename result< get_unique_adjacent_vertices(
                Vertex, Graph const&) >::type Set;
            return Set(
                adjacent_vertices(v, g).first, adjacent_vertices(v, g).second);
        }
    };

    //! @internal
    //! Return whether a container contains a given value.
    //! This is not meant as a general purpose membership testing function; it
    //! would have to be more clever about possible optimizations.
    template < typename Container, typename Value >
    bool contains(Container const& c, Value const& v)
    {
        return std::find(boost::begin(c), boost::end(c), v) != boost::end(c);
    }

    /*!
     * @internal
     * Algorithm finding all the cycles starting from a given vertex.
     *
     * The search is only done in the subgraph induced by the starting vertex
     * and the vertices with an index higher than the starting vertex.
     */
    template < typename Graph, typename Visitor, typename VertexIndexMap,
        typename Stack, typename ClosedMatrix, typename GetAdjacentVertices >
    struct hawick_circuits_from
    {
    private:
        typedef graph_traits< Graph > Traits;
        typedef typename Traits::vertex_descriptor Vertex;
        typedef typename Traits::edge_descriptor Edge;
        typedef typename Traits::vertices_size_type VerticesSize;
        typedef
            typename property_traits< VertexIndexMap >::value_type VertexIndex;

        typedef typename result_of< GetAdjacentVertices(
            Vertex, Graph const&) >::type AdjacentVertices;
        typedef typename range_iterator< AdjacentVertices const >::type
            AdjacencyIterator;

        // The one_bit_color_map starts all white, i.e. not blocked.
        // Since we make that assumption (I looked at the implementation, but
        // I can't find anything that documents this behavior), we're gonna
        // assert it in the constructor.
        typedef one_bit_color_map< VertexIndexMap > BlockedMap;
        typedef typename property_traits< BlockedMap >::value_type BlockedColor;

        static BlockedColor blocked_false_color()
        {
            return color_traits< BlockedColor >::white();
        }

        static BlockedColor blocked_true_color()
        {
            return color_traits< BlockedColor >::black();
        }

        // This is used by the constructor to secure the assumption
        // documented above.
        bool blocked_map_starts_all_unblocked() const
        {
            BOOST_FOREACH (Vertex v, vertices(graph_))
                if (is_blocked(v))
                    return false;
            return true;
        }

        // This is only used in the constructor to make sure the optimization of
        // sharing data structures between iterations does not break the code.
        bool all_closed_rows_are_empty() const
        {
            BOOST_FOREACH (typename ClosedMatrix::reference row, closed_)
                if (!row.empty())
                    return false;
            return true;
        }

    public:
        hawick_circuits_from(Graph const& graph, Visitor& visitor,
            VertexIndexMap const& vim, Stack& stack, ClosedMatrix& closed,
            VerticesSize n_vertices)
        : graph_(graph)
        , visitor_(visitor)
        , vim_(vim)
        , stack_(stack)
        , closed_(closed)
        , blocked_(n_vertices, vim_)
        {
            BOOST_ASSERT(blocked_map_starts_all_unblocked());

            // Since sharing the data structures between iterations is
            // just an optimization, it must always be equivalent to
            // constructing new ones in this constructor.
            BOOST_ASSERT(stack_.empty());
            BOOST_ASSERT(closed_.size() == n_vertices);
            BOOST_ASSERT(all_closed_rows_are_empty());
        }

    private:
        //! @internal Return the index of a given vertex.
        VertexIndex index_of(Vertex v) const { return get(vim_, v); }

        //! @internal Return whether a vertex `v` is closed to a vertex `u`.
        bool is_closed_to(Vertex u, Vertex v) const
        {
            typedef typename ClosedMatrix::const_reference VertexList;
            VertexList closed_to_u = closed_[index_of(u)];
            return contains(closed_to_u, v);
        }

        //! @internal Close a vertex `v` to a vertex `u`.
        void close_to(Vertex u, Vertex v)
        {
            BOOST_ASSERT(!is_closed_to(u, v));
            closed_[index_of(u)].push_back(v);
        }

        //! @internal Return whether a given vertex is blocked.
        bool is_blocked(Vertex v) const
        {
            return get(blocked_, v) == blocked_true_color();
        }

        //! @internal Block a given vertex.
        void block(Vertex v) { put(blocked_, v, blocked_true_color()); }

        //! @internal Unblock a given vertex.
        void unblock(Vertex u)
        {
            typedef typename ClosedMatrix::reference VertexList;

            put(blocked_, u, blocked_false_color());
            VertexList closed_to_u = closed_[index_of(u)];

            while (!closed_to_u.empty())
            {
                Vertex const w = closed_to_u.back();
                closed_to_u.pop_back();
                if (is_blocked(w))
                    unblock(w);
            }
            BOOST_ASSERT(closed_to_u.empty());
        }

        //! @internal Main procedure as described in the paper.
        bool circuit(Vertex start, Vertex v)
        {
            bool found_circuit = false;
            stack_.push_back(v);
            block(v);

            // Cache some values that are used more than once in the function.
            VertexIndex const index_of_start = index_of(start);
            AdjacentVertices const adj_vertices
                = GetAdjacentVertices()(v, graph_);
            AdjacencyIterator const w_end = boost::end(adj_vertices);

            for (AdjacencyIterator w_it = boost::begin(adj_vertices);
                 w_it != w_end; ++w_it)
            {
                Vertex const w = *w_it;
                // Since we're only looking in the subgraph induced by `start`
                // and the vertices with an index higher than `start`, we skip
                // any vertex that does not satisfy that.
                if (index_of(w) < index_of_start)
                    continue;

                // If the last vertex is equal to `start`, we have a circuit.
                else if (w == start)
                {
                    // const_cast to ensure the visitor does not modify the
                    // stack
                    visitor_.cycle(const_cast< Stack const& >(stack_), graph_);
                    found_circuit = true;
                }

                // If `w` is not blocked, we continue searching further down the
                // same path for a cycle with `w` in it.
                else if (!is_blocked(w) && circuit(start, w))
                    found_circuit = true;
            }

            if (found_circuit)
                unblock(v);
            else
                for (AdjacencyIterator w_it = boost::begin(adj_vertices);
                     w_it != w_end; ++w_it)
                {
                    Vertex const w = *w_it;
                    // Like above, we skip vertices that are not in the subgraph
                    // we're considering.
                    if (index_of(w) < index_of_start)
                        continue;

                    // If `v` is not closed to `w`, we make it so.
                    if (!is_closed_to(w, v))
                        close_to(w, v);
                }

            BOOST_ASSERT(v == stack_.back());
            stack_.pop_back();
            return found_circuit;
        }

    public:
        void operator()(Vertex start) { circuit(start, start); }

    private:
        Graph const& graph_;
        Visitor& visitor_;
        VertexIndexMap const& vim_;
        Stack& stack_;
        ClosedMatrix& closed_;
        BlockedMap blocked_;
    };

    template < typename GetAdjacentVertices, typename Graph, typename Visitor,
        typename VertexIndexMap >
    void call_hawick_circuits(Graph const& graph,
        Visitor /* by value */ visitor, VertexIndexMap const& vertex_index_map)
    {
        typedef graph_traits< Graph > Traits;
        typedef typename Traits::vertex_descriptor Vertex;
        typedef typename Traits::vertices_size_type VerticesSize;
        typedef typename Traits::vertex_iterator VertexIterator;

        typedef std::vector< Vertex > Stack;
        typedef std::vector< std::vector< Vertex > > ClosedMatrix;

        typedef hawick_circuits_from< Graph, Visitor, VertexIndexMap, Stack,
            ClosedMatrix, GetAdjacentVertices >
            SubAlgorithm;

        VerticesSize const n_vertices = num_vertices(graph);
        Stack stack;
        stack.reserve(n_vertices);
        ClosedMatrix closed(n_vertices);

        VertexIterator start, last;
        for (boost::tie(start, last) = vertices(graph); start != last; ++start)
        {
            // Note1: The sub algorithm may NOT be reused once it has been
            // called.

            // Note2: We reuse the Stack and the ClosedMatrix (after clearing
            // them) in each iteration to avoid redundant destruction and
            // construction. It would be strictly equivalent to have these as
            // member variables of the sub algorithm.
            SubAlgorithm sub_algo(
                graph, visitor, vertex_index_map, stack, closed, n_vertices);
            sub_algo(*start);
            stack.clear();
            typename ClosedMatrix::iterator row, last_row = closed.end();
            for (row = closed.begin(); row != last_row; ++row)
                row->clear();
        }
    }

    template < typename GetAdjacentVertices, typename Graph, typename Visitor >
    void call_hawick_circuits(
        Graph const& graph, BOOST_FWD_REF(Visitor) visitor)
    {
        call_hawick_circuits< GetAdjacentVertices >(graph,
            boost::forward< Visitor >(visitor), get(vertex_index, graph));
    }
} // end namespace hawick_circuits_detail

//! Enumerate all the elementary circuits in a directed multigraph.
template < typename Graph, typename Visitor, typename VertexIndexMap >
void hawick_circuits(BOOST_FWD_REF(Graph) graph, BOOST_FWD_REF(Visitor) visitor,
    BOOST_FWD_REF(VertexIndexMap) vertex_index_map)
{
    hawick_circuits_detail::call_hawick_circuits<
        hawick_circuits_detail::get_all_adjacent_vertices >(
        boost::forward< Graph >(graph), boost::forward< Visitor >(visitor),
        boost::forward< VertexIndexMap >(vertex_index_map));
}

template < typename Graph, typename Visitor >
void hawick_circuits(BOOST_FWD_REF(Graph) graph, BOOST_FWD_REF(Visitor) visitor)
{
    hawick_circuits_detail::call_hawick_circuits<
        hawick_circuits_detail::get_all_adjacent_vertices >(
        boost::forward< Graph >(graph), boost::forward< Visitor >(visitor));
}

/*!
 * Same as `boost::hawick_circuits`, but duplicate circuits caused by parallel
 * edges will not be considered. Each circuit will be considered only once.
 */
template < typename Graph, typename Visitor, typename VertexIndexMap >
void hawick_unique_circuits(BOOST_FWD_REF(Graph) graph,
    BOOST_FWD_REF(Visitor) visitor,
    BOOST_FWD_REF(VertexIndexMap) vertex_index_map)
{
    hawick_circuits_detail::call_hawick_circuits<
        hawick_circuits_detail::get_unique_adjacent_vertices >(
        boost::forward< Graph >(graph), boost::forward< Visitor >(visitor),
        boost::forward< VertexIndexMap >(vertex_index_map));
}

template < typename Graph, typename Visitor >
void hawick_unique_circuits(
    BOOST_FWD_REF(Graph) graph, BOOST_FWD_REF(Visitor) visitor)
{
    hawick_circuits_detail::call_hawick_circuits<
        hawick_circuits_detail::get_unique_adjacent_vertices >(
        boost::forward< Graph >(graph), boost::forward< Visitor >(visitor));
}
} // end namespace boost

#endif // !BOOST_GRAPH_HAWICK_CIRCUITS_HPP