// Copyright (C) 2017 EDF
// All Rights Reserved
// This code is published under the GNU Lesser General Public License (GNU LGPL)
#ifndef GRIDKERNELCONSTRUCTION_H
#define GRIDKERNELCONSTRUCTION_H
#include <vector>
#include <Eigen/Dense>


/// \file gridKernelConstruction.h
/// \brief Perform a grid calculation for the grid kernel method
///

namespace StOpt
{

/// \brief dimGrid
/// Builds the valuation grid where the local regressions will be performed
/// The grid will contain (roughly) p_nbSimul p_q points
/// The grid is rectilinear, of size p_aN[0]*p_aN[1]*...*p_aN[d-1]
/// Each aN[i] is chosen proportional to the singular value p_Sing[i] of the ith dimension
/// If p_Sing[i] is so small that aN[i] < 2, then the ith dimension is ignored
/// The effective dimension after these removals is d_eff
/// The array p_aK defines the shape of the bandwidths
/// It is such that p_aK[0]/p_nbSimul * p_aK[1]/p_nbSimul * ... * p_aK[d-1]/p_nbSimul is (roughly) equal to p_prop
/// The probabilities p_aK[i]/p_nbSimul are chosen proportional to the singular values p_Sing[i]
/// (capped to the maximum probability 1.0)
/// p_aK[i] is therefore the number of points that the bandwidths shall contain in the ith dimension
///
/// The function returns the arrays aN and aK, and the effective dimension d_eff
/// \param p_nbSimul number of simulations
/// \param p_Sing    singular values (dim)
/// \param p_prop    multiplicative coefficient to define the size of bandwidth
/// \param p_q       multiplicative coefficient to define the number of points in the grid,
/// \param d_eff     number of effective dimensions
/// \param p_aK      p_aK[i] is  the number of points that the bandwidths shall contain in the ith dimension
/// \param p_aN      number of grid points in each direction
void dimgrid(const int &p_nbSimul, const Eigen::ArrayXd &p_Sing, const double &p_prop, const double   &p_q, int &p_dEff, Eigen::ArrayXi   &p_aK, Eigen::ArrayXi &p_aN);


/// \brieff preprocess date
/// \param p_X       particles  (renormalized)
/// \param p_sing    singular values of SVD matrixc associated to particles
/// \param p_prop    multiplicative coefficient to define the size of bandwidth
/// \param p_q       multiplicative coefficient to define the number of points in the grid,
/// \param p_aN      number of grid points in each direction
/// \param p_aK      p_aK[i] is  the number of points that the bandwidths shall contain in the ith dimension
/// \param d_eff     number of effective dimensions
void   preprocessData(Eigen::ArrayXXd &p_X, const Eigen::ArrayXd &p_sing, const double &p_prop, const double &p_q, Eigen::ArrayXi &p_aN, Eigen::ArrayXi &p_aK, int &p_dEff);

///\brief Permits to define for each point of p_sx
///       the bandwith h, so that  [p_sx(i)-h(i), p_sx(i)+h(i)]  contains p_k points
/// p_sx   vector of points sorted
/// p_k   number of points
/// return the bandwidth in each direction
Eigen::ArrayXd  KNearestBandwithKPt1D(const Eigen::ArrayXd &p_sx, const int &p_k);

/// \brief Merge p_z-p_h and p_z+p_h in p_g
///        return  p_g, p_zL, p_zR
///        p_zL (p_zR) are indices in p_g corresponding to p_x-p_h  (p_x+p_h)
///        Suppress interval in g containing non particle p_sx
///  \param   p_sx  sorted particles
///  \param   p_z increasing point values
///  \param   p_h bandwith for each point of p_sx
///  \param   p_g merged p_z-p_h and p_z+p_h with suppressed intervals
///  \param   p_zL are indices in p_g corresponding to p_x-p_h
///  \param   p_zR are indices in p_g corresponding to p_x+p_h
void   dataPartition1D(const Eigen::ArrayXd &p_sx, const Eigen::ArrayXd &p_z, const Eigen::ArrayXd &p_h, Eigen::ArrayXd &p_g, Eigen::ArrayXi &p_zl, Eigen::ArrayXi &p_zr);


/// \brief creation partition in all dimension
///
/// \param p_sx     sorted particles in each direction
/// \param p_iSort  index giving in each direction the particle number such that there are sorted p_sx(p_iSort(j,i),i) <= p_sx(p_iSort(j+1,i),i)
/// \param p_aN     in each direction give  the number of points defining the grid
/// \param p_aK     in each direction give the number of particles defining the bandwith
/// \param p_h      int each direction the bandwith for the p_an(id) point mesh
/// \param p_z      int each direction the point grid for the p_an(id) point choosen to define the grid
/// \param p_g      grid poinst +-  the bandwith
/// \param p_zL     in each direction store  for each point z the position in g of the left position of the boundary point associated to z
/// \param p_zR     in each direction store  for each point z the position in g of the right position of the boundary point associated to z
/// \param p_xG     permits to affect each particle to a slice defined by p_g in each direction
void adaptiveBandwithNd(const Eigen::ArrayXXd  &p_sx, const Eigen::ArrayXXi &p_iSort, const Eigen::ArrayXi &p_aN, const Eigen::ArrayXi &p_aK,
                        std::vector< std::shared_ptr<Eigen::ArrayXd> > &p_h,
                        std::vector< std::shared_ptr<Eigen::ArrayXd> > &p_z,
                        std::vector< std::shared_ptr<Eigen::ArrayXi> > &p_zl,
                        std::vector< std::shared_ptr<Eigen::ArrayXi> > &p_zr,
                        std::vector<  std::shared_ptr<Eigen::ArrayXd> > &p_g,
                        Eigen::ArrayXXi &p_xG)  ;
}

#endif /* GRIDKERNELCONSTRUCTION_H */
