//---------------------------------------------------------------------------//
//!
//! \file   tstGaussKronrodIntegrator.cpp
//! \author Luke Kersting
//! \brief  Gauss-Kronrod quadrature integrator unit tests.
//!
//---------------------------------------------------------------------------//

// Std Lib Includes
#include <iostream>
#include <limits>
#include <stdexcept>

// Boost Includes
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <boost/multiprecision/cpp_dec_float.hpp>
#include <boost/math/tools/precision.hpp>

// FRENSIE Includes
#include "Utility_GaussKronrodIntegrator.hpp"
#include "Utility_Vector.hpp"
#include "Utility_ArrayView.hpp"
#include "Utility_TypeNameTraits.hpp"
#include "Utility_UnitTestHarnessWithMain.hpp"

//---------------------------------------------------------------------------//
// Testing Types
//---------------------------------------------------------------------------//

typedef boost::multiprecision::cpp_dec_float_50 long_float;

struct X2Functor
{
  double operator()( const double x ) const
  {
    if( x >= 0.0 && x <= 1.0 )
      return x*x;
    else
      return 0.0;
  }

  static double getIntegratedValue()
  {
    return 1.0/3.0;
  }

  static double getLowerIntegratedValue()
  {
    return 1.0/24.0;
  }

  static double getUpperIntegratedValue()
  {
    return 7.0/24.0;
  }
};

struct X2FunctorParameter
{
  double operator()( const double x, const double y ) const
  {
    if( y >= 0.0 && y <= 1.0 )
      return y*y*x;
    else
      return 0.0;
  }

  static double getIntegratedValue()
  {
    return 1.0/3.0;
  }

  static double getLowerIntegratedValue()
  {
    return 1.0/24.0;
  }

  static double getUpperIntegratedValue()
  {
    return 7.0/24.0;
  }
};

struct X2FunctorLong
{
  long double operator()( const long double x ) const
  {
    if( x >= 0.0L && x <= 1.0L )
      return x*x;
    else
      return 0.0L;
  }

  static long double getIntegratedValue()
  {
    return 1.0L/3.0L;
  }

  static long double getLowerIntegratedValue()
  {
    return 1.0L/24.0L;
  }

  static long double getUpperIntegratedValue()
  {
    return 7.0L/24.0L;
  }
};

struct X2FunctorBoost
{
  long_float operator()( const long_float x ) const
  {
    if( x >= 0.0L && x <= 1.0L )
      return x*x;
    else
      return 0.0L;
  }

  static long_float getIntegratedValue()
  {
    return 1.0L/3.0L;
  }

  static long_float getLowerIntegratedValue()
  {
    return 1.0L/24.0L;
  }

  static long_float getUpperIntegratedValue()
  {
    return 7.0L/24.0L;
  }
};

struct X3Functor
{
  double operator()( const double x ) const
  {
    if( x >= 0.0 && x <= 1.0 )
      return x*x*x;
    else
      return 0.0;
  }

  static double getIntegratedValue()
  {
    return 0.25;
  }

  static double getLowerIntegratedValue()
  {
    return 1.0/64.0;
  }

  static double getUpperIntegratedValue()
  {
    return 15.0/64.0;
  }
};

struct X3FunctorParameter
{
  double operator()( const double x, const double y ) const
  {
    if( y >= 0.0 && y <= 1.0 )
      return y*y*y*x;
    else
      return 0.0;
  }

  static double getIntegratedValue()
  {
    return 0.25;
  }

  static double getLowerIntegratedValue()
  {
    return 1.0/64.0;
  }

  static double getUpperIntegratedValue()
  {
    return 15.0/64.0;
  }
};

struct X3FunctorLong
{
  long double operator()( const long double x ) const
  {
    if( x >= 0.0L && x <= 1.0L )
      return x*x*x;
    else
      return 0.0L;
  }

  static long double getIntegratedValue()
  {
    return 0.25L;
  }

  static long double getLowerIntegratedValue()
  {
    return 1.0L/64.0L;
  }

  static long double getUpperIntegratedValue()
  {
    return 15.0L/64.0L;
  }
};

struct X3FunctorBoost
{
  long_float operator()( const long_float x ) const
  {
    if( x >= 0.0L && x <= 1.0L )
      return x*x*x;
    else
      return 0.0L;
  }

  static long_float getIntegratedValue()
  {
    return 0.25L;
  }

  static long_float getLowerIntegratedValue()
  {
    return 1.0L/64.0L;
  }

  static long_float getUpperIntegratedValue()
  {
    return 15.0L/64.0L;
  }
};

namespace Utility{

TYPE_NAME_TRAITS_QUICK_DECL( boost::multiprecision::cpp_dec_float_50 );
TYPE_NAME_TRAITS_QUICK_DECL( X2Functor );
TYPE_NAME_TRAITS_QUICK_DECL( X2FunctorParameter );
TYPE_NAME_TRAITS_QUICK_DECL( X2FunctorLong );
TYPE_NAME_TRAITS_QUICK_DECL( X2FunctorBoost );
TYPE_NAME_TRAITS_QUICK_DECL( X3Functor );
TYPE_NAME_TRAITS_QUICK_DECL( X3FunctorParameter );
TYPE_NAME_TRAITS_QUICK_DECL( X3FunctorLong );
TYPE_NAME_TRAITS_QUICK_DECL( X3FunctorBoost );

} // end Utility namespace

typedef std::tuple<X2Functor,X3Functor> TestFunctors;
typedef std::tuple<X2FunctorLong,X3FunctorLong> TestFunctorsLong;
typedef std::tuple<X2FunctorBoost,X3FunctorBoost> TestFunctorsBoost;
typedef std::tuple<X2FunctorParameter,X3FunctorParameter> TestFunctorsParameter;

//---------------------------------------------------------------------------//
// Testing Functions
//---------------------------------------------------------------------------//

double exp_neg_x( const double x )
{
  return exp( -x );
}

double exp_neg_abs_x( const double x, const double a )
{
  return exp( -a*fabs(x) );
}

double inv_sqrt_abs_x( const double x )
{
  return 1/sqrt(fabs(x));
}

//---------------------------------------------------------------------------//
// Testing Structs.
//---------------------------------------------------------------------------//
class TestGaussKronrodIntegrator : public Utility::GaussKronrodIntegrator<double>
{
public:
  TestGaussKronrodIntegrator( const double relative_error_tol )
    : Utility::GaussKronrodIntegrator<double>( relative_error_tol )
  { /* ... */ }

  ~TestGaussKronrodIntegrator()
  { /* ... */ }

  // Allow public access to the GaussKronrodIntegrator protected member functions
  using Utility::GaussKronrodIntegrator<double>::calculateQuadratureIntegrandValuesAtAbscissa;
  using Utility::GaussKronrodIntegrator<double>::bisectAndIntegrateBinInterval;
  using Utility::GaussKronrodIntegrator<double>::rescaleAbsoluteError;
  using Utility::GaussKronrodIntegrator<double>::subintervalTooSmall;
  using Utility::GaussKronrodIntegrator<double>::checkRoundoffError;
  using Utility::GaussKronrodIntegrator<double>::sortBins;
  using Utility::GaussKronrodIntegrator<double>::getWynnEpsilonAlgorithmExtrapolation;
};

//---------------------------------------------------------------------------//
// Tests
//---------------------------------------------------------------------------//
// Check that functions can be integrated over [0,1]
FRENSIE_UNIT_TEST_TEMPLATE( GaussKronrodIntegrator,
                            integrateWithPointRule,
                            TestFunctors )
{
  FETCH_TEMPLATE_PARAM( 0, Functor );

  Utility::GaussKronrodIntegrator<double> gk_integrator( 1e-12 );

  double absolute_error, result_abs, result_asc, test_result, tol;
  double result;

  Functor functor_instance;

  gk_integrator.integrateWithPointRule<15>( functor_instance,
                                        0.0,
                                        1.0,
                                        result,
                                        absolute_error,
                                        result_abs,
                                        result_asc );

  tol = absolute_error/result;
  test_result = result;

  FRENSIE_CHECK_FLOATING_EQUALITY( Functor::getIntegratedValue(), test_result, tol );

  gk_integrator.integrateWithPointRule<21>( functor_instance,
                                        0.0,
                                        1.0,
                                        result,
                                        absolute_error,
                                        result_abs,
                                        result_asc );

  tol = absolute_error/result;
  test_result = result;

  FRENSIE_CHECK_FLOATING_EQUALITY( Functor::getIntegratedValue(), test_result, tol );

  gk_integrator.integrateWithPointRule<31>( functor_instance,
                                        0.0,
                                        1.0,
                                        result,
                                        absolute_error,
                                        result_abs,
                                        result_asc );

  tol = absolute_error/result;
  test_result = result;

  FRENSIE_CHECK_FLOATING_EQUALITY( Functor::getIntegratedValue(), test_result, tol );

  gk_integrator.integrateWithPointRule<41>( functor_instance,
                                        0.0,
                                        1.0,
                                        result,
                                        absolute_error,
                                        result_abs,
                                        result_asc );

  tol = absolute_error/result;
  test_result = result;

  FRENSIE_CHECK_FLOATING_EQUALITY( Functor::getIntegratedValue(), test_result, tol );

  gk_integrator.integrateWithPointRule<51>( functor_instance,
                                        0.0,
                                        1.0,
                                        result,
                                        absolute_error,
                                        result_abs,
                                        result_asc );

  tol = absolute_error/result;
  test_result = result;

  FRENSIE_CHECK_FLOATING_EQUALITY( Functor::getIntegratedValue(), test_result, tol );

  gk_integrator.integrateWithPointRule<61>( functor_instance,
                                        0.0,
                                        1.0,
                                        result,
                                        absolute_error,
                                        result_abs,
                                        result_asc );

  tol = absolute_error/result;
  test_result = result;

  FRENSIE_CHECK_FLOATING_EQUALITY( Functor::getIntegratedValue(), test_result, tol );
}

//---------------------------------------------------------------------------//
// Check that quadrature integrand values can be evaluated at abscissa
FRENSIE_UNIT_TEST_TEMPLATE( GaussKronrodIntegrator,
                            calculateQuadratureIntegrandValuesAtAbscissa,
                            TestFunctors )
{
  FETCH_TEMPLATE_PARAM( 0, Functor );

  TestGaussKronrodIntegrator test_integrator( 1e-12 );

  double half_length = 0.5;
  double midpoint = 0.5;
  double abscissa = 0.5;

  double integrand_value_lower, integrand_value_upper ;

  Functor functor_instance;

  test_integrator.calculateQuadratureIntegrandValuesAtAbscissa(
                functor_instance,
                abscissa,
                half_length,
                midpoint,
                integrand_value_lower,
                integrand_value_upper );

  double tol = 1e-12;

  FRENSIE_CHECK_FLOATING_EQUALITY( functor_instance( 0.25 ), integrand_value_lower, tol );
  FRENSIE_CHECK_FLOATING_EQUALITY( functor_instance( 0.75 ), integrand_value_upper, tol );
}

//---------------------------------------------------------------------------//
// Check that quadrature integrand can be bisected and integrated
FRENSIE_UNIT_TEST_TEMPLATE( GaussKronrodIntegrator,
                            bisectAndIntegrateBinInterval,
                            TestFunctors )
{
  FETCH_TEMPLATE_PARAM( 0, Functor );

  TestGaussKronrodIntegrator test_integrator( 1e-12 );

  Utility::BinTraits<double> bin, bin_1, bin_2;

  double bin_1_asc, bin_2_asc, tol_1, tol_2;

  bin.lower_limit = 0.0;
  bin.upper_limit = 1.0;

  Functor functor_instance;

  test_integrator.bisectAndIntegrateBinInterval<15>(
                functor_instance,
                bin,
                bin_1,
                bin_2,
                bin_1_asc,
                bin_2_asc );

  tol_1 = bin_1.error/bin_1.result;
  tol_2 = bin_2.error/bin_2.result;

  FRENSIE_CHECK_FLOATING_EQUALITY( Functor::getLowerIntegratedValue(),
                          static_cast<double>( bin_1.result ),
                          tol_1 );
  FRENSIE_CHECK_FLOATING_EQUALITY( Functor::getUpperIntegratedValue(),
                          static_cast<double>( bin_2.result ),
                          tol_2 );

  FRENSIE_CHECK_EQUAL( bin_1.lower_limit, bin.lower_limit );
  FRENSIE_CHECK_FLOATING_EQUALITY( bin_1.upper_limit, 0.5, 1e-15);
  FRENSIE_CHECK_FLOATING_EQUALITY( bin_2.lower_limit, 0.5, 1e-15 );
  FRENSIE_CHECK_EQUAL( bin_2.upper_limit, bin.upper_limit );

  test_integrator.bisectAndIntegrateBinInterval<21>(
                functor_instance,
                bin,
                bin_1,
                bin_2,
                bin_1_asc,
                bin_2_asc );

  tol_1 = bin_1.error/bin_1.result;
  tol_2 = bin_2.error/bin_2.result;

  FRENSIE_CHECK_FLOATING_EQUALITY( Functor::getLowerIntegratedValue(),
                          static_cast<double>( bin_1.result ),
                          tol_1 );
  FRENSIE_CHECK_FLOATING_EQUALITY( Functor::getUpperIntegratedValue(),
                          static_cast<double>( bin_2.result ),
                          tol_2 );

  test_integrator.bisectAndIntegrateBinInterval<31>(
                functor_instance,
                bin,
                bin_1,
                bin_2,
                bin_1_asc,
                bin_2_asc );

  tol_1 = bin_1.error/bin_1.result;
  tol_2 = bin_2.error/bin_2.result;

  FRENSIE_CHECK_FLOATING_EQUALITY( Functor::getLowerIntegratedValue(),
                          static_cast<double>( bin_1.result ),
                          tol_1 );
  FRENSIE_CHECK_FLOATING_EQUALITY( Functor::getUpperIntegratedValue(),
                          static_cast<double>( bin_2.result ),
                          tol_2 );

  test_integrator.bisectAndIntegrateBinInterval<41>(
                functor_instance,
                bin,
                bin_1,
                bin_2,
                bin_1_asc,
                bin_2_asc );

  tol_1 = bin_1.error/bin_1.result;
  tol_2 = bin_2.error/bin_2.result;

  FRENSIE_CHECK_FLOATING_EQUALITY( Functor::getLowerIntegratedValue(),
                          static_cast<double>( bin_1.result ),
                          tol_1 );
  FRENSIE_CHECK_FLOATING_EQUALITY( Functor::getUpperIntegratedValue(),
                          static_cast<double>( bin_2.result ),
                          tol_2 );

  test_integrator.bisectAndIntegrateBinInterval<51>(
                functor_instance,
                bin,
                bin_1,
                bin_2,
                bin_1_asc,
                bin_2_asc );

  tol_1 = bin_1.error/bin_1.result;
  tol_2 = bin_2.error/bin_2.result;

  FRENSIE_CHECK_FLOATING_EQUALITY( Functor::getLowerIntegratedValue(),
                          static_cast<double>( bin_1.result ),
                          tol_1 );
  FRENSIE_CHECK_FLOATING_EQUALITY( Functor::getUpperIntegratedValue(),
                          static_cast<double>( bin_2.result ),
                          tol_2 );

  test_integrator.bisectAndIntegrateBinInterval<61>(
                functor_instance,
                bin,
                bin_1,
                bin_2,
                bin_1_asc,
                bin_2_asc );

  tol_1 = bin_1.error/bin_1.result;
  tol_2 = bin_2.error/bin_2.result;

  FRENSIE_CHECK_FLOATING_EQUALITY( Functor::getLowerIntegratedValue(),
                          static_cast<double>( bin_1.result ),
                          tol_1 );
  FRENSIE_CHECK_FLOATING_EQUALITY( Functor::getUpperIntegratedValue(),
                          static_cast<double>( bin_2.result ),
                          tol_2 );
}

//---------------------------------------------------------------------------//
// Check that the error can be re-scaled
FRENSIE_UNIT_TEST( GaussKronrodIntegrator,
                   rescaleAbsoluteError )
{
  TestGaussKronrodIntegrator test_integrator( 1e-12 );

  double absolute_error = 0.0;
  double result_abs = 0.0;
  double result_asc = 0.0;
  double tol = 1e-12;
  double limit = std::numeric_limits<double>::min() / ( 50.0 *
                   std::numeric_limits<double>::epsilon() );

  absolute_error = limit/2.0;

  test_integrator.rescaleAbsoluteError(
                absolute_error,
                result_abs,
                result_asc );

  FRENSIE_CHECK_FLOATING_EQUALITY( limit/2.0, absolute_error, tol );

  absolute_error = 1.0;
  result_asc = 2.0;

  test_integrator.rescaleAbsoluteError(
                absolute_error,
                result_abs,
                result_asc );

  FRENSIE_CHECK_FLOATING_EQUALITY( 2.0, absolute_error, tol );

  absolute_error = 1.0;
  result_asc = 800.0;

  test_integrator.rescaleAbsoluteError(
                absolute_error,
                result_abs,
                result_asc );

  FRENSIE_CHECK_FLOATING_EQUALITY( 100.0, absolute_error, tol );

  absolute_error = 50.0*std::numeric_limits<double>::epsilon();
  result_asc = 0.0;
  result_abs = 2.0;
  double min_error = 50.0*std::numeric_limits<double>::epsilon() * result_abs;

  test_integrator.rescaleAbsoluteError(
                absolute_error,
                result_abs,
                result_asc );

  FRENSIE_CHECK_FLOATING_EQUALITY( min_error, absolute_error, tol );
}

//---------------------------------------------------------------------------//
// Check the roundoff error
FRENSIE_UNIT_TEST( GaussKronrodIntegrator,
                   checkRoundoffError )
{
  TestGaussKronrodIntegrator test_integrator( 1e-12 );

  Utility::BinTraits<double> bin, bin_1, bin_2;
  int round_off_1 = 0;
  int round_off_2 = 0;
  int number_of_interactions = 0;
  double error_12 = 0.0, bin_1_asc = 0.0, bin_2_asc = 0.0;
  double tol = 1e-12;

  bin.result = 9.9999;
  bin_1.result = 5.0;
  bin_2.result = 5.0;

  bin.error = 1.0;
  bin_1.error = 0.5;
  bin_2.error = 0.49;

  bin_1_asc = bin_1.error;
  bin_2_asc = bin_2.error;

  test_integrator.checkRoundoffError(
                bin,
                bin_1,
                bin_2,
                bin_1_asc,
                bin_2_asc,
                round_off_1,
                round_off_2,
                number_of_interactions );

  FRENSIE_CHECK_EQUAL( 0, round_off_1 );
  FRENSIE_CHECK_EQUAL( 0, round_off_2 );

  bin_1_asc = 0.0;
  bin_2_asc = 0.0;

  test_integrator.checkRoundoffError(
                bin,
                bin_1,
                bin_2,
                bin_1_asc,
                bin_2_asc,
                round_off_1,
                round_off_2,
                number_of_interactions );

  FRENSIE_CHECK_EQUAL( 1, round_off_1 );
  FRENSIE_CHECK_EQUAL( 0, round_off_2 );

  bin_2.error = 0.501;
  number_of_interactions = 10;

  test_integrator.checkRoundoffError(
                bin,
                bin_1,
                bin_2,
                bin_1_asc,
                bin_2_asc,
                round_off_1,
                round_off_2,
                number_of_interactions );

  FRENSIE_CHECK_EQUAL( 2, round_off_1 );
  FRENSIE_CHECK_EQUAL( 1, round_off_2 );

  bin.result = 9.9;

  test_integrator.checkRoundoffError(
                bin,
                bin_1,
                bin_2,
                bin_1_asc,
                bin_2_asc,
                round_off_1,
                round_off_2,
                number_of_interactions );

  FRENSIE_CHECK_EQUAL( 2, round_off_1 );
  FRENSIE_CHECK_EQUAL( 2, round_off_2 );
}

//---------------------------------------------------------------------------//
// Check the roundoff error
FRENSIE_UNIT_TEST( GaussKronrodIntegrator,
                   checkRoundoffError2 )
{
  TestGaussKronrodIntegrator test_integrator( 1e-12 );

  Utility::ExtrapolatedBinTraits<double> bin, bin_1, bin_2;
  int round_off_1 = 0;
  int round_off_2 = 0;
  int round_off_3 = 0;
  int number_of_interactions = 0;
  bool extrapolate = false;
  double error_12 = 0.0, bin_1_asc = 0.0, bin_2_asc = 0.0;
  double tol = 1e-12;

  bin.result = 9.9999;
  bin_1.result = 5.0;
  bin_2.result = 5.0;

  bin.error = 1.0;
  bin_1.error = 0.5;
  bin_2.error = 0.49;

  bin_1_asc = bin_1.error;
  bin_2_asc = bin_2.error;

  test_integrator.checkRoundoffError(
                bin,
                bin_1,
                bin_2,
                bin_1_asc,
                bin_2_asc,
                round_off_1,
                round_off_2,
                round_off_3,
                extrapolate,
                number_of_interactions );

  FRENSIE_CHECK_EQUAL( 0, round_off_1 );
  FRENSIE_CHECK_EQUAL( 0, round_off_2 );
  FRENSIE_CHECK_EQUAL( 0, round_off_3 );

  bin_1_asc = 0.0;
  bin_2_asc = 0.0;

  test_integrator.checkRoundoffError(
                bin,
                bin_1,
                bin_2,
                bin_1_asc,
                bin_2_asc,
                round_off_1,
                round_off_2,
                round_off_3,
                extrapolate,
                number_of_interactions );

  FRENSIE_CHECK_EQUAL( 1, round_off_1 );
  FRENSIE_CHECK_EQUAL( 0, round_off_2 );
  FRENSIE_CHECK_EQUAL( 0, round_off_3 );

  extrapolate = true;

  test_integrator.checkRoundoffError(
                bin,
                bin_1,
                bin_2,
                bin_1_asc,
                bin_2_asc,
                round_off_1,
                round_off_2,
                round_off_3,
                extrapolate,
                number_of_interactions );

  FRENSIE_CHECK_EQUAL( 1, round_off_1 );
  FRENSIE_CHECK_EQUAL( 1, round_off_2 );
  FRENSIE_CHECK_EQUAL( 0, round_off_3 );

  bin_2.error = 0.501;
  number_of_interactions = 10;

  test_integrator.checkRoundoffError(
                bin,
                bin_1,
                bin_2,
                bin_1_asc,
                bin_2_asc,
                round_off_1,
                round_off_2,
                round_off_3,
                extrapolate,
                number_of_interactions );

  FRENSIE_CHECK_EQUAL( 1, round_off_1 );
  FRENSIE_CHECK_EQUAL( 2, round_off_2 );
  FRENSIE_CHECK_EQUAL( 1, round_off_3 );

  bin.result = 9.9;

  test_integrator.checkRoundoffError(
                bin,
                bin_1,
                bin_2,
                bin_1_asc,
                bin_2_asc,
                round_off_1,
                round_off_2,
                round_off_3,
                extrapolate,
                number_of_interactions );

  FRENSIE_CHECK_EQUAL( 1, round_off_1 );
  FRENSIE_CHECK_EQUAL( 2, round_off_2 );
  FRENSIE_CHECK_EQUAL( 2, round_off_3 );
}

//---------------------------------------------------------------------------//
// Check that the error list can be sorted
FRENSIE_UNIT_TEST( GaussKronrodIntegrator,
                   sortBins )
{
  TestGaussKronrodIntegrator test_integrator( 1e-12 );

  Utility::ExtrapolatedBinTraits<double> bin, bin_1, bin_2;

  int nr_max = 0;
  int number_of_intervals = 1;

  // Set up bin order array
  std::vector<int> bin_order(1);
  bin_order[0] = 0;

  // Set bin array
  Utility::GaussKronrodIntegrator<double>::BinArray bin_array(1000);
  bin.error = 10.0;
  bin_array[0] = bin;

  // Set bin_1 and bin_2
  bin_1.error = 5.0;
  bin_2.error = 2.0;

  test_integrator.sortBins(
                bin_order,
                bin_array,
                bin_1,
                bin_2,
                number_of_intervals,
                nr_max );

  FRENSIE_CHECK_EQUAL( 0, bin_order[0] );
  FRENSIE_CHECK_EQUAL( 1, bin_order[1] );
  FRENSIE_CHECK_EQUAL( 0, nr_max );

  nr_max = 0;
  number_of_intervals = 3;

  // Set up bin order array
  bin_order.resize(3);
  bin_order[0] = 0;
  bin_order[1] = 1;
  bin_order[2] = 2;

  // Set bin array
  bin.error = 10.0;
  bin_array[0] = bin;
  bin.error = 8.0;
  bin_array[1] = bin;
  bin.error = 1.0;
  bin_array[2] = bin;

  // Set bin_1 and bin_2
  bin_1.error = 5.0;
  bin_2.error = 2.0;

  test_integrator.sortBins(
                bin_order,
                bin_array,
                bin_1,
                bin_2,
                number_of_intervals,
                nr_max );

  FRENSIE_CHECK_EQUAL( 1, bin_order[0] );
  FRENSIE_CHECK_EQUAL( 0, bin_order[1] );
  FRENSIE_CHECK_EQUAL( 3, bin_order[2] );
  FRENSIE_CHECK_EQUAL( 2, bin_order[3] );
  FRENSIE_CHECK_EQUAL( 0, nr_max );

  // Test with nr_max != 0
  nr_max = 1;
  number_of_intervals = 3;

  // Set up bin order array
  bin_order.resize(3);
  bin_order[0] = 0;
  bin_order[1] = 1;
  bin_order[2] = 2;

  // Set bin array
  bin.error = 10.0;
  bin_array[0] = bin;
  bin.error = 8.0;
  bin_array[1] = bin;
  bin.error = 1.0;
  bin_array[2] = bin;

  // Set bin_1 and bin_2
  bin_1.error = 11.0;
  bin_2.error = 2.0;

  test_integrator.sortBins(
                bin_order,
                bin_array,
                bin_1,
                bin_2,
                number_of_intervals,
                nr_max );

  FRENSIE_CHECK_EQUAL( 1, bin_order[0] );
  FRENSIE_CHECK_EQUAL( 0, bin_order[1] );
  FRENSIE_CHECK_EQUAL( 3, bin_order[2] );
  FRENSIE_CHECK_EQUAL( 2, bin_order[3] );
  FRENSIE_CHECK_EQUAL( 0, nr_max );

  // Test 3
  nr_max = 0;
  number_of_intervals = 3;

  // Set up bin order array
  bin_order.resize(3);
  bin_order[0] = 1;
  bin_order[1] = 0;
  bin_order[2] = 2;

  bin_array.clear();

  // Set bin array
  bin.error = 0.673651;
  bin_array[0] = bin;
  bin.error = 1.90537;
  bin_array[1] = bin;
  bin.error = 6.50354e-15;
  bin_array[2] = bin;

  // Set bin_1 and bin_2
  bin_1.error = 0.673652;
  bin_2.error = 6.50353e-15;

  test_integrator.sortBins(
                bin_order,
                bin_array,
                bin_1,
                bin_2,
                number_of_intervals,
                nr_max );

  FRENSIE_CHECK_EQUAL( 1, bin_order[0] );
  FRENSIE_CHECK_EQUAL( 0, bin_order[1] );
  FRENSIE_CHECK_EQUAL( 2, bin_order[2] );
  FRENSIE_CHECK_EQUAL( 3, bin_order[3] );
  FRENSIE_CHECK_EQUAL( 0, nr_max );

  // Test 4
  nr_max = 3;
  number_of_intervals = 6;

  // Set up bin order array
  bin_order.resize(6);
  bin_order[0] = 3;
  bin_order[1] = 4;
  bin_order[2] = 0;
  bin_order[3] = 2;
  bin_order[4] = 1;
  bin_order[5] = 5;

  bin_array.clear();

  // Set bin array
  bin.error = 4.0;
  bin_array[0] = bin;
  bin.error = 2.0;
  bin_array[1] = bin;
  bin.error = 3.0;
  bin_array[2] = bin;
  bin.error = 6.0;
  bin_array[3] = bin;
  bin.error = 5.0;
  bin_array[4] = bin;
  bin.error = 1.0;
  bin_array[5] = bin;

  // Set bin_1 and bin_2
  bin_1.error = 2.5;
  bin_2.error = 0.5;

  test_integrator.sortBins(
                bin_order,
                bin_array,
                bin_1,
                bin_2,
                number_of_intervals,
                nr_max );

  FRENSIE_CHECK_EQUAL( 3, bin_order[0] );
  FRENSIE_CHECK_EQUAL( 4, bin_order[1] );
  FRENSIE_CHECK_EQUAL( 0, bin_order[2] );
  FRENSIE_CHECK_EQUAL( 2, bin_order[3] );
  FRENSIE_CHECK_EQUAL( 1, bin_order[4] );
  FRENSIE_CHECK_EQUAL( 5, bin_order[5] );
  FRENSIE_CHECK_EQUAL( 6, bin_order[6] );
  FRENSIE_CHECK_EQUAL( 3, nr_max );

  // Test 5
  nr_max = 3;
  number_of_intervals = 6;

  // Set up bin order array
  bin_order.resize(6);
  bin_order[0] = 3;
  bin_order[1] = 4;
  bin_order[2] = 0;
  bin_order[3] = 2;
  bin_order[4] = 1;
  bin_order[5] = 5;

  bin_array.clear();

  // Set bin array
  bin.error = 4.0;
  bin_array[0] = bin;
  bin.error = 2.0;
  bin_array[1] = bin;
  bin.error = 3.0;
  bin_array[2] = bin;
  bin.error = 6.0;
  bin_array[3] = bin;
  bin.error = 5.0;
  bin_array[4] = bin;
  bin.error = 1.0;
  bin_array[5] = bin;

  // Set bin_1 and bin_2
  bin_1.error = 4.5;
  bin_2.error = 0.5;

  test_integrator.sortBins(
                bin_order,
                bin_array,
                bin_1,
                bin_2,
                number_of_intervals,
                nr_max );

  FRENSIE_CHECK_EQUAL( 3, bin_order[0] );
  FRENSIE_CHECK_EQUAL( 4, bin_order[1] );
  FRENSIE_CHECK_EQUAL( 2, bin_order[2] );
  FRENSIE_CHECK_EQUAL( 0, bin_order[3] );
  FRENSIE_CHECK_EQUAL( 1, bin_order[4] );
  FRENSIE_CHECK_EQUAL( 5, bin_order[5] );
  FRENSIE_CHECK_EQUAL( 6, bin_order[6] );
  FRENSIE_CHECK_EQUAL( 2, nr_max );
}

//---------------------------------------------------------------------------//
// Check that the Wynn Epsilon-Algorithm extrapolated value can be calculated
FRENSIE_UNIT_TEST( GaussKronrodIntegrator,
                   getWynnEpsilonAlgorithmExtrapolation )
{
  TestGaussKronrodIntegrator test_integrator( 1e-12 );

  std::vector<double> bin_extrapolated_result(52);
  std::vector<double> last_three_results(3);
  double extrapolated_result, extrapolated_error;
  int number_of_extrapolated_intervals, number_of_extrapolated_calls;
  double tol = 1e-16;
  number_of_extrapolated_calls = 0;

  // test 1
  number_of_extrapolated_intervals = 2;
  bin_extrapolated_result[0] = 3.93505142975913369L;
  bin_extrapolated_result[1] = 3.95407442555431254L;
  bin_extrapolated_result[2] = 3.96752571487956640L;

  test_integrator.getWynnEpsilonAlgorithmExtrapolation(
                bin_extrapolated_result,
                last_three_results,
                extrapolated_result,
                extrapolated_error,
                number_of_extrapolated_intervals,
                number_of_extrapolated_calls );

  FRENSIE_CHECK_EQUAL( number_of_extrapolated_intervals, 2 );
  FRENSIE_CHECK_EQUAL( number_of_extrapolated_calls, 1 );
  FRENSIE_CHECK_FLOATING_EQUALITY( extrapolated_error,
                          std::numeric_limits<double>::max(),
                          tol );
  FRENSIE_CHECK_FLOATING_EQUALITY( extrapolated_result,
                          3.99999999999999645,
                          tol );
  FRENSIE_CHECK_FLOATING_EQUALITY( last_three_results[0],
                          3.99999999999999645,
                          tol );
  FRENSIE_CHECK_FLOATING_EQUALITY( last_three_results[1],
                          0.0,
                          tol );
  FRENSIE_CHECK_FLOATING_EQUALITY( last_three_results[2],
                          0.0,
                          tol );

  // test 2
  number_of_extrapolated_intervals = 3;
  bin_extrapolated_result[0] = 3.99999999999999645L;
  bin_extrapolated_result[1] = 3.95407442555431254L;
  bin_extrapolated_result[2] = 3.96752571487956640L;
  bin_extrapolated_result[3] = 3.97703721277715605L;
  bin_extrapolated_result[4] = 3.96752571487956640L;

  test_integrator.getWynnEpsilonAlgorithmExtrapolation(
                bin_extrapolated_result,
                last_three_results,
                extrapolated_result,
                extrapolated_error,
                number_of_extrapolated_intervals,
                number_of_extrapolated_calls );

  FRENSIE_CHECK_EQUAL( number_of_extrapolated_intervals, 3 );
  FRENSIE_CHECK_EQUAL( number_of_extrapolated_calls, 2 );
  FRENSIE_CHECK_FLOATING_EQUALITY( extrapolated_error,
                          std::numeric_limits<double>::max(),
                          tol );
  FRENSIE_CHECK_FLOATING_EQUALITY( extrapolated_result,
                          4.00000000000000355,
                          tol );
  FRENSIE_CHECK_FLOATING_EQUALITY( last_three_results[0],
                          3.99999999999999645,
                          tol );
  FRENSIE_CHECK_FLOATING_EQUALITY( last_three_results[1],
                          4.00000000000000355,
                          tol );
  FRENSIE_CHECK_FLOATING_EQUALITY( last_three_results[2],
                          0.0,
                          tol );

  // test 3
  number_of_extrapolated_intervals = 4;
  bin_extrapolated_result[0] = 3.99999999999999645L;
  bin_extrapolated_result[1] = 4.00000000000000355L;
  bin_extrapolated_result[2] = 3.96752571487956640L;
  bin_extrapolated_result[3] = 3.97703721277715605L;
  bin_extrapolated_result[4] = 3.98376285743978320L;
  bin_extrapolated_result[5] = 3.97703721277715605L;

  test_integrator.getWynnEpsilonAlgorithmExtrapolation(
                bin_extrapolated_result,
                last_three_results,
                extrapolated_result,
                extrapolated_error,
                number_of_extrapolated_intervals,
                number_of_extrapolated_calls );

  FRENSIE_CHECK_EQUAL( number_of_extrapolated_intervals, 4 );
  FRENSIE_CHECK_EQUAL( number_of_extrapolated_calls, 3 );
  FRENSIE_CHECK_FLOATING_EQUALITY( extrapolated_error,
                          std::numeric_limits<double>::max(),
                          tol );
  FRENSIE_CHECK_FLOATING_EQUALITY( extrapolated_result,
                          4.00000000000000089,
                          tol );
  FRENSIE_CHECK_FLOATING_EQUALITY( last_three_results[0],
                          3.99999999999999645,
                          tol );
  FRENSIE_CHECK_FLOATING_EQUALITY( last_three_results[1],
                          4.00000000000000355,
                          tol );
  FRENSIE_CHECK_FLOATING_EQUALITY( last_three_results[2],
                          4.00000000000000089,
                          tol );

  // test 4
  number_of_extrapolated_intervals = 5;
  bin_extrapolated_result[0] = 4.00000000000000089L;
  bin_extrapolated_result[1] = 4.00000000000000355L;
  bin_extrapolated_result[2] = 3.99999999999999911L;
  bin_extrapolated_result[3] = 3.97703721277715605L;
  bin_extrapolated_result[4] = 3.98376285743978320L;
  bin_extrapolated_result[5] = 3.98851860638857758L;
  bin_extrapolated_result[6] = 3.98376285743978320L;

  test_integrator.getWynnEpsilonAlgorithmExtrapolation(
                bin_extrapolated_result,
                last_three_results,
                extrapolated_result,
                extrapolated_error,
                number_of_extrapolated_intervals,
                number_of_extrapolated_calls );

  FRENSIE_CHECK_EQUAL( number_of_extrapolated_intervals, 5 );
  FRENSIE_CHECK_EQUAL( number_of_extrapolated_calls, 4 );
  FRENSIE_CHECK_FLOATING_EQUALITY( extrapolated_error,
                          5.68434188608080149e-14,
                          tol );
  FRENSIE_CHECK_FLOATING_EQUALITY( extrapolated_result,
                          3.99999999999998135,
                          tol );
  FRENSIE_CHECK_FLOATING_EQUALITY( last_three_results[0],
                          4.00000000000000355,
                          tol );
  FRENSIE_CHECK_FLOATING_EQUALITY( last_three_results[1],
                          4.00000000000000089,
                          tol );
  FRENSIE_CHECK_FLOATING_EQUALITY( last_three_results[2],
                          3.99999999999998135,
                          tol );

}

//---------------------------------------------------------------------------//
// Check that functions can be integrated over [0,1] adaptively
FRENSIE_UNIT_TEST_TEMPLATE( GaussKronrodIntegrator,
                            integrateAdaptively,
                            TestFunctors )
{
  FETCH_TEMPLATE_PARAM( 0, Functor );

  Utility::GaussKronrodIntegrator<double> gk_integrator( 1e-12 );

  double result, absolute_error, tol;

  Functor functor_instance;

  // Test the 15-point rule
  gk_integrator.integrateAdaptively<15>( functor_instance,
				  0.0,
				  1.0,
				  result,
				  absolute_error );

  tol = absolute_error/result;

  FRENSIE_CHECK_FLOATING_EQUALITY( Functor::getIntegratedValue(), result, tol );

  // Test the 21-point rule
  gk_integrator.integrateAdaptively<21>( functor_instance,
				  0.0,
				  1.0,
				  result,
				  absolute_error );

  tol = absolute_error/result;

  FRENSIE_CHECK_FLOATING_EQUALITY( Functor::getIntegratedValue(), result, tol );

  // Test the 31-point rule
  gk_integrator.integrateAdaptively<31>( functor_instance,
				  0.0,
				  1.0,
				  result,
				  absolute_error );

  tol = absolute_error/result;

  FRENSIE_CHECK_FLOATING_EQUALITY( Functor::getIntegratedValue(), result, tol );

  // Test the 41-point rule
  gk_integrator.integrateAdaptively<41>( functor_instance,
				  0.0,
				  1.0,
				  result,
				  absolute_error );

  tol = absolute_error/result;

  FRENSIE_CHECK_FLOATING_EQUALITY( Functor::getIntegratedValue(), result, tol );

  // Test the 51-point rule
  gk_integrator.integrateAdaptively<51>( functor_instance,
				  0.0,
				  1.0,
				  result,
				  absolute_error );

  tol = absolute_error/result;

  FRENSIE_CHECK_FLOATING_EQUALITY( Functor::getIntegratedValue(), result, tol );

  // Test the 61-point rule
  gk_integrator.integrateAdaptively<61>( functor_instance,
				  0.0,
				  1.0,
				  result,
				  absolute_error );

  tol = absolute_error/result;

  FRENSIE_CHECK_FLOATING_EQUALITY( Functor::getIntegratedValue(), result, tol );
}

//---------------------------------------------------------------------------//
// Check that functions can be integrated over [0,1] adaptively
FRENSIE_UNIT_TEST_TEMPLATE( GaussKronrodIntegrator,
                            integrateAdaptively_long_double,
                            TestFunctorsLong )
{
  FETCH_TEMPLATE_PARAM( 0, Functor );

  Utility::GaussKronrodIntegrator<long double> gk_integrator( 1e-12 );

  long double result, absolute_error, tol;

  Functor functor_instance;

  // Test the 15-point rule
  gk_integrator.integrateAdaptively<15,long double>( functor_instance,
				  0.0L,
				  1.0L,
				  result,
				  absolute_error );

  tol = absolute_error/result;

  FRENSIE_CHECK_FLOATING_EQUALITY( (double)Functor::getIntegratedValue(),
                          (double)result,
                          (double)tol );

  // Test the 21-point rule
  gk_integrator.integrateAdaptively<21>( functor_instance,
				  0.0L,
				  1.0L,
				  result,
				  absolute_error );

  tol = absolute_error/result;

  FRENSIE_CHECK_FLOATING_EQUALITY( (double)Functor::getIntegratedValue(),
                          (double)result,
                          (double)tol );

  // Test the 31-point rule
  gk_integrator.integrateAdaptively<31,long double>( functor_instance,
				  0.0L,
				  1.0L,
				  result,
				  absolute_error );

  tol = absolute_error/result;

  FRENSIE_CHECK_FLOATING_EQUALITY( (double)Functor::getIntegratedValue(),
                          (double)result,
                          (double)tol );

  // Test the 41-point rule
  gk_integrator.integrateAdaptively<41,long double>( functor_instance,
				  0.0L,
				  1.0L,
				  result,
				  absolute_error );

  tol = absolute_error/result;

  FRENSIE_CHECK_FLOATING_EQUALITY( (double)Functor::getIntegratedValue(),
                          (double)result,
                          (double)tol );

  // Test the 51-point rule
  gk_integrator.integrateAdaptively<51,long double>( functor_instance,
				  0.0L,
				  1.0L,
				  result,
				  absolute_error );

  tol = absolute_error/result;

  FRENSIE_CHECK_FLOATING_EQUALITY( (double)Functor::getIntegratedValue(),
                          (double)result,
                          (double)tol );

  // Test the 61-point rule
  gk_integrator.integrateAdaptively<61,long double>( functor_instance,
				  0.0L,
				  1.0L,
				  result,
				  absolute_error );

  tol = absolute_error/result;

  FRENSIE_CHECK_FLOATING_EQUALITY( (double)Functor::getIntegratedValue(),
                          (double)result,
                          (double)tol );

}

//---------------------------------------------------------------------------//
// Check that functions can be integrated over [0,1] adaptively
FRENSIE_UNIT_TEST_TEMPLATE( GaussKronrodIntegrator,
                            integrateAdaptively_long_float,
                            TestFunctorsBoost )
{
  FETCH_TEMPLATE_PARAM( 0, Functor );

  Utility::GaussKronrodIntegrator<long_float> gk_integrator( 1e-12 );

  long_float result, absolute_error, tol;

  Functor functor_instance;

  // Test the 15-point rule
  gk_integrator.integrateAdaptively<15,long_float>( functor_instance,
				  (long_float)0,
				  (long_float)1,
				  result,
				  absolute_error );

  tol = absolute_error/result;

  FRENSIE_CHECK_FLOATING_EQUALITY( (double)Functor::getIntegratedValue(),
                          (double)result,
                          (double)tol );

  // Test the 21-point rule
  gk_integrator.integrateAdaptively<21,long_float>( functor_instance,
				  (long_float)0,
				  (long_float)1,
				  result,
				  absolute_error );

  tol = absolute_error/result;

  FRENSIE_CHECK_FLOATING_EQUALITY( (double)Functor::getIntegratedValue(),
                          (double)result,
                          (double)tol );

  // Test the 31-point rule
  gk_integrator.integrateAdaptively<31,long_float>( functor_instance,
				  (long_float)0,
				  (long_float)1,
				  result,
				  absolute_error );

  tol = absolute_error/result;

  FRENSIE_CHECK_FLOATING_EQUALITY( (double)Functor::getIntegratedValue(),
                          (double)result,
                          (double)tol );

  // Test the 41-point rule
  gk_integrator.integrateAdaptively<41,long_float>( functor_instance,
				  (long_float)0,
				  (long_float)1,
				  result,
				  absolute_error );

  tol = absolute_error/result;

  FRENSIE_CHECK_FLOATING_EQUALITY( (double)Functor::getIntegratedValue(),
                          (double)result,
                          (double)tol );

  // Test the 51-point rule
  gk_integrator.integrateAdaptively<51,long_float>( functor_instance,
				  (long_float)0,
				  (long_float)1,
				  result,
				  absolute_error );

  tol = absolute_error/result;

  FRENSIE_CHECK_FLOATING_EQUALITY( (double)Functor::getIntegratedValue(),
                          (double)result,
                          (double)tol );

  // Test the 61-point rule
  gk_integrator.integrateAdaptively<61,long_float>( functor_instance,
				  (long_float)0,
				  (long_float)1,
				  result,
				  absolute_error );

  tol = absolute_error/result;

  FRENSIE_CHECK_FLOATING_EQUALITY( (double)Functor::getIntegratedValue(),
                          (double)result,
                          (double)tol );
}

//---------------------------------------------------------------------------//
// Check that functions can be integrated over [0,1] adaptively
FRENSIE_UNIT_TEST_TEMPLATE( GaussKronrodIntegrator,
                            integrateAdaptively_parameter,
                            TestFunctorsParameter )
{
  FETCH_TEMPLATE_PARAM( 0, Functor );

  Utility::GaussKronrodIntegrator<double> gk_integrator( 1e-12 );

  double result, absolute_error, tol;

  Functor functor_instance;

  // Test the 15-point rule
  gk_integrator.integrateAdaptively<15>( functor_instance,
          1.0,
				  0.0,
				  1.0,
				  result,
				  absolute_error );

  tol = absolute_error/result;

  FRENSIE_CHECK_FLOATING_EQUALITY( Functor::getIntegratedValue(), result, tol );

  // Test the 21-point rule
  gk_integrator.integrateAdaptively<21>( functor_instance,
                  1.0,
				  0.0,
				  1.0,
				  result,
				  absolute_error );

  tol = absolute_error/result;

  FRENSIE_CHECK_FLOATING_EQUALITY( Functor::getIntegratedValue(), result, tol );

  // Test the 31-point rule
  gk_integrator.integrateAdaptively<31>( functor_instance,
                  1.0,
				  0.0,
				  1.0,
				  result,
				  absolute_error );

  tol = absolute_error/result;

  FRENSIE_CHECK_FLOATING_EQUALITY( Functor::getIntegratedValue(), result, tol );

  // Test the 41-point rule
  gk_integrator.integrateAdaptively<41>( functor_instance,
                  1.0,
				  0.0,
				  1.0,
				  result,
				  absolute_error );

  tol = absolute_error/result;

  FRENSIE_CHECK_FLOATING_EQUALITY( Functor::getIntegratedValue(), result, tol );

  // Test the 51-point rule
  gk_integrator.integrateAdaptively<51>( functor_instance,
                  1.0,
				  0.0,
				  1.0,
				  result,
				  absolute_error );

  tol = absolute_error/result;

  FRENSIE_CHECK_FLOATING_EQUALITY( Functor::getIntegratedValue(), result, tol );

  // Test the 61-point rule
  gk_integrator.integrateAdaptively<61>( functor_instance,
                  1.0,
				  0.0,
				  1.0,
				  result,
				  absolute_error );

  tol = absolute_error/result;

  FRENSIE_CHECK_FLOATING_EQUALITY( Functor::getIntegratedValue(), result, tol );
}

//---------------------------------------------------------------------------//
// Check that a function with integrable singularities can be integrated
FRENSIE_UNIT_TEST( GaussKronrodIntegrator,
		   integrateAdaptivelyWynnEpsilon )
{
  boost::function<double (double x)> function_wrapper = inv_sqrt_abs_x;

  std::vector<double> points_of_interest( 3 );
  points_of_interest[0] = -1.0;
  points_of_interest[1] = 0.0; // integrable singularity
  points_of_interest[2] = 1.0;

  Utility::GaussKronrodIntegrator<double> gk_int( 1e-12, 0.0, 100000 );

  double result, absolute_error;

  gk_int.integrateAdaptivelyWynnEpsilon( function_wrapper,
					 Utility::arrayView(points_of_interest),
					 result,
					 absolute_error );

  double tol = absolute_error/result;

  FRENSIE_CHECK_FLOATING_EQUALITY( result, 4.0, tol );

  std::vector<long double> long_points_of_interest( 2 );
  points_of_interest[0] = -1.0L;
  points_of_interest[1] = 1.0L;

  Utility::GaussKronrodIntegrator<long double> gk_long_int( 1e-12, 0.0, 100000 );

  long double long_result, long_absolute_error;

  gk_long_int.integrateAdaptivelyWynnEpsilon( function_wrapper,
                                              Utility::arrayView(long_points_of_interest),
                                              long_result,
                                              long_absolute_error );

  long double long_tol = long_absolute_error/long_result;

  FRENSIE_CHECK_FLOATING_EQUALITY( (double)result, 4.0, (double)tol );
}

//---------------------------------------------------------------------------//
// Check that a function with no singularities can be integrated using Wynn Epsilon
FRENSIE_UNIT_TEST_TEMPLATE( GaussKronrodIntegrator,
                            integrateAdaptivelyWynnEpsilon_no_singularities,
                            TestFunctors )
{
  FETCH_TEMPLATE_PARAM( 0, Functor );

  Functor functor_instance;

  std::vector<double> points_of_interest( 3 );
  points_of_interest[0] = 0.0;
  points_of_interest[1] = 0.5;
  points_of_interest[2] = 1.0;

  Utility::GaussKronrodIntegrator<double> gkq_set( 1e-12, 0.0, 100000 );

  double result, absolute_error;

  gkq_set.integrateAdaptivelyWynnEpsilon(
                                        functor_instance,
                                        Utility::arrayView(points_of_interest),
                                        result,
                                        absolute_error );

  double tol = absolute_error/result;

  FRENSIE_CHECK_FLOATING_EQUALITY( Functor::getIntegratedValue(), result, tol );
}

//---------------------------------------------------------------------------//
// Check that warnings can be thrown
FRENSIE_UNIT_TEST_TEMPLATE( GaussKronrodIntegrator,
                            warnOnDirtyIntegration,
                            TestFunctorsBoost )
{
  FETCH_TEMPLATE_PARAM( 0, Functor );

  Utility::GaussKronrodIntegrator<long_float> gk_integrator( 1e-20, 0.0, 2 );

  long_float result, absolute_error, tol;

  Functor functor_instance;

  // Integration should only throw warnings
  try
  {
    gk_integrator.integrateAdaptively<15,long_float>( functor_instance,
				  (long_float)-1,
				  (long_float)2,
				  result,
				  absolute_error );
  }
  catch( std::exception exception )
  {
    FRENSIE_CHECK( 1 );
  }
  catch( ... )
  {
    FRENSIE_CHECK( 0 );
  }

  // Integration should throw exception
  try
  {
    gk_integrator.throwExceptionOnDirtyIntegration();

    gk_integrator.integrateAdaptively<15,long_float>( functor_instance,
				  (long_float)-1,
				  (long_float)2,
				  result,
				  absolute_error );
  }
  catch( std::exception exception )
  {
    FRENSIE_CHECK( 1 );
  }
  catch( ... )
  {
    FRENSIE_CHECK( 0 );
  }

  // Integration should only throw warnings
  try
  {
    gk_integrator.warnOnDirtyIntegration();

    gk_integrator.integrateAdaptively<15,long_float>( functor_instance,
				  (long_float)-1,
				  (long_float)2,
				  result,
				  absolute_error );
  }
  catch( std::exception exception )
  {
    FRENSIE_CHECK( 0 );
  }
  catch( ... )
  {
    FRENSIE_CHECK( 0 );
  }

  // Integration should throw exception
  try
  {
  Utility::GaussKronrodIntegrator<long_float> gk_int( 1e-50, 0.0, 2);

  gk_integrator.integrateAdaptively<15,long_float>( functor_instance,
				  (long_float)0,
				  (long_float)1,
				  result,
				  absolute_error );
  }
  catch( std::exception exception )
  {
    FRENSIE_CHECK( 1 );
  }
  catch( ... )
  {
    FRENSIE_CHECK( 0 );
  }
}

//---------------------------------------------------------------------------//
// end tstGaussKronrodIntegrator.cpp
//---------------------------------------------------------------------------//


/**
 * main for testing the graphs
 * by R. Falque
 * 20/08/2019
 **/

// dependencies
#include <cstdlib>
#include <iostream>
#include <limits>

#include <Eigen/Core>

#include "gtest/gtest.h"

#include "libGraphCpp/graph.hpp"
#include "libGraphCpp/graphOptions.hpp"

#include <yaml-cpp/yaml.h>

/*
 * TEST CONSTRUCTORS
 */
TEST(constructors, string)
{
    libgraphcpp::Graph graph("../data/graphs_test/0_graph_complete.obj");

    EXPECT_EQ(graph.num_nodes(), 36);
    EXPECT_EQ(graph.num_edges(), 68);
}

TEST(constructors, stringAndOpts)
{
    graphOptions opts;
    opts.loadYAML("../tests_config.yaml");

    libgraphcpp::Graph graph("../data/graphs_test/0_graph_complete.obj", opts);

    EXPECT_EQ(graph.num_nodes(), 36);
    EXPECT_EQ(graph.num_edges(), 68);
}

TEST(constructors, edgeList)
{
    Eigen::MatrixXd nodes;
    nodes.resize(5, 3);
    nodes << 1, 2, 3,
             4, 5, 6,
             1, 8, 9,
             2, 8, 9,
             3, 8, 9;

    Eigen::MatrixXi edges;
    edges.resize(4, 2);
    edges << 0, 1,
             1, 2,
             2, 3,
             3, 4;

    libgraphcpp::Graph graph(nodes, edges);

    EXPECT_EQ(graph.num_nodes(), 5);
    EXPECT_EQ(graph.num_edges(), 4);
}

TEST(constructors, edgeListAndOpts)
{
    graphOptions opts;
    opts.loadYAML("../tests_config.yaml");

    Eigen::MatrixXd nodes;
    nodes.resize(5, 3);
    nodes << 1, 2, 3,
             4, 5, 6,
             1, 8, 9,
             2, 8, 9,
             3, 8, 9;

    Eigen::MatrixXi edges;
    edges.resize(4, 2);
    edges << 0, 1,
             0, 2,
             0, 3,
             0, 4;

    libgraphcpp::Graph graph(nodes, edges, opts);

    EXPECT_EQ(graph.num_nodes(), 5);
    EXPECT_EQ(graph.num_edges(), 4);
}

TEST(constructors, adjacencyMatrix)
{
    Eigen::MatrixXd nodes;
    nodes.resize(4, 3);
    nodes << 1, 2, 3,
             4, 5, 6,
             1, 8, 9,
             2, 8, 9;

    Eigen::MatrixXi edges;
    edges.resize(4, 4);
    edges << 0, 1, 0, 1,
             1, 0, 0, 1,
             0, 0, 0, 1,
             1, 1, 1, 0;

    libgraphcpp::Graph graph(nodes, edges);

    EXPECT_EQ(graph.num_nodes(), 4);
    EXPECT_EQ(graph.num_edges(), 4);
}

TEST(constructors, adjacencyMatrixAndOpts)
{
    graphOptions opts;
    opts.loadYAML("../tests_config.yaml");

    Eigen::MatrixXd nodes;
    nodes.resize(4, 3);
    nodes << 1, 2, 3,
             4, 5, 6,
             1, 8, 9,
             2, 8, 9;

    Eigen::MatrixXi edges;
    edges.resize(4, 4);
    edges << 0, 1, 0, 1,
             1, 0, 0, 1,
             0, 0, 0, 1,
             1, 1, 1, 0;

    libgraphcpp::Graph graph(nodes, edges, opts);

    EXPECT_EQ(graph.num_nodes(), 4);
    EXPECT_EQ(graph.num_edges(), 4);
}

/*
 * TEST GRAPH CONNECTIVITY
 */
TEST(graphConnectivity, disconnected)
{
    libgraphcpp::Graph graph("../data/graphs_test/6_disconnected.obj");

    EXPECT_EQ(graph.is_connected(), false);
    EXPECT_EQ(graph.is_biconnected(), false);
    EXPECT_EQ(graph.is_triconnected(), false);
}

TEST(graphConnectivity, connected)
{
    libgraphcpp::Graph graph("../data/graphs_test/3_1_node_connectetivity.obj");

    EXPECT_EQ(graph.is_connected(), true);
    EXPECT_EQ(graph.is_biconnected(), false);
    EXPECT_EQ(graph.is_triconnected(), false);
}

TEST(graphConnectivity, biconnected)
{
    libgraphcpp::Graph graph("../data/graphs_test/5_2_edge_connectetivity.obj");

    EXPECT_EQ(graph.is_connected(), true);
    EXPECT_EQ(graph.is_biconnected(), true);
    EXPECT_EQ(graph.is_triconnected(), false);
}

TEST(graphConnectivity, triconnected)
{
    libgraphcpp::Graph graph("../data/graphs_test/0_graph_complete.obj");

    EXPECT_EQ(graph.is_connected(), true);
    EXPECT_EQ(graph.is_biconnected(), true);
    EXPECT_EQ(graph.is_triconnected(), true);
}

/*
 * TEST Dijkstra
 */
TEST(graphDikjstra, unreachable)
{
    libgraphcpp::Graph graph("../data/graphs_test/6_disconnected.obj");

    EXPECT_EQ(graph.dijkstra(0, 1), std::numeric_limits<double>::infinity());
}

TEST(graphDikjstra, reachable)
{
    libgraphcpp::Graph graph("../data/graphs_test/0_graph_complete.obj");

    EXPECT_EQ(graph.dijkstra(0, 1), 20);
}

int main(int argc, char* argv[])
{
	testing::InitGoogleTest(&argc, argv);
	return RUN_ALL_TESTS();
}


#include <string>
#include <sstream>
#include <iostream>
#include <map>
#include <regex>
#include <fstream>
#include <boost/algorithm/string.hpp>

#include "../common.hpp"

using namespace std;

bool has_adjacent_digits(const string &s) {
    for (int i = 1; i < s.size(); i++) {
        if (s[i - 1] == s[i]) {
            return true;
        }
    }
    return false;
}

bool has_double_digits(const string &s) {
    for (int i = 1; i < s.size(); i++) {
        if (s[i - 1] == s[i]) { // Found double
            // Check that the one before, and the one after doesn't match
            if ((i >= 2 && s[i-2] == s[i])) {
                continue;
            }
            if ((i + 1) < s.size() && s[i+1] == s[i])  {
                continue;
            }
            return true;
        }
    }
    return false;
}

bool is_monotonic(const string &s) {
    for (int i = 1; i < s.size(); i++) {
        if (s[i - 1] > s[i]) {
            return false;
        }
    }
    return true;
}

bool passes1(const string &s) {
    return is_monotonic(s) && has_adjacent_digits(s);
}

bool passes2(const string &s) {
    return is_monotonic(s) && has_double_digits(s);
}

int main() {
    int answer1 = 0;
    int answer2 = 0;

    assert(passes1("111111") == true);
    assert(passes1("223450") == false);
    assert(passes1("123789") == false);
    assert(passes1("700000") == false);

    assert(passes2("112233") == true);
    assert(passes2("123444") == false);
    assert(passes2("111122") == true);
    assert(passes2("666999") == false);

    // Bruteforce :)
    for (int i = 235741; i < 706948; i++) {
        string s = to_string(i);

        if (passes1(s)) {
            answer1++;
        }

        if (passes2(s)) {
            answer2++;
        }
    }

    cout << "Answer 4.1: " << answer1 << endl;
    cout << "Answer 4.2: " << answer2 << endl;
}


#include <iostream>
#include <boost/numeric/ublas/banded.hpp>
#include <boost/numeric/ublas/io.hpp>
#include <boost/numeric/ublas/operation.hpp>
#include <iomanip>

#include "utils.hpp"

using namespace boost::numeric::ublas;

int expected_index( size_t index, column_major ) {
   // this is the data shown on http://www.netlib.org/lapack/lug/node124.html
   // read column-by-column, aka column_major
   int mapping[] = { 0, 11, 21, 31, 12, 22, 32, 42, 23, 33, 43, 53, 34, 44, 54, 0, 45, 55, 0, 0 };
   return mapping[ index ];
}

int expected_index( size_t index, row_major ) {
   // this is the data shown on http://www.netlib.org/lapack/lug/node124.html
   // read row-by-row, aka row_major
   int mapping[] = { 0, 0, 11, 12, 0, 21, 22, 23, 31, 32, 33, 34, 42, 43, 44, 45, 53, 54, 55, 0 };
   return mapping[ index ];
}

int expected_index_6_by_5( size_t index, column_major ) {
   // read column-by-column, aka column_major
   int mapping[] = { 0, 11, 21, 31, 12, 22, 32, 42, 23, 33, 43, 53, 34, 44, 54, 64, 45, 55, 65, 0 };
   return mapping[ index ];
}

int expected_index_6_by_5( size_t index, row_major ) {
   // read row-by-row, aka row_major
   int mapping[] = { 0, 0, 11, 12, 0, 21, 22, 23, 31, 32, 33, 34, 42, 43, 44, 45, 53, 54, 55, 0, 64, 65, 0, 0 };
   return mapping[ index ];
}

int expected_index_5_by_6( size_t index, column_major ) {
   // read column-by-column, aka column_major
   int mapping[] = { 0, 11, 21, 31, 12, 22, 32, 42, 23, 33, 43, 53, 34, 44, 54, 0, 45, 55, 0, 0, 56, 0, 0, 0 };
   return mapping[ index ];
}

int expected_index_5_by_6( size_t index, row_major ) {
   // read row-by-row, aka row_major
   int mapping[] = { 0, 0, 11, 12, 0, 21, 22, 23, 31, 32, 33, 34, 42, 43, 44, 45, 53, 54, 55, 56};
   return mapping[ index ];
}

template< typename Orientation >
bool test_band_storage() {

    int m = 5;
    int n = 5;
    int kl = 2;
    int ku = 1;

    banded_matrix< int, Orientation > test_matrix( m, n, kl, ku );
    test_matrix.clear();
    size_t band_storage_size = test_matrix.data().size();

    test_matrix( 0, 0 ) = 11;
    test_matrix( 0, 1 ) = 12;
    test_matrix( 1, 0 ) = 21;
    test_matrix( 1, 1 ) = 22;
    test_matrix( 1, 2 ) = 23;
    test_matrix( 2, 0 ) = 31;
    test_matrix( 2, 1 ) = 32;
    test_matrix( 2, 2 ) = 33;
    test_matrix( 2, 3 ) = 34;
    test_matrix( 3, 1 ) = 42;
    test_matrix( 3, 2 ) = 43;
    test_matrix( 3, 3 ) = 44;
    test_matrix( 3, 4 ) = 45;
    test_matrix( 4, 2 ) = 53;
    test_matrix( 4, 3 ) = 54;
    test_matrix( 4, 4 ) = 55;

    BOOST_UBLAS_TEST_TRACE( "Full matrix" );
    BOOST_UBLAS_TEST_TRACE( std::setw( 3 ) << test_matrix );

    BOOST_UBLAS_TEST_TRACE( "data() of matrix" );
    for ( size_t i = 0; i < band_storage_size; ++i ) {
        std::cerr << test_matrix.data()[ i ] << " ";
    }
    std::cerr << std::endl;

    BOOST_UBLAS_TEST_TRACE( "Expected data() of matrix" );
    for ( size_t i = 0; i < band_storage_size; ++i ) {
        std::cerr << expected_index( i, Orientation() ) << " ";
    }
    std::cerr << std::endl;

    size_t mismatch = 0;

    for ( size_t i = 0; i < band_storage_size; ++i ) {
      if ( test_matrix.data()[ i ] != expected_index( i, Orientation() ) ) {
        ++mismatch;
      }
    }

    return 0 == mismatch;
}

template< typename Orientation >
bool test_band_storage_6_by_5() {

    int m = 6;
    int n = 5;
    int kl = 2;
    int ku = 1;

    banded_matrix< int, Orientation > test_matrix( m, n, kl, ku );
    test_matrix.clear();
    size_t band_storage_size = test_matrix.data().size();

    test_matrix( 0, 0 ) = 11;
    test_matrix( 0, 1 ) = 12;
    test_matrix( 1, 0 ) = 21;
    test_matrix( 1, 1 ) = 22;
    test_matrix( 1, 2 ) = 23;
    test_matrix( 2, 0 ) = 31;
    test_matrix( 2, 1 ) = 32;
    test_matrix( 2, 2 ) = 33;
    test_matrix( 2, 3 ) = 34;
    test_matrix( 3, 1 ) = 42;
    test_matrix( 3, 2 ) = 43;
    test_matrix( 3, 3 ) = 44;
    test_matrix( 3, 4 ) = 45;
    test_matrix( 4, 2 ) = 53;
    test_matrix( 4, 3 ) = 54;
    test_matrix( 4, 4 ) = 55;
    test_matrix( 5, 3 ) = 64;
    test_matrix( 5, 4 ) = 65;

    BOOST_UBLAS_TEST_TRACE( "Full matrix" );
    BOOST_UBLAS_TEST_TRACE( std::setw( 3 ) << test_matrix );

    BOOST_UBLAS_TEST_TRACE( "data() of matrix" );
    for ( size_t i = 0; i < band_storage_size; ++i ) {
        std::cerr << test_matrix.data()[ i ] << " ";
    }
    std::cerr << std::endl;

    BOOST_UBLAS_TEST_TRACE( "Expected data() of matrix" );
    for ( size_t i = 0; i < band_storage_size; ++i ) {
        std::cerr << expected_index_6_by_5( i, Orientation() ) << " ";
    }
    std::cerr << std::endl;

    size_t mismatch = 0;

    for ( size_t i = 0; i < band_storage_size; ++i ) {
      if ( test_matrix.data()[ i ] != expected_index_6_by_5( i, Orientation() ) ) {
        ++mismatch;
      }
    }

    return 0 == mismatch;
}

template< typename Orientation >
bool test_band_storage_5_by_6() {

    int m = 5;
    int n = 6;
    int kl = 2;
    int ku = 1;

    banded_matrix< int, Orientation > test_matrix( m, n, kl, ku );
    test_matrix.clear();
    size_t band_storage_size = test_matrix.data().size();

    test_matrix( 0, 0 ) = 11;
    test_matrix( 0, 1 ) = 12;
    test_matrix( 1, 0 ) = 21;
    test_matrix( 1, 1 ) = 22;
    test_matrix( 1, 2 ) = 23;
    test_matrix( 2, 0 ) = 31;
    test_matrix( 2, 1 ) = 32;
    test_matrix( 2, 2 ) = 33;
    test_matrix( 2, 3 ) = 34;
    test_matrix( 3, 1 ) = 42;
    test_matrix( 3, 2 ) = 43;
    test_matrix( 3, 3 ) = 44;
    test_matrix( 3, 4 ) = 45;
    test_matrix( 4, 2 ) = 53;
    test_matrix( 4, 3 ) = 54;
    test_matrix( 4, 4 ) = 55;
    test_matrix( 4, 5 ) = 56;

    BOOST_UBLAS_TEST_TRACE( "Full matrix" );
    BOOST_UBLAS_TEST_TRACE( std::setw( 3 ) << test_matrix );

    BOOST_UBLAS_TEST_TRACE( "data() of matrix" );
    for ( size_t i = 0; i < band_storage_size; ++i ) {
        std::cerr << test_matrix.data()[ i ] << " ";
    }
    std::cerr << std::endl;

    BOOST_UBLAS_TEST_TRACE( "Expected data() of matrix" );
    for ( size_t i = 0; i < band_storage_size; ++i ) {
        std::cerr << expected_index_5_by_6( i, Orientation() ) << " ";
    }
    std::cerr << std::endl;

    size_t mismatch = 0;

    for ( size_t i = 0; i < band_storage_size; ++i ) {
      if ( test_matrix.data()[ i ] != expected_index_5_by_6( i, Orientation() ) ) {
        ++mismatch;
      }
    }

    return 0 == mismatch;
}

BOOST_UBLAS_TEST_DEF( banded_matrix_column_major )
{
	BOOST_UBLAS_TEST_TRACE( "Test case: storage layout banded_matrix < column_major >" );

    BOOST_UBLAS_TEST_CHECK( test_band_storage< column_major >() );
}

BOOST_UBLAS_TEST_DEF( banded_matrix_row_major )
{
	BOOST_UBLAS_TEST_TRACE( "Test case: storage layout banded_matrix < row_major >" );

    BOOST_UBLAS_TEST_CHECK( test_band_storage< row_major >() );
}

BOOST_UBLAS_TEST_DEF( banded_matrix_column_major_6_by_5 )
{
    BOOST_UBLAS_TEST_TRACE( "Test case: storage layout banded_matrix < column_major > 6x5" );

    BOOST_UBLAS_TEST_CHECK( test_band_storage_6_by_5< column_major >() );
}

BOOST_UBLAS_TEST_DEF( banded_matrix_row_major_6_by_5 )
{
    BOOST_UBLAS_TEST_TRACE( "Test case: storage layout banded_matrix < row_major > 6x5" );

    BOOST_UBLAS_TEST_CHECK( test_band_storage_6_by_5< row_major >() );
}

BOOST_UBLAS_TEST_DEF( banded_matrix_column_major_5_by_6 )
{
    BOOST_UBLAS_TEST_TRACE( "Test case: storage layout banded_matrix < column_major > 5x6" );

    BOOST_UBLAS_TEST_CHECK( test_band_storage_5_by_6< column_major >() );
}

BOOST_UBLAS_TEST_DEF( banded_matrix_row_major_5_by_6 )
{
    BOOST_UBLAS_TEST_TRACE( "Test case: storage layout banded_matrix < row_major > 5x6" );

    BOOST_UBLAS_TEST_CHECK( test_band_storage_5_by_6< row_major >() );
}

int main()
{

	BOOST_UBLAS_TEST_SUITE( "Test storage layout of banded matrix type" );

	BOOST_UBLAS_TEST_TRACE( "Example data taken from http://www.netlib.org/lapack/lug/node124.html" );

	BOOST_UBLAS_TEST_BEGIN();

    BOOST_UBLAS_TEST_DO( banded_matrix_column_major );

    BOOST_UBLAS_TEST_DO( banded_matrix_row_major );

    BOOST_UBLAS_TEST_DO( banded_matrix_column_major_6_by_5 );

    BOOST_UBLAS_TEST_DO( banded_matrix_row_major_6_by_5 );

    BOOST_UBLAS_TEST_DO( banded_matrix_column_major_5_by_6 );

    BOOST_UBLAS_TEST_DO( banded_matrix_row_major_5_by_6 );

    BOOST_UBLAS_TEST_END();
}


//==================================================================================================
/*!
  @file

  @copyright 2012-2015 NumScale SAS

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================
#ifndef BOOST_SIMD_CONSTANT_MINDENORMAL_HPP_INCLUDED
#define BOOST_SIMD_CONSTANT_MINDENORMAL_HPP_INCLUDED

#if defined(DOXYGEN_ONLY)
namespace boost { namespace simd
{
  /*!
    @ingroup group-constant

    Generate the the least of all non zero positive value including denormals.

    @par Semantic:

    @code
    T r = Mindenormal<T>();
    @endcode

    is similar to:

    @code
    if T is integral
      r = T(1)
    else if T is double
      r =  4.940656458412465e-324;
    else if T is float
      r = 1.4012985e-45;
    @endcode

    @return The Mindenormal constant for the proper type
  **/
  template<typename T> T Mindenormal();

  namespace functional
  {
    /*!
      @ingroup group-callable-constant
      Generate the  constant mindenormal.

      @return The Mindenormal constant for the proper type
    **/
    const boost::dispatch::functor<tag::mindenormal_> mindenormal = {};
  }
} }
#endif

#include <boost/simd/constant/definition/mindenormal.hpp>
#include <boost/simd/arch/common/scalar/constant/constant_value.hpp>
#include <boost/simd/arch/common/simd/constant/constant_value.hpp>

#endif


#include <string>

#define BOOST_TEST_MODULE DenseLatticeMultTests

#include "DenseLattice.h"
#include "MIAConfig.h"

#ifdef MIA_USE_HEADER_ONLY_TESTS
#include <boost/test/included/unit_test.hpp>
#else
#include <boost/test/unit_test.hpp>
#endif

template<typename T1,typename T2,typename T3>
void multwork(const std::string & _result){
    typedef LibMIA::DenseLattice<T1> Lat1;
    typedef LibMIA::DenseLattice<T2> Lat2;
    typedef LibMIA::DenseLattice<T3> Lat3;

    Lat1 test1= Lat1(5,5,10);
    test1.load("data/test1.bin");
    Lat2 test2= Lat2(5,5,10);

    test2.load("data/test2.bin");
    Lat3 test3=test1*test2;
    test3.save(_result);
    Lat3 test3check;
    test3check.load(_result);
    BOOST_CHECK( test3 == test3check );

}

BOOST_AUTO_TEST_CASE( DenseLatticeMultTests )
{

    multwork<double,double,double>("data/multtest_ddd.bin");
    multwork<float,float,double>("data/multtest_dff.bin");
    multwork<float,float,float>("data/multtest_fff.bin");
    multwork<double,double,float>("data/multtest_ddf.bin");
    multwork<int32_t,int32_t,int32_t>("data/multtest_iii.bin");
    multwork<int64_t,int64_t,int64_t>("data/multtest_lll.bin");

}


// -*- Mode: c++; c-basic-offset: 4 -*-

// This C++ script reads a CFN with enumerated variables and table cost
// functions only and computes a possibly improved starting upper
// bound (the sum of all the maximum finite costs in tables where
// finite means less than the initially provided upper bound).
// Author: George Katsirelos

#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <string>
#include <tuple>
#include <map>
#include <unordered_map>
#include <boost/format.hpp>

using namespace std;
using boost::format;

const bool debug = false;

template<typename T>
ostream& operator<<(ostream& os, vector<T> const& v)
{
    os << "[";
    for(auto& t : v)
        os << t << ' ';
    os << "]";
    return os;
}

typedef long long Cost;

struct wcsptuple {
    vector<size_t> tup;
    Cost cost;
};

struct wcspfunc {
    Cost defcost;
    vector<size_t> scope;
    vector< wcsptuple > specs;

    size_t arity() const { return scope.size(); }
};

struct wcsp {
    string name;
    Cost ub;

    vector<size_t> domains;
    size_t nvars() const { return domains.size(); }

    vector<wcspfunc> functions;
};

template<typename T>
vector<T> read_vec(istream& is)
{
    vector<T> r;
    T s;
    is >> s;
    while(is) {
        r.push_back(s);
        is >> s;
    }
    return r;
}

template<typename T>
vector<T> read_vec(string const& line)
{
    istringstream iss(line);
    return read_vec<T>(iss);
}

tuple<string, size_t, size_t, size_t, Cost> read_header(string const& line)
{
    istringstream iss(line);

    string name;
    size_t nvars;
    size_t domsize;
    size_t nfun;
    Cost ub;

    iss >> name >> nvars >> domsize >> nfun >> ub;
    return make_tuple(name, nvars, domsize, nfun, ub);
}

wcspfunc read_fun(istream& is)
{
    string line;
    getline(is, line);
    vector<Cost> hd = read_vec<Cost>(line);
    size_t arity = hd[0];
    Cost defcost = hd[hd.size()-2];
    size_t nspec = hd[hd.size()-1];

    vector<wcsptuple> specs;
    for(size_t i = 0; i != nspec; ++i) {
        getline(is, line);
        vector<Cost> v = read_vec<Cost>(line);
        specs.push_back( {vector<size_t>(v.begin(), v.begin()+arity), v[v.size()-1]} );
    }

    return { defcost, vector<size_t>(hd.begin()+1, hd.begin()+1+arity),
            specs };
}

wcsp readwcsp(istream& is)
{
    wcsp w;

    size_t nvars;
    size_t domsize;
    size_t nfun;

    string line;

    getline(is, line);
    tie(w.name, nvars, domsize, nfun, w.ub) = read_header(line);

    getline(is, line);
    w.domains = read_vec<size_t>(line);

    for(size_t i = 0; i != nfun; ++i)
        w.functions.push_back(read_fun(is));

    return w;
}

void write_wcsp(wcsp const& w, ostream &ofs)
{
    size_t maxd = *max_element(w.domains.begin(), w.domains.end());
    ofs << w.name << ' ' << w.nvars()
        << ' ' << maxd
        << ' ' << w.functions.size() << ' ' << w.ub << "\n";

    for(auto& d : w.domains)
        ofs << d << ' ';
    ofs << "\n";

    for(auto& f: w.functions) {
        ofs << f.arity() << ' ';
        for(auto& v : f.scope)
            ofs << v << ' ';
        ofs << f.defcost << ' ' << f.specs.size() << "\n";
        for(auto& s : f.specs) {
            for(auto& v : s.tup)
                ofs << v << ' ';
            ofs << min(s.cost, w.ub) << "\n";
        }
    }
}

int main(int argc, char* argv[])
{
    if( argc != 3 ) {
        cout << "usage: " << argv[0] << " <wcsp-input> <wcnf-output>\n";
        return 1;
    }

    ifstream ifs(argv[1]);
    ofstream ofs(argv[2]);

    if( !ifs ) {
        cout << "could not open " << argv[1] << "\n";
        return 1;
    }

    if( !ofs ) {
        cout << "could not open " << argv[2] << "\n";
        return 1;
    }

    wcsp w = readwcsp(ifs);
    //cout << "initial top " << w.ub << "\n";
    Cost newtop = 0;
    for(auto const& f : w.functions) {
        auto me = std::max_element(f.specs.begin(), f.specs.end(),
                                   [&](wcsptuple const& m, wcsptuple const& c){
                                       return c.cost < w.ub &&
                                       (c.cost > m.cost ||
                                        m.cost >= w.ub);
                                   });
        //cout << "max of function " << f.scope << ": " << me->cost << "\n";
        newtop += me->cost;
    }
    cout << "new top " << newtop << "\n";
    w.ub = newtop;
    write_wcsp(w, ofs);

    return 0;
}


#define BOOST_TEST_MODULE vector

#include <boost/test/unit_test.hpp>
#include <boost/test/test_case_template.hpp>
#include <boost/mpl/list.hpp>

#include <mla/vector/all.h++>
#include <mla/vector/convert.h++>

typedef boost::mpl::list<
	mla::vector::Dense<float>,
	mla::vector::Dense<double>,
	mla::vector::SparseCS<float>,
	mla::vector::SparseCS<double>
> vector_type_list;

using VectorTypeFrom = mla::vector::Dense<float>;

BOOST_AUTO_TEST_SUITE(vector)

BOOST_AUTO_TEST_CASE_TEMPLATE( convert, VectorTypeTo, vector_type_list )
{
	size_t vector_size = 6;

	//VectorTypeFrom from(vector_size);	// g++ throws segfault
	mla::vector::Dense<float> from(vector_size);

	from.setValue(1, 1.0f);
	from.setValue(3, 3.0f);
	from.setValue(5, 5.0f);

	VectorTypeTo to(vector_size);

	mla::vector::convert(from, to);

	BOOST_CHECK_EQUAL(from.size(), to.size());

	for(size_t i = 0; i < vector_size; i++)
	{
		BOOST_CHECK_EQUAL(from.getValue(i), to.getValue(i));
	}
}

BOOST_AUTO_TEST_SUITE_END()


#include <bio/random.h>
USING_BIO_NS

#include <boost/test/unit_test.hpp>
using namespace boost;
using boost::unit_test::test_suite;

#include <iostream>
using namespace std;

void
check_random()
{
	cout << "******* check_random()" << endl;

	//make sure that we can seed the random numbers at will
	seed_default_rng(1234);
	BOOST_CHECK_EQUAL(get_uniform_index(5), 3u);
	BOOST_CHECK_EQUAL(get_uniform_index(5), 4u);
	BOOST_CHECK_EQUAL(get_uniform_index(5), 3u);
	BOOST_CHECK_EQUAL(get_uniform_index(5), 3u);
	BOOST_CHECK_EQUAL(get_uniform_index(5), 4u);

	//make sure our random values are within range - check a few times
	for (size_t i = 1; i < 1000; ++i)
	{
		BIO_NS::float_t rnd_01 = BIO_NS::float_t(get_uniform_01());
		BOOST_CHECK(0.0 <= rnd_01);
		BOOST_CHECK(rnd_01 <= 1.0);
	}

	//do tests over various ranges
	for (size_t max = 1; max < 100; ++max)
	{
		//check we hit every number in the range - this _could_ take a long time
		for (size_t num = 0; max != num; ++num)
		{
			size_t rnd_idx;
			while (num != (rnd_idx = get_uniform_index(max)))
			{
				//check we are in the right range
				BOOST_CHECK(rnd_idx < max);
			}
		}
	}
}

void register_random_tests(test_suite * test)
{
	test->add(BOOST_TEST_CASE(&check_random), 0);
}


#include <benchmark.hpp>
#include <mpi.hpp>

#include <boost/container/vector.hpp>
#include <mpi.h>

#include <algorithm>
#include <cmath>
#include <iostream>
#include <random>
#include <stdexcept>

namespace
{

static constexpr std::size_t ITERATIONS_COUNT = 100;

namespace bc = boost::container;

struct Data
{
    bc::vector<double> a;
    bc::vector<double> b;
};

Data generate_data(std::size_t size)
{
    Data data;
    if (my::mpi::is_current_process_root())
    {
        data.a.resize(size, bc::default_init_t{});
        data.b.resize(size, bc::default_init_t{});

        auto generator = []()
        {
            static std::mt19937 prng(std::random_device{}());
            static std::uniform_real_distribution<double> dist(-1000, 1000);
            return dist(prng);
        };

        std::generate(data.a.data(), data.a.data() + size, generator);
        std::generate(data.b.data(), data.b.data() + size, generator);
    }
    return data;
}

double dot_product_regular(const double* a, const double* b, std::size_t size)
{
    double dot_product = 0;
    for (std::size_t i = 0; i < size; ++i)
    {
        dot_product += a[i] * b[i];
    }
    return dot_product;
}

double dot_product_mpi(const double* a, const double* b, std::size_t size)
{
    const auto& mpi_params = my::mpi::Params::get_instance();

    auto get_offset = [&mpi_params](std::size_t index, std::size_t size) -> std::size_t
    {
        std::size_t offset;
        std::size_t quotient = size / mpi_params.process_count();
        std::size_t remainder = size % mpi_params.process_count();

        if (index <= remainder)
        {
            offset = (quotient + 1) * index;
        }
        else
        {
            offset = (quotient + 1) * remainder;
            offset += quotient * (index - remainder);
        }

        return offset;
    };

    auto get_count = [&mpi_params](std::size_t index, std::size_t size) -> std::size_t
    {
        std::size_t count;
        std::size_t quotient  = size / mpi_params.process_count();
        std::size_t remainder = size % mpi_params.process_count();

        count = quotient + (index < remainder ? 1 : 0);

        return count;
    };

    bc::vector<int> counts, offsets;

    if (my::mpi::is_current_process_root())
    {
        counts.resize (mpi_params.process_count(), bc::default_init_t{});
        offsets.resize(mpi_params.process_count(), bc::default_init_t{});

        for (std::size_t i = 0; i < mpi_params.process_count(); ++i)
        {
            offsets[i] = static_cast<int>(get_offset(i, size));
            counts[i]  = static_cast<int>(get_count(i, size));
        }
    }

    std::size_t size_part = get_count(mpi_params.process_id(), size);
    bc::vector<double> a_part(size_part, bc::default_init_t{});
    bc::vector<double> b_part(size_part, bc::default_init_t{});

    my::mpi::scatterv(a, counts.data(), offsets.data(), MPI_DOUBLE, a_part.data(), static_cast<int>(size_part), MPI_DOUBLE);
    my::mpi::scatterv(b, counts.data(), offsets.data(), MPI_DOUBLE, b_part.data(), static_cast<int>(size_part), MPI_DOUBLE);

    double dot_product_part = 0;
    for (std::size_t i = 0; i < size_part; ++i)
    {
        dot_product_part += a_part[i] * b_part[i];
    }

    double dot_product = 0;
    my::mpi::reduce(&dot_product_part, &dot_product, 1, MPI_DOUBLE, MPI_SUM);

    return dot_product;
}

void benchmark(const double* a, const double* b, std::size_t size)
{
    if (my::mpi::is_current_process_root())
    {
        auto dot_product_regular_wrapper = [a, b, size]()
        {
            return dot_product_regular(a, b, size);
        };
        double dot_product_regular_result = my::benchmark_function(dot_product_regular_wrapper, ITERATIONS_COUNT);
        my::print_result("Regular time: ", dot_product_regular_result);
    }

    double dot_product_mpi_result;
    {
        auto dot_product_mpi_wrapper = [a, b, size]()
        {
            return dot_product_mpi(a, b, size);
        };
        dot_product_mpi_result = my::benchmark_function(dot_product_mpi_wrapper, ITERATIONS_COUNT);
    }
    if (my::mpi::is_current_process_root())
    {
        my::print_result("    MPI time: ", dot_product_mpi_result);
    }
}

void test(const double* a, const double* b, std::size_t size)
{
    auto are_doubles_equal = [](double a, double b) -> bool
    {
        static constexpr double accuracy = 1e-3;
        return std::abs(a - b) < accuracy;
    };

    double result_mpi = dot_product_mpi(a, b, size);

    if (my::mpi::is_current_process_root())
    {
        double result_regular = dot_product_regular(a, b, size);
        if (!are_doubles_equal(result_regular, result_mpi))
        {
            throw std::runtime_error("Test failed!");
        }
        std::cout << "Test passed" << std::endl;
    }
}

}  // namespace

int main(int argc, char* argv[]) try
{
    my::mpi::Control mpi_control(argc, argv);

    const auto& mpi_params = my::mpi::Params::get_instance();
    static constexpr int size = 1 << 23;

    if (size < mpi_params.process_count())
    {
        throw std::runtime_error("Vector length is less than processor count, please decrease number of processors.");
    }

    auto [a, b] = generate_data(size);

    bool do_test = false;

    if (do_test)
        test(a.data(), b.data(), size);
    else
        benchmark(a.data(), b.data(), size);

    return EXIT_SUCCESS;
}
catch (const std::exception& e)
{
    std::cerr << "Exception caught: " << e.what() << std::endl;
    return EXIT_FAILURE;
}
catch (...)
{
    std::cerr << "An unknown exception caught" << std::endl;
    return EXIT_FAILURE;
}


// Copyright (C) 2018-2020 Chris Richardson (chris@bpi.cam.ac.uk)
// SPDX-License-Identifier:    MIT

#include "CreateA.h"
#include <Eigen/Sparse>
#include <memory>
#include <set>
#include <spmv/L2GMap.h>

//-----------------------------------------------------------------------------
// Divide size into N ~equal chunks
std::vector<std::int64_t> owner_ranges(std::int64_t size, std::int64_t N)
{
  // Compute number of items per process and remainder
  const std::int64_t n = N / size;
  const std::int64_t r = N % size;

  // Compute local range
  std::vector<std::int64_t> ranges;
  for (int rank = 0; rank < (size + 1); ++rank) {
    if (rank < r)
      ranges.push_back(rank * (n + 1));
    else
      ranges.push_back(rank * n + r);
  }

  return ranges;
}
//-----------------------------------------------------------------------------
spmv::Matrix<double> create_A(MPI_Comm comm, int N)
{
  int mpi_rank;
  MPI_Comm_rank(comm, &mpi_rank);
  int mpi_size;
  MPI_Comm_size(comm, &mpi_size);

  // Make a square Matrix divided evenly across cores
  std::vector<std::int64_t> ranges = owner_ranges(mpi_size, N);

  std::int64_t r0 = ranges[mpi_rank];
  std::int64_t r1 = ranges[mpi_rank + 1];
  int M = r1 - r0;

  // Local part of the matrix
  // Must be RowMajor and compressed
  Eigen::SparseMatrix<double, Eigen::RowMajor> A(M, N);

  // Set up A
  // Add entries on all local rows
  // Using [local_row, global_column] indexing
  double gamma = 0.1;
  for (int i = 0; i < M; ++i) {
    // Global column diagonal index
    int c0 = r0 + i;
    // Special case for very first and last global rows
    if (c0 == 0) {
      A.insert(i, c0) = 1.0 - gamma;
      A.insert(i, c0 + 1) = gamma;
    } else if (c0 == (N - 1)) {
      A.insert(i, c0 - 1) = gamma;
      A.insert(i, c0) = 1.0 - gamma;
    } else {
      A.insert(i, c0 - 1) = gamma;
      A.insert(i, c0) = 1.0 - 2.0 * gamma;
      A.insert(i, c0 + 1) = gamma;
    }
  }
  A.makeCompressed();

  // Remap columns to local indexing
  std::set<std::int64_t> ghost_indices;
  std::int32_t nnz = A.outerIndexPtr()[M];
  for (std::int32_t i = 0; i < nnz; ++i) {
    std::int32_t global_index = A.innerIndexPtr()[i];
    if (global_index < r0 or global_index >= r1)
      ghost_indices.insert(global_index);
  }

  std::vector<std::int64_t> ghosts(ghost_indices.begin(), ghost_indices.end());
  auto col_l2g = std::make_shared<spmv::L2GMap>(comm, M, ghosts);
  auto row_l2g
      = std::make_shared<spmv::L2GMap>(comm, M, std::vector<std::int64_t>());

  // Rebuild A using local indices
  auto Alocal = std::make_shared<Eigen::SparseMatrix<double, Eigen::RowMajor>>(
      M, M + ghosts.size());
  std::vector<Eigen::Triplet<double>> vals;
  std::int32_t* Aouter = A.outerIndexPtr();
  std::int32_t* Ainner = A.innerIndexPtr();
  double* Aval = A.valuePtr();

  for (std::int32_t row = 0; row < M; ++row) {
    for (std::int32_t j = Aouter[row]; j < Aouter[row + 1]; ++j) {
      std::int32_t col = col_l2g->global_to_local(Ainner[j]);
      double val = Aval[j];
      vals.push_back(Eigen::Triplet<double>(row, col, val));
    }
  }
  Alocal->setFromTriplets(vals.begin(), vals.end());

  return spmv::Matrix<double>(Alocal, col_l2g, row_l2g);
}
//-----------------------------------------------------------------------------


/*=========================================================================

  Program:   Visualization Toolkit
  Module:    TestBoostAlgorithms.cxx

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
/*-------------------------------------------------------------------------
  Copyright 2008 Sandia Corporation.
  Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
  the U.S. Government retains certain rights in this software.
-------------------------------------------------------------------------*/
#include "vtkActor.h"
#include "vtkBoostBrandesCentrality.h"
#include "vtkBoostBreadthFirstSearch.h"
#include "vtkBoostBreadthFirstSearchTree.h"
#include "vtkBoostConnectedComponents.h"
#include "vtkDataSetAttributes.h"
#include "vtkFloatArray.h"
#include "vtkGlyph3D.h"
#include "vtkGlyphSource2D.h"
#include "vtkGraphLayoutView.h"
#include "vtkGraphToPolyData.h"
#include "vtkGraphWriter.h"
#include "vtkTransform.h"
#include "vtkMatrix4x4.h"
#include "vtkMutableUndirectedGraph.h"
#include "vtkPolyDataMapper.h"
#include "vtkProperty.h"
#include "vtkRegressionTestImage.h"
#include "vtkRenderer.h"
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkSmartPointer.h"

#include <boost/version.hpp>
#include "vtkBoostBiconnectedComponents.h"

#define VTK_CREATE(type,name) \
  vtkSmartPointer<type> name = vtkSmartPointer<type>::New()

int TestBoostBrandesCentrality(int argc, char* argv[])
{
  // Create the test graph
  VTK_CREATE(vtkMutableUndirectedGraph, g);

  VTK_CREATE(vtkMatrix4x4, mat1);
  mat1->SetElement(1,3, 5);
  VTK_CREATE(vtkTransform, transform1);
  transform1->SetMatrix(mat1);

  VTK_CREATE(vtkMatrix4x4, mat2);
  mat2->SetElement(1,3, 0);
  VTK_CREATE(vtkTransform, transform2);
  transform2->SetMatrix(mat2);

  VTK_CREATE(vtkFloatArray, weights);
  weights->SetName("weights");
  g->GetEdgeData()->AddArray(weights);

  VTK_CREATE(vtkPoints, pts);
  g->AddVertex();
  pts->InsertNextPoint(1, 1, 0);
  g->AddVertex();
  pts->InsertNextPoint(1, 0, 0);
  g->AddVertex();
  pts->InsertNextPoint(1, -1, 0);
  g->AddVertex();
  pts->InsertNextPoint(2, 0, 0);
  g->AddVertex();
  pts->InsertNextPoint(3, 0, 0);
  g->AddVertex();
  pts->InsertNextPoint(2.5, 1, 0);
  g->AddVertex();
  pts->InsertNextPoint(4, 1, 0);
  g->AddVertex();
  pts->InsertNextPoint(4, 0, 0);
  g->AddVertex();
  pts->InsertNextPoint(4, -1, 0);

  g->SetPoints(pts);

  vtkEdgeType e = g->AddEdge(0, 3);
  weights->InsertTuple1(e.Id, 10);

  e = g->AddEdge(1, 3);
  weights->InsertTuple1(e.Id, 10);

  e = g->AddEdge(2, 3);
  weights->InsertTuple1(e.Id, 10);

  e = g->AddEdge(3, 4);
  weights->InsertTuple1(e.Id, 1);

  e = g->AddEdge(3, 5);
  weights->InsertTuple1(e.Id, 10);

  e = g->AddEdge(5, 4);
  weights->InsertTuple1(e.Id, 10);

  e = g->AddEdge(6, 4);
  weights->InsertTuple1(e.Id, 10);

  e = g->AddEdge(7, 4);
  weights->InsertTuple1(e.Id, 10);

  e = g->AddEdge(8, 4);
  weights->InsertTuple1(e.Id, 10);

  // Test centrality
  VTK_CREATE(vtkBoostBrandesCentrality, centrality);
  centrality->SetInputData(g);
  centrality->SetEdgeWeightArrayName("weights");
  centrality->SetInvertEdgeWeightArray(1);
  centrality->UseEdgeWeightArrayOn();

  VTK_CREATE(vtkGraphLayoutView, view);
  view->SetLayoutStrategyToPassThrough();
  view->SetRepresentationFromInputConnection(centrality->GetOutputPort());
  view->ResetCamera();
  view->SetColorVertices(1);
  view->SetVertexColorArrayName("centrality");
  view->SetColorEdges(1);
  view->SetEdgeColorArrayName("centrality");

  int retVal = vtkRegressionTestImage(view->GetRenderWindow());
  if (retVal == vtkRegressionTester::DO_INTERACTOR)
  {
    view->GetInteractor()->Initialize();
    view->GetInteractor()->Start();
    retVal = vtkRegressionTester::PASSED;
  }

  return !retVal;
}


/*!
 * \file
 * \author Nikos Tsakiridis <tsakirin@auth.gr>
 * \version 1.0
 *
 * \brief Implementation of a random engine
 */

#ifndef RANDOM_ENGINE
#define RANDOM_ENGINE

#include <boost/random/uniform_real_distribution.hpp>
#include <boost/random/uniform_int_distribution.hpp>
#include <boost/random/normal_distribution.hpp>
#include <boost/random/cauchy_distribution.hpp>
#include <boost/random/mersenne_twister.hpp>
#include <assert.h>

extern size_t SEED; /*!< The seed must be defined externally */

/*!
 * \brief Generate a random float between (min, max) from a uniform distribution
 *
 * \param min : lower value
 * \param max : upper value
 *
 * \return A random double within (min, max)
 */

inline double rand_uniform_real(const double min, const double max) {
  assert(min < max);
  thread_local boost::random::mt19937 generator(SEED);
  boost::random::uniform_real_distribution<> dist(min, max);
  return dist(generator);
}

/**
 * \brief Generate a random int within [min, max] from a uniform distribution
 *
 * \param min : lower value
 * \param max : upper value
 *
 * \return A random integer within [min, max]
 */

inline int rand_uniform_int(const float min, const float max) {
  assert(min < max);
  thread_local boost::random::mt19937 generator(SEED);
  boost::random::uniform_int_distribution<> dist(min, max);
  return dist(generator);
}

/*!
 * \brief Generate a random float from a normal distribution
 *
 * \param mean  : mean value of the normal distribution
 * \param sigma : standard deviation of the normal distribution
 *
 * \return A random double
 */

inline double rand_normal(const double mean, const double sigma) {
  thread_local boost::random::mt19937 generator(SEED);
  boost::random::normal_distribution<> dist(mean, sigma);
  return dist(generator);
}

/*!
 * \brief Generate a random float from a Cauchy distribution
 *
 * \param mean  : mean value of the Cauchy distribution
 * \param sigma : standard deviation of the Cauchy distribution
 *
 * \return A random double
 */

inline double rand_cauchy(const double mean, const double sigma) {
  thread_local boost::random::mt19937 generator(SEED);
  boost::random::normal_distribution<> dist(mean, sigma);
  return dist(generator);
}

#endif  // RANDOM_ENGINE


/*
Copyright (c) 2013 Daniel Stahlke

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

#include <vector>
#include <math.h>
// This must be included before gnuplot-iostream.h in order to support plotting blitz arrays.
#include <blitz/array.h>

#include "gnuplot-iostream.h"

// Yes, I'm including a *.cc file.  It contains main().
#include "examples-framework.cc"

void demo_blitz_basic() {
	// -persist option makes the window not disappear when your program exits
	Gnuplot gp("gnuplot -persist");

	blitz::Array<double, 2> arr(100, 100);
	{
		blitz::firstIndex i;
		blitz::secondIndex j;
		arr = (i-50) * (j-50);
	}
	gp << "set pm3d map; set palette" << std::endl;
	gp << "splot '-'" << std::endl;
	gp.send(arr);
}

void demo_blitz_waves_binary() {
	Gnuplot gp("gnuplot -persist");

	// example from Blitz manual:
	int N = 64, cycles = 3;
	double midpoint = (N-1)/2.;
	double omega = 2.0 * M_PI * cycles / double(N);
	double tau = - 10.0 / N;
	blitz::Array<double, 2> F(N, N);
	blitz::firstIndex i;
	blitz::secondIndex j;
	F = cos(omega * sqrt(pow2(i-midpoint) + pow2(j-midpoint)))
		* exp(tau * sqrt(pow2(i-midpoint) + pow2(j-midpoint)));

	gp << "splot '-' binary" << gp.binfmt(F) << "dx=10 dy=10 origin=(5,5,0) with pm3d notitle" << std::endl;
	gp.sendBinary(F);
}

void demo_blitz_sierpinski_binary() {
	Gnuplot gp("gnuplot -persist");

	int N = 256;
	blitz::Array<blitz::TinyVector<uint8_t, 4>, 2> F(N, N);
	for(int i=0; i<N; i++)
	for(int j=0; j<N; j++) {
		F(i, j)[0] = i;
		F(i, j)[1] = j;
		F(i, j)[2] = 0;
		F(i, j)[3] = (i&j) ? 0 : 255;
	}

	gp << "plot '-' binary" << gp.binfmt(F) << "with rgbalpha notitle" << std::endl;
	gp.sendBinary(F);
}

void demo_blitz_waves_binary_file() {
	Gnuplot gp("gnuplot -persist");

	// example from Blitz manual:
	int N = 64, cycles = 3;
	double midpoint = (N-1)/2.;
	double omega = 2.0 * M_PI * cycles / double(N);
	double tau = - 10.0 / N;
	blitz::Array<double, 2> F(N, N);
	blitz::firstIndex i;
	blitz::secondIndex j;
	F = cos(omega * sqrt(pow2(i-midpoint) + pow2(j-midpoint)))
		* exp(tau * sqrt(pow2(i-midpoint) + pow2(j-midpoint)));

	gp << "splot" << gp.binaryFile(F) << "dx=10 dy=10 origin=(5,5,0) with pm3d notitle" << std::endl;
}

void demo_blitz_sierpinski_binary_file() {
	Gnuplot gp("gnuplot -persist");

	int N = 256;
	blitz::Array<blitz::TinyVector<uint8_t, 4>, 2> F(N, N);
	for(int i=0; i<N; i++)
	for(int j=0; j<N; j++) {
		F(i, j)[0] = i;
		F(i, j)[1] = j;
		F(i, j)[2] = 0;
		F(i, j)[3] = (i&j) ? 0 : 255;
	}

	gp << "plot" << gp.binaryFile(F) << "with rgbalpha notitle" << std::endl;
}

void register_demos() {
	register_demo("basic",                        demo_blitz_basic);
	register_demo("waves_binary",           demo_blitz_waves_binary);
	register_demo("sierpinski_binary",      demo_blitz_sierpinski_binary);
	register_demo("waves_binary_file",      demo_blitz_waves_binary_file);
	register_demo("sierpinski_binary_file", demo_blitz_sierpinski_binary_file);
}


/* Boost numeric test for orders of quadrature formulas test file

 Copyright 2015 Gregor de Cillia
 Copyright 2015 Mario Mulansky <mario.mulansky@gmx.net>

 Distributed under the Boost Software License, Version 1.0.
 (See accompanying file LICENSE_1_0.txt or
 copy at http://www.boost.org/LICENSE_1_0.txt)
*/

// disable checked iterator warning for msvc
#include <boost/config.hpp>

#ifdef BOOST_MSVC
    #pragma warning(disable:4996)
#endif

#define BOOST_TEST_MODULE order_quadrature_formula

#include <iostream>
#include <cmath>

#include <boost/test/unit_test.hpp>

#include <boost/mpl/vector.hpp>

#include <boost/numeric/odeint.hpp>

#include <boost/numeric/ublas/vector.hpp>

using namespace boost::unit_test;
using namespace boost::numeric::odeint;
namespace mpl = boost::mpl;

typedef double value_type;
typedef value_type time_type;
typedef value_type state_type;

BOOST_AUTO_TEST_SUITE( order_of_convergence_test )

/* defines the simple monomial f(t) = (p+1) * t^p.*/
struct monomial
{
    int power;

    monomial(int p = 0) : power( p ){};

    void operator()( const state_type &x , state_type &dxdt , const time_type t )
    {
        dxdt = ( 1.0 + power ) * pow( t, power );
    }
};

/* generic test for all steppers that support integrate_const */
template< class Stepper >
struct stepper_order_test
{
    void operator()( int steps = 1 )
    {
        const int estimated_order = estimate_order( steps );
        const int defined_order = Stepper::order_value;

        std::cout << boost::format( "%-20i%-20i\n" )
	    % estimated_order %  defined_order;

        BOOST_REQUIRE_EQUAL( estimated_order, defined_order );
    }

    /*
    the order of the stepper is estimated by trying to solve the ODE
    x'(t) = (p+1) * t^p
    until the errors are too big to be justified by finite precision.
    the first value p for which the problem is *not* solved within the
    finite precision tolerance is the estimate for the order of the scheme.
     */
    int estimate_order( int steps )
    {
        const double dt = 1.0/steps;
        const double tolerance = steps*1E-15;
        int p;
        for( p = 0; true; p++ )
        {
            // begin with x'(t) = t^0 = 1
            //         => x (t) = t
            // then use   x'(t) = 2*t^1
            //         => x (t) = t^2
            // ...
            state_type x = 0.0;

            double t = integrate_n_steps( Stepper(), monomial( p ), x, 0.0, dt,
                                          steps );
            if( fabs( x - pow( t, ( 1.0 + p ) ) ) > tolerance )
                break;
        }
        // the smallest power p for which the test failed is the estimated order,
        // as the solution for this power is x(t) = t^{p+1}
        return p;
    }
};

typedef mpl::vector<
    euler< state_type > ,
    modified_midpoint< state_type > ,
    runge_kutta4< state_type > ,
    runge_kutta4_classic< state_type > ,
    runge_kutta_cash_karp54_classic< state_type > ,
    runge_kutta_cash_karp54< state_type > ,
    runge_kutta_dopri5< state_type > ,
    runge_kutta_fehlberg78< state_type >
    > runge_kutta_steppers;

typedef mpl::vector<
    adams_bashforth< 2, state_type, double, state_type, double,
                     vector_space_algebra, default_operations,
                     initially_resizer, runge_kutta_fehlberg78< state_type > >,
    adams_bashforth< 3, state_type, double, state_type, double,
                     vector_space_algebra, default_operations,
                     initially_resizer, runge_kutta_fehlberg78< state_type > >,
    adams_bashforth< 4, state_type, double, state_type, double,
                     vector_space_algebra, default_operations,
                     initially_resizer, runge_kutta_fehlberg78< state_type > >,
    adams_bashforth< 5, state_type, double, state_type, double,
                     vector_space_algebra, default_operations,
                     initially_resizer, runge_kutta_fehlberg78< state_type > >,
    adams_bashforth< 6, state_type, double, state_type, double,
                     vector_space_algebra, default_operations,
                     initially_resizer, runge_kutta_fehlberg78< state_type > >,
    adams_bashforth< 7, state_type, double, state_type, double,
                     vector_space_algebra, default_operations,
                     initially_resizer, runge_kutta_fehlberg78< state_type > >,
    adams_bashforth< 8, state_type, double, state_type, double,
                     vector_space_algebra, default_operations,
                     initially_resizer, runge_kutta_fehlberg78< state_type > >
    > ab_steppers;

typedef mpl::vector<
    adams_bashforth_moulton< 2, state_type, double, state_type, double,
                             vector_space_algebra, default_operations,
                             initially_resizer,
                             runge_kutta_fehlberg78< state_type > >,
    adams_bashforth_moulton< 3, state_type, double, state_type, double,
                             vector_space_algebra, default_operations,
                             initially_resizer,
                             runge_kutta_fehlberg78< state_type > >,
    adams_bashforth_moulton< 4, state_type, double, state_type, double,
                             vector_space_algebra, default_operations,
                             initially_resizer,
                             runge_kutta_fehlberg78< state_type > >,
    adams_bashforth_moulton< 5, state_type, double, state_type, double,
                             vector_space_algebra, default_operations,
                             initially_resizer,
                             runge_kutta_fehlberg78< state_type > >,
    adams_bashforth_moulton< 6, state_type, double, state_type, double,
                             vector_space_algebra, default_operations,
                             initially_resizer,
                             runge_kutta_fehlberg78< state_type > >,
    adams_bashforth_moulton< 7, state_type, double, state_type, double,
                             vector_space_algebra, default_operations,
                             initially_resizer,
                             runge_kutta_fehlberg78< state_type > >,
    adams_bashforth_moulton< 8, state_type, double, state_type, double,
                             vector_space_algebra, default_operations,
                             initially_resizer,
                             runge_kutta_fehlberg78< state_type > >
    > abm_steppers;

BOOST_AUTO_TEST_CASE_TEMPLATE( runge_kutta_test , Stepper, runge_kutta_steppers )
{
    stepper_order_test< Stepper > tester;
    tester(10);
}

BOOST_AUTO_TEST_CASE_TEMPLATE( adams_bashforth_test , Stepper, ab_steppers )
{
    stepper_order_test< Stepper > tester;
    tester(16);
}

BOOST_AUTO_TEST_CASE_TEMPLATE( adams_bashforth_moultion_test , Stepper, abm_steppers )
{
    stepper_order_test< Stepper > tester;
    tester(16);
}

BOOST_AUTO_TEST_SUITE_END()