// Copyright (C) 2019 EDF
// All Rights Reserved
// This code is published under the GNU Lesser General Public License (GNU LGPL)

/** \file PyBind11StOptGrids.cpp
 * \brief Map grids  classes to python
 * \author Xavier Warin
 */
#include <Eigen/Dense>
#include <iostream>
#include <memory>
#include <pybind11/pybind11.h>
#include <pybind11/eigen.h>
#include <pybind11/stl.h>
#include "StOpt/core/utils/constant.h"
#include "StOpt/core/utils/version.h"
#include "StOpt/core/grids/Interpolator.h"
#include "StOpt/core/grids/LinearInterpolator.h"
#include "StOpt/core/grids/LegendreInterpolator.h"
#include "StOpt/core/grids/SparseNoBoundInterpolator.h"
#include "StOpt/core/grids/SparseBoundInterpolator.h"
#include "StOpt/core/grids/GridIterator.h"
#include "StOpt/core/grids/FullGridIterator.h"
#include "StOpt/core/grids/FullRegularIntGridIterator.h"
#include "StOpt/core/grids/FullRegularGridIterator.h"
#include "StOpt/core/grids/FullLegendreGridIterator.h"
#include "StOpt/core/grids/FullGeneralGridIterator.h"
#include "StOpt/core/grids/SparseGridNoBoundIterator.h"
#include "StOpt/core/grids/SparseGridBoundIterator.h"
#include "StOpt/core/grids/RegularSpaceGrid.h"
#include "StOpt/core/grids/RegularLegendreGrid.h"
#include "StOpt/core/grids/SparseSpaceGridBound.h"
#include "StOpt/core/grids/SparseSpaceGridNoBound.h"
#include "StOpt/core/grids/GeneralSpaceGrid.h"
#include "StOpt/core/grids/RegularSpaceIntGrid.h"
#include "StOpt/core/grids/OneDimRegularSpaceGrid.h"
#include "StOpt/core/grids/OneDimSpaceGrid.h"
#include "StOpt/core/grids/Mesh2D.h"
#include "StOpt/core/grids/GridAdapt2D.h"
#include "StOpt/core/sparse/sparseGridUtils.h"
#include  "StOpt/python/Pybind11VectorAndList.h"


namespace py = pybind11;



template< class Sparse>
std::pair<Eigen::ArrayXd, Eigen::ArrayXd>  refineSparseWrapp(Sparse &p_sparse, const double &p_precision, py::object &p_fInterpol,
        const Eigen::ArrayXd &p_valuesFunction,
        const Eigen::ArrayXd &p_hierarValues)
{
    auto phiLam([](const SparseSet::const_iterator & p_iterLevel, const Eigen::ArrayXd & p_values)
    {
        double smax = -StOpt::infty;
        for (const auto &index : p_iterLevel->second)
            smax = std::max(smax, fabs(p_values(index.second)));
        return smax;
    });
    std::function< double(const SparseSet::const_iterator &, const Eigen::ArrayXd &)> phi(std::cref(phiLam));
    auto phiMultLam([](const std::vector< double> &p_vec)
    {
        assert(p_vec.size() > 0);
        double smax = p_vec[0];
        for (size_t i = 1; i < p_vec.size(); ++i)
            smax = std::max(smax, p_vec[i]);
        return smax;
    });
    std::function< double(const std::vector< double> &) > phiMult(std::cref(phiMultLam));

    Eigen::ArrayXd  valuesFunction(p_valuesFunction);
    Eigen::ArrayXd  hierarValues(p_hierarValues);
    auto lambda = [p_fInterpol](const Eigen::ArrayXd & p_x)->double { return  p_fInterpol(p_x).cast<double>();};

    p_sparse.refine(p_precision, std::function<double(const Eigen::ArrayXd &p_x)> (lambda), phi, phiMult, valuesFunction, hierarValues);
    return std::make_pair(valuesFunction, hierarValues);
}


template< class Sparse>
std::pair<Eigen::ArrayXd, Eigen::ArrayXd>  coarsenSparseWrapp(Sparse &p_sparse, const double &p_precision, const Eigen::ArrayXd &p_valuesFunction,
        const Eigen::ArrayXd   &p_hierarValues)
{
    auto phiLam([](const SparseSet::const_iterator & p_iterLevel, const Eigen::ArrayXd & p_values)
    {
        double smax = -StOpt::infty;
        for (SparseLevel::const_iterator iterIndex = p_iterLevel->second.begin(); iterIndex != p_iterLevel->second.end(); ++iterIndex)
        {
            smax = std::max(smax, fabs(p_values(iterIndex->second)));
        }
        return smax;
    });
    std::function< double(const SparseSet::const_iterator &, const Eigen::ArrayXd &)> phi(std::cref(phiLam));

    Eigen::ArrayXd  valuesFunction(p_valuesFunction);
    Eigen::ArrayXd  hierarValues(p_hierarValues);
    p_sparse.coarsen(p_precision, phi, valuesFunction, hierarValues);

    return std::make_pair(valuesFunction, hierarValues);
}


// Iterators wrapper
//*******************

class PyGridIterator : public StOpt::GridIterator
{
public:

    using StOpt::GridIterator::GridIterator;

    inline Eigen::ArrayXd  getCoordinate() const override
    {
        PYBIND11_OVERLOAD_PURE(Eigen::ArrayXd, StOpt::GridIterator, getCoordinate,);
    }
    inline bool isValid() const override
    {
        PYBIND11_OVERLOAD_PURE(bool, StOpt::GridIterator, isValid,);
    }
    inline void next() override
    {
        PYBIND11_OVERLOAD_PURE(void, StOpt::GridIterator, next,);
    }
    inline void nextInc(const int &p_incr) override
    {
        PYBIND11_OVERLOAD_PURE(void, StOpt::GridIterator, nextInc, p_incr);
    }
    inline int  getCount() const override
    {
        PYBIND11_OVERLOAD_PURE(bool, StOpt::GridIterator, getCount,);
    }
    inline void jumpToAndInc(const int &p_rank, const int &p_nbProc, const int &p_jump) override
    {
        PYBIND11_OVERLOAD_PURE(void, StOpt::GridIterator, jumpToAndInc, p_rank, p_nbProc, p_jump);
    }
    inline int getRelativePosition()  const override
    {
        PYBIND11_OVERLOAD_PURE(int, StOpt::GridIterator, getRelativePosition,);
    }
    inline int getNbPointRelative()  const override
    {
        PYBIND11_OVERLOAD_PURE(int, StOpt::GridIterator, getNbPointRelative,);
    }
    inline void reset() override
    {
        PYBIND11_OVERLOAD_PURE(void, StOpt::GridIterator, reset,);
    }
};

class PyFullGeneralGridIterator : public StOpt::FullGeneralGridIterator
{
public:

    using StOpt::FullGeneralGridIterator::FullGeneralGridIterator;

    inline Eigen::ArrayXd  getCoordinate() const override
    {
        PYBIND11_OVERLOAD_PURE(Eigen::ArrayXd, StOpt::FullGeneralGridIterator, getCoordinate,);
    }
    inline bool isValid() const override
    {
        PYBIND11_OVERLOAD_PURE(bool, StOpt::FullGeneralGridIterator, isValid,);
    }
    inline void next() override
    {
        PYBIND11_OVERLOAD_PURE(void, StOpt::FullGeneralGridIterator, next,);
    }
    inline void nextInc(const int &p_incr) override
    {
        PYBIND11_OVERLOAD_PURE(void, StOpt::FullGeneralGridIterator, nextInc, p_incr);
    }
    inline int  getCount() const override
    {
        PYBIND11_OVERLOAD_PURE(bool, StOpt::FullGeneralGridIterator, getCount,);
    }
    inline void jumpToAndInc(const int &p_rank, const int &p_nbProc, const int &p_jump) override
    {
        PYBIND11_OVERLOAD_PURE(void, StOpt::FullGeneralGridIterator, jumpToAndInc, p_rank, p_nbProc, p_jump);
    }
    inline int getRelativePosition()  const override
    {
        PYBIND11_OVERLOAD_PURE(int, StOpt::FullGeneralGridIterator, getRelativePosition,);
    }
    inline int getNbPointRelative()  const override
    {
        PYBIND11_OVERLOAD_PURE(int, StOpt::FullGeneralGridIterator, getNbPointRelative,);
    }
    inline void reset() override
    {
        PYBIND11_OVERLOAD_PURE(void, StOpt::FullGeneralGridIterator, reset,);
    }
};

class PyFullRegularGridIterator : public StOpt::FullRegularGridIterator
{
public:

    using StOpt::FullRegularGridIterator::FullRegularGridIterator;

    inline Eigen::ArrayXd  getCoordinate() const override
    {
        PYBIND11_OVERLOAD_PURE(Eigen::ArrayXd, StOpt::FullRegularGridIterator, getCoordinate,);
    }
    inline bool isValid() const override
    {
        PYBIND11_OVERLOAD_PURE(bool, StOpt::FullRegularGridIterator, isValid,);
    }
    inline void next() override
    {
        PYBIND11_OVERLOAD_PURE(void, StOpt::FullRegularGridIterator, next,);
    }
    inline void nextInc(const int &p_incr) override
    {
        PYBIND11_OVERLOAD_PURE(void, StOpt::FullRegularGridIterator, nextInc, p_incr);
    }
    inline int  getCount() const override
    {
        PYBIND11_OVERLOAD_PURE(bool, StOpt::FullRegularGridIterator, getCount,);
    }
    inline void jumpToAndInc(const int &p_rank, const int &p_nbProc, const int &p_jump) override
    {
        PYBIND11_OVERLOAD_PURE(void, StOpt::FullRegularGridIterator, jumpToAndInc, p_rank, p_nbProc, p_jump);
    }
    inline int getRelativePosition()  const override
    {
        PYBIND11_OVERLOAD_PURE(int, StOpt::FullRegularGridIterator, getRelativePosition,);
    }
    inline int getNbPointRelative()  const override
    {
        PYBIND11_OVERLOAD_PURE(int, StOpt::FullRegularGridIterator, getNbPointRelative,);
    }
    inline void reset() override
    {
        PYBIND11_OVERLOAD_PURE(void, StOpt::FullRegularGridIterator, reset,);
    }
};


class PyFullLegendreGridIterator : public StOpt::FullLegendreGridIterator
{
public:

    using StOpt::FullLegendreGridIterator::FullLegendreGridIterator;

    inline Eigen::ArrayXd  getCoordinate() const override
    {
        PYBIND11_OVERLOAD_PURE(Eigen::ArrayXd, StOpt::FullLegendreGridIterator, getCoordinate,);
    }
    inline bool isValid() const override
    {
        PYBIND11_OVERLOAD_PURE(bool, StOpt::FullLegendreGridIterator, isValid,);
    }
    inline void next() override
    {
        PYBIND11_OVERLOAD_PURE(void, StOpt::FullLegendreGridIterator, next,);
    }
    inline void nextInc(const int &p_incr) override
    {
        PYBIND11_OVERLOAD_PURE(void, StOpt::FullLegendreGridIterator, nextInc, p_incr);
    }
    inline int  getCount() const override
    {
        PYBIND11_OVERLOAD_PURE(bool, StOpt::FullLegendreGridIterator, getCount,);
    }
    inline void jumpToAndInc(const int &p_rank, const int &p_nbProc, const int &p_jump) override
    {
        PYBIND11_OVERLOAD_PURE(void, StOpt::FullLegendreGridIterator, jumpToAndInc, p_rank, p_nbProc, p_jump);
    }
    inline int getRelativePosition()  const override
    {
        PYBIND11_OVERLOAD_PURE(int, StOpt::FullLegendreGridIterator, getRelativePosition,);
    }
    inline int getNbPointRelative()  const override
    {
        PYBIND11_OVERLOAD_PURE(int, StOpt::FullLegendreGridIterator, getNbPointRelative,);
    }
    inline void reset() override
    {
        PYBIND11_OVERLOAD_PURE(void, StOpt::FullLegendreGridIterator, reset,);
    }
};


class PySparseGridNoBoundIterator : public StOpt::SparseGridNoBoundIterator
{
public:

    using StOpt::SparseGridNoBoundIterator::SparseGridNoBoundIterator;

    inline Eigen::ArrayXd  getCoordinate() const override
    {
        PYBIND11_OVERLOAD_PURE(Eigen::ArrayXd, StOpt::SparseGridNoBoundIterator, getCoordinate,);
    }
    inline bool isValid() const override
    {
        PYBIND11_OVERLOAD_PURE(bool, StOpt::SparseGridNoBoundIterator, isValid,);
    }
    inline void next() override
    {
        PYBIND11_OVERLOAD_PURE(void, StOpt::SparseGridNoBoundIterator, next,);
    }
    inline void nextInc(const int &p_incr) override
    {
        PYBIND11_OVERLOAD_PURE(void, StOpt::SparseGridNoBoundIterator, nextInc, p_incr);
    }
    inline int  getCount() const override
    {
        PYBIND11_OVERLOAD_PURE(bool, StOpt::SparseGridNoBoundIterator, getCount,);
    }
    inline void jumpToAndInc(const int &p_rank, const int &p_nbProc, const int &p_jump) override
    {
        PYBIND11_OVERLOAD_PURE(void, StOpt::SparseGridNoBoundIterator, jumpToAndInc, p_rank, p_nbProc, p_jump);
    }
    inline int getRelativePosition()  const override
    {
        PYBIND11_OVERLOAD_PURE(int, StOpt::SparseGridNoBoundIterator, getRelativePosition,);
    }
    inline int getNbPointRelative()  const override
    {
        PYBIND11_OVERLOAD_PURE(int, StOpt::SparseGridNoBoundIterator, getNbPointRelative,);
    }
    inline void reset() override
    {
        PYBIND11_OVERLOAD_PURE(void, StOpt::SparseGridNoBoundIterator, reset,);
    }
};


class PySparseGridBoundIterator : public StOpt::SparseGridBoundIterator
{
public:

    using StOpt::SparseGridBoundIterator::SparseGridBoundIterator;

    inline Eigen::ArrayXd  getCoordinate() const override
    {
        PYBIND11_OVERLOAD_PURE(Eigen::ArrayXd, StOpt::SparseGridBoundIterator, getCoordinate,);
    }
    inline bool isValid() const override
    {
        PYBIND11_OVERLOAD_PURE(bool, StOpt::SparseGridBoundIterator, isValid,);
    }
    inline void next() override
    {
        PYBIND11_OVERLOAD_PURE(void, StOpt::SparseGridBoundIterator, next,);
    }
    inline void nextInc(const int &p_incr) override
    {
        PYBIND11_OVERLOAD_PURE(void, StOpt::SparseGridBoundIterator, nextInc, p_incr);
    }
    inline int  getCount() const override
    {
        PYBIND11_OVERLOAD_PURE(bool, StOpt::SparseGridBoundIterator, getCount,);
    }
    inline void jumpToAndInc(const int &p_rank, const int &p_nbProc, const int &p_jump) override
    {
        PYBIND11_OVERLOAD_PURE(void, StOpt::SparseGridBoundIterator, jumpToAndInc, p_rank, p_nbProc, p_jump);
    }
    inline int getRelativePosition()  const override
    {
        PYBIND11_OVERLOAD_PURE(int, StOpt::SparseGridBoundIterator, getRelativePosition,);
    }
    inline int getNbPointRelative()  const override
    {
        PYBIND11_OVERLOAD_PURE(int, StOpt::SparseGridBoundIterator, getNbPointRelative,);
    }
    inline void reset() override
    {
        PYBIND11_OVERLOAD_PURE(void, StOpt::SparseGridBoundIterator, reset,);
    }
};


// Interpolators wrapper
//*********************

class PyInterpolator: public StOpt::Interpolator
{
public:
    using StOpt::Interpolator::Interpolator;

    inline double apply(const Eigen::Ref< const Eigen::ArrayXd >   &p_dataValues) const override
    {
        PYBIND11_OVERLOAD_PURE(double, StOpt::Interpolator, apply, p_dataValues);
    }
    inline Eigen::ArrayXd applyVecPy(py::EigenDRef<Eigen::ArrayXXd>p_dataValues) const override
    {
        PYBIND11_OVERLOAD_PURE(Eigen::ArrayXd, StOpt::Interpolator, applyVecPy, p_dataValues);
    }
};

class PyLinearInterpolator: public StOpt::LinearInterpolator
{
public:
    using StOpt::LinearInterpolator::LinearInterpolator;

    inline double apply(const Eigen::Ref< const Eigen::ArrayXd > &p_dataValues) const override
    {
        PYBIND11_OVERLOAD_PURE(double, StOpt::LinearInterpolator, apply, p_dataValues);
    }
    inline Eigen::ArrayXd applyVecPy(py::EigenDRef<Eigen::ArrayXXd> p_dataValues) const override
    {
        PYBIND11_OVERLOAD_PURE(Eigen::ArrayXd, StOpt::LinearInterpolator, applyVecPy, p_dataValues);
    }
};

class PyLegendreInterpolator: public StOpt::LegendreInterpolator
{
public:
    using StOpt::LegendreInterpolator::LegendreInterpolator;

    inline double apply(const Eigen::Ref< const Eigen::ArrayXd > &p_dataValues) const override
    {
        PYBIND11_OVERLOAD_PURE(double, StOpt::LegendreInterpolator, apply, p_dataValues);
    }
    inline Eigen::ArrayXd applyVecPy(py::EigenDRef<Eigen::ArrayXXd> p_dataValues) const override
    {
        PYBIND11_OVERLOAD_PURE(Eigen::ArrayXd, StOpt::LegendreInterpolator, applyVecPy, p_dataValues);
    }
};


class PySparseNoBoundInterpolatorQuadratic: public StOpt::SparseNoBoundInterpolator<StOpt::QuadraticValue, StOpt::QuadraticValue, StOpt::QuadraticValue  >
{

    inline double apply(const Eigen::Ref< const Eigen::ArrayXd > &p_dataValues) const override
    {
        using SparseNoBoundInterpolatorTemp  = StOpt::SparseNoBoundInterpolator< StOpt::QuadraticValue, StOpt::QuadraticValue,  StOpt::QuadraticValue >;
        PYBIND11_OVERLOAD_PURE(double, SparseNoBoundInterpolatorTemp, apply, p_dataValues);

    }
    inline Eigen::ArrayXd applyVecPy(py::EigenDRef<Eigen::ArrayXXd> p_dataValues) const override
    {
        using SparseNoBoundInterpolatorTemp  = StOpt::SparseNoBoundInterpolator< StOpt::QuadraticValue, StOpt::QuadraticValue,  StOpt::QuadraticValue >;
        PYBIND11_OVERLOAD_PURE(Eigen::ArrayXd, SparseNoBoundInterpolatorTemp, applyVecPy, p_dataValues);

    }
};


class PySparseBoundInterpolatorQuadratic: public StOpt::SparseBoundInterpolator<StOpt::QuadraticValue, StOpt::QuadraticValue, StOpt::QuadraticValue  >
{

    inline double apply(const Eigen::Ref< const Eigen::ArrayXd > &p_dataValues) const override
    {
        using SparseBoundInterpolatorTemp  = StOpt::SparseBoundInterpolator< StOpt::QuadraticValue, StOpt::QuadraticValue,  StOpt::QuadraticValue >;
        PYBIND11_OVERLOAD_PURE(double, SparseBoundInterpolatorTemp, apply, p_dataValues);

    }
    inline Eigen::ArrayXd applyVecPy(py::EigenDRef<Eigen::ArrayXXd> p_dataValues) const override
    {
        using SparseBoundInterpolatorTemp  = StOpt::SparseBoundInterpolator< StOpt::QuadraticValue, StOpt::QuadraticValue,  StOpt::QuadraticValue >;
        PYBIND11_OVERLOAD_PURE(Eigen::ArrayXd, SparseBoundInterpolatorTemp, applyVecPy, p_dataValues);

    }
};


class PySparseNoBoundInterpolatorLinear: public StOpt::SparseNoBoundInterpolator<StOpt::LinearHatValue, StOpt::LinearHatValue, StOpt::LinearHatValue  >
{

    inline double apply(const Eigen::Ref< const Eigen::ArrayXd > &p_dataValues) const override
    {
        using SparseNoBoundInterpolatorTemp  = StOpt::SparseNoBoundInterpolator< StOpt::LinearHatValue, StOpt::LinearHatValue,  StOpt::LinearHatValue >;
        PYBIND11_OVERLOAD_PURE(double, SparseNoBoundInterpolatorTemp, apply, p_dataValues);

    }
    inline Eigen::ArrayXd applyVecPy(py::EigenDRef<Eigen::ArrayXXd> p_dataValues) const override
    {
        using SparseNoBoundInterpolatorTemp  = StOpt::SparseNoBoundInterpolator< StOpt::LinearHatValue, StOpt::LinearHatValue,  StOpt::LinearHatValue >;
        PYBIND11_OVERLOAD_PURE(Eigen::ArrayXd, SparseNoBoundInterpolatorTemp, applyVecPy, p_dataValues);

    }
};


class PySparseBoundInterpolatorLinear: public StOpt::SparseBoundInterpolator<StOpt::LinearHatValue, StOpt::LinearHatValue, StOpt::LinearHatValue  >
{

    inline double apply(const Eigen::Ref< const Eigen::ArrayXd > &p_dataValues) const override
    {
        using SparseBoundInterpolatorTemp  = StOpt::SparseBoundInterpolator< StOpt::LinearHatValue, StOpt::LinearHatValue,  StOpt::LinearHatValue >;
        PYBIND11_OVERLOAD_PURE(double, SparseBoundInterpolatorTemp, apply, p_dataValues);

    }
    inline Eigen::ArrayXd applyVecPy(py::EigenDRef<Eigen::ArrayXXd> p_dataValues) const override
    {
        using SparseBoundInterpolatorTemp  = StOpt::SparseBoundInterpolator< StOpt::LinearHatValue, StOpt::LinearHatValue,  StOpt::LinearHatValue >;
        PYBIND11_OVERLOAD_PURE(Eigen::ArrayXd, SparseBoundInterpolatorTemp, applyVecPy, p_dataValues);

    }
};




// Grid wrapper
//*************

class PySpaceGrid: public StOpt::SpaceGrid
{
    using StOpt::SpaceGrid::SpaceGrid;

    size_t getNbPoints() const override
    {
        PYBIND11_OVERLOAD_PURE(size_t, StOpt::SpaceGrid, getNbPoints);
    }

    std::shared_ptr< StOpt::GridIterator> getGridIterator() const override
    {
        using shGrid = std::shared_ptr< StOpt::GridIterator>;
        PYBIND11_OVERLOAD_PURE(shGrid,  StOpt::SpaceGrid, getGridIterator);
    }

    std::shared_ptr< StOpt::GridIterator> getGridIteratorInc(const int &p_iThread) const override
    {
        using shGrid = std::shared_ptr< StOpt::GridIterator>;
        PYBIND11_OVERLOAD_PURE(shGrid,  StOpt::SpaceGrid, getGridIteratorInc, p_iThread);
    }

    std::shared_ptr<StOpt::Interpolator> createInterpolator(const Eigen::ArrayXd &p_coord) const override
    {
        using shInterp =  std::shared_ptr<StOpt::Interpolator>;
        PYBIND11_OVERLOAD_PURE(shInterp, StOpt::SpaceGrid, createInterpolator, p_coord);
    }
    std::shared_ptr<StOpt::InterpolatorSpectral> createInterpolatorSpectral(const Eigen::ArrayXd &p_values) const override
    {
        using shInterp = std::shared_ptr<StOpt::InterpolatorSpectral>;
        PYBIND11_OVERLOAD_PURE(shInterp, StOpt::SpaceGrid, createInterpolatorSpectral, p_values);
    }

    int getDimension() const override
    {
        PYBIND11_OVERLOAD_PURE(int, StOpt::SpaceGrid, getDimension);
    }

    std::vector <std::array< double, 2>  > getExtremeValues() const override
    {
        using vecAr = std::vector <std::array< double, 2>  >;
        PYBIND11_OVERLOAD_PURE(vecAr, StOpt::SpaceGrid, getExtremeValues);
    }

    bool isStrictlyInside(const Eigen::ArrayXd &p_point) const override
    {
        PYBIND11_OVERLOAD_PURE(bool, StOpt::SpaceGrid, isStrictlyInside, p_point);
    }

    bool isInside(const Eigen::ArrayXd &p_point) const override
    {
        PYBIND11_OVERLOAD_PURE(bool, StOpt::SpaceGrid, isInside, p_point);
    }

    void truncatePoint(Eigen::ArrayXd &p_point) const override
    {
        PYBIND11_OVERLOAD_PURE(void, StOpt::SpaceGrid, truncatePoint, p_point);
    }
};

class PyRegularLegendreGrid: public StOpt::RegularLegendreGrid
{
    using StOpt::RegularLegendreGrid::RegularLegendreGrid;

    size_t getNbPoints() const override
    {
        PYBIND11_OVERLOAD_PURE(size_t, StOpt::RegularLegendreGrid, getNbPoints);
    }

    std::shared_ptr< StOpt::GridIterator> getGridIterator() const override
    {
        using shGrid = std::shared_ptr< StOpt::GridIterator>;
        PYBIND11_OVERLOAD_PURE(shGrid,  StOpt::RegularLegendreGrid, getGridIterator);
    }

    std::shared_ptr< StOpt::GridIterator> getGridIteratorInc(const int &p_iThread) const override
    {
        using shGrid = std::shared_ptr< StOpt::GridIterator>;
        PYBIND11_OVERLOAD_PURE(shGrid,  StOpt::RegularLegendreGrid, getGridIteratorInc, p_iThread);
    }

    std::shared_ptr<StOpt::Interpolator> createInterpolator(const Eigen::ArrayXd &p_coord) const override
    {
        using shInterp =  std::shared_ptr<StOpt::Interpolator>;
        PYBIND11_OVERLOAD_PURE(shInterp, StOpt::RegularLegendreGrid, createInterpolator, p_coord);
    }
    std::shared_ptr<StOpt::InterpolatorSpectral> createInterpolatorSpectral(const Eigen::ArrayXd &p_values) const override
    {
        using shInterp = std::shared_ptr<StOpt::InterpolatorSpectral>;
        PYBIND11_OVERLOAD_PURE(shInterp, StOpt::RegularLegendreGrid, createInterpolatorSpectral, p_values);
    }

    int getDimension() const override
    {
        PYBIND11_OVERLOAD_PURE(int, StOpt::RegularLegendreGrid, getDimension);
    }

    std::vector <std::array< double, 2>  > getExtremeValues() const override
    {
        using vecAr = std::vector <std::array< double, 2>  >;
        PYBIND11_OVERLOAD_PURE(vecAr, StOpt::RegularLegendreGrid, getExtremeValues);
    }

    bool isStrictlyInside(const Eigen::ArrayXd &p_point) const override
    {
        PYBIND11_OVERLOAD_PURE(bool, StOpt::RegularLegendreGrid, isStrictlyInside, p_point);
    }

    bool isInside(const Eigen::ArrayXd &p_point) const override
    {
        PYBIND11_OVERLOAD_PURE(bool, StOpt::RegularLegendreGrid, isInside, p_point);
    }

    void truncatePoint(Eigen::ArrayXd &p_point) const override
    {
        PYBIND11_OVERLOAD_PURE(void, StOpt::RegularLegendreGrid, truncatePoint, p_point);
    }
};

class PyRegularSpaceGrid: public StOpt::RegularSpaceGrid
{
    using StOpt::RegularSpaceGrid::RegularSpaceGrid;

    size_t getNbPoints() const override
    {
        PYBIND11_OVERLOAD_PURE(size_t, StOpt::RegularSpaceGrid, getNbPoints);
    }

    std::shared_ptr< StOpt::GridIterator> getGridIterator() const override
    {
        using shGrid = std::shared_ptr< StOpt::GridIterator>;
        PYBIND11_OVERLOAD_PURE(shGrid,  StOpt::RegularSpaceGrid, getGridIterator);
    }

    std::shared_ptr< StOpt::GridIterator> getGridIteratorInc(const int &p_iThread) const override
    {
        using shGrid = std::shared_ptr< StOpt::GridIterator>;
        PYBIND11_OVERLOAD_PURE(shGrid,  StOpt::RegularSpaceGrid, getGridIteratorInc, p_iThread);
    }

    std::shared_ptr<StOpt::Interpolator> createInterpolator(const Eigen::ArrayXd &p_coord) const override
    {
        using shInterp =  std::shared_ptr<StOpt::Interpolator>;
        PYBIND11_OVERLOAD_PURE(shInterp, StOpt::RegularSpaceGrid, createInterpolator, p_coord);
    }
    std::shared_ptr<StOpt::InterpolatorSpectral> createInterpolatorSpectral(const Eigen::ArrayXd &p_values) const override
    {
        using shInterp = std::shared_ptr<StOpt::InterpolatorSpectral>;
        PYBIND11_OVERLOAD_PURE(shInterp, StOpt::RegularSpaceGrid, createInterpolatorSpectral, p_values);
    }

    int getDimension() const override
    {
        PYBIND11_OVERLOAD_PURE(int, StOpt::RegularSpaceGrid, getDimension);
    }

    std::vector <std::array< double, 2>  > getExtremeValues() const override
    {
        using vecAr = std::vector <std::array< double, 2>  >;
        PYBIND11_OVERLOAD_PURE(vecAr, StOpt::RegularSpaceGrid, getExtremeValues);
    }

    bool isStrictlyInside(const Eigen::ArrayXd &p_point) const override
    {
        PYBIND11_OVERLOAD_PURE(bool, StOpt::RegularSpaceGrid, isStrictlyInside, p_point);
    }

    bool isInside(const Eigen::ArrayXd &p_point) const override
    {
        PYBIND11_OVERLOAD_PURE(bool, StOpt::RegularSpaceGrid, isInside, p_point);
    }

    void truncatePoint(Eigen::ArrayXd &p_point) const override
    {
        PYBIND11_OVERLOAD_PURE(void, StOpt::RegularSpaceGrid, truncatePoint, p_point);
    }
};

class PySparseSpaceGridNoBound: public StOpt::SparseSpaceGridNoBound
{
    using StOpt::SparseSpaceGridNoBound::SparseSpaceGridNoBound;

    size_t getNbPoints() const override
    {
        PYBIND11_OVERLOAD_PURE(size_t, StOpt::SparseSpaceGridNoBound, getNbPoints);
    }

    std::shared_ptr< StOpt::GridIterator> getGridIterator() const override
    {
        using shGrid = std::shared_ptr< StOpt::GridIterator>;
        PYBIND11_OVERLOAD_PURE(shGrid,  StOpt::SparseSpaceGridNoBound, getGridIterator);
    }

    std::shared_ptr< StOpt::GridIterator> getGridIteratorInc(const int &p_iThread) const override
    {
        using shGrid = std::shared_ptr< StOpt::GridIterator>;
        PYBIND11_OVERLOAD_PURE(shGrid,  StOpt::SparseSpaceGridNoBound, getGridIteratorInc, p_iThread);
    }

    std::shared_ptr<StOpt::Interpolator> createInterpolator(const Eigen::ArrayXd &p_coord) const override
    {
        using shInterp =  std::shared_ptr<StOpt::Interpolator>;
        PYBIND11_OVERLOAD_PURE(shInterp, StOpt::SparseSpaceGridNoBound, createInterpolator, p_coord);
    }
    std::shared_ptr<StOpt::InterpolatorSpectral> createInterpolatorSpectral(const Eigen::ArrayXd &p_values) const override
    {
        using shInterp = std::shared_ptr<StOpt::InterpolatorSpectral>;
        PYBIND11_OVERLOAD_PURE(shInterp, StOpt::SparseSpaceGridNoBound, createInterpolatorSpectral, p_values);
    }

    int getDimension() const override
    {
        PYBIND11_OVERLOAD_PURE(int, StOpt::SparseSpaceGridNoBound, getDimension);
    }

    std::vector <std::array< double, 2>  > getExtremeValues() const override
    {
        using vecAr = std::vector <std::array< double, 2>  >;
        PYBIND11_OVERLOAD_PURE(vecAr, StOpt::SparseSpaceGridNoBound, getExtremeValues);
    }

    bool isStrictlyInside(const Eigen::ArrayXd &p_point) const override
    {
        PYBIND11_OVERLOAD_PURE(bool, StOpt::SparseSpaceGridNoBound, isStrictlyInside, p_point);
    }

    bool isInside(const Eigen::ArrayXd &p_point) const override
    {
        PYBIND11_OVERLOAD_PURE(bool, StOpt::SparseSpaceGridNoBound, isInside, p_point);
    }

    void truncatePoint(Eigen::ArrayXd &p_point) const override
    {
        PYBIND11_OVERLOAD_PURE(void, StOpt::SparseSpaceGridNoBound, truncatePoint, p_point);
    }
};

class PySparseSpaceGridBound: public StOpt::SparseSpaceGridBound
{
    using StOpt::SparseSpaceGridBound::SparseSpaceGridBound;

    size_t getNbPoints() const override
    {
        PYBIND11_OVERLOAD_PURE(size_t, StOpt::SparseSpaceGridBound, getNbPoints);
    }

    std::shared_ptr< StOpt::GridIterator> getGridIterator() const override
    {
        using shGrid = std::shared_ptr< StOpt::GridIterator>;
        PYBIND11_OVERLOAD_PURE(shGrid,  StOpt::SparseSpaceGridBound, getGridIterator);
    }

    std::shared_ptr< StOpt::GridIterator> getGridIteratorInc(const int &p_iThread) const override
    {
        using shGrid = std::shared_ptr< StOpt::GridIterator>;
        PYBIND11_OVERLOAD_PURE(shGrid,  StOpt::SparseSpaceGridBound, getGridIteratorInc, p_iThread);
    }

    std::shared_ptr<StOpt::Interpolator> createInterpolator(const Eigen::ArrayXd &p_coord) const override
    {
        using shInterp =  std::shared_ptr<StOpt::Interpolator>;
        PYBIND11_OVERLOAD_PURE(shInterp, StOpt::SparseSpaceGridBound, createInterpolator, p_coord);
    }
    std::shared_ptr<StOpt::InterpolatorSpectral> createInterpolatorSpectral(const Eigen::ArrayXd &p_values) const override
    {
        using shInterp = std::shared_ptr<StOpt::InterpolatorSpectral>;
        PYBIND11_OVERLOAD_PURE(shInterp, StOpt::SparseSpaceGridBound, createInterpolatorSpectral, p_values);
    }

    int getDimension() const override
    {
        PYBIND11_OVERLOAD_PURE(int, StOpt::SparseSpaceGridBound, getDimension);
    }

    std::vector <std::array< double, 2>  > getExtremeValues() const override
    {
        using vecAr = std::vector <std::array< double, 2>  >;
        PYBIND11_OVERLOAD_PURE(vecAr, StOpt::SparseSpaceGridBound, getExtremeValues);
    }

    bool isStrictlyInside(const Eigen::ArrayXd &p_point) const override
    {
        PYBIND11_OVERLOAD_PURE(bool, StOpt::SparseSpaceGridBound, isStrictlyInside, p_point);
    }

    bool isInside(const Eigen::ArrayXd &p_point) const override
    {
        PYBIND11_OVERLOAD_PURE(bool, StOpt::SparseSpaceGridBound, isInside, p_point);
    }

    void truncatePoint(Eigen::ArrayXd &p_point) const override
    {
        PYBIND11_OVERLOAD_PURE(void, StOpt::SparseSpaceGridBound, truncatePoint, p_point);
    }
};


class PyGeneralSpaceGrid : public StOpt::GeneralSpaceGrid
{
public :
    using StOpt::GeneralSpaceGrid::GeneralSpaceGrid ;

    inline Eigen::ArrayXi upperPositionCoord(const Eigen::Ref<const Eigen::ArrayXd > &p_point) const override
    {
        return StOpt::GeneralSpaceGrid::upperPositionCoord(p_point);
    }
    inline Eigen::ArrayXd getMeshSize(const Eigen::Ref<const Eigen::ArrayXi > &p_icoord) const override
    {
        return StOpt::GeneralSpaceGrid::getMeshSize(p_icoord);
    }
    inline const Eigen::ArrayXi &getDimensions() const  override
    {
        return StOpt::GeneralSpaceGrid::getDimensions();
    }
    inline std::shared_ptr< StOpt::GridIterator> getGridIterator() const override
    {
        return StOpt::GeneralSpaceGrid::getGridIterator();
    }
    inline std::shared_ptr< StOpt::GridIterator> getGridIteratorInc(const int &p_iThread) const override
    {
        return StOpt::GeneralSpaceGrid::getGridIteratorInc(p_iThread);
    }
    inline std::shared_ptr<StOpt::Interpolator> createInterpolator(const Eigen::ArrayXd &p_coord) const override
    {
        return StOpt::GeneralSpaceGrid::createInterpolator(p_coord);
    }
    inline bool isStrictlyInside(const Eigen::ArrayXd &p_point) const override
    {
        return StOpt::GeneralSpaceGrid::isStrictlyInside(p_point);
    }
    inline bool isInside(const Eigen::ArrayXd &p_point) const override
    {
        return StOpt::GeneralSpaceGrid::isInside(p_point);
    }
    inline int getDimension() const override
    {
        return StOpt::GeneralSpaceGrid::getDimension();
    }
    inline std::vector <std::array< double, 2>  > getExtremeValues() const override
    {
        return StOpt::GeneralSpaceGrid::getExtremeValues() ;
    }
    inline size_t getNbPoints() const override
    {
        return  StOpt::GeneralSpaceGrid::getNbPoints();
    }
    inline void truncatePoint(Eigen::ArrayXd &p_point) const override
    {
        StOpt::GeneralSpaceGrid::truncatePoint(p_point);
    }
};




/// \brief Encapsulation for regression module
PYBIND11_MODULE(StOptGrids, m)
{
    // version
    m.def("getVersion", StOpt::getStOptVersion);

    // Map iterators on grids
    //***********************
    py::class_<StOpt::GridIterator, std::shared_ptr<StOpt::GridIterator>, PyGridIterator > (m, "GridIterator")
    .def(py::init<>())
    .def("getCoordinate", &StOpt::GridIterator::getCoordinate)
    .def("isValid", &StOpt::GridIterator::isValid)
    .def("next", &StOpt::GridIterator::next)
    .def("nextInc", &StOpt::GridIterator::nextInc)
    .def("getCount", &StOpt::GridIterator::getCount)
    .def("getNbPointRelative", &StOpt::GridIterator::getNbPointRelative)
    .def("getRelativePosition", &StOpt::GridIterator::getRelativePosition)
    .def("reset", &StOpt::GridIterator::reset)
    ;

    py::class_<StOpt::FullGeneralGridIterator, std::shared_ptr<StOpt::FullGeneralGridIterator>, PyFullGeneralGridIterator, StOpt::GridIterator> (m, "FullGeneralGridIterator")
    .def(py::init<>())
    .def("getCoordinate", &StOpt::FullGeneralGridIterator::getCoordinate)
    .def("isValid", &StOpt::FullGeneralGridIterator::isValid)
    .def("next", &StOpt::FullGeneralGridIterator::next)
    .def("nextInc", &StOpt::FullGeneralGridIterator::nextInc)
    .def("getCount", &StOpt::FullGeneralGridIterator::getCount)
    ;

    py::class_<StOpt::FullRegularGridIterator, std::shared_ptr<StOpt::FullRegularGridIterator>, PyFullRegularGridIterator, StOpt::GridIterator> (m, "FullRegularGridIterator")
    .def(py::init<>())
    .def("getCoordinate", &StOpt::GridIterator::getCoordinate)
    .def("isValid", &StOpt::GridIterator::isValid)
    .def("next", &StOpt::GridIterator::next)
    .def("nextInc", &StOpt::GridIterator::nextInc)
    .def("getCount", &StOpt::GridIterator::getCount)
    .def("getNbPointRelative", &StOpt::GridIterator::getNbPointRelative)
    .def("getRelativePosition", &StOpt::GridIterator::getRelativePosition)
    .def("reset", &StOpt::GridIterator::reset)
    ;

    py::class_<StOpt::FullLegendreGridIterator, std::shared_ptr<StOpt::FullLegendreGridIterator>, PyFullLegendreGridIterator, StOpt::GridIterator> (m, "FullLegendreGridIterator")
    .def(py::init<>())
    .def("getCoordinate", &StOpt::GridIterator::getCoordinate)
    .def("isValid", &StOpt::GridIterator::isValid)
    .def("next", &StOpt::GridIterator::next)
    .def("nextInc", &StOpt::GridIterator::nextInc)
    .def("getCount", &StOpt::GridIterator::getCount)
    .def("getNbPointRelative", &StOpt::GridIterator::getNbPointRelative)
    .def("getRelativePosition", &StOpt::GridIterator::getRelativePosition)
    .def("reset", &StOpt::GridIterator::reset)
    ;

    py::class_<StOpt::SparseGridNoBoundIterator, std::shared_ptr<StOpt::SparseGridNoBoundIterator>, PySparseGridNoBoundIterator, StOpt::GridIterator> (m, "SparseGridNoBoundIterator")
    .def(py::init<>())
    .def("getCoordinate", &StOpt::GridIterator::getCoordinate)
    .def("isValid", &StOpt::GridIterator::isValid)
    .def("next", &StOpt::GridIterator::next)
    .def("nextInc", &StOpt::GridIterator::nextInc)
    .def("getCount", &StOpt::GridIterator::getCount)
    ;

    py::class_<StOpt::SparseGridBoundIterator, std::shared_ptr<StOpt::SparseGridBoundIterator>, PySparseGridBoundIterator, StOpt::GridIterator> (m, "SparseGridBoundIterator")
    .def(py::init<>())
    .def("getCoordinate", &StOpt::GridIterator::getCoordinate)
    .def("isValid", &StOpt::GridIterator::isValid)
    .def("next", &StOpt::GridIterator::next)
    .def("nextInc", &StOpt::GridIterator::nextInc)
    .def("getCount", &StOpt::GridIterator::getCount)
    ;

    py::class_<StOpt::FullRegularIntGridIterator, std::shared_ptr<StOpt::FullRegularIntGridIterator>, StOpt::GridIterator>(m, "FullRegularIntGridIterator")
    .def(py::init<>())
    .def(py::init< const Eigen::ArrayXi &, const  Eigen::ArrayXi & >())
    .def("getCoordinate", &StOpt::FullRegularIntGridIterator::getCoordinate)
    .def("getIntCoordinate", &StOpt::FullRegularIntGridIterator::getIntCoordinate)
    .def("isValid", &StOpt::GridIterator::isValid)
    .def("next", &StOpt::GridIterator::next)
    .def("nextInc", &StOpt::GridIterator::nextInc)
    .def("getCount", &StOpt::GridIterator::getCount)
    .def("getNbPointRelative", &StOpt::GridIterator::getNbPointRelative)
    .def("getRelativePosition", &StOpt::GridIterator::getRelativePosition)
    .def("reset", &StOpt::GridIterator::reset)
    ;

    // Interpolators
    // *************

    py::class_<StOpt::Interpolator, std::shared_ptr<StOpt::Interpolator>, PyInterpolator > (m, "Interpolator")
    .def("apply", &StOpt::Interpolator::apply)
    .def("applyVec", &StOpt::Interpolator::applyVecPy)
    ;

    py::class_<StOpt::LinearInterpolator, std::shared_ptr<StOpt::LinearInterpolator>, PyLinearInterpolator, StOpt::Interpolator > (m, "LinearInterpolator")
    .def("apply", &StOpt::Interpolator::apply)
    .def("applyVec", &StOpt::Interpolator::applyVecPy)
    ;

    py::class_<StOpt::LegendreInterpolator, std::shared_ptr<StOpt::LegendreInterpolator>, PyLegendreInterpolator, StOpt::Interpolator> (m, "LegendreInterpolator")
    .def("apply", &StOpt::Interpolator::apply)
    .def("applyVec", &StOpt::Interpolator::applyVecPy)
    ;

    using SparseNoBoundInterpolatorLinTemp  = StOpt::SparseNoBoundInterpolator< StOpt::LinearHatValue, StOpt::LinearHatValue,  StOpt::LinearHatValue >;
    py::class_<SparseNoBoundInterpolatorLinTemp, std::shared_ptr<SparseNoBoundInterpolatorLinTemp>, PySparseNoBoundInterpolatorLinear, StOpt::Interpolator> (m, "SparseNoBoundInterpolatorLinear")
    .def("apply", &SparseNoBoundInterpolatorLinTemp::apply)
    .def("applyVec", &SparseNoBoundInterpolatorLinTemp::applyVecPy)
    ;

    using SparseBoundInterpolatorLinTemp  = StOpt::SparseBoundInterpolator< StOpt::LinearHatValue, StOpt::LinearHatValue,  StOpt::LinearHatValue >;
    py::class_<SparseBoundInterpolatorLinTemp, std::shared_ptr<SparseBoundInterpolatorLinTemp>, PySparseBoundInterpolatorLinear, StOpt::Interpolator> (m, "SparseBoundInterpolatorLinear")
    .def("apply", &SparseBoundInterpolatorLinTemp::apply)
    .def("applyVec", &SparseBoundInterpolatorLinTemp::applyVecPy)
    ;

    using SparseNoBoundInterpolatorQuadTemp  = StOpt::SparseNoBoundInterpolator< StOpt::QuadraticValue, StOpt::QuadraticValue,  StOpt::QuadraticValue >;
    py::class_<SparseNoBoundInterpolatorQuadTemp, std::shared_ptr<SparseNoBoundInterpolatorQuadTemp>, PySparseNoBoundInterpolatorQuadratic, StOpt::Interpolator> (m, "SparseNoBoundInterpolatorQuadratic")
    .def("apply", &SparseNoBoundInterpolatorQuadTemp::apply)
    .def("applyVec", &SparseNoBoundInterpolatorQuadTemp::applyVecPy)
    ;

    using SparseBoundInterpolatorQuadTemp  = StOpt::SparseBoundInterpolator< StOpt::QuadraticValue, StOpt::QuadraticValue,  StOpt::QuadraticValue >;
    py::class_<SparseBoundInterpolatorQuadTemp, std::shared_ptr<SparseBoundInterpolatorQuadTemp>, PySparseBoundInterpolatorQuadratic, StOpt::Interpolator> (m, "SparseBoundInterpolatorQuadratic")
    .def("apply", &SparseBoundInterpolatorQuadTemp::apply)
    .def("applyVec", &SparseBoundInterpolatorQuadTemp::applyVecPy)
    ;


    // Map grids
    //**********
    py::class_<StOpt::SpaceGrid, std::shared_ptr<StOpt::SpaceGrid>, PySpaceGrid >(m, "SpaceGrid")
    .def(py::init<>())
    .def("getNbPoints", &StOpt::SpaceGrid::getNbPoints)
    .def("getGridIterator", &StOpt::SpaceGrid::getGridIterator)
    .def("getGridIteratorInc", &StOpt::SpaceGrid::getGridIteratorInc)
    .def("createInterpolator", &StOpt::SpaceGrid::createInterpolator)
    .def("isStrictlyInside", &StOpt::SpaceGrid::isStrictlyInside)
    .def("isInside", &StOpt::SpaceGrid::isInside)
    .def("getDimension", &StOpt::SpaceGrid::getDimension)
    .def("getExtremeValues", &StOpt::SpaceGrid::getExtremeValues)
    .def("__repr__", [](const StOpt::SpaceGrid &)
    {
        return "< SpaceGrid  >" ;
    })
    ;

    py::class_<StOpt::RegularSpaceGrid, std::shared_ptr<StOpt::RegularSpaceGrid>, PyRegularSpaceGrid, StOpt::SpaceGrid>(m, "RegularSpaceGrid")
    .def(py::init<const Eigen::ArrayXd &, const Eigen::ArrayXd &, const  Eigen::ArrayXi & >())
    .def("getNbPoints", &StOpt::RegularSpaceGrid::getNbPoints)
    .def("getGridIterator", &StOpt::RegularSpaceGrid::getGridIterator)
    .def("getGridIteratorInc", &StOpt::RegularSpaceGrid::getGridIteratorInc)
    .def("createInterpolator", &StOpt::RegularSpaceGrid::createInterpolator)
    .def("isStrictlyInside", &StOpt::RegularSpaceGrid::isStrictlyInside)
    .def("isInside", &StOpt::RegularSpaceGrid::isInside)
    .def("getDimension", &StOpt::RegularSpaceGrid::getDimension)
    .def("getExtremeValues", &StOpt::RegularSpaceGrid::getExtremeValues)
    .def("lowerPositionCoord", &StOpt::RegularSpaceGrid::lowerPositionCoord)
    .def("upperPositionCoord", &StOpt::RegularSpaceGrid::upperPositionCoord)
    .def("getMeshSize", &StOpt::RegularSpaceGrid::getMeshSize)
    .def("getDimensions", &StOpt::RegularSpaceGrid::getDimensions)
    .def("__repr__", [](const StOpt::RegularSpaceGrid & p_g)
    {
        std::stringstream ss ;
        ss << std::endl << " Low Values :" <<  p_g.getLowValues().transpose() << std::endl << " Step : " << p_g.getStep().transpose() << std::endl << " nbStep : " <<   p_g.getNbStep().transpose() ;
        return "< RegularSpaceGrid" +  ss.str() +  "  >" ;
    })
    ;

    py::class_<StOpt::RegularLegendreGrid, std::shared_ptr<StOpt::RegularLegendreGrid>, PyRegularLegendreGrid, StOpt::SpaceGrid>(m, "RegularLegendreGrid")
    .def(py::init< const Eigen::ArrayXd &, const Eigen::ArrayXd, const  Eigen::ArrayXi &, const Eigen::ArrayXi & >())
    .def("getNbPoints", &StOpt::RegularLegendreGrid::getNbPoints)
    .def("getGridIterator", &StOpt::RegularLegendreGrid::getGridIterator)
    .def("getGridIteratorInc", &StOpt::RegularLegendreGrid::getGridIteratorInc)
    .def("createInterpolator", &StOpt::RegularLegendreGrid::createInterpolator)
    .def("isStrictlyInside", &StOpt::RegularLegendreGrid::isStrictlyInside)
    .def("isInside", &StOpt::RegularLegendreGrid::isInside)
    .def("getDimension", &StOpt::RegularLegendreGrid::getDimension)
    .def("getExtremeValues", &StOpt::RegularLegendreGrid::getExtremeValues)
    .def("lowerPositionCoord", &StOpt::RegularLegendreGrid::lowerPositionCoord)
    .def("upperPositionCoord", &StOpt::RegularLegendreGrid::upperPositionCoord)
    .def("getMeshSize", &StOpt::RegularLegendreGrid::getMeshSize)
    .def("getDimensions", &StOpt::RegularLegendreGrid::getDimensions)
    .def("__repr__", [](const StOpt::RegularLegendreGrid & p_g)
    {
        std::stringstream ss ;
        ss << std::endl << " Low Values :" <<  p_g.getLowValues().transpose() << std::endl << " Step : " << p_g.getStep().transpose() << std::endl << " nbStep : " <<   p_g.getNbStep().transpose() ;
        return "< RegularLegendreGrid" +  ss.str() +  "  >" ;
    })
    ;

    py::class_<StOpt::GeneralSpaceGrid, std::shared_ptr< StOpt::GeneralSpaceGrid>, PyGeneralSpaceGrid, StOpt::SpaceGrid > (m, "GeneralSpaceGrid")
    .def(py::init([](const py::list & p_x)
    {
        std::vector<std::shared_ptr<Eigen::ArrayXd> >  x = convertFromListShPtr<Eigen::ArrayXd>(p_x);
        return new StOpt::GeneralSpaceGrid(x);
    }))
    .def("lowerPositionCoord", &StOpt::GeneralSpaceGrid::lowerPositionCoord)
    .def("upperPositionCoord", &StOpt::GeneralSpaceGrid::upperPositionCoord)
    .def("getMeshSize", & StOpt::GeneralSpaceGrid::getMeshSize)
    .def("getDimensions", &StOpt::GeneralSpaceGrid::getDimensions, py::return_value_policy::reference_internal)
    .def("getGridIterator", &StOpt::GeneralSpaceGrid::getGridIterator)
    .def("getGridIteratorInc", &StOpt::GeneralSpaceGrid::getGridIteratorInc)
    .def("createInterpolator", &StOpt::GeneralSpaceGrid::createInterpolator)
    .def("isStrictlyInside", &StOpt::GeneralSpaceGrid::isStrictlyInside)
    .def("isInside", &StOpt::GeneralSpaceGrid::isInside)
    .def("getDimension", &StOpt::GeneralSpaceGrid::getDimension)
    .def("getExtremeValues", &StOpt::GeneralSpaceGrid::getExtremeValues)
    .def("getNbPoints", &StOpt::GeneralSpaceGrid::getNbPoints)
    .def("truncatePoint", &StOpt::GeneralSpaceGrid::truncatePoint)

    ;

    py::class_<StOpt::SparseSpaceGridBound, std::shared_ptr<StOpt::SparseSpaceGridBound>, PySparseSpaceGridBound, StOpt::SpaceGrid >(m, "SparseSpaceGridBound")
    .def(py::init< const int &,  const Eigen::ArrayXd &, const size_t &> ())
    .def(py::init< const Eigen::ArrayXd &, const Eigen::ArrayXd &, const int &,  const Eigen::ArrayXd &, const size_t & > ())
    .def("getNbPoints", &StOpt::SparseSpaceGridBound::getNbPoints)
    .def("getGridIterator", &StOpt::SparseSpaceGridBound::getGridIterator)
    .def("getGridIteratorInc", &StOpt::SparseSpaceGridBound::getGridIteratorInc)
    .def("createInterpolator", &StOpt::SparseSpaceGridBound::createInterpolator)
    .def("isStrictlyInside", &StOpt::SparseSpaceGridBound::isStrictlyInside)
    .def("isInside", &StOpt::SparseSpaceGridBound::isInside)
    .def("getDimension", &StOpt::SparseSpaceGridBound::getDimension)
    .def("getExtremeValues", &StOpt::SparseSpaceGridBound::getExtremeValues)
    .def("toHierarchize", &StOpt::SparseSpaceGridBound::toHierarchizeD)
    .def("toHierarchizeVec", &StOpt::SparseSpaceGridBound::toHierarchizeVecD)
    .def("refine", &refineSparseWrapp<StOpt::SparseSpaceGridBound>)
    .def("coarsen", &coarsenSparseWrapp<StOpt::SparseSpaceGridBound>)
    ;

    py::class_<StOpt::SparseSpaceGridNoBound, std::shared_ptr<StOpt::SparseSpaceGridNoBound>, PySparseSpaceGridNoBound, StOpt::SpaceGrid >(m, "SparseSpaceGridNoBound")
    .def(py::init< const int &,  const Eigen::ArrayXd &, const size_t &> ())
    .def(py::init< const Eigen::ArrayXd &, const Eigen::ArrayXd &, const int &,  const Eigen::ArrayXd &, const size_t & > ())
    .def("getNbPoints", &StOpt::SparseSpaceGridNoBound::getNbPoints)
    .def("getGridIterator", &StOpt::SparseSpaceGridNoBound::getGridIterator)
    .def("getGridIteratorInc", &StOpt::SparseSpaceGridNoBound::getGridIteratorInc)
    .def("createInterpolator", &StOpt::SparseSpaceGridNoBound::createInterpolator)
    .def("isStrictlyInside", &StOpt::SparseSpaceGridNoBound::isStrictlyInside)
    .def("isInside", &StOpt::SparseSpaceGridNoBound::isInside)
    .def("getDimension", &StOpt::SparseSpaceGridNoBound::getDimension)
    .def("getExtremeValues", &StOpt::SparseSpaceGridNoBound::getExtremeValues)
    .def("toHierarchize", &StOpt::SparseSpaceGridNoBound::toHierarchizeD)
    .def("toHierarchizeVec", &StOpt::SparseSpaceGridNoBound::toHierarchizeVecD)
    .def("refine", &refineSparseWrapp<StOpt::SparseSpaceGridNoBound>)
    .def("coarsen", &coarsenSparseWrapp<StOpt::SparseSpaceGridNoBound>)
    ;

    py::class_<StOpt::OneDimRegularSpaceGrid, std::shared_ptr<StOpt::OneDimRegularSpaceGrid> >(m, "OneDimRegularSpaceGrid")
    .def(py::init< const double &, const double &, const  int & >())
    .def("getMesh", &StOpt::OneDimRegularSpaceGrid::getMesh)
    ;

    py::class_<StOpt::RegularSpaceIntGrid, std::shared_ptr<StOpt::RegularSpaceIntGrid>>(m, "RegularSpaceIntGrid")
            .def(py::init< const Eigen::ArrayXi &, const  Eigen::ArrayXi & >())
            .def("globCoordPerDimToLocal", &StOpt::RegularSpaceIntGrid::globCoordPerDimToLocal)
            .def("getGridIterator", &StOpt::RegularSpaceIntGrid::getGridIterator)
            .def("getNbPoints", &StOpt::RegularSpaceIntGrid::getNbPoints)
            .def("getDimension", &StOpt::RegularSpaceIntGrid::getDimension)
            .def("getLowValueDim", &StOpt::RegularSpaceIntGrid::getLowValueDim)
            .def("getMaxValueDim", &StOpt::RegularSpaceIntGrid::getMaxValueDim)
            .def("getSizeInDim", &StOpt::RegularSpaceIntGrid::getSizeInDim)
            ;

    py::class_<StOpt::OneDimSpaceGrid, std::shared_ptr<StOpt::OneDimSpaceGrid>>(m, "OneDimSpaceGrid")
            .def(py::init<const Eigen::ArrayXd &>())
            .def("getMesh", &StOpt::OneDimSpaceGrid::getMesh)
            .def("getNbStep", &StOpt::OneDimSpaceGrid::getNbStep)
            ;
    py::class_<StOpt::Mesh2D, std::shared_ptr<StOpt::Mesh2D>>(m, "Mesh2D")
            .def(py::init<const double &,    const double &, const  double &,   const double &,   const int &,  const int &,  const int &,   const int &>())
            .def("split", &StOpt::Mesh2D::split)
            ;

    py::class_<StOpt::GridAdapt2D, std::shared_ptr<StOpt::GridAdapt2D>>(m, "GridAdapt2D")
            .def(py::init<const double &, const double &, const int &, const double &, const double &, const int & >())
            ;
}
