// Copyright (C) 2016 EDF
// All Rights Reserved
// This code is published under the GNU Lesser General Public License (GNU LGPL)
#define BOOST_TEST_MODULE testHierchizationNoBound
#define BOOST_TEST_DYN_LINK
#include <array>
#include <functional>
#include <boost/test/unit_test.hpp>
#include <Eigen/Dense>
#include "StOpt/core/sparse/sparseGridTypes.h"
#include "StOpt/core/sparse/sparseGridCommon.h"
#include "StOpt/core/sparse/GetCoordinateNoBound.h"
#include "StOpt/core/sparse/sparseGridNoBound.h"
#include "StOpt/core/sparse/SparseGridLinNoBound.h"
#include "StOpt/core/sparse/SparseGridQuadNoBound.h"
#include "StOpt/core/sparse/SparseGridCubicNoBound.h"
#include "StOpt/core/sparse/SparseGridHierarOnePointLinNoBound.h"
#include "StOpt/core/sparse/SparseGridHierarOnePointQuadNoBound.h"
#include "StOpt/core/sparse/SparseGridHierarOnePointCubicNoBound.h"

using namespace std;
using namespace Eigen;
using namespace StOpt;


#if defined   __linux
#include <fenv.h>
#define enable_abort_on_floating_point_exception() feenableexcept(FE_DIVBYZERO | FE_INVALID)
#endif


/// For Clang < 3.7 (and above ?) to be compatible GCC 5.1 and above
namespace boost
{
namespace unit_test
{
namespace ut_detail
{
std::string normalize_test_case_name(const_string name)
{
    return (name[0] == '&' ? std::string(name.begin() + 1, name.size() - 1) : std::string(name.begin(), name.size()));
}
}
}
}

double accuracyEqual = 1e-9;
double accuracyEqualFirst  = 1e-7;
double accuracyEqualSecond  = 1e-4;
double accuracyEqualThird  = 1e-1;

/// \class FunctionExp testHierarchizationNoBound.cpp
/// Test Hierarchization on exponential
class FunctionExp
{
public :
    double operator()(const Eigen::ArrayXd &p_x) const
    {
        double ret = 1.;
        for (int id = 0; id < p_x.size(); ++id)
            ret *=  exp(p_x(id));
        return ret;
    }
};

/// \param p_func  function to test
/// \param p_level level $L$ for hierarchization
/// \param p_weight  weight function $\f$W\f$ such that \f$ \sum_{i=1}^{NDIM} W(i) l_i \le  NDIM + L-1 \f$
/// \param p_bSparse true if sparse, otherwise full
/// \param p_precision to check precision for hierarchized coefficients
template<  class Hierar, class Dehierar, class HierarOnePoint >
void testHierachization(const function< double(const ArrayXd &) > &p_func,  const unsigned int    &p_level, const ArrayXd &p_weight, const bool &p_bSparse, const double &p_precision)
{
    // create sparse structure
    SparseSet dataSetSparse;
    size_t iPosition = 0 ;
    if (p_bSparse)
        initialSparseConstructionNoBound(p_level, p_weight, dataSetSparse, iPosition);
    else
        initialFullConstructionNoBound(p_level, p_weight, dataSetSparse, iPosition);
    // to store nodal values
    Eigen::ArrayXd nodalValues(iPosition);
    // nodal values
    for (typename SparseSet::const_iterator iterLevel = dataSetSparse.begin(); iterLevel != dataSetSparse.end(); ++iterLevel)
        for (typename SparseLevel::const_iterator iterPosition = iterLevel->second.begin();	iterPosition != iterLevel->second.end(); ++iterPosition)
        {
            // get coordinate on [0,1]^d
            Eigen::ArrayXd coord = GetCoordinateNoBound()(iterLevel->first, iterPosition->first);
            nodalValues(iterPosition->second) = p_func(coord);
        }

    // hierarchization with linear
    Eigen::ArrayXd hierarValues = nodalValues;
    ExplorationNoBound<  Hierar, double, Eigen::ArrayXd  >(dataSetSparse, p_weight.size(), hierarValues);

    // dehierarchization
    Eigen::ArrayXd nodalValuesBis = hierarValues;
    ExplorationNoBound<  Dehierar, double, Eigen::ArrayXd  >(dataSetSparse, p_weight.size(), nodalValuesBis);

    // check
    for (int i = 0; i < nodalValuesBis.size(); ++i)
    {
        BOOST_CHECK_CLOSE(nodalValuesBis(i), nodalValues(i), accuracyEqual);
    }
    ArrayXd hierarValuesPointByPoint(nodalValues.size());
    // now hierarchize point by point
    for (typename SparseSet::const_iterator iterLevel = dataSetSparse.begin(); iterLevel != dataSetSparse.end(); ++iterLevel)
        for (typename SparseLevel::const_iterator iterPosition = iterLevel->second.begin();	iterPosition != iterLevel->second.end(); ++iterPosition)
        {
            hierarValuesPointByPoint(iterPosition->second) = HierarOnePoint()(iterLevel->first, iterPosition->first, dataSetSparse, nodalValues);
        }
    // check
    for (int i = 0; i < nodalValuesBis.size(); ++i)
    {
        BOOST_CHECK_CLOSE(hierarValues(i), hierarValuesPointByPoint(i), p_precision);
    }
}



BOOST_AUTO_TEST_CASE(test1DLin)
{

    Eigen::ArrayXd weight(1);
    weight(0) = 1.;
    function< double(const Eigen::ArrayXd &) >  f1D =  FunctionExp() ;
    int level = 1 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DLinNoBound, Dehierar1DLinNoBound, SparseGridHierarOnePointLinNoBound<double, ArrayXd> >(f1D, level, weight, bSparse, accuracyEqual);
        bSparse = false ;
        testHierachization< Hierar1DLinNoBound, Dehierar1DLinNoBound, SparseGridHierarOnePointLinNoBound<double, ArrayXd> >(f1D, level, weight, bSparse, accuracyEqual);
    }
    level = 2 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DLinNoBound, Dehierar1DLinNoBound, SparseGridHierarOnePointLinNoBound<double, ArrayXd> >(f1D, level, weight, bSparse, accuracyEqual);
        bSparse = false ;
        testHierachization< Hierar1DLinNoBound, Dehierar1DLinNoBound, SparseGridHierarOnePointLinNoBound<double, ArrayXd> >(f1D, level, weight, bSparse, accuracyEqual);
    }
    level = 3 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DLinNoBound, Dehierar1DLinNoBound, SparseGridHierarOnePointLinNoBound<double, ArrayXd> >(f1D, level, weight, bSparse, accuracyEqual);
        bSparse = false ;
        testHierachization< Hierar1DLinNoBound, Dehierar1DLinNoBound, SparseGridHierarOnePointLinNoBound<double, ArrayXd> >(f1D, level, weight, bSparse, accuracyEqualFirst);
    }
    level = 7 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DLinNoBound, Dehierar1DLinNoBound, SparseGridHierarOnePointLinNoBound<double, ArrayXd> >(f1D, level, weight, bSparse, accuracyEqualSecond);
        bSparse = false ;
        testHierachization< Hierar1DLinNoBound, Dehierar1DLinNoBound, SparseGridHierarOnePointLinNoBound<double, ArrayXd> >(f1D, level, weight, bSparse, accuracyEqualSecond);
    }
}

BOOST_AUTO_TEST_CASE(test2DLin)
{

    Eigen::ArrayXd weight(2);
    weight << 1, 1 ;
    function< double(const ArrayXd &) >  f2D =  FunctionExp() ;
    int level = 1 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DLinNoBound, Dehierar1DLinNoBound, SparseGridHierarOnePointLinNoBound<double, ArrayXd>>(f2D, level, weight, bSparse, accuracyEqual);
        bSparse = false ;
        testHierachization< Hierar1DLinNoBound, Dehierar1DLinNoBound, SparseGridHierarOnePointLinNoBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqual);
    }
    level = 2 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DLinNoBound, Dehierar1DLinNoBound, SparseGridHierarOnePointLinNoBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqual);
        bSparse = false ;
        testHierachization< Hierar1DLinNoBound, Dehierar1DLinNoBound, SparseGridHierarOnePointLinNoBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqual);
    }
    level = 3 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DLinNoBound, Dehierar1DLinNoBound, SparseGridHierarOnePointLinNoBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqual);
        bSparse = false ;
        testHierachization< Hierar1DLinNoBound, Dehierar1DLinNoBound, SparseGridHierarOnePointLinNoBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqualFirst);
    }
    level = 7 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DLinNoBound, Dehierar1DLinNoBound, SparseGridHierarOnePointLinNoBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqualFirst);
        bSparse = false ;
        testHierachization< Hierar1DLinNoBound, Dehierar1DLinNoBound, SparseGridHierarOnePointLinNoBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqualSecond);
    }
}

BOOST_AUTO_TEST_CASE(test2DAnisotropicLin)
{

    Eigen::ArrayXd weight(2);
    weight << 1., 2. ;
    function< double(const ArrayXd &) >  f2D =  FunctionExp() ;
    int level = 1 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DLinNoBound, Dehierar1DLinNoBound, SparseGridHierarOnePointLinNoBound<double, ArrayXd>  >(f2D, level, weight, bSparse, accuracyEqual);
        bSparse = false ;
        testHierachization< Hierar1DLinNoBound, Dehierar1DLinNoBound, SparseGridHierarOnePointLinNoBound<double, ArrayXd>  >(f2D, level, weight, bSparse, accuracyEqual);
    }
    level = 2 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DLinNoBound, Dehierar1DLinNoBound, SparseGridHierarOnePointLinNoBound<double, ArrayXd>  >(f2D, level, weight, bSparse, accuracyEqual);
        bSparse = false ;
        testHierachization< Hierar1DLinNoBound, Dehierar1DLinNoBound, SparseGridHierarOnePointLinNoBound<double, ArrayXd>  >(f2D, level, weight, bSparse, accuracyEqual);
    }
    level = 3 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DLinNoBound, Dehierar1DLinNoBound, SparseGridHierarOnePointLinNoBound<double, ArrayXd>  >(f2D, level, weight, bSparse, accuracyEqual);
        bSparse = false ;
        testHierachization< Hierar1DLinNoBound, Dehierar1DLinNoBound, SparseGridHierarOnePointLinNoBound<double, ArrayXd>  >(f2D, level, weight, bSparse, accuracyEqualFirst);
    }
    level = 7 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DLinNoBound, Dehierar1DLinNoBound, SparseGridHierarOnePointLinNoBound<double, ArrayXd>  >(f2D, level, weight, bSparse, accuracyEqualFirst);
        bSparse = false ;
        testHierachization< Hierar1DLinNoBound, Dehierar1DLinNoBound, SparseGridHierarOnePointLinNoBound<double, ArrayXd>  >(f2D, level, weight, bSparse, accuracyEqualSecond);
    }
}


BOOST_AUTO_TEST_CASE(test3DLin)
{

    Eigen::ArrayXd weight(3);
    weight << 1., 1., 1 ;
    function< double(const ArrayXd &) >  f3D =  FunctionExp() ;
    int level = 1 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DLinNoBound, Dehierar1DLinNoBound, SparseGridHierarOnePointLinNoBound<double, ArrayXd> >(f3D, level, weight, bSparse, accuracyEqual);
    }
    level = 2 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DLinNoBound, Dehierar1DLinNoBound, SparseGridHierarOnePointLinNoBound<double, ArrayXd> >(f3D, level, weight, bSparse, accuracyEqual);
    }
    level = 3 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DLinNoBound, Dehierar1DLinNoBound, SparseGridHierarOnePointLinNoBound<double, ArrayXd>>(f3D, level, weight, bSparse, accuracyEqual);
    }
    level = 7 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DLinNoBound, Dehierar1DLinNoBound, SparseGridHierarOnePointLinNoBound<double, ArrayXd> >(f3D, level, weight, bSparse, accuracyEqualFirst);
    }
}

BOOST_AUTO_TEST_CASE(test5DLin)
{

    Eigen::ArrayXd weight(5);
    weight << 1., 1., 1., 1., 1. ;
    function< double(const ArrayXd &) >  f5D =  FunctionExp() ;
    int level = 1 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DLinNoBound, Dehierar1DLinNoBound, SparseGridHierarOnePointLinNoBound<double, ArrayXd>>(f5D, level, weight, bSparse, accuracyEqual);
    }
    level = 2 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DLinNoBound, Dehierar1DLinNoBound, SparseGridHierarOnePointLinNoBound<double, ArrayXd> >(f5D, level, weight, bSparse, accuracyEqual);
    }
    level = 3 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DLinNoBound, Dehierar1DLinNoBound, SparseGridHierarOnePointLinNoBound<double, ArrayXd>>(f5D, level, weight, bSparse, accuracyEqualFirst);
    }
    level = 7 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DLinNoBound, Dehierar1DLinNoBound, SparseGridHierarOnePointLinNoBound<double, ArrayXd> >(f5D, level, weight, bSparse, accuracyEqualFirst);
    }
}


BOOST_AUTO_TEST_CASE(test1DQuad)
{

    Eigen::ArrayXd weight(1);
    weight << 1 ;
    function< double(const ArrayXd &) >  f1D =  FunctionExp() ;
    int level = 1 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DQuadNoBound, Dehierar1DQuadNoBound, SparseGridHierarOnePointQuadNoBound<double, ArrayXd>  >(f1D, level, weight, bSparse, accuracyEqual);
        bSparse = false ;
        testHierachization< Hierar1DQuadNoBound, Dehierar1DQuadNoBound, SparseGridHierarOnePointQuadNoBound<double, ArrayXd>  >(f1D, level, weight, bSparse, accuracyEqual);
    }
    level = 2 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DQuadNoBound, Dehierar1DQuadNoBound, SparseGridHierarOnePointQuadNoBound<double, ArrayXd>  >(f1D, level, weight, bSparse, accuracyEqual);
        bSparse = false ;
        testHierachization< Hierar1DQuadNoBound, Dehierar1DQuadNoBound, SparseGridHierarOnePointQuadNoBound<double, ArrayXd>  >(f1D, level, weight, bSparse, accuracyEqual);
    }
    level = 3 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DQuadNoBound, Dehierar1DQuadNoBound, SparseGridHierarOnePointQuadNoBound<double, ArrayXd>  >(f1D, level, weight, bSparse, accuracyEqual);
        bSparse = false ;
        testHierachization< Hierar1DQuadNoBound, Dehierar1DQuadNoBound, SparseGridHierarOnePointQuadNoBound<double, ArrayXd>  >(f1D, level, weight, bSparse, accuracyEqual);
    }
    level = 7 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DQuadNoBound, Dehierar1DQuadNoBound, SparseGridHierarOnePointQuadNoBound<double, ArrayXd>  >(f1D, level, weight, bSparse, accuracyEqualFirst);
        bSparse = false ;
        testHierachization< Hierar1DQuadNoBound, Dehierar1DQuadNoBound, SparseGridHierarOnePointQuadNoBound<double, ArrayXd>  >(f1D, level, weight, bSparse, accuracyEqualFirst);
    }
}

BOOST_AUTO_TEST_CASE(test2DQuad)
{

    Eigen::ArrayXd weight(2);
    weight << 1, 1 ;
    function< double(const ArrayXd &) >  f2D =  FunctionExp() ;
    int level = 1 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DQuadNoBound, Dehierar1DQuadNoBound, SparseGridHierarOnePointQuadNoBound<double, ArrayXd>>(f2D, level, weight, bSparse, accuracyEqual);
        bSparse = false ;
        testHierachization< Hierar1DQuadNoBound, Dehierar1DQuadNoBound, SparseGridHierarOnePointQuadNoBound<double, ArrayXd>>(f2D, level, weight, bSparse, accuracyEqual);
    }
    level = 2 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DQuadNoBound, Dehierar1DQuadNoBound, SparseGridHierarOnePointQuadNoBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqual);
        bSparse = false ;
        testHierachization< Hierar1DQuadNoBound, Dehierar1DQuadNoBound, SparseGridHierarOnePointQuadNoBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqual);
    }
    level = 3 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DQuadNoBound, Dehierar1DQuadNoBound, SparseGridHierarOnePointQuadNoBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqual);
        bSparse = false ;
        testHierachization< Hierar1DQuadNoBound, Dehierar1DQuadNoBound, SparseGridHierarOnePointQuadNoBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqual);
    }
    level = 5 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DQuadNoBound, Dehierar1DQuadNoBound, SparseGridHierarOnePointQuadNoBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqualSecond);
        bSparse = false ;
        testHierachization< Hierar1DQuadNoBound, Dehierar1DQuadNoBound, SparseGridHierarOnePointQuadNoBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqualSecond);
    }
}

BOOST_AUTO_TEST_CASE(test2DAnisotropicQuad)
{

    Eigen::ArrayXd weight(2);
    weight << 1, 2 ;
    function< double(const ArrayXd &) >  f2D =  FunctionExp() ;
    int level = 1 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DQuadNoBound, Dehierar1DQuadNoBound, SparseGridHierarOnePointQuadNoBound<double, ArrayXd>>(f2D, level, weight, bSparse, accuracyEqual);
        bSparse = false ;
        testHierachization< Hierar1DQuadNoBound, Dehierar1DQuadNoBound, SparseGridHierarOnePointQuadNoBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqual);
    }
    level = 2 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DQuadNoBound, Dehierar1DQuadNoBound, SparseGridHierarOnePointQuadNoBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqual);
        bSparse = false ;
        testHierachization< Hierar1DQuadNoBound, Dehierar1DQuadNoBound, SparseGridHierarOnePointQuadNoBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqual);
    }
    level = 3 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DQuadNoBound, Dehierar1DQuadNoBound, SparseGridHierarOnePointQuadNoBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqual);
        bSparse = false ;
        testHierachization< Hierar1DQuadNoBound, Dehierar1DQuadNoBound, SparseGridHierarOnePointQuadNoBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqual);
    }
    level = 7 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DQuadNoBound, Dehierar1DQuadNoBound, SparseGridHierarOnePointQuadNoBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqualSecond);
        bSparse = false ;
        testHierachization< Hierar1DQuadNoBound, Dehierar1DQuadNoBound, SparseGridHierarOnePointQuadNoBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqualSecond);
    }
}


BOOST_AUTO_TEST_CASE(test3DQuad)
{
    Eigen::ArrayXd weight(3);
    weight << 1., 1., 1 ;
    function< double(const ArrayXd &) >  f3D =  FunctionExp() ;
    int level = 1 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DQuadNoBound, Dehierar1DQuadNoBound, SparseGridHierarOnePointQuadNoBound<double, ArrayXd> >(f3D, level, weight, bSparse, accuracyEqual);
    }
    level = 2 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DQuadNoBound, Dehierar1DQuadNoBound, SparseGridHierarOnePointQuadNoBound<double, ArrayXd> >(f3D, level, weight, bSparse, accuracyEqual);
    }
    level = 3 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DQuadNoBound, Dehierar1DQuadNoBound, SparseGridHierarOnePointQuadNoBound<double, ArrayXd> >(f3D, level, weight, bSparse, accuracyEqual);
    }
    level = 7 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DQuadNoBound, Dehierar1DQuadNoBound, SparseGridHierarOnePointQuadNoBound<double, ArrayXd> >(f3D, level, weight, bSparse, accuracyEqualSecond);
    }
}

BOOST_AUTO_TEST_CASE(test5DQuad)
{

    Eigen::ArrayXd weight(5);
    weight << 1., 1., 1., 1., 1. ;
    function< double(const ArrayXd &) >  f5D =  FunctionExp() ;
    int level = 1 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DQuadNoBound, Dehierar1DQuadNoBound, SparseGridHierarOnePointQuadNoBound<double, ArrayXd> >(f5D, level, weight, bSparse, accuracyEqual);
    }
    level = 2 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DQuadNoBound, Dehierar1DQuadNoBound, SparseGridHierarOnePointQuadNoBound<double, ArrayXd> >(f5D, level, weight, bSparse, accuracyEqual);
    }
    level = 3 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DQuadNoBound, Dehierar1DQuadNoBound, SparseGridHierarOnePointQuadNoBound<double, ArrayXd> >(f5D, level, weight, bSparse, accuracyEqual);
    }
    level = 7 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DQuadNoBound, Dehierar1DQuadNoBound, SparseGridHierarOnePointQuadNoBound<double, ArrayXd> >(f5D, level, weight, bSparse, accuracyEqualSecond);
    }
}


BOOST_AUTO_TEST_CASE(test1DCubic)
{

    Eigen::ArrayXd weight(1);
    weight << 1  ;
    function< double(const ArrayXd &) >  f1D =  FunctionExp() ;
    int level = 1 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DCubicNoBound, Dehierar1DCubicNoBound, SparseGridHierarOnePointCubicNoBound<double, ArrayXd> >(f1D, level, weight, bSparse, accuracyEqual);
        bSparse = false ;
        testHierachization< Hierar1DCubicNoBound, Dehierar1DCubicNoBound, SparseGridHierarOnePointCubicNoBound<double, ArrayXd> >(f1D, level, weight, bSparse, accuracyEqual);
    }
    level = 2 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DCubicNoBound, Dehierar1DCubicNoBound, SparseGridHierarOnePointCubicNoBound<double, ArrayXd> >(f1D, level, weight, bSparse, accuracyEqual);
        bSparse = false ;
        testHierachization< Hierar1DCubicNoBound, Dehierar1DCubicNoBound, SparseGridHierarOnePointCubicNoBound<double, ArrayXd> >(f1D, level, weight, bSparse, accuracyEqual);
    }
    level = 3 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DCubicNoBound, Dehierar1DCubicNoBound, SparseGridHierarOnePointCubicNoBound<double, ArrayXd> >(f1D, level, weight, bSparse, accuracyEqual);
        bSparse = false ;
        testHierachization< Hierar1DCubicNoBound, Dehierar1DCubicNoBound, SparseGridHierarOnePointCubicNoBound<double, ArrayXd>  >(f1D, level, weight, bSparse, accuracyEqual);
    }
    level = 4 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DCubicNoBound, Dehierar1DCubicNoBound, SparseGridHierarOnePointCubicNoBound<double, ArrayXd>>(f1D, level, weight, bSparse, accuracyEqualSecond);
        bSparse = false ;
        testHierachization< Hierar1DCubicNoBound, Dehierar1DCubicNoBound, SparseGridHierarOnePointCubicNoBound<double, ArrayXd>  >(f1D, level, weight, bSparse, accuracyEqualSecond);
    }
    level = 5 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DCubicNoBound, Dehierar1DCubicNoBound, SparseGridHierarOnePointCubicNoBound<double, ArrayXd>>(f1D, level, weight, bSparse, accuracyEqualSecond);
        // bSparse = false ;
        testHierachization< Hierar1DCubicNoBound, Dehierar1DCubicNoBound, SparseGridHierarOnePointCubicNoBound<double, ArrayXd>  >(f1D, level, weight, bSparse, accuracyEqualSecond);
    }
    level = 7 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DCubicNoBound, Dehierar1DCubicNoBound, SparseGridHierarOnePointCubicNoBound<double, ArrayXd>>(f1D, level, weight, bSparse, accuracyEqualSecond);
        bSparse = false ;
        testHierachization< Hierar1DCubicNoBound, Dehierar1DCubicNoBound, SparseGridHierarOnePointCubicNoBound<double, ArrayXd>  >(f1D, level, weight, bSparse, accuracyEqualSecond);
    }
}

BOOST_AUTO_TEST_CASE(test2DCubic)
{

    Eigen::ArrayXd weight(2);
    weight << 1., 1.  ;
    function< double(const ArrayXd &) >  f2D =  FunctionExp() ;
    int level = 1 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DCubicNoBound, Dehierar1DCubicNoBound, SparseGridHierarOnePointCubicNoBound<double, ArrayXd>  >(f2D, level, weight, bSparse, accuracyEqual);
        bSparse = false ;
        testHierachization< Hierar1DCubicNoBound, Dehierar1DCubicNoBound, SparseGridHierarOnePointCubicNoBound<double, ArrayXd>  >(f2D, level, weight, bSparse, accuracyEqual);
    }
    level = 2 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DCubicNoBound, Dehierar1DCubicNoBound, SparseGridHierarOnePointCubicNoBound<double, ArrayXd>  >(f2D, level, weight, bSparse, accuracyEqual);
        bSparse = false ;
        testHierachization< Hierar1DCubicNoBound, Dehierar1DCubicNoBound, SparseGridHierarOnePointCubicNoBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqual);
    }
    level = 3 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DCubicNoBound, Dehierar1DCubicNoBound, SparseGridHierarOnePointCubicNoBound<double, ArrayXd>  >(f2D, level, weight, bSparse, accuracyEqual);
        bSparse = false ;
        testHierachization< Hierar1DCubicNoBound, Dehierar1DCubicNoBound, SparseGridHierarOnePointCubicNoBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqual);
    }
    level = 7 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DCubicNoBound, Dehierar1DCubicNoBound, SparseGridHierarOnePointCubicNoBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqualSecond);
    }
}

BOOST_AUTO_TEST_CASE(test2DAnisotropicCubic)
{

    Eigen::ArrayXd weight(2);
    weight << 1., 2.  ;
    function< double(const ArrayXd &) >  f2D =  FunctionExp() ;
    int level = 1 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DCubicNoBound, Dehierar1DCubicNoBound, SparseGridHierarOnePointCubicNoBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqual);
        bSparse = false ;
        testHierachization< Hierar1DCubicNoBound, Dehierar1DCubicNoBound, SparseGridHierarOnePointCubicNoBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqual);
    }
    level = 2 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DCubicNoBound, Dehierar1DCubicNoBound, SparseGridHierarOnePointCubicNoBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqual);
        bSparse = false ;
        testHierachization< Hierar1DCubicNoBound, Dehierar1DCubicNoBound, SparseGridHierarOnePointCubicNoBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqual);
    }
    level = 3 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DCubicNoBound, Dehierar1DCubicNoBound, SparseGridHierarOnePointCubicNoBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqual);
        bSparse = false ;
        testHierachization< Hierar1DCubicNoBound, Dehierar1DCubicNoBound, SparseGridHierarOnePointCubicNoBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqual);
    }
    level = 5 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DCubicNoBound, Dehierar1DCubicNoBound, SparseGridHierarOnePointCubicNoBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqual);
        bSparse = false ;
        testHierachization< Hierar1DCubicNoBound, Dehierar1DCubicNoBound, SparseGridHierarOnePointCubicNoBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqual);
    }
}


BOOST_AUTO_TEST_CASE(test3DCubic)
{

    Eigen::ArrayXd weight(3);
    weight << 1., 1., 1.  ;
    function< double(const ArrayXd &) >  f3D =  FunctionExp() ;
    int level = 1 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DCubicNoBound, Dehierar1DCubicNoBound, SparseGridHierarOnePointCubicNoBound<double, ArrayXd> >(f3D, level, weight, bSparse, accuracyEqual);
    }
    level = 2 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DCubicNoBound, Dehierar1DCubicNoBound, SparseGridHierarOnePointCubicNoBound<double, ArrayXd> >(f3D, level, weight, bSparse, accuracyEqual);
    }
    level = 3 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DCubicNoBound, Dehierar1DCubicNoBound, SparseGridHierarOnePointCubicNoBound<double, ArrayXd> >(f3D, level, weight, bSparse, accuracyEqual);
    }
    level = 5 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DCubicNoBound, Dehierar1DCubicNoBound, SparseGridHierarOnePointCubicNoBound<double, ArrayXd> >(f3D, level, weight, bSparse, accuracyEqual);
    }
}

BOOST_AUTO_TEST_CASE(test5DCubic)
{

    Eigen::ArrayXd weight(5);
    weight << 1., 1., 1., 1., 1.  ;
    function< double(const ArrayXd &) >  f5D =  FunctionExp() ;
    int level = 1 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DCubicNoBound, Dehierar1DCubicNoBound, SparseGridHierarOnePointCubicNoBound<double, ArrayXd> >(f5D, level, weight, bSparse, accuracyEqual);
    }
    level = 2 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DCubicNoBound, Dehierar1DCubicNoBound, SparseGridHierarOnePointCubicNoBound<double, ArrayXd> >(f5D, level, weight, bSparse, accuracyEqual);
    }
    level = 3 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DCubicNoBound, Dehierar1DCubicNoBound, SparseGridHierarOnePointCubicNoBound<double, ArrayXd> >(f5D, level, weight, bSparse, accuracyEqual);
    }
    level = 7 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DCubicNoBound, Dehierar1DCubicNoBound, SparseGridHierarOnePointCubicNoBound<double, ArrayXd> >(f5D, level, weight, bSparse, accuracyEqualSecond);
    }
}


/// \param p_func  function to test
/// \param p_level level $L$ for hierarchization
/// \param p_weight  weight function $\f$W\f$ such that \f$ \sum_{i=1}^{NDIM} W(i) l_i \le  NDIM + L-1 \f$
/// \param p_bSparse true if sparse, otherwise full
template<  class Hierar, class Dehierar >
void testHierachizationBis(const function< double(const ArrayXd &) > &p_func,  const unsigned int    &p_level, const ArrayXd &p_weight, const bool &p_bSparse)
{
    // create sparse structure
    SparseSet dataSetSparse;
    size_t iPosition = 0 ;
    if (p_bSparse)
        initialSparseConstructionNoBound(p_level, p_weight, dataSetSparse, iPosition);
    else
        initialFullConstructionNoBound(p_level, p_weight, dataSetSparse, iPosition);
    // to store nodal values
    Eigen::ArrayXXd nodalValues(4, iPosition);
    // nodal values
    for (typename SparseSet::const_iterator iterLevel = dataSetSparse.begin(); iterLevel != dataSetSparse.end(); ++iterLevel)
        for (typename SparseLevel::const_iterator iterPosition = iterLevel->second.begin();	iterPosition != iterLevel->second.end(); ++iterPosition)
        {
            // get coordinate on [0,1]^d
            Eigen::ArrayXd coord = GetCoordinateNoBound()(iterLevel->first, iterPosition->first);
            nodalValues.col(iterPosition->second).setConstant(p_func(coord));
        }

    // hierarchization with linear
    Eigen::ArrayXXd hierarValues = nodalValues;
    ExplorationNoBound<  Hierar, Eigen::ArrayXd, Eigen::ArrayXXd  >(dataSetSparse, p_weight.size(), hierarValues);

    // dehierarchization
    Eigen::ArrayXXd nodalValuesBis = hierarValues;
    ExplorationNoBound<  Dehierar, Eigen::ArrayXd, Eigen::ArrayXXd  >(dataSetSparse, p_weight.size(), nodalValuesBis);

    // check
    for (int i = 0; i < nodalValuesBis.cols(); ++i)
    {
        BOOST_CHECK_CLOSE(nodalValuesBis(0, i), nodalValues(0, i), accuracyEqual);
        BOOST_CHECK_CLOSE(nodalValuesBis(3, i), nodalValues(3, i), accuracyEqual);
    }

}



BOOST_AUTO_TEST_CASE(test5DLinMultiple)
{

    Eigen::ArrayXd weight(5);
    weight << 1., 1., 1., 1., 1. ;
    function< double(const ArrayXd &) >  f5D =  FunctionExp() ;
    int level = 1 ;
    {
        // sparse
        bool bSparse = true;
        testHierachizationBis< Hierar1DLinNoBound, Dehierar1DLinNoBound >(f5D, level, weight, bSparse);
    }
    level = 2 ;
    {
        // sparse
        bool bSparse = true;
        testHierachizationBis< Hierar1DLinNoBound, Dehierar1DLinNoBound >(f5D, level, weight, bSparse);
    }
    level = 3 ;
    {
        // sparse
        bool bSparse = true;
        testHierachizationBis< Hierar1DLinNoBound, Dehierar1DLinNoBound >(f5D, level, weight, bSparse);
    }
    level = 7 ;
    {
        // sparse
        bool bSparse = true;
        testHierachizationBis< Hierar1DLinNoBound, Dehierar1DLinNoBound >(f5D, level, weight, bSparse);
    }
}

BOOST_AUTO_TEST_CASE(test5DQuadMultiple)
{

    Eigen::ArrayXd weight(5);
    weight << 1., 1., 1., 1., 1. ;
    function< double(const ArrayXd &) >  f5D =  FunctionExp() ;
    int level = 1 ;
    {
        // sparse
        bool bSparse = true;
        testHierachizationBis< Hierar1DQuadNoBound, Dehierar1DQuadNoBound >(f5D, level, weight, bSparse);
    }
    level = 2 ;
    {
        // sparse
        bool bSparse = true;
        testHierachizationBis< Hierar1DQuadNoBound, Dehierar1DQuadNoBound >(f5D, level, weight, bSparse);
    }
    level = 3 ;
    {
        // sparse
        bool bSparse = true;
        testHierachizationBis< Hierar1DQuadNoBound, Dehierar1DQuadNoBound >(f5D, level, weight, bSparse);
    }
    level = 7 ;
    {
        // sparse
        bool bSparse = true;
        testHierachizationBis< Hierar1DQuadNoBound, Dehierar1DQuadNoBound >(f5D, level, weight, bSparse);
    }
}


BOOST_AUTO_TEST_CASE(test5DCubicMultiple)
{

    Eigen::ArrayXd weight(5);
    weight << 1., 1., 1., 1., 1.  ;
    function< double(const ArrayXd &) >  f5D =  FunctionExp() ;
    int level = 1 ;
    {
        // sparse
        bool bSparse = true;
        testHierachizationBis< Hierar1DCubicNoBound, Dehierar1DCubicNoBound >(f5D, level, weight, bSparse);
    }
    level = 2 ;
    {
        // sparse
        bool bSparse = true;
        testHierachizationBis< Hierar1DCubicNoBound, Dehierar1DCubicNoBound >(f5D, level, weight, bSparse);
    }
    level = 3 ;
    {
        // sparse
        bool bSparse = true;
        testHierachizationBis< Hierar1DCubicNoBound, Dehierar1DCubicNoBound >(f5D, level, weight, bSparse);
    }
    level = 7 ;
    {
        // sparse
        bool bSparse = true;
        testHierachizationBis< Hierar1DCubicNoBound, Dehierar1DCubicNoBound >(f5D, level, weight, bSparse);
    }
}

