// Copyright (C) 2025 EDF
// All Rights Reserved
// This code is published under the GNU Lesser General Public License (GNU LGPL)
#ifndef CONTINUATIONCUTSGRIDADAPTNONCONCAVE_H
#define CONTINUATIONCUTSGRIDADAPTNONCONCAVE_H
#include <vector>
#include <array>
#include <map>
#include <Eigen/Dense>
#include "StOpt/core/grids/GridAdaptBase.h"
#include "StOpt/core/grids/Mesh1D.h"
#include "StOpt/core/grids/Mesh2D.h"
#include "StOpt/core/utils/constant.h"
#include "StOpt/core/utils/eigenComparison.h"
#include "StOpt/regression/BaseRegression.h"

/** \file ContinuationCutsGridAdaptNonConcave.h
 *  \brief In DP, when transition problem are solved using LP some final cuts are given to the LP solver as ending conditions
 *        This permits to store cuts regressing the cuts coefficients.
 *        For each point of the grid, the cuts are stored.
 *        From one point given, one gets a list of cuts of the grids points corresponding to a given mesh that can be reached
 *       Do not suppose that the function is  concave everywhere
 * \author  Xavier Warin
 */

namespace StOpt
{

/// \class ContinuationCutsGridAdaptNonConcave ContinuationCutsGridAdaptNonConcave.h
/// Permits to store  some cuts at each point of a space grid which is 1D or 2D and adaptative
/// By defining cuts per mesh, non concavity can be treated by brut force on the LP
class ContinuationCutsGridAdaptNonConcave
{

private :
    std::shared_ptr< GridAdaptBase >    m_grid ; ///< grid used to define stock points
    std::shared_ptr< BaseRegression >  m_condExp ; ///< conditional expectation
    Eigen::Array< Eigen::ArrayXXd, Eigen::Dynamic, 1  > m_regressedCutCoeff ; ///< for each cut (so each point on the grid)   \f$ \bar a_0 + \sum_i^dim a_i x_i  \f$ store the  coefficients coefficient for each basis function and each stock points (nb basis , nb points). Notice that  \f$ \bar a_0 =  a_0 - \sum_i^dim a_i \bar x_i \f$


  /// \brief From given cuts on 1D mesh, say if it represents a concave function
  ///   Points  in mesh
  ///       0  1
  ///       Check that the derivatives are decreasing
  /// \param p_cuts    Array of cuts on a mesh  size : (dim+1 , nb point)= (2,2)
  /// \param p_xL      left coordinate of mesh  
  /// \param p_xR      right coordinate of mesh  
  /// \param p_bStrict if true check that the values at edge are below the cut values for x increasing
  /// \return  a bool true if the function is concave
  inline bool  isConcave1D( const Eigen::ArrayXXd & p_cuts, const double & p_xL, const double & p_xR, const bool&  p_bStrict) const
  {
    // check if concave  on mesh
    bool bConcave = true;

    // get back values
    double val0 = p_cuts(0,0) + p_cuts(1,0)*p_xL; 
    double val1 = p_cuts(0,1) + p_cuts(1,1)*p_xR; 
    
    // Left derivative  should be in x  above  Right
    if ( p_cuts(1, 0)+ small<  p_cuts(1,1))
      {
	return false;
      }

    if (p_bStrict)
      {
	// Hyperplane   from  point 0  should be  above the value of point 1
	if (val1 > p_cuts(0,0) + p_cuts(1,0)*p_xR +small )
	  {
	    return false;
	  }
      }
 
    return bConcave;
  }

  /// \brief From given cuts on 2D rectangular mesh, say if it represents a concave function
  ///   Points  in mesh
  ///       0  3
  ///       1  2
  ///       Check that the derivatives in each diretion are decreasing
  /// \param p_cuts    Array of cuts on a mesh  size : (dim+1 , nb point)= (3,4)
  /// \param p_xL      left coordinate of mesh  
  /// \param p_xR      right coordinate of mesh  
  /// \param p_yB      bottom coodinate of mesh  
  /// \param p_yT      top  coodinate of mesh  
  /// \param p_bStrict if true check that the values at edge are below the cut values for x , y increasing
  /// \return  a bool true if the function is concave
  inline bool  isConcave2D( const Eigen::ArrayXXd & p_cuts, const double & p_xL, const double & p_xR,  const double & p_yB, const double & p_yT, const bool&  p_bStrict) const
  {
    // check if concave  on mesh
    bool bConcave = true;

    // get back values
    double val0 = p_cuts(0,0) + p_cuts(1,0)*p_xL  + p_cuts(2,0)*p_yT ; 
    double val1 = p_cuts(0,1) + p_cuts(1,1)*p_xL +  p_cuts(2,1)*p_yB ; 
    double val2 = p_cuts(0,2) + p_cuts(1,2)*p_xR +  p_cuts(2,2)*p_yB ;
    double val3 = p_cuts(0,3) + p_cuts(1,3)*p_xR +  p_cuts(2,3)*p_yT ;
    
    // Left Top derivative  should be in y below  Left Bottom
    if ( p_cuts(2, 0) >  p_cuts(2,1)+small ) 
      {
	return false;
      }
    // Left Top derivative  should be in x  above  Right Top
    else if ( p_cuts(1, 0)+ small<  p_cuts(1,3))
      {
	return false;
      }
    // Left Bottom  derivative  should be in x  above  Right Bottom
    else if  ( p_cuts(1, 1)+small<  p_cuts(1,2))
      {
	return  false;
      }
     // Right Top  derivative  should be in y below  Right Bottom
     else if ( p_cuts(2, 3)>  p_cuts(2,2)+small)
      {
	return  false;
      }

    if (p_bStrict)
      {
	// Hyperplane   from  point 1  should be  above the value of point 0
	if (val0 > p_cuts(0,1) + p_cuts(1,1)*p_xL +  p_cuts(2,1)*p_yT+small )
	  {
	    return false;
	  }
	//  Hyperplane   from  point 1  should be  above the value of point 2
	else if   (val2 > p_cuts(0,1) + p_cuts(1,1)*p_xR +  p_cuts(2,1)*p_yB +small)
	  {
	    return false;
	  }	
	else
	  {
	    //   Points  in mesh
	    //       0  3
	    //       1  2
	    // check that value in 3 is below the 3 hyperplanes
	    for (int ii =0; ii < 3; ++ii)
	      {
		if ( val3 >  p_cuts(0,ii) + p_cuts(1,ii)*p_xR +  p_cuts(2,ii)*p_yT +small)
		  {
		    return false;
		  }
	      }
	  }
      }
 
    return bConcave;
  }

  /// \brief Giving 2 grids  G1 and G2  and there index in a 1 by 1 grid  in L= [(0),(1)] , in 1D
  ///        Say that the coordinates of G1 and G2 permits to merge the grid
  /// \param p_G1        Sub grid G1
  /// \param p_G2        Sub grid G2
  bool areTheTwoGridsMergable1D( const   std::shared_ptr< GridAdaptBase> & p_G1, const   std::shared_ptr< GridAdaptBase> & p_G2) const;

  /// \brief Giving 2 grids  G1 and G2  and there index in a 2 by 2 grid  in L= [(0,0),(1,0),(1,0),(1,1)] , in 2D
  ///        Say that the coordinates of G1 and G2 permits to merge the grid
  /// \param p_indexG1  current local index of grid G1 in grid composed of 4 sub grids
  /// \param p_indexG2  current local index of grid G2 in grid composed of 4 sub grids
  /// \param p_G1        Sub grid G1
  /// \param p_G2        Sub grid G2
  bool areTheTwoGridsMergable2D( const Eigen::ArrayXi & p_indexG1, const Eigen::ArrayXi & p_indexG2, const   std::shared_ptr< GridAdaptBase> & p_G1, const   std::shared_ptr< GridAdaptBase> & p_G2) const;

  /// Given a grid G with a given index [(i0) , .., (ik)] with is mergable, in 1D
  /// Try to merge all mergable sub grid in G.
  /// If possible some new mergable grids can be created, otherwise existing not merge are added to vector of existing grids that cannot be further treated
  /// \param p_levelCut           level of current grid
  /// \param p_mergableMapMesh    Map of mergable grids with level indexation
  /// \param p_newMapMeshPM       map of elements merge of  p_mergableMapMesh  (updated)
  /// \param p_deletedKeysPM      vector of  multi index (vector of index) to be delete in  p_mergableMapMesh   (updated)
  /// \param p_mergeGridsVec      list of final grids to be returned to delimit convex areas where the solution is concave
  void treatMergableGrids1D( const std::vector<Eigen::ArrayXi > &  p_levelCut, 
			   std::map< std::vector<Eigen::ArrayXi >,std::pair< std::shared_ptr< GridAdaptBase>, std::shared_ptr<Eigen::ArrayXXd> > , cmpVectorArrayXi> & p_mergableMapMesh,
			   std::map< std::vector<Eigen::ArrayXi >,std::pair< std::shared_ptr< GridAdaptBase>, std::shared_ptr<Eigen::ArrayXXd > > , cmpVectorArrayXi> &  p_newMapMeshPM,
			   std::vector<  std::vector<Eigen::ArrayXi > >  & p_deletedKeysPM,
			   std::vector< std::pair <std::shared_ptr<GridAdaptBase>, std::shared_ptr<Eigen::ArrayXXd>  > > & p_mergeGridsVec) const;

  /// Given a grid G with a given index [(i0,j0) , .., (ik,jk)] with is mergable, in 2D
  /// Try to merge all mergable sub grid in G.
  /// If possible some new mergable grids can be created, otherwise existing not merge are added to vector of existing grids that cannot be futher treated
  /// \param p_levelCut           level of current grid
  /// \param p_mergableMapMesh    Map of mergable grids with level indexation
  /// \param p_newMapMeshPM       map of elements merge of  p_mergableMapMesh  (updated)
  /// \param p_deletedKeysPM      vector of  multi index (vector of index) to be delete in  p_mergableMapMesh   (updated)
  /// \param p_mergeGridsVec      list of final grids to be returned to delimit convex areas where the solution is concave
  void treatMergableGrids2D( const std::vector<Eigen::ArrayXi > &  p_levelCut, 
			   std::map< std::vector<Eigen::ArrayXi >,std::pair< std::shared_ptr< GridAdaptBase>, std::shared_ptr<Eigen::ArrayXXd> > , cmpVectorArrayXi> & p_mergableMapMesh,
			   std::map< std::vector<Eigen::ArrayXi >,std::pair< std::shared_ptr< GridAdaptBase>, std::shared_ptr<Eigen::ArrayXXd > > , cmpVectorArrayXi> &  p_newMapMeshPM,
			   std::vector<  std::vector<Eigen::ArrayXi > >  & p_deletedKeysPM,
			   std::vector< std::pair <std::shared_ptr<GridAdaptBase>, std::shared_ptr<Eigen::ArrayXXd>  > > & p_mergeGridsVec) const;
  
  /// \brief Inputs a vector of iterators with  (grid1D, cut) and merge the grids in single one, in 1D
  /// \param  p_vecGridToMerge the collection of grids to merge
  /// \return  The merge grid and cuts associated
  std::pair<std::shared_ptr<StOpt::GridAdaptBase > , std::shared_ptr<Eigen::ArrayXXd> > mergeGridAndCuts1D(const std::vector< std::map< std::vector< Eigen::ArrayXi > , std::pair< std::shared_ptr< GridAdaptBase> , std::shared_ptr<Eigen::ArrayXXd > >, cmpVectorArrayXi >::iterator > & p_vecGridToMerge) const;

  /// \brief Inputs a vector of iterators with  (grid2D, cut) and merge the grids in single one, in 2D
  /// \param  p_vecGridToMerge the collection of grids to merge
  /// \return  The merge grid and cuts associated
  std::pair<std::shared_ptr<StOpt::GridAdaptBase > , std::shared_ptr<Eigen::ArrayXXd> > mergeGridAndCuts2D(const std::vector< std::map< std::vector< Eigen::ArrayXi > , std::pair< std::shared_ptr< GridAdaptBase> , std::shared_ptr<Eigen::ArrayXXd > >, cmpVectorArrayXi >::iterator > & p_vecGridToMerge) const;
  
  /// \brief internal function  to separate concave and non concave grids/mesh, in 1D
  /// \param p_thesMeshesAndPos     the list of mehes associated to the grid and a description of the levels of the mesh
  /// \param p_isim                 simulation number
  /// \param p_nonConvavec          a list of grids/mesh non concave and cuts associated on points of the grid (output)
  /// \param p_mapMesh              a map storing the concave meshes and cuts associated (output)
  /// \param p_bStrict              if true check concavity using hyperplanes values too
  /// \return  calculate the max level of refinement in the grid
  int  separateNonConcaveConcaveGrids1D(const std::list<  std::pair< std::shared_ptr< Mesh1D> ,
				    std::shared_ptr<std::vector<Eigen::ArrayXi > > > > &  p_thesMeshesAndPos,
				    const int & isim,  std::vector< std::pair <std::shared_ptr<GridAdaptBase>, std::shared_ptr<Eigen::ArrayXXd>  > > & p_nonConvaVec,
				      std::map< std::vector<Eigen::ArrayXi > , std::pair< std::shared_ptr< GridAdaptBase> ,
				      std::shared_ptr<Eigen::ArrayXXd >> , cmpVectorArrayXi> & p_mapMesh,
				      const bool & p_bStrict ) const;

  /// \brief internal function  to separate concave and non concave grids/mesh, in 2D
  /// \param p_thesMeshesAndPos     the list of mehes associated to the grid and a description of the levels of the mesh
  /// \param p_isim                 simulation number
  /// \param p_nonConvavec          a list of grids/mesh non concave and cuts associated on points of the grid (output)
  /// \param p_mapMesh              a map storing the concave meshes and cuts associated (output)
  /// \param p_bStrict              if true check concavity using hyperplanes values too
  /// \return  calculate the max level of refinement in the grid
  int  separateNonConcaveConcaveGrids2D(const std::list<  std::pair< std::shared_ptr< Mesh2D> ,
				    std::shared_ptr<std::vector<Eigen::ArrayXi > > > > &  p_thesMeshesAndPos,
				    const int & isim,  std::vector< std::pair <std::shared_ptr<GridAdaptBase>, std::shared_ptr<Eigen::ArrayXXd>  > > & p_nonConvaVec,
				      std::map< std::vector<Eigen::ArrayXi > , std::pair< std::shared_ptr< GridAdaptBase> ,
				      std::shared_ptr<Eigen::ArrayXXd >> , cmpVectorArrayXi> & p_mapMesh,
				      const bool & p_bStrict ) const;

  /// \brief internal function  to separate concave and non concave grids/mesh, in 1D
  /// \param p_thesMeshesAndPos     the list of meshes associated to the grid and a description of the levels of the mesh
  /// \param p_coordinate           contains values of uncertainties
  /// \param p_nonConvavec          a list of grids/mesh non concave and cuts associated on points of the grid (output)
  /// \param p_mapMesh              a map storing the concave meshes and cuts associated (output)
  /// \param p_bStrict              if true check concavity using hyperplanes values too
  /// \return  calculate the max level of refinement in the grid
  int  separateNonConcaveConcaveGrids1D(const std::list<  std::pair< std::shared_ptr< Mesh1D> ,
				      std::shared_ptr<std::vector<Eigen::ArrayXi > > > > &  p_thesMeshesAndPos,
				      const Eigen::ArrayXd & p_coordinates,
				      std::vector< std::pair <std::shared_ptr<GridAdaptBase>, std::shared_ptr<Eigen::ArrayXXd>  > > & p_nonConvaVec,
				      std::map< std::vector<Eigen::ArrayXi > , std::pair< std::shared_ptr< GridAdaptBase> , std::shared_ptr<Eigen::ArrayXXd> > , cmpVectorArrayXi> & p_mapMesh,
				      const bool & p_bStrict) const;

  /// \brief internal function  to separate concave and non concave grids/mesh, in 2D
  /// \param p_thesMeshesAndPos     the list of mehes associated to the grid and a description of the levels of the mesh
  /// \param p_coordinate           contains values of uncertainties
  /// \param p_nonConvavec          a list of grids/mesh non concave and cuts associated on points of the grid (output)
  /// \param p_mapMesh              a map storing the concave meshes and cuts associated (output)
  /// \param p_bStrict              if true check concavity using hyperplanes values too
  /// \return  calculate the max level of refinement in the grid
  int  separateNonConcaveConcaveGrids2D(const std::list<  std::pair< std::shared_ptr< Mesh2D> ,
				      std::shared_ptr<std::vector<Eigen::ArrayXi > > > > &  p_thesMeshesAndPos,
				      const Eigen::ArrayXd & p_coordinates,
				      std::vector< std::pair <std::shared_ptr<GridAdaptBase>, std::shared_ptr<Eigen::ArrayXXd>  > > & p_nonConvaVec,
				      std::map< std::vector<Eigen::ArrayXi > , std::pair< std::shared_ptr< GridAdaptBase> , std::shared_ptr<Eigen::ArrayXXd> > , cmpVectorArrayXi> & p_mapMesh,
				      const bool & p_bStrict) const;


  /// \brief internal function that merge from the max level where the mesh are refine, in 1D
  /// \param   p_maxLevel       maximum refinement level
  /// \param   p_mergeGridsVec  update a vector of final grids and cuts that are merged (modified)
  /// \param   p_mapMesh        a map storing the concave meshes and cuts associated (modified)
  void mergeGridsByLevel1D(const int & p_maxLevel,
			 std::vector< std::pair <std::shared_ptr<GridAdaptBase>, std::shared_ptr<Eigen::ArrayXXd>  > > & p_mergeGridsVec,
			 std::map< std::vector<Eigen::ArrayXi > , std::pair< std::shared_ptr< GridAdaptBase> , std::shared_ptr<Eigen::ArrayXXd > > , cmpVectorArrayXi> & p_mapMesh) const;

  /// \brief internal function that merge from the max level where the mesh are refine, in 2D
  /// \param   p_maxLevel       maximum refinement level
  /// \param   p_mergeGridsVec  update a vector of final grids and cuts that are merged (modified)
  /// \param   p_mapMesh        a map storing the concave meshes and cuts associated (modified)
  void mergeGridsByLevel2D(const int & p_maxLevel,
			 std::vector< std::pair <std::shared_ptr<GridAdaptBase>, std::shared_ptr<Eigen::ArrayXXd>  > > & p_mergeGridsVec,
			 std::map< std::vector<Eigen::ArrayXi > , std::pair< std::shared_ptr< GridAdaptBase> , std::shared_ptr<Eigen::ArrayXXd > > , cmpVectorArrayXi> & p_mapMesh) const;
  

public :
    /// \brief Default constructor
    ContinuationCutsGridAdaptNonConcave() {}

    /// \brief Constructor
    /// \param p_grid   grid for stocks
    /// \param p_condExp regressor for conditional expectation
    /// \param p_values   functions to store  ((number of simulations by number of cuts) by (nb of stocks)). The pValues are given as
    ///                   \f$ a_0 + \sum_i^dim a_i (x_i -\bar x_i) \f$ at a point \f$ \bar x \f$.
    ContinuationCutsGridAdaptNonConcave(const  std::shared_ptr< GridAdaptBase >   &p_grid,
                     const  std::shared_ptr< BaseRegression >   &p_condExp,
                     const  Eigen::ArrayXXd &p_values);

    /// \brief Constructor
    ContinuationCutsGridAdaptNonConcave(const std::shared_ptr<BaseRegression>   &p_condExp) :
        m_condExp(p_condExp)
    {}

    /// \brief Load another Continuation value object
    ///  Only a partial load of the objects is achieved
    /// \param p_grid   Grid to load
    /// \param p_condExp Condition expectation
    /// \param p_values coefficient polynomials for regression
    virtual void loadForSimulation(const  std::shared_ptr< GridAdaptBase > &p_grid,
                                   const std::shared_ptr< BaseRegression >   &p_condExp,
                                   const Eigen::Array< Eigen::ArrayXXd, Eigen::Dynamic, 1  >  &p_values)
    {
        m_grid = p_grid;
        m_condExp = p_condExp;
        m_regressedCutCoeff = p_values ;
    }

    /// \brief Get a list of all cuts for all simulations  \f$ (\bar a_0, a_1, ...a_d) \f$
    ///        for  stock points in mesh
    /// \param  p_hypStock list of points  defining an hypercube :
    ///          - (i,0)  coordinate corresponds to min value in dimension i
    ///          - (i,1)  coordinate corresponds to max value in dimension i
    ///          .
    ///  \return   a vector of a pair.
    ///  Each element of the pair is composed of a GridAdaptBase and  an array of cuts C for points in the GridAdaptBase
    ///  C  shape  :  first dimenson :   (nb simulations by number of cuts)
    ///               second dimension : nb of points  in the grid  ( number associated to points in GridAdaptBase)
    std::vector< std::pair <std::shared_ptr< GridAdaptBase>, std::shared_ptr<Eigen::ArrayXXd> > >    getCutsAllSimulations(const  Eigen::ArrayXXd &p_hypStock) const;


    /// \brief  get  a vector of grids corresponding of a mesh  in the hypercube  with cuts associated to the  mesh
    /// \param  p_hypStock list of points  defining an hypercube :
    ///          - (i,0)  coordinate corresponds to min value in dimension i
    ///          - (i,1)  coordinate corresponds to max value in dimension i
    ///          .
    /// \param  p_coordinates   simulation coordinates
    /// \return  a vector of a pair
    ///  Each element of the pair is composed of a  grid and  an array of cuts C for points in the grid
    ///  C  shape  :  first dimension :   number of cuts
    ///               second dimension : nb of points  in the grid  ( number associated to points in GridAdaptBase)
    std::vector< std::pair <std::shared_ptr< GridAdaptBase>, std::shared_ptr<Eigen::ArrayXXd>  > >  getCutsASim(const  Eigen::ArrayXXd &  p_hypStock, const Eigen::ArrayXd &p_coordinates) const;


    /// \brief  get  a vector of grids corresponding of a mesh  in the hypercube  with cuts associated to the  mesh
    /// \param  p_hypStock list of points  defining an hypercube :
    ///          - (i,0)  coordinate corresponds to min value in dimension i
    ///          - (i,1)  coordinate corresponds to max value in dimension i
    ///          .
    /// \param  p_isim   : scenario number
    /// \return  a vector of a pair.
    ///  Each element of the pair is composed of a  grid  and  an array of cuts C for points in the grid
    ///  C  shape  :  first dimenson :   number of cuts
   ///               second dimension : nb of points  in the grid  ( number associated to points in GridAdaptBase)
    std::vector< std::pair <std::shared_ptr< GridAdaptBase>, std::shared_ptr<Eigen::ArrayXXd>  > >  getCutsASim(const  Eigen::ArrayXXd &  p_hypStock, const int & p_isim) const;



    /// \brief  Get  a vector of grids  in the hypercube  with cuts associated to the  grid
    ///         If the grid is the mesh, the function given by the cuts may be not concave
    ///         If the grid gathers some meshes, then the solution is concave on the grid
    /// \param  p_hypStock list of points  defining an hypercube :
    ///          - (i,0)  coordinate corresponds to min value in dimension i
    ///          - (i,1)  coordinate corresponds to max value in dimension i
    ///          .
    /// \param  p_isim   : scenario number
    /// \param  p_bStrict       if true check concavity using hyperplanes values too
    /// \return  a vector of a pair.
    ///  Each element of the pair is composed of a  grid  and  an array of cuts C for points in the grid
    ///  C  shape  :  first dimenson :   number of cuts
    ///               second dimension : nb of points  in the grid  ( number associated to points in GridAdaptBase)
  std::vector< std::pair <std::shared_ptr< GridAdaptBase>, std::shared_ptr<Eigen::ArrayXXd>  > >  getCutsConcGatherASim(const  Eigen::ArrayXXd &  p_hypStock, const int & p_isim, const bool& p_bStrict) const;


    /// \brief Exactely as the previous getCutsConcGatherASim function but for a given point .
    /// \param  p_coordinates   simulation coordinates
    /// \param  p_bStrict       if true check concavity using hyperplanes values too
    /// \return  a vector of a pair.
    ///  Each element of the pair is composed of a  grid  and  an array of cuts C for points in the grid
    ///  C  shape  :  first dimenson :   number of cuts
    ///               second dimension : nb of points  in the grid  ( number associated to points in GridAdaptBase)
   std::vector< std::pair <std::shared_ptr< GridAdaptBase>, std::shared_ptr<Eigen::ArrayXXd>  > >  getCutsConcGatherASim(const  Eigen::ArrayXXd &  p_hypStock, const Eigen::ArrayXd &p_coordinates, const bool & p_bStrict) const;




  
    /// \brief get Regressed values stored
    const Eigen::Array< Eigen::ArrayXXd, Eigen::Dynamic, 1 >   &getValues() const
    {
        return m_regressedCutCoeff;
    }

    /// \brief Get back particles associated to regression
    inline Eigen::ArrayXXd  getParticles() const
    {
        return m_condExp->getParticles();
    }

    //// \brief Get back
    ///@{
    std::shared_ptr< GridAdaptBase > getGrid() const
    {
        return m_grid;
    }
    std::shared_ptr<BaseRegression > getCondExp()  const
    {
        return m_condExp ;
    }

    inline int getNbSimul() const
    {
        return m_condExp->getNbSimul() ;
    }
    ///@}

    
    /// \brief  Get  a vector of grids  in the hypercube  where concavity is not respected
    /// \param  p_hypStock list of points  defining an hypercube :
    ///          - (i,0)  coordinate corresponds to min value in dimension i
    ///          - (i,1)  coordinate corresponds to max value in dimension i
    ///          .
    /// \param  p_isim   : scenario number
    /// \param  p_bStrict : if true check concavity using hyperplanes values too
    /// \return  a vector of a grids where concavity not respected
    ///    
    std::vector<   std::shared_ptr<GridAdaptBase> >  getMeshNotConcASim(const  Eigen::ArrayXXd &  p_hypStock, const int & p_isim, const bool & p_bStrict) const;

    /// \brief get back  VB  and VU for a  given particle (uncertainty) and a stock level
    /// \param p_aParticle  a particule
    /// \param p_stock      stock level    /// \brief Permits to get back VB and VU par a point
    std::pair< double, Eigen::ArrayXd> getVBAndVU(const Eigen::ArrayXd &p_aParticle , const Eigen::ArrayXd & p_stock) const ;
};
}
#endif
