/*
LICENSE: see isogeometric_application/LICENSE.txt
*/

//
//   Project Name:        Kratos
//   Last modified by:    $Author: hbui $
//   Date:                $Date: Nov 11, 2017 $
//   Revision:            $Revision: 1.1 $
//
//

// System includes
#include <string>

// External includes
#include <boost/foreach.hpp>
#include <boost/python.hpp>
#include <boost/python/stl_iterator.hpp>
#include <boost/python/operators.hpp>

// Project includes
#include "includes/define.h"
#include "custom_utilities/control_point.h"
#include "custom_utilities/control_grid.h"
#include "custom_utilities/control_grid_library.h"
#include "custom_utilities/control_grid_utility.h"
#include "custom_utilities/tsplines/tcell.h"
#include "custom_utilities/nurbs/pbbsplines_basis_function.h"
#include "custom_utilities/nurbs/pbbsplines_fespace.h"
#include "custom_utilities/hbsplines/hbsplines_basis_function.h"
#include "custom_utilities/hbsplines/hbsplines_fespace.h"
#include "custom_python/add_utilities_to_python.h"
#include "custom_python/add_control_grids_to_python.h"

namespace Kratos
{

namespace Python
{

using namespace boost::python;

ControlGrid<ControlPoint<double> >::Pointer ControlGridLibrary_CreateLinearControlPointGrid(
        ControlGridLibrary& rDummy,
        const double& start_x, const double& start_y, const double& start_z,
        const std::size_t& n_points_u,
        const double& end_x, const double& end_y, const double& end_z)
{
    std::vector<double> start(3);
    start[0] = start_x;
    start[1] = start_y;
    start[2] = start_z;

    std::vector<std::size_t> ngrid(1);
    ngrid[0] = n_points_u;

    std::vector<double> end(3);
    end[0] = end_x;
    end[1] = end_y;
    end[2] = end_z;

    return rDummy.CreateStructuredControlPointGrid<1>(start, ngrid, end);
}

ControlGrid<ControlPoint<double> >::Pointer ControlGridLibrary_CreateRectangularControlPointGrid1(
        ControlGridLibrary& rDummy,
        const double& start_x, const double& start_y,
        const std::size_t& n_points_u, const std::size_t& n_points_v,
        const double& end_x, const double& end_y)
{
    std::vector<double> start(2);
    start[0] = start_x;
    start[1] = start_y;

    std::vector<std::size_t> ngrid(2);
    ngrid[0] = n_points_u;
    ngrid[1] = n_points_v;

    std::vector<double> end(2);
    end[0] = end_x;
    end[1] = end_y;

    return rDummy.CreateStructuredControlPointGrid<2>(start, ngrid, end);
}

ControlGrid<ControlPoint<double> >::Pointer ControlGridLibrary_CreateRectangularControlPointGrid2(
        ControlGridLibrary& rDummy,
        const double& start_x, const double& start_y, const double& start_z,
        const std::size_t& n_points_u, const std::size_t& n_points_v,
        const double& space1_x, const double& space1_y, const double& space1_z,
        const double& space2_x, const double& space2_y, const double& space2_z)
{
    std::vector<double> start(3);
    start[0] = start_x;
    start[1] = start_y;
    start[2] = start_z;

    std::vector<std::size_t> ngrid(2);
    ngrid[0] = n_points_u;
    ngrid[1] = n_points_v;

    std::vector<double> space1(3);
    space1[0] = space1_x;
    space1[1] = space1_y;
    space1[2] = space1_z;

    std::vector<double> space2(3);
    space2[0] = space2_x;
    space2[1] = space2_y;
    space2[2] = space2_z;

    std::vector<std::vector<double> > spacing_vectors(2);
    spacing_vectors[0] = space1;
    spacing_vectors[1] = space2;

    return rDummy.CreateStructuredControlPointGrid<2>(start, ngrid, spacing_vectors);
}

ControlGrid<ControlPoint<double> >::Pointer ControlGridLibrary_CreateCubicControlPointGrid1(
        ControlGridLibrary& rDummy,
        const double& start_x, const double& start_y, const double& start_z,
        const std::size_t& n_points_u, const std::size_t& n_points_v, const std::size_t& n_points_w,
        const double& end_x, const double& end_y, const double& end_z)
{
    std::vector<double> start(3);
    start[0] = start_x;
    start[1] = start_y;
    start[2] = start_z;

    std::vector<std::size_t> ngrid(3);
    ngrid[0] = n_points_u;
    ngrid[1] = n_points_v;
    ngrid[2] = n_points_w;

    std::vector<double> end(3);
    end[0] = end_x;
    end[1] = end_y;
    end[2] = end_z;

    return rDummy.CreateStructuredControlPointGrid<3>(start, ngrid, end);
}

ControlGrid<ControlPoint<double> >::Pointer ControlGridLibrary_CreateCubicControlPointGrid2(
        ControlGridLibrary& rDummy,
        const double& start_x, const double& start_y, const double& start_z,
        const std::size_t& n_points_u, const std::size_t& n_points_v, const std::size_t& n_points_w,
        boost::python::list spacing_vectors_data)
{
    std::vector<double> start(3);
    start[0] = start_x;
    start[1] = start_y;
    start[2] = start_z;

    std::vector<std::size_t> ngrid(3);
    ngrid[0] = n_points_u;
    ngrid[1] = n_points_v;
    ngrid[2] = n_points_w;

    std::vector<std::vector<double> > spacing_vectors;
    std::size_t cnt1 = 0, cnt2 = 0;
    typedef boost::python::stl_input_iterator<boost::python::list> iterator_value_type;
    BOOST_FOREACH(const iterator_value_type::value_type& vect, std::make_pair(iterator_value_type(spacing_vectors_data), iterator_value_type() ) )
    {
        typedef boost::python::stl_input_iterator<double> iterator_value_type2;
        std::vector<double> space_vect;
        BOOST_FOREACH(const iterator_value_type2::value_type& v, std::make_pair(iterator_value_type2(vect), iterator_value_type2() ) )
        {
            space_vect.push_back(v);
        }
        spacing_vectors.push_back(space_vect);
    }

    return rDummy.CreateStructuredControlPointGrid<3>(start, ngrid, spacing_vectors);
}

////////////////////////////////////////

template<class TVariableType>
inline typename ControlGrid<typename TVariableType::Type>::Pointer ControlGridLibrary_CreateLinearZeroControlGridWithVariable(
        ControlGridLibrary& rDummy,
        const TVariableType& rVariable,
        const std::size_t& n_points_u)
{
    std::vector<std::size_t> ngrid(1);
    ngrid[0] = n_points_u;
    return rDummy.CreateStructuredZeroControlGrid<1, TVariableType>(rVariable, ngrid);
}

template<class TVariableType>
inline typename ControlGrid<typename TVariableType::Type>::Pointer ControlGridLibrary_CreateRectangularZeroControlGridWithVariable(
        ControlGridLibrary& rDummy,
        const TVariableType& rVariable,
        const std::size_t& n_points_u, const std::size_t& n_points_v)
{
    std::vector<std::size_t> ngrid(2);
    ngrid[0] = n_points_u;
    ngrid[1] = n_points_v;
    return rDummy.CreateStructuredZeroControlGrid<2, TVariableType>(rVariable, ngrid);
}

template<class TVariableType>
inline typename ControlGrid<typename TVariableType::Type>::Pointer ControlGridLibrary_CreateCubicZeroControlGridWithVariable(
        ControlGridLibrary& rDummy,
        const TVariableType& rVariable,
        const std::size_t& n_points_u, const std::size_t& n_points_v, const std::size_t& n_points_w)
{
    std::vector<std::size_t> ngrid(3);
    ngrid[0] = n_points_u;
    ngrid[1] = n_points_v;
    ngrid[2] = n_points_w;
    return rDummy.CreateStructuredZeroControlGrid<3, TVariableType>(rVariable, ngrid);
}

template<typename TDataType, class TFESpaceType>
inline typename ControlGrid<TDataType>::Pointer ControlGridUtility_CreatePointBasedControlGrid(
        ControlGridUtility& rDummy,
        const Variable<TDataType>& rVariable, typename TFESpaceType::Pointer pFESpace)
{
    return rDummy.CreatePointBasedControlGrid<TDataType, TFESpaceType>(rVariable, pFESpace);
}

////////////////////////////////////////

void IsogeometricApplication_AddControlGridsToPython()
{
    /////////////////////////////////////////////////////////////////
    ///////////////////////CONTROL GRIDS/////////////////////////////
    /////////////////////////////////////////////////////////////////

    class_<ControlGrid<ControlPoint<double> >, ControlGrid<ControlPoint<double> >::Pointer, boost::noncopyable>
    ("ControlPointControlGrid", init<>())
    .def("Size", &ControlGrid<ControlPoint<double> >::Size)
    .def("size", &ControlGrid<ControlPoint<double> >::Size)
    .def("__setitem__", &ControlGrid_SetItem<ControlPoint<double> >)
    .def("__getitem__", &ControlGrid_GetItem<ControlPoint<double> >)
    .def(self_ns::str(self))
    ;

    class_<ControlGrid<double>, ControlGrid<double>::Pointer, boost::noncopyable>
    ("DoubleControlGrid", init<>())
    .def("Size", &ControlGrid<double>::Size)
    .def("size", &ControlGrid<double>::Size)
    .def("__setitem__", &ControlGrid_SetItem<double>)
    .def("__getitem__", &ControlGrid_GetItem<double>)
    .def(self_ns::str(self))
    ;

    class_<ControlGrid<array_1d<double, 3> >, ControlGrid<array_1d<double, 3> >::Pointer, boost::noncopyable>
    ("Array1DControlGrid", init<>())
    .def("Size", &ControlGrid<array_1d<double, 3> >::Size)
    .def("size", &ControlGrid<array_1d<double, 3> >::Size)
    .def("__setitem__", &ControlGrid_SetItem<array_1d<double, 3> >)
    .def("__getitem__", &ControlGrid_GetItem<array_1d<double, 3> >)
    .def(self_ns::str(self))
    ;

    class_<ControlGrid<Vector>, ControlGrid<Vector>::Pointer, boost::noncopyable>
    ("VectorControlGrid", init<>())
    .def("Size", &ControlGrid<Vector>::Size)
    .def("size", &ControlGrid<Vector>::Size)
    .def("__setitem__", &ControlGrid_SetItem<Vector>)
    .def("__getitem__", &ControlGrid_GetItem<Vector>)
    .def(self_ns::str(self))
    ;

    /////////////////////////////////////////////////////////////////////////////////////////////////

    class_<UnstructuredControlGrid<ControlPoint<double> >, UnstructuredControlGrid<ControlPoint<double> >::Pointer, bases<ControlGrid<ControlPoint<double> > >, boost::noncopyable>
    ("UnstructuredControlPointGrid", init<const std::size_t&>())
    .def(self_ns::str(self))
    ;

    class_<UnstructuredControlGrid<double>, UnstructuredControlGrid<double>::Pointer, bases<ControlGrid<double> >, boost::noncopyable>
    ("UnstructuredDoubleControlGrid", init<const std::size_t&>())
    .def(self_ns::str(self))
    ;

    class_<UnstructuredControlGrid<array_1d<double, 3> >, UnstructuredControlGrid<array_1d<double, 3> >::Pointer, bases<ControlGrid<array_1d<double, 3> > >, boost::noncopyable>
    ("UnstructuredArray1DControlGrid", init<const std::size_t&>())
    .def(self_ns::str(self))
    ;

    class_<UnstructuredControlGrid<Vector>, UnstructuredControlGrid<Vector>::Pointer, bases<ControlGrid<Vector> >, boost::noncopyable>
    ("UnstructuredVectorControlGrid", init<const std::size_t&>())
    .def(self_ns::str(self))
    ;

    /////////////////////////////////////////////////////////////////////////////////////////////////

    class_<ControlGridLibrary, ControlGridLibrary::Pointer, boost::noncopyable>
    ("ControlGridLibrary", init<>())
    .def("CreateLinearControlPointGrid", &ControlGridLibrary_CreateLinearControlPointGrid)
    .def("CreateRectangularControlPointGrid", &ControlGridLibrary_CreateRectangularControlPointGrid1)
    .def("CreateRectangularControlPointGrid", &ControlGridLibrary_CreateRectangularControlPointGrid2)
    .def("CreateCubicControlPointGrid", &ControlGridLibrary_CreateCubicControlPointGrid1)
    .def("CreateCubicControlPointGrid", &ControlGridLibrary_CreateCubicControlPointGrid2)
    .def("CreateLinearZeroDoubleControlGrid", &ControlGridLibrary_CreateLinearZeroControlGridWithVariable<Variable<double> >)
    .def("CreateRectangularZeroDoubleControlGrid", &ControlGridLibrary_CreateRectangularZeroControlGridWithVariable<Variable<double> >)
    .def("CreateCubicZeroDoubleControlGrid", &ControlGridLibrary_CreateCubicZeroControlGridWithVariable<Variable<double> >)
    .def("CreateLinearZeroArray1DControlGrid", &ControlGridLibrary_CreateLinearZeroControlGridWithVariable<Variable<array_1d<double, 3> > >)
    .def("CreateRectangularZeroArray1DControlGrid", &ControlGridLibrary_CreateRectangularZeroControlGridWithVariable<Variable<array_1d<double, 3> > >)
    .def("CreateCubicZeroArray1DControlGrid", &ControlGridLibrary_CreateCubicZeroControlGridWithVariable<Variable<array_1d<double, 3> > >)
    ;

    /////////////////////////////////////////////////////////////////////////////////////////////////

    /////////////////////////////////////////////////////////////////
    ///////////////////////CONTROL GRID UTILITIY/////////////////////
    /////////////////////////////////////////////////////////////////

    class_<ControlGridUtility, ControlGridUtility::Pointer, boost::noncopyable>
    ("PointBasedControlGridUtility", init<>())
    .def("CreatePointBasedControlGrid", &ControlGridUtility_CreatePointBasedControlGrid<double, PBBSplinesFESpace<1, PBBSplinesBasisFunction<1, TCell>, BCellManager<1, TCell> > >)
    .def("CreatePointBasedControlGrid", &ControlGridUtility_CreatePointBasedControlGrid<double, PBBSplinesFESpace<2, PBBSplinesBasisFunction<2, TCell>, BCellManager<2, TCell> > >)
    .def("CreatePointBasedControlGrid", &ControlGridUtility_CreatePointBasedControlGrid<double, PBBSplinesFESpace<3, PBBSplinesBasisFunction<3, TCell>, BCellManager<3, TCell> > >)
    .def("CreatePointBasedControlGrid", &ControlGridUtility_CreatePointBasedControlGrid<array_1d<double, 3>, PBBSplinesFESpace<1, PBBSplinesBasisFunction<1, TCell>, BCellManager<1, TCell> > >)
    .def("CreatePointBasedControlGrid", &ControlGridUtility_CreatePointBasedControlGrid<array_1d<double, 3>, PBBSplinesFESpace<2, PBBSplinesBasisFunction<2, TCell>, BCellManager<2, TCell> > >)
    .def("CreatePointBasedControlGrid", &ControlGridUtility_CreatePointBasedControlGrid<array_1d<double, 3>, PBBSplinesFESpace<3, PBBSplinesBasisFunction<3, TCell>, BCellManager<3, TCell> > >)
    .def("CreatePointBasedControlGrid", &ControlGridUtility_CreatePointBasedControlGrid<Vector, PBBSplinesFESpace<1, PBBSplinesBasisFunction<1, TCell>, BCellManager<1, TCell> > >)
    .def("CreatePointBasedControlGrid", &ControlGridUtility_CreatePointBasedControlGrid<Vector, PBBSplinesFESpace<2, PBBSplinesBasisFunction<2, TCell>, BCellManager<2, TCell> > >)
    .def("CreatePointBasedControlGrid", &ControlGridUtility_CreatePointBasedControlGrid<Vector, PBBSplinesFESpace<3, PBBSplinesBasisFunction<3, TCell>, BCellManager<3, TCell> > >)
    ////
    .def("CreatePointBasedControlGrid", &ControlGridUtility_CreatePointBasedControlGrid<double, HBSplinesFESpace<1> >)
    .def("CreatePointBasedControlGrid", &ControlGridUtility_CreatePointBasedControlGrid<double, HBSplinesFESpace<2> >)
    .def("CreatePointBasedControlGrid", &ControlGridUtility_CreatePointBasedControlGrid<double, HBSplinesFESpace<3> >)
    .def("CreatePointBasedControlGrid", &ControlGridUtility_CreatePointBasedControlGrid<array_1d<double, 3>, HBSplinesFESpace<1> >)
    .def("CreatePointBasedControlGrid", &ControlGridUtility_CreatePointBasedControlGrid<array_1d<double, 3>, HBSplinesFESpace<2> >)
    .def("CreatePointBasedControlGrid", &ControlGridUtility_CreatePointBasedControlGrid<array_1d<double, 3>, HBSplinesFESpace<3> >)
    .def("CreatePointBasedControlGrid", &ControlGridUtility_CreatePointBasedControlGrid<Vector, HBSplinesFESpace<1> >)
    .def("CreatePointBasedControlGrid", &ControlGridUtility_CreatePointBasedControlGrid<Vector, HBSplinesFESpace<2> >)
    .def("CreatePointBasedControlGrid", &ControlGridUtility_CreatePointBasedControlGrid<Vector, HBSplinesFESpace<3> >)
    ;
}

}  // namespace Python.

} // Namespace Kratos


/*
 * X-Stream
 *
 * Copyright 2013 Operating Systems Laboratory EPFL
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef _TRIANGLE_COUNTING_
#define _TRIANGLE_COUNTING_
#include<sys/time.h>
#include<sys/resource.h>
#include "../../core/x-lib.hpp"
#include <cmath>
#include <boost/random.hpp>

// Triangle counting using the semi-streaming approach of
// Becchetti et. al.

namespace algorithm {
  namespace triangle_counting {
    const unsigned long step_init_hstream = 0;
    const unsigned long step_init_zstream = 1;
    const unsigned long step_compute_hash = 2;
    const unsigned long step_send_hash    = 3;
    const unsigned long step_rcv_hash     = 4;
    const unsigned long step_zout         = 5;
    const unsigned long step_zin          = 6;
    const unsigned long step_rollup       = 7;

    struct triangle_counting_per_processor_data:public per_processor_data {
      unsigned long processor_id;
      float total_triangles_local;
      static unsigned long total_bsp_phases;
      static float total;
      /* begin work specs. */
      static unsigned long bsp_phase;
      static unsigned long current_step;
      /* end work specs. */
      boost::mt19937 generator;
      boost::uniform_int<unsigned long> distribution;
      static float total_triangles_global;
      unsigned long total_processors;

      bool reduce(per_processor_data **per_cpu_array,
		  unsigned long processors)
      {
	if(current_step == triangle_counting::step_rollup) {
	  for(unsigned long i=0;i<processors;i++) {
	    triangle_counting_per_processor_data *pcpu =
	      static_cast<triangle_counting_per_processor_data *>(per_cpu_array[i]);
	    total_triangles_global += pcpu->total_triangles_local;
	    pcpu->total_triangles_local   = 0.0f;
	  }
	}
	return false;
      }
      triangle_counting_per_processor_data(unsigned long processor_id_in,
					   unsigned long total_processors_in)
	:processor_id(processor_id_in),
	 total_triangles_local(0.0),
	 generator(0xdeadbeef), // Deterministic seed
	 distribution(0, ULONG_MAX),
	 total_processors(total_processors_in)
      {
	for(unsigned long i=0;i<processor_id;i++) {
	  (void)distribution(generator); // skip
	}
      }
    } __attribute__((__aligned__(64)));

    struct __attribute__((__packed__)) tc_vertex {
      unsigned long hash_value;
      unsigned long min_adj_hash;
      float incident_triangles;
      unsigned long degree;
    };

    struct __attribute__((__packed__)) tc_hash {
      vertex_t other;
      unsigned long hash_value;
    };

    struct __attribute__((__packed__)) tc_z {
      vertex_t me;
      vertex_t other;
      unsigned long z;
      unsigned long calculated_min; // Or degree of other end
    };

    template<typename F>
    class triangle_counting {
      static unsigned long niters;
      static triangle_counting_per_processor_data ** pcpu_array;
      bool heartbeat;
      x_lib::streamIO<triangle_counting> *graph_storage;
      unsigned long vertex_stream;
      unsigned long init_stream;
      unsigned long edge_stream;
      unsigned long hash_stream;
      unsigned long z0_stream;
      unsigned long z1_stream;
      rtc_clock wall_clock;
      rtc_clock setup_time;
    public:
      triangle_counting();
      static void partition_pre_callback(unsigned long super_partition,
					 unsigned long partition,
					 per_processor_data* cpu_state);
      static void partition_callback(x_lib::stream_callback_state *state);
      static void partition_post_callback(unsigned long super_partition,
					  unsigned long partition,
					  per_processor_data *cpu_state);
      void operator() ();
      static unsigned long max_streams()
      {
	return 6; // vertices, init, edges, z0, z1, hash
      }
      static unsigned long max_buffers()
      {
	return 5;
      }
      static unsigned long vertex_state_bytes()
      {
	return sizeof(struct tc_vertex);
      }
      static unsigned long vertex_stream_buffer_bytes()
      {
	return sizeof(tc_vertex)+
	  MAX(F::split_size_bytes(),
	      MAX(sizeof(tc_z),
		  sizeof(tc_hash)));
      }
      static void state_iter_callback(unsigned long superp,
				      unsigned long partition,
				      unsigned long index,
				      unsigned char *vertex,
				      per_processor_data *cpu_state)
      {

	tc_vertex* v =  (struct tc_vertex*)vertex;
	triangle_counting_per_processor_data *pcpu =
	  static_cast<triangle_counting_per_processor_data *>(cpu_state);
	if(triangle_counting_per_processor_data::current_step == step_compute_hash) {
	  if(triangle_counting_per_processor_data::bsp_phase == 0) {
	    v->degree = 0;
	    v->incident_triangles = 0.0f;
	  }
	  v->hash_value = pcpu->distribution(pcpu->generator);
	  unsigned long skip = pcpu->total_processors - 1;
	  while(skip--) {
	    (void)pcpu->distribution(pcpu->generator);
	  }
	  v->min_adj_hash = ULONG_MAX;
	}
	else if(triangle_counting_per_processor_data::current_step == step_rollup) {
	  v->incident_triangles /= 2.0f;
	  pcpu->total_triangles_local += v->incident_triangles;
	}
	else {
	  BOOST_LOG_TRIVIAL(fatal) << "Unexpected state iter callback !";
	  exit(-1);
	}
      }
      static per_processor_data *
      create_per_processor_data(unsigned long processor_id)
      {
	return pcpu_array[processor_id];
      }

      static void do_cpu_callback(per_processor_data *cpu_state)
      {
      }
    };

    template<typename F>triangle_counting<F>::triangle_counting()
    {
      wall_clock.start();
      setup_time.start();
      heartbeat = (vm.count("heartbeat") > 0);
      unsigned long num_processors = vm["processors"].as<unsigned long>();
      niters =  (vm["triangle_counting::niters"].as<unsigned long>());
      pcpu_array = new triangle_counting_per_processor_data *[num_processors];
      for(unsigned long i=0;i<num_processors;i++) {
	pcpu_array[i] = new
	  triangle_counting_per_processor_data(i, num_processors);
      }
      graph_storage = new x_lib::streamIO<triangle_counting>();
      triangle_counting_per_processor_data::bsp_phase = 0;
      vertex_stream =
	graph_storage->open_stream("vertices", true,
				   vm["vertices_disk"].as<unsigned long>(),
				   graph_storage->get_config()->vertex_size);
      if(graph_storage->get_config()->super_partitions == 1) {
	std::string efile = pt.get<std::string>("graph.name");
	edge_stream =
	  graph_storage->open_stream((const char *)efile.c_str(), false,
				     vm["input_disk"].as<unsigned long>(),
				     F::split_size_bytes());
      }
      else {
	edge_stream =
	  graph_storage->open_stream("edges", true,
				     vm["edges_disk"].as<unsigned long>(),
				     F::split_size_bytes());
	std::string efile = pt.get<std::string>("graph.name");
	init_stream =
	  graph_storage->open_stream((const char *)efile.c_str(), false,
				     vm["input_disk"].as<unsigned long>(),
				     F::split_size_bytes(), 1);
      }
      hash_stream =
	graph_storage->open_stream("hash", true,
				   vm["updates0_disk"].as<unsigned long>(),
				   sizeof(struct tc_hash));
      z0_stream =
	graph_storage->open_stream("z0", true,
				   vm["updates0_disk"].as<unsigned long>(),
				   sizeof(struct tc_z));
      z1_stream =
	graph_storage->open_stream("z1", true,
				   vm["updates1_disk"].as<unsigned long>(),
				   sizeof(struct tc_z));
      setup_time.stop();

    }

    template<typename F> struct init_edge_wrapper
    {
      static unsigned long item_size()
      {
	return F::split_size_bytes();
      }

      static unsigned long key(unsigned char *buffer)
      {
	return F::split_key(buffer, 0);
      }
    };

    struct tc_hash_wrapper
    {
      static unsigned long item_size()
      {
	return sizeof(struct tc_hash);
      }
      static unsigned long key(unsigned char *buffer)
      {
	return ((struct tc_hash *)buffer)->other;
      }
    };
    struct tc_z_wrapper
    {
      static unsigned long item_size()
      {
	return sizeof(struct tc_z);
      }
      static unsigned long key(unsigned char *buffer)
      {
	return ((struct tc_z *)buffer)->me;
      }
    };

#define SP_PRE(_i)							\
    do {								\
      if(graph_storage->get_config()->super_partitions > 1) {		\
	if(triangle_counting_per_processor_data::bsp_phase > 0) {	\
	  graph_storage->state_load(vertex_stream, _i);			\
	}								\
	graph_storage->state_prepare(_i);				\
      }									\
      else if(triangle_counting_per_processor_data::bsp_phase == 0) {	\
	graph_storage->state_prepare(_i);				\
      }									\
    } while(0);

#define SP_POST(_i)						\
    do {							\
      if(graph_storage->get_config()->super_partitions > 1) {	\
	graph_storage->state_store(vertex_stream, (_i));	\
      }								\
    } while(0)

#define SWAP_STREAMS(_i, _j)			\
    do {					\
      unsigned long _tmp;			\
      _tmp = _i;				\
      _i = _j;					\
      _j = _tmp;				\
    }while(0)

    template<typename F>void triangle_counting<F>::operator() ()
    {
      // Setup
      triangle_counting_per_processor_data::current_step = step_init_hstream;
      if(graph_storage->get_config()->super_partitions > 1) {
	x_lib::do_stream<triangle_counting<F>,
			 init_edge_wrapper<F>,
			 init_edge_wrapper<F> >
	  (graph_storage, 0, init_stream, edge_stream, NULL);
	graph_storage->close_stream(init_stream);
      }
      graph_storage->rewind_stream(edge_stream);
      triangle_counting_per_processor_data::current_step = step_init_zstream;
      x_lib::do_stream<triangle_counting<F>,
		       init_edge_wrapper<F>,
		       tc_z_wrapper >
	(graph_storage, 0, edge_stream, z0_stream, NULL);
      // TC loop
      for(unsigned long iter=0;iter < niters;iter++) {
	graph_storage->rewind_stream(edge_stream);
	for(unsigned long i=0;i<graph_storage->get_config()->super_partitions;i++) {
	  SP_PRE(i);
	  triangle_counting_per_processor_data::current_step = step_compute_hash;
	  x_lib::do_state_iter<triangle_counting<F> > (graph_storage, i);
	  triangle_counting_per_processor_data::current_step = step_send_hash;
	  x_lib::do_stream<triangle_counting<F>,
			   init_edge_wrapper<F>,
			   tc_hash_wrapper >
	    (graph_storage, i, edge_stream, hash_stream, NULL, true);
	  SP_POST(i);
	}
	graph_storage->rewind_stream(hash_stream);
	graph_storage->rewind_stream(z0_stream);
	for(unsigned long i=0;i<graph_storage->get_config()->super_partitions;i++) {
	  SP_PRE(i);
	  triangle_counting_per_processor_data::current_step = step_rcv_hash;
	  x_lib::do_stream<triangle_counting<F>,
			   tc_hash_wrapper,
			   tc_hash_wrapper >
	    (graph_storage, i, hash_stream, ULONG_MAX, NULL);
	  graph_storage->reset_stream(hash_stream, i);
	  triangle_counting_per_processor_data::current_step = step_zout;
	  x_lib::do_stream<triangle_counting<F>,
			   tc_z_wrapper,
			   tc_z_wrapper >
	    (graph_storage, i, z0_stream, z1_stream, NULL);
	  graph_storage->reset_stream(z0_stream, i);
	  SP_POST(i);
	}
	graph_storage->rewind_stream(z1_stream);
	for(unsigned long i=0;i<graph_storage->get_config()->super_partitions;i++) {
	  SP_PRE(i);
	  triangle_counting_per_processor_data::current_step = step_zin;
	  x_lib::do_stream<triangle_counting<F>,
			   tc_z_wrapper,
			   tc_z_wrapper >
	    (graph_storage, i, z1_stream, z0_stream, NULL);
	  graph_storage->reset_stream(z1_stream, i);
	  SP_POST(i);
	}
	if(heartbeat) {
	  BOOST_LOG_TRIVIAL(info) << clock::timestamp() << " Completed phase " <<triangle_counting_per_processor_data::bsp_phase;
	}
	triangle_counting_per_processor_data::bsp_phase++;
      }
      graph_storage->rewind_stream(z0_stream);
      triangle_counting_per_processor_data::current_step = step_rollup;
      for(unsigned long i=0;i<graph_storage->get_config()->super_partitions;i++) {
	SP_PRE(i);
	x_lib::do_stream<triangle_counting<F>,
			 tc_z_wrapper,
			 tc_z_wrapper >
	  (graph_storage, i, z0_stream, ULONG_MAX, NULL);
	graph_storage->reset_stream(z0_stream, i);
	x_lib::do_state_iter<triangle_counting<F> > (graph_storage, i);
	x_lib::do_cpu<triangle_counting>(graph_storage, i);
	SP_POST(i);
      }
      if(graph_storage->get_config()->super_partitions == 1) {
	graph_storage->state_store(vertex_stream, 0);
      }
      setup_time.start();
      graph_storage->terminate();
      setup_time.stop();
      wall_clock.stop();
      // Note div by 3.0 as each triangle is counted once at each incident
      // vertex
      BOOST_LOG_TRIVIAL(info) << "ALGORITHM::TRIANGLE_COUNTING::TRIANGLES "
			      <<
	triangle_counting_per_processor_data::total_triangles_global/3.0f;
      setup_time.print("CORE::TIME::SETUP");
      wall_clock.print("CORE::TIME::WALL");
    }

#undef SP_PRE
#undef SP_POST
#undef SWAP_STREAMS

    template<typename F>
    void triangle_counting<F>::partition_pre_callback(unsigned long superp,
						      unsigned long partition,
						      per_processor_data *pcpu)
    {

    }

    template<typename F>
    void triangle_counting<F>::partition_callback(x_lib::stream_callback_state *callback)
    {
      if(callback->loopback) {
	while(callback->bytes_in) {
	  tc_hash *uin  = (tc_hash *)(callback->bufin);
	  tc_vertex* v  = ((tc_vertex *)callback->state) +
	    x_lib::configuration::map_offset(uin->other);
	  if(uin->hash_value < v->min_adj_hash) {
	    v->min_adj_hash = uin->hash_value;
	  }
	  callback->bufin     += sizeof(struct tc_hash);
	  callback->bytes_in  -= sizeof(struct tc_hash);
	}
	return;
      }
      switch(triangle_counting_per_processor_data::current_step) {
      case step_init_hstream:
	{
	  while(callback->bytes_in) {
	    if((callback->bytes_out + sizeof(F::split_size_bytes()))
	       >callback->bytes_out_max) {
	      break;
	    }
	    memcpy(callback->bufout, callback->bufin, F::split_size_bytes());
	    callback->bytes_out += F::split_size_bytes();
	    callback->bufout += F::split_size_bytes();
	    callback->bufin +=F::split_size_bytes();
	    callback->bytes_in -=F::split_size_bytes();
	  }
	  break;
	}

      case step_init_zstream:
	{
	  while(callback->bytes_in) {
	    if((callback->bytes_out + sizeof(tc_z)) >callback->bytes_out_max) {
	      break;
	    }
	    tc_z *z = (tc_z *)(callback->bufout);
	    vertex_t src, dst;
	    F::read_edge(callback->bufin, src, dst);
	    z->me    = src;
	    z->other = dst;
	    z->z = 0;
	    z->calculated_min = 0xdeadbeefUL;
	    callback->bytes_out += sizeof(tc_z);
	    callback->bufout += sizeof(tc_z);
	    callback->bufin +=F::split_size_bytes();
	    callback->bytes_in -=F::split_size_bytes();
	  }
	  break;
	}

      case step_send_hash:
	{
	  while(callback->bytes_in) {
	    if((callback->bytes_out + sizeof(tc_hash)) >callback->bytes_out_max) {
	      break;
	    }
	    vertex_t src, dst;
	    F::read_edge(callback->bufin, src, dst);
	    tc_hash *uout = (tc_hash *)(callback->bufout);
	    tc_vertex* v  = ((tc_vertex *)callback->state) +
	      x_lib::configuration::map_offset(src);
	    uout->hash_value = v->hash_value;
	    uout->other      = dst;
	    if(triangle_counting_per_processor_data::bsp_phase == 0){
	      v->degree++;
	    }
	    callback->bufin     += F::split_size_bytes();
	    callback->bytes_in  -= F::split_size_bytes();
	    callback->bufout    += sizeof(struct tc_hash);
	    callback->bytes_out += sizeof(struct tc_hash);
	  }
	  break;
	}

      case step_rcv_hash:
	{
	  while(callback->bytes_in) {
	    tc_hash *uin  = (tc_hash *)(callback->bufin);
	    tc_vertex* v  = ((tc_vertex *)callback->state) +
	      x_lib::configuration::map_offset(uin->other);
	    if(uin->hash_value < v->min_adj_hash) {
	      v->min_adj_hash = uin->hash_value;
	    }
	    callback->bufin     += sizeof(struct tc_hash);
	    callback->bytes_in  -= sizeof(struct tc_hash);
	  }
	  break;
	}
      case step_zout:
	{
	  while(callback->bytes_in) {
	    if((callback->bytes_out + sizeof(tc_z)) > callback->bytes_out_max) {
	      break;
	    }
	    tc_z* zin = (tc_z *)(callback->bufin);
	    tc_z* zout = (tc_z *)(callback->bufout);
	    tc_vertex* v =  ((tc_vertex *)(callback->state)) +
	      x_lib::configuration::map_offset(zin->me);
	    zout->me             = zin->other;
	    zout->other          = zin->me;
	    zout->z              = zin->z;
	    zout->calculated_min = v->min_adj_hash;
	    callback->bufin     += sizeof(struct tc_z);
	    callback->bufout    += sizeof(struct tc_z);
	    callback->bytes_in  -= sizeof(struct tc_z);
	    callback->bytes_out += sizeof(struct tc_z);
	  }
	  break;
	}

      case step_zin:
	{
	  while(callback->bytes_in) {
	    if((callback->bytes_out + sizeof(tc_z)) > callback->bytes_out_max) {
	      break;
	    }
	    tc_z* zin  = (tc_z *)(callback->bufin);
	    tc_z* zout = (tc_z *)(callback->bufout);
	    tc_vertex* v    =  ((tc_vertex *)(callback->state)) +
	      x_lib::configuration::map_offset(zin->me);
	    zout->me             = zin->other;
	    zout->other          = zin->me;
	    if(zin->calculated_min == v->min_adj_hash) {
	      zout->z = zin->z + 1;
	    }
	    else {
	      zout->z = zin->z;
	    }
	    zout->calculated_min = v->degree;
	    callback->bufin     += sizeof(struct tc_z);
	    callback->bufout    += sizeof(struct tc_z);
	    callback->bytes_in  -= sizeof(struct tc_z);
	    callback->bytes_out += sizeof(struct tc_z);
	  }
	  break;
	}

      case step_rollup:
	{
	  while(callback->bytes_in) {
	    tc_z* zin  = (tc_z *)(callback->bufin);
	    tc_vertex* v    =  ((tc_vertex *)(callback->state)) +
	      x_lib::configuration::map_offset(zin->me);
	    v->incident_triangles = v->incident_triangles +
	      ((zin->z/(zin->z + (float)niters))*
	       (zin->calculated_min + v->degree));
	    callback->bufin     += sizeof(struct tc_z);
	    callback->bytes_in  -= sizeof(struct tc_z);
	  }
	  break;
	}
      default:
	exit(-1);
      }
    }

    template<typename F>
    void triangle_counting<F>::partition_post_callback
    (unsigned long superp,
     unsigned long partition,
     per_processor_data *pcpu)
    {
    }

    template<typename F> triangle_counting_per_processor_data **
    triangle_counting<F>::pcpu_array = NULL;
    template<typename F> unsigned long triangle_counting<F>::niters = 0;
    unsigned long triangle_counting_per_processor_data::bsp_phase = 0;
    unsigned long triangle_counting_per_processor_data::current_step;
    float triangle_counting_per_processor_data::total_triangles_global = 0.0;
  }
}
#endif


//
// Copyright (c) 2002--2010
// Toon Knapen, Karl Meerbergen, Kresimir Fresl,
// Thomas Klimpel and Rutger ter Borg
//
// 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)
//
// THIS FILE IS AUTOMATICALLY GENERATED
// PLEASE DO NOT EDIT!
//

#ifndef BOOST_NUMERIC_BINDINGS_LAPACK_DRIVER_GEESX_HPP
#define BOOST_NUMERIC_BINDINGS_LAPACK_DRIVER_GEESX_HPP

#include <boost/assert.hpp>
#include <boost/numeric/bindings/begin.hpp>
#include <boost/numeric/bindings/detail/array.hpp>
#include <boost/numeric/bindings/is_column_major.hpp>
#include <boost/numeric/bindings/is_complex.hpp>
#include <boost/numeric/bindings/is_mutable.hpp>
#include <boost/numeric/bindings/is_real.hpp>
#include <boost/numeric/bindings/lapack/workspace.hpp>
#include <boost/numeric/bindings/remove_imaginary.hpp>
#include <boost/numeric/bindings/size.hpp>
#include <boost/numeric/bindings/stride.hpp>
#include <boost/numeric/bindings/traits/detail/utils.hpp>
#include <boost/numeric/bindings/value_type.hpp>
#include <boost/static_assert.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/type_traits/remove_const.hpp>
#include <boost/utility/enable_if.hpp>

//
// The LAPACK-backend for geesx is the netlib-compatible backend.
//
#include <boost/numeric/bindings/lapack/detail/lapack.h>
#include <boost/numeric/bindings/lapack/detail/lapack_option.hpp>

namespace boost {
namespace numeric {
namespace bindings {
namespace lapack {

//
// The detail namespace contains value-type-overloaded functions that
// dispatch to the appropriate back-end LAPACK-routine.
//
namespace detail {

//
// Overloaded function for dispatching to
// * netlib-compatible LAPACK backend (the default), and
// * float value-type.
//
inline std::ptrdiff_t geesx( const char jobvs, const char sort,
        external_fp select, const char sense, const fortran_int_t n, float* a,
        const fortran_int_t lda, fortran_int_t& sdim, float* wr, float* wi,
        float* vs, const fortran_int_t ldvs, float& rconde, float& rcondv,
        float* work, const fortran_int_t lwork, fortran_int_t* iwork,
        const fortran_int_t liwork, fortran_bool_t* bwork ) {
    fortran_int_t info(0);
    LAPACK_SGEESX( &jobvs, &sort, select, &sense, &n, a, &lda, &sdim, wr, wi,
            vs, &ldvs, &rconde, &rcondv, work, &lwork, iwork, &liwork, bwork,
            &info );
    return info;
}

//
// Overloaded function for dispatching to
// * netlib-compatible LAPACK backend (the default), and
// * double value-type.
//
inline std::ptrdiff_t geesx( const char jobvs, const char sort,
        external_fp select, const char sense, const fortran_int_t n,
        double* a, const fortran_int_t lda, fortran_int_t& sdim, double* wr,
        double* wi, double* vs, const fortran_int_t ldvs, double& rconde,
        double& rcondv, double* work, const fortran_int_t lwork,
        fortran_int_t* iwork, const fortran_int_t liwork,
        fortran_bool_t* bwork ) {
    fortran_int_t info(0);
    LAPACK_DGEESX( &jobvs, &sort, select, &sense, &n, a, &lda, &sdim, wr, wi,
            vs, &ldvs, &rconde, &rcondv, work, &lwork, iwork, &liwork, bwork,
            &info );
    return info;
}

//
// Overloaded function for dispatching to
// * netlib-compatible LAPACK backend (the default), and
// * complex<float> value-type.
//
inline std::ptrdiff_t geesx( const char jobvs, const char sort,
        external_fp select, const char sense, const fortran_int_t n,
        std::complex<float>* a, const fortran_int_t lda, fortran_int_t& sdim,
        std::complex<float>* w, std::complex<float>* vs,
        const fortran_int_t ldvs, float& rconde, float& rcondv,
        std::complex<float>* work, const fortran_int_t lwork, float* rwork,
        fortran_bool_t* bwork ) {
    fortran_int_t info(0);
    LAPACK_CGEESX( &jobvs, &sort, select, &sense, &n, a, &lda, &sdim, w, vs,
            &ldvs, &rconde, &rcondv, work, &lwork, rwork, bwork, &info );
    return info;
}

//
// Overloaded function for dispatching to
// * netlib-compatible LAPACK backend (the default), and
// * complex<double> value-type.
//
inline std::ptrdiff_t geesx( const char jobvs, const char sort,
        external_fp select, const char sense, const fortran_int_t n,
        std::complex<double>* a, const fortran_int_t lda, fortran_int_t& sdim,
        std::complex<double>* w, std::complex<double>* vs,
        const fortran_int_t ldvs, double& rconde, double& rcondv,
        std::complex<double>* work, const fortran_int_t lwork, double* rwork,
        fortran_bool_t* bwork ) {
    fortran_int_t info(0);
    LAPACK_ZGEESX( &jobvs, &sort, select, &sense, &n, a, &lda, &sdim, w, vs,
            &ldvs, &rconde, &rcondv, work, &lwork, rwork, bwork, &info );
    return info;
}

} // namespace detail

//
// Value-type based template class. Use this class if you need a type
// for dispatching to geesx.
//
template< typename Value, typename Enable = void >
struct geesx_impl {};

//
// This implementation is enabled if Value is a real type.
//
template< typename Value >
struct geesx_impl< Value, typename boost::enable_if< is_real< Value > >::type > {

    typedef Value value_type;
    typedef typename remove_imaginary< Value >::type real_type;

    //
    // Static member function for user-defined workspaces, that
    // * Deduces the required arguments for dispatching to LAPACK, and
    // * Asserts that most arguments make sense.
    //
    template< typename MatrixA, typename VectorWR, typename VectorWI,
            typename MatrixVS, typename WORK, typename IWORK, typename BWORK >
    static std::ptrdiff_t invoke( const char jobvs, const char sort,
            external_fp select, const char sense, MatrixA& a,
            fortran_int_t& sdim, VectorWR& wr, VectorWI& wi, MatrixVS& vs,
            real_type& rconde, real_type& rcondv, detail::workspace3< WORK,
            IWORK, BWORK > work ) {
        namespace bindings = ::boost::numeric::bindings;
        BOOST_STATIC_ASSERT( (bindings::is_column_major< MatrixA >::value) );
        BOOST_STATIC_ASSERT( (bindings::is_column_major< MatrixVS >::value) );
        BOOST_STATIC_ASSERT( (boost::is_same< typename remove_const<
                typename bindings::value_type< MatrixA >::type >::type,
                typename remove_const< typename bindings::value_type<
                VectorWR >::type >::type >::value) );
        BOOST_STATIC_ASSERT( (boost::is_same< typename remove_const<
                typename bindings::value_type< MatrixA >::type >::type,
                typename remove_const< typename bindings::value_type<
                VectorWI >::type >::type >::value) );
        BOOST_STATIC_ASSERT( (boost::is_same< typename remove_const<
                typename bindings::value_type< MatrixA >::type >::type,
                typename remove_const< typename bindings::value_type<
                MatrixVS >::type >::type >::value) );
        BOOST_STATIC_ASSERT( (bindings::is_mutable< MatrixA >::value) );
        BOOST_STATIC_ASSERT( (bindings::is_mutable< VectorWR >::value) );
        BOOST_STATIC_ASSERT( (bindings::is_mutable< VectorWI >::value) );
        BOOST_STATIC_ASSERT( (bindings::is_mutable< MatrixVS >::value) );
        BOOST_ASSERT( bindings::size(wi) >= bindings::size_column(a) );
        BOOST_ASSERT( bindings::size(work.select(fortran_int_t())) >=
                min_size_iwork( bindings::size_column(a), sense ));
        BOOST_ASSERT( bindings::size(work.select(fortran_bool_t())) >=
                min_size_bwork( bindings::size_column(a), sort ));
        BOOST_ASSERT( bindings::size(work.select(real_type())) >=
                min_size_work( bindings::size_column(a), sense ));
        BOOST_ASSERT( bindings::size(wr) >= bindings::size_column(a) );
        BOOST_ASSERT( bindings::size_column(a) >= 0 );
        BOOST_ASSERT( bindings::size_minor(a) == 1 ||
                bindings::stride_minor(a) == 1 );
        BOOST_ASSERT( bindings::size_minor(vs) == 1 ||
                bindings::stride_minor(vs) == 1 );
        BOOST_ASSERT( bindings::stride_major(a) >= std::max< std::ptrdiff_t >(1,
                bindings::size_column(a)) );
        BOOST_ASSERT( jobvs == 'N' || jobvs == 'V' );
        BOOST_ASSERT( sense == 'N' || sense == 'E' || sense == 'V' ||
                sense == 'B' );
        BOOST_ASSERT( sort == 'N' || sort == 'S' );
        return detail::geesx( jobvs, sort, select, sense,
                bindings::size_column(a), bindings::begin_value(a),
                bindings::stride_major(a), sdim, bindings::begin_value(wr),
                bindings::begin_value(wi), bindings::begin_value(vs),
                bindings::stride_major(vs), rconde, rcondv,
                bindings::begin_value(work.select(real_type())),
                bindings::size(work.select(real_type())),
                bindings::begin_value(work.select(fortran_int_t())),
                bindings::size(work.select(fortran_int_t())),
                bindings::begin_value(work.select(fortran_bool_t())) );
    }

    //
    // Static member function that
    // * Figures out the minimal workspace requirements, and passes
    //   the results to the user-defined workspace overload of the
    //   invoke static member function
    // * Enables the unblocked algorithm (BLAS level 2)
    //
    template< typename MatrixA, typename VectorWR, typename VectorWI,
            typename MatrixVS >
    static std::ptrdiff_t invoke( const char jobvs, const char sort,
            external_fp select, const char sense, MatrixA& a,
            fortran_int_t& sdim, VectorWR& wr, VectorWI& wi, MatrixVS& vs,
            real_type& rconde, real_type& rcondv, minimal_workspace ) {
        namespace bindings = ::boost::numeric::bindings;
        bindings::detail::array< real_type > tmp_work( min_size_work(
                bindings::size_column(a), sense ) );
        bindings::detail::array< fortran_int_t > tmp_iwork(
                min_size_iwork( bindings::size_column(a), sense ) );
        bindings::detail::array< fortran_bool_t > tmp_bwork( min_size_bwork(
                bindings::size_column(a), sort ) );
        return invoke( jobvs, sort, select, sense, a, sdim, wr, wi, vs,
                rconde, rcondv, workspace( tmp_work, tmp_iwork, tmp_bwork ) );
    }

    //
    // Static member function that
    // * Figures out the optimal workspace requirements, and passes
    //   the results to the user-defined workspace overload of the
    //   invoke static member
    // * Enables the blocked algorithm (BLAS level 3)
    //
    template< typename MatrixA, typename VectorWR, typename VectorWI,
            typename MatrixVS >
    static std::ptrdiff_t invoke( const char jobvs, const char sort,
            external_fp select, const char sense, MatrixA& a,
            fortran_int_t& sdim, VectorWR& wr, VectorWI& wi, MatrixVS& vs,
            real_type& rconde, real_type& rcondv, optimal_workspace ) {
        namespace bindings = ::boost::numeric::bindings;
        real_type opt_size_work;
        fortran_int_t opt_size_iwork;
        bindings::detail::array< fortran_bool_t > tmp_bwork( min_size_bwork(
                bindings::size_column(a), sort ) );
        detail::geesx( jobvs, sort, select, sense,
                bindings::size_column(a), bindings::begin_value(a),
                bindings::stride_major(a), sdim, bindings::begin_value(wr),
                bindings::begin_value(wi), bindings::begin_value(vs),
                bindings::stride_major(vs), rconde, rcondv, &opt_size_work,
                -1, &opt_size_iwork, -1, bindings::begin_value(tmp_bwork) );
        bindings::detail::array< real_type > tmp_work(
                traits::detail::to_int( opt_size_work ) );
        bindings::detail::array< fortran_int_t > tmp_iwork(
                opt_size_iwork );
        return invoke( jobvs, sort, select, sense, a, sdim, wr, wi, vs,
                rconde, rcondv, workspace( tmp_work, tmp_iwork, tmp_bwork ) );
    }

    //
    // Static member function that returns the minimum size of
    // workspace-array work.
    //
    static std::ptrdiff_t min_size_work( const std::ptrdiff_t n,
            const char sense ) {
        if ( sense == 'N' )
            return std::max< std::ptrdiff_t >( 1, 3*n );
        else
            return std::max< std::ptrdiff_t >( 1, n+n*n/2 );
    }

    //
    // Static member function that returns the minimum size of
    // workspace-array iwork.
    //
    static std::ptrdiff_t min_size_iwork( const std::ptrdiff_t n,
            const char sense ) {
        if ( sense == 'N' || sense == 'E' )
            return 1;
        else
            return std::max< std::ptrdiff_t >( 1, n*n/4 );
    }

    //
    // Static member function that returns the minimum size of
    // workspace-array bwork.
    //
    static std::ptrdiff_t min_size_bwork( const std::ptrdiff_t n,
            const char sort ) {
        if ( sort == 'N' )
            return 0;
        else
            return n;
    }
};

//
// This implementation is enabled if Value is a complex type.
//
template< typename Value >
struct geesx_impl< Value, typename boost::enable_if< is_complex< Value > >::type > {

    typedef Value value_type;
    typedef typename remove_imaginary< Value >::type real_type;

    //
    // Static member function for user-defined workspaces, that
    // * Deduces the required arguments for dispatching to LAPACK, and
    // * Asserts that most arguments make sense.
    //
    template< typename MatrixA, typename VectorW, typename MatrixVS,
            typename WORK, typename RWORK, typename BWORK >
    static std::ptrdiff_t invoke( const char jobvs, const char sort,
            external_fp select, const char sense, MatrixA& a,
            fortran_int_t& sdim, VectorW& w, MatrixVS& vs,
            real_type& rconde, real_type& rcondv, detail::workspace3< WORK,
            RWORK, BWORK > work ) {
        namespace bindings = ::boost::numeric::bindings;
        BOOST_STATIC_ASSERT( (bindings::is_column_major< MatrixA >::value) );
        BOOST_STATIC_ASSERT( (bindings::is_column_major< MatrixVS >::value) );
        BOOST_STATIC_ASSERT( (boost::is_same< typename remove_const<
                typename bindings::value_type< MatrixA >::type >::type,
                typename remove_const< typename bindings::value_type<
                VectorW >::type >::type >::value) );
        BOOST_STATIC_ASSERT( (boost::is_same< typename remove_const<
                typename bindings::value_type< MatrixA >::type >::type,
                typename remove_const< typename bindings::value_type<
                MatrixVS >::type >::type >::value) );
        BOOST_STATIC_ASSERT( (bindings::is_mutable< MatrixA >::value) );
        BOOST_STATIC_ASSERT( (bindings::is_mutable< VectorW >::value) );
        BOOST_STATIC_ASSERT( (bindings::is_mutable< MatrixVS >::value) );
        BOOST_ASSERT( bindings::size(w) >= bindings::size_column(a) );
        BOOST_ASSERT( bindings::size(work.select(fortran_bool_t())) >=
                min_size_bwork( bindings::size_column(a), sort ));
        BOOST_ASSERT( bindings::size(work.select(real_type())) >=
                min_size_rwork( bindings::size_column(a) ));
        BOOST_ASSERT( bindings::size(work.select(value_type())) >=
                min_size_work( bindings::size_column(a), sense ));
        BOOST_ASSERT( bindings::size_column(a) >= 0 );
        BOOST_ASSERT( bindings::size_minor(a) == 1 ||
                bindings::stride_minor(a) == 1 );
        BOOST_ASSERT( bindings::size_minor(vs) == 1 ||
                bindings::stride_minor(vs) == 1 );
        BOOST_ASSERT( bindings::stride_major(a) >= std::max< std::ptrdiff_t >(1,
                bindings::size_column(a)) );
        BOOST_ASSERT( jobvs == 'N' || jobvs == 'V' );
        BOOST_ASSERT( sense == 'N' || sense == 'E' || sense == 'V' ||
                sense == 'B' );
        BOOST_ASSERT( sort == 'N' || sort == 'S' );
        return detail::geesx( jobvs, sort, select, sense,
                bindings::size_column(a), bindings::begin_value(a),
                bindings::stride_major(a), sdim, bindings::begin_value(w),
                bindings::begin_value(vs), bindings::stride_major(vs), rconde,
                rcondv, bindings::begin_value(work.select(value_type())),
                bindings::size(work.select(value_type())),
                bindings::begin_value(work.select(real_type())),
                bindings::begin_value(work.select(fortran_bool_t())) );
    }

    //
    // Static member function that
    // * Figures out the minimal workspace requirements, and passes
    //   the results to the user-defined workspace overload of the
    //   invoke static member function
    // * Enables the unblocked algorithm (BLAS level 2)
    //
    template< typename MatrixA, typename VectorW, typename MatrixVS >
    static std::ptrdiff_t invoke( const char jobvs, const char sort,
            external_fp select, const char sense, MatrixA& a,
            fortran_int_t& sdim, VectorW& w, MatrixVS& vs,
            real_type& rconde, real_type& rcondv, minimal_workspace ) {
        namespace bindings = ::boost::numeric::bindings;
        bindings::detail::array< value_type > tmp_work( min_size_work(
                bindings::size_column(a), sense ) );
        bindings::detail::array< real_type > tmp_rwork( min_size_rwork(
                bindings::size_column(a) ) );
        bindings::detail::array< fortran_bool_t > tmp_bwork( min_size_bwork(
                bindings::size_column(a), sort ) );
        return invoke( jobvs, sort, select, sense, a, sdim, w, vs, rconde,
                rcondv, workspace( tmp_work, tmp_rwork, tmp_bwork ) );
    }

    //
    // Static member function that
    // * Figures out the optimal workspace requirements, and passes
    //   the results to the user-defined workspace overload of the
    //   invoke static member
    // * Enables the blocked algorithm (BLAS level 3)
    //
    template< typename MatrixA, typename VectorW, typename MatrixVS >
    static std::ptrdiff_t invoke( const char jobvs, const char sort,
            external_fp select, const char sense, MatrixA& a,
            fortran_int_t& sdim, VectorW& w, MatrixVS& vs,
            real_type& rconde, real_type& rcondv, optimal_workspace ) {
        namespace bindings = ::boost::numeric::bindings;
        value_type opt_size_work;
        bindings::detail::array< real_type > tmp_rwork( min_size_rwork(
                bindings::size_column(a) ) );
        bindings::detail::array< fortran_bool_t > tmp_bwork( min_size_bwork(
                bindings::size_column(a), sort ) );
        detail::geesx( jobvs, sort, select, sense,
                bindings::size_column(a), bindings::begin_value(a),
                bindings::stride_major(a), sdim, bindings::begin_value(w),
                bindings::begin_value(vs), bindings::stride_major(vs), rconde,
                rcondv, &opt_size_work, -1, bindings::begin_value(tmp_rwork),
                bindings::begin_value(tmp_bwork) );
        bindings::detail::array< value_type > tmp_work(
                traits::detail::to_int( opt_size_work ) );
        return invoke( jobvs, sort, select, sense, a, sdim, w, vs, rconde,
                rcondv, workspace( tmp_work, tmp_rwork, tmp_bwork ) );
    }

    //
    // Static member function that returns the minimum size of
    // workspace-array work.
    //
    static std::ptrdiff_t min_size_work( const std::ptrdiff_t n,
            const char sense ) {
        if ( sense == 'N' )
            return std::max< std::ptrdiff_t >( 1, 2*n );
        else
            return std::max< std::ptrdiff_t >( 1, n*n/2 );
    }

    //
    // Static member function that returns the minimum size of
    // workspace-array rwork.
    //
    static std::ptrdiff_t min_size_rwork( const std::ptrdiff_t n ) {
        return n;
    }

    //
    // Static member function that returns the minimum size of
    // workspace-array bwork.
    //
    static std::ptrdiff_t min_size_bwork( const std::ptrdiff_t n,
            const char sort ) {
        if ( sort == 'N' )
            return 0;
        else
            return n;
    }
};

//
// Functions for direct use. These functions are overloaded for temporaries,
// so that wrapped types can still be passed and used for write-access. In
// addition, if applicable, they are overloaded for user-defined workspaces.
// Calls to these functions are passed to the geesx_impl classes. In the
// documentation, most overloads are collapsed to avoid a large number of
// prototypes which are very similar.
//

//
// Overloaded function for geesx. Its overload differs for
// * User-defined workspace
//
template< typename MatrixA, typename VectorWR, typename VectorWI,
        typename MatrixVS, typename Workspace >
inline typename boost::enable_if< detail::is_workspace< Workspace >,
        std::ptrdiff_t >::type
geesx( const char jobvs, const char sort, external_fp select,
        const char sense, MatrixA& a, fortran_int_t& sdim, VectorWR& wr,
        VectorWI& wi, MatrixVS& vs, typename remove_imaginary<
        typename bindings::value_type< MatrixA >::type >::type& rconde,
        typename remove_imaginary< typename bindings::value_type<
        MatrixA >::type >::type& rcondv, Workspace work ) {
    return geesx_impl< typename bindings::value_type<
            MatrixA >::type >::invoke( jobvs, sort, select, sense, a, sdim,
            wr, wi, vs, rconde, rcondv, work );
}

//
// Overloaded function for geesx. Its overload differs for
// * Default workspace-type (optimal)
//
template< typename MatrixA, typename VectorWR, typename VectorWI,
        typename MatrixVS >
inline typename boost::disable_if< detail::is_workspace< MatrixVS >,
        std::ptrdiff_t >::type
geesx( const char jobvs, const char sort, external_fp select,
        const char sense, MatrixA& a, fortran_int_t& sdim, VectorWR& wr,
        VectorWI& wi, MatrixVS& vs, typename remove_imaginary<
        typename bindings::value_type< MatrixA >::type >::type& rconde,
        typename remove_imaginary< typename bindings::value_type<
        MatrixA >::type >::type& rcondv ) {
    return geesx_impl< typename bindings::value_type<
            MatrixA >::type >::invoke( jobvs, sort, select, sense, a, sdim,
            wr, wi, vs, rconde, rcondv, optimal_workspace() );
}

//
// Overloaded function for geesx. Its overload differs for
// * User-defined workspace
//
template< typename MatrixA, typename VectorW, typename MatrixVS,
        typename Workspace >
inline typename boost::enable_if< detail::is_workspace< Workspace >,
        std::ptrdiff_t >::type
geesx( const char jobvs, const char sort, external_fp select,
        const char sense, MatrixA& a, fortran_int_t& sdim, VectorW& w,
        MatrixVS& vs, typename remove_imaginary<
        typename bindings::value_type< MatrixA >::type >::type& rconde,
        typename remove_imaginary< typename bindings::value_type<
        MatrixA >::type >::type& rcondv, Workspace work ) {
    return geesx_impl< typename bindings::value_type<
            MatrixA >::type >::invoke( jobvs, sort, select, sense, a, sdim, w,
            vs, rconde, rcondv, work );
}

//
// Overloaded function for geesx. Its overload differs for
// * Default workspace-type (optimal)
//
template< typename MatrixA, typename VectorW, typename MatrixVS >
inline typename boost::disable_if< detail::is_workspace< MatrixVS >,
        std::ptrdiff_t >::type
geesx( const char jobvs, const char sort, external_fp select,
        const char sense, MatrixA& a, fortran_int_t& sdim, VectorW& w,
        MatrixVS& vs, typename remove_imaginary<
        typename bindings::value_type< MatrixA >::type >::type& rconde,
        typename remove_imaginary< typename bindings::value_type<
        MatrixA >::type >::type& rcondv ) {
    return geesx_impl< typename bindings::value_type<
            MatrixA >::type >::invoke( jobvs, sort, select, sense, a, sdim, w,
            vs, rconde, rcondv, optimal_workspace() );
}

} // namespace lapack
} // namespace bindings
} // namespace numeric
} // namespace boost

#endif


#include <boost/numeric/odeint/stepper/generation/generation_runge_kutta_fehlberg78.hpp>


//==================================================================================================
/*!
  @file

    @copyright 2016 NumScale SAS

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================
#ifndef BOOST_SIMD_FUNCTION_ARG_HPP_INCLUDED
#define BOOST_SIMD_FUNCTION_ARG_HPP_INCLUDED

#if defined(DOXYGEN_ONLY)
namespace boost { namespace simd
{

  /*!
    @ingroup group-arithmetic
    This function computes the angular orientation of its floating parameter
    (the result is undifined for Nan entries if the @c pedantic_ decorator is not
    used)

    @par Header <boost/simd/function/arg.hpp>

    Using  `arg(x)` is equivalent to  `(is_negative(x)) ? Pi(as(x)) : Zero(as(x))`

    @par Notes

    - always returns \f$+0\f$ or \f$\pi\f$ taking into account the bit of sign
       even in the Nan case.

    - This function implement the restriction to real numbers of the
       complex arg(ument) function.

    @par Decorators

    - `pedantic_`  returns `Nan(as(x))` for a  nan input,

    @see is_negative, Pi, Nan

    @par Example:

      @snippet arg.cpp arg

    @par Possible output:

      @snippet arg.txt arg

  **/
  IEEEValue arg(IEEEValue const & x);
} }
#endif

#include <boost/simd/function/scalar/arg.hpp>
#include <boost/simd/function/simd/arg.hpp>

#endif


//
// Created by Alex Beccaro on 18/01/18.
//

#define BOOST_TEST_DYN_LINK
#include <boost/test/unit_test.hpp>
#include "../../src/problems/1-50/17/problem17.hpp"

BOOST_AUTO_TEST_SUITE( Problem17 )

    BOOST_AUTO_TEST_CASE( Example ) {
        auto res = problems::problem17::solve(5);
        BOOST_CHECK_EQUAL(res, 19);
    }

    BOOST_AUTO_TEST_CASE( Solution ) {
        auto res = problems::problem17::solve();
        BOOST_CHECK_EQUAL(res, 21124);
    }

BOOST_AUTO_TEST_SUITE_END()


/* Author: Markus Buerg, University of Karlsruhe, 2010 */

/*    $Id: step-45.cc 27657 2012-11-21 13:19:08Z bangerth $       */
/*                                                                */
/*    Copyright (C) 2010-2012 by the deal.II authors */
/*                                                                */
/*    This file is subject to QPL and may not be  distributed     */
/*    without copyright and license information. Please refer     */
/*    to the file deal.II/doc/license.html for the  text  and     */
/*    further information on this license.                        */

// @sect3{Include files}

// The include files are already known. The one critical for the current
// program is the one that contains the ConstraintMatrix in the
// lac/ directory:
#include <deal.II/base/function.h>
#include <deal.II/base/quadrature_lib.h>

#include <deal.II/lac/constraint_matrix.h>
#include <deal.II/lac/precondition.h>
#include <deal.II/lac/solver_cg.h>
#include <deal.II/lac/solver_control.h>
#include <deal.II/lac/sparse_matrix.h>
#include <deal.II/lac/sparsity_pattern.h>
#include <deal.II/lac/compressed_sparsity_pattern.h>

#include <deal.II/grid/grid_generator.h>
#include <deal.II/grid/tria.h>

#include <deal.II/dofs/dof_accessor.h>
#include <deal.II/dofs/dof_handler.h>
#include <deal.II/dofs/dof_tools.h>

#include <deal.II/fe/fe_q.h>
#include <deal.II/fe/fe_values.h>

#include <deal.II/numerics/data_out.h>
#include <deal.II/numerics/vector_tools.h>

#include <fstream>

namespace Step45
{
  using namespace dealii;

  // @sect3{The LaplaceProblem class}

  // The class LaplaceProblem is the main class of this
  // problem. As mentioned in the introduction, it is fashioned after the
  // corresponding class in step-3. Correspondingly, the documentation from
  // that tutorial program applies here as well. The only new member variable
  // is the constraints variables that will hold the constraints
  // from the periodic boundary condition. We will initialize it in the
  // make_periodicity_constraints() function which we call from
  // make_grid_and_dofs().
  class LaplaceProblem
  {
  public:
    LaplaceProblem ();
    void run ();

  private:
    Triangulation<2> triangulation;

    FE_Q<2> fe;
    DoFHandler<2> dof_handler;

    ConstraintMatrix constraints;

    SparsityPattern sparsity_pattern;
    SparseMatrix<double> system_matrix;
    Vector<double> system_rhs;
    Vector<double> solution;

    void assemble_system ();
    void output_results ();
    void make_grid_and_dofs ();
    void make_periodicity_constraints ();
    void solve ();
  };

  // @sect3{The RightHandSide class}

  // The following implements the right hand side function discussed in the
  // introduction. Its implementation is obvious given what has been shown in
  // step-4 before:
  class RightHandSide: public Function<2>
  {
  public:
    RightHandSide ();

    virtual double value (const Point<2> &p,
                          const unsigned int component = 0) const;
  };

  RightHandSide::RightHandSide ()
    :
    Function<2> ()
  {}

  double
  RightHandSide::value (const Point<2> &p,
                        const unsigned int) const
  {
    return (std::cos (2 * numbers::PI * p(0)) *
            std::exp (- 2 * p(0)) *
            std::cos (2 * numbers::PI * p(1)) *
            std::exp (- 2 * p(1)));
  }

  // @sect3{Implementation of the LaplaceProblem class}

  // The first part of implementing the main class is the constructor. It is
  // unchanged from step-3 and step-4:
  LaplaceProblem::LaplaceProblem ()
    :
    fe (1),
    dof_handler (triangulation)
  {}

  // @sect4{LaplaceProblem::make_grid_and_dofs}

  // The following is the first function to be called in
  // run(). It sets up the mesh and degrees of freedom.
  //
  // We start by creating the usual square mesh and changing the boundary
  // indicator on the parts of the boundary where we have Dirichlet boundary
  // conditions (top and bottom, i.e. faces two and three of the reference
  // cell as defined by GeometryInfo), so that we can distinguish between the
  // parts of the boundary where periodic and where Dirichlet boundary
  // conditions hold. We then refine the mesh a fixed number of times, with
  // child faces inheriting the boundary indicators previously set on the
  // coarse mesh from their parents.
  void LaplaceProblem::make_grid_and_dofs ()
  {
    GridGenerator::hyper_cube (triangulation);
    triangulation.begin_active ()->face (2)->set_boundary_indicator (1);
    triangulation.begin_active ()->face (3)->set_boundary_indicator (1);
    triangulation.refine_global (5);

    // The next step is to distribute the degrees of freedom and produce a
    // little bit of graphical output:
    dof_handler.distribute_dofs (fe);
    std::cout << "Number of active cells: "
              << triangulation.n_active_cells ()
              << std::endl
              << "Degrees of freedom: " << dof_handler.n_dofs ()
              << std::endl;

    // Now it is the time for the constraints that come from the periodicity
    // constraints. We do this in the following, separate function, after
    // clearing any possible prior content from the constraints object:
    constraints.clear ();
    make_periodicity_constraints ();

    // We also incorporate the homogeneous Dirichlet boundary conditions on
    // the upper and lower parts of the boundary (i.e. the ones with boundary
    // indicator 1) and close the ConstraintMatrix object:
    VectorTools::interpolate_boundary_values (dof_handler, 1,
                                              ZeroFunction<2> (),
                                              constraints);
    constraints.close ();

    // Then we create the sparsity pattern and the system matrix and
    // initialize the solution and right-hand side vectors. This is again as
    // in step-3 or step-6, for example:
    CompressedSparsityPattern c_sparsity_pattern (dof_handler.n_dofs(),
                                                  dof_handler.n_dofs());
    DoFTools::make_sparsity_pattern (dof_handler,
                                     c_sparsity_pattern,
                                     constraints,
                                     false);
    c_sparsity_pattern.compress ();
    sparsity_pattern.copy_from (c_sparsity_pattern);

    system_matrix.reinit (sparsity_pattern);
    system_rhs.reinit (dof_handler.n_dofs());
    solution.reinit (dof_handler.n_dofs());
  }

  // @sect4{LaplaceProblem::make_periodicity_constraints}

  // This is the function that provides the new material of this tutorial
  // program. The general outline of the algorithm is as follows: we first
  // loop over all the degrees of freedom on the right boundary and record
  // their $y$-locations in a map together with their global indices. Then we
  // go along the left boundary, find matching $y$-locations for each degree
  // of freedom, and then add constraints that identify these matched degrees
  // of freedom.
  //
  // In this function, we make use of the fact that we have a scalar element
  // (i.e. the only valid vector component that can be passed to
  // DoFAccessor::vertex_dof_index is zero) and that we have a $Q_1$ element
  // for which all degrees of freedom live in the vertices of the
  // cell. Furthermore, we have assumed that we are in 2d and that meshes were
  // not refined adaptively — the latter assumption would imply that
  // there may be vertices that aren't matched one-to-one and for which we
  // won't be able to compute constraints this easily. We will discuss in the
  // "outlook" part of the results section below other strategies to write the
  // current function that can work in cases like this as well.
  void LaplaceProblem::make_periodicity_constraints ()
  {
    // To start with the actual implementation, we loop over all active cells
    // and check whether the cell is located at the right boundary (i.e. face
    // 1 — the one at the right end of the cell — is at the
    // boundary). If that is so, then we use that for the currently used
    // finite element, each degree of freedom of the face is located on one
    // vertex, and store their $y$-coordinate along with the global number of
    // this degree of freedom in the following map:
    std::map<unsigned int, double> dof_locations;

    for (DoFHandler<2>::active_cell_iterator cell = dof_handler.begin_active ();
         cell != dof_handler.end (); ++cell)
      if (cell->at_boundary ()
          &&
          cell->face(1)->at_boundary ())
        {
          dof_locations[cell->face(1)->vertex_dof_index(0, 0)]
            = cell->face(1)->vertex(0)[1];
          dof_locations[cell->face(1)->vertex_dof_index(1, 0)]
            = cell->face(1)->vertex(1)[1];
        }
    // Note that in the above block, we add vertices zero and one of the
    // affected face to the map. This means that we will add each vertex
    // twice, once from each of the two adjacent cells (unless the vertex is a
    // corner of the domain). Since the coordinates of the vertex are the same
    // both times of course, there is no harm: we replace one value in the map
    // with itself the second time we visit an entry.
    //
    // The same will be true below where we add the same constraint twice to
    // the ConstraintMatrix — again, we will overwrite the constraint
    // with itself, and no harm is done.

    // Now we have to find the corresponding degrees of freedom on the left
    // part of the boundary. Therefore we loop over all cells again and choose
    // the ones where face 0 is at the boundary:
    for (DoFHandler<2>::active_cell_iterator cell = dof_handler.begin_active ();
         cell != dof_handler.end (); ++cell)
      if (cell->at_boundary ()
          &&
          cell->face (0)->at_boundary ())
        {
          // Every degree of freedom on this face needs to have a
          // corresponding one on the right side of the face, and our goal is
          // to add a constraint for the one on the left in terms of the one
          // on the right. To this end we first add a new line to the
          // constraint matrix for this one degree of freedom. Then we
          // identify it with the corresponding degree of freedom on the right
          // part of the boundary by constraining the degree of freedom on the
          // left with the one on the right times a weight of 1.0.
          //
          // Consequently, we loop over the two vertices of each face we find
          // and then loop over all the $y$-locations we've previously
          // recorded to find which degree of freedom on the right boundary
          // corresponds to the one we currently look at. Note that we have
          // entered these into a map, and when looping over the iterators
          // p of this map, p-@>first corresponds to
          // the "key" of an entry (the global number of the degree of
          // freedom), whereas p-@>second is the "value" (the
          // $y$-location we have entered above).
          //
          // We are quite sure here that we should be finding such a
          // corresponding degree of freedom. However, sometimes stuff happens
          // and so the bottom of the block contains an assertion that our
          // assumption was indeed correct and that a vertex was found.
          for (unsigned int face_vertex = 0; face_vertex<2; ++face_vertex)
            {
              constraints.add_line (cell->face(0)->vertex_dof_index (face_vertex, 0));

              std::map<unsigned int, double>::const_iterator p = dof_locations.begin();
              for (; p != dof_locations.end(); ++p)
                if (std::fabs(p->second - cell->face(0)->vertex(face_vertex)[1]) < 1e-8)
                  {
                    constraints.add_entry (cell->face(0)->vertex_dof_index (face_vertex, 0),
                                           p->first, 1.0);
                    break;
                  }
              Assert (p != dof_locations.end(),
                      ExcMessage ("No corresponding degree of freedom was found!"));
            }
        }
  }

  // @sect4{LaplaceProblem::assemble_system}

  // Assembling the system matrix and the right-hand side vector is done as in
  // other tutorials before.
  //
  // The only difference here is that we don't copy elements from local
  // contributions into the global matrix and later fix up constrained degrees
  // of freedom, but that we let the ConstraintMatrix do this job in one swoop
  // for us using the ConstraintMatrix::distribute_local_to_global
  // function(). This was previously already demonstrated in step-16, step-22,
  // for example, along with a discussion in the introduction of step-27.
  void LaplaceProblem::assemble_system ()
  {
    QGauss<2>  quadrature_formula(2);
    FEValues<2> fe_values (fe, quadrature_formula,
                           update_values   | update_gradients |
                           update_quadrature_points | update_JxW_values);

    const unsigned int   dofs_per_cell = fe.dofs_per_cell;
    const unsigned int   n_q_points    = quadrature_formula.size();

    FullMatrix<double>   cell_matrix (dofs_per_cell, dofs_per_cell);
    Vector<double>       cell_rhs (dofs_per_cell);

    std::vector<unsigned int> local_dof_indices (dofs_per_cell);

    const RightHandSide right_hand_side;

    DoFHandler<2>::active_cell_iterator cell = dof_handler.begin_active(),
                                        endc = dof_handler.end();
    for (; cell!=endc; ++cell)
      {
        fe_values.reinit (cell);
        cell_matrix = 0;
        cell_rhs = 0;

        for (unsigned int q_point=0; q_point<n_q_points; ++q_point)
          for (unsigned int i=0; i<dofs_per_cell; ++i)
            {
              for (unsigned int j=0; j<dofs_per_cell; ++j)
                cell_matrix(i,j) += (fe_values.shape_grad (i, q_point) *
                                     fe_values.shape_grad (j, q_point) *
                                     fe_values.JxW (q_point));

              cell_rhs(i) += (fe_values.shape_value (i, q_point) *
                              right_hand_side.value (fe_values.quadrature_point (q_point)) *
                              fe_values.JxW (q_point));
            }

        cell->get_dof_indices (local_dof_indices);
        constraints.distribute_local_to_global (cell_matrix, cell_rhs,
                                                local_dof_indices,
                                                system_matrix, system_rhs);
      }
  }

  // @sect4{LaplaceProblem::solve}

  // To solve the linear system of equations $Au=b$ we use the CG solver with
  // an SSOR-preconditioner. This is, again, copied almost verbatim from
  // step-6. As in step-6, we need to make sure that constrained degrees of
  // freedom get their correct values after solving by calling the
  // ConstraintMatrix::distribute function:
  void LaplaceProblem::solve ()
  {
    SolverControl solver_control (dof_handler.n_dofs (), 1e-12);
    PreconditionSSOR<SparseMatrix<double> > precondition;

    precondition.initialize (system_matrix);

    SolverCG<> cg (solver_control);

    cg.solve (system_matrix, solution, system_rhs, precondition);
    constraints.distribute (solution);
  }

  // @sect4{LaplaceProblem::output_results}

  // This is another function copied from previous tutorial programs. It
  // generates graphical output in VTK format:
  void LaplaceProblem::output_results ()
  {
    DataOut<2> data_out;

    data_out.attach_dof_handler (dof_handler);
    data_out.add_data_vector (solution, "u");
    data_out.build_patches ();

    std::ofstream output ("solution.vtk");

    data_out.write_vtk (output);
  }

  // @sect4{LaplaceProblem::run}

  // And another function copied from previous programs:
  void LaplaceProblem::run ()
  {
    make_grid_and_dofs();
    assemble_system ();
    solve ();
    output_results ();
  }
}

// @sect3{The main function}

// And at the end we have the main function as usual, this time copied from
// step-6:
int main ()
{
  try
    {
      using namespace dealii;
      using namespace Step45;

      deallog.depth_console (0);

      LaplaceProblem laplace_problem;
      laplace_problem.run ();
    }
  catch (std::exception &exc)
    {
      std::cerr << std::endl << std::endl
                << "----------------------------------------------------"
                << std::endl;
      std::cerr << "Exception on processing: " << std::endl
                << exc.what() << std::endl
                << "Aborting!" << std::endl
                << "----------------------------------------------------"
                << std::endl;

      return 1;
    }
  catch (...)
    {
      std::cerr << std::endl << std::endl
                << "----------------------------------------------------"
                << std::endl;
      std::cerr << "Unknown exception!" << std::endl
                << "Aborting!" << std::endl
                << "----------------------------------------------------"
                << std::endl;
      return 1;
    }

  return 0;
}