#include <map>
#include <vector>
#include <iostream>
#include <iomanip>
#include <string>
#include <boost/multiprecision/cpp_bin_float.hpp>
#include <boost/random/uniform_real_distribution.hpp>
#include <boost/random/mersenne_twister.hpp>
#include "DatasetAR.h"
#include "DatasetPoly.h"
#include "GaussSeq.h"
#include "ARSeq.h"
#include "ARPoly.h"
#include "utils.h"
using utils::my_float;
using boost::multiprecision::cpp_bin_float_50;
using boost::random::uniform_real_distribution;

int main() {
    boost::random::mt19937 gen {};
    uniform_real_distribution<my_float> u(0.001, 1);

    // prepare
    std::map<int, my_float> coeff_3 {
        {1, 0.4},
        {3, -20},
    };
    std::map<int, my_float> pow_3 {
        {1, 3},
        {3, 2},
    };
    std::map<int, my_float> coeff_4 {
        {1, 0.4},
        {3, 0.1},
    };
    std::map<int, my_float> pow_4 {
        {1, 7},
        {3, 0.5},
    };
    std::vector<my_float> v_seed_three(3);

    // Non-stationary, non-linear AR process 1
    for (auto type = 0; type < 5; type++) {
        for (auto samp = 0; samp < utils::N_SAMPLE; samp++) {
            // Seed differently
            for (auto i = 0; i < 3; i++) {
                v_seed_three[i] = u(gen);
            }
            ARPoly targ_seq(coeff_3, pow_3, u(gen));
            targ_seq.seed_prev_vals(v_seed_three);

            DatasetPoly dat("./ARPoly-1/ARPoly-1-" + std::to_string(type) +
                "/Sample-" + std::to_string(samp), targ_seq, type);
            dat.write_csv();
        }
    }

    // Non-stationary, non-linear AR process 2
    for (auto type = 0; type < 5; type++) {
        for (auto samp = 0; samp < utils::N_SAMPLE; samp++) {
            // Seed differently
            for (auto i = 0; i < 3; i++) {
                v_seed_three[i] = u(gen);
            }
            ARPoly targ_seq(coeff_4, pow_4, u(gen));
            targ_seq.seed_prev_vals(v_seed_three);

            DatasetPoly dat("./ARPoly-2/ARPoly-2-" + std::to_string(type) +
                "/Sample-" + std::to_string(samp), targ_seq, type);
            dat.write_csv();
        }
    }

    return 0;
}


//  (C) Copyright Matt Borland 2021.
//  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 <cmath>
#include <cfloat>
#include <cstdint>
#include <limits>
#include <type_traits>
#include <boost/math/ccmath/remainder.hpp>
#include <boost/math/ccmath/isnan.hpp>
#include <boost/math/ccmath/isinf.hpp>

#ifdef BOOST_HAS_FLOAT128
#include <boost/multiprecision/float128.hpp>
#endif

#if !defined(BOOST_MATH_NO_CONSTEXPR_DETECTION) && !defined(BOOST_MATH_USING_BUILTIN_CONSTANT_P)
template <typename T>
constexpr void test()
{
    // Error Handling
    if constexpr (std::numeric_limits<T>::has_quiet_NaN)
    {
        static_assert(boost::math::ccmath::isnan(boost::math::ccmath::remainder(std::numeric_limits<T>::quiet_NaN(), T(1))), "If x is NaN, NaN is returned");
        static_assert(boost::math::ccmath::isnan(boost::math::ccmath::remainder(T(1), std::numeric_limits<T>::quiet_NaN())), "If y is NaN, NaN is returned");
    }

    static_assert(boost::math::ccmath::isnan(boost::math::ccmath::remainder(std::numeric_limits<T>::infinity(), T(1))));
    static_assert(boost::math::ccmath::isnan(boost::math::ccmath::remainder(-std::numeric_limits<T>::infinity(), T(1))));
    static_assert(boost::math::ccmath::isnan(boost::math::ccmath::remainder(T(1), T(0))));
    static_assert(boost::math::ccmath::isnan(boost::math::ccmath::remainder(T(1), T(-0))));

    // Functionality
    static_assert(boost::math::ccmath::remainder(T(6), T(2)) == T(0));
    static_assert(boost::math::ccmath::remainder(T(3.0/2), T(1.0) == T(3.0/2)));
    static_assert(boost::math::ccmath::remainder(T(7.0/3), T(2.0) == T(1.0/3)));
    static_assert(boost::math::ccmath::remainder(T(-8.0/3), T(2.0) == T(-2.0/3)));
    static_assert(boost::math::ccmath::remainder(T(-0), T(1)) == T(-0));

    // Not exact values but pulled from https://en.cppreference.com/w/cpp/numeric/math/remainder as general functionality tests so allow for some error
    // std::is_floating_point_v excludes multi-precision types
    if constexpr (std::is_floating_point_v<T>)
    {
        static_assert(boost::math::ccmath::abs(boost::math::ccmath::remainder(T(5.1l), T(3.0l)) - T(-0.9l)) < 2*std::numeric_limits<T>::epsilon());
        static_assert(boost::math::ccmath::abs(boost::math::ccmath::remainder(T(-5.1l), T(3.0l)) - T(0.9l)) < 2*std::numeric_limits<T>::epsilon());
        static_assert(boost::math::ccmath::abs(boost::math::ccmath::remainder(T(5.1l), T(-3.0l)) - T(-0.9l)) < 2*std::numeric_limits<T>::epsilon());
        static_assert(boost::math::ccmath::abs(boost::math::ccmath::remainder(T(-5.1l), T(-3.0l)) - T(0.9l)) < 2*std::numeric_limits<T>::epsilon());
    }

    // Correct promoted types
    if constexpr (!std::is_same_v<T, float>)
    {
        constexpr auto test_type = boost::math::ccmath::remainder(T(1), 1.0f);
        static_assert(std::is_same_v<T, std::remove_cv_t<decltype(test_type)>>);
    }
    else
    {
        constexpr auto test_type = boost::math::ccmath::remainder(1.0f, 1);
        static_assert(std::is_same_v<double, std::remove_cv_t<decltype(test_type)>>);
    }
}

int main()
{
    test<float>();
    test<double>();

    #ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
    test<long double>();
    #endif

    #ifdef BOOST_HAS_FLOAT128
    test<boost::multiprecision::float128>();
    #endif

    return 0;
}
#else
int main()
{
    return 0;
}
#endif


#include <limits>

#include <gtest/gtest.h>

#include <Eigen/Dense>
#include <rsvd/Constants.hpp>
#include <rsvd/ErrorEstimators.hpp>
#include <rsvd/RandomizedSvd.hpp>

using Eigen::Index;

template <typename T> struct RandomizedSvd : public ::testing::Test {
  using MatrixType = Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic>;

  const Index numRows = 50;
  const Index numCols = 25;
  const Index rank = 5;
  const unsigned int prngSeed = 777;
};

using NumericalTypes = ::testing::Types<float, double, std::complex<float>, std::complex<double>>;

TYPED_TEST_CASE(RandomizedSvd, NumericalTypes, );

TYPED_TEST(RandomizedSvd, ExactRankApproximationNoConditioner) {
  using MatrixType = typename TestFixture::MatrixType;

  std::srand(TestFixture::prngSeed);
  const MatrixType m = MatrixType::Random(TestFixture::numRows, TestFixture::rank) *
                       MatrixType::Random(TestFixture::rank, TestFixture::numCols);

  Eigen::JacobiSVD<MatrixType> svd(m, Eigen::ComputeThinU | Eigen::ComputeThinV);
  const MatrixType reconstructedSvd =
      svd.matrixU() * svd.singularValues().asDiagonal() * svd.matrixV().adjoint();
  const auto relErrSvd = Rsvd::relativeFrobeniusNormError(m, reconstructedSvd);

  std::mt19937_64 randomEngine;
  randomEngine.seed(TestFixture::prngSeed);
  Rsvd::RandomizedSvd<MatrixType, std::mt19937_64, Rsvd::NoConditioner> rsvd(randomEngine);
  rsvd.compute(m, TestFixture::rank);
  const MatrixType reconstructedRsvd =
      rsvd.matrixU() * rsvd.singularValues().asDiagonal() * rsvd.matrixV().adjoint();
  const auto relErrRsvd = Rsvd::relativeFrobeniusNormError(m, reconstructedRsvd);

  ASSERT_LE(relErrRsvd, 1.15 * relErrSvd);
}

TYPED_TEST(RandomizedSvd, ExactRankApproximationLuConditioner) {
  using MatrixType = typename TestFixture::MatrixType;

  std::srand(TestFixture::prngSeed);
  const MatrixType m = MatrixType::Random(TestFixture::numRows, TestFixture::rank) *
                       MatrixType::Random(TestFixture::rank, TestFixture::numCols);

  Eigen::JacobiSVD<MatrixType> svd(m, Eigen::ComputeThinU | Eigen::ComputeThinV);
  const MatrixType reconstructedSvd =
      svd.matrixU() * svd.singularValues().asDiagonal() * svd.matrixV().adjoint();
  const auto relErrSvd = Rsvd::relativeFrobeniusNormError(m, reconstructedSvd);

  std::mt19937_64 randomEngine;
  randomEngine.seed(TestFixture::prngSeed);
  Rsvd::RandomizedSvd<MatrixType, std::mt19937_64, Rsvd::LuConditioner> rsvd(randomEngine);
  rsvd.compute(m, TestFixture::rank);
  const MatrixType reconstructedRsvd =
      rsvd.matrixU() * rsvd.singularValues().asDiagonal() * rsvd.matrixV().adjoint();
  const auto relErrRsvd = Rsvd::relativeFrobeniusNormError(m, reconstructedRsvd);

  ASSERT_LE(relErrRsvd, 1.05 * relErrSvd);
}

TYPED_TEST(RandomizedSvd, ExactRankApproximationMgsConditioner) {
  using MatrixType = typename TestFixture::MatrixType;

  std::srand(TestFixture::prngSeed);
  const MatrixType m = MatrixType::Random(TestFixture::numRows, TestFixture::rank) *
                       MatrixType::Random(TestFixture::rank, TestFixture::numCols);

  Eigen::JacobiSVD<MatrixType> svd(m, Eigen::ComputeThinU | Eigen::ComputeThinV);
  const MatrixType reconstructedSvd =
      svd.matrixU() * svd.singularValues().asDiagonal() * svd.matrixV().adjoint();
  const auto relErrSvd = Rsvd::relativeFrobeniusNormError(m, reconstructedSvd);

  std::mt19937_64 randomEngine;
  randomEngine.seed(TestFixture::prngSeed);
  Rsvd::RandomizedSvd<MatrixType, std::mt19937_64, Rsvd::MgsConditioner> rsvd(randomEngine);
  rsvd.compute(m, TestFixture::rank);
  const MatrixType reconstructedRsvd =
      rsvd.matrixU() * rsvd.singularValues().asDiagonal() * rsvd.matrixV().adjoint();
  const auto relErrRsvd = Rsvd::relativeFrobeniusNormError(m, reconstructedRsvd);

  ASSERT_LE(relErrRsvd, 1.30 * relErrSvd);
}

TYPED_TEST(RandomizedSvd, OversamplingMgsConditioner) {
  using MatrixType = typename TestFixture::MatrixType;

  std::srand(TestFixture::prngSeed);
  const MatrixType m = MatrixType::Random(TestFixture::numRows, TestFixture::rank) *
                       MatrixType::Random(TestFixture::rank, TestFixture::numCols);

  Eigen::JacobiSVD<MatrixType> svd(m, Eigen::ComputeThinU | Eigen::ComputeThinV);
  const MatrixType reconstructedSvd =
      svd.matrixU() * svd.singularValues().asDiagonal() * svd.matrixV().adjoint();
  const auto relErrSvd = Rsvd::relativeFrobeniusNormError(m, reconstructedSvd);

  std::mt19937_64 randomEngine;
  randomEngine.seed(TestFixture::prngSeed);
  Rsvd::RandomizedSvd<MatrixType, std::mt19937_64, Rsvd::MgsConditioner> rsvd(randomEngine);
  // Oversample (twice the rank)
  rsvd.compute(m, TestFixture::rank, TestFixture::rank);
  const MatrixType reconstructedRsvd =
      rsvd.matrixU() * rsvd.singularValues().asDiagonal() * rsvd.matrixV().adjoint();
  const auto relErrRsvd = Rsvd::relativeFrobeniusNormError(m, reconstructedRsvd);

  ASSERT_LE(relErrRsvd, 1.30 * relErrSvd);
}

TYPED_TEST(RandomizedSvd, ExactRankApproximationQrConditioner) {
  using MatrixType = typename TestFixture::MatrixType;

  std::srand(TestFixture::prngSeed);
  const MatrixType m = MatrixType::Random(TestFixture::numRows, TestFixture::rank) *
                       MatrixType::Random(TestFixture::rank, TestFixture::numCols);

  Eigen::JacobiSVD<MatrixType> svd(m, Eigen::ComputeThinU | Eigen::ComputeThinV);
  const MatrixType reconstructedSvd =
      svd.matrixU() * svd.singularValues().asDiagonal() * svd.matrixV().adjoint();
  const auto relErrSvd = Rsvd::relativeFrobeniusNormError(m, reconstructedSvd);

  std::mt19937_64 randomEngine;
  randomEngine.seed(TestFixture::prngSeed);
  Rsvd::RandomizedSvd<MatrixType, std::mt19937_64, Rsvd::QrConditioner> rsvd(randomEngine);
  rsvd.compute(m, TestFixture::rank);
  const MatrixType reconstructedRsvd =
      rsvd.matrixU() * rsvd.singularValues().asDiagonal() * rsvd.matrixV().adjoint();
  const auto relErrRsvd = Rsvd::relativeFrobeniusNormError(m, reconstructedRsvd);

  ASSERT_LE(relErrRsvd, 1.05 * relErrSvd);
}


//
// Copyright 1997, University of Notre Dame.
// Authors: Andrew Lumsdaine, Jeremy G. Siek
//
// This file is part of the Matrix Template Library
//
// You should have received a copy of the License Agreement for the
// Matrix Template Library along with the software;  see the
// file LICENSE.  If not, contact Office of Research, University of Notre
// Dame, Notre Dame, IN  46556.
//
// Permission to modify the code and to distribute modified code is
// granted, provided the text of this NOTICE is retained, a notice that
// the code was modified is included with the above COPYRIGHT NOTICE and
// with the COPYRIGHT NOTICE in the LICENSE file, and that the LICENSE
// file is distributed with the modified code.
//
// LICENSOR MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED.
// By way of example, but not limitation, Licensor MAKES NO
// REPRESENTATIONS OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY
// PARTICULAR PURPOSE OR THAT THE USE OF THE LICENSED SOFTWARE COMPONENTS
// OR DOCUMENTATION WILL NOT INFRINGE ANY PATENTS, COPYRIGHTS, TRADEMARKS
// OR OTHER RIGHTS.
//
// $Id: symm_matvec_mult.cc 1749 2004-01-27 00:01:18Z gabrielt $
//

#include <mtl/dense1D.h>
#include <mtl/mtl.h>
#include <mtl/matrix.h>

/*

  Sample Output

  A in full form:
  4x4
  [
  [1,2,3,4],
  [2,5,6,7],
  [3,6,8,9],
  [4,7,9,10]
  ]
  x:
  [2,2,2,2,]
  y:
  [4000,5000,6000,7000,]
  Ax + y:
  [20,40,52,60,]

 */

using namespace mtl;

//begin
typedef matrix<double, symmetric<lower>, array< dense<> >, row_major>::type Matrix;
typedef dense1D<double> Vector;
//end

int
main()
{
  typedef Matrix::size_type sizeT;
  sizeT i, j;
  const sizeT N = 4;
  //begin
  Matrix A(N);
  Vector x(N);
  Vector y(N);
  //end
  //         1  2  3  4       2       4000
  //
  //     A = 2  5  6  7   x = 2   y = 5000
  //
  //         3  6  8  9       2       6000
  //
  //         4  7  9 10       2       7000

  int c = 0;
  for (i = 0; i < N; ++i)
    for (j = i; j < N; ++j)
      A(i,j) = ++c;

  for (i = 0; i < N; ++i) {
    x[i] = 2;
    y[i] = (i + 4) * 1000;
  }

  std::cout << "A in full form:" << std::endl;
  print_all_matrix(A);

  std::cout << "x:" << std::endl;
  print_vector(x);

  std::cout << "y:" << std::endl;
  print_vector(y);
  //begin
  mult(A, x, y);

  std::cout << "Ax + y:" << std::endl;
  print_vector(y);
  //end
  return 0;
}


//! [mathematical-all]
#include <cmath>
#include <iostream>
#include <string>
#include <vector>

#include <boost/align/aligned_allocator.hpp>
#include <boost/align/aligned_delete.hpp>

#include <boost/simd/constant/pio_4.hpp>
#include <boost/simd/function/cos.hpp>
#include <boost/simd/function/load.hpp>
#include <boost/simd/function/sin.hpp>
#include <boost/simd/function/sincos.hpp>
#include <boost/simd/function/store.hpp>
#include <boost/simd/function/ulpdist.hpp>
#include <boost/simd/pack.hpp>

namespace bs = boost::simd;

template <typename T>
void test_results(const std::string& mes, const T& scalr, const T& simdr)
{
  std::cout << mes;
  for (int i = 0; i < scalr.size(); ++i) {
    if (bs::ulpdist(scalr[i], simdr[i]) > 0.5) {
      std::cout << " failed" << std::endl;
      return;
    }
  }
  std::cout << " succeeded" << std::endl;
}

int main(int argc, char** argv)
{
  namespace ba = boost::alignment;

  using pack_t = bs::pack<float>;

  std::size_t num_elements = 1024;
  std::size_t alignment    = pack_t::alignment;
  //! [mathematical-declare]
  std::vector<float, ba::aligned_allocator<float, pack_t::alignment>> X(num_elements);
  std::vector<float, ba::aligned_allocator<float, pack_t::alignment>> sinX(num_elements),
    sc_sinX(num_elements);
  std::vector<float, ba::aligned_allocator<float, pack_t::alignment>> cosX(num_elements),
    sc_cosX(num_elements);
  //! [mathematical-declare]

  //! [fill-input]
  for (int i = 0; i < num_elements; ++i) {
    X[i] = (float(i) / num_elements) * bs::Pio_4<float>();
  }

  //! [fill-input]
  //! [mathematical-scalar]
  for (int i = 0; i < num_elements; ++i) {
    sc_sinX[i] = std::sin(X[i]);
    sc_cosX[i] = std::cos(X[i]);
  }
  //! [mathematical-scalar]

  //! [mathematical-calc-individ]
  for (int i = 0; i < num_elements; i += pack_t::static_size) {
    pack_t v0 = bs::load<pack_t>(&X[i]);
    bs::store(bs::sin(v0), &sinX[i]);
    bs::store(bs::cos(v0), &cosX[i]);
  }
  //! [mathematical-calc-individ]
  test_results("sin test               ", sc_sinX, sinX);
  test_results("cos test               ", sc_cosX, cosX);

  //! [mathematical-calc-combine]
  for (int i = 0; i < num_elements; i += pack_t::static_size) {
    pack_t v0 = bs::load<pack_t>(&X[i]);
    auto res  = bs::sincos(v0);
    bs::store(res.first, &sinX[i]);
    bs::store(res.second, &cosX[i]);
  }
  //! [mathematical-calc-combine]
  test_results("sincos test for sin    ", sc_sinX, sinX);
  test_results("sincos test for cos    ", sc_cosX, cosX);

  //! [mathematical-calc-restricted]
  for (int i = 0; i < num_elements; i += pack_t::static_size) {
    pack_t v0 = bs::load<pack_t>(&X[i]);
    bs::store(bs::restricted_(bs::sin)(v0), &sinX[i]);
    bs::store(bs::restricted_(bs::cos)(v0), &cosX[i]);
  }
  //! [mathematical-calc-restricted]
  test_results("restricted_(sin) test  ", sc_sinX, sinX);
  test_results("restricted_(cos) test  ", sc_cosX, cosX);

  return 0;
}
// This code can be compiled using (for instance for gcc)
// g++ mathematical.cpp -msse4.2 -std=c++11 -O3 -DNDEBUG -o mathematical
// -I/path_to/boost_simd/ -I/path_to/boost/

//! [mathematical-all]


/* vim: set tabstop=4 expandtab shiftwidth=4 softtabstop=4: */

/**
 * \file boost/numeric/ublasx/operation/sign.hpp
 *
 * \brief Compute the sign function for each element of a vector or matrix
 *  expression.
 *
 * The sign function for real numbers as defined as follows:
 * \f[
 * \operatorname{sign}(x):=\begin{cases}
 *                          -1 & \text{if } x<0,\\
 *                           0 & \text{if } x=0,\\
 *                           1 & \text{if } x>0.
 *                         \end{cases}
 * \f]
 * In case of complex numbers, the sign function is defined as follows:
 * \f[
 * \operatorname{sign}(z)=\begin{cases}
 *                          \frac{z}{|z|} & \text{if } z \ne 0,\\
 *                          0 & \text{if } z = 0+0i.
 *                        \end{cases}
 * \f]
 *
 * \sa The sign function at Wikipedia: https://en.wikipedia.org/wiki/Sign_function
 *
 * \author comcon1 (original version)
 * \author Marco Guazzone (marco.guazzone@gmail.com)
 *
 * <hr/>
 *
 * Distributed under the Boost Software License, Version 1.0. (See
 * accompanying file LICENSE_1_0.txt or copy at
 * http://www.boost.org/LICENSE_1_0.txt)
 */

#ifndef BOOST_NUMERIC_UBLASX_OPERATION_SIGN_HPP
#define BOOST_NUMERIC_UBLASX_OPERATION_SIGN_HPP

#include <boost/numeric/ublas/traits.hpp>
#include <boost/numeric/ublasx/expression/matrix_unary_functor.hpp>
#include <boost/numeric/ublasx/expression/vector_unary_functor.hpp>
#include <boost/type_traits/is_complex.hpp>
#include <boost/utility/enable_if.hpp>
#include <cmath>
#include <complex>
#include <limits>

namespace boost { namespace numeric { namespace ublasx {

using namespace ::boost::numeric::ublas;

namespace detail {

template <typename VectorExprT>
struct vector_sign_functor_traits
{
    typedef VectorExprT input_expression_type;
    typedef typename vector_traits<input_expression_type>::value_type signature_argument_type;
    typedef typename type_traits<signature_argument_type>::value_type signature_result_type;
    typedef vector_unary_functor_traits<
                input_expression_type,
                signature_result_type (signature_argument_type)
            > unary_functor_expression_type;
    typedef typename unary_functor_expression_type::result_type result_type;
    typedef typename unary_functor_expression_type::expression_type expression_type;
};

template <typename MatrixExprT>
struct matrix_sign_functor_traits
{
    typedef MatrixExprT input_expression_type;
    typedef typename matrix_traits<input_expression_type>::value_type signature_argument_type;
    typedef typename type_traits<signature_argument_type>::value_type signature_result_type;
    typedef matrix_unary_functor_traits<
                input_expression_type,
                signature_result_type (signature_argument_type)
            > unary_functor_expression_type;
    typedef typename unary_functor_expression_type::result_type result_type;
    typedef typename unary_functor_expression_type::expression_type expression_type;
};

/// Auxiliary function for real types: sign(x) = 1 if x > 0, 0 if x == 0, -1 otherwise.
template <typename T>
BOOST_UBLAS_INLINE
typename ::boost::disable_if<
            ::boost::is_complex<T>,
            T
>::type sign_impl(T x)
{
    if (::std::isnan(x))
    {
        return ::std::numeric_limits<T>::quiet_NaN();
    }
    return (x > 0) ? 1 : ((x < 0) ? -1 : 0);
}

/// Auxiliary function for complex types: sign(x) = x ./ abs(x)
template <typename T>
BOOST_UBLAS_INLINE
typename ::boost::enable_if<
            ::boost::is_complex<T>,
            T
>::type sign_impl(T x)
{
    typename T::value_type a = ::std::abs(x);
    return (a == 0) ? T(0,0) : (x / a);
}

//template <typename RealType>
//BOOST_UBLAS_INLINE
//RealType sign(RealType v)
//{
//    return ( -(RealType)( ::std::signbit(v) ) + 0.5 ) * 2.0;
//}

} // Namespace detail

/**
 * \brief Applies the \c std::sign function to a given vector expression.
 *
 * \tparam VectorExprT The type of the input vector expression.
 *
 * \param ve The input vector expression.
 * \return A vector expression representing the application of \c std::sign to
 *  each element of \a ve.
 *
 * \author Marco Guazzone, marco.guazzone@gmail.com
 */
template <typename VectorExprT>
BOOST_UBLAS_INLINE
typename detail::vector_sign_functor_traits<VectorExprT>::result_type sign(vector_expression<VectorExprT> const& ve)
{
    typedef typename detail::vector_sign_functor_traits<VectorExprT>::expression_type expression_type;
    typedef typename detail::vector_sign_functor_traits<VectorExprT>::signature_result_type signature_result_type;

    return expression_type(ve(), detail::sign_impl<signature_result_type>);
}

/**
 * \brief Applies the \c std::sign function to a given matrix expression.
 *
 * \tparam MatrixExprT The type of the input matrix expression.
 *
 * \param me The input matrix expression.
 * \return A matrix expression representing the application of \c std::sign to
 *  each element of \a me.
 *
 * \author Marco Guazzone, marco.guazzone@gmail.com
 */
template <typename MatrixExprT>
BOOST_UBLAS_INLINE
typename detail::matrix_sign_functor_traits<MatrixExprT>::result_type sign(matrix_expression<MatrixExprT> const& me)
{
    typedef typename detail::matrix_sign_functor_traits<MatrixExprT>::expression_type expression_type;
    typedef typename detail::matrix_sign_functor_traits<MatrixExprT>::signature_result_type signature_result_type;

    return expression_type(me(), detail::sign_impl<signature_result_type>);
}

}}} // Namespace boost::numeric::ublasx

#endif // BOOST_NUMERIC_UBLASX_OPERATION_SIGN_HPP


/**
 * @file  maximumprinciple.cc
 * @brief NPDE homework "MaximumPrinciple" code
 * @author Oliver Rietmann
 * @date 25.03.2019
 * @copyright Developed at ETH Zurich
 */

#include "maximumprinciple.h"

#include <Eigen/Core>
#include <Eigen/SparseCore>
#include <vector>

namespace MaximumPrinciple {

/**
 * @brief Assembly on a tensor product mesh
 *
 * Compute the global Galerkin matrix from the local
 * element matrix.
 *
 * @param M Number of interior vertices in x and y direction.
 * @param B_K Local element matrix.
 * @return Global Galerkin matrix of size M^2 times M^2.
 */
/* SAM_LISTING_BEGIN_1 */
Eigen::SparseMatrix<double> assemble(int M, const Eigen::Matrix3d &B_K) {
  int M2 = M * M;
  Eigen::SparseMatrix<double> A(M2, M2);
  double near_neighbour_contribution = 2.0 * B_K(0, 1);
  double far_neighbour_contribution = 2.0 * B_K(1, 2);
  double self_contribution = 2.0 * (B_K(0, 0) + B_K(1, 1) + B_K(2, 2));

  std::vector<double> contribution = {
      far_neighbour_contribution,  near_neighbour_contribution,
      near_neighbour_contribution, self_contribution,
      near_neighbour_contribution, near_neighbour_contribution,
      far_neighbour_contribution};

  std::vector<Eigen::Vector2i> shift = {{-1, -1}, {0, -1}, {-1, 0}, {0, 0},
                                        {1, 0},   {0, 1},  {1, 1}};

  std::vector<Eigen::Triplet<double>> tripletList;
  for (int i = 0; i < M; ++i) {
    for (int j = 0; j < M; ++j) {
      Eigen::Vector2i self = Eigen::Vector2i(i, j);
      for (int k = 0; k < 7; ++k) {
        Eigen::Vector2i other = self + shift[k];
        if (0 <= other(0) && other(0) < M && 0 <= other(1) && other(1) < M) {
          tripletList.push_back(Eigen::Triplet<double>(
              self(0) + M * self(1), other(0) + M * other(1), contribution[k]));
        }
      }
    }
  }
  A.setFromTriplets(tripletList.begin(), tripletList.end());
  return A;
}
/* SAM_LISTING_END_1 */

/* SAM_LISTING_BEGIN_2 */
Eigen::SparseMatrix<double> computeGalerkinMatrix(int M, double c) {
  Eigen::Matrix3d B_K;
  double h = 1.0 / (M + 1);
  Eigen::Matrix3d A_K;
  A_K << 1.0, -0.5, -0.5, -0.5, 0.5, 0.0, -0.5, 0.0, 0.5;
  Eigen::Matrix3d M_K;
  M_K << 2.0, 1.0, 1.0, 1.0, 2.0, 1.0, 1.0, 1.0, 2.0;
  M_K *= h * h / 24.0;
  B_K = (1.0 - c) * A_K + c * M_K;
  return assemble(M, B_K);
}
/* SAM_LISTING_END_2 */

/* SAM_LISTING_BEGIN_4 */
Eigen::SparseMatrix<double> computeGalerkinMatrixTR(int M, double c) {
  Eigen::Matrix3d B_K;
  double h = 1.0 / (M + 1);
  Eigen::Matrix3d A_K;
  A_K << 1.0, -0.5, -0.5, -0.5, 0.5, 0.0, -0.5, 0.0, 0.5;
  Eigen::Matrix3d M_K = h * h / 6.0 * Eigen::Matrix3d::Identity();
  B_K = (1.0 - c) * A_K + c * M_K;
  return assemble(M, B_K);
}
/* SAM_LISTING_END_4 */

}  // namespace MaximumPrinciple


#ifndef MLT_MODELS_OPTIMIZABLE_LINEAR_MODEL_HPP
#define MLT_MODELS_OPTIMIZABLE_LINEAR_MODEL_HPP

#include <tuple>
#include <type_traits>

#include <Eigen/Core>

#include "../defs.hpp"
#include "../utils/eigen.hpp"
#include "../utils/linear_algebra.hpp"

namespace mlt {
namespace models {
	template <class LinearBase, class Loss, class Optimizer>
	class OptimizableLinearModel : public LinearBase {
	public:
		Self& fit(Features input, Target target, bool cold_start = true) {
			auto target_matrix = _to_target_matrix(target);
			auto init = _fitted && !cold_start ? coefficients() : (MatrixXd::Random(target_matrix.rows(), input.rows() + (fit_intercept() ? 1 : 0)) * 0.005).eval();
			_set_coefficients(_optimizer(*this, input, target_matrix, init, cold_start));

			return _self();
		}

		auto loss(MatrixXdRef coeffs, Features input, MatrixXdRef target) const {
			auto l = _loss.loss(_apply_linear_transformation(input, coeffs), target);
			if (_fit_intercept) {
				return l + _regularization * (coeffs.leftCols(coeffs.cols() - 1).array().pow(2).sum());
			}
			else {
				return l + _regularization * (coeffs.array().pow(2).sum());
			}
		}

		auto gradient(MatrixXdRef coeffs, Features input, MatrixXdRef target) const {
			auto g = _loss.gradient(_apply_linear_transformation(input, coeffs), target);

			if (_fit_intercept) {
				auto full_g = MatrixXd::Zero(coeffs.rows(), coeffs.cols()).eval();
				full_g.leftCols(coeffs.cols() - 1) = g * input.transpose() + _regularization * 2 * coeffs.leftCols(coeffs.cols() - 1);
				full_g.rightCols<1>() = g.rowwise().sum();
				return full_g;
			} else {
				return (g * input.transpose() + _regularization * 2 * coeffs).eval();
			}
		}

		auto loss_and_gradient(MatrixXdRef coeffs, Features input, MatrixXdRef target) const {
			double l;
			MatrixXd g;

			tie(l, g) = _loss.loss_and_gradient(_apply_linear_transformation(input, coeffs), target);
			if (_fit_intercept) {
				auto full_g = MatrixXd::Zero(coeffs.rows(), coeffs.cols()).eval();
				full_g.leftCols(coeffs.cols() - 1) = g * input.transpose() + _regularization * 2 * coeffs.leftCols(coeffs.cols() - 1);
				full_g.rightCols<1>() = g.rowwise().sum();
				return make_tuple(l + _regularization * (coeffs.leftCols(coeffs.cols() - 1).array().pow(2).sum()) , full_g);
			}
			else {
				return make_tuple(l + _regularization * coeffs.array().pow(2).sum(), (g * input.transpose() + _regularization * 2 * coeffs).eval());
			}
		}

	protected:
		template <class L, class O, class = enable_if<is_same<decay_t<L>, Loss>::value && is_convertible<decay_t<O>, Optimizer>::value>>
		explicit OptimizableLinearModel(L&& loss, O&& optimizer, double regularization, bool fit_intercept) : LinearBase(fit_intercept), _loss(forward<L>(loss)), _optimizer(forward<O>(optimizer)), _regularization(regularization) {}

		Loss _loss;
		Optimizer _optimizer;
		double _regularization;
	};
}
}
#endif


// ideal_random_walk.cpp

#include <algorithm>
#include <fstream>

#include <boost/math/special_functions/factorials.hpp>

#include "ideal_random_walk.h"

namespace idealRandomWalk {

using boost::math::factorial;

long double IdealRandomWalks::num_walks(
        VectorThree start_pos,
        VectorThree end_pos,
        int steps) {

    // Check stored values
    VectorThree DR {end_pos - start_pos};

    // Only work with one permutation of DR
    DR = DR.absolute().sort();
    pair<VectorThree, int> walk_key {DR, steps};
    if (m_num_walks.count(walk_key)) {
        return m_num_walks.at(walk_key);
    }
    int DX {DR[0]};
    int DY {DR[1]};
    int DZ {DR[2]};
    int Nminus {(steps - DX - DY - DZ) / 2};
    int Nplus {(steps - DX - DY + DZ) / 2};
    long double walks {0};

    int DR_sum {DX + DY + DZ};
    if (DR_sum > steps or (steps - DR_sum) % 2 != 0) {

        // Add entry
        m_num_walks[walk_key] = walks;
        return walks;
    }

    // Need some negative steps to reach a negative location
    for (int ybar {0}; ybar != Nminus + 1; ybar++) {
        for (int xbar {0}; xbar != Nminus + 1 - ybar; xbar++) {
            auto f1 {factorial<long double>(steps)};
            auto f2 {factorial<long double>(xbar)};
            if (xbar + DX < 0) {
                continue;
            }

            auto f3 {factorial<long double>(xbar + DX)};
            auto f4 {factorial<long double>(ybar)};
            if (ybar + DY < 0) {
                continue;
            }

            auto f5 {factorial<long double>(ybar + DY)};
            auto f6 {factorial<long double>(Nminus - xbar - ybar)};
            if (Nplus - xbar - ybar < 0) {
                continue;
            }

            auto f7 {factorial<long double>(Nplus - xbar - ybar)};
            walks += f1 / (f2 * f3 * f4 * f5 * f6 * f7);
        }
    }

    // Add entries
    m_num_walks[walk_key] = walks;

    return walks;
}

void IdealRandomWalks::delete_entry(
        VectorThree start_pos,
        VectorThree end_pos,
        int steps) {

    VectorThree DR {end_pos - start_pos};
    DR = DR.absolute().sort();
    pair<VectorThree, int> walk_key {DR, steps};
    m_num_walks.erase(walk_key);
}
} // namespace idealRandomWalk


/* pcmsolver_copyright_start */
/*
 *     PCMSolver, an API for the Polarizable Continuum Model
 *     Copyright (C) 2013-2016 Roberto Di Remigio, Luca Frediani and contributors
 *
 *     This file is part of PCMSolver.
 *
 *     PCMSolver is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU Lesser General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     PCMSolver 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
 *     GNU Lesser General Public License for more details.
 *
 *     You should have received a copy of the GNU Lesser General Public License
 *     along with PCMSolver.  If not, see <http://www.gnu.org/licenses/>.
 *
 *     For information on the complete list of contributors to the
 *     PCMSolver API, see: <http://pcmsolver.readthedocs.io/>
 */
/* pcmsolver_copyright_end */

#include "catch.hpp"

#include <cmath>
#include <iomanip>
#include <iostream>
#include <limits>

#include <Eigen/Core>

#include "bi_operators/CollocationIntegrator.hpp"
#include "green/DerivativeTypes.hpp"
#include "green/SphericalDiffuse.hpp"
#include "green/dielectric_profile/OneLayerErf.hpp"
#include "green/dielectric_profile/OneLayerTanh.hpp"

SCENARIO("Evaluation of the spherical diffuse Green's function and its derivatives", "[green][green_spherical_diffuse]")
{
    GIVEN("A permittivity profile modelled by the hyperbolic tangent function")
    {
        int maxL = 3;
        // High dielectric constant inside
        double eps1 = 80.0;
        // Low dielectric constant outside
        double eps2 = 2.0;
        double sphereRadius = 100.0;
        double width = 5.0;
        // Evaluation inside the sphere
        Eigen::Vector3d source1 = (Eigen::Vector3d() << 1.0, 0.0, 0.0).finished();
        Eigen::Vector3d sourceNormal1 = source1;
        sourceNormal1.normalize();
        Eigen::Vector3d probe1 = (Eigen::Vector3d() << 2.0, 0.0, 0.0).finished();
        Eigen::Vector3d probeNormal1 = probe1;
        probeNormal1.normalize();
        // Evaluation outside the sphere
        Eigen::Vector3d source2 = (Eigen::Vector3d() << 150.0, 150.0, 150.0).finished();
        Eigen::Vector3d sourceNormal2 = source2;
        sourceNormal2.normalize();
        Eigen::Vector3d probe2 = (Eigen::Vector3d() << 151.0, 150.0, 150.0).finished();
        Eigen::Vector3d probeNormal2 = probe2;
        probeNormal2.normalize();
        WHEN("the spherical droplet is centered at the origin")
        {
            Eigen::Vector3d sphereCenter = Eigen::Vector3d::Zero();
            SphericalDiffuse<> gf(eps1, eps2, width, sphereRadius, sphereCenter, maxL);
            THEN("the value of the Green's function inside the droplet is")
            {
                double value = 0.012507311388168523;
                double gf_value = gf.kernelS(source1, probe1);
                INFO("ref_value = " << std::setprecision(std::numeric_limits<long double>::digits10) << value);
                INFO("gf_value  = " << std::setprecision(std::numeric_limits<long double>::digits10) << gf_value);
                REQUIRE(value == Approx(gf_value));
            }
            AND_THEN("the value of the Green's function outside the droplet is")
            {
                double value = 0.50004567416494572;
                double gf_value = gf.kernelS(source2, probe2);
                INFO("ref_value = " << std::setprecision(std::numeric_limits<long double>::digits10) << value);
                INFO("gf_value  = " << std::setprecision(std::numeric_limits<long double>::digits10) << gf_value);
                REQUIRE(value == Approx(gf_value));
            }
            THEN("the value of the Green's function directional derivative wrt the probe point inside the droplet is")
            {
                double derProbe = -0.012506305835640469;
                double gf_derProbe = gf.derivativeProbe(probeNormal1, source1, probe1);
                INFO("ref_derProbe = " << std::setprecision(std::numeric_limits<long double>::digits10) << derProbe);
                INFO("gf_derProbe  = " << std::setprecision(std::numeric_limits<long double>::digits10) << gf_derProbe);
                REQUIRE(derProbe == Approx(gf_derProbe));
            }
            AND_THEN("the value of the Green's function directional derivative wrt the probe point outside the droplet is")
            {
                double derProbe = -0.29005549287308696;
                double gf_derProbe = gf.derivativeProbe(probeNormal2, source2, probe2);
                INFO("ref_derProbe = " << std::setprecision(std::numeric_limits<long double>::digits10) << derProbe);
                INFO("gf_derProbe  = " << std::setprecision(std::numeric_limits<long double>::digits10) << gf_derProbe);
                REQUIRE(derProbe == Approx(gf_derProbe));
            }
            THEN("the value of the Green's function directional derivative wrt the source point inside the droplet is")
            {
                double derSource = 0.012498621932118328;
                double gf_derSource = gf.derivativeSource(sourceNormal1, source1, probe1);
                INFO("ref_derSource = " << std::setprecision(std::numeric_limits<long double>::digits10) << derSource);
                INFO("gf_derSource  = " << std::setprecision(std::numeric_limits<long double>::digits10) << gf_derSource);
                REQUIRE(derSource == Approx(gf_derSource));
            }
            AND_THEN("the value of the Green's function directional derivative wrt the source point outside the droplet is")
            {
                double derSource = 0.28879776627577236;
                double gf_derSource = gf.derivativeSource(sourceNormal2, source2, probe2);
                INFO("ref_derSource = " << std::setprecision(std::numeric_limits<long double>::digits10) << derSource);
                INFO("gf_derSource  = " << std::setprecision(std::numeric_limits<long double>::digits10) << gf_derSource);
                REQUIRE(derSource == Approx(gf_derSource));
            }
        }

        AND_WHEN("the spherical droplet is centered away from the origin")
        {
            Eigen::Vector3d sphereCenter = (Eigen::Vector3d() << 25.0, 0.0, 0.0).finished();
            SphericalDiffuse<> gf(eps1, eps2, width, sphereRadius, sphereCenter, maxL);
            THEN("the value of the Green's function inside the droplet is")
            {
                double value = 0.0125233347669694017;
                double gf_value = gf.kernelS(source1, probe1);
                INFO("ref_value = " << std::setprecision(std::numeric_limits<long double>::digits10) << value);
                INFO("gf_value  = " << std::setprecision(std::numeric_limits<long double>::digits10) << gf_value);
                REQUIRE(value == Approx(gf_value));
            }
            AND_THEN("the value of the Green's function outside the droplet is")
            {
                double value = 0.5000329900631173;
                double gf_value = gf.kernelS(source2, probe2);
                INFO("ref_value = " << std::setprecision(std::numeric_limits<long double>::digits10) << value);
                INFO("gf_value  = " << std::setprecision(std::numeric_limits<long double>::digits10) << gf_value);
                REQUIRE(value == Approx(gf_value));
            }
            THEN("the value of the Green's function directional derivative wrt the probe point inside the droplet is")
            {
                double derProbe = -0.0125024363466751109;
                double gf_derProbe = gf.derivativeProbe(probeNormal1, source1, probe1);
                INFO("ref_derProbe = " << std::setprecision(std::numeric_limits<long double>::digits10) << derProbe);
                INFO("gf_derProbe  = " << std::setprecision(std::numeric_limits<long double>::digits10) << gf_derProbe);
                REQUIRE(derProbe == Approx(gf_derProbe));
            }
            AND_THEN("the value of the Green's function directional derivative wrt the probe point outside the droplet is")
            {
                double derProbe = -0.289999709125188243;
                double gf_derProbe = gf.derivativeProbe(probeNormal2, source2, probe2);
                INFO("ref_derProbe = " << std::setprecision(std::numeric_limits<long double>::digits10) << derProbe);
                INFO("gf_derProbe  = " << std::setprecision(std::numeric_limits<long double>::digits10) << gf_derProbe);
                REQUIRE(derProbe == Approx(gf_derProbe));
            }
            THEN("the value of the Green's function directional derivative wrt the source point inside the droplet is")
            {
                double derSource = 0.0124899030052444404;
                double gf_derSource = gf.derivativeSource(sourceNormal1, source1, probe1);
                INFO("ref_derSource = " << std::setprecision(std::numeric_limits<long double>::digits10) << derSource);
                INFO("gf_derSource  = " << std::setprecision(std::numeric_limits<long double>::digits10) << gf_derSource);
                REQUIRE(derSource == Approx(gf_derSource));
            }
            AND_THEN("the value of the Green's function directional derivative wrt the source point outside the droplet is")
            {
                double derSource = 0.288657584958107449;
                double gf_derSource = gf.derivativeSource(sourceNormal2, source2, probe2);
                INFO("ref_derSource = " << std::setprecision(std::numeric_limits<long double>::digits10) << derSource);
                INFO("gf_derSource  = " << std::setprecision(std::numeric_limits<long double>::digits10) << gf_derSource);
                REQUIRE(derSource == Approx(gf_derSource));
            }
        }
    }

    GIVEN("A permittivity profile modelled by the error function")
    {
        int maxL = 3;
        // High dielectric constant inside
        double eps1 = 80.0;
        // Low dielectric constant outside
        double eps2 = 2.0;
        double sphereRadius = 100.0;
        double width = 5.0;
        // Evaluation inside the sphere
        Eigen::Vector3d source1 = (Eigen::Vector3d() << 1.0, 0.0, 0.0).finished();
        Eigen::Vector3d sourceNormal1 = source1;
        sourceNormal1.normalize();
        Eigen::Vector3d probe1 = (Eigen::Vector3d() << 2.0, 0.0, 0.0).finished();
        Eigen::Vector3d probeNormal1 = probe1;
        probeNormal1.normalize();
        // Evaluation outside the sphere
        Eigen::Vector3d source2 = (Eigen::Vector3d() << 150.0, 150.0, 150.0).finished();
        Eigen::Vector3d sourceNormal2 = source2;
        sourceNormal2.normalize();
        Eigen::Vector3d probe2 = (Eigen::Vector3d() << 151.0, 150.0, 150.0).finished();
        Eigen::Vector3d probeNormal2 = probe2;
        probeNormal2.normalize();
        WHEN("the spherical droplet is centered at the origin")
        {
            Eigen::Vector3d sphereCenter = Eigen::Vector3d::Zero();
            SphericalDiffuse<CollocationIntegrator, OneLayerErf> gf(eps1, eps2, width, sphereRadius, sphereCenter, maxL);
            THEN("the value of the Green's function inside the droplet is")
            {
                double value = 0.012507311377769814;
                double gf_value = gf.kernelS(source1, probe1);
                INFO("ref_value = " << std::setprecision(std::numeric_limits<long double>::digits10) << value);
                INFO("gf_value  = " << std::setprecision(std::numeric_limits<long double>::digits10) << gf_value);
                REQUIRE(value == Approx(gf_value));
            }
            AND_THEN("the value of the Green's function outside the droplet is")
            {
                double value = 0.49991229576650942;
                double gf_value = gf.kernelS(source2, probe2);
                INFO("ref_value = " << std::setprecision(std::numeric_limits<long double>::digits10) << value);
                INFO("gf_value  = " << std::setprecision(std::numeric_limits<long double>::digits10) << gf_value);
                REQUIRE(value == Approx(gf_value));
            }
            THEN("the value of the Green's function directional derivative wrt the probe point inside the droplet is")
            {
                double derProbe = -0.012506340818360315;
                double gf_derProbe = gf.derivativeProbe(probeNormal1, source1, probe1);
                INFO("ref_derProbe = " << std::setprecision(std::numeric_limits<long double>::digits10) << derProbe);
                INFO("gf_derProbe  = " << std::setprecision(std::numeric_limits<long double>::digits10) << gf_derProbe);
                REQUIRE(derProbe == Approx(gf_derProbe));
            }
            AND_THEN("the value of the Green's function directional derivative wrt the probe point outside the droplet is")
            {
                double derProbe = -0.28997553534915177;
                double gf_derProbe = gf.derivativeProbe(probeNormal2, source2, probe2);
                INFO("ref_derProbe = " << std::setprecision(std::numeric_limits<long double>::digits10) << derProbe);
                INFO("gf_derProbe  = " << std::setprecision(std::numeric_limits<long double>::digits10) << gf_derProbe);
                REQUIRE(derProbe == Approx(gf_derProbe));
            }
            THEN("the value of the Green's function directional derivative wrt the source point inside the droplet is")
            {
                double derSource = 0.012498621813619368;
                double gf_derSource = gf.derivativeSource(sourceNormal1, source1, probe1);
                INFO("ref_derSource = " << std::setprecision(std::numeric_limits<long double>::digits10) << derSource);
                INFO("gf_derSource  = " << std::setprecision(std::numeric_limits<long double>::digits10) << gf_derSource);
                REQUIRE(derSource == Approx(gf_derSource));
            }
            AND_THEN("the value of the Green's function directional derivative wrt the source point outside the droplet is")
            {
                double derSource = 0.28871292628573908;
                double gf_derSource = gf.derivativeSource(sourceNormal2, source2, probe2);
                INFO("ref_derSource = " << std::setprecision(std::numeric_limits<long double>::digits10) << derSource);
                INFO("gf_derSource  = " << std::setprecision(std::numeric_limits<long double>::digits10) << gf_derSource);
                REQUIRE(derSource == Approx(gf_derSource));
            }
        }

        AND_WHEN("the spherical droplet is centered away from the origin")
        {
            Eigen::Vector3d sphereCenter = (Eigen::Vector3d() << 25.0, 0.0, 0.0).finished();
            SphericalDiffuse<CollocationIntegrator, OneLayerErf> gf(eps1, eps2, width, sphereRadius, sphereCenter, maxL);
            THEN("the value of the Green's function inside the droplet is")
            {
                double value = 0.012523344896520634;
                double gf_value = gf.kernelS(source1, probe1);
                INFO("ref_value = " << std::setprecision(std::numeric_limits<long double>::digits10) << value);
                INFO("gf_value  = " << std::setprecision(std::numeric_limits<long double>::digits10) << gf_value);
                REQUIRE(value == Approx(gf_value));
            }
            AND_THEN("the value of the Green's function outside the droplet is")
            {
                double value = 0.49989736527661349;
                double gf_value = gf.kernelS(source2, probe2);
                INFO("ref_value = " << std::setprecision(std::numeric_limits<long double>::digits10) << value);
                INFO("gf_value  = " << std::setprecision(std::numeric_limits<long double>::digits10) << gf_value);
                REQUIRE(value == Approx(gf_value));
            }
            THEN("the value of the Green's function directional derivative wrt the probe point inside the droplet is")
            {
                double derProbe = -0.012502439280500169;
                double gf_derProbe = gf.derivativeProbe(probeNormal1, source1, probe1);
                INFO("ref_derProbe = " << std::setprecision(std::numeric_limits<long double>::digits10) << derProbe);
                INFO("gf_derProbe  = " << std::setprecision(std::numeric_limits<long double>::digits10) << gf_derProbe);
                REQUIRE(derProbe == Approx(gf_derProbe));
            }
            AND_THEN("the value of the Green's function directional derivative wrt the probe point outside the droplet is")
            {
                double derProbe = -0.28995345687399254;
                double gf_derProbe = gf.derivativeProbe(probeNormal2, source2, probe2);
                INFO("ref_derProbe = " << std::setprecision(std::numeric_limits<long double>::digits10) << derProbe);
                INFO("gf_derProbe  = " << std::setprecision(std::numeric_limits<long double>::digits10) << gf_derProbe);
                REQUIRE(derProbe == Approx(gf_derProbe));
            }
            THEN("the value of the Green's function directional derivative wrt the source point inside the droplet is")
            {
                double derSource = 0.012489903372303254;
                double gf_derSource = gf.derivativeSource(sourceNormal1, source1, probe1);
                INFO("ref_derSource = " << std::setprecision(std::numeric_limits<long double>::digits10) << derSource);
                INFO("gf_derSource  = " << std::setprecision(std::numeric_limits<long double>::digits10) << gf_derSource);
                REQUIRE(derSource == Approx(gf_derSource));
            }
            AND_THEN("the value of the Green's function directional derivative wrt the source point outside the droplet is")
            {
                double derSource = 0.28869402146192158;
                double gf_derSource = gf.derivativeSource(sourceNormal2, source2, probe2);
                INFO("ref_derSource = " << std::setprecision(std::numeric_limits<long double>::digits10) << derSource);
                INFO("gf_derSource  = " << std::setprecision(std::numeric_limits<long double>::digits10) << gf_derSource);
                REQUIRE(derSource == Approx(gf_derSource));
            }
        }
    }
}


#ifndef PERLINNOISESOURCE_HPP
#define PERLINNOISESOURCE_HPP

#include <cmath>

#include <boost/multi_array.hpp>

#include "Math.hpp"
#include "NoiseSource.hpp"
#include "Random.hpp"
#include "Vector3.hpp"

/**
 * A noise source that uses the perlin noise algorithm.
 */
template <typename T>
class PerlinNoiseSource : public NoiseSource<T>
{
public:
	/**
	 * Create a perlin noise source.
	 *
	 * @param size the range in each dimension that will be unique.
	 * @param rng the random number generator to use.
	 */
	PerlinNoiseSource(unsigned int size, Random& rng) :
		gradientArray(boost::extents[size][size][size]),
		size(size)
	{
		//Generate random gradients
		for( unsigned int x = 0; x < size; x++ )
		{
			for( unsigned int y = 0; y < size; y++ )
			{
				for( unsigned int z = 0; z < size; z++ )
				{
					Vector3<T> gradient( normalizeReal(rng.nextReal()), normalizeReal(rng.nextReal()), normalizeReal(rng.nextReal()) );
					gradient.normalize();
					this->gradientArray[x][y][z] = gradient;
				}
			}
		}
	}

	T noise(T x, T y = 0, T z = 0) const
	{
		clampDomain<T>(x, size);
		clampDomain<T>(y, size);
		clampDomain<T>(z, size);

		//Determine the cell that we are in
		T xi, yi, zi;
		T xf = std::modf(x, &xi);
		T yf = std::modf(y, &yi);
		T zf = std::modf(z, &zi);

		int a[] = {static_cast<int>(xi) % static_cast<int>(size),
			static_cast<int>(xi + 1) % static_cast<int>(size)};
		int b[] = {static_cast<int>(yi) % static_cast<int>(size),
			static_cast<int>(yi + 1) % static_cast<int>(size)};
		int c[] = {static_cast<int>(zi) % static_cast<int>(size),
			static_cast<int>(zi + 1) % static_cast<int>(size)};

		//Compute dot products
		T dots[2][2][2];
		for( int i = 0; i < 2; i++ )
		{
			for( int j = 0; j < 2; j++ )
			{
				for( int k = 0; k < 2; k++ )
				{
					dots[i][j][k] = dot(this->gradientArray[a[i]][b[j]][c[k]], Vector3<T>(xf - (T)i, yf - (T)j, zf - (T)k));
				}
			}
		}

		//Compute fade in each dimension
		T fadeX = fade<T>(xf);
		T fadeY = fade<T>(yf);
		T fadeZ = fade<T>(zf);

		//Perform trilinear interpolation
		T ix1 = lerp<T>(dots[0][0][0], dots[1][0][0], fadeX);
		T ix2 = lerp<T>(dots[0][1][0], dots[1][1][0], fadeX);
		T ix3 = lerp<T>(dots[0][0][1], dots[1][0][1], fadeX);
		T ix4 = lerp<T>(dots[0][1][1], dots[1][1][1], fadeX);

		T iy1 = lerp<T>(ix1, ix2, fadeY);
		T iy2 = lerp<T>(ix3, ix4, fadeY);

		return lerp<T>(iy1, iy2, fadeZ);
	}

private:
	boost::multi_array<Vector3<T>, 3> gradientArray;
	unsigned int size;
};

#endif // PERLINNOISESOURCE_HPP


#include <stdio.h>
#include <math.h>
#include <iostream>
#include <Eigen/Eigen>

template<int innodes, int outnodes>
struct NNLayer {
  Eigen::Matrix<float, innodes, outnodes> w;
  Eigen::Matrix<float, 1, outnodes> bias;
  Eigen::Matrix<float, 1, outnodes> output;
  bool needActivation;

  NNLayer() : needActivation(true)
    {
      w      = Eigen::Matrix<float, innodes, outnodes>::Random(innodes, outnodes);
      bias   = Eigen::Matrix<float, 1, outnodes>::Random(1, outnodes);
      output = Eigen::Matrix<float, 1, outnodes>::Random(1, outnodes);
    }

  Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic> forward(Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic> m)
    {
      output = (m * w);
      output = output - bias;
      if (needActivation)
        return activate();
      else
        return output;
    }

  Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic> activate()
    {
      Eigen::Matrix<float, 1, outnodes> o = output;
      for(int i=0; i<output.cols(); i++)
        {
          o(0, i) = 1.0/(1.0 + exp(o(0, i)));
        }
      return o;
    }

  void setActivation(bool isneed)
    {
      needActivation = isneed;
    }
};

class NeuralNetwork {
  NNLayer<2, 2> l1;
  NNLayer<2, 2> l2;
  NNLayer<2, 1> l3;

  public:
    NeuralNetwork()
      {
        l3.setActivation(false);
      };

    Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic> forward(Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic> input)
      {
        return l3.forward( l2.forward( l1.forward( input ) ) );
      };

    void back_propagation()
      {
      };
};

int main(void)
{
  NeuralNetwork nn;

  Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic> out;
  Eigen::Matrix<float, 1, 2> input;
  input << 3, 3;

  out = nn.forward( input );

  std::cout << "out = " << out << std::endl;

  return 0;
}


/*
 Copyright 2011 Mario Mulansky
 Copyright 2012-2013 Karsten Ahnert

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

/* strongly nonlinear hamiltonian lattice in 2d */

#ifndef LATTICE2D_HPP
#define LATTICE2D_HPP

#include <vector>

#include <boost/math/special_functions/pow.hpp>

using boost::math::pow;

template< int Kappa , int Lambda >
struct lattice2d {

    const double m_beta;
    std::vector< std::vector< double > > m_omega;

    lattice2d( const double beta )
        : m_beta( beta )
    { }

    template< class StateIn , class StateOut >
    void operator()( const StateIn &q , StateOut &dpdt )
    {
        // q and dpdt are 2d
        const int N = q.size();

        int i;
        for( i = 0 ; i < N ; ++i )
        {
            const int i_l = (i-1+N) % N;
            const int i_r = (i+1) % N;
            for( int j = 0 ; j < N ; ++j )
            {
            const int j_l = (j-1+N) % N;
            const int j_r = (j+1) % N;
            dpdt[i][j] = - m_omega[i][j] * pow<Kappa-1>( q[i][j] )
                - m_beta * pow<Lambda-1>( q[i][j] - q[i][j_l] )
                - m_beta * pow<Lambda-1>( q[i][j] - q[i][j_r] )
                - m_beta * pow<Lambda-1>( q[i][j] - q[i_l][j] )
                - m_beta * pow<Lambda-1>( q[i][j] - q[i_r][j] );
            }
        }
    }

    template< class StateIn >
    double energy( const StateIn &q , const StateIn &p )
    {
        // q and dpdt are 2d
        const int N = q.size();
        double energy = 0.0;
        int i;
        for( i = 0 ; i < N ; ++i )
        {
            const int i_l = (i-1+N) % N;
            const int i_r = (i+1) % N;
            for( int j = 0 ; j < N ; ++j )
            {
            const int j_l = (j-1+N) % N;
            const int j_r = (j+1) % N;
            energy += p[i][j]*p[i][j] / 2.0
                        + m_omega[i][j] * pow<Kappa>( q[i][j] ) / Kappa
                + m_beta * pow<Lambda>( q[i][j] - q[i][j_l] ) / Lambda / 2
                + m_beta * pow<Lambda>( q[i][j] - q[i][j_r] ) / Lambda / 2
                + m_beta * pow<Lambda>( q[i][j] - q[i_l][j] ) / Lambda / 2
                + m_beta * pow<Lambda>( q[i][j] - q[i_r][j] ) / Lambda / 2;
            }
        }
        return energy;
    }

    template< class StateIn , class StateOut >
    double local_energy( const StateIn &q , const StateIn &p , StateOut &energy )
    {
        // q and dpdt are 2d
        const int N = q.size();
        double e = 0.0;
        int i;
        for( i = 0 ; i < N ; ++i )
        {
            const int i_l = (i-1+N) % N;
            const int i_r = (i+1) % N;
            for( int j = 0 ; j < N ; ++j )
            {
                const int j_l = (j-1+N) % N;
                const int j_r = (j+1) % N;
                energy[i][j] = p[i][j]*p[i][j] / 2.0
                    + m_omega[i][j] * pow<Kappa>( q[i][j] ) / Kappa
                    + m_beta * pow<Lambda>( q[i][j] - q[i][j_l] ) / Lambda / 2
                    + m_beta * pow<Lambda>( q[i][j] - q[i][j_r] ) / Lambda / 2
                    + m_beta * pow<Lambda>( q[i][j] - q[i_l][j] ) / Lambda / 2
                    + m_beta * pow<Lambda>( q[i][j] - q[i_r][j] ) / Lambda / 2;
                e += energy[i][j];
            }
        }
        //rescale
        e = 1.0/e;
        for( i = 0 ; i < N ; ++i )
            for( int j = 0 ; j < N ; ++j )
                energy[i][j] *= e;
        return 1.0/e;
    }

    void load_pot( const char* filename , const double W , const double gap ,
                   const size_t dim )
    {
        std::ifstream in( filename , std::ios::in | std::ios::binary );
        if( !in.is_open() ) {
            std::cerr << "pot file not found: " << filename << std::endl;
            exit(0);
        } else {
            std::cout << "using pot file: " << filename << std::endl;
        }

        m_omega.resize( dim );
        for( int i = 0 ; i < dim ; ++i )
        {
            m_omega[i].resize( dim );
            for( size_t j = 0 ; j < dim ; ++j )
            {
                if( !in.good() )
                {
                    std::cerr << "I/O Error: " << filename << std::endl;
                    exit(0);
                }
                double d;
                in.read( (char*) &d , sizeof(d) );
                if( (d < 0) || (d > 1.0) )
                {
                    std::cerr << "ERROR: " << d << std::endl;
                    exit(0);
                }
                m_omega[i][j] = W*d + gap;
            }
        }

    }

    void generate_pot( const double W , const double gap , const size_t dim )
    {
        m_omega.resize( dim );
        for( size_t i = 0 ; i < dim ; ++i )
        {
            m_omega[i].resize( dim );
            for( size_t j = 0 ; j < dim ; ++j )
            {
                m_omega[i][j] = W*static_cast<double>(rand())/RAND_MAX + gap;
            }
        }
    }

};

#endif


#include <iostream>
#include <armadillo>
#include <vector>
#include "rbf.hpp"

using namespace arma;
using namespace std;

const double LAMBDA = 0.01;

int main() {
    FILE *input = fopen("../../Spectra100.csv", "r");

    vector<double> xs, ys;
    double a, b;
    while ( fscanf(input, "%lF,%lF", &a, &b) != EOF ) {
        xs.push_back(a), ys.push_back(b);
    }
    fclose(input);

    rbf my_rbf(xs, ys, LAMBDA);

    vector<double> all_xs;
    for ( double i = 0; i <= 5; i += 0.01 )
        all_xs.push_back(i);

    vector<double> fxs = my_rbf.test(all_xs);

    int n = fxs.size();
    for (int i = 0; i < n; ++i) {
        printf("%lF,%lF\n", all_xs[i], fxs[i]);
    }

    return 0;
}


//Authors: Dario Cattaruzza, Alessandro Abate, Peter Schrammel, Daniel Kroening
//University of Oxford 2016
//This code is supplied under the BSD license agreement (see license.txt)

#include <math.h>
#include "JordanSolver.h"
#include "MatrixToString.h"
#include <Eigen/Eigenvalues>

namespace abstract{

template <class scalar>
typename JordanSolver<scalar>::complexType JordanSolver<scalar>::ms_complexOne(1,0);

template <class scalar>
MatToStr<scalar> JordanSolver<scalar>::ms_logger(true);

template <class scalar>
MatToStr<scalar> JordanSolver<scalar>::ms_decoder(false);

template <class scalar>
traceDynamics_t JordanSolver<scalar>::ms_trace_dynamics=eTraceNoDynamics;

/// Constructs an empty matrix
template <class scalar>
JordanSolver<scalar>::JordanSolver(const int dimension) :
    m_dimension(dimension),
    m_zero(func::ms_weakZero),
    m_largeZero(dimension*dimension*func::ms_weakZero),
    m_dynamics(dimension,dimension)
{
}

/// Changes the default dimension of the system
template <class scalar>
void JordanSolver<scalar>::changeDimensions(const int dimension)
{
  if (dimension!=m_dimension) {
    m_dimension=dimension;
    m_dynamics.resize(dimension,dimension);
  }
}

template <class scalar>
void JordanSolver<scalar>::computeJordan(const MatrixType &matrix)
{
  this->setMaxIterations(1000);
  changeDimensions(matrix.rows());
  m_dynamics=matrix;
  calculateJordanForm();
}

/// Transforms the matrix to Row Echelon Form
template <class scalar>
int JordanSolver<scalar>::toREF(ComplexMatrixType &matrix)
{
  int rank=m_dimension;
  int col=0;
  for (int row=0;col<matrix.rows();row++,col++) {
    while (func::isZero(func::norm2(matrix.coeff(row,col)),m_zero)) {
      for (int row2=row+1;row2<matrix.rows();row2++) {
        if (!func::isZero(func::norm2(matrix.coeff(row2,col)),m_zero)) {
          matrix.row(row)+=matrix.row(row2);
          break;
        }
      }
      if (func::isZero(func::norm2(matrix.coeff(row,col)),m_zero)) {
        col++;
        if (col==matrix.rows()) break;
      }
    }
    if (col==matrix.rows()) break;
    complexType multiplier=matrix.coeff(row,col);
    if (!func::isZero(func::norm2(multiplier),m_zero)) {
      matrix.row(row)/=multiplier;
      rank--;
    }
    for (int row2=row+1;row2<matrix.rows();row2++) {
      complexType multiplier=matrix.coeff(row2,col);
      matrix.row(row2)-=multiplier*matrix.row(row);
    }
  }
  if (ms_trace_dynamics>=eTraceREF) ms_logger.logData(matrix,"REF:");
  return rank;
}

/// Transforms the matrix to Row Echelon Form
template <class scalar>
int JordanSolver<scalar>::toRREF(ComplexMatrixType &matrix)
{
  int rank=toREF(matrix);
  int col=0;
  for (int row=1;(row<matrix.rows()) && (col<matrix.cols());row++) {
    while ((col<matrix.cols()) && func::isZero(func::norm2(matrix.coeff(row,col)),m_zero)) col++;
    if (col<matrix.cols()) {
      for (int row2=0;row2<row;row2++) {
        if (!func::isZero(func::norm2(matrix.coeff(row2,col)),m_zero)) {
          complexType multiplier=matrix.coeff(row2,col);
          matrix.row(row2)-=multiplier*matrix.row(row);
        }
      }
    }
  }
  if (ms_trace_dynamics>=eTraceREF) ms_logger.logData(matrix,"RREF:");
  return rank;
}

/// Checks if the given row pair belongs to the same jordan block
template <class scalar>
bool JordanSolver<scalar>::isJordanBlock(const int row1,const int row2)
{
  if (func::norm2(m_eigenValues.coeff(row1,row1)-m_eigenValues.coeff(row2,row2))>m_largeZero) return false;
  if (m_conjugatePair[row1]>=0) {
    MatrixType dotReal=m_eigenVectors.row(row1).real().transpose()*m_eigenVectors.row(row2).real();
    MatrixType dotImag=m_eigenVectors.row(row1).imag().transpose()*m_eigenVectors.row(row2).imag();
    scalar vNorm1real=m_eigenVectors.row(row1).real().norm();
    scalar vNorm2real=m_eigenVectors.row(row2).real().norm();
    scalar vNorm1imag=m_eigenVectors.row(row1).imag().norm();
    scalar vNorm2imag=m_eigenVectors.row(row2).imag().norm();
    scalar realAngle=func::norm2(dotReal.coeff(0,0))/(vNorm1real*vNorm2real);
    scalar imagAngle=func::norm2(dotImag.coeff(0,0))/(vNorm1imag*vNorm2imag);
    realAngle=acos(realAngle);
    imagAngle=acos(imagAngle);
    return func::isZero(func::toUpper(realAngle),0.01/*m_zero*/) && func::isZero(func::toUpper(imagAngle),0.01/*m_zero*/);
  }
  else {
    ComplexMatrixType dotProd=m_eigenVectors.row(row1).transpose()*m_eigenVectors.row(row2);
    scalar vNorm1=m_eigenVectors.row(row1).norm();
    scalar vNorm2=m_eigenVectors.row(row2).norm();
    scalar angle=func::norm2(dotProd.coeff(0,0))/(vNorm1*vNorm2);
    angle=acos(angle);
    return func::isZero(func::toUpper(angle),0.01/*m_zero*/);
  }
  return false;
}

/// Calculates the Jordan block and generalised eigenvector for the row pair
template <class scalar> bool JordanSolver<scalar>::makeJordanBlock(const int row1,const int row2)
{
  scalar radius=func::norm2(m_eigenValues.coeff(row1,row1)-m_eigenValues.coeff(row2,row2));
  if (!func::isZero(radius)) return false;
  m_hasMultiplicities=true;
  ComplexMatrixType matrixBase=ComplexMatrixType::Zero(m_dimension,m_dimension);
  matrixBase.real()=m_dynamics;
  for (int i=0;i<m_dimension;i++) {
    matrixBase.coeffRef(i,i)-=m_eigenValues.coeff(row1,row1);
  }
  ComplexMatrixType matrix=matrixBase;
  int order=1;
  int rank=toREF(matrix);
  if (rank==0) return false;
  if (rank==m_dimension) return false;
  m_jordanIndex[row1]=m_jordanIndex[row2]+1;
  while (rank<=m_jordanIndex[row1]) {
    order++;
    matrix=matrixBase;
    for (int i=1;i<order;i++) matrix*=matrixBase;
    rank=toREF(matrix);
  }

  int row=m_dimension-rank-1;
  if (row<0) row=0;//TODO: What happens when rank is m_dim? Is this right?
  int col=row;
  while ((col<m_dimension) && (func::isZero(func::norm2(matrix.coeff(row,col)),m_zero))) col++;
  if (++col>=m_dimension) col=m_dimension-1;//TODO: check for col out of range and 0 coeffs (is this right?)
  ComplexMatrixType vector=ComplexMatrixType::Zero(m_dimension,1);
  vector.coeffRef(col,0)=1;

  while (row>=0) {
    vector.coeffRef(row,0)=-(matrix.row(row)*vector).sum();
    row--;
  }
  for (int i=1;i<rank-m_jordanIndex[row1];i++) vector=matrixBase*vector;
  refScalar vectorNorm=func::toUpper(vector.norm());
  refScalar vectorEpsilon=vectorNorm*func::ms_weakEpsilon*func::ms_weakEpsilon;
  for (int i=0;i<vector.rows();i++) {
    if (func::norm2(vector.coeff(i,0))<vectorEpsilon) vector.coeffRef(i,0)=0;
  }
  vector/=scalar(vectorNorm);
  m_eigenVectors.col(row1)=vector;
  m_eigenValues.coeffRef(row2,row1)=ms_complexOne;

  if (m_conjugatePair[row1]>=0) {
    m_eigenValues.coeffRef(row2+1,row1+1)=ms_complexOne;
    for (int row=0;row<m_dimension;row++) m_eigenVectors.coeffRef(row,row1+1)=conj(vector.coeff(row,0));
  }
  for (int i=1;i<=m_jordanIndex[row1];i++) {
    vector=matrixBase*vector;
    if (m_conjugatePair[row1]>=0) {
      m_eigenVectors.col(row1-2*i)=vector;
      for (int row=0;row<m_dimension;row++) m_eigenVectors.coeffRef(row,row1-2*i+1)=conj(vector.coeff(row,0));
    }
    else {
      m_eigenVectors.col(row1-i)=vector;
    }
  }

  if (ms_trace_dynamics>=eTraceREF) {
    ms_logger.logData(m_eigenVectors,"Intermediate EigenVectors:");
    matrix=matrixBase;
    for (int i=1;i<=m_jordanIndex[row1];i++) matrix*=matrixBase;
    ms_logger.logData(matrix,"Matrix Base:");
    ComplexMatrixType nullSpace=getNullSpace(matrix);
    ms_logger.logData(nullSpace,"nullSpace:");
  }
  return true;
}

/// calculates the estimated roundoff error of a matrix operation
template <class scalar>
inline typename JordanSolver<scalar>::refScalar JordanSolver<scalar>::calculateEpsilon(const MatrixType &matrix)
{
  if (matrix.rows()>0) {
    refScalar max=func::toUpper(func::norm2(matrix.coeff(0,0)));
    refScalar min=func::toLower(func::norm2(matrix.coeff(0,0)));
    for (int row=0;row<matrix.rows();row++) {
      for (int col=0;col<matrix.cols();col++) {
        refScalar upper=func::toUpper(func::norm2(matrix.coeff(row,col)));
        refScalar lower=func::toLower(func::norm2(matrix.coeff(row,col)));
        if (upper>max) max=upper;
        if (lower<min) min=lower;
      }
    }
    //scalar max=matrix.maxCoeff();
    //scalar min=matrix.minCoeff();
    if (-min>max) max=-min;
    return max*func::ms_weakEpsilon;
  }
  return 0;
}

/// calculates the estimated roundoff error of a matrix operation
template <class scalar>
inline typename JordanSolver<scalar>::refScalar JordanSolver<scalar>::calculateEpsilon(const ComplexMatrixType &matrix)
{
  if (matrix.rows()>0) {
    refScalar max=func::toUpper(func::norm2(matrix.coeff(0,0)));
    refScalar min=func::toLower(func::norm2(matrix.coeff(0,0)));
    for (int row=0;row<matrix.rows();row++) {
      for (int col=0;col<matrix.cols();col++) {
        refScalar upper=func::toUpper(func::norm2(matrix.coeff(row,col)));
        refScalar lower=func::toLower(func::norm2(matrix.coeff(row,col)));
        if (upper>max) max=upper;
        if (lower<min) min=lower;
      }
    }
    //scalar max=matrix.maxCoeff();
    //scalar min=matrix.minCoeff();
    if (-min>max) max=-min;
    return max*func::ms_weakEpsilon;
  }
  return 0;
}

/// Loads the transformation matrix for the state space
template <class scalar>
bool JordanSolver<scalar>::calculateJordanForm()
{
    m_zero=calculateEpsilon(m_dynamics);
    m_inverse.conservativeResize(0,0);
    m_largeZero=m_zero*m_dimension*m_dimension;
    this->setMaxIterations(1000);
    this->compute(m_dynamics);

    if (this->info()!=Eigen::Success) return false;
    m_eigenValues=this->eigenvalues().asDiagonal();
    m_eigenVectors=this->eigenvectors();

    if (ms_trace_dynamics>=eTraceAll) {
      ms_logger.logData(m_dynamics,"Dynamics:");
      ms_logger.logData(m_eigenValues,"EigenValues:");
      ms_logger.logData(m_eigenVectors,"Initial EigenVectors:");
    }
    m_hasOnes=false;
    m_hasZeros=false;
    m_hasMultiplicities=false;
    m_isOne.resize(2*m_dimension);
    m_conjugatePair.resize(2*m_dimension);
    m_jordanIndex.resize(2*m_dimension);
    for (int i=0;i<m_dimension;i++) {
      if (func::isZero(func::norm2(m_eigenValues.coeff(i,i)))) m_hasZeros=true;
    }
    for (int i=0;i<m_dimension;i++) {
      m_conjugatePair[i]=-1;
      m_jordanIndex[i]=0;
      m_isOne[i]=false;
      if ((i<(m_dimension-1)) && !func::isZero(m_eigenValues.coeff(i,i).imag(),m_zero)) {
        m_conjugatePair[i]=i+1;
        if (i>=2) makeJordanBlock(i,i-2);
        m_jordanIndex[i+1]=m_jordanIndex[i];
        m_conjugatePair[i+1]=i;
        i++;
      }
      else {
        m_isOne[i]=func::isZero(func::norm2(m_eigenValues.coeff(i,i)-ms_complexOne),m_zero);
        m_hasOnes|=m_isOne[i];
        if (i>0) makeJordanBlock(i,i-1);
      }
    }
    refScalar eigenVectorEpsilon=calculateEpsilon(m_eigenVectors);
    for (int row=0;row<m_eigenVectors.rows();row++) {
      for (int col=0;col<m_eigenVectors.cols();col++) {
        if (func::norm2(m_eigenVectors.coeff(row,col))<eigenVectorEpsilon) m_eigenVectors.coeffRef(row,col)=func::ms_hardZero;
      }
    }

    for (int i=m_dimension;i<2*m_dimension;i++) {
      m_conjugatePair[i]=-1;
      m_jordanIndex[i]=0;
      m_isOne[i]=m_isOne[i-m_dimension];
    }
    if (ms_trace_dynamics>=eTraceDynamics) ms_logger.logData(m_eigenVectors,"Generalised EigenVectors:");
    return true;
}

/// Returns the nullSpace vectors of M
template<class scalar>
typename JordanSolver<scalar>::ComplexMatrixType JordanSolver<scalar>::getNullSpace(const ComplexMatrixType &matrixBase,bool normalized)
{
  ComplexMatrixType result;
  ComplexMatrixType nullSpace=matrixBase;
  toRREF(nullSpace);
  std::vector<bool> vars(nullSpace.cols());
  int row=0;
  int freeVars=nullSpace.cols();
  for (int col=0;col<nullSpace.cols();col++) {
    vars[col]=true;
    if (!func::isZero(norm(nullSpace.coeff(row,col)))) {
      vars[col]=false;
      row++;
      freeVars--;
    }
  }
  result.resize(m_dimension,freeVars);
  int col=0;
  for (int j=0;j<nullSpace.cols();j++) {
    if (vars[j]) {
      result.row(j)=ComplexMatrixType::Zero(1,freeVars);
      result.coeffRef(j,col)=func::ms_c_1;
    }
    else {
      int pos=0;
      for (int k=0;k<nullSpace.cols();k++) {
        if (vars[k]) result.coeffRef(j,pos++)=-nullSpace.coeff(j,k);
      }
    }
  }
  if (normalized) {
    for (int col=0;col<result.cols();col++) {
      scalar scale=result.col(col).norm();
      result.col(col)/=scale;
    }
  }
  return result;
}

#ifdef USE_LDOUBLE
  template class JordanSolver<long double>;
#endif
#ifdef USE_MPREAL
  template class JordanSolver<mpfr::mpreal>;
#endif
}


/* =========================================================================
   Copyright (c) 2010-2016, Institute for Microelectronics,
                            Institute for Analysis and Scientific Computing,
                            TU Wien.
   Portions of this software are copyright by UChicago Argonne, LLC.

                            -----------------
                  ViennaCL - The Vienna Computing Library
                            -----------------

   Project Head:    Karl Rupp                   rupp@iue.tuwien.ac.at

   (A list of authors and contributors can be found in the PDF manual)

   License:         MIT (X11), see file LICENSE in the base directory
============================================================================= */

/** \example iterative-eigen.cpp
*
*   The following tutorial shows how to use the iterative solvers in ViennaCL with objects from the Eigen Library directly.
*
*   \note Eigen provides its own iterative solvers in the meanwhile. Check these first.
*
*   We begin with including the necessary headers:
**/

// System headers
#include <iostream>

#ifndef NDEBUG
 #define NDEBUG
#endif

// Eigen headers
#include <Eigen/Core>
#include <Eigen/Sparse>

// Must be set prior to any ViennaCL includes if you want to use ViennaCL algorithms on Eigen objects
#define VIENNACL_WITH_EIGEN 1

// ViennaCL headers
#include "viennacl/linalg/ilu.hpp"
#include "viennacl/linalg/cg.hpp"
#include "viennacl/linalg/bicgstab.hpp"
#include "viennacl/linalg/gmres.hpp"
#include "viennacl/io/matrix_market.hpp"

// Some helper functions for this tutorial:
#include "vector-io.hpp"

/**
*  In the following we run the CG method, the BiCGStab method, and the GMRES method with Eigen types directly.
*  First, the matrices are set up, then the respective solvers are called.
**/
int main(int, char *[])
{
  typedef float ScalarType;

  Eigen::SparseMatrix<ScalarType, Eigen::RowMajor> eigen_matrix(65025, 65025);
  Eigen::VectorXf eigen_rhs;
  Eigen::VectorXf eigen_result;
  Eigen::VectorXf ref_result;
  Eigen::VectorXf residual;

  /**
  * Read system from file
  **/
  std::cout << "Reading matrix (this might take some time)..." << std::endl;
  eigen_matrix.reserve(65025 * 7);
  if (!viennacl::io::read_matrix_market_file(eigen_matrix, "../examples/testdata/mat65k.mtx"))
  {
    std::cout << "Error reading Matrix file. Make sure you run from the build/-folder." << std::endl;
    return EXIT_FAILURE;
  }
  //eigen_matrix.endFill();
  std::cout << "Done: reading matrix" << std::endl;

  if (!readVectorFromFile("../examples/testdata/rhs65025.txt", eigen_rhs))
  {
    std::cout << "Error reading RHS file" << std::endl;
    return EXIT_FAILURE;
  }

  if (!readVectorFromFile("../examples/testdata/result65025.txt", ref_result))
  {
    std::cout << "Error reading Result file" << std::endl;
    return EXIT_FAILURE;
  }

  /**
  *  Conjugate Gradient (CG) solver:
  **/
  std::cout << "----- Running CG -----" << std::endl;
  eigen_result = viennacl::linalg::solve(eigen_matrix, eigen_rhs, viennacl::linalg::cg_tag());

  residual = eigen_matrix * eigen_result - eigen_rhs;
  std::cout << "Relative residual: " << viennacl::linalg::norm_2(residual) / viennacl::linalg::norm_2(eigen_rhs) << std::endl;

  /**
  *  Stabilized Bi-Conjugate Gradient (BiCGStab) solver:
  **/
  std::cout << "----- Running BiCGStab -----" << std::endl;
  eigen_result = viennacl::linalg::solve(eigen_matrix, eigen_rhs, viennacl::linalg::bicgstab_tag());

  residual = eigen_matrix * eigen_result - eigen_rhs;
  std::cout << "Relative residual: " << viennacl::linalg::norm_2(residual) / viennacl::linalg::norm_2(eigen_rhs) << std::endl;

  /**
  *  Generalized Minimum Residual (GMRES) solver:
  **/
  std::cout << "----- Running GMRES -----" << std::endl;
  eigen_result = viennacl::linalg::solve(eigen_matrix, eigen_rhs, viennacl::linalg::gmres_tag());

  residual = eigen_matrix * eigen_result - eigen_rhs;
  std::cout << "Relative residual: " << viennacl::linalg::norm_2(residual) / viennacl::linalg::norm_2(eigen_rhs) << std::endl;

  /**
  *   That's it. Print a success message and exit.
  **/
  std::cout << std::endl;
  std::cout << "!!!! TUTORIAL COMPLETED SUCCESSFULLY !!!!" << std::endl;
  std::cout << std::endl;
}