// Copyright (C) 2016 EDF
// All Rights Reserved
// This code is published under the GNU Lesser General Public License (GNU LGPL)
#define BOOST_TEST_MODULE testHierchizationBound
#define BOOST_TEST_DYN_LINK
#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/GetCoordinateBound.h"
#include "StOpt/core/sparse/sparseGridBound.h"
#include "StOpt/core/sparse/SparseGridLinBound.h"
#include "StOpt/core/sparse/SparseGridQuadBound.h"
#include "StOpt/core/sparse/SparseGridCubicBound.h"
#include "StOpt/core/sparse/SparseGridHierarOnePointLinBound.h"
#include "StOpt/core/sparse/SparseGridHierarOnePointQuadBound.h"
#include "StOpt/core/sparse/SparseGridHierarOnePointCubicBound.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-3;

/// \class FunctionExp testHierarchizationBound.cpp
/// Test Hierarchization on exponential
class FunctionExp
{
public :
    double operator()(const 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 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)
        initialSparseConstructionBound(p_level, p_weight, dataSetSparse, iPosition);
    else
        initialFullConstructionBound(p_level, p_weight, dataSetSparse, iPosition);
    // to store nodal values
    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
            ArrayXd coord = GetCoordinateBound()(iterLevel->first, iterPosition->first);
            nodalValues(iterPosition->second) = p_func(coord);
        }

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

    // dehierarchization
    ArrayXd nodalValuesBis = hierarValues;
    ExplorationBound< Dehierar, double, 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)
{

    ArrayXd weight(1);
    weight(0) = 1.;
    function< double(const ArrayXd &) >  f1D =  FunctionExp() ;
    int level = 1 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DLinBound, Dehierar1DLinBound, SparseGridHierarOnePointLinBound<double, ArrayXd> >(f1D, level, weight, bSparse, accuracyEqualFirst);
        bSparse = false ;
        testHierachization< Hierar1DLinBound, Dehierar1DLinBound, SparseGridHierarOnePointLinBound<double, ArrayXd> >(f1D, level, weight, bSparse, accuracyEqualFirst);
    }
    level = 2 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DLinBound, Dehierar1DLinBound, SparseGridHierarOnePointLinBound<double, ArrayXd> >(f1D, level, weight, bSparse, accuracyEqualFirst);
        bSparse = false ;
        testHierachization< Hierar1DLinBound, Dehierar1DLinBound, SparseGridHierarOnePointLinBound<double, ArrayXd> >(f1D, level, weight, bSparse, accuracyEqualFirst);
    }
    level = 3 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DLinBound, Dehierar1DLinBound, SparseGridHierarOnePointLinBound<double, ArrayXd> >(f1D, level, weight, bSparse, accuracyEqualFirst);
        bSparse = false ;
        testHierachization< Hierar1DLinBound, Dehierar1DLinBound, SparseGridHierarOnePointLinBound<double, ArrayXd> >(f1D, level, weight, bSparse, accuracyEqualFirst);
    }
    level = 7 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DLinBound, Dehierar1DLinBound, SparseGridHierarOnePointLinBound<double, ArrayXd> >(f1D, level, weight, bSparse, accuracyEqualFirst);
        bSparse = false ;
        testHierachization< Hierar1DLinBound, Dehierar1DLinBound, SparseGridHierarOnePointLinBound<double, ArrayXd>  >(f1D, level, weight, bSparse, accuracyEqualFirst);
    }
}

BOOST_AUTO_TEST_CASE(test2DLin)
{

    ArrayXd weight(2);
    weight << 1, 1 ;
    function< double(const ArrayXd &) >  f2D =  FunctionExp() ;
    int level = 1 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DLinBound, Dehierar1DLinBound, SparseGridHierarOnePointLinBound<double, ArrayXd>  >(f2D, level, weight, bSparse, accuracyEqualFirst);
        bSparse = false ;
        testHierachization< Hierar1DLinBound, Dehierar1DLinBound, SparseGridHierarOnePointLinBound<double, ArrayXd>  >(f2D, level, weight, bSparse, accuracyEqualFirst);
    }
    level = 2 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DLinBound, Dehierar1DLinBound, SparseGridHierarOnePointLinBound<double, ArrayXd>  >(f2D, level, weight, bSparse, accuracyEqualFirst);
        bSparse = false ;
        testHierachization< Hierar1DLinBound, Dehierar1DLinBound, SparseGridHierarOnePointLinBound<double, ArrayXd>  >(f2D, level, weight, bSparse, accuracyEqualFirst);
    }
    level = 3 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DLinBound, Dehierar1DLinBound, SparseGridHierarOnePointLinBound<double, ArrayXd>  >(f2D, level, weight, bSparse, accuracyEqualFirst);
        bSparse = false ;
        testHierachization< Hierar1DLinBound, Dehierar1DLinBound, SparseGridHierarOnePointLinBound<double, ArrayXd>  >(f2D, level, weight, bSparse, accuracyEqualFirst);
    }
    level = 7 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DLinBound, Dehierar1DLinBound, SparseGridHierarOnePointLinBound<double, ArrayXd>  >(f2D, level, weight, bSparse, accuracyEqualFirst);
        bSparse = false ;
        testHierachization< Hierar1DLinBound, Dehierar1DLinBound, SparseGridHierarOnePointLinBound<double, ArrayXd>  >(f2D, level, weight, bSparse, accuracyEqualFirst);
    }
}

BOOST_AUTO_TEST_CASE(test2DAnisotropicLin)
{

    ArrayXd weight(2);
    weight << 1., 2. ;
    function< double(const ArrayXd &) >  f2D =  FunctionExp() ;
    int level = 1 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DLinBound, Dehierar1DLinBound, SparseGridHierarOnePointLinBound<double, ArrayXd>  >(f2D, level, weight, bSparse, accuracyEqualFirst);
        bSparse = false ;
        testHierachization< Hierar1DLinBound, Dehierar1DLinBound, SparseGridHierarOnePointLinBound<double, ArrayXd>  >(f2D, level, weight, bSparse, accuracyEqualFirst);
    }
    level = 2 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DLinBound, Dehierar1DLinBound, SparseGridHierarOnePointLinBound<double, ArrayXd>  >(f2D, level, weight, bSparse, accuracyEqualFirst);
        bSparse = false ;
        testHierachization< Hierar1DLinBound, Dehierar1DLinBound, SparseGridHierarOnePointLinBound<double, ArrayXd>  >(f2D, level, weight, bSparse, accuracyEqualFirst);
    }
    level = 3 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DLinBound, Dehierar1DLinBound, SparseGridHierarOnePointLinBound<double, ArrayXd>  >(f2D, level, weight, bSparse, accuracyEqualFirst);
        bSparse = false ;
        testHierachization< Hierar1DLinBound, Dehierar1DLinBound, SparseGridHierarOnePointLinBound<double, ArrayXd>  >(f2D, level, weight, bSparse, accuracyEqualFirst);
    }
    level = 7 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DLinBound, Dehierar1DLinBound, SparseGridHierarOnePointLinBound<double, ArrayXd>  >(f2D, level, weight, bSparse, accuracyEqualFirst);
        bSparse = false ;
        testHierachization< Hierar1DLinBound, Dehierar1DLinBound, SparseGridHierarOnePointLinBound<double, ArrayXd>  >(f2D, level, weight, bSparse, accuracyEqualFirst);
    }
}


BOOST_AUTO_TEST_CASE(test3DLin)
{

    ArrayXd weight(3);
    weight << 1., 1., 1 ;
    function< double(const ArrayXd &) >  f3D =  FunctionExp() ;
    int level = 1 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DLinBound, Dehierar1DLinBound, SparseGridHierarOnePointLinBound<double, ArrayXd>  >(f3D, level, weight, bSparse, accuracyEqualFirst);
    }
    level = 2 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DLinBound, Dehierar1DLinBound, SparseGridHierarOnePointLinBound<double, ArrayXd>  >(f3D, level, weight, bSparse, accuracyEqualFirst);
    }
    level = 3 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DLinBound, Dehierar1DLinBound, SparseGridHierarOnePointLinBound<double, ArrayXd>  >(f3D, level, weight, bSparse, accuracyEqualFirst);
    }
    level = 7 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DLinBound, Dehierar1DLinBound, SparseGridHierarOnePointLinBound<double, ArrayXd>  >(f3D, level, weight, bSparse, accuracyEqualFirst);
    }
}

BOOST_AUTO_TEST_CASE(test5DLin)
{

    ArrayXd weight(5);
    weight << 1., 1., 1., 1., 1. ;
    function< double(const ArrayXd &) >  f5D =  FunctionExp() ;
    int level = 1 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DLinBound, Dehierar1DLinBound, SparseGridHierarOnePointLinBound<double, ArrayXd>  >(f5D, level, weight, bSparse, accuracyEqualFirst);
    }
    level = 2 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DLinBound, Dehierar1DLinBound, SparseGridHierarOnePointLinBound<double, ArrayXd>  >(f5D, level, weight, bSparse, accuracyEqualFirst);
    }
    level = 3 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DLinBound, Dehierar1DLinBound, SparseGridHierarOnePointLinBound<double, ArrayXd>  >(f5D, level, weight, bSparse, accuracyEqualFirst);
    }
    level = 7 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DLinBound, Dehierar1DLinBound, SparseGridHierarOnePointLinBound<double, ArrayXd>  >(f5D, level, weight, bSparse, accuracyEqualFirst);
    }
}


BOOST_AUTO_TEST_CASE(test1DQuad)
{

    ArrayXd weight(1);
    weight << 1 ;
    function< double(const ArrayXd &) >  f1D =  FunctionExp() ;
    int level = 1 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DQuadBound, Dehierar1DQuadBound, SparseGridHierarOnePointQuadBound<double, ArrayXd> >(f1D, level, weight, bSparse, accuracyEqualFirst);
        bSparse = false ;
        testHierachization< Hierar1DQuadBound, Dehierar1DQuadBound, SparseGridHierarOnePointQuadBound<double, ArrayXd> >(f1D, level, weight, bSparse, accuracyEqualFirst);
    }
    level = 2 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DQuadBound, Dehierar1DQuadBound, SparseGridHierarOnePointQuadBound<double, ArrayXd> >(f1D, level, weight, bSparse, accuracyEqualFirst);
        bSparse = false ;
        testHierachization< Hierar1DQuadBound, Dehierar1DQuadBound, SparseGridHierarOnePointQuadBound<double, ArrayXd> >(f1D, level, weight, bSparse, accuracyEqualFirst);
    }
    level = 3 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DQuadBound, Dehierar1DQuadBound, SparseGridHierarOnePointQuadBound<double, ArrayXd> >(f1D, level, weight, bSparse, accuracyEqualFirst);
        bSparse = false ;
        testHierachization< Hierar1DQuadBound, Dehierar1DQuadBound, SparseGridHierarOnePointQuadBound<double, ArrayXd> >(f1D, level, weight, bSparse, accuracyEqualFirst);
    }
    level = 7 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DQuadBound, Dehierar1DQuadBound, SparseGridHierarOnePointQuadBound<double, ArrayXd> >(f1D, level, weight, bSparse, accuracyEqualFirst);
        bSparse = false ;
        testHierachization< Hierar1DQuadBound, Dehierar1DQuadBound, SparseGridHierarOnePointQuadBound<double, ArrayXd>>(f1D, level, weight, bSparse, accuracyEqualFirst);
    }
}

BOOST_AUTO_TEST_CASE(test2DQuad)
{
    ArrayXd weight(2);
    weight << 1, 1 ;
    function< double(const ArrayXd &) >  f2D =  FunctionExp() ;
    int level = 1 ;
    {
        //sparse
        bool bSparse = true;
        testHierachization< Hierar1DQuadBound, Dehierar1DQuadBound, SparseGridHierarOnePointQuadBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqual);
        bSparse = false ;
        testHierachization< Hierar1DQuadBound, Dehierar1DQuadBound, SparseGridHierarOnePointQuadBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqual);
    }
    level = 2 ;
    {
        //sparse
        bool bSparse = true;
        testHierachization< Hierar1DQuadBound, Dehierar1DQuadBound, SparseGridHierarOnePointQuadBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqual);
        bSparse = false ;
        testHierachization< Hierar1DQuadBound, Dehierar1DQuadBound, SparseGridHierarOnePointQuadBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqual);
    }
    level = 3 ;
    {
        //sparse
        bool bSparse = true;
        testHierachization< Hierar1DQuadBound, Dehierar1DQuadBound, SparseGridHierarOnePointQuadBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqualFirst);
        bSparse = false ;
        testHierachization< Hierar1DQuadBound, Dehierar1DQuadBound, SparseGridHierarOnePointQuadBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqualFirst);
    }
    level = 5 ;
    {
        //sparse
        bool bSparse = true;
        testHierachization< Hierar1DQuadBound, Dehierar1DQuadBound, SparseGridHierarOnePointQuadBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqualSecond);
        bSparse = false ;
        testHierachization< Hierar1DQuadBound, Dehierar1DQuadBound, SparseGridHierarOnePointQuadBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqualSecond);
    }

}

BOOST_AUTO_TEST_CASE(test2DAnisotropicQuad)
{

    ArrayXd weight(2);
    weight << 1, 2 ;
    function< double(const ArrayXd &) >  f2D =  FunctionExp() ;
    int level = 1 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DQuadBound, Dehierar1DQuadBound, SparseGridHierarOnePointQuadBound<double, ArrayXd>>(f2D, level, weight, bSparse, accuracyEqual);
        bSparse = false ;
        testHierachization< Hierar1DQuadBound, Dehierar1DQuadBound, SparseGridHierarOnePointQuadBound<double, ArrayXd>>(f2D, level, weight, bSparse, accuracyEqual);
    }
    level = 2 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DQuadBound, Dehierar1DQuadBound, SparseGridHierarOnePointQuadBound<double, ArrayXd>>(f2D, level, weight, bSparse, accuracyEqual);
        bSparse = false ;
        testHierachization< Hierar1DQuadBound, Dehierar1DQuadBound, SparseGridHierarOnePointQuadBound<double, ArrayXd>>(f2D, level, weight, bSparse, accuracyEqual);
    }
    level = 3 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DQuadBound, Dehierar1DQuadBound, SparseGridHierarOnePointQuadBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqualFirst);
        bSparse = false ;
        testHierachization< Hierar1DQuadBound, Dehierar1DQuadBound, SparseGridHierarOnePointQuadBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqualFirst);
    }
    level = 5 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DQuadBound, Dehierar1DQuadBound, SparseGridHierarOnePointQuadBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqualSecond);
        bSparse = false ;
        testHierachization< Hierar1DQuadBound, Dehierar1DQuadBound, SparseGridHierarOnePointQuadBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqualSecond);
    }
}


BOOST_AUTO_TEST_CASE(test3DQuad)
{

    ArrayXd weight(3);
    weight << 1., 1., 1 ;
    function< double(const ArrayXd &) >  f3D =  FunctionExp() ;
    int level = 1 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DQuadBound, Dehierar1DQuadBound, SparseGridHierarOnePointQuadBound<double, ArrayXd>  >(f3D, level, weight, bSparse, accuracyEqual);
    }
    level = 2 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DQuadBound, Dehierar1DQuadBound, SparseGridHierarOnePointQuadBound<double, ArrayXd>  >(f3D, level, weight, bSparse, accuracyEqual);
    }
    level = 3 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DQuadBound, Dehierar1DQuadBound, SparseGridHierarOnePointQuadBound<double, ArrayXd> >(f3D, level, weight, bSparse, accuracyEqualFirst);
    }
    level = 5 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DQuadBound, Dehierar1DQuadBound, SparseGridHierarOnePointQuadBound<double, ArrayXd>  >(f3D, level, weight, bSparse, accuracyEqualSecond);
    }
}

BOOST_AUTO_TEST_CASE(test5DQuad)
{

    ArrayXd weight(5);
    weight << 1., 1., 1., 1., 1. ;
    function< double(const ArrayXd &) >  f5D =  FunctionExp() ;
    int level = 1 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DQuadBound, Dehierar1DQuadBound, SparseGridHierarOnePointQuadBound<double, ArrayXd>  >(f5D, level, weight, bSparse, accuracyEqual);
    }
    level = 2 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DQuadBound, Dehierar1DQuadBound, SparseGridHierarOnePointQuadBound<double, ArrayXd>  >(f5D, level, weight, bSparse, accuracyEqualFirst);
    }
    level = 3 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DQuadBound, Dehierar1DQuadBound, SparseGridHierarOnePointQuadBound<double, ArrayXd>  >(f5D, level, weight, bSparse, accuracyEqualSecond);
    }
    level = 5 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DQuadBound, Dehierar1DQuadBound, SparseGridHierarOnePointQuadBound<double, ArrayXd>  >(f5D, level, weight, bSparse, accuracyEqualThird);
    }
}


BOOST_AUTO_TEST_CASE(test1DCubic)
{

    ArrayXd weight(1);
    weight << 1  ;
    function< double(const ArrayXd &) >  f1D =  FunctionExp() ;
    int level = 1 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DCubicBound, Dehierar1DCubicBound, SparseGridHierarOnePointCubicBound<double, ArrayXd> >(f1D, level, weight, bSparse, accuracyEqual);
        bSparse = false ;
        testHierachization< Hierar1DCubicBound, Dehierar1DCubicBound, SparseGridHierarOnePointCubicBound<double, ArrayXd> >(f1D, level, weight, bSparse, accuracyEqual);
    }
    level = 2 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DCubicBound, Dehierar1DCubicBound, SparseGridHierarOnePointCubicBound<double, ArrayXd> >(f1D, level, weight, bSparse, accuracyEqual);
        bSparse = false ;
        testHierachization< Hierar1DCubicBound, Dehierar1DCubicBound, SparseGridHierarOnePointCubicBound<double, ArrayXd> >(f1D, level, weight, bSparse, accuracyEqual);
    }
    level = 3 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DCubicBound, Dehierar1DCubicBound, SparseGridHierarOnePointCubicBound<double, ArrayXd> >(f1D, level, weight, bSparse, accuracyEqual);
        bSparse = false ;
        testHierachization< Hierar1DCubicBound, Dehierar1DCubicBound, SparseGridHierarOnePointCubicBound<double, ArrayXd> >(f1D, level, weight, bSparse, accuracyEqual);
    }
    level = 7 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DCubicBound, Dehierar1DCubicBound, SparseGridHierarOnePointCubicBound<double, ArrayXd> >(f1D, level, weight, bSparse, accuracyEqual);
        bSparse = false ;
        testHierachization< Hierar1DCubicBound, Dehierar1DCubicBound, SparseGridHierarOnePointCubicBound<double, ArrayXd> >(f1D, level, weight, bSparse, accuracyEqual);
    }
}

BOOST_AUTO_TEST_CASE(test2DCubic)
{

    ArrayXd weight(2);
    weight << 1., 1.  ;
    function< double(const ArrayXd &) >  f2D =  FunctionExp() ;
    int level = 1 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DCubicBound, Dehierar1DCubicBound, SparseGridHierarOnePointCubicBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqual);
        bSparse = false ;
        testHierachization< Hierar1DCubicBound, Dehierar1DCubicBound, SparseGridHierarOnePointCubicBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqual);
    }
    level = 2 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DCubicBound, Dehierar1DCubicBound, SparseGridHierarOnePointCubicBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqualFirst);
        bSparse = false ;
        testHierachization< Hierar1DCubicBound, Dehierar1DCubicBound, SparseGridHierarOnePointCubicBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqualFirst);
    }
    level = 3 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DCubicBound, Dehierar1DCubicBound, SparseGridHierarOnePointCubicBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqualFirst);
        bSparse = false ;
        testHierachization< Hierar1DCubicBound, Dehierar1DCubicBound, SparseGridHierarOnePointCubicBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqualFirst);
    }
    level = 5 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DCubicBound, Dehierar1DCubicBound, SparseGridHierarOnePointCubicBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqualFirst);
        bSparse = false ;
        testHierachization< Hierar1DCubicBound, Dehierar1DCubicBound, SparseGridHierarOnePointCubicBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqualFirst);
    }
}

BOOST_AUTO_TEST_CASE(test2DAnisotropicCubic)
{

    ArrayXd weight(2);
    weight << 1., 2.  ;
    function< double(const ArrayXd &) >  f2D =  FunctionExp() ;
    int level = 1 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DCubicBound, Dehierar1DCubicBound, SparseGridHierarOnePointCubicBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqual);
        bSparse = false ;
        testHierachization< Hierar1DCubicBound, Dehierar1DCubicBound, SparseGridHierarOnePointCubicBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqual);
    }
    level = 2 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DCubicBound, Dehierar1DCubicBound, SparseGridHierarOnePointCubicBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqual);
        bSparse = false ;
        testHierachization< Hierar1DCubicBound, Dehierar1DCubicBound, SparseGridHierarOnePointCubicBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqual);
    }
    level = 3 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DCubicBound, Dehierar1DCubicBound, SparseGridHierarOnePointCubicBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqualFirst);
        bSparse = false ;
        testHierachization< Hierar1DCubicBound, Dehierar1DCubicBound, SparseGridHierarOnePointCubicBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqualFirst);
    }
    level = 5 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DCubicBound, Dehierar1DCubicBound, SparseGridHierarOnePointCubicBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqualFirst);
        bSparse = false ;
        testHierachization< Hierar1DCubicBound, Dehierar1DCubicBound, SparseGridHierarOnePointCubicBound<double, ArrayXd> >(f2D, level, weight, bSparse, accuracyEqualFirst);
    }
}


BOOST_AUTO_TEST_CASE(test3DCubic)
{

    ArrayXd weight(3);
    weight << 1., 1., 1.  ;
    function< double(const ArrayXd &) >  f3D =  FunctionExp() ;
    int level = 1 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DCubicBound, Dehierar1DCubicBound, SparseGridHierarOnePointCubicBound<double, ArrayXd> >(f3D, level, weight, bSparse, accuracyEqual);
    }
    level = 2 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DCubicBound, Dehierar1DCubicBound, SparseGridHierarOnePointCubicBound<double, ArrayXd> >(f3D, level, weight, bSparse, accuracyEqual);
    }
    level = 3 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DCubicBound, Dehierar1DCubicBound, SparseGridHierarOnePointCubicBound<double, ArrayXd> >(f3D, level, weight, bSparse, accuracyEqual);
    }
    level = 5 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization<  Hierar1DCubicBound, Dehierar1DCubicBound, SparseGridHierarOnePointCubicBound<double, ArrayXd> >(f3D, level, weight, bSparse, accuracyEqualFirst);
    }
}

BOOST_AUTO_TEST_CASE(test5DCubic)
{

    ArrayXd weight(5);
    weight << 1., 1., 1., 1., 1.  ;
    function< double(const ArrayXd &) >  f5D =  FunctionExp() ;
    int level = 1 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DCubicBound, Dehierar1DCubicBound, SparseGridHierarOnePointCubicBound<double, ArrayXd>  >(f5D, level, weight, bSparse, accuracyEqual);
    }
    level = 2 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DCubicBound, Dehierar1DCubicBound, SparseGridHierarOnePointCubicBound<double, ArrayXd>  >(f5D, level, weight, bSparse, accuracyEqual);
    }
    level = 3 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DCubicBound, Dehierar1DCubicBound, SparseGridHierarOnePointCubicBound<double, ArrayXd>  >(f5D, level, weight, bSparse, accuracyEqual);
    }
    level = 5 ;
    {
        // sparse
        bool bSparse = true;
        testHierachization< Hierar1DCubicBound, Dehierar1DCubicBound, SparseGridHierarOnePointCubicBound<double, ArrayXd>  >(f5D, level, weight, bSparse, accuracyEqual);
    }
}


/// \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 int    &p_level, const ArrayXd &p_weight, const bool &p_bSparse)
{
    // create sparse structure
    SparseSet dataSetSparse;
    size_t iPosition = 0 ;
    if (p_bSparse)
        initialSparseConstructionBound(p_level, p_weight, dataSetSparse, iPosition);
    else
        initialFullConstructionBound(p_level, p_weight, dataSetSparse, iPosition);
    // to store nodal values
    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
            ArrayXd coord = GetCoordinateBound()(iterLevel->first, iterPosition->first);
            nodalValues.col(iterPosition->second).setConstant(p_func(coord));
        }

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

    // dehierarchization
    ArrayXXd nodalValuesBis = hierarValues;
    ExplorationBound< Dehierar, ArrayXd, 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(test2DLinMultiple)
{

    ArrayXd weight(2);
    weight << 1, 1 ;
    function< double(const ArrayXd &) >  f2D =  FunctionExp() ;
    int level = 1 ;
    {
        // sparse
        bool bSparse = true;
        testHierachizationBis< Hierar1DLinBound, Dehierar1DLinBound >(f2D, level, weight, bSparse);
        bSparse = false ;
        testHierachizationBis< Hierar1DLinBound, Dehierar1DLinBound >(f2D, level, weight, bSparse);
    }
    level = 2 ;
    {
        // sparse
        bool bSparse = true;
        testHierachizationBis< Hierar1DLinBound, Dehierar1DLinBound >(f2D, level, weight, bSparse);
        bSparse = false ;
        testHierachizationBis< Hierar1DLinBound, Dehierar1DLinBound >(f2D, level, weight, bSparse);
    }
    level = 3 ;
    {
        // sparse
        bool bSparse = true;
        testHierachizationBis< Hierar1DLinBound, Dehierar1DLinBound >(f2D, level, weight, bSparse);
        bSparse = false ;
        testHierachizationBis< Hierar1DLinBound, Dehierar1DLinBound >(f2D, level, weight, bSparse);
    }
    level = 7 ;
    {
        // sparse
        bool bSparse = true;
        testHierachizationBis< Hierar1DLinBound, Dehierar1DLinBound >(f2D, level, weight, bSparse);
        bSparse = false ;
        testHierachizationBis< Hierar1DLinBound, Dehierar1DLinBound >(f2D, level, weight, bSparse);
    }
}


BOOST_AUTO_TEST_CASE(test3DQuadMultiple)
{

    ArrayXd weight(3);
    weight << 1., 1., 1 ;
    function< double(const ArrayXd &) >  f3D =  FunctionExp() ;
    int level = 1 ;
    {
        // sparse
        bool bSparse = true;
        testHierachizationBis< Hierar1DQuadBound, Dehierar1DQuadBound >(f3D, level, weight, bSparse);
    }
    level = 2 ;
    {
        // sparse
        bool bSparse = true;
        testHierachizationBis< Hierar1DQuadBound, Dehierar1DQuadBound >(f3D, level, weight, bSparse);
    }
    level = 3 ;
    {
        // sparse
        bool bSparse = true;
        testHierachizationBis< Hierar1DQuadBound, Dehierar1DQuadBound >(f3D, level, weight, bSparse);
    }
    level = 7 ;
    {
        // sparse
        bool bSparse = true;
        testHierachizationBis< Hierar1DQuadBound, Dehierar1DQuadBound >(f3D, level, weight, bSparse);
    }
}


BOOST_AUTO_TEST_CASE(test3DCubicMultiple)
{

    ArrayXd weight(3);
    weight << 1., 1., 1.  ;
    function< double(const ArrayXd &) >  f3D =  FunctionExp() ;
    int level = 1 ;
    {
        // sparse
        bool bSparse = true;
        testHierachizationBis< Hierar1DCubicBound, Dehierar1DCubicBound >(f3D, level, weight, bSparse);
    }
    level = 2 ;
    {
        // sparse
        bool bSparse = true;
        testHierachizationBis< Hierar1DCubicBound, Dehierar1DCubicBound >(f3D, level, weight, bSparse);
    }
    level = 3 ;
    {
        // sparse
        bool bSparse = true;
        testHierachizationBis< Hierar1DCubicBound, Dehierar1DCubicBound >(f3D, level, weight, bSparse);
    }
    level = 7 ;
    {
        // sparse
        bool bSparse = true;
        testHierachizationBis<  Hierar1DCubicBound, Dehierar1DCubicBound >(f3D, level, weight, bSparse);
    }
}
