#include "advent.hpp"

#include <iostream>
#include <fstream>
#include <robin_hood.h>
#include <vector>
#include <string>
#include <fmt/ranges.h>

#include <Eigen/Core>

using std::pair;
using std::ifstream;
using std::string;
using std::vector;

template<typename K, typename V>
using flat_map = robin_hood::unordered_flat_map<K, V>;

template<typename K>
using flat_set = robin_hood::unordered_flat_set<K>;

auto day14(int argc, char** argv) -> int
{
    if (argc < 2) {
        fmt::print("Error: no input.");
        return 1;
    }

    ifstream infile(argv[1]); // NOLINT
    string line;

    flat_map<std::string, char> rules;

    std::getline(infile, line);
    auto t = line; // the polymer template

    vector<string> tokens;
    while (std::getline(infile, line)) {
        if (line.empty()) { continue; }
        tokens.clear();
        util::tokenize(line, ' ', tokens);
        ENSURE(!tokens[0].empty());
        rules[tokens[0]] = tokens[2][0];
    }

    constexpr i64 alphabet_size = 26;
    auto idx = [](char c) { return static_cast<i64>(std::tolower(c) - 'a'); };
    constexpr i64 steps{40};

    Eigen::Array<i64, alphabet_size, alphabet_size> cnt;
    cnt.fill(0);
    for (auto i = 0UL; i < std::ssize(t) - 1; ++i) {
        auto a = idx(t[i]);
        auto b = idx(t[i+1]);
        ++cnt(a, b);
    }

    for (i64 step = 0; step < steps; ++step) {
        auto tmp = cnt;
        for (auto const& [k, v] : rules) {
            auto a = idx(k[0]);
            auto b = idx(k[1]);

            if (cnt(a, b) > 0) {
                auto c = idx(rules[k]);
                tmp(a, c) += cnt(a, b);
                tmp(c, b) += cnt(a, b);
                tmp(a, b) -= cnt(a, b);
            }
        }
        std::swap(cnt, tmp);
    }
    i64 min_count{std::numeric_limits<i64>::max()};
    i64 max_count{0};
    i64 most_common{0};
    i64 least_common{0};
    for (i64 i = 0; i != cnt.rows(); ++i) {
        auto sum = cnt.col(i).sum();
        if (sum > 0) {
            if (sum == cnt(i, i)) {
                sum += 1; // add 1 when the string is just one single repeated letter
            }
            if (min_count > sum) {
                min_count = sum;
                least_common = i;
            }
            if (max_count < sum) {
                max_count = sum;
                most_common = i;
            }
        }
    }
    fmt::print("least common: {:c}, most common: {:c}\n", static_cast<char>(least_common) + 'a', static_cast<char>(most_common) + 'a');
    fmt::print("min: {} ({}), max: {} ({})\n", min_count, cnt(least_common, least_common), max_count, cnt(most_common, most_common));
    fmt::print("string length: {}\n", cnt.sum() + 1);
    fmt::print("part 2: {}\n", max_count - min_count);

    return 0;
}


#include "ExperimentDataUtils.hpp"
#include "dakota_global_defs.hpp"
#include "DataMethod.hpp"
#include "DakotaResponse.hpp"
#include "NonDBayesCalibration.hpp"
// Boost.Test
#define BOOST_TEST_MODULE dakota_field_covariance_utils
#include <boost/test/included/unit_test.hpp>

//#include <boost/assign/std/vector.hpp>
#include <boost/foreach.hpp>

#include <cassert>
#include <iostream>

namespace Dakota {
namespace TestFieldCovariance {

void test_multiple_scalar_covariance_matrix()
{
  std::vector<RealMatrix> matrices;
  std::vector<RealVector> diagonals;
  RealVector scalars;
  IntVector matrix_map_indices, diagonal_map_indices, scalar_map_indices;

  int num_scalars = 3;
  Real scalar_array[] = {1.,2.,4.};
  int scalar_map_index_array[] = {0, 1, 2};
  scalars.sizeUninitialized( num_scalars );
  scalar_map_indices.sizeUninitialized( num_scalars );
  for ( int i=0; i<num_scalars; i++ ){
    scalars[i] = scalar_array[i];
    scalar_map_indices[i] = scalar_map_index_array[i];
  }

  ExperimentCovariance exper_cov;
  exper_cov.set_covariance_matrices( matrices, diagonals, scalars,
				     matrix_map_indices,
				     diagonal_map_indices,
				     scalar_map_indices );

  // Test determinant and log_determinant
  BOOST_CHECK_CLOSE(exper_cov.determinant(), 8.0, 1.0e-12);
  BOOST_CHECK_CLOSE(exper_cov.log_determinant(), std::log(8.0), 1.0e-12);

  int num_residuals = 3;
  Real residual_array[] = {1.,2.,4.};
  RealVector residual( Teuchos::Copy, residual_array, num_residuals );

  // Test application of the covariance inverse to residual vector
  Real prod = exper_cov.apply_experiment_covariance( residual );
  BOOST_CHECK(  std::abs( prod - 7. ) <
		10.*std::numeric_limits<double>::epsilon() );

  // Test application of the sqrt of the covariance inverse to residual vector
  RealVector result;
  exper_cov.apply_experiment_covariance_inverse_sqrt( residual, result );
  prod = result.dot( result );
  BOOST_CHECK(  std::abs( prod - 7. ) <
		10.*std::numeric_limits<double>::epsilon() );

  // Test application of the sqrt of the covariance inverse to matrix of
  // gradient vectors
  RealMatrix scaled_grads;
  Real grads_array[] = {1.,2.,2.,4.,4.,8.};
  RealMatrix grads( Teuchos::Copy, grads_array, 2, 2, 3 );
  exper_cov.apply_experiment_covariance_inverse_sqrt_to_gradients( grads,
								   scaled_grads);

  RealMatrix grammian( grads.numRows(), grads.numRows(), false );
  grammian.multiply( Teuchos::NO_TRANS, Teuchos::TRANS, 1.0, scaled_grads,
		     scaled_grads, 0. );
  BOOST_CHECK(  std::abs( grammian(0,0) - 7. ) <
		10.*std::numeric_limits<double>::epsilon() );
  BOOST_CHECK(  std::abs( grammian(1,1) - 28. ) <
		10.*std::numeric_limits<double>::epsilon() );

  // Test application of the sqrt of the covariance inverse to matrix of
  // Hessian matrices
  Real hessian_0_array[] = {4., 2., 2., 2.};
  RealSymMatrix hessian_0( Teuchos::Copy, false, hessian_0_array, 2, 2 );
  RealSymMatrix hessian_1( hessian_0 );
  RealSymMatrix hessian_2( hessian_0 );
  hessian_1 *= 2.;
  hessian_2 *= 4.;

  RealSymMatrixArray hessians( 3 );
  hessians[0] = hessian_0;
  hessians[1] = hessian_1;
  hessians[2] = hessian_2;
  RealSymMatrixArray scaled_hessians;
  exper_cov.apply_experiment_covariance_inverse_sqrt_to_hessians( hessians,
							       scaled_hessians );

  Real exact_scaled_hessian_0_array[] = {4.,2.,2.,2.};
  Real exact_scaled_hessian_1_array[] = {8./std::sqrt(2.),4./std::sqrt(2.),
				   4./std::sqrt(2.),4./std::sqrt(2.)};
  Real exact_scaled_hessian_2_array[] = {8., 4., 4., 4.};
  RealSymMatrix exact_scaled_hessian_0( Teuchos::View, false,
				  exact_scaled_hessian_0_array, 2, 2 );
  RealSymMatrix exact_scaled_hessian_1( Teuchos::View, false,
				  exact_scaled_hessian_1_array, 2, 2 );
  RealSymMatrix exact_scaled_hessian_2( Teuchos::View, false,
				  exact_scaled_hessian_2_array, 2, 2 );

  scaled_hessians[0] -= exact_scaled_hessian_0;
  scaled_hessians[1] -= exact_scaled_hessian_1;
  scaled_hessians[2] -= exact_scaled_hessian_2;

  BOOST_CHECK( scaled_hessians[0].normInf() <
	       10.*std::numeric_limits<double>::epsilon() );
  BOOST_CHECK( scaled_hessians[1].normInf() <
	       10.*std::numeric_limits<double>::epsilon() );
  BOOST_CHECK( scaled_hessians[2].normInf() <
	       10.*std::numeric_limits<double>::epsilon() );

  // Test extraction of main diagonal
  RealVector diagonal;
  exper_cov.get_main_diagonal( diagonal );

  Real exact_diagonal_array[] = {1.,2.,4.};
  RealVector exact_diagonal(Teuchos::View, exact_diagonal_array, 3 );

  exact_diagonal -= diagonal;
  BOOST_CHECK( exact_diagonal.normInf() <
	       10.*std::numeric_limits<double>::epsilon() );

}

void test_single_diagonal_block_covariance_matrix()
{
  std::vector<RealMatrix> matrices;
  std::vector<RealVector> diagonals;
  RealVector scalars;
  IntVector matrix_map_indices, diagonal_map_indices, scalar_map_indices;

  int num_diags = 1;
  int num_diag_entries = 3;
  Real diagonal_array[] = {1.,2.,4.};
  diagonal_map_indices.sizeUninitialized( num_diags );
  diagonal_map_indices[0] = 0;
  diagonals.resize( num_diags );
  diagonals[0].sizeUninitialized( num_diag_entries );
  for ( int i=0; i<num_diag_entries; i++ ){
    diagonals[0][i] = diagonal_array[i];
  }

  ExperimentCovariance exper_cov;
  exper_cov.set_covariance_matrices( matrices, diagonals, scalars,
				     matrix_map_indices,
				     diagonal_map_indices,
				     scalar_map_indices );

  // Test determinant and log_determinant
  BOOST_CHECK_CLOSE(exper_cov.determinant(), 8.0, 1.0e-12);
  BOOST_CHECK_CLOSE(exper_cov.log_determinant(), std::log(8.0), 1.0e-12);

  int num_residuals = 3;
  Real residual_array[] = {1.,2.,4.};
  RealVector residual( Teuchos::Copy, residual_array, num_residuals );

  // Test application of the covariance inverse to residual vector
  Real prod = exper_cov.apply_experiment_covariance( residual );
  BOOST_CHECK(  std::abs( prod - 7. ) <
		10.*std::numeric_limits<double>::epsilon() );

  // Test application of the sqrt of the covariance inverse to residual vector
  RealVector result;
  exper_cov.apply_experiment_covariance_inverse_sqrt( residual, result );
  prod = result.dot( result );
  BOOST_CHECK(  std::abs( prod - 7. ) <
		10.*std::numeric_limits<double>::epsilon() );

  // Test application of the sqrt of the covariance inverse to matrix of
  // gradient vectors
  RealMatrix scaled_grads;
  Real grads_array[] = {1.,2.,2.,4.,4.,8.};
  RealMatrix grads( Teuchos::Copy, grads_array, 2, 2, 3 );
  exper_cov.apply_experiment_covariance_inverse_sqrt_to_gradients( grads,
								   scaled_grads);

  RealMatrix grammian( grads.numRows(), grads.numRows(), false );
  grammian.multiply( Teuchos::NO_TRANS, Teuchos::TRANS, 1.0, scaled_grads,
		     scaled_grads, 0. );
  BOOST_CHECK(  std::abs( grammian(0,0) - 7. ) <
		10.*std::numeric_limits<double>::epsilon() );
  BOOST_CHECK(  std::abs( grammian(1,1) - 28. ) <
		10.*std::numeric_limits<double>::epsilon() );

  // Test application of the sqrt of the covariance inverse to matrix of
  // Hessian matrices
  Real hessian_0_array[] = {4., 2., 2., 2.};
  RealSymMatrix hessian_0( Teuchos::Copy, false, hessian_0_array, 2, 2 );
  RealSymMatrix hessian_1( hessian_0 );
  RealSymMatrix hessian_2( hessian_0 );
  hessian_1 *= 2.;
  hessian_2 *= 4.;

  RealSymMatrixArray hessians( 3 );
  hessians[0] = hessian_0;
  hessians[1] = hessian_1;
  hessians[2] = hessian_2;
  RealSymMatrixArray scaled_hessians;
  exper_cov.apply_experiment_covariance_inverse_sqrt_to_hessians( hessians,
							       scaled_hessians );

  Real exact_scaled_hessian_0_array[] = {4.,2.,2.,2.};
  Real exact_scaled_hessian_1_array[] = {8./std::sqrt(2.),4./std::sqrt(2.),
				   4./std::sqrt(2.),4./std::sqrt(2.)};
  Real exact_scaled_hessian_2_array[] = {8., 4., 4., 4.};
  RealSymMatrix exact_scaled_hessian_0( Teuchos::View, false,
				  exact_scaled_hessian_0_array, 2, 2 );
  RealSymMatrix exact_scaled_hessian_1( Teuchos::View, false,
				  exact_scaled_hessian_1_array, 2, 2 );
  RealSymMatrix exact_scaled_hessian_2( Teuchos::View, false,
				  exact_scaled_hessian_2_array, 2, 2 );

  scaled_hessians[0] -= exact_scaled_hessian_0;
  scaled_hessians[1] -= exact_scaled_hessian_1;
  scaled_hessians[2] -= exact_scaled_hessian_2;

  BOOST_CHECK( scaled_hessians[0].normInf() <
	       10.*std::numeric_limits<double>::epsilon() );
  BOOST_CHECK( scaled_hessians[1].normInf() <
	       10.*std::numeric_limits<double>::epsilon() );
  BOOST_CHECK( scaled_hessians[2].normInf() <
	       10.*std::numeric_limits<double>::epsilon() );
}

void test_single_full_block_covariance_matrix()
{
  std::vector<RealMatrix> matrices;
  std::vector<RealVector> diagonals;
  RealVector scalars;
  IntVector matrix_map_indices, diagonal_map_indices, scalar_map_indices;

  int num_matrices = 1;
  int num_matrix_rows = 3;
  Real matrix_array[] = {1.,0.5,0.25,0.5,2.,0.5,0.25,0.5,4.};
  matrix_map_indices.sizeUninitialized( num_matrices );
  matrix_map_indices[0] = 0;
  matrices.resize( num_matrices );
  matrices[0].shapeUninitialized( num_matrix_rows, num_matrix_rows );
  for ( int j=0; j<num_matrix_rows; j++ ){
    for ( int i=0; i<num_matrix_rows; i++ )
      matrices[0](i,j) = matrix_array[j*num_matrix_rows+i];
  }

  ExperimentCovariance exper_cov;
  exper_cov.set_covariance_matrices( matrices, diagonals, scalars,
				     matrix_map_indices,
				     diagonal_map_indices,
				     scalar_map_indices );

  // Test determinant and log_determinant
  BOOST_CHECK_CLOSE(exper_cov.determinant(), 6.75, 1.0e-12);
  BOOST_CHECK_CLOSE(exper_cov.log_determinant(), std::log(6.75), 1.0e-12);

  int num_residuals = 3;
  Real residual_array[] = {1.,2.,4.};
  RealVector residual( Teuchos::Copy, residual_array, num_residuals );

  // Test application of the covariance inverse to residual vector
  Real prod = exper_cov.apply_experiment_covariance( residual );
  BOOST_CHECK(  std::abs( prod - 16./3. ) <
		10.*std::numeric_limits<double>::epsilon() );

  // Test application of the sqrt of the covariance inverse to residual vector
  RealVector result;
  exper_cov.apply_experiment_covariance_inverse_sqrt( residual, result );
  prod = result.dot( result );
  BOOST_CHECK( std::abs( prod - 16./3. ) <
	       10.*std::numeric_limits<double>::epsilon() );

  // Test application of the sqrt of the covariance inverse to matrix of
  // gradient vectors
  RealMatrix scaled_grads;
  Real grads_array[] = {1.,2.,2.,4.,4.,8.};
  RealMatrix grads( Teuchos::Copy, grads_array, 2, 2, 3 );
  exper_cov.apply_experiment_covariance_inverse_sqrt_to_gradients( grads,
								   scaled_grads);

  RealMatrix grammian( grads.numRows(), grads.numRows(), false );
  grammian.multiply( Teuchos::NO_TRANS, Teuchos::TRANS, 1.0, scaled_grads,
		     scaled_grads, 0. );
  BOOST_CHECK(  std::abs( grammian(0,0) - 16./3. ) <
		10.*std::numeric_limits<double>::epsilon() );
  BOOST_CHECK(  std::abs( grammian(1,1) - 64./3. ) <
		20.*std::numeric_limits<double>::epsilon() );

  // Test application of the sqrt of the covariance inverse to matrix of
  // Hessian matrices
  Real hessian_0_array[] = {4., 2., 2., 2.};
  RealSymMatrix hessian_0( Teuchos::Copy, false, hessian_0_array, 2, 2 );
  RealSymMatrix hessian_1( hessian_0 );
  RealSymMatrix hessian_2( hessian_0 );
  hessian_1 *= 2.;
  hessian_2 *= 4.;

  RealSymMatrixArray hessians( 3 );
  hessians[0] = hessian_0;
  hessians[1] = hessian_1;
  hessians[2] = hessian_2;
  RealSymMatrixArray scaled_hessians;
  exper_cov.apply_experiment_covariance_inverse_sqrt_to_hessians( hessians,
							  scaled_hessians );

  Real exact_scaled_hessian_0_array[] = {4.,2.,2.,2.};
  Real exact_scaled_hessian_1_array[] = {4.53557367611073,2.26778683805536,
				   2.26778683805536,2.26778683805536};
  Real exact_scaled_hessian_2_array[] = {6.98297248755176,3.49148624377588,
				   3.49148624377588,3.49148624377588};
  RealSymMatrix exact_scaled_hessian_0( Teuchos::View, false,
				  exact_scaled_hessian_0_array, 2, 2 );
  RealSymMatrix exact_scaled_hessian_1( Teuchos::View, false,
				  exact_scaled_hessian_1_array, 2, 2 );
  RealSymMatrix exact_scaled_hessian_2( Teuchos::View, false,
				  exact_scaled_hessian_2_array, 2, 2 );

  scaled_hessians[0] -= exact_scaled_hessian_0;
  scaled_hessians[1] -= exact_scaled_hessian_1;
  scaled_hessians[2] -= exact_scaled_hessian_2;

  BOOST_CHECK( scaled_hessians[0].normInf() <
	       100.*std::numeric_limits<double>::epsilon() );
  BOOST_CHECK( scaled_hessians[1].normInf() <
	       100.*std::numeric_limits<double>::epsilon() );
  BOOST_CHECK( scaled_hessians[2].normInf() <
	       100.*std::numeric_limits<double>::epsilon() );

  // Test extraction of main diagonal
  RealVector diagonal;
  exper_cov.get_main_diagonal( diagonal );

  Real exact_diagonal_array[] = {1.,2.,4.};
  RealVector exact_diagonal(Teuchos::View, exact_diagonal_array, 3 );

  exact_diagonal -= diagonal;
  BOOST_CHECK( exact_diagonal.normInf() <
	       10.*std::numeric_limits<double>::epsilon() );

}

void test_mixed_scalar_diagonal_full_block_covariance_matrix()
{
  std::vector<RealMatrix> matrices;
  std::vector<RealVector> diagonals;
  RealVector scalars;
  IntVector matrix_map_indices, diagonal_map_indices, scalar_map_indices;

  // Experiment covariance matrix consists of the following blocks
  // scalar_1, diagonal_1, matrix_1, scalar_2, scalar_3,

  // MATLAB CODE:
  // A = [1,0.5,0.25;0.5,2,0.5;0.25,0.5,4.]; B = eye(9); B(1,1)=1.;
  // B(8,8) = 2.; B(9,9) = 4.; B(3,3) = 2.; B(4,4) = 4.; B(5:7,5:7)=A;
  // d = [1., 1., 2., 4., 1., 2., 4., 2., 4.]';
  // chol(inv(B))*d; r'*r

  // Generate scalar matrix blocks
  int num_scalars = 3;
  Real scalar_array[] = {1.,2.,4.};
  int scalar_map_index_array[] = {0, 3, 4};
  scalars.sizeUninitialized( num_scalars );
  scalar_map_indices.sizeUninitialized( num_scalars );
  for ( int i=0; i<num_scalars; i++ ){
    scalars[i] = scalar_array[i];
    scalar_map_indices[i] = scalar_map_index_array[i];
  }

  // Generate diagonal covariance matrix blocks
  int num_diags = 1;
  int num_diag_entries = 3;
  Real diagonal_array[] = {1.,2.,4.};
  diagonal_map_indices.sizeUninitialized( num_diags );
  diagonal_map_indices[0] = 1;
  diagonals.resize( num_diags );
  diagonals[0].sizeUninitialized( num_diag_entries );
  for ( int i=0; i<num_diag_entries; i++ ){
    diagonals[0][i] = diagonal_array[i];
  }

  // Generate full covariance matrix blocks
  int num_matrices=1;
  int num_matrix_rows = 3;
  Real matrix_array[] = {1.,0.5,0.25,0.5,2.,0.5,0.25,0.5,4.};
  matrix_map_indices.sizeUninitialized( num_matrices );
  matrix_map_indices[0] = 2;
  matrices.resize( num_matrices );
  matrices[0].shapeUninitialized( num_matrix_rows, num_matrix_rows );
  for ( int j=0; j<num_matrix_rows; j++ ){
    for ( int i=0; i<num_matrix_rows; i++ )
      matrices[0](i,j) = matrix_array[j*num_matrix_rows+i];
  }

  ExperimentCovariance exper_cov;
  exper_cov.set_covariance_matrices( matrices, diagonals, scalars,
				     matrix_map_indices,
				     diagonal_map_indices,
				     scalar_map_indices );

  // Test determinant and log_determinant
  BOOST_CHECK_CLOSE(exper_cov.determinant(), 432.0, 1.0e-12);
  BOOST_CHECK_CLOSE(exper_cov.log_determinant(), std::log(432.0), 1.0e-12);

  int num_residuals = 9;
  Real residual_array[] = {1., 1., 2., 4., 1., 2., 4., 2., 4.};
  RealVector residual( Teuchos::Copy, residual_array, num_residuals );

  // Test application of the covariance inverse to residual vector
  Real prod = exper_cov.apply_experiment_covariance( residual );
  BOOST_CHECK( std::abs( prod - 58./3. ) <
	       20.*std::numeric_limits<double>::epsilon() );

  // Test application of the sqrt of the covariance inverse to residual vector
  RealVector result;
  exper_cov.apply_experiment_covariance_inverse_sqrt( residual, result );
  prod = result.dot( result );
  BOOST_CHECK( std::abs( prod - 58./3. ) <
	       10.*std::numeric_limits<double>::epsilon() );

  // Test application of the sqrt of the covariance inverse to matrix of
  // gradient vectors
  RealMatrix scaled_grads;
  Real grads_array[] = { 1., 2., 1., 2., 2., 4., 4., 8., 1., 2., 2., 4., 4., 8.,
			 2., 4., 4., 8. };
  RealMatrix grads( Teuchos::Copy, grads_array, 2, 2, 9 );
  exper_cov.apply_experiment_covariance_inverse_sqrt_to_gradients( grads,
								   scaled_grads);

  RealMatrix grammian( grads.numRows(), grads.numRows(), false );
  grammian.multiply( Teuchos::NO_TRANS, Teuchos::TRANS, 1.0, scaled_grads,
		     scaled_grads, 0. );
  BOOST_CHECK(  std::abs( grammian(0,0) - 58./3. ) <
		20.*std::numeric_limits<double>::epsilon() );
  BOOST_CHECK(  std::abs( grammian(1,1) - 232./3. ) <
		20.*std::numeric_limits<double>::epsilon() );

  // Test application of the sqrt of the covariance inverse to matrix of
  // Hessian matrices
  Real hessian_0_array[] = {4., 2., 2., 2.};
  RealSymMatrix hessian_0( Teuchos::Copy, false, hessian_0_array, 2, 2 );
  RealSymMatrix hessian_1( hessian_0 ), hessian_2( hessian_0 ),
    hessian_3( hessian_0 ),  hessian_4( hessian_0 ),
    hessian_5( hessian_0 ),  hessian_6( hessian_0 ),
    hessian_7( hessian_0 ), hessian_8( hessian_0 );
  hessian_1 *= 1.;  hessian_2 *= 2.;  hessian_3 *= 4.;  hessian_4 *= 1.;
  hessian_5 *= 2.;  hessian_6 *= 4.;  hessian_7 *= 2.;  hessian_8 *= 4.;

  RealSymMatrixArray hessians( 9 );
  hessians[0] = hessian_0;  hessians[1] = hessian_1;
  hessians[2] = hessian_2;  hessians[3] = hessian_3;
  hessians[4] = hessian_4;  hessians[5] = hessian_5;
  hessians[6] = hessian_6;  hessians[7] = hessian_7;
  hessians[8] = hessian_8;

  RealSymMatrixArray scaled_hessians;
  exper_cov.apply_experiment_covariance_inverse_sqrt_to_hessians( hessians,
							       scaled_hessians );

  Real exact_scaled_hessian_0_array[] = {4.,2.,2.,2.};
  Real exact_scaled_hessian_1_array[] = {4.,2.,2.,2.};
  Real exact_scaled_hessian_2_array[] = {8./std::sqrt(2.),4./std::sqrt(2.),
				   4./std::sqrt(2.),4./std::sqrt(2.)};
  Real exact_scaled_hessian_3_array[] = {8., 4., 4., 4.};
  Real exact_scaled_hessian_4_array[] = {4.,2.,2.,2.};
  Real exact_scaled_hessian_5_array[] = {4.53557367611073,2.26778683805536,
				   2.26778683805536,2.26778683805536};
  Real exact_scaled_hessian_6_array[] = {6.98297248755176,3.49148624377588,
				   3.49148624377588,3.49148624377588};
  Real exact_scaled_hessian_7_array[] = {8./std::sqrt(2.),4./std::sqrt(2.),
				   4./std::sqrt(2.),4./std::sqrt(2.)};
  Real exact_scaled_hessian_8_array[] = {8., 4., 4., 4.};

  RealSymMatrix exact_scaled_hessian_0( Teuchos::View, false,
				  exact_scaled_hessian_0_array, 2, 2 );
  RealSymMatrix exact_scaled_hessian_1( Teuchos::View, false,
				  exact_scaled_hessian_1_array, 2, 2 );
  RealSymMatrix exact_scaled_hessian_2( Teuchos::View, false,
				  exact_scaled_hessian_2_array, 2, 2 );
  RealSymMatrix exact_scaled_hessian_3( Teuchos::View, false,
				  exact_scaled_hessian_3_array, 2, 2 );
  RealSymMatrix exact_scaled_hessian_4( Teuchos::View, false,
				  exact_scaled_hessian_4_array, 2, 2 );
  RealSymMatrix exact_scaled_hessian_5( Teuchos::View, false,
				  exact_scaled_hessian_5_array, 2, 2 );
  RealSymMatrix exact_scaled_hessian_6( Teuchos::View, false,
				  exact_scaled_hessian_6_array, 2, 2 );
  RealSymMatrix exact_scaled_hessian_7( Teuchos::View, false,
				  exact_scaled_hessian_7_array, 2, 2 );
  RealSymMatrix exact_scaled_hessian_8( Teuchos::View, false,
				  exact_scaled_hessian_8_array, 2, 2 );

  scaled_hessians[0] -= exact_scaled_hessian_0;
  scaled_hessians[1] -= exact_scaled_hessian_1;
  scaled_hessians[2] -= exact_scaled_hessian_2;
  scaled_hessians[3] -= exact_scaled_hessian_3;
  scaled_hessians[4] -= exact_scaled_hessian_4;
  scaled_hessians[5] -= exact_scaled_hessian_5;
  scaled_hessians[6] -= exact_scaled_hessian_6;
  scaled_hessians[7] -= exact_scaled_hessian_7;
  scaled_hessians[8] -= exact_scaled_hessian_8;

  BOOST_CHECK( scaled_hessians[0].normInf() <
	       100.*std::numeric_limits<double>::epsilon() );
  BOOST_CHECK( scaled_hessians[1].normInf() <
	       100.*std::numeric_limits<double>::epsilon() );
  BOOST_CHECK( scaled_hessians[2].normInf() <
	       100.*std::numeric_limits<double>::epsilon() );
  BOOST_CHECK( scaled_hessians[3].normInf() <
	       100.*std::numeric_limits<double>::epsilon() );
  BOOST_CHECK( scaled_hessians[4].normInf() <
	       100.*std::numeric_limits<double>::epsilon() );
  BOOST_CHECK( scaled_hessians[5].normInf() <
	       100.*std::numeric_limits<double>::epsilon() );
  BOOST_CHECK( scaled_hessians[6].normInf() <
	       100.*std::numeric_limits<double>::epsilon() );
  BOOST_CHECK( scaled_hessians[7].normInf() <
	       100.*std::numeric_limits<double>::epsilon() );
  BOOST_CHECK( scaled_hessians[8].normInf() <
	       100.*std::numeric_limits<double>::epsilon() );

  // Test extraction of main diagonal
  RealVector diagonal;
  exper_cov.get_main_diagonal( diagonal );

  Real exact_diagonal_array[] = {1.,1.,2.,4.,1.,2.,4.,2.,4.};
  RealVector exact_diagonal(Teuchos::View, exact_diagonal_array, 9 );

  exact_diagonal -= diagonal;
  BOOST_CHECK( exact_diagonal.normInf() <
	       10.*std::numeric_limits<double>::epsilon() );

  // Test conversion to correlation matrix
  // Matlab for correlation matrix
  // std_dev = sqrt(diag(B)); correl_mat = diag(1./std_dev)*B*diag(1./std_dev);
  RealSymMatrix exact_correl(9);
  for (int i=0; i<9; ++i)
    exact_correl(i,i) = 1.0;
  // update off-diagonal entries for the full matrix block
  for (int i=0; i<3; ++i)
    for (int j=0; j<i; ++j)
      exact_correl(4+i,4+j) = matrices[0](i,j) /
        std::sqrt(matrices[0](i,i)) / std::sqrt(matrices[0](j,j));

  RealSymMatrix calc_correl;
  exper_cov.as_correlation(calc_correl);

  calc_correl -= exact_correl;
  BOOST_CHECK( calc_correl.normInf() <
               10.*std::numeric_limits<double>::epsilon() );
}

void test_linear_interpolate_1d_no_extrapolation()
{
  // Generate field coordinates on [0,1]
  int num_field_pts = 6;
  Real field_pts_array[] = {1./6.,2./5.,7./12.,2./3.,6./7.,1.};
  RealMatrix field_pts( Teuchos::View, field_pts_array, 1, num_field_pts, 1 );
  // Generate field data which is the 3rd degree Legendre polynomial 1/2(5x^3-3x)
  RealVector field_vals( num_field_pts, false );
  for ( int i=0; i<num_field_pts; i++)
    field_vals[i] = 0.5*(5.*std::pow(field_pts(i,0),3)-3.*field_pts(i,0));

  // Generate simulation coordinates on equally spaced on [0,1]
  int num_sim_pts = 11;
  RealMatrix sim_pts( num_sim_pts, 1, false );
  Real dx = 1./(num_sim_pts-1);
  for ( int i=0; i<num_sim_pts; i++)
    sim_pts(i,0) = i*dx;
  // Generate simulation data which is the 2nd degree Legendre polynomial
  // 1/2(3x^2-1)
  RealVector sim_vals( num_sim_pts, false );
  for ( int i=0; i<num_sim_pts; i++)
    sim_vals[i] = 0.5*(3.*std::pow(sim_pts(i,0),2)-1.);
  // Generate gradients of simulation data which is [3x,4x]
  int num_vars = 2;
  RealMatrix sim_grads( num_vars, num_sim_pts, false );
  for ( int i=0; i<num_sim_pts; i++){
    sim_grads(0,i) = 3.*sim_pts(i,0);
    sim_grads(1,i) = 4.*sim_pts(i,0);
  }
  // Generate gradients of simulation data which we assume is also
  // [3x+0.2,3x+0.05;3x+0.05,3x+0.1]
  // Note interp sets num_vars from gradients and so hessian must be
  // consistent with grads
  RealSymMatrixArray sim_hessians( num_sim_pts );
  for ( int i=0; i<num_sim_pts; i++){
    sim_hessians[i].shapeUninitialized(num_vars);
    //symmetric so do not have to set all entries, just upper triangular ones
    sim_hessians[i](0,0)=3.*sim_pts(i,0)+0.2;
    sim_hessians[i](0,1)=3.*sim_pts(i,0)+0.05;
    sim_hessians[i](1,1)=3.*sim_pts(i,0)+0.1;
  }

  // Interpolate the simulation data onto the coordinates of the field data
  RealVector interp_vals;
  RealMatrix interp_grads;
  RealSymMatrixArray interp_hessians;
  linear_interpolate_1d( sim_pts, sim_vals, sim_grads, sim_hessians,
			 field_pts, interp_vals, interp_grads, interp_hessians );
  interp_vals -= field_vals;

  Real diff_array[] = {-2.16574074074074e-1,1.8e-1,3.91261574074074e-1,
		       4.29259259259259e-1,3.17084548104956e-1,0.0};
  RealVector diff( Teuchos::View, diff_array, num_field_pts );
  diff -= interp_vals;
  BOOST_CHECK( diff.normInf() < 10.*std::numeric_limits<double>::epsilon() );

  RealMatrix true_grads(num_vars,num_field_pts,false);
  for ( int i=0; i<num_field_pts; i++){
    true_grads(0,i) = 3.*field_pts(i,0);
    true_grads(1,i) = 4.*field_pts(i,0);
  }
  true_grads -= interp_grads;
  BOOST_CHECK( true_grads.normInf()<10.*std::numeric_limits<double>::epsilon() );

  RealSymMatrixArray true_hessians( num_sim_pts );
  for ( int i=0; i<num_field_pts; i++){
    true_hessians[i].shapeUninitialized(num_vars);
    //symmetric so do not have to set all entries, just upper triangular ones
    true_hessians[i](0,0)=3.*field_pts(i,0)+0.2;
    true_hessians[i](0,1)=3.*field_pts(i,0)+0.05;
    true_hessians[i](1,1)=3.*field_pts(i,0)+0.1;
    true_hessians[i] -= interp_hessians[i];
    BOOST_CHECK( true_hessians[i].normInf()<
		 10.*std::numeric_limits<double>::epsilon() );
  }
}

void test_linear_interpolate_1d_with_extrapolation()
{
  // Linear interpolate uses constant extrapolation

  // Generate field coordinates on [-1/6,11/10]
  int num_field_pts = 6;
  Real field_pts_array[] = {-1./6.,2./5.,7./12.,2./3.,6./7.,11./10.};
  RealMatrix field_pts( Teuchos::View, field_pts_array, 1, num_field_pts, 1 );
  // Generate field data which is the 3rd degree Legendre polynomial 1/2(5x^3-3x)
  RealVector field_vals( num_field_pts, false );
  for ( int i=0; i<num_field_pts; i++)
    field_vals[i] = 0.5*(5.*std::pow(field_pts(i,0),3)-3.*field_pts(i,0));

  // Generate simulation coordinates on equally spaced on [0,1]
  int num_sim_pts = 11;
  RealMatrix sim_pts( num_sim_pts, 1, false );
  Real dx = 1./(num_sim_pts-1);
  for ( int i=0; i<num_sim_pts; i++)
    sim_pts(i,0) = i*dx;
  // Generate simulation data which is the 2nd degree Legendre polynomial
  // 1/2(3x^2-1)
  RealVector sim_vals( num_sim_pts, false );
  for ( int i=0; i<num_sim_pts; i++)
    sim_vals[i] = 0.5*(3.*std::pow(sim_pts(i,0),2)-1.);

  // Interpolate the simulation data onto the coordinates of the field data
  RealVector interp_vals;
  RealMatrix sim_grads, interp_grads;
  RealSymMatrixArray sim_hessians, interp_hessians;
  linear_interpolate_1d( sim_pts, sim_vals, sim_grads, sim_hessians,
			 field_pts, interp_vals, interp_grads, interp_hessians );
  interp_vals -= field_vals;

  Real diff_array[] = {-7.38425925925926e-1,1.8e-1,3.91261574074074e-1,
		       4.29259259259259e-1,3.17084548104956e-1,-6.775e-1};
  RealVector diff( Teuchos::View, diff_array, num_field_pts );
  diff -= interp_vals;
  BOOST_CHECK( diff.normInf() < 10.*std::numeric_limits<double>::epsilon() );
}

/*void test_build_hessian_of_sum_square_residuals_from_function_hessians()
{
  int num_residuals = 3;
  RealSymMatrixArray func_hessians( num_residuals );
  RealMatrix func_gradients( 2, num_residuals, false );
  RealVector residuals( num_residuals );

  Real pts_array[] = {-1,-1,-0.5,0.5,1./3.,2./3.};
  RealMatrix pts( Teuchos::View, pts_array, 2, 2, 3 );

  for ( int i=0; i<num_residuals; i++ ){
    Real x = pts(0,i), y = pts(1,i);
    Real x2 = x*x, y2 = y*y;
    residuals[i] = (2.*x2-y2)*(2.*x2-y2)/10.-x2*y2;
    // The following will not work. Build hessian assumes
    // residual = (approx-data)
    //residuals[i] = x2*y2-(2.*x2-y2)*(2.*x2-y2)/10.;
    func_gradients(0,i) = 4./5.*x*(2.*x2-y2);
    func_gradients(1,i) = 2./5.*y*(y2-2.*x2);
    func_hessians[i].shape( 2 );
    func_hessians[i](0,0) = -4./5.*(y2-6.*x2);
    func_hessians[i](1,0) = -8.*x*y/5.;
    func_hessians[i](1,1) = 2./5.*(3.*y2-2.*x2);
  }

  ActiveSet set(num_residuals, 2); set.request_values(7);
  Response resp(SIMULATION_RESPONSE, set);
  resp.function_values(residuals);
  resp.function_gradients(func_gradients);
  resp.function_hessians(func_hessians);

  // -------------------------------------- //
  // Build hessian without noise covariance
  // -------------------------------------- //

  // If no noise covariance specify exper_cov as empty
  ExperimentCovariance exper_cov;

  RealSymMatrix ssr_hessian;
  build_hessian_of_sum_square_residuals_from_response(resp, exper_cov,
						      ssr_hessian);
  // hessian computed for ssr= r'r/2
  RealSymMatrix truth_ssr_hessian( 2 );
  for ( int i=0; i<num_residuals; i++ ){
    Real x = pts(0,i), y = pts(1,i);
    Real x2 = x*x, x3 = x2*x, x4 = x2*x2, x5 = x3*x2, x6 = x4*x2,
      y2 = y*y, y3 = y2*y, y4 = y2*y2, y5 = y3*y2, y6 = y4*y2;
    truth_ssr_hessian(0,0) += 2./25.*( 10.*x2*y2*(y2-6.*x2)-
					(y2-14.*x2)*(2.*x2-y2)*(2.*x2-y2) );
    truth_ssr_hessian(1,0) += 4./25.*y*x*( 10.*x2*y2-3.*(2.*x2-y2)*(2.*x2-y2) );
    truth_ssr_hessian(1,1) += 1./25.*( 10.*x2*y2*(2.*x2-3.*y2) +
				       (7.*y2-2.*x2)*(2.*x2-y2)*(2.*x2-y2) );
  }
  // hack until build hessian can use covariance for multiple experiments
  truth_ssr_hessian *=2;

  truth_ssr_hessian -= ssr_hessian;
  BOOST_CHECK( truth_ssr_hessian.normInf() <
	       10.*std::numeric_limits<double>::epsilon() );

  // -------------------------------------- //
  // Build hessian with noise covariance
  // -------------------------------------- //

  // Fill exper_cov with noise covariance
  std::vector<RealMatrix> matrices;
  std::vector<RealVector> diagonals;
  RealVector scalars;
  IntVector matrix_map_indices, diagonal_map_indices, scalar_map_indices;

  // Experiment covariance matrix consists of the following blocks
  // scalar_1, matrix_1

  // MATLAB CODE:
  //
  //  A = [1,0.5;0.5,2.]; S = eye(3); S(1,1)=1.; S(2:3,2:3)=A;
  //  U = chol( inv(S) );

  //  r = [-0.9 -0.05625 -0.04444444444444444]';
  //  g = [-0.8, -0.1, -0.05925925925925925; 0.4, -0.05, 0.05925925925925925];
  //  h1 = [4,-1.6;-1.6,0.4]; h2=[1 0.4;0.4,0.1];
  //  h3 =[0.1777777777777778,-0.3555555555555555;
  //  -0.3555555555555555, 0.4444444444444445];

  //  gnewton_hess = g*inv(S)*g';
  //  rs = r'*inv(S);
  //  hess = gnewton_hess + rs(1)*h1+rs(2)*h2+rs(3)*h3

  // Generate scalar matrix blocks
  int num_scalars = 1;
  Real scalar_array[] = {1.};
  int scalar_map_index_array[] = {0};
  scalars.sizeUninitialized( num_scalars );
  scalar_map_indices.sizeUninitialized( num_scalars );
  for ( int i=0; i<num_scalars; i++ ){
    scalars[i] = scalar_array[i];
    scalar_map_indices[i] = scalar_map_index_array[i];
  }

  // Generate full covariance matrix blocks
  int num_matrices=1;
  int num_matrix_rows = 2;
  Real matrix_array[] = {1.,0.5,0.5,2.};
  matrix_map_indices.sizeUninitialized( num_matrices );
  matrix_map_indices[0] = 1;
  matrices.resize( num_matrices );
  matrices[0].shapeUninitialized( num_matrix_rows, num_matrix_rows );
  for ( int j=0; j<num_matrix_rows; j++ ){
    for ( int i=0; i<num_matrix_rows; i++ )
      matrices[0](i,j) = matrix_array[j*num_matrix_rows+i];
  }

  exper_cov.set_covariance_matrices( matrices, diagonals, scalars,
				     matrix_map_indices,
				     diagonal_map_indices,
				     scalar_map_indices );

  // must reset ssr_hessian because if it is the right size
  // the build_hessians... function will assume we want to add to it
  ssr_hessian.shape(0);
  build_hessian_of_sum_square_residuals_from_response(resp, exper_cov,
						      ssr_hessian);

  Real truth_noise_scaled_ssr_hessian_array[] = {-3.00319615912208e+00,
						 1.10723495982755e+00,
						 1.10723495982755e+00,
						 -2.02746423672350e-01};
  RealSymMatrix truth_noise_scaled_ssr_hessian( Teuchos::View, true,
					  truth_noise_scaled_ssr_hessian_array,
					  2, 2 );

  // hack until build hessian can use covariance for multiple experiments
  truth_noise_scaled_ssr_hessian *=2;

  truth_noise_scaled_ssr_hessian -= ssr_hessian;
  BOOST_CHECK( truth_noise_scaled_ssr_hessian.normInf() <
	       100.*std::numeric_limits<double>::epsilon() );

  // -------------------------------------- //
  // Build Gauss-Newton hessian with noise covariance
  // -------------------------------------- //
  ActiveSet set1(num_residuals, 2); set1.request_values(3);
  Response resp1(SIMULATION_RESPONSE, set1);
  resp1.function_values(residuals);
  resp1.function_gradients(func_gradients);
  resp1.function_hessians(func_hessians);

  // must reset ssr_hessian because if it is the right size
  // the build_hessians... function will assume we want to add to it
  ssr_hessian.shape(0);
  build_hessian_of_sum_square_residuals_from_response(resp1, exper_cov,
						      ssr_hessian);

  Real truth_noise_scaled_gn_ssr_hessian_array[] = {6.50048990789732e-01,
						    -3.15445816186557e-01,
						    -3.15445816186557e-01,
						    1.66556927297668e-01};
  RealSymMatrix truth_noise_scaled_gn_ssr_hessian( Teuchos::View, true,
					truth_noise_scaled_gn_ssr_hessian_array,
					2, 2 );

  // hack until build hessian can use covariance for multiple experiments
  truth_noise_scaled_gn_ssr_hessian *=2;

  truth_noise_scaled_gn_ssr_hessian -= ssr_hessian;
  BOOST_CHECK( truth_noise_scaled_gn_ssr_hessian.normInf() <
	       100.*std::numeric_limits<double>::epsilon() );
}*/

void test_symmetric_eigenvalue_decomposition()
{
  Real matrix_array[] = { 1.64, 0.48, 0.48, 1.36 };
  RealSymMatrix matrix( Teuchos::View, false, matrix_array, 2, 2 );

  RealVector eigenvalues;
  RealMatrix eigenvectors;
  symmetric_eigenvalue_decomposition( matrix, eigenvalues, eigenvectors );

  Real truth_eigenvalues_array[] = {1.,2.};
  RealVector truth_eigenvalues( Teuchos::View, truth_eigenvalues_array, 2 );

  truth_eigenvalues -=  eigenvalues;
  BOOST_CHECK( truth_eigenvalues.normInf() <
	       10.*std::numeric_limits<double>::epsilon() );

  Real truth_eigenvectors_array[] ={ 0.6, -0.8, -0.8, -0.6 };
  RealMatrix truth_eigenvectors( Teuchos::View, truth_eigenvectors_array, 2,2,2);

  truth_eigenvectors -= eigenvectors;
  BOOST_CHECK( truth_eigenvectors.normInf() <
	       10.*std::numeric_limits<double>::epsilon() );
}

void test_get_positive_definite_covariance_from_hessian()
{
  // uncorrelated prior
  Real prior_chol_fact1[] = { 0.2, 0., 0., 0.2 };
  RealMatrix prior_L1(Teuchos::View, prior_chol_fact1, 2, 2, 2);

  // non positive definite matrix
  Real misfit_h_array1[] = { 0.92, 1.44, 1.44, 0.08 };
  RealSymMatrix misfit_hessian1(Teuchos::View, false, misfit_h_array1, 2, 2 );

  RealSymMatrix covariance1;
  NonDBayesCalibration::
    get_positive_definite_covariance_from_hessian(misfit_hessian1, prior_L1,
						  covariance1, NORMAL_OUTPUT);

  // MATLAB result (no truncation of eigenvalues)
  //Real truth_cov1_array[] ={  0.038703703703704, -0.002222222222222,
  //			       -0.002222222222222,  0.04 };
  // Dakota result (truncation of 1 eigenvalue)
  Real truth_cov1_array[] ={ 3.81037037037037e-02, -1.42222222222222e-03,
			    -1.42222222222222e-03,  3.89333333333333e-02 };
  RealSymMatrix truth_covariance1(Teuchos::View, false, truth_cov1_array, 2, 2);

  truth_covariance1 -= covariance1;
  BOOST_CHECK( truth_covariance1.normInf() <
	       10.*std::numeric_limits<double>::epsilon() );

  //////////////////////////////////////////////////////////////////////////////

  // correlated prior
  Real prior_chol_fact2[] = { 0.2, 0.05, 0., 0.2 };
  RealMatrix prior_L2(Teuchos::View, prior_chol_fact2, 2, 2, 2);

  // positive definite matrix
  Real misfit_h_array2[] = { 1.64, 0.48, 0.48, 1.36 };
  RealSymMatrix misfit_hessian2(Teuchos::View, false, misfit_h_array2, 2, 2);

  RealSymMatrix covariance2;
  NonDBayesCalibration::
    get_positive_definite_covariance_from_hessian(misfit_hessian2, prior_L2,
						  covariance2, NORMAL_OUTPUT);

  // MATLAB and Dakota result (no truncation of eigenvalues):
  Real truth_cov2_array[] = { 0.037120225312445, 0.008125330047527,
			      0.008125330047527, 0.039714838936807 };
  RealSymMatrix truth_covariance2(Teuchos::View, false, truth_cov2_array, 2, 2);

  truth_covariance2 -= covariance2;
  BOOST_CHECK( truth_covariance2.normInf() <
	       10.*std::numeric_limits<double>::epsilon() );

  //////////////////////////////////////////////////////////////////////////////
  /*
  // zero misfit matrix: proposal covariance = prior covariance
  // can't perform symmetric eigen-deomposition for L'HL = 0
  Real misfit_h_array3[] = { 0., 0., 0., 0. };
  RealSymMatrix misfit_hessian3(Teuchos::View, false, misfit_h_array3, 2, 2);

  RealSymMatrix covariance3;
  NonDBayesCalibration::
    get_positive_definite_covariance_from_hessian(misfit_hessian3, prior_L2,
						  covariance3, NORMAL_OUTPUT);

  Real truth_cov3_array[] = { 4., 0.1, 0.1, 4.0025 };
  RealSymMatrix truth_covariance3(Teuchos::View, false, truth_cov3_array, 2, 2);

  truth_covariance3 -= covariance3;
  BOOST_CHECK( truth_covariance3.normInf() <
	       10.*std::numeric_limits<double>::epsilon() );
  */
}

void test_matrix_symmetry()
{
  // Test non-square matrix
  RealMatrix test_rect_matrix(4,3, true);
  bool is_symm = is_matrix_symmetric(test_rect_matrix);
  BOOST_CHECK( !is_symm );

  Real matrix_array[] = { 1.0, 0.7, 0.7, 0.7,
                          0.7, 1.0, 0.7, 0.7,
                          0.7, 0.7, 1.0, 0.7,
                          0.7, 0.7, 0.7, 1.0 };

  // Test symmetric matrix
  RealMatrix test_symm_mat(Teuchos::Copy, matrix_array, 4, 4, 4);
  is_symm = is_matrix_symmetric(test_symm_mat);
  BOOST_CHECK( is_symm );

  // Test non-symmetric square matrix
  RealMatrix test_nonsymm_mat(test_symm_mat);
  test_nonsymm_mat(1,0) = 0.5;
  is_symm = is_matrix_symmetric(test_nonsymm_mat);
  BOOST_CHECK( !is_symm );
}

} // end namespace TestFieldCovariance
} // end namespace Dakota

// NOTE: Boost.Test framework provides the main progran driver

//____________________________________________________________________________//

BOOST_AUTO_TEST_CASE( test_main )
//int test_main( int argc, char* argv[] )      // note the name!
{
  using namespace Dakota::TestFieldCovariance;

  // Test ExperimentData covariance matrix
  test_multiple_scalar_covariance_matrix();
  test_single_diagonal_block_covariance_matrix();
  test_single_full_block_covariance_matrix();
  test_mixed_scalar_diagonal_full_block_covariance_matrix();

  // Test field interpolation functions
  test_linear_interpolate_1d_no_extrapolation();
  test_linear_interpolate_1d_with_extrapolation();

  // Test hessian functions
  // Turn following test off until I can create an ExperimentData object
  //test_build_hessian_of_sum_square_residuals_from_function_hessians();
  test_get_positive_definite_covariance_from_hessian();

  // Test linear algebra routines
  test_symmetric_eigenvalue_decomposition();
  test_matrix_symmetry();

  int run_result = 0;
  BOOST_CHECK( run_result == 0 || run_result == boost::exit_success );

  //  return boost::exit_success;
}


#include <iostream>
#include <vector>
#include <string>

#include <Eigen/Core>

#include "igl/adjacency_list.h"
#include "igl/adjacency_matrix.h"
#include "igl/components.h"
#include "igl/cotmatrix.h"
#include "igl/dijkstra.h"
#include "igl/embree/ambient_occlusion.h"
#include "igl/embree/unproject_onto_mesh.h"
#include "igl/gaussian_curvature.h"
#include "igl/invert_diag.h"
#include "igl/jet.h"
#include "igl/massmatrix.h"
#include "igl/per_vertex_attribute_smoothing.h"
#include "igl/per_vertex_normals.h"
#include "igl/polygon_mesh_to_triangle_mesh.h"
#include "igl/principal_curvature.h"
#include "igl/ray_mesh_intersect.h"
#include "igl/readOFF.h"
#include "igl/unproject_onto_mesh.h"
#include "igl/viewer/Viewer.h"
#include "igl/writeOFF.h"

void getMeanCurvature(const Eigen::MatrixXd &V, const Eigen::MatrixXi &F,
                      Eigen::VectorXd &VCs) {
  // Get the laplacian
  Eigen::SparseMatrix<double> L,M,Minv;
  //   from the cotangent matrix
  igl::cotmatrix(V,F, L);
  //   and the mass matrix
  igl::massmatrix(V,F, igl::MASSMATRIX_TYPE_VORONOI, M);
  igl::invert_diag(M, Minv);

  Eigen::MatrixXd HN = -Minv*(L * V);
  // Mean curviture is this value, up to a sign.
  Eigen::VectorXd VC = HN.rowwise().norm();

  // Smooth this out a bit.
  //igl::per_vertex_attribute_smoothing(VC,F, VCs);
  VCs = VC;
  Eigen::VectorXd VC_before = VCs;
  VCs = VCs.cwiseMin(0.1);

  // Normalize all the values.
  VCs /= VCs.maxCoeff();
  VCs = VCs.cwiseSqrt();
  Eigen::VectorXd ones = Eigen::VectorXd::Constant(VCs.rows(), 1, 1);
  VCs = ones - VCs;
}

int main(int argc, char* argv[]) {
  if (argc < 2) {
    fprintf(stderr, "Used for selecting points on the surface of a mesh. Will write the\n"
                    "corresponding vertices to output.off\n");
    fprintf(stderr, "usage: %s <input.off>\n", argv[0]);
    return -1;
  }

  Eigen::MatrixXd V;
  Eigen::MatrixXi F;

  printf("Reading in mesh...\n");
  // Read in the input file.
  igl::readOFF(argv[1], V, F);
  // Need the adjacency list
  std::vector<std::vector<int> > VV;
  igl::adjacency_list(F, VV);
  // Also caluculate the curvature values.
  printf("Calucalting curvature...\n");
  std::vector<std::vector<double> > curveWeight;
  Eigen::VectorXd mean_curve;

  Eigen::MatrixXd PD1,PD2;
  Eigen::VectorXd PV1,PV2;
  igl::principal_curvature(V,F,PD1,PD2,PV1,PV2, 5);
  //mean_curve = 0.5 * (PV1 + PV2);
  mean_curve = PV1.cwiseProduct(PV1) + PV2.cwiseProduct(PV2);
  mean_curve = mean_curve.cwiseMin(0.1);
  mean_curve.cwiseSqrt();
  std::cout << mean_curve << std::endl;
  //getMeanCurvature(V,F, mean_curve);

  igl::viewer::Viewer viewer;
  viewer.data.set_mesh(V, F);
  Eigen::MatrixXd this_col;
  igl::jet(mean_curve, true, this_col);
  viewer.data.set_colors(this_col);
  viewer.core.show_lines = false;
  viewer.launch();
}


/*
@copyright Louis Dionne 2014
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
 */

#include <boost/hana/ext/std/tuple.hpp>

#include <boost/hana/detail/assert.hpp>
#include <boost/hana/detail/constexpr.hpp>
#include <boost/hana/integral.hpp>
#include <boost/hana/monad/laws.hpp>

#include <tuple>
using namespace boost::hana;

BOOST_HANA_CONSTEXPR_LAMBDA auto f = [](auto x) {
    return std::make_tuple(x + int_<1>);
};

BOOST_HANA_CONSTEXPR_LAMBDA auto g = [](auto x) {
    return std::make_tuple(x * int_<3>);
};

int main() {
    BOOST_HANA_CONSTANT_ASSERT(Monad::laws::check(std::make_tuple(), int_<1>, f, g));
    BOOST_HANA_CONSTANT_ASSERT(Monad::laws::check(std::make_tuple(int_<1>), int_<1>, f, g));
    BOOST_HANA_CONSTANT_ASSERT(Monad::laws::check(std::make_tuple(int_<1>, int_<2>), int_<1>, f, g));
    BOOST_HANA_CONSTEXPR_ASSERT(Monad::laws::check(std::make_tuple(1, 2, 3, 4), int_<1>, f, g));
}


#ifndef SMALL
#define SMALL

#include <Eigen/Dense>
#include <algorithm>
#include <iostream>

#include "MiniDnn/layer.h"
#include "MiniDnn/layer/conv.h"
#include "MiniDnn/layer/fully_connected.h"
#include "MiniDnn/layer/ave_pooling.h"
#include "MiniDnn/layer/max_pooling.h"
#include "MiniDnn/layer/relu.h"
#include "MiniDnn/layer/sigmoid.h"
#include "MiniDnn/layer/softmax.h"
#include "MiniDnn/loss.h"
#include "MiniDnn/loss/mse_loss.h"
#include "MiniDnn/loss/cross_entropy_loss.h"
#include "MiniDnn/mnist.h"
#include "MiniDnn/network.h"
#include "MiniDnn/optimizer.h"
#include "MiniDnn/optimizer/sgd.h"
#include "Enclave.h"
#include "Enclave_t.h"  /* print_string */

using namespace std;

class RandData {
public:
    Matrix train_data;
    Matrix train_labels;
    Matrix test_data;
    Matrix test_labels;

    RandData(int size) {
        train_data = Matrix::Random(28 * 28, size);
        train_labels = Matrix::Ones(1, size);
        test_data = Matrix::Random(28 * 28, size);
        test_labels = Matrix::Ones(1, size);
    }
};

void ecall_ml_small() {
    // data
//    MNIST dataset("/Users/rc/Study/Projects/mini-dnn/data/mnist/");
//    dataset.read();
//    int n_train = dataset.train_data.cols();
//    int dim_in = dataset.train_data.rows();
//    std::cout << "mnist train number: " << n_train << std::endl;
//    std::cout << "mnist test number: " << dataset.test_labels.cols() << std::endl;
    RandData dataset(1000);
//    dataset.read();
    int n_train = dataset.train_data.cols();
    int dim_in = dataset.train_data.rows();
    // dnn
    Network dnn;

    //------------------------------------------------------------
    Layer *conv1 = new Conv(1, 28, 28, 128, 3, 3, 1, 1, 1);
    Layer *relu1 = new ReLU;
    Layer *conv2 = new Conv(128, 28, 28, 128, 3, 3, 1, 1, 1);
    Layer *relu2 = new ReLU;
    dnn.add_layer(conv1);
    dnn.add_layer(relu1);
    dnn.add_layer(conv2);
    dnn.add_layer(relu2);

    Layer *b1_pool = new MaxPooling(128, 28, 28, 2, 2, 2);
    dnn.add_layer(b1_pool);

    Layer *fc_fc1 = new FullyConnected(b1_pool->output_dim(), 10);
    Layer *fc_relu1 = new Softmax;

    dnn.add_layer(fc_fc1);
    dnn.add_layer(fc_relu1);

    // loss
    Loss *loss = new CrossEntropy;
    dnn.add_loss(loss);
    // train & test
    SGD opt(0.001, 5e-4, 0.9, true);
    // SGD opt(0.001);
    const int n_epoch = 5;
    const int batch_size = 14;
    for (int epoch = 0; epoch < n_epoch; epoch++) {
        shuffle_data(dataset.train_data, dataset.train_labels);
        for (int start_idx = 0; start_idx < n_train; start_idx += batch_size) {
            int ith_batch = start_idx / batch_size;
            Matrix x_batch = dataset.train_data.block(0, start_idx, dim_in,
                                                      std::min(batch_size, n_train - start_idx));
            Matrix label_batch = dataset.train_labels.block(0, start_idx, 1,
                                                            std::min(batch_size, n_train - start_idx));
            Matrix target_batch = one_hot_encode(label_batch, 10);
            if (false && ith_batch % 10 == 1) {
                // std::cout << ith_batch << "-th grad: " << std::endl;
                printf("ith_batch: %d-th grad: \n");
                dnn.check_gradient(x_batch, target_batch, 10);
            }

            ocall_start_clock();
            dnn.forward(x_batch);
            ocall_end_clock("Forward: %f\n");

            dnn.backward(x_batch, target_batch);
            ocall_end_clock("Backward: %f\n");

            // display
            if (ith_batch % 2 == 0) {
                //std::cout << ith_batch << "-th batch, loss: " << dnn.get_loss() << std::endl;
                printf("%d-th, loss: %f\n", ith_batch, dnn.get_loss());
            }
            // optimize
            dnn.update(opt);
        }

//        // test
        dnn.forward(dataset.test_data);
        float acc = compute_accuracy(dnn.output(), dataset.test_labels);
        // std::cout << std::endl;
        // std::cout << epoch + 1 << "-th epoch, test acc: " << acc << std::endl;
        // std::cout << std::endl;
    }
    return;
}

#endif


//
// Created by dchansen on 6/16/18.
//
#include <numeric>
#include <boost/math/constants/constants.hpp>
#include "correct_frequency_shift.h"
#include "hoNDFFT.h"
#include "hoNDArray_elemwise.h"
namespace Gadgetron{
    namespace FatWater {

        using namespace std::complex_literals;
        static constexpr float PI = boost::math::constants::pi<float>();

        void correct_frequency_shift(hoNDArray<std::complex<float>> &species_images, const Parameters &parameters){

            uint16_t X = species_images.get_size(0);
            uint16_t Y = species_images.get_size(1);
            uint16_t Z = species_images.get_size(2);
            uint16_t CHA = species_images.get_size(3);
            uint16_t N = species_images.get_size(4);
            uint16_t S = species_images.get_size(5);
            uint16_t LOC = species_images.get_size(6);

            std::vector<size_t> sub_dimension = {X,Y,Z,CHA,N,1,1};
            auto data_ptr = species_images.get_data_ptr();

            hoNDFFT<float>::instance()->fft(&species_images,0);

            for (int kspecies = 0; kspecies < parameters.species.size(); kspecies++){

                auto& species = parameters.species[kspecies];

                auto mean_frequency_offset = std::accumulate(
                        species.amplitude_frequency_pairs.begin(), species.amplitude_frequency_pairs.end(),0.0f,
                                              [](auto v, auto pair){ return v+pair.first.real()*pair.second;}
                                              ) /
                              std::accumulate(
                        species.amplitude_frequency_pairs.begin(),species.amplitude_frequency_pairs.end(), 0.0f,
                                            [](auto v, auto pair) { return v + pair.first.real();});
                mean_frequency_offset *= parameters.field_strength_T*parameters.gyromagnetic_ratio_Mhz;

                if (std::abs(mean_frequency_offset) < 1.0 ) continue;

                hoNDArray<std::complex<float>> phase_ramp(X);

                for (int i = 0; i < phase_ramp.get_number_of_elements(); i++){
                    phase_ramp[i] = std::exp(2if*PI*parameters.sample_time_us*1e-6f*float(i)*mean_frequency_offset);
                }

                //Kristoffer will have a field day with this.
                for (int kL = 0; kL < LOC; kL++)
                    for (int kN = 0; kN < N; kN++)
                        for (int kCHA = 0; kCHA < CHA; kCHA++)
                            for (int kZ = 0; kZ < Z; kZ++)
                                for (int kY = 0; kY < Y; kY++)
                                    for (int kX = 0; kX < Y; kX++)
                                        species_images(kX,kY,kZ,kCHA,kN,kspecies,kL) *= phase_ramp[kX];

            }
//
            hoNDFFT<float>::instance()->ifft(&species_images,0);

        }
    }
}


//#include "test_cases.h"
//#include <iostream>
//#include "Mesh.h"
//#include "vector_var.h"
//#include <stdio.h>      /* printf */
//#include <iostream>
//#include <math.h>
//#include "Boundary_Conditions.h"
//#include "Solution.h"
//#include "Solver.h"
//#include "quad_bcs.h"
//#include "external_forces.h"
//#include "global_variables.h"
//#include <algorithm>
//#include <string>
//#include <sstream>
//#include <boost/algorithm/string/replace.hpp>
//#include <boost/filesystem.hpp>
//#include <cstdio>
//#include <ctime>
//
//using namespace std;
//using namespace boost::filesystem;
//
//test_cases::test_cases()
//{
//    //ctor
//}
//
//test_cases::~test_cases()
//{
//    //dtor
//}
//
//void test_cases::west_to_east_poiseuille_flow(){
//
//    double X,Y,dx,dy,dt; // dt is streaming time step
//    double kine_viscosity,tau;
//    double reynolds, umax;
//    double simulation_length;
//    double delta_t; // time stepping step
//    quad_bcs_plus bcs;
//    double cs;
//    double pressure_grad;
//    global_variables globals;
//    std::string output_file;
//    double average_pressure;
//    double pre_condition_gamma;
//    std::clock_t start;
//    double duration;
//
//    pre_condition_gamma = 1;
//    //average_pressure =1*3*pre_condition_gamma;
//    average_pressure =1*3;
//
//    //vector_var pressure_gradient(-0.01,0,0), origin(1.1,0,0), origin_loc(0,0,0) ;
//    vector_var pressure_gradient(0,0,0), origin(average_pressure,0,0), origin_loc(0,0,0) ;
//
//    /// Parameters unique to test case
//
//    X= 16;
//    Y= 4.4;
//    dx= 0.4; // grid spacing
//    dy = 0.4;  // grid spacing
//    dt = 0.2;  // streaming time step -> dictates mach number -> grid spacing /2
//
//
//    /// Error :: let dt = l_dx = l_dy i.e. lattice spacing
//
//    simulation_length = 5000;
//    //kine_viscosity = U * X/ reynolds;
//    kine_viscosity = 0.0833333;
//
//    delta_t = 0.3;  // time marching step
//    cs = 1/sqrt(3);
//
//    reynolds = 33.7094;
//    umax = 0.17557;
//    // tau = 0.5 + Umax* Length / reynolds/dt  --- assumes c = 1.
//
//    tau = 0.5 + umax*X /reynolds /dt;
//    tau = 0.5 + umax*X /reynolds /dt *pre_condition_gamma;
//    //tau = 1/pre_condition_gammma*(tau-0.5) + 0.5;
//
//
//
//    pressure_grad =0;
//
//    //tau =0.75
//    output_file = create_output_directory(globals.tolerance,dx,delta_t);
//
//
//    // set boundary conditions for this test case
//    //bcs.w_rho = 1.1*3*pre_condition_gammma;
//    bcs.w_rho = 1.1*3*pre_condition_gamma;
//    bcs.w_u = 0;
//    bcs.w_v = 0;
//    bcs.w_w = 0;
//    bcs.w_type_vel = globals.neumann;
//    bcs.w_type_rho = globals.dirichlet;
//
//    bcs.s_rho = 0;
//    bcs.s_u = 0;
//    bcs.s_v = 0;
//    bcs.s_w = 0;
//    bcs.s_type_vel= globals.dirichlet;
//    bcs.s_type_rho = globals.neumann;
//
//    //bcs.e_rho = 1*3*pre_condition_gammma;
//    bcs.e_rho = 1*3*pre_condition_gamma;
//    bcs.e_u = 0;
//    bcs.e_v = 0;
//    bcs.e_w = 0;
//    bcs.e_type_vel= globals.neumann;
//    bcs.e_type_rho = globals.dirichlet;
//
//    bcs.n_rho = 0;
//    bcs.n_u = 0;
//    bcs.n_v = 0;
//    bcs.n_w = 0;
//    bcs.n_type_vel= globals.dirichlet;
//    bcs.n_type_rho = globals.neumann;
//
//
//
//
//
//    /// Methods to run the test case
//    //vector_var_tests();
//
//    // create Mesh
//    Mesh mesh(X,Y,dx,dy);
//
//    // create boundary conditions
//    Boundary_Conditions bc(mesh.get_num_x(), mesh.get_num_y());
//    bc.assign_boundary_conditions(mesh.get_num_x(), mesh.get_num_y(),bcs);
//
//    // assign external force terms
//    external_forces source_term(mesh.get_total_nodes());
//    source_term.set_uniform_force(pressure_grad);
//
//    //create solution
//    Solution soln(mesh.get_total_nodes());
//    soln.assign_pressure_gradient(pressure_gradient,origin_loc,origin,mesh);
//    soln.set_average_rho(average_pressure);
//    // Solvec
//
//    Solver solve;
//
//    solve.Mesh_Solver(dt,tau,mesh,soln,bc,simulation_length, delta_t,dx ,output_file,source_term,
//                              pre_condition_gamma);
//
//    soln.post_process(pre_condition_gamma);
//    soln.output(output_file);
//    tau = 1;
//
//}
//
//
//
//void test_cases::lid_driven_cavity_N(){
//
//    double X,Y,dx,dy,dt; // dt is streaming time step
//    double reynolds,kine_viscosity,tau;
//    double U;
//
//
//    quad_bcs bcs;
//    double cs,delta_t;
//
//    /// Parameters unique to test case
//    reynolds = 1000;
//    X= 100;
//    Y=100;
//    dx=1; // grid spacing
//    dy = 1;  // grid spacing
//    dt = 0.05;  // streaming time step let dt =dx = dy i.e. lattice spacing
//    U = 1;
//    simulation_length = 200;
//    kine_viscosity = U * X/ reynolds;
//    //kine_viscosity = 20;
//    delta_t = 0.1;
//    cs = 1/sqrt(3);
//    tau = kine_viscosity + 0.5* pow(cs,2) *dt;
//    //tau =0.75;
//
//
//    // set boundary conditions for this test case
//    bcs.w_rho = 1;
//    bcs.w_u = 0;
//    bcs.w_v = 0;
//    bcs.w_w = 0;
//
//    bcs.s_rho = 1;
//    bcs.s_u = 0;
//    bcs.s_v = 0;
//    bcs.s_w = 0;
//
//    bcs.e_rho = 1;
//    bcs.e_u = 0;
//    bcs.e_v = 0;
//    bcs.e_w = 0;
//
//    bcs.n_rho = 1;
//    bcs.n_u = 1;
//    bcs.n_v = 0;
//    bcs.n_w = 0;
//
//    /// Methods to run the test case
//    //vector_var_tests();
//
//    // create Mesh
//    Mesh mesh(X,Y,dx,dy);
//
//    // create boundary conditions
//    Boundary_Conditions bc(mesh.get_num_x(), mesh.get_num_y());
//    bc.assign_boundary_conditions(mesh.get_num_x(), mesh.get_num_y(),bcs);
//
//
//    //create solution
//    Solution soln(mesh.get_total_nodes());
//
//    // Solve
//
//    Solver solve;
//    //solve.Mesh_Solver(dt,tau,mesh,soln,bc,simulation_length, delta_t, dx,tolerance);
//
//    tau = 1;
//
//}
//void test_cases::west_to_east_couette_flow(){
//
//    double X,Y,dx,dy,dt; // dt is streaming time step
//    double kine_viscosity,tau;
//    double U;
//    double simulation_length;
//    double delta_t; // time stepping step
//    quad_bcs bcs;
//    double cs;
//    double pre_conditioned_gamma;
//    pre_conditioned_gamma = 1.0;
//    std::string output_file;
//
//    /// Parameters unique to test case
//
//    X= 0.3;
//    Y=1;
//    dx=0.1; // grid spacing
//    dy = 0.1;  // grid spacing
//    dt = 0.05;  // streaming time step -> dictates mach number -> grid spacing /2
//    /// Error :: let dt =dx = dy i.e. lattice spacing
//    U = 2;
//    simulation_length = 2500;
//    //kine_viscosity = U * X/ reynolds;
//    kine_viscosity = 0.6;
//
//    delta_t = 0.1;  // time marching step
//    cs = 1/sqrt(3);
//    tau = kine_viscosity + 0.5* pow(cs,2) *dt;
//
//
//    //output_file = "/home/brendan/Dropbox/PhD/Test Cases/Couette Flow/";
//
//    output_file = "C:/Users/brendan/Dropbox/PhD/Test Cases/Couette Flow/";
//    //tau =0.75;
//
//    // set boundary conditions for this test case
//    bcs.w_rho = 1;
//    bcs.w_u = 1;
//    bcs.w_v = 0;
//    bcs.w_w = 0;
//    bcs.w_type = 3;
//
//    bcs.s_rho = 1;
//    bcs.s_u = 0;
//    bcs.s_v = 0;
//    bcs.s_w = 0;
//    bcs.s_type = 1;
//
//    bcs.e_rho = 0;
//    bcs.e_u = 0;
//    bcs.e_v = 0;
//    bcs.e_w = 0;
//    bcs.e_type =2;
//
//    bcs.n_rho = 1;
//    bcs.n_u = U;
//    bcs.n_v = 0;
//    bcs.n_w = 0;
//    bcs.n_type = 1;
//
//
//
//    /// Methods to run the test case
//    //vector_var_tests();
//
//    // create Mesh
//    Mesh mesh(X,Y,dx,dy);
//
//    // create boundary conditions
//    Boundary_Conditions bc(mesh.get_num_x(), mesh.get_num_y());
//    bc.assign_boundary_conditions(mesh.get_num_x(), mesh.get_num_y(),bcs);
//
//    // assign external force terms
//    external_forces source_term(mesh.get_total_nodes());
//    source_term.set_uniform_force(0.0);
//
//
//    //create solution
//    Solution soln(mesh.get_total_nodes());
//    soln.set_average_rho(1.0);
//
//    // Solve
//
//    Solver solve;
//    solve.Mesh_Solver(dt,tau,mesh,soln,bc,simulation_length, delta_t,dx ,output_file, source_term,pre_conditioned_gamma);
//    soln.output(output_file);
//    tau = 1;
//
//}
//
//void test_cases::east_to_west_couette_flow(){
//
//    double X,Y,dx,dy,dt; // dt is streaming time step
//    double kine_viscosity,tau;
//    double U;
//    double simulation_length;
//    double delta_t; // time stepping step
//    quad_bcs bcs;
//    double cs;
//
//    double pre_conditioned_gamma;
//    pre_conditioned_gamma = 1.0;
//
//    std::string output_file;
//     output_file = "/home/brendan/Dropbox/PhD/Test Cases/Couette Flow/";
//    /// Parameters unique to test case
//
//    X= 0.3;
//    Y=1;
//    dx=0.1; // grid spacing
//    dy = 0.1;  // grid spacing
//    dt = 0.05;  // streaming time step -> dictates mach number -> grid spacing /2
//    /// Error :: let dt =dx = dy i.e. lattice spacing
//    U = -2;
//    simulation_length = 2500;
//    //kine_viscosity = U * X/ reynolds;
//    kine_viscosity = 0.6;
//
//    delta_t = 0.1;  // time marching step
//    cs = 1/sqrt(3);
//    tau = kine_viscosity + 0.5* pow(cs,2) *dt;
//    //tau =0.75;
//
//
//    output_file = "/home/brendan/Dropbox/PhD/Test Cases/Couette Flow/";
//
//    // set boundary conditions for this test case
//    bcs.w_rho = 0;
//    bcs.w_u = 0;
//    bcs.w_v = 0;
//    bcs.w_w = 0;
//    bcs.w_type = 2;
//
//    bcs.s_rho = 1;
//    bcs.s_u = 0;
//    bcs.s_v = 0;
//    bcs.s_w = 0;
//    bcs.s_type = 1;
//
//    bcs.e_rho = 1;
//    bcs.e_u = 0;
//    bcs.e_v = 0;
//    bcs.e_w = 0;
//    bcs.e_type =3;
//
//    bcs.n_rho = 1;
//    bcs.n_u = U;
//    bcs.n_v = 0;
//    bcs.n_w = 0;
//    bcs.n_type = 1;
//
//
//
//    /// Methods to run the test case
//    //vector_var_tests();
//
//    // create Mesh
//    Mesh mesh(X,Y,dx,dy);
//
//    // create boundary conditions
//    Boundary_Conditions bc(mesh.get_num_x(), mesh.get_num_y());
//    bc.assign_boundary_conditions(mesh.get_num_x(), mesh.get_num_y(),bcs);
//
//    // assign external force terms
//    external_forces source_term(mesh.get_total_nodes());
//    source_term.set_uniform_force(0.0);
//
//    //create solution
//    Solution soln(mesh.get_total_nodes());
//
//    // Solve
//
//
//    Solver solve;
//    solve.Mesh_Solver(dt,tau,mesh,soln,bc,simulation_length, delta_t,dx, output_file,source_term,pre_conditioned_gamma);
//    soln.output(output_file);
//
//    tau = 1;
//
//}
//void test_cases::north_to_south_couette_flow(){
//
//    double X,Y,dx,dy,dt; // dt is streaming time step
//    double kine_viscosity,tau;
//    double U;
//    double simulation_length;
//    double delta_t; // time stepping step
//    quad_bcs bcs;
//    double cs;
//    std::string output_file;
//
//    double pre_conditioned_gamma;
//    pre_conditioned_gamma = 1.0;
//     output_file = "/home/brendan/Dropbox/PhD/Test Cases/Couette Flow/";
//
//    /// Parameters unique to test case
//
//    X= 1;
//    Y=0.3;
//    dx=0.1; // grid spacing
//    dy = 0.1;  // grid spacing
//    dt = 0.05;  // streaming time step -> dictates mach number -> grid spacing /2
//    /// Error :: let dt =dx = dy i.e. lattice spacing
//    U = -2;
//    simulation_length = 2500;
//    //kine_viscosity = U * X/ reynolds;
//    kine_viscosity = 0.6;
//
//    delta_t = 0.1;  // time marching step
//    cs = 1/sqrt(3);
//    tau = kine_viscosity + 0.5* pow(cs,2) *dt;
//    //tau =0.75;
//
//
//
//    // set boundary conditions for this test case
//    bcs.w_rho = 1;
//    bcs.w_u = 0;
//    bcs.w_v = 0;
//    bcs.w_w = 0;
//    bcs.w_type = 1;
//
//    bcs.s_rho = 0;
//    bcs.s_u = 0;
//    bcs.s_v = 0;
//    bcs.s_w = 0;
//    bcs.s_type = 2;
//
//    bcs.e_rho = 1;
//    bcs.e_u = 0;
//    bcs.e_v = U;
//    bcs.e_w = 0;
//    bcs.e_type =1;
//
//    bcs.n_rho = 1;
//    bcs.n_u = 0;
//    bcs.n_v = 0;
//    bcs.n_w = 0;
//    bcs.n_type = 3;
//
//
//
//    /// Methods to run the test case
//    //vector_var_tests();
//
//    // create Mesh
//    Mesh mesh(X,Y,dx,dy);
//
//    // create boundary conditions
//    Boundary_Conditions bc(mesh.get_num_x(), mesh.get_num_y());
//    bc.assign_boundary_conditions(mesh.get_num_x(), mesh.get_num_y(),bcs);
//
//    // assign external force terms
//    external_forces source_term(mesh.get_total_nodes());
//    source_term.set_uniform_force(0.0);
//
//    //create solution
//    Solution soln(mesh.get_total_nodes());
//
//    // Solve
//
//    Solver solve;
//    solve.Mesh_Solver(dt,tau,mesh,soln,bc,simulation_length, delta_t,dx,output_file,source_term,pre_conditioned_gamma);
//
//    tau = 1;
//
//}
//
//void test_cases::south_to_north_couette_flow(){
//
//    double X,Y,dx,dy,dt; // dt is streaming time step
//    double kine_viscosity,tau;
//    double U;
//    double simulation_length;
//    double delta_t; // time stepping step
//    quad_bcs bcs;
//    double cs;
//    std::string output_file;
//
//    double pre_conditioned_gamma;
//    pre_conditioned_gamma = 1.0;
//     output_file = "/home/brendan/Dropbox/PhD/Test Cases/Couette Flow/";
//
//    /// Parameters unique to test case
//
//    X= 1;
//    Y=0.3;
//    dx=0.1; // grid spacing
//    dy = 0.1;  // grid spacing
//    dt = 0.05;  // streaming time step -> dictates mach number -> grid spacing /2
//    /// Error :: let dt =dx = dy i.e. lattice spacing
//    U = 2;
//    simulation_length = 2500;
//    //kine_viscosity = U * X/ reynolds;
//    kine_viscosity = 0.6;
//
//    delta_t = 0.1;  // time marching step
//    cs = 1/sqrt(3);
//    tau = kine_viscosity + 0.5* pow(cs,2) *dt;
//    //tau =0.75;
//
//    // set boundary conditions for this test case
//    bcs.w_rho = 1;
//    bcs.w_u = 0;
//    bcs.w_v = 0;
//    bcs.w_w = 0;
//    bcs.w_type = 1;
//
//    bcs.s_rho = 1;
//    bcs.s_u = 0;
//    bcs.s_v = 0;
//    bcs.s_w = 0;
//    bcs.s_type = 3;
//
//    bcs.e_rho = 1;
//    bcs.e_u = 0;
//    bcs.e_v = U;
//    bcs.e_w = 0;
//    bcs.e_type =1;
//
//    bcs.n_rho = 0;
//    bcs.n_u = 0;
//    bcs.n_v = 0;
//    bcs.n_w = 0;
//    bcs.n_type = 2;
//
//
//
//    /// Methods to run the test case
//    //vector_var_tests();
//
//    // create Mesh
//    Mesh mesh(X,Y,dx,dy);
//
//    // create boundary conditions
//    Boundary_Conditions bc(mesh.get_num_x(), mesh.get_num_y());
//    bc.assign_boundary_conditions(mesh.get_num_x(), mesh.get_num_y(),bcs);
//
//    // assign external force terms
//    external_forces source_term(mesh.get_total_nodes());
//    source_term.set_uniform_force(0.0);
//
//    //create solution
//    Solution soln(mesh.get_total_nodes());
//
//    // Solve
//
//    Solver solve;
//    solve.Mesh_Solver(dt,tau,mesh,soln,bc,simulation_length, delta_t,dx, output_file, source_term,pre_conditioned_gamma);
//
//    tau = 1;
//
//}
//
//std::string test_cases::create_output_directory(double tol, double dx, double dt){
//
//    std::string output_file;
//    std::string folder;
//    std::ostringstream s;
//    //output_file = "C:/Users/brendan/Dropbox/PhD/Test Cases/Poiseuille Flow/";
//
//    output_file = "/home/brendan/Dropbox/PhD/Test Cases/Poiseuille Flow/";
//    s << "tol " << tol << " x " << dx
//     << " t " << dt;
//     folder = s.str();
//    //folder.replace(folder.begin(),folder.end(), ".",  "_");
//    boost::replace_all(folder, "." , "_");
//    output_file = output_file + folder;
//
//    boost::filesystem::path dir(output_file);
//    boost::filesystem::create_directories(dir);
//
//    return output_file;
//
//}
//
//void test_cases::vector_var_tests(){
//     vector_var a,b,c,d,e,f;
//
//    a.x = 3;
//    a.y = 4;
//    a.z = 0;
//
//    b.x = 2;
//    b.y = 2;
//    b.z = 2;
//
//    c.x = 4;
//    c.y = -3;
//    c.z = 0;
//
//    d.x= 0;
//    d.y = 0;
//    d.z = 0;
//
//    f.x = 5;
//    f.y = 5;
//    f.z = 0;
//
//
//
//
//    double mag , dp, ang;
//    mag = a.Magnitude();
//
//    cout <<  "Magnitude of a:" << mag << endl ;
//    dp = a.Dot_Product(b);
//    cout << "Dot product of a and b: " << dp << endl ;
//    ang = a.Angle_Between_Vectors(c);
//    ang = ang *360/2/M_PI;
//    cout << "angle between a and c: " << ang << endl ;
//    e.Get_Gradient(10,20,d,b);
//    cout << "gradient vector between d and b =" << e.x << "," << e.y << "," << e.z << endl ;
//
//
//
//}


/*
// Copyright 2018 University of Liege
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Authors:
// - Adrien Crovato
*/

//// Body panels creation
// Compute panel collocation point, surface, vertices, normal, longitudinal, transverse and perpendicular unit vectors
// from data contained into sGrid
//
// I/O:
// - sGrid: temporary dynamic array containing body panel vertices
// - bPan: body panels (structure)

#include <iostream>
#include <Eigen/Dense>
#include "create_panel.h"

#define NDIM 3

using namespace std;
using namespace Eigen;

void create_panel(MatrixX3d &sGrid, Network &bPan) {

    // Temporary variables
    double norm = 0;
    int panIdx;
    int c1, c2, c3, c4;
    Vector3d v1(NDIM), v2(NDIM);

    //// Begin
    cout << "Creating panels... " << flush;

    // Resizing network matrices
    bPan.CG.resize(bPan.nP, NDIM);
    bPan.v0.resize(bPan.nP, NDIM);
    bPan.v1.resize(bPan.nP, NDIM);
    bPan.v2.resize(bPan.nP, NDIM);
    bPan.v3.resize(bPan.nP, NDIM);
    bPan.S.resize(bPan.nP, 1);
    bPan.n.resize(bPan.nP, NDIM);
    bPan.l.resize(bPan.nP, NDIM);
    bPan.t.resize(bPan.nP, NDIM);
    bPan.p.resize(bPan.nP, NDIM);

    // Compute corner points, collocation points and vectors
    for (int i = 0; i < bPan.nC_; ++i) {
        for (int k = 0; k < bPan.nS_; ++k) {

            panIdx = i + k * (bPan.nC_);
            c1 = i + k * bPan.nC;
            c2 = i + 1 + k * bPan.nC;
            c3 = i + 1 + (k+1) *bPan.nC;
            c4 = i + (k+1) * bPan.nC;

            bPan.v0(panIdx,0) = sGrid(c1,0); // pts[i][k]
            bPan.v1(panIdx,0) = sGrid(c2,0); // pts[i+1][k]
            bPan.v2(panIdx,0) = sGrid(c3,0); // pts[i+1][k+1]
            bPan.v3(panIdx,0) = sGrid(c4,0); // pts[i][k+1]
            bPan.v0(panIdx,1) = sGrid(c1,1);
            bPan.v1(panIdx,1) = sGrid(c2,1);
            bPan.v2(panIdx,1) = sGrid(c3,1);
            bPan.v3(panIdx,1) = sGrid(c4,1);
            bPan.v0(panIdx,2) = sGrid(c1,2);
            bPan.v1(panIdx,2) = sGrid(c2,2);
            bPan.v2(panIdx,2) = sGrid(c3,2);
            bPan.v3(panIdx,2) = sGrid(c4,2);

            bPan.CG(panIdx,0) = (bPan.v0(panIdx,0) + bPan.v1(panIdx,0) + bPan.v2(panIdx,0) + bPan.v3(panIdx,0)) / 4;
            bPan.CG(panIdx,1) = (bPan.v0(panIdx,1) + bPan.v1(panIdx,1) + bPan.v2(panIdx,1) + bPan.v3(panIdx,1)) / 4;
            bPan.CG(panIdx,2) = (bPan.v0(panIdx,2) + bPan.v1(panIdx,2) + bPan.v2(panIdx,2) + bPan.v3(panIdx,2)) / 4;

            bPan.l(panIdx,0) = (bPan.v0(panIdx,0) + bPan.v3(panIdx,0) - bPan.v1(panIdx,0) - bPan.v2(panIdx,0)) / 4;
            bPan.l(panIdx,1) = (bPan.v0(panIdx,1) + bPan.v3(panIdx,1) - bPan.v1(panIdx,1) - bPan.v2(panIdx,1)) / 4;
            bPan.l(panIdx,2) = (bPan.v0(panIdx,2) + bPan.v3(panIdx,2) - bPan.v1(panIdx,2) - bPan.v2(panIdx,2)) / 4;
            bPan.l.row(panIdx) /= bPan.l.row(panIdx).norm();

            bPan.t(panIdx,0) = (bPan.v2(panIdx,0) + bPan.v3(panIdx,0) - bPan.v0(panIdx,0) - bPan.v1(panIdx,0)) / 4;
            bPan.t(panIdx,1) = (bPan.v2(panIdx,1) + bPan.v3(panIdx,1) - bPan.v0(panIdx,1) - bPan.v1(panIdx,1)) / 4;
            bPan.t(panIdx,2) = (bPan.v2(panIdx,2) + bPan.v3(panIdx,2) - bPan.v0(panIdx,2) - bPan.v1(panIdx,2)) / 4;
            bPan.t.row(panIdx) /= bPan.t.row(panIdx).norm();
        }
    }
    // Compute panel normals and surfaces
    for (int i = 0; i < bPan.nC_; ++i) {
        for (int k = 0; k < bPan.nS_; ++k) {
            panIdx = i + k * (bPan.nC_);

            v1(0) = bPan.v2(panIdx,0) - bPan.v0(panIdx,0);
            v2(0) = bPan.v1(panIdx,0) - bPan.v3(panIdx,0);
            v1(1) = bPan.v2(panIdx,1) - bPan.v0(panIdx,1);
            v2(1) = bPan.v1(panIdx,1) - bPan.v3(panIdx,1);
            v1(2) = bPan.v2(panIdx,2) - bPan.v0(panIdx,2);
            v2(2) = bPan.v1(panIdx,2) - bPan.v3(panIdx,2);

            bPan.n.row(panIdx) = v1.cross(v2);
            norm = bPan.n.row(panIdx).norm();

            bPan.S(panIdx) = norm/2;
            bPan.n.row(panIdx) /= norm;
        }
    }
    // Compute panel perpendicular vector
    for (int i = 0; i < bPan.nC_; ++i) {
        for (int k = 0; k < bPan.nS_; ++k) {
            panIdx = i + k * (bPan.nC_);
            bPan.p.row(panIdx) = bPan.n.row(panIdx).cross(bPan.l.row(panIdx));
        }
    }

    //// Control display
    cout << "Done!" << endl;
    #ifdef VERBOSE
        cout << "Collocation points: " << bPan.CG.rows() << 'X' << bPan.CG.cols() << endl;
        for (int i = 0; i < bPan.nP; ++i)
            cout << i << ' ' << bPan.CG(i,0) << ' ' << bPan.CG(i,1) << ' ' << bPan.CG(i,2) << endl;
        cout << "Panel surfaces: " << bPan.S.rows() << 'X' << bPan.S.cols() << endl;
        for (int i = 0; i < bPan.nP; ++i)
            cout << i << ' ' << bPan.S(i) << endl;
        cout << "Unit normals: " << bPan.n.rows() << 'X' << bPan.n.cols() << endl;
        for (int i = 0; i < bPan.nP; ++i)
            cout << i << ' ' << bPan.n(i,0) << ' ' << bPan.n(i,1) << ' ' << bPan.n(i,2) << endl;
        cout << "Unit longitudinal vectors: " << bPan.l.rows() << 'X' << bPan.l.cols() << endl;
        for (int i = 0; i < bPan.nP; ++i)
            cout << i << ' ' << bPan.l(i,0) << ' ' << bPan.l(i,1) << ' ' << bPan.l(i,2) << endl;
        cout << "Unit transverse vectors: " << bPan.t.rows() << 'X' << bPan.t.cols() << endl;
        for (int i = 0; i < bPan.nP; ++i)
            cout << i << ' ' << bPan.t(i,0) << ' ' << bPan.t(i,1) << ' ' << bPan.t(i,2) << endl;
        cout << "Unit perpendicular vectors: " << bPan.p.rows() << 'X' << bPan.p.cols() << endl;
        for (int i = 0; i < bPan.nP; ++i)
            cout << i << ' ' << bPan.p(i,0) << ' ' << bPan.p(i,1) << ' ' << bPan.p(i,2) << endl;
    #endif
    cout << endl;
}


#include "func.hpp"

#include <core/func.hpp>

#include <boost/python.hpp>

namespace python {

  // TODO this should go somewhere else
  template<class U>
  struct norm2 : ::func< scalar<U> ( U ) > {

	using To = scalar<U>;
	using From = U;

	using dTo = deriv<To>;
	using dFrom = deriv<From>;

	virtual std::size_t size( slice<const From> ) const {
	  return 1;
	}

	virtual void apply( slice<To> to, slice<const From> from) const {
	  to[0] = 0;

	  for(const From& x : from) {
		to[0] += traits<From>::dot(x, x);
	  }

	  to[0] /= 2.0;
	}

	virtual void jacobian(triplet_iterator block, slice<const From> from) const {

	  unsigned off = 0;

	  for(const From& x : from) {
		for(unsigned j = 0, m = traits<From >::dim; j < m; ++j, ++off) {
		  *block++ = triplet(0, off, traits<From>::coord(j, x));
		}
	  }

	}

	virtual void hessian(triplet_iterator block,
						 slice< const dTo > lambda, slice< const dFrom > from) const {
	  for(int i = 0, n = from.size() * traits< From >::dim; i < n; ++i) {
		*block++ = triplet(i, i, lambda[0]);
	  }
	}

  };

  void func::module() {
	using namespace boost::python;
	using ::func;

	class_<func_base, std::shared_ptr<func_base>, boost::noncopyable >("func_base", no_init);

	// TODO moar
	class_<norm2<vec3>, std::shared_ptr<norm2<vec3>>,
		   bases<func_base>, boost::noncopyable >("norm2_vec3");
  }

}


//==============================================================================
//         Copyright 2003 - 2011   LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2011   LRI    UMR 8623 CNRS/Univ Paris Sud XI
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
#ifndef NT2_TRIGONOMETRIC_CONSTANTS_INV2PI_HPP_INCLUDED
#define NT2_TRIGONOMETRIC_CONSTANTS_INV2PI_HPP_INCLUDED

#include <nt2/include/functor.hpp>
#include <boost/simd/constant/hierarchy.hpp>
#include <boost/simd/constant/register.hpp>

namespace nt2
{
  namespace tag
  {
   /*!
     @brief Inv2pi generic tag

     Represents the Inv2pi constant in generic contexts.

     @par Models:
        Hierarchy
   **/
    BOOST_SIMD_CONSTANT_REGISTER( Inv2pi, double
                                  , 0, 0x3e22f983
                                  , 0x3fc45f306dc9c883ll
                                  )
  }
  namespace ext
  {
   template<class Site, class... Ts>
   BOOST_FORCEINLINE generic_dispatcher<tag::Inv2pi, Site> dispatching_Inv2pi(adl_helper, boost::dispatch::meta::unknown_<Site>, boost::dispatch::meta::unknown_<Ts>...)
   {
     return generic_dispatcher<tag::Inv2pi, Site>();
   }
   template<class... Args>
   struct impl_Inv2pi;
  }
  /*!
    Constant \f$\frac1\pi\f$.

    @par Semantic:

    For type T0:

    @code
    T0 r = Inv2pi<T0>();
    @endcode

    is similar to:

    @code
    T0 r = rec(Two<T0>()*Pi<T0>());
    @endcode

    @return a value of type T0
  **/
  BOOST_SIMD_CONSTANT_IMPLEMENTATION(tag::Inv2pi, Inv2pi);
}

#endif


#define BOOST_TEST_MODULE Gpufit

#include "Gpufit/gpufit.h"

#include <boost/test/included/unit_test.hpp>

#include <array>

template<std::size_t n_points>
void generate_gauss_1d(
    std::array< float, n_points >& values,
    std::array< float, 4 > const & parameters )
{
    float const a = parameters[ 0 ];
    float const x0 = parameters[ 1 ];
    float const s = parameters[ 2 ];
    float const b = parameters[ 3 ];

    for ( int point_index = 0; point_index < n_points; point_index++ )
    {
        float const argx = ( ( point_index - x0 )*( point_index - x0 ) ) / ( 2.f * s * s );
        float const ex = exp( -argx );
        values[ point_index ] = a * ex + b;
    }
}

BOOST_AUTO_TEST_CASE( Gauss_Fit_1D )
{
	/*
		Performs a single fit using the GAUSS_1D model.
		- Doesn't use user_info or weights.
		- No noise is added.
		- Checks fitted parameters equalling the true parameters.
	*/

    std::size_t const n_fits{ 1 } ;
    std::size_t const n_points{ 5 } ;

    std::array< float, 4 > const true_parameters{ { 4.f, 2.f, 0.5f, 1.f } };

    std::array< float, n_points > data{};
    generate_gauss_1d( data, true_parameters );

    std::array< float, 4 > initial_parameters{ { 2.f, 1.5f, 0.3f, 0.f } };

    float tolerance{ 0.001f };

    int max_n_iterations{ 10 };

    std::array< int, 4 > parameters_to_fit{ { 1, 1, 1, 1 } };

    std::array< float, 4 > output_parameters;
    int output_states;
    float output_chi_square;
    int output_n_iterations;

    int const status
            = gpufit
            (
                n_fits,
                n_points,
                data.data(),
                0,
                GAUSS_1D,
                initial_parameters.data(),
                tolerance,
                max_n_iterations,
                parameters_to_fit.data(),
                LSE,
                0,
                0,
                output_parameters.data(),
                &output_states,
                &output_chi_square,
                &output_n_iterations
            ) ;

    BOOST_CHECK( status == 0 ) ;
    BOOST_CHECK( output_states == 0 );
    BOOST_CHECK( output_chi_square < 1e-6f );
    BOOST_CHECK( output_n_iterations <= max_n_iterations );

    BOOST_CHECK( std::fabsf(output_parameters[ 0 ] - true_parameters[ 0 ] ) < 1e-6f );
    BOOST_CHECK( std::fabsf(output_parameters[ 1 ] - true_parameters[ 1 ] ) < 1e-6f );
    BOOST_CHECK( std::fabsf(output_parameters[ 2 ] - true_parameters[ 2 ] ) < 1e-6f );
    BOOST_CHECK( std::fabsf(output_parameters[ 3 ] - true_parameters[ 3 ] ) < 1e-6f );
}


/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */

/*
Copyright (C) 2004 StatPro Italia srl
Copyright (C) 2015 CompatibL

This file is part of QuantLib, a free-software/open-source library
for financial quantitative analysts and developers - http://quantlib.org/

QuantLib is free software: you can redistribute it and/or modify it
under the terms of the QuantLib license.  You should have received a
copy of the license along with this program; if not, please email
<quantlib-dev@lists.sf.net>. The license is also available online at
<http://quantlib.org/license.shtml>.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.  See the license for more details.
*/

//based on exchangerate.cpp from test-suite

#ifndef cl_adjoint_exchange_rate_impl_hpp
#define cl_adjoint_exchange_rate_impl_hpp
#pragma once

#include "adjointexchangeratetest.hpp"
#include "adjointtestutilities.hpp"
#include "adjointtestbase.hpp"
#include "utilities.hpp"
#include <ql/exchangerate.hpp>
#include <ql/currencies/europe.hpp>
#include <ql/currencies/america.hpp>
#include <ql/currencies/asia.hpp>
#include <ql/currencies/exchangeratemanager.hpp>
#include <boost/make_shared.hpp>
#include <ql/quantlib.hpp>

using namespace QuantLib;
using namespace std;
using namespace boost::unit_test_framework;

namespace
{
   struct ExchangeTest
        : cl::AdjointTest<ExchangeTest>
    {
        explicit ExchangeTest(Size size)
            : AdjointTest()
            , size_(size)
            , rates_(size)
            , derivedAmount_(1)
            , EUR_(EURCurrency())
            , USD_(USDCurrency())
            , GBP_(GBPCurrency())
            , m1_(50000.0 * GBP_)
        {
            Money::conversionType = Money::NoConversion;
            rates_ = { 1.2042, 0.6612 };
        }

        Size indepVarNumber() { return size_; }

        Size depVarNumber() { return 1; }

        Size minPerfIteration() { return 0; }

        void recordTape()
        {
            cl::Independent(rates_);
            calculateAmount();
            f_ = std::make_unique<cl::tape_function<double>>(rates_, derivedAmount_);
        }

        void calculateAmount()
        {
            ExchangeRate derived = ExchangeRate::chain(ExchangeRate(EUR_, USD_, rates_[0]), ExchangeRate(EUR_, GBP_, rates_[1]));
            derivedAmount_[0] = derived.exchange(m1_).value();
        }

        // Calculates derivatives using analytical formula.
        void calcAnalytical()
        {
            analyticalResults_.resize(size_, 0.0);
            analyticalResults_[0] = m1_.value() / 0.6612;
            analyticalResults_[1] = -m1_.value()*rates_[0] / std::pow(rates_[1], 2);
        }

        double relativeTol() const { return 1e-3; }

        double absTol() const { return 1e-5; }

        Size size_;
        std::vector<cl::tape_double> rates_;
        std::vector<cl::tape_double> derivedAmount_;
        Currency EUR_, USD_, GBP_;
        Money m1_;
    };
}

#endif


/*
Copyright 2008 Intel Corporation

Use, modification and distribution are subject to 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).
*/
#include <boost/polygon/polygon.hpp>
#include <cassert>
namespace gtl = boost::polygon;
using namespace boost::polygon::operators;

//lets make the body of main from point_usage.cpp
//a generic function parameterized by point type
template <typename Point>
void test_point() {
  //constructing a gtl point
  int x = 10;
  int y = 20;
  //Point pt(x, y);
  Point pt = gtl::construct<Point>(x, y);
  assert(gtl::x(pt) == 10);
  assert(gtl::y(pt) == 20);

  //a quick primer in isotropic point access
  typedef gtl::orientation_2d O;
  using gtl::HORIZONTAL;
  using gtl::VERTICAL;
  O o = HORIZONTAL;
  assert(gtl::x(pt) == gtl::get(pt, o));

  o = o.get_perpendicular();
  assert(o == VERTICAL);
  assert(gtl::y(pt) == gtl::get(pt, o));

  gtl::set(pt, o, 30);
  assert(gtl::y(pt) == 30);

  //using some of the library functions
  //Point pt2(10, 30);
  Point pt2 = gtl::construct<Point>(10, 30);
  assert(gtl::equivalence(pt, pt2));

  gtl::transformation<int> tr(gtl::axis_transformation::SWAP_XY);
  gtl::transform(pt, tr);
  assert(gtl::equivalence(pt, gtl::construct<Point>(30, 10)));

  gtl::transformation<int> tr2 = tr.inverse();
  assert(tr == tr2); //SWAP_XY is its own inverse transform

  gtl::transform(pt, tr2);
  assert(gtl::equivalence(pt, pt2)); //the two points are equal again

  gtl::move(pt, o, 10); //move pt 10 units in y
  assert(gtl::euclidean_distance(pt, pt2) == 10.0f);

  gtl::move(pt, o.get_perpendicular(), 10); //move pt 10 units in x
  assert(gtl::manhattan_distance(pt, pt2) == 20);
}

//Now lets declare our own point type
//Bjarne says that if a class doesn't maintain an
//invariant just use a struct.
struct CPoint {
  int x;
  int y;
};

//There, nice a simple...but wait, it doesn't do anything
//how do we use it to do all the things a point needs to do?

//First we register it as a point with boost polygon
namespace boost { namespace polygon {
    template <>
    struct geometry_concept<CPoint> { typedef point_concept type; };

    //Then we specialize the gtl point traits for our point type
    template <>
    struct point_traits<CPoint> {
      typedef int coordinate_type;

      static inline coordinate_type get(const CPoint& point,
					orientation_2d orient) {
	if(orient == HORIZONTAL)
	  return point.x;
	return point.y;
      }
    };

    template <>
    struct point_mutable_traits<CPoint> {
      typedef int coordinate_type;

      static inline void set(CPoint& point, orientation_2d orient, int value) {
	if(orient == HORIZONTAL)
	  point.x = value;
	else
	  point.y = value;
      }
      static inline CPoint construct(int x_value, int y_value) {
	CPoint retval;
	retval.x = x_value;
	retval.y = y_value;
	return retval;
      }
    };
  } }

//Now lets see if the CPoint works with the library functions
int main() {
  test_point<CPoint>(); //yay! All your testing is done for you.
  return 0;
}

//Now you know how to map a user type to the library point concept
//and how to write a generic function parameterized by point type
//using the library interfaces to access it.


#include <iostream>
#include <Eigen/Core>
using namespace Eigen;

template<typename Derived>
void print_size(const EigenBase<Derived> &b) {
  std::cout << "size (rows, cols): " << b.size() << " (" << b.rows()
            << ", " << b.cols() << ")" << std::endl;
}

int main() {
  Vector3f v;
  print_size(v);
  // v.asDiagonal() returns a 3x3 diagonal matrix pseudo-expression
  print_size(v.asDiagonal());
}


#include "algorithm/BruteForce.hpp"
#include "generator/Generation.hpp"
#include "sudoku/Grid.hpp"
#include <boost/program_options.hpp>
#include <iostream>
#include <random>
#include <string>

namespace po = boost::program_options;

auto getProgramOptions(int argc, char **argv, po::options_description desc) {
  po::variables_map vm;
  po::store(po::parse_command_line(argc, argv, desc), vm);
  po::notify(vm);
  return vm;
}

auto createOptionsDescription() {
  po::options_description desc("Allowed options");
  desc.add_options()("help,h", "Produce help message")(
      "inline,i", po::value<std::string>(),
      "Grid written line by line, e.g. 123000456789... with total of 81 "
      "numbers from 0 to 9")("generate,g", po::value<unsigned>(),
                             "Return random board with given empty cells")(
      "seed,s", po::value<unsigned>(), "Seed for random number generator")(
      "with-solution,l", "Will print solution, when generating board");
  return desc;
}

auto toString(const Sudoku::Grid &grid) {
  std::stringstream ss;
  for (const auto &row : grid.getRows()) {
    for (const auto &cell : row.getCells()) {
      ss << std::to_string(cell.getValue());
    }
  }
  return ss.str();
}

auto solveSudoku(const Sudoku::Grid &grid) {
  std::unique_ptr<Sudoku::Algorithm> algorithm =
      std::make_unique<Sudoku::BruteForce>();
  const auto result = algorithm->solve(grid);
  std::cout << toString(result.solution) << std::endl;
  return result.success ? 0 : 1;
}

auto generateSudoku(const po::variables_map &vm) {
  unsigned amountOfEmptyCells = vm["generate"].as<unsigned>();
  const auto seed = [&]() {
    if (vm.count("seed")) {
      return vm["seed"].as<unsigned>();
    }
    std::random_device os_seed;
    return os_seed();
  }();
  const auto generated = Sudoku::generateRandomGrid(seed, amountOfEmptyCells);
  std::cout << toString(generated.problem) << std::endl;
  if (vm.count("with-solution")) {
    std::cout << toString(generated.solution) << std::endl;
  }
  return 0;
}

int main(int argc, char **argv) {
  const auto desc = createOptionsDescription();
  po::variables_map vm = getProgramOptions(argc, argv, desc);
  if (vm.count("help")) {
    std::cout << desc << "\n";
    return 0;
  }

  if (vm.count("generate")) {
    return generateSudoku(vm);
  }

  if (vm.count("inline")) {
    return solveSudoku(Sudoku::Grid(vm["inline"].as<std::string>()));
  }

  std::cout << desc << "\n";
  return 0;
}


#pragma once

#include <Eigen/Dense>
#include <iostream>
#include <vector>

namespace tools::linalg {

struct SolverData {
  double relative_residual = 0;
  double initial_algebraic_error = 0;
  double algebraic_error = 0;
  size_t iterations = 0;
  bool converged = false;
};

enum StoppingCriterium { Relative, Algebraic };

// Loosely based off Eigen/ConjugateGradient.h.
template <typename MatType, typename PrecondType>
std::pair<Eigen::VectorXd, SolverData> PCG(
    const MatType &A, const Eigen::VectorXd &b, const PrecondType &M,
    const Eigen::VectorXd &x0, int imax, double tol,
    enum StoppingCriterium stopping = StoppingCriterium::Relative);

template <typename MatType, typename PrecondType>
class Lanczos {
 public:
  Lanczos(const MatType &A, const PrecondType &P, size_t max_iterations = 200,
          double tol = 0.0001, double tol_bisec = 0.000001)
      : Lanczos(A, P, Eigen::VectorXd::Random(A.cols()), max_iterations, tol,
                tol_bisec) {}
  Lanczos(const MatType &A, const PrecondType &P,
          const Eigen::VectorXd &initial_guess, size_t max_iterations = 200,
          double tol = 0.0001, double tol_bisec = 0.000001);

  double max() const { return lmax_; }
  double min() const { return lmin_; }
  double cond() const { return lmax_ / lmin_; }
  float time() const { return time_; };

  size_t iterations() const { return iterations_; }
  bool converged() const { return converged_; }

  // overload the << operator
  friend std::ostream &operator<<(std::ostream &os, const Lanczos &lanczos) {
    if (lanczos.converged())
      os << "converged\t";
    else
      os << "NOT converged\t";

    os << "its=" << lanczos.iterations() << "\tlmax=" << lanczos.max()
       << "\tlmin=" << lanczos.min() << "\tkappa=" << lanczos.cond()
       << "\ttime=" << lanczos.time() << " s";
    return os;
  }

 private:
  Eigen::VectorXd alpha_, beta_;
  double lmax_, lmin_;

  size_t iterations_;
  bool converged_;
  float time_;

  void bisec(size_t k, double &ymax, double &zmin, double tol_bisec);
  double pol(int k, double x);
};

};  // namespace tools::linalg

#include "linalg.ipp"


/*
Author: Rohan Chetan Thanki
Date created: 16-Oct-2021
*/

/*
This contains the implementation of the Hedging_Portfolio class
*/

#include "Hedging_Portfolio.hpp"
#include <string>
#include <boost/math/distributions.hpp>

using namespace std;

/******************************* Constructors and Destructors ***************************************/

// Default constructor
Hedging_Portfolio::Hedging_Portfolio() : Option() { }

// Parametrised constructor
Hedging_Portfolio::Hedging_Portfolio(const double& K1, const double& S1, const double& r1, const double& T1, const double& vol1, const char& optionType) : Option(K1, S1, r1, T1, vol1, optionType)
{
	if (!(optionType == 'c' || optionType == 'C' || optionType == 'p' || optionType == 'P'))
		throw(20);
}

// Destructor
Hedging_Portfolio::~Hedging_Portfolio(void) { }

/******************************* Getters ***************************************/

double Hedging_Portfolio::getDelta(void) const { return delta; }
double Hedging_Portfolio::getOptionPrice(void) const { return optionPrice; }
double Hedging_Portfolio::getB(void) const { return B; }
double Hedging_Portfolio::getHedgingError(void) const { return HE; }

string Hedging_Portfolio::getDate(void) const { return date; }
string Hedging_Portfolio::getExpDate(void) const { return expDate; }
double Hedging_Portfolio::getpnlNaked(void) const { return pnlNaked; }
double Hedging_Portfolio::getpnlHedged(void) const { return pnlHedged; }

/******************************* Setters ***************************************/

void Hedging_Portfolio::setDelta(const double& delta1) { delta = delta1; }
void Hedging_Portfolio::setOptionPrice(const double& optionPrice1) { optionPrice = optionPrice1; }
void Hedging_Portfolio::setB(const double& B1) { B = B1; }
void Hedging_Portfolio::setHedgingError(const double& HE1) { HE = HE1; }

void Hedging_Portfolio::setDate(const string& date1) { date = date1; }
void Hedging_Portfolio::setExpDate(const string& expDate1) { expDate = expDate1; }
void Hedging_Portfolio::setpnlNaked(const double& pnlNaked1) { pnlNaked = pnlNaked1; }
void Hedging_Portfolio::setpnlHedged(const double& pnlHedged1) { pnlHedged = pnlHedged1; }

/******************************* Other Functions ***************************************/

// Get CDF of Standard Normal
inline double N(const double& x)
{
	boost::math::normal_distribution<> stdNormal(0.0, 1.0);
	return(cdf(stdNormal, x));
}

double Hedging_Portfolio::computeDelta(void) const
{
	char flag = getFlag();
	double K = getStrikePrice();
	double S = getSpotPrice();
	double r = getRiskFreeRate();
	double T = getTimeToMaturity();
	double sigma = getVolatility();
	double d1 = (log(S / K) + ((r + (pow(sigma, 2) / 2)) * T)) / (sigma * sqrt(T));
	if (flag == 'c' || flag == 'C')
		return (N(d1));
	else if (flag == 'p' || flag == 'P')
		return (N(d1));
	else
		throw(10);
}

// Compute option price using Black Scholes formula
double Hedging_Portfolio::computeBlackScholesOptionPrice(const double& sigma) const
{
	char flag = getFlag();
	double K = getStrikePrice();
	double S = getSpotPrice();
	double r = getRiskFreeRate();
	double T = getTimeToMaturity();

	Option opt1(K, S, r, T, sigma, flag);

	double d1 = (log(S / K) + ((r + (pow(sigma, 2) / 2)) * T)) / (sigma * sqrt(T));
	double d2 = d1 - (sigma * sqrt(T));

	if (flag == 'c' || flag == 'C')
		return (S * N(d1)) - (K * exp(-r * T) * N(d2));
	else if (flag == 'p' || flag == 'P')
		return (K * exp(-r * T) * N(-d2)) - (S * N(-d1));
	else
		throw(10);
}

//double getImpliedVol(const double&, const double&, const double&,
double Hedging_Portfolio:: computeImpliedVol(double& volMax) const
{
	double flag = getFlag();
	double volMin = 0;
	double impVol;
	double TMat = getTimeToMaturity();
	double epsilon = 0.001;
	double modelPrice;
	string temp;

	while (true)
	{
		impVol = 0.5 * (volMin + volMax);
		modelPrice = computeBlackScholesOptionPrice(impVol);

		if (modelPrice > optionPrice + epsilon)
			volMax = impVol;
		else if (modelPrice < optionPrice - epsilon)
			volMin = impVol;
		else
			return(impVol);
	}
}


#include "ros_package_template/Algorithm.hpp"

#include <utility>

#include <boost/accumulators/accumulators.hpp>
#include <boost/accumulators/statistics/mean.hpp>
#include <boost/accumulators/statistics/count.hpp>

namespace ros_package_template {

using namespace boost::accumulators;

struct Algorithm::Data {
  accumulator_set<double, features<tag::mean, tag::count>> acc;
};

Algorithm::Algorithm() {
  data_ = std::make_unique<Data>();
}

Algorithm::~Algorithm() = default;

void Algorithm::addData(const double data)
{
  data_->acc(data);
}

void Algorithm::addData(const Eigen::VectorXd& data)
{
  for(auto i = 0; i < data.size(); ++i)
    addData(data[i]);
}

double Algorithm::getAverage() const
{
  return count(data_->acc) ? mean(data_->acc) : 0;
}

} /* namespace */


#ifndef NORMALIZED_NUMERICS_HH
#define NORMALIZED_NUMERICS_HH

#include <boost/static_assert.hpp>

namespace Typelib {
    namespace details {
        template<int Bits> struct sint_t;
        template<> struct sint_t<7>  { typedef int8_t type; };
        template<> struct sint_t<15> { typedef int16_t type; };
        template<> struct sint_t<31> { typedef int32_t type; };
        template<> struct sint_t<63> { typedef int64_t type; };

        template<int Bits> struct uint_t;
        template<> struct uint_t<8>  { typedef uint8_t type; };
        template<> struct uint_t<16> { typedef uint16_t type; };
        template<> struct uint_t<32> { typedef uint32_t type; };
        template<> struct uint_t<64> { typedef uint64_t type; };
    }

    /** This template converts base C types (long, int, ...) in their normalized form (uin8_t, int16_t, ...)
     * For consistency, it is also specialized for float and double */
    template<typename T>
    struct normalized_numeric_type
    {
        typedef std::numeric_limits<T>  limits;
        BOOST_STATIC_ASSERT(( limits::is_integer )); // will specialize for float and double

        typedef typename boost::mpl::if_
            < boost::mpl::bool_<std::numeric_limits<T>::is_signed>
            , details::sint_t< limits::digits >
            , details::uint_t< limits::digits >
            >::type                             getter;
        typedef typename getter::type           type;
    };

    template<> struct normalized_numeric_type<float>  { typedef float	type; };
    template<> struct normalized_numeric_type<double> { typedef double	type; };
}

#endif