//
// 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_GEEVX_HPP
#define BOOST_NUMERIC_BINDINGS_LAPACK_DRIVER_GEEVX_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 geevx 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 geevx( const char balanc, const char jobvl,
        const char jobvr, const char sense, const fortran_int_t n, float* a,
        const fortran_int_t lda, float* wr, float* wi, float* vl,
        const fortran_int_t ldvl, float* vr, const fortran_int_t ldvr,
        fortran_int_t& ilo, fortran_int_t& ihi, float* scale, float& abnrm,
        float* rconde, float* rcondv, float* work, const fortran_int_t lwork,
        fortran_int_t* iwork ) {
    fortran_int_t info(0);
    LAPACK_SGEEVX( &balanc, &jobvl, &jobvr, &sense, &n, a, &lda, wr, wi, vl,
            &ldvl, vr, &ldvr, &ilo, &ihi, scale, &abnrm, rconde, rcondv, work,
            &lwork, iwork, &info );
    return info;
}

//
// Overloaded function for dispatching to
// * netlib-compatible LAPACK backend (the default), and
// * double value-type.
//
inline std::ptrdiff_t geevx( const char balanc, const char jobvl,
        const char jobvr, const char sense, const fortran_int_t n, double* a,
        const fortran_int_t lda, double* wr, double* wi, double* vl,
        const fortran_int_t ldvl, double* vr, const fortran_int_t ldvr,
        fortran_int_t& ilo, fortran_int_t& ihi, double* scale, double& abnrm,
        double* rconde, double* rcondv, double* work,
        const fortran_int_t lwork, fortran_int_t* iwork ) {
    fortran_int_t info(0);
    LAPACK_DGEEVX( &balanc, &jobvl, &jobvr, &sense, &n, a, &lda, wr, wi, vl,
            &ldvl, vr, &ldvr, &ilo, &ihi, scale, &abnrm, rconde, rcondv, work,
            &lwork, iwork, &info );
    return info;
}

//
// Overloaded function for dispatching to
// * netlib-compatible LAPACK backend (the default), and
// * complex<float> value-type.
//
inline std::ptrdiff_t geevx( const char balanc, const char jobvl,
        const char jobvr, const char sense, const fortran_int_t n,
        std::complex<float>* a, const fortran_int_t lda,
        std::complex<float>* w, std::complex<float>* vl,
        const fortran_int_t ldvl, std::complex<float>* vr,
        const fortran_int_t ldvr, fortran_int_t& ilo, fortran_int_t& ihi,
        float* scale, float& abnrm, float* rconde, float* rcondv,
        std::complex<float>* work, const fortran_int_t lwork, float* rwork ) {
    fortran_int_t info(0);
    LAPACK_CGEEVX( &balanc, &jobvl, &jobvr, &sense, &n, a, &lda, w, vl, &ldvl,
            vr, &ldvr, &ilo, &ihi, scale, &abnrm, rconde, rcondv, work,
            &lwork, rwork, &info );
    return info;
}

//
// Overloaded function for dispatching to
// * netlib-compatible LAPACK backend (the default), and
// * complex<double> value-type.
//
inline std::ptrdiff_t geevx( const char balanc, const char jobvl,
        const char jobvr, const char sense, const fortran_int_t n,
        std::complex<double>* a, const fortran_int_t lda,
        std::complex<double>* w, std::complex<double>* vl,
        const fortran_int_t ldvl, std::complex<double>* vr,
        const fortran_int_t ldvr, fortran_int_t& ilo, fortran_int_t& ihi,
        double* scale, double& abnrm, double* rconde, double* rcondv,
        std::complex<double>* work, const fortran_int_t lwork,
        double* rwork ) {
    fortran_int_t info(0);
    LAPACK_ZGEEVX( &balanc, &jobvl, &jobvr, &sense, &n, a, &lda, w, vl, &ldvl,
            vr, &ldvr, &ilo, &ihi, scale, &abnrm, rconde, rcondv, work,
            &lwork, rwork, &info );
    return info;
}

} // namespace detail

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

//
// This implementation is enabled if Value is a real type.
//
template< typename Value >
struct geevx_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 MatrixVL, typename MatrixVR, typename VectorSCALE,
            typename VectorRCONDE, typename VectorRCONDV, typename WORK,
            typename IWORK >
    static std::ptrdiff_t invoke( const char balanc, const char jobvl,
            const char jobvr, const char sense, MatrixA& a, VectorWR& wr,
            VectorWI& wi, MatrixVL& vl, MatrixVR& vr, fortran_int_t& ilo,
            fortran_int_t& ihi, VectorSCALE& scale, real_type& abnrm,
            VectorRCONDE& rconde, VectorRCONDV& rcondv, detail::workspace2<
            WORK, IWORK > work ) {
        namespace bindings = ::boost::numeric::bindings;
        BOOST_STATIC_ASSERT( (bindings::is_column_major< MatrixA >::value) );
        BOOST_STATIC_ASSERT( (bindings::is_column_major< MatrixVL >::value) );
        BOOST_STATIC_ASSERT( (bindings::is_column_major< MatrixVR >::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<
                MatrixVL >::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<
                MatrixVR >::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<
                VectorSCALE >::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<
                VectorRCONDE >::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<
                VectorRCONDV >::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< MatrixVL >::value) );
        BOOST_STATIC_ASSERT( (bindings::is_mutable< MatrixVR >::value) );
        BOOST_STATIC_ASSERT( (bindings::is_mutable< VectorSCALE >::value) );
        BOOST_STATIC_ASSERT( (bindings::is_mutable< VectorRCONDE >::value) );
        BOOST_STATIC_ASSERT( (bindings::is_mutable< VectorRCONDV >::value) );
        BOOST_ASSERT( bindings::size(rconde) >= bindings::size_column(a) );
        BOOST_ASSERT( bindings::size(rcondv) >= bindings::size_column(a) );
        BOOST_ASSERT( bindings::size(wi) >= bindings::size_column(a) );
        BOOST_ASSERT( bindings::size(work.select(fortran_int_t())) >=
                min_size_iwork( sense, bindings::size_column(a) ));
        BOOST_ASSERT( bindings::size(work.select(real_type())) >=
                min_size_work( sense, jobvl, jobvr,
                bindings::size_column(a) ));
        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(vl) == 1 ||
                bindings::stride_minor(vl) == 1 );
        BOOST_ASSERT( bindings::size_minor(vr) == 1 ||
                bindings::stride_minor(vr) == 1 );
        BOOST_ASSERT( bindings::stride_major(a) >= std::max< std::ptrdiff_t >(1,
                bindings::size_column(a)) );
        BOOST_ASSERT( balanc == 'N' || balanc == 'P' || balanc == 'S' ||
                balanc == 'B' );
        BOOST_ASSERT( jobvl == 'N' || jobvl == 'V' || jobvl == 'E' ||
                jobvl == 'B' );
        BOOST_ASSERT( jobvr == 'N' || jobvr == 'V' || jobvr == 'E' ||
                jobvr == 'B' );
        BOOST_ASSERT( sense == 'N' || sense == 'E' || sense == 'V' ||
                sense == 'B' );
        return detail::geevx( balanc, jobvl, jobvr, sense,
                bindings::size_column(a), bindings::begin_value(a),
                bindings::stride_major(a), bindings::begin_value(wr),
                bindings::begin_value(wi), bindings::begin_value(vl),
                bindings::stride_major(vl), bindings::begin_value(vr),
                bindings::stride_major(vr), ilo, ihi,
                bindings::begin_value(scale), abnrm,
                bindings::begin_value(rconde), bindings::begin_value(rcondv),
                bindings::begin_value(work.select(real_type())),
                bindings::size(work.select(real_type())),
                bindings::begin_value(work.select(fortran_int_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 MatrixVL, typename MatrixVR, typename VectorSCALE,
            typename VectorRCONDE, typename VectorRCONDV >
    static std::ptrdiff_t invoke( const char balanc, const char jobvl,
            const char jobvr, const char sense, MatrixA& a, VectorWR& wr,
            VectorWI& wi, MatrixVL& vl, MatrixVR& vr, fortran_int_t& ilo,
            fortran_int_t& ihi, VectorSCALE& scale, real_type& abnrm,
            VectorRCONDE& rconde, VectorRCONDV& rcondv, minimal_workspace ) {
        namespace bindings = ::boost::numeric::bindings;
        bindings::detail::array< real_type > tmp_work( min_size_work( sense,
                jobvl, jobvr, bindings::size_column(a) ) );
        bindings::detail::array< fortran_int_t > tmp_iwork(
                min_size_iwork( sense, bindings::size_column(a) ) );
        return invoke( balanc, jobvl, jobvr, sense, a, wr, wi, vl, vr, ilo,
                ihi, scale, abnrm, rconde, rcondv, workspace( tmp_work,
                tmp_iwork ) );
    }

    //
    // 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 MatrixVL, typename MatrixVR, typename VectorSCALE,
            typename VectorRCONDE, typename VectorRCONDV >
    static std::ptrdiff_t invoke( const char balanc, const char jobvl,
            const char jobvr, const char sense, MatrixA& a, VectorWR& wr,
            VectorWI& wi, MatrixVL& vl, MatrixVR& vr, fortran_int_t& ilo,
            fortran_int_t& ihi, VectorSCALE& scale, real_type& abnrm,
            VectorRCONDE& rconde, VectorRCONDV& rcondv, optimal_workspace ) {
        namespace bindings = ::boost::numeric::bindings;
        real_type opt_size_work;
        bindings::detail::array< fortran_int_t > tmp_iwork(
                min_size_iwork( sense, bindings::size_column(a) ) );
        detail::geevx( balanc, jobvl, jobvr, sense,
                bindings::size_column(a), bindings::begin_value(a),
                bindings::stride_major(a), bindings::begin_value(wr),
                bindings::begin_value(wi), bindings::begin_value(vl),
                bindings::stride_major(vl), bindings::begin_value(vr),
                bindings::stride_major(vr), ilo, ihi,
                bindings::begin_value(scale), abnrm,
                bindings::begin_value(rconde), bindings::begin_value(rcondv),
                &opt_size_work, -1, bindings::begin_value(tmp_iwork) );
        bindings::detail::array< real_type > tmp_work(
                traits::detail::to_int( opt_size_work ) );
        return invoke( balanc, jobvl, jobvr, sense, a, wr, wi, vl, vr, ilo,
                ihi, scale, abnrm, rconde, rcondv, workspace( tmp_work,
                tmp_iwork ) );
    }

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

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

//
// This implementation is enabled if Value is a complex type.
//
template< typename Value >
struct geevx_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 MatrixVL,
            typename MatrixVR, typename VectorSCALE, typename VectorRCONDE,
            typename VectorRCONDV, typename WORK, typename RWORK >
    static std::ptrdiff_t invoke( const char balanc, const char jobvl,
            const char jobvr, const char sense, MatrixA& a, VectorW& w,
            MatrixVL& vl, MatrixVR& vr, fortran_int_t& ilo,
            fortran_int_t& ihi, VectorSCALE& scale, real_type& abnrm,
            VectorRCONDE& rconde, VectorRCONDV& rcondv, detail::workspace2<
            WORK, RWORK > work ) {
        namespace bindings = ::boost::numeric::bindings;
        BOOST_STATIC_ASSERT( (bindings::is_column_major< MatrixA >::value) );
        BOOST_STATIC_ASSERT( (bindings::is_column_major< MatrixVL >::value) );
        BOOST_STATIC_ASSERT( (bindings::is_column_major< MatrixVR >::value) );
        BOOST_STATIC_ASSERT( (boost::is_same< typename remove_const<
                typename bindings::value_type< VectorSCALE >::type >::type,
                typename remove_const< typename bindings::value_type<
                VectorRCONDE >::type >::type >::value) );
        BOOST_STATIC_ASSERT( (boost::is_same< typename remove_const<
                typename bindings::value_type< VectorSCALE >::type >::type,
                typename remove_const< typename bindings::value_type<
                VectorRCONDV >::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<
                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<
                MatrixVL >::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<
                MatrixVR >::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< MatrixVL >::value) );
        BOOST_STATIC_ASSERT( (bindings::is_mutable< MatrixVR >::value) );
        BOOST_STATIC_ASSERT( (bindings::is_mutable< VectorSCALE >::value) );
        BOOST_STATIC_ASSERT( (bindings::is_mutable< VectorRCONDE >::value) );
        BOOST_STATIC_ASSERT( (bindings::is_mutable< VectorRCONDV >::value) );
        BOOST_ASSERT( bindings::size(rconde) >= bindings::size_column(a) );
        BOOST_ASSERT( bindings::size(rcondv) >= bindings::size_column(a) );
        BOOST_ASSERT( bindings::size(w) >= bindings::size_column(a) );
        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( sense, 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(vl) == 1 ||
                bindings::stride_minor(vl) == 1 );
        BOOST_ASSERT( bindings::size_minor(vr) == 1 ||
                bindings::stride_minor(vr) == 1 );
        BOOST_ASSERT( bindings::stride_major(a) >= std::max< std::ptrdiff_t >(1,
                bindings::size_column(a)) );
        BOOST_ASSERT( balanc == 'N' || balanc == 'P' || balanc == 'S' ||
                balanc == 'B' );
        BOOST_ASSERT( jobvl == 'N' || jobvl == 'V' || jobvl == 'E' ||
                jobvl == 'B' );
        BOOST_ASSERT( jobvr == 'N' || jobvr == 'V' || jobvr == 'E' ||
                jobvr == 'B' );
        BOOST_ASSERT( sense == 'N' || sense == 'E' || sense == 'V' ||
                sense == 'B' );
        return detail::geevx( balanc, jobvl, jobvr, sense,
                bindings::size_column(a), bindings::begin_value(a),
                bindings::stride_major(a), bindings::begin_value(w),
                bindings::begin_value(vl), bindings::stride_major(vl),
                bindings::begin_value(vr), bindings::stride_major(vr), ilo,
                ihi, bindings::begin_value(scale), abnrm,
                bindings::begin_value(rconde), bindings::begin_value(rcondv),
                bindings::begin_value(work.select(value_type())),
                bindings::size(work.select(value_type())),
                bindings::begin_value(work.select(real_type())) );
    }

    //
    // 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 MatrixVL,
            typename MatrixVR, typename VectorSCALE, typename VectorRCONDE,
            typename VectorRCONDV >
    static std::ptrdiff_t invoke( const char balanc, const char jobvl,
            const char jobvr, const char sense, MatrixA& a, VectorW& w,
            MatrixVL& vl, MatrixVR& vr, fortran_int_t& ilo,
            fortran_int_t& ihi, VectorSCALE& scale, real_type& abnrm,
            VectorRCONDE& rconde, VectorRCONDV& rcondv, minimal_workspace ) {
        namespace bindings = ::boost::numeric::bindings;
        bindings::detail::array< value_type > tmp_work( min_size_work( sense,
                bindings::size_column(a) ) );
        bindings::detail::array< real_type > tmp_rwork( min_size_rwork(
                bindings::size_column(a) ) );
        return invoke( balanc, jobvl, jobvr, sense, a, w, vl, vr, ilo, ihi,
                scale, abnrm, rconde, rcondv, workspace( tmp_work,
                tmp_rwork ) );
    }

    //
    // 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 MatrixVL,
            typename MatrixVR, typename VectorSCALE, typename VectorRCONDE,
            typename VectorRCONDV >
    static std::ptrdiff_t invoke( const char balanc, const char jobvl,
            const char jobvr, const char sense, MatrixA& a, VectorW& w,
            MatrixVL& vl, MatrixVR& vr, fortran_int_t& ilo,
            fortran_int_t& ihi, VectorSCALE& scale, real_type& abnrm,
            VectorRCONDE& rconde, VectorRCONDV& 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) ) );
        detail::geevx( balanc, jobvl, jobvr, sense,
                bindings::size_column(a), bindings::begin_value(a),
                bindings::stride_major(a), bindings::begin_value(w),
                bindings::begin_value(vl), bindings::stride_major(vl),
                bindings::begin_value(vr), bindings::stride_major(vr), ilo,
                ihi, bindings::begin_value(scale), abnrm,
                bindings::begin_value(rconde), bindings::begin_value(rcondv),
                &opt_size_work, -1, bindings::begin_value(tmp_rwork) );
        bindings::detail::array< value_type > tmp_work(
                traits::detail::to_int( opt_size_work ) );
        return invoke( balanc, jobvl, jobvr, sense, a, w, vl, vr, ilo, ihi,
                scale, abnrm, rconde, rcondv, workspace( tmp_work,
                tmp_rwork ) );
    }

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

    //
    // 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 2*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 geevx_impl classes. In the
// documentation, most overloads are collapsed to avoid a large number of
// prototypes which are very similar.
//

//
// Overloaded function for geevx. Its overload differs for
// * User-defined workspace
//
template< typename MatrixA, typename VectorWR, typename VectorWI,
        typename MatrixVL, typename MatrixVR, typename VectorSCALE,
        typename VectorRCONDE, typename VectorRCONDV, typename Workspace >
inline typename boost::enable_if< detail::is_workspace< Workspace >,
        std::ptrdiff_t >::type
geevx( const char balanc, const char jobvl, const char jobvr,
        const char sense, MatrixA& a, VectorWR& wr, VectorWI& wi,
        MatrixVL& vl, MatrixVR& vr, fortran_int_t& ilo,
        fortran_int_t& ihi, VectorSCALE& scale, typename remove_imaginary<
        typename bindings::value_type< MatrixA >::type >::type& abnrm,
        VectorRCONDE& rconde, VectorRCONDV& rcondv, Workspace work ) {
    return geevx_impl< typename bindings::value_type<
            MatrixA >::type >::invoke( balanc, jobvl, jobvr, sense, a, wr, wi,
            vl, vr, ilo, ihi, scale, abnrm, rconde, rcondv, work );
}

//
// Overloaded function for geevx. Its overload differs for
// * Default workspace-type (optimal)
//
template< typename MatrixA, typename VectorWR, typename VectorWI,
        typename MatrixVL, typename MatrixVR, typename VectorSCALE,
        typename VectorRCONDE, typename VectorRCONDV >
inline typename boost::disable_if< detail::is_workspace< VectorRCONDV >,
        std::ptrdiff_t >::type
geevx( const char balanc, const char jobvl, const char jobvr,
        const char sense, MatrixA& a, VectorWR& wr, VectorWI& wi,
        MatrixVL& vl, MatrixVR& vr, fortran_int_t& ilo,
        fortran_int_t& ihi, VectorSCALE& scale, typename remove_imaginary<
        typename bindings::value_type< MatrixA >::type >::type& abnrm,
        VectorRCONDE& rconde, VectorRCONDV& rcondv ) {
    return geevx_impl< typename bindings::value_type<
            MatrixA >::type >::invoke( balanc, jobvl, jobvr, sense, a, wr, wi,
            vl, vr, ilo, ihi, scale, abnrm, rconde, rcondv,
            optimal_workspace() );
}

//
// Overloaded function for geevx. Its overload differs for
// * User-defined workspace
//
template< typename MatrixA, typename VectorW, typename MatrixVL,
        typename MatrixVR, typename VectorSCALE, typename VectorRCONDE,
        typename VectorRCONDV, typename Workspace >
inline typename boost::enable_if< detail::is_workspace< Workspace >,
        std::ptrdiff_t >::type
geevx( const char balanc, const char jobvl, const char jobvr,
        const char sense, MatrixA& a, VectorW& w, MatrixVL& vl, MatrixVR& vr,
        fortran_int_t& ilo, fortran_int_t& ihi, VectorSCALE& scale,
        typename remove_imaginary< typename bindings::value_type<
        MatrixA >::type >::type& abnrm, VectorRCONDE& rconde,
        VectorRCONDV& rcondv, Workspace work ) {
    return geevx_impl< typename bindings::value_type<
            MatrixA >::type >::invoke( balanc, jobvl, jobvr, sense, a, w, vl,
            vr, ilo, ihi, scale, abnrm, rconde, rcondv, work );
}

//
// Overloaded function for geevx. Its overload differs for
// * Default workspace-type (optimal)
//
template< typename MatrixA, typename VectorW, typename MatrixVL,
        typename MatrixVR, typename VectorSCALE, typename VectorRCONDE,
        typename VectorRCONDV >
inline typename boost::disable_if< detail::is_workspace< VectorRCONDV >,
        std::ptrdiff_t >::type
geevx( const char balanc, const char jobvl, const char jobvr,
        const char sense, MatrixA& a, VectorW& w, MatrixVL& vl, MatrixVR& vr,
        fortran_int_t& ilo, fortran_int_t& ihi, VectorSCALE& scale,
        typename remove_imaginary< typename bindings::value_type<
        MatrixA >::type >::type& abnrm, VectorRCONDE& rconde,
        VectorRCONDV& rcondv ) {
    return geevx_impl< typename bindings::value_type<
            MatrixA >::type >::invoke( balanc, jobvl, jobvr, sense, a, w, vl,
            vr, ilo, ihi, scale, abnrm, rconde, rcondv, optimal_workspace() );
}

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

#endif


#ifndef PHYSYCOM_UTILS_VORONOI_HPP
#define PHYSYCOM_UTILS_VORONOI_HPP

#include <iostream>
#include <fstream>
#include <sstream>
#include <cstdio>
#include <vector>

#include <boost/polygon/voronoi.hpp>

using boost::polygon::voronoi_builder;
using boost::polygon::voronoi_diagram;
using boost::polygon::x;
using boost::polygon::y;
using boost::polygon::low;
using boost::polygon::high;

namespace physycom
{
  constexpr double PI = 3.1415926535;
  inline double atan2_2pi(const double &y, const double &x)
  {
    double th = std::atan2(y,x);
    return (th >= 0) ? th : 2*PI + th;
  }

  struct Point
  {
    int x, y;
    Point() : x(0), y(0) {}
    Point(int x, int y) : x(x), y(y) {}
    bool operator==(const Point &p)
    {
      return x == p.x && y == p.y;
    }
    bool operator!=(const Point &p)
    {
      return ! (*this == p);
    }
    friend std::ostream &operator<<(std::ostream &stream, const Point &p)
    {
      stream << "[" << p.x << "," << p.y << "]";
      return stream;
    }
  };

  struct Segment
  {
    Point p0, p1;
    Segment(int x1, int y1, int x2, int y2) : p0(x1, y1), p1(x2, y2) {}
  };

  struct Box
  {
    int xmin, xmax, ymin, ymax;
    Box() : xmin(0), xmax(0), ymin(0), ymax(0) {}
    Box(const int &xmin, const int &xmax, const int &ymin, const int &ymax) : xmin(xmin), xmax(xmax), ymin(ymin), ymax(ymax) {}
    bool contains(const Point &p)
    {
      return p.x > xmin && p.x < xmax && p.y > ymin && p.y < ymax;
    }
    void encompass(const Point &p)
    {
      if (xmin > p.x)
        xmin = (p.x > 0) ? 0.9*p.x : 1.1*p.x;
      if (xmax < p.x)
        xmax = (p.x > 0) ? 1.1*p.x : 0.9*p.x;
      if (ymin > p.y)
        ymin = (p.y > 0) ? 0.9*p.y : 1.1*p.y;
      if (xmax < p.x)
        ymax = (p.y > 0) ? 1.1*p.y : 0.9*p.y;
    }
    friend std::ostream &operator<<(std::ostream &stream, const Box &b)
    {
      stream << "[" << b.xmin << "," << b.xmax << "]x[" << b.ymin << "," << b.ymax << "]";
      return stream;
    }
  };

  struct Polygon
  {
    std::vector<Point> points;
    Polygon() {}
    Polygon(std::vector<Point> &points) : points(points) {}
  };

} // physycom

namespace boost
{
  namespace polygon
  {
    using namespace physycom;

    template <>
    struct geometry_concept<Point>
    {
      typedef point_concept type;
    };

    template <>
    struct point_traits<Point>
    {
      typedef int coordinate_type;

      static inline coordinate_type get(const Point& point, orientation_2d orient)
      {
        return (orient == HORIZONTAL) ? point.x : point.y;
      }
    };

    template <>
    struct geometry_concept<Segment>
    {
      typedef segment_concept type;
    };

    template <>
    struct segment_traits<Segment>
    {
      typedef int coordinate_type;
      typedef Point point_type;

      static inline point_type get(const Segment& segment, direction_1d dir)
      {
        return dir.to_int() ? segment.p1 : segment.p0;
      }
    };
  }  // polygon
}  // boost

namespace physycom
{
  struct voronoi
  {
    std::vector<Point> points, points_voro;
    Box bounds;
    std::vector<Polygon> cells;
    voronoi_diagram<double> vd;

    voronoi() {}
    voronoi(const std::vector<Point> &points, const Box &bounds) : points(points), bounds(bounds)
    {
      construct_voronoi(this->points.begin(), this->points.end(), &(this->vd));
      int cell_index = 0;
      for (auto cell = vd.cells().begin(); cell != vd.cells().end(); ++cell)
      {
        Polygon pg;
        if (cell->contains_point())
        {
          if (cell->source_category() == boost::polygon::SOURCE_CATEGORY_SINGLE_POINT)
          {
            // retrieve point in cell
            std::size_t index = cell->source_index();
            Point p = this->points[index];
            points_voro.push_back(p);
            std::cout << "cell #" << cell_index << std::endl;

            // loop over cell edges
            auto *edge = cell->incident_edge();
            int edge_cnt = 0;
            do
            {
              edge = edge->next();
              if (edge->is_finite())
              {
                Point p0 = Point(edge->vertex0()->x(), edge->vertex0()->y());
                Point p1 = Point(edge->vertex1()->x(), edge->vertex1()->y());
                std::cout << "FINITE " << p0 << " " << p1 << std::endl;
                if (!pg.points.size())
                  pg.points.push_back(p0);
                pg.points.push_back(p1);
              }
              else if (edge->is_infinite())
              {
                auto cell1 = edge->cell();
                auto cell2 = edge->twin()->cell();
                Point origin, direction;
                std::vector<Point> clipped_edge;
                if (cell1->contains_point() && cell2->contains_point())
                {
                  Point p1 = points[edge->cell()->source_index()];
                  Point p2 = points[edge->twin()->cell()->source_index()];
                  origin.x = (p1.x + p2.x) * 0.5;
                  origin.y = (p1.y + p2.y) * 0.5;
                  direction.x = p1.y - p2.y;
                  direction.y = p2.x - p1.x;
                }

                double theta1 = physycom::atan2_2pi(bounds.ymax - origin.y, bounds.xmax - origin.x);
                double theta2 = physycom::atan2_2pi(bounds.ymax - origin.y, bounds.xmin - origin.x);
                double theta3 = physycom::atan2_2pi(bounds.ymin - origin.y, bounds.xmin - origin.x);
                double theta4 = physycom::atan2_2pi(bounds.ymin - origin.y, bounds.xmax - origin.x);

/*
                std::cout << "t1 " << theta1 * 180 / PI << " " << bounds.ymax - origin.y << " " << bounds.xmax - origin.x << std::endl
                          << "t2 " << theta2 * 180 / PI << " " << bounds.ymax - origin.y << " " << bounds.xmin - origin.x << std::endl
                          << "t3 " << theta3 * 180 / PI << " " << bounds.ymin - origin.y << " " << bounds.xmin - origin.x << std::endl
                          << "t4 " << theta4 * 180 / PI << " " << bounds.ymin - origin.y << " " << bounds.xmax - origin.x << std::endl;
*/

                if (edge->vertex0() == NULL)
                {
                  direction.x *= -1;
                  direction.y *= -1;
                }
                double thetad = physycom::atan2_2pi(direction.y, direction.x);

                Point clipped;
                if ( thetad <= theta1 || thetad > theta4 )
                  clipped = Point(bounds.xmax, origin.y + ( direction.y * ( bounds.xmax - origin.x) ) / direction.x );
                else if ( thetad <= theta2 )
                  clipped = Point(origin.x + ( direction.x * ( bounds.ymax - origin.y) ) / direction.y, bounds.ymax );
                else if ( thetad <= theta3 )
                  clipped = Point(bounds.xmin, origin.y - ( direction.y * ( bounds.xmin - origin.x) ) / direction.x );
                else if ( thetad <= theta4 )
                  clipped = Point(origin.x - ( direction.x * ( bounds.ymin - origin.y) ) / direction.y, bounds.ymin );
                std::cout << "clip " << origin << " " << direction << " " << clipped << std::endl;
                std::cout << "INFINITE " << origin << " " << clipped <<  std::endl;

                if (edge->vertex0() != NULL)
                {
                  if (pg.points.size() == 0)
                    pg.points.push_back(origin);
                  if (pg.points.back() != origin)
                    pg.points.push_back(origin);
                  pg.points.push_back(clipped);
                }
                else
                {
                  pg.points.push_back(clipped);
                  pg.points.push_back(origin);
                }

              }
              edge_cnt++;
            } while (edge != cell->incident_edge());
          }
        }
        ++cell_index;
        this->cells.push_back(pg);
      }
    }
  };

} // end of namespace physycom

#endif // PHYSYCOM_UTILS_VORONOI_HPP


/** \file kernel_functors.hpp \brief Kernel (covariance) functions */
/*
-------------------------------------------------------------------------
   This file is part of BayesOpt, an efficient C++ library for
   Bayesian optimization.

   Copyright (C) 2011-2015 Ruben Martinez-Cantin <rmcantin@unizar.es>

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

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

   You should have received a copy of the GNU Affero General Public License
   along with BayesOpt.  If not, see <http://www.gnu.org/licenses/>.
------------------------------------------------------------------------
*/

#ifndef  _KERNEL_FUNCTORS_HPP_
#define  _KERNEL_FUNCTORS_HPP_

#include <map>
#include <boost/scoped_ptr.hpp>
#include <boost/math/distributions/normal.hpp>
#include "bayesopt/parameters.hpp"
#include "specialtypes.hpp"

namespace bayesopt
{

  /**\addtogroup KernelFunctions
   * \brief Set of kernel or covariance functions for the nonparametric
   * processes.
   */
  //@{

  /** \brief Interface for kernel functors */
  class Kernel
  {
  public:
    virtual ~Kernel(){};
    virtual void init(size_t input_dim) {};
    virtual void init(size_t input_dim, Kernel* left, Kernel* right) {};

    virtual void setHyperParameters(const vectord &theta) = 0;
    virtual vectord getHyperParameters() = 0;
    virtual size_t nHyperParameters() = 0;

    virtual double operator()( const vectord &x1, const vectord &x2 ) = 0;
    virtual double gradient( const vectord &x1, const vectord &x2,
			     size_t component ) = 0;

  protected:
    size_t n_inputs;
  };

  template <typename KernelType> Kernel * create_func()
  {
    return new KernelType();
  }

  /**
   * \brief Factory model for kernel functions
   * This factory is based on the libgp library by Manuel Blum
   *      https://bitbucket.org/mblum/libgp
   * which follows the squeme of GPML by Rasmussen and Nickisch
   *     http://www.gaussianprocess.org/gpml/code/matlab/doc/
   */
  class KernelFactory
  {
  public:
    KernelFactory ();
    virtual ~KernelFactory () {};

    Kernel* create(std::string name, size_t input_dim);

  private:
    typedef Kernel* (*create_func_definition)();
    std::map<std::string , KernelFactory::create_func_definition> registry;
  };

  class KernelModel
  {
  public:
    KernelModel(size_t dim, Parameters parameters);
    virtual ~KernelModel() {};

    Kernel* getKernel();

    void setHyperParameters(const vectord &theta);
    vectord getHyperParameters();
    size_t nHyperParameters();

    /**
     * \brief Select kernel (covariance function) for the surrogate process.
     * @param thetav kernel parameters (mean)
     * @param stheta kernel parameters (std)
     * @param k_name kernel name
     */
    void setKernel (const vectord &thetav, const vectord &stheta,
		   std::string k_name, size_t dim);

    /** Wrapper of setKernel for C++ kernel structure */
    void setKernel (KernelParameters kernel, size_t dim);

    void computeCorrMatrix(const vecOfvec& XX, matrixd& corrMatrix, double nugget);
    void computeDerivativeCorrMatrix(const vecOfvec& XX, matrixd& corrMatrix,
				    int dth_index);
    vectord computeCrossCorrelation(const vecOfvec& XX, const vectord &query);
    void computeCrossCorrelation(const vecOfvec& XX, const vectord &query,
				 vectord& knx);
    double computeSelfCorrelation(const vectord& query);
    double kernelLogPrior();

  private:
    /** Set prior (Gaussian) for kernel hyperparameters */
    void setKernelPrior (const vectord &theta, const vectord &s_theta);

    boost::scoped_ptr<Kernel> mKernel;            ///< Pointer to kernel function
    std::vector<boost::math::normal> priorKernel; ///< Prior of kernel parameters
  };

  inline Kernel* KernelModel::getKernel()
  { return mKernel.get();  }

  inline void KernelModel::setHyperParameters(const vectord &theta)
  { mKernel->setHyperParameters(theta); };

  inline vectord KernelModel::getHyperParameters()
  {return mKernel->getHyperParameters();};

  inline size_t KernelModel::nHyperParameters()
  {return mKernel->nHyperParameters();};

  inline vectord KernelModel::computeCrossCorrelation(const vecOfvec& XX,
						      const vectord &query)
  {
    vectord knx(XX.size());
    computeCrossCorrelation(XX,query,knx);
    return knx;
  }

  inline void KernelModel::computeCrossCorrelation(const vecOfvec& XX,
						   const vectord &query,
						   vectord& knx)
  {
    std::vector<vectord>::const_iterator x_it  = XX.begin();
    vectord::iterator k_it = knx.begin();
    while(x_it != XX.end())
      {	*k_it++ = (*mKernel)(*x_it++, query); }
  }

  inline double KernelModel::computeSelfCorrelation(const vectord& query)
  { return (*mKernel)(query,query); }

  inline void KernelModel::setKernelPrior (const vectord &theta,
					   const vectord &s_theta)
  {
    for (size_t i = 0; i<theta.size(); ++i)
      {
	boost::math::normal n(theta(i),s_theta(i));
	priorKernel.push_back(n);
      }
  };

  //@}

} //namespace bayesopt

#endif


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

#include <array>
#include <cmath>
#include <fstream>
#include <iostream>
#include <limits>
#include <string>

#include <pybind11/numpy.h>
#include <pybind11/pybind11.h>

#include <deal.II/base/quadrature_lib.h>
#include <deal.II/fe/fe_q.h>
#include <deal.II/fe/fe_values.h>
#include <deal.II/grid/grid_generator.h>
#include <deal.II/grid/grid_out.h>
#include <deal.II/grid/grid_refinement.h>
#include <deal.II/grid/tria.h>
#include <deal.II/lac/vector.h>

namespace py = pybind11;
namespace d = dealii;

constexpr double a = -1;
constexpr double b = 1;
const d::UpdateFlags update_flags =
    d::update_quadrature_points | d::update_JxW_values;

void greet() { std::cout << "Hello from meshgen11_dealii." << std::endl; }

// {{{ utils

// make a copy of py::array to dealii::Vector
template <class Number>
d::Vector<Number> pyarr_to_dvec(py::array_t<Number> arr) {

  py::buffer_info info = arr.request();
  if (info.ndim != 1) {
    throw std::runtime_error("Number of dimensions must be one");
  }

  // pointer to the data buffer
  Number *iter = (Number *)info.ptr;

  d::Vector<Number> dvec;
  dvec.reinit(info.shape[0]);
  Number *ptc = iter;
  for (unsigned int c = 0; c < info.shape[0]; ++c) {
    dvec[c] = *ptc;
    ++ptc;
  }

  return dvec;
}

template <int dim>
double l_infty_distance(d::Point<dim> &p1, d::Point<dim> &p2) {
  double dist = std::numeric_limits<double>::max();
  for (unsigned int d = 0; d < dim; ++d) {
    double tmp = std::abs(p1[d] - p2[d]);
    dist = tmp > dist ? dist : tmp;
  }
  return dist;
}

template <int dim>
double compute_q_point_radii(
    d::Point<dim> q_point,
    std::array<d::Point<dim>, d::GeometryInfo<dim>::vertices_per_cell>
        &vertices) {
  double rad = std::numeric_limits<double>::max();
  for (auto &&v : vertices) {
    double tmp = l_infty_distance(q_point, v);
    rad = tmp > rad ? rad : tmp;
  }
  return rad;
}

template <int dimension, int n_vertices>
d::Point<dimension>
compute_barycenter(std::array<d::Point<dimension>, n_vertices> &vertices) {

  d::Point<dimension> bc;
  for (unsigned int d = 0; d < dimension; ++d) {
    bc[d] = 0;
  }

  for (auto &&v : vertices) {
    bc += v;
  }
  bc /= n_vertices;
  return bc;
}

// }}}

// {{{ MeshGenerator class

template <int dimension> class MeshGenerator {
public:
  d::Triangulation<dimension> triangulation;
  d::FE_Q<dimension> fe;
  d::DoFHandler<dimension> dof_handler;
  d::QGauss<dimension> quadrature_formula;

  double box_a, box_b;

  unsigned int max_n_cells;
  unsigned int min_grid_level, max_grid_level;

  // q: quad order
  // level: initial (uniform) level
  // bounding box: [aa, bb]^dim
  MeshGenerator(
      int q, int level, double aa, double bb,
      py::args args, py::kwargs kwargs,
      unsigned int max_n_cells = std::numeric_limits<unsigned int>::max(),
      unsigned int min_grid_level = std::numeric_limits<unsigned int>::min(),
      unsigned int max_grid_level = std::numeric_limits<unsigned int>::max())
      : triangulation(d::Triangulation<dimension>::limit_level_difference_at_vertices), fe(q),
        dof_handler(triangulation), quadrature_formula(q), box_a(aa), box_b(bb),
        max_n_cells(max_n_cells), min_grid_level(min_grid_level),
        max_grid_level(max_grid_level) {
    d::GridGenerator::hyper_cube(triangulation, box_a, box_b);

    // initial number of levels must be compatible
    assert(level > min_grid_level);
    assert(level <= max_grid_level + 1);
    assert(std::pow(std::pow(2, dimension), level - 1) <= max_n_cells);

    if (level > 1) {
      triangulation.refine_global(level - 1);
    }
    this->dof_handler.distribute_dofs(fe);
  }

  // NOTE: Copy constructor is not supported since dealii::DoFHandler is not
  // copy constructable, but a dummy copy constructor is required by
  // boost.python to build the code.
  MeshGenerator(const MeshGenerator<dimension> &) : MeshGenerator(1, 1){};

  std::string greet() { return "Hello from MeshGen."; }

  void generate_gmsh(const std::string fn) {
    std::string filename = fn;
    std::ofstream output_file(filename);
    d::GridOut().write_msh(this->triangulation, output_file);
    std::cout << "Mesh written in " << filename << std::endl;
  }

  void write_vtu(const std::string fn) {
    std::string filename = fn;
    std::ofstream output_file(filename);
    d::GridOut().write_vtu(this->triangulation, output_file);
    std::cout << "Mesh written in " << filename << std::endl;
  }

  py::array get_q_points() {
    py::dtype dtype = py::dtype::of<double>();

    const size_t n_q_points = this->quadrature_formula.size();
    const size_t total_n_q_points = triangulation.n_active_cells() * n_q_points;

    std::array<size_t, 2> pt_shape = {total_n_q_points, dimension};
    std::array<size_t, 2> pt_strides = {dimension * sizeof(double),
                                        sizeof(double)};

    d::FEValues<dimension> fe_values(this->fe, this->quadrature_formula,
                                     d::update_quadrature_points);
    std::vector<double> points(pt_shape[0] * pt_shape[1], 0);
    auto ptp = points.data();

    auto cell = dof_handler.begin_active();
    auto endc = dof_handler.end();
    for (; cell != endc; ++cell) {
      fe_values.reinit(cell);
      std::vector<d::Point<dimension>> q_points =
          fe_values.get_quadrature_points();

      for (auto &&point : q_points) {
        for (unsigned int d = 0; d < dimension; ++d) {
          // For a preferred ordering of quad points
          // This does not change anything but the ordering due to the symmetry
          // in each direction (as long as the subdivided boxes are cubes), not
          // even weights are changed.
          *ptp = point[dimension - 1 - d];
          ++ptp;
        }
      }
    }

    return py::array(py::buffer_info(points.data(), sizeof(double),
                                     py::format_descriptor<double>::value, 2,
                                     pt_shape, pt_strides));
  }

  py::array get_q_weights() {
    py::dtype dtype = py::dtype::of<double>();

    const size_t n_q_points = this->quadrature_formula.size();
    const size_t total_n_q_points =
        this->triangulation.n_active_cells() * n_q_points;

    std::array<size_t, 1> w_shape = {total_n_q_points};
    std::array<size_t, 1> w_strides = {sizeof(double)};

    d::FEValues<dimension> fe_values(this->fe, this->quadrature_formula,
                                     d::update_JxW_values);

    std::vector<double> weights(w_shape[0], 0);
    auto ptw = weights.data();

    auto cell = this->dof_handler.begin_active();
    auto endc = this->dof_handler.end();
    for (; cell != endc; ++cell) {
      fe_values.reinit(cell);
      std::vector<double> q_weights;

      for (unsigned int q_index = 0; q_index < n_q_points; ++q_index) {
        q_weights.push_back(fe_values.JxW(q_index));
      }

      for (auto &&weight : q_weights) {
        *ptw = weight;
        ++ptw;
      }
    }

    return py::array(py::buffer_info(weights.data(), sizeof(double),
                                     py::format_descriptor<double>::value, 1,
                                     w_shape, w_strides));
  }

  py::array get_cell_measures() {
    py::dtype dtype = py::dtype::of<double>();
    std::array<size_t, 1> m_shape = {this->triangulation.n_active_cells()};
    std::array<size_t, 1> m_strides = {sizeof(double)};
    std::vector<double> measures(m_shape[0], 0);
    auto ptm = measures.data();

    auto cell = this->dof_handler.begin_active();
    auto endc = this->dof_handler.end();
    for (; cell != endc; ++cell) {
      *ptm = cell->measure();
      ++ptm;
    }

    return py::array(py::buffer_info(measures.data(), sizeof(double),
                                     py::format_descriptor<double>::value, 1,
                                     m_shape, m_strides));
  }

  py::array get_cell_extents() {
    py::dtype dtype = py::dtype::of<double>();
    std::array<size_t, 1> m_shape = {this->triangulation.n_active_cells()};
    std::array<size_t, 1> m_strides = {sizeof(double)};
    std::vector<double> extents(m_shape[0], 0);
    auto ptm = extents.data();

    auto cell = this->dof_handler.begin_active();
    auto endc = this->dof_handler.end();
    for (; cell != endc; ++cell) {
      auto v0 = cell->vertex(0);
      auto v1 = cell->vertex(1);
      *ptm = l_infty_distance<dimension>(v0, v1);
      ++ptm;
    }

    return py::array(py::buffer_info(extents.data(), sizeof(double),
                                     py::format_descriptor<double>::value, 1,
                                     m_shape, m_strides));
  }

  py::array get_cell_centers() {
    py::dtype dtype = py::dtype::of<double>();
    std::array<size_t, 2> c_shape = {this->triangulation.n_active_cells(),
                                     dimension};
    std::array<size_t, 2> c_strides = {dimension * sizeof(double),
                                       sizeof(double)};
    std::vector<double> centers(c_shape[0] * c_shape[1], 0);
    auto ptc = centers.data();

    auto cell = this->dof_handler.begin_active();
    auto endc = this->dof_handler.end();
    for (; cell != endc; ++cell) {
      std::array<d::Point<dimension>,
                 d::GeometryInfo<dimension>::vertices_per_cell>
          vertices;
      for (unsigned int v = 0;
           v < d::GeometryInfo<dimension>::vertices_per_cell; ++v) {
        vertices[v] = cell->vertex(v);
      }
      auto barycenter = compute_barycenter<
          dimension, d::GeometryInfo<dimension>::vertices_per_cell>(vertices);
      for (unsigned int d = 0; d < dimension; ++d) {
        *ptc = barycenter[dimension - 1 - d];
        ++ptc;
      }
    }

    return py::array(py::buffer_info(centers.data(), sizeof(double),
                                     py::format_descriptor<double>::value, 2,
                                     c_shape, c_strides));
  }

  int n_active_cells() { return this->triangulation.n_active_cells(); }

  // Do both preparation for refinement and coarsening as well as mesh
  // smoothing. The function returns whether some cells' flagging has been
  // changed in the process.
  bool prepare_coarsening_and_refinement() {

    bool flagging_changed = false;

    if (this->triangulation.n_levels() > this->max_grid_level) {
      flagging_changed = true;
      for (const auto &cell :
           this->triangulation.active_cell_iterators_on_level(max_grid_level))
        cell->clear_refine_flag();
    }

    for (const auto &cell :
         triangulation.active_cell_iterators_on_level(min_grid_level))
      if (cell->coarsen_flag_set()) {
        flagging_changed = true;
        cell->clear_coarsen_flag();
      }

    return (flagging_changed &&
            this->triangulation.prepare_coarsening_and_refinement());
  }

  void execute_coarsening_and_refinement() {
    this->triangulation.execute_coarsening_and_refinement();
    this->dof_handler.distribute_dofs(fe);
  }

  // {{{ adaptive mesh refinement

  // (Legacy) driver function for mesh adaptivity
  int update_mesh(py::array_t<double> &criteria,
                  const double top_fraction_of_cells,
                  const double bottom_fraction_of_cells) {

    // calls refine_and_coarsen_fixed_number
    int n_active_cells = this->refine_and_coarsen_fixed_number(
        criteria, top_fraction_of_cells, bottom_fraction_of_cells);

    return n_active_cells;
  }

  // Interface for dealii::GridRefinement::refine_and_coarsen_fixed_number
  int refine_and_coarsen_fixed_number(py::array_t<double> &criteria,
                                      const double top_fraction_of_cells,
                                      const double bottom_fraction_of_cells) {

    auto ctr = pyarr_to_dvec<double>(criteria);

    d::GridRefinement::refine_and_coarsen_fixed_number(
        this->triangulation, ctr, top_fraction_of_cells,
        bottom_fraction_of_cells, this->max_n_cells);

    this->prepare_coarsening_and_refinement();
    this->execute_coarsening_and_refinement();

    return this->n_active_cells();
  }

  // Interface for dealii::GridRefinement::refine_and_coarsen_fixed_fraction
  int refine_and_coarsen_fixed_fraction(
      py::array_t<double> &criteria, const double top_fraction_of_errors,
      const double bottom_fraction_of_errors) {

    auto ctr = pyarr_to_dvec<double>(criteria);

    d::GridRefinement::refine_and_coarsen_fixed_fraction(
        this->triangulation, ctr, top_fraction_of_errors,
        bottom_fraction_of_errors, this->max_n_cells);

    this->prepare_coarsening_and_refinement();
    this->execute_coarsening_and_refinement();

    return this->n_active_cells();
  }

  // Interface for dealii::GridRefinement::refine_and_coarsen_optimize
  // NOTE: this function does not repsect max_n_cells!
  int refine_and_coarsen_optimize(py::array_t<double> &criteria,
                                  const unsigned int order) {

    auto ctr = pyarr_to_dvec<double>(criteria);

    d::GridRefinement::refine_and_coarsen_optimize(this->triangulation, ctr,
                                                   order);

    this->prepare_coarsening_and_refinement();
    this->execute_coarsening_and_refinement();

    if (this->n_active_cells() > this->max_n_cells) {
      std::cout << "Warning: max_n_cells has been exceeded!" << std::endl;
    }

    return this->n_active_cells();
  }

  // Interface for dealii::GridRefinement::refine
  // NOTE: this function does not repsect max_n_cells!
  int refine(py::array_t<double> &criteria, const double threshold,
             const unsigned int max_to_mark =
                 std::numeric_limits<unsigned int>::max()) {

    auto ctr = pyarr_to_dvec<double>(criteria);

    d::GridRefinement::refine(this->triangulation, ctr, threshold, max_to_mark);

    this->prepare_coarsening_and_refinement();
    this->execute_coarsening_and_refinement();

    if (this->n_active_cells() > this->max_n_cells) {
      std::cout << "Warning: max_n_cells has been exceeded!" << std::endl;
    }

    return this->n_active_cells();
  }

  // Interface for dealii::GridRefinement::coarsen
  int coarsen(py::array_t<double> &criteria, const double threshold) {

    auto ctr = pyarr_to_dvec<double>(criteria);

    d::GridRefinement::coarsen(this->triangulation, ctr, threshold);

    this->prepare_coarsening_and_refinement();
    this->execute_coarsening_and_refinement();

    return this->n_active_cells();
  }

  // }}}

  // Show some info about the mesh
  void print_info() {

    std::cout << "Number of cells: " << this->triangulation.n_cells()
              << std::endl;

    std::cout << "Number of active cells: "
              << this->triangulation.n_active_cells() << std::endl;

    d::FEValues<dimension> fe_values(fe, quadrature_formula,
                                     d::update_quadrature_points);
    const unsigned int n_q_points = quadrature_formula.size();
    std::cout << "Number of quad points per cell: " << n_q_points << std::endl;
  }
};
typedef MeshGenerator<1> MeshGen1D;
typedef MeshGenerator<2> MeshGen2D;
typedef MeshGenerator<3> MeshGen3D;

// }}}

// {{{ (Legacy) make uniform mesh
// A legacy one-stop mesh generation function, returns quad points, weights
// and some spacing info within a tuple.
template <int dim> py::tuple make_uniform_cubic_grid_details(int q, int level) {
  py::dtype dtype = py::dtype::of<double>();

  d::Triangulation<dim> triangulation;
  d::FE_Q<dim> fe(q);
  d::DoFHandler<dim> dof_handler(triangulation);
  d::QGauss<dim> quadrature_formula(q);

  d::GridGenerator::hyper_cube(triangulation, a, b);
  if (level > 1) {
    triangulation.refine_global(level - 1);
  }
  dof_handler.distribute_dofs(fe);
  // std::cout << "Number of active cells: " << triangulation.n_active_cells()
  //<< std::endl;

  d::FEValues<dim> fe_values(fe, quadrature_formula, update_flags);
  const size_t n_q_points = quadrature_formula.size();
  // std::cout << "Number of quad points per cell: " << n_q_points << std::endl;

  const size_t total_n_q_points = triangulation.n_active_cells() * n_q_points;

  std::array<size_t, 2> pt_shape = {total_n_q_points, dim};
  std::array<size_t, 2> pt_strides = {dim * sizeof(double), sizeof(double)};
  std::array<size_t, 1> w_shape = {total_n_q_points};
  std::array<size_t, 1> w_strides = {sizeof(double)};

  // Quad points
  std::vector<double> points(pt_shape[0] * pt_shape[1], 0);
  // Quad weights
  std::vector<double> weights(w_shape[0], 0);
  // Distance (l_infty) to the closest cell vertex
  // (used for reconstructing the mesh in boxtree)
  std::vector<double> radii(w_shape[0], 0);
  auto ptp = points.data();
  auto ptw = weights.data();
  auto ptr = radii.data();

  // For margins
  std::array<double, dim * 2> margins;
  std::array<double *, dim * 2> mpts;
  margins.fill(std::numeric_limits<double>::max());

  auto cell = dof_handler.begin_active();
  auto endc = dof_handler.end();
  for (; cell != endc; ++cell) {
    fe_values.reinit(cell);
    std::vector<d::Point<dim>> q_points = fe_values.get_quadrature_points();
    std::vector<double> q_weights;
    std::array<d::Point<dim>, d::GeometryInfo<dim>::vertices_per_cell> vertices;

    for (unsigned int q_index = 0; q_index < n_q_points; ++q_index) {
      q_weights.push_back(fe_values.JxW(q_index));
    }

    for (unsigned int v = 0; v < d::GeometryInfo<dim>::vertices_per_cell; ++v) {
      vertices[v] = cell->vertex(v);
    }

    for (auto &&point : q_points) {
      for (unsigned int d = 0; d < dim; ++d) {
        // For a preferred ordering of quad points
        // This does not change anything but the ordering due to the symmetry
        // in each direction (as long as the subdivided boxes are cubes), not
        // even weights are changed.
        *ptp = point[dim - 1 - d];
        ++ptp;
      }

      // gather some info about margins
      for (unsigned int d = 0; d < dim; ++d) {
        auto margin_a_id = d * 2;
        auto margin_b_id = d * 2 + 1;
        if (std::abs(point[d] - a) < margins[margin_a_id]) {
          margins[margin_a_id] = std::abs(point[d] - a);
          mpts[margin_a_id] = ptr;
        }
        if (std::abs(point[d] - b) < margins[margin_b_id]) {
          margins[margin_b_id] = std::abs(point[d] - b);
          mpts[margin_b_id] = ptr;
        }
      }

      ++ptr;
    }

    for (auto &&weight : q_weights) {
      *ptw = weight;
      ++ptw;
    }
  }

  for (unsigned int i = 0; i < dim * 2; ++i) {
    *(mpts[i]) = margins[i];
  }

  py::tuple result = py::make_tuple(
      py::array(py::buffer_info(points.data(), sizeof(double),
                                py::format_descriptor<double>::value, 2,
                                pt_shape, pt_strides)),
      py::array(py::buffer_info(weights.data(), sizeof(double),
                                py::format_descriptor<double>::value, 1,
                                w_shape, w_strides)),
      py::array(py::buffer_info(radii.data(), sizeof(double),
                                py::format_descriptor<double>::value, 1,
                                w_shape, w_strides)));

  return result;
}

py::tuple make_uniform_cubic_grid(int q, int level, int dim,
                                  py::args args, py::kwargs kwargs) {
  if (dim == 1) {
    return make_uniform_cubic_grid_details<1>(q, level);
  } else if (dim == 2) {
    return make_uniform_cubic_grid_details<2>(q, level);
  } else if (dim == 3) {
    return make_uniform_cubic_grid_details<3>(q, level);
  } else {
    std::cout << "Dimension must be 1,2 or 3." << std::endl;
  }
}

// }}}

PYBIND11_MODULE(meshgen_dealii, m) {
  m.doc() = "A mesh generator for volumential.";

  m.def("greet", &greet, "Greetings! This is meshgen11.");

  m.def("make_uniform_cubic_grid", &make_uniform_cubic_grid,
        "Make a simple grid", py::arg("degree"), py::arg("level") = 1,
        py::arg("dim") = 2);

  py::class_<MeshGen1D>(m, "MeshGen1D")
      .def(py::init<int, int, double, double, py::args, py::kwargs>(), py::arg("degree"),
           py::arg("level"), py::arg("a"), py::arg("b"))
      .def("greet", &MeshGen1D::greet)
      .def("get_q_points", &MeshGen1D::get_q_points)
      .def("get_q_weights", &MeshGen1D::get_q_weights)
      .def("get_cell_centers", &MeshGen1D::get_cell_centers)
      .def("get_cell_extents", &MeshGen1D::get_cell_extents)
      .def("get_cell_measures", &MeshGen1D::get_cell_measures)
      .def("n_active_cells", &MeshGen1D::n_active_cells)
      .def("prepare_coarsening_and_refinement",
           &MeshGen1D::prepare_coarsening_and_refinement)
      .def("execute_coarsening_and_refinement",
           &MeshGen1D::execute_coarsening_and_refinement)
      .def("update_mesh", &MeshGen1D::update_mesh)
      .def("refine_and_coarsen_fixed_number",
           &MeshGen1D::refine_and_coarsen_fixed_number)
      .def("refine_and_coarsen_fixed_fraction",
           &MeshGen1D::refine_and_coarsen_fixed_fraction)
      .def("refine_and_coarsen_optimize",
           &MeshGen1D::refine_and_coarsen_optimize)
      .def("refine", &MeshGen1D::refine)
      .def("coarsen", &MeshGen1D::coarsen)
      .def("print_info", &MeshGen1D::print_info)
      .def("generate_gmsh", &MeshGen1D::generate_gmsh)
      .def("write_vtu", &MeshGen1D::write_vtu);

  py::class_<MeshGen2D>(m, "MeshGen2D")
      .def(py::init<int, int, double, double, py::args, py::kwargs>(), py::arg("degree"),
           py::arg("level"), py::arg("a"), py::arg("b"))
      .def("greet", &MeshGen2D::greet)
      .def("get_q_points", &MeshGen2D::get_q_points)
      .def("get_q_weights", &MeshGen2D::get_q_weights)
      .def("get_cell_centers", &MeshGen2D::get_cell_centers)
      .def("get_cell_extents", &MeshGen2D::get_cell_extents)
      .def("get_cell_measures", &MeshGen2D::get_cell_measures)
      .def("n_active_cells", &MeshGen2D::n_active_cells)
      .def("prepare_coarsening_and_refinement",
           &MeshGen2D::prepare_coarsening_and_refinement)
      .def("execute_coarsening_and_refinement",
           &MeshGen2D::execute_coarsening_and_refinement)
      .def("update_mesh", &MeshGen2D::update_mesh)
      .def("refine_and_coarsen_fixed_number",
           &MeshGen2D::refine_and_coarsen_fixed_number)
      .def("refine_and_coarsen_fixed_fraction",
           &MeshGen2D::refine_and_coarsen_fixed_fraction)
      .def("refine_and_coarsen_optimize",
           &MeshGen2D::refine_and_coarsen_optimize)
      .def("refine", &MeshGen2D::refine)
      .def("coarsen", &MeshGen2D::coarsen)
      .def("print_info", &MeshGen2D::print_info)
      .def("generate_gmsh", &MeshGen2D::generate_gmsh)
      .def("write_vtu", &MeshGen2D::write_vtu);

  py::class_<MeshGen3D>(m, "MeshGen3D")
      .def(py::init<int, int, double, double, py::args, py::kwargs>(), py::arg("degree"),
           py::arg("level"), py::arg("a"), py::arg("b"))
      .def("greet", &MeshGen3D::greet)
      .def("get_q_points", &MeshGen3D::get_q_points)
      .def("get_q_weights", &MeshGen3D::get_q_weights)
      .def("get_cell_centers", &MeshGen3D::get_cell_centers)
      .def("get_cell_extents", &MeshGen3D::get_cell_extents)
      .def("get_cell_measures", &MeshGen3D::get_cell_measures)
      .def("n_active_cells", &MeshGen3D::n_active_cells)
      .def("prepare_coarsening_and_refinement",
           &MeshGen3D::prepare_coarsening_and_refinement)
      .def("execute_coarsening_and_refinement",
           &MeshGen3D::execute_coarsening_and_refinement)
      .def("update_mesh", &MeshGen3D::update_mesh)
      .def("refine_and_coarsen_fixed_number",
           &MeshGen3D::refine_and_coarsen_fixed_number)
      .def("refine_and_coarsen_fixed_fraction",
           &MeshGen3D::refine_and_coarsen_fixed_fraction)
      .def("refine_and_coarsen_optimize",
           &MeshGen3D::refine_and_coarsen_optimize)
      .def("refine", &MeshGen3D::refine)
      .def("coarsen", &MeshGen3D::coarsen)
      .def("print_info", &MeshGen3D::print_info)
      .def("generate_gmsh", &MeshGen3D::generate_gmsh)
      .def("write_vtu", &MeshGen3D::write_vtu);
}


#include "math.hpp"

#include <boost/integer.hpp>

#include <cstring>
#include <limits>

namespace base
{

template <typename Float>
bool AlmostEqualULPs(Float x, Float y, uint32_t maxULPs)
{
  static_assert(std::is_floating_point<Float>::value, "");
  static_assert(std::numeric_limits<Float>::is_iec559, "");

  // Make sure maxUlps is non-negative and small enough that the
  // default NaN won't compare as equal to anything.
  ASSERT_LESS(maxULPs, 4 * 1024 * 1024, ());

  int constexpr bits = CHAR_BIT * sizeof(Float);
  typedef typename boost::int_t<bits>::exact IntType;
  typedef typename boost::uint_t<bits>::exact UIntType;

  // Same as *reinterpret_cast<IntType const *>(&x), but without warnings.
  IntType xInt, yInt;
  static_assert(sizeof(xInt) == sizeof(x), "bit_cast impossible");
  std::memcpy(&xInt, &x, sizeof(x));
  std::memcpy(&yInt, &y, sizeof(y));

  // Make xInt and yInt lexicographically ordered as a twos-complement int.
  IntType const highestBit = IntType(1) << (bits - 1);
  if (xInt < 0)
    xInt = highestBit - xInt;
  if (yInt < 0)
    yInt = highestBit - yInt;

  // Calculate diff with special case to avoid IntType overflow.
  UIntType diff;
  if ((xInt >= 0) == (yInt >= 0))
    diff = Abs(xInt - yInt);
  else
    diff = UIntType(Abs(xInt)) + UIntType(Abs(yInt));

  return diff <= maxULPs;
}

template bool AlmostEqualULPs<float>(float x, float y, uint32_t maxULPs);
template bool AlmostEqualULPs<double>(double x, double y, uint32_t maxULPs);

} // namespace base


// Copyright 2004 The Trustees of Indiana University.

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

//  Authors: Jeremiah Willcock
//           Douglas Gregor
//           Andrew Lumsdaine
#ifndef BOOST_GRAPH_GURSOY_ATUN_LAYOUT_HPP
#define BOOST_GRAPH_GURSOY_ATUN_LAYOUT_HPP

// Gursoy-Atun graph layout, based on:
// "Neighbourhood Preserving Load Balancing: A Self-Organizing Approach"
// in EuroPar 2000, p. 234 of LNCS 1900
// http://springerlink.metapress.com/link.asp?id=pcu07ew5rhexp9yt

#include <cmath>
#include <vector>
#include <exception>
#include <algorithm>

#include <boost/graph/visitors.hpp>
#include <boost/graph/properties.hpp>
#include <boost/random/uniform_01.hpp>
#include <boost/random/linear_congruential.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/graph/breadth_first_search.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <boost/graph/named_function_params.hpp>

namespace boost {

namespace detail {

struct over_distance_limit : public std::exception {};

template <typename PositionMap, typename NodeDistanceMap,  typename Topology,
          typename Graph>
struct update_position_visitor {
  typedef typename Topology::point_type Point;
  PositionMap position_map;
  NodeDistanceMap node_distance;
  const Topology& space;
  Point input_vector;
  double distance_limit;
  double learning_constant;
  double falloff_ratio;

  typedef boost::on_examine_vertex event_filter;

  typedef typename graph_traits<Graph>::vertex_descriptor
    vertex_descriptor;

  update_position_visitor(PositionMap position_map,
                          NodeDistanceMap node_distance,
                          const Topology& space,
                          const Point& input_vector,
                          double distance_limit,
                          double learning_constant,
                          double falloff_ratio):
    position_map(position_map), node_distance(node_distance),
    space(space),
    input_vector(input_vector), distance_limit(distance_limit),
    learning_constant(learning_constant), falloff_ratio(falloff_ratio) {}

  void operator()(vertex_descriptor v, const Graph&) const
  {
#ifndef BOOST_NO_STDC_NAMESPACE
    using std::pow;
#endif

    if (get(node_distance, v) > distance_limit)
      throw over_distance_limit();
    Point old_position = get(position_map, v);
    double distance = get(node_distance, v);
    double fraction =
      learning_constant * pow(falloff_ratio, distance * distance);
    put(position_map, v,
        space.move_position_toward(old_position, fraction, input_vector));
  }
};

template<typename EdgeWeightMap>
struct gursoy_shortest
{
  template<typename Graph, typename NodeDistanceMap, typename UpdatePosition>
  static inline void
  run(const Graph& g, typename graph_traits<Graph>::vertex_descriptor s,
      NodeDistanceMap node_distance,  UpdatePosition& update_position,
      EdgeWeightMap weight)
  {
    boost::dijkstra_shortest_paths(g, s, weight_map(weight).
      visitor(boost::make_dijkstra_visitor(std::make_pair(
       boost::record_distances(node_distance, boost::on_edge_relaxed()),
        update_position))));
  }
};

template<>
struct gursoy_shortest<dummy_property_map>
{
  template<typename Graph, typename NodeDistanceMap, typename UpdatePosition>
  static inline void
  run(const Graph& g, typename graph_traits<Graph>::vertex_descriptor s,
      NodeDistanceMap node_distance,  UpdatePosition& update_position,
      dummy_property_map)
  {
    boost::breadth_first_search(g, s,
      visitor(boost::make_bfs_visitor(std::make_pair(
        boost::record_distances(node_distance, boost::on_tree_edge()),
        update_position))));
  }
};

} // namespace detail

template <typename VertexListAndIncidenceGraph,  typename Topology,
          typename PositionMap, typename Diameter, typename VertexIndexMap,
          typename EdgeWeightMap>
void
gursoy_atun_step
  (const VertexListAndIncidenceGraph& graph,
   const Topology& space,
   PositionMap position,
   Diameter diameter,
   double learning_constant,
   VertexIndexMap vertex_index_map,
   EdgeWeightMap weight)
{
#ifndef BOOST_NO_STDC_NAMESPACE
  using std::pow;
  using std::exp;
#endif

  typedef typename graph_traits<VertexListAndIncidenceGraph>::vertex_iterator
    vertex_iterator;
  typedef typename graph_traits<VertexListAndIncidenceGraph>::vertex_descriptor
    vertex_descriptor;
  typedef typename Topology::point_type point_type;
  vertex_iterator i, iend;
  std::vector<double> distance_from_input_vector(num_vertices(graph));
  typedef boost::iterator_property_map<std::vector<double>::iterator,
                                       VertexIndexMap,
                                       double, double&>
    DistanceFromInputMap;
  DistanceFromInputMap distance_from_input(distance_from_input_vector.begin(),
                                           vertex_index_map);
  std::vector<double> node_distance_map_vector(num_vertices(graph));
  typedef boost::iterator_property_map<std::vector<double>::iterator,
                                       VertexIndexMap,
                                       double, double&>
    NodeDistanceMap;
  NodeDistanceMap node_distance(node_distance_map_vector.begin(),
                                vertex_index_map);
  point_type input_vector = space.random_point();
  vertex_descriptor min_distance_loc
    = graph_traits<VertexListAndIncidenceGraph>::null_vertex();
  double min_distance = 0.0;
  bool min_distance_unset = true;
  for (boost::tie(i, iend) = vertices(graph); i != iend; ++i) {
    double this_distance = space.distance(get(position, *i), input_vector);
    put(distance_from_input, *i, this_distance);
    if (min_distance_unset || this_distance < min_distance) {
      min_distance = this_distance;
      min_distance_loc = *i;
    }
    min_distance_unset = false;
  }
  assert (!min_distance_unset); // Graph must have at least one vertex
  boost::detail::update_position_visitor<
      PositionMap, NodeDistanceMap, Topology,
      VertexListAndIncidenceGraph>
    update_position(position, node_distance, space,
                    input_vector, diameter, learning_constant,
                    exp(-1. / (2 * diameter * diameter)));
  std::fill(node_distance_map_vector.begin(), node_distance_map_vector.end(), 0);
  try {
    typedef detail::gursoy_shortest<EdgeWeightMap> shortest;
    shortest::run(graph, min_distance_loc, node_distance, update_position,
                  weight);
  } catch (detail::over_distance_limit) {
    /* Thrown to break out of BFS or Dijkstra early */
  }
}

template <typename VertexListAndIncidenceGraph,  typename Topology,
          typename PositionMap, typename VertexIndexMap,
          typename EdgeWeightMap>
void gursoy_atun_refine(const VertexListAndIncidenceGraph& graph,
                        const Topology& space,
                        PositionMap position,
                        int nsteps,
                        double diameter_initial,
                        double diameter_final,
                        double learning_constant_initial,
                        double learning_constant_final,
                        VertexIndexMap vertex_index_map,
                        EdgeWeightMap weight)
{
#ifndef BOOST_NO_STDC_NAMESPACE
  using std::pow;
  using std::exp;
#endif

  typedef typename graph_traits<VertexListAndIncidenceGraph>::vertex_iterator
    vertex_iterator;
  typedef typename graph_traits<VertexListAndIncidenceGraph>::vertex_descriptor
    vertex_descriptor;
  typedef typename Topology::point_type point_type;
  vertex_iterator i, iend;
  double diameter_ratio = (double)diameter_final / diameter_initial;
  double learning_constant_ratio =
    learning_constant_final / learning_constant_initial;
  std::vector<double> distance_from_input_vector(num_vertices(graph));
  typedef boost::iterator_property_map<std::vector<double>::iterator,
                                       VertexIndexMap,
                                       double, double&>
    DistanceFromInputMap;
  DistanceFromInputMap distance_from_input(distance_from_input_vector.begin(),
                                           vertex_index_map);
  std::vector<int> node_distance_map_vector(num_vertices(graph));
  typedef boost::iterator_property_map<std::vector<int>::iterator,
                                       VertexIndexMap, double, double&>
    NodeDistanceMap;
  NodeDistanceMap node_distance(node_distance_map_vector.begin(),
                                vertex_index_map);
  for (int round = 0; round < nsteps; ++round) {
    double part_done = (double)round / (nsteps - 1);
    int diameter = (int)(diameter_initial * pow(diameter_ratio, part_done));
    double learning_constant =
      learning_constant_initial * pow(learning_constant_ratio, part_done);
    gursoy_atun_step(graph, space, position, diameter, learning_constant,
                     vertex_index_map, weight);
  }
}

template <typename VertexListAndIncidenceGraph,  typename Topology,
          typename PositionMap, typename VertexIndexMap,
          typename EdgeWeightMap>
void gursoy_atun_layout(const VertexListAndIncidenceGraph& graph,
                        const Topology& space,
                        PositionMap position,
                        int nsteps,
                        double diameter_initial,
                        double diameter_final,
                        double learning_constant_initial,
                        double learning_constant_final,
                        VertexIndexMap vertex_index_map,
                        EdgeWeightMap weight)
{
  typedef typename graph_traits<VertexListAndIncidenceGraph>::vertex_iterator
    vertex_iterator;
  vertex_iterator i, iend;
  for (boost::tie(i, iend) = vertices(graph); i != iend; ++i) {
    put(position, *i, space.random_point());
  }
  gursoy_atun_refine(graph, space,
                     position, nsteps,
                     diameter_initial, diameter_final,
                     learning_constant_initial, learning_constant_final,
                     vertex_index_map, weight);
}

template <typename VertexListAndIncidenceGraph,  typename Topology,
          typename PositionMap, typename VertexIndexMap>
void gursoy_atun_layout(const VertexListAndIncidenceGraph& graph,
                        const Topology& space,
                        PositionMap position,
                        int nsteps,
                        double diameter_initial,
                        double diameter_final,
                        double learning_constant_initial,
                        double learning_constant_final,
                        VertexIndexMap vertex_index_map)
{
  gursoy_atun_layout(graph, space, position, nsteps,
                     diameter_initial, diameter_final,
                     learning_constant_initial, learning_constant_final,
                     vertex_index_map, dummy_property_map());
}

template <typename VertexListAndIncidenceGraph, typename Topology,
          typename PositionMap>
void gursoy_atun_layout(const VertexListAndIncidenceGraph& graph,
                        const Topology& space,
                        PositionMap position,
                        int nsteps,
                        double diameter_initial,
                        double diameter_final = 1.0,
                        double learning_constant_initial = 0.8,
                        double learning_constant_final = 0.2)
{
  gursoy_atun_layout(graph, space, position, nsteps, diameter_initial,
                     diameter_final, learning_constant_initial,
                     learning_constant_final, get(vertex_index, graph));
}

template <typename VertexListAndIncidenceGraph, typename Topology,
          typename PositionMap>
void gursoy_atun_layout(const VertexListAndIncidenceGraph& graph,
                        const Topology& space,
                        PositionMap position,
                        int nsteps)
{
#ifndef BOOST_NO_STDC_NAMESPACE
  using std::sqrt;
#endif

  gursoy_atun_layout(graph, space, position, nsteps,
                     sqrt((double)num_vertices(graph)));
}

template <typename VertexListAndIncidenceGraph, typename Topology,
          typename PositionMap>
void gursoy_atun_layout(const VertexListAndIncidenceGraph& graph,
                        const Topology& space,
                        PositionMap position)
{
  gursoy_atun_layout(graph, space, position, num_vertices(graph));
}

template<typename VertexListAndIncidenceGraph, typename Topology,
         typename PositionMap, typename P, typename T, typename R>
void
gursoy_atun_layout(const VertexListAndIncidenceGraph& graph,
                   const Topology& space,
                   PositionMap position,
                   const bgl_named_params<P,T,R>& params)
{
#ifndef BOOST_NO_STDC_NAMESPACE
  using std::sqrt;
#endif

  std::pair<double, double> diam(sqrt(double(num_vertices(graph))), 1.0);
  std::pair<double, double> learn(0.8, 0.2);
  gursoy_atun_layout(graph, space, position,
                     choose_param(get_param(params, iterations_t()),
                                  num_vertices(graph)),
                     choose_param(get_param(params, diameter_range_t()),
                                  diam).first,
                     choose_param(get_param(params, diameter_range_t()),
                                  diam).second,
                     choose_param(get_param(params, learning_constant_range_t()),
                                  learn).first,
                     choose_param(get_param(params, learning_constant_range_t()),
                                  learn).second,
                     choose_const_pmap(get_param(params, vertex_index), graph,
                                       vertex_index),
                     choose_param(get_param(params, edge_weight),
                                  dummy_property_map()));
}

/***********************************************************
 * Topologies                                              *
 ***********************************************************/
template<std::size_t Dims>
class convex_topology
{
  struct point
  {
    point() { }
    double& operator[](std::size_t i) {return values[i];}
    const double& operator[](std::size_t i) const {return values[i];}

  private:
    double values[Dims];
  };

 public:
  typedef point point_type;

  double distance(point a, point b) const
  {
    double dist = 0;
    for (std::size_t i = 0; i < Dims; ++i) {
      double diff = b[i] - a[i];
      dist += diff * diff;
    }
    // Exact properties of the distance are not important, as long as
    // < on what this returns matches real distances
    return dist;
  }

  point move_position_toward(point a, double fraction, point b) const
  {
    point result;
    for (std::size_t i = 0; i < Dims; ++i)
      result[i] = a[i] + (b[i] - a[i]) * fraction;
    return result;
  }
};

template<std::size_t Dims,
         typename RandomNumberGenerator = minstd_rand>
class hypercube_topology : public convex_topology<Dims>
{
  typedef uniform_01<RandomNumberGenerator, double> rand_t;

 public:
  typedef typename convex_topology<Dims>::point_type point_type;

  explicit hypercube_topology(double scaling = 1.0)
    : gen_ptr(new RandomNumberGenerator), rand(new rand_t(*gen_ptr)),
      scaling(scaling)
  { }

  hypercube_topology(RandomNumberGenerator& gen, double scaling = 1.0)
    : gen_ptr(), rand(new rand_t(gen)), scaling(scaling) { }

  point_type random_point() const
  {
    point_type p;
    for (std::size_t i = 0; i < Dims; ++i)
      p[i] = (*rand)() * scaling;
    return p;
  }

 private:
  shared_ptr<RandomNumberGenerator> gen_ptr;
  shared_ptr<rand_t> rand;
  double scaling;
};

template<typename RandomNumberGenerator = minstd_rand>
class square_topology : public hypercube_topology<2, RandomNumberGenerator>
{
  typedef hypercube_topology<2, RandomNumberGenerator> inherited;

 public:
  explicit square_topology(double scaling = 1.0) : inherited(scaling) { }

  square_topology(RandomNumberGenerator& gen, double scaling = 1.0)
    : inherited(gen, scaling) { }
};

template<typename RandomNumberGenerator = minstd_rand>
class cube_topology : public hypercube_topology<3, RandomNumberGenerator>
{
  typedef hypercube_topology<3, RandomNumberGenerator> inherited;

 public:
  explicit cube_topology(double scaling = 1.0) : inherited(scaling) { }

  cube_topology(RandomNumberGenerator& gen, double scaling = 1.0)
    : inherited(gen, scaling) { }
};

template<std::size_t Dims,
         typename RandomNumberGenerator = minstd_rand>
class ball_topology : public convex_topology<Dims>
{
  typedef uniform_01<RandomNumberGenerator, double> rand_t;

 public:
  typedef typename convex_topology<Dims>::point_type point_type;

  explicit ball_topology(double radius = 1.0)
    : gen_ptr(new RandomNumberGenerator), rand(new rand_t(*gen_ptr)),
      radius(radius)
  { }

  ball_topology(RandomNumberGenerator& gen, double radius = 1.0)
    : gen_ptr(), rand(new rand_t(gen)), radius(radius) { }

  point_type random_point() const
  {
    point_type p;
    double dist_sum;
    do {
      dist_sum = 0.0;
      for (std::size_t i = 0; i < Dims; ++i) {
        double x = (*rand)() * 2*radius - radius;
        p[i] = x;
        dist_sum += x * x;
      }
    } while (dist_sum > radius*radius);
    return p;
  }

 private:
  shared_ptr<RandomNumberGenerator> gen_ptr;
  shared_ptr<rand_t> rand;
  double radius;
};

template<typename RandomNumberGenerator = minstd_rand>
class circle_topology : public ball_topology<2, RandomNumberGenerator>
{
  typedef ball_topology<2, RandomNumberGenerator> inherited;

 public:
  explicit circle_topology(double radius = 1.0) : inherited(radius) { }

  circle_topology(RandomNumberGenerator& gen, double radius = 1.0)
    : inherited(gen, radius) { }
};

template<typename RandomNumberGenerator = minstd_rand>
class sphere_topology : public ball_topology<3, RandomNumberGenerator>
{
  typedef ball_topology<3, RandomNumberGenerator> inherited;

 public:
  explicit sphere_topology(double radius = 1.0) : inherited(radius) { }

  sphere_topology(RandomNumberGenerator& gen, double radius = 1.0)
    : inherited(gen, radius) { }
};

template<typename RandomNumberGenerator = minstd_rand>
class heart_topology
{
  // Heart is defined as the union of three shapes:
  // Square w/ corners (+-1000, -1000), (0, 0), (0, -2000)
  // Circle centered at (-500, -500) radius 500*sqrt(2)
  // Circle centered at (500, -500) radius 500*sqrt(2)
  // Bounding box (-1000, -2000) - (1000, 500*(sqrt(2) - 1))

  struct point
  {
    point() { values[0] = 0.0; values[1] = 0.0; }
    point(double x, double y) { values[0] = x; values[1] = y; }

    double& operator[](std::size_t i)       { return values[i]; }
    double  operator[](std::size_t i) const { return values[i]; }

  private:
    double values[2];
  };

  bool in_heart(point p) const
  {
#ifndef BOOST_NO_STDC_NAMESPACE
    using std::abs;
    using std::pow;
#endif

    if (p[1] < abs(p[0]) - 2000) return false; // Bottom
    if (p[1] <= -1000) return true; // Diagonal of square
    if (pow(p[0] - -500, 2) + pow(p[1] - -500, 2) <= 500000)
      return true; // Left circle
    if (pow(p[0] - 500, 2) + pow(p[1] - -500, 2) <= 500000)
      return true; // Right circle
    return false;
  }

  bool segment_within_heart(point p1, point p2) const
  {
    // Assumes that p1 and p2 are within the heart
    if ((p1[0] < 0) == (p2[0] < 0)) return true; // Same side of symmetry line
    if (p1[0] == p2[0]) return true; // Vertical
    double slope = (p2[1] - p1[1]) / (p2[0] - p1[0]);
    double intercept = p1[1] - p1[0] * slope;
    if (intercept > 0) return false; // Crosses between circles
    return true;
  }

  typedef uniform_01<RandomNumberGenerator, double> rand_t;

 public:
  typedef point point_type;

  heart_topology()
    : gen_ptr(new RandomNumberGenerator), rand(new rand_t(*gen_ptr)) { }

  heart_topology(RandomNumberGenerator& gen)
    : gen_ptr(), rand(new rand_t(gen)) { }

  point random_point() const
  {
#ifndef BOOST_NO_STDC_NAMESPACE
    using std::sqrt;
#endif

    point result;
    double sqrt2 = sqrt(2.);
    do {
      result[0] = (*rand)() * (1000 + 1000 * sqrt2) - (500 + 500 * sqrt2);
      result[1] = (*rand)() * (2000 + 500 * (sqrt2 - 1)) - 2000;
    } while (!in_heart(result));
    return result;
  }

  double distance(point a, point b) const
  {
#ifndef BOOST_NO_STDC_NAMESPACE
    using std::sqrt;
#endif
    if (segment_within_heart(a, b)) {
      // Straight line
      return sqrt((b[0] - a[0]) * (b[0] - a[0]) + (b[1] - a[1]) * (b[1] - a[1]));
    } else {
      // Straight line bending around (0, 0)
      return sqrt(a[0] * a[0] + a[1] * a[1]) + sqrt(b[0] * b[0] + b[1] * b[1]);
    }
  }

  point move_position_toward(point a, double fraction, point b) const
  {
#ifndef BOOST_NO_STDC_NAMESPACE
    using std::sqrt;
#endif

    if (segment_within_heart(a, b)) {
      // Straight line
      return point(a[0] + (b[0] - a[0]) * fraction,
                   a[1] + (b[1] - a[1]) * fraction);
    } else {
      double distance_to_point_a = sqrt(a[0] * a[0] + a[1] * a[1]);
      double distance_to_point_b = sqrt(b[0] * b[0] + b[1] * b[1]);
      double location_of_point = distance_to_point_a /
                                   (distance_to_point_a + distance_to_point_b);
      if (fraction < location_of_point)
        return point(a[0] * (1 - fraction / location_of_point),
                     a[1] * (1 - fraction / location_of_point));
      else
        return point(
          b[0] * ((fraction - location_of_point) / (1 - location_of_point)),
          b[1] * ((fraction - location_of_point) / (1 - location_of_point)));
    }
  }

 private:
  shared_ptr<RandomNumberGenerator> gen_ptr;
  shared_ptr<rand_t> rand;
};

} // namespace boost

#endif // BOOST_GRAPH_GURSOY_ATUN_LAYOUT_HPP