// Copyright (C) 2016 EDF
// All Rights Reserved
// This code is published under the GNU Lesser General Public License (GNU LGPL)
#ifndef GRIDANDREGRESSEDVALUE_H
#define GRIDANDREGRESSEDVALUE_H
#include <Eigen/Dense>
#include <memory>
#include "StOpt/regression/BaseRegression.h"
#include "StOpt/core/grids/SpaceGrid.h"
#include "StOpt/core/grids/InterpolatorSpectral.h"
#include "StOpt/core/grids/GridIterator.h"

/** \file GridAndRegressedValue.h
 * \brief Permits to store a function value  defined in dimension \f$n  = p+q \f$
 *    defined partly on a grid (dimension \f$ p \f$)  and partly by regression (dimension \f$ q \f$)
 *    \author Xavier Warin
 */


namespace StOpt
{
/// \class GridAndRegressedValue GridAndRegressedValue.h
/// Permits to store  a function value partly defined on a grid and by regression
class GridAndRegressedValue
{

private :
    std::shared_ptr< SpaceGrid >    m_grid ; ///< grid used to define stock points
    std::shared_ptr< BaseRegression >  m_reg ; ///< regressor
    std::vector< std::shared_ptr<InterpolatorSpectral> > m_interpFuncBasis; /// vector of spectral operator associated to the grid used for each regressed function basis

public :

    /// \brief Default constructor
    GridAndRegressedValue() {}

    /// \brief Constructor
    /// \param p_grid     grid for stocks
    /// \param p_reg      regressor
    /// \param p_values   functions to store  (number of simulations by nb of stocks)
    GridAndRegressedValue(const  std::shared_ptr< SpaceGrid >   &p_grid,
                          const std::shared_ptr< BaseRegression >   &p_reg,
                          const Eigen::ArrayXXd &p_values) : m_grid(p_grid), m_reg(p_reg)
    {
        // coefficients of regressed functions (nb stock points, nb function basis)
        Eigen::ArrayXXd valRegressed = m_reg->getCoordBasisFunctionMultiple(p_values.transpose());
        // create regressor
        m_interpFuncBasis.resize(valRegressed.cols());
        for (int i = 0; i < valRegressed.cols(); ++i)
            m_interpFuncBasis[i] = p_grid->createInterpolatorSpectral(valRegressed.col(i));
    }

    /// \brief Constructor used to store the grid and the regressor
    /// \param p_grid     grid for stocks
    /// \param p_reg      regressor
    GridAndRegressedValue(const  std::shared_ptr< SpaceGrid >   &p_grid,
                          const std::shared_ptr< BaseRegression >   &p_reg) : m_grid(p_grid), m_reg(p_reg) {}

    /// \brief Constructor used to only store the regressor
    /// \param p_reg      regressor
    GridAndRegressedValue(const std::shared_ptr< BaseRegression >   &p_reg) :  m_reg(p_reg) {}

    /// \brief Constructor used for deserialization
    /// \param  p_grid                grid for stocks
    /// \param  p_reg                 regressor
    /// \param  p_interpFuncBasis     spectral interpolator associated to each regression function basis
    GridAndRegressedValue(const  std::shared_ptr< SpaceGrid >   &p_grid,
                          const std::shared_ptr< BaseRegression >   &p_reg,
                          const std::vector< std::shared_ptr<InterpolatorSpectral> > &p_interpFuncBasis):  m_grid(p_grid), m_reg(p_reg), m_interpFuncBasis(p_interpFuncBasis) {}



    /// \brief Get value function for one stock and one simulation
    /// \param  p_ptOfStock     stock points
    /// \param  p_coordinates   simulation coordinates
    inline double getValue(const Eigen::ArrayXd &p_ptOfStock, const Eigen::ArrayXd &p_coordinates) const
    {
        return  m_reg->getAValue(p_coordinates, p_ptOfStock, m_interpFuncBasis);
    }

    /// \brief Permits to get back all regressed values on the grid
    Eigen::ArrayXXd getRegressedValues() const
    {
        if ((!m_grid) || (m_interpFuncBasis.size() == 0))
            return  Eigen::ArrayXXd();
        Eigen::ArrayXXd ret(m_interpFuncBasis.size(), m_grid->getNbPoints());
        std::shared_ptr<GridIterator> gridIterator = m_grid->getGridIterator();
        while (gridIterator->isValid())
        {
            Eigen::ArrayXd  ptOfStock = gridIterator->getCoordinate();
            for (size_t i = 0; i < m_interpFuncBasis.size(); ++i)
                ret(i, gridIterator->getCount()) = m_interpFuncBasis[i]->apply(ptOfStock);
            gridIterator->next();
        }
        return ret;
    }

    /// \brief Permits to create the spectral operator using the regressed values
    /// \param p_regValues   regressed values used for the spectral interpolator.
    void setRegressedValues(const Eigen::ArrayXXd &p_regValues)
    {
        // create regressor
        m_interpFuncBasis.resize(p_regValues.rows());
        for (int i = 0; i < p_regValues.rows(); ++i)
            m_interpFuncBasis[i] = m_grid->createInterpolatorSpectral(p_regValues.row(i).transpose());

    }

    /// \brief Get the grid
    std::shared_ptr< SpaceGrid >  getGrid() const
    {
        return m_grid ;
    }

    /// \brief Get back the regressor
    std::shared_ptr< BaseRegression >  getRegressor() const
    {
        return m_reg;
    }

    /// \brief Get back the interpolators
    const std::vector< std::shared_ptr<InterpolatorSpectral> >   &getInterpolators() const
    {
        return m_interpFuncBasis;
    }

};
}
#endif
