#include <geometry.h>
#include <tiny_math_types.h>

#define BOOST_AUTO_TEST_MAIN
#include <boost/test/auto_unit_test.hpp>
#include <boost/test/unit_test_suite.hpp>
#include <boost/test/floating_point_comparison.hpp>
#include <boost/test/test_tools.hpp>

BOOST_AUTO_TEST_SUITE(geometry);

BOOST_AUTO_TEST_CASE(raycast_sphere)
{
  using std::sqrt;

  typedef tiny::MathTypes<double>   MT;
  typedef MT::vector3_type          V;
  typedef MT::real_type             T;
  typedef MT::value_traits          VT;

  V const center = V::make(0.0,0.0,0.0);
  T const radius = VT::one();

  geometry::Sphere<V> sphere = geometry::make_sphere(center, radius);

  // ray hitting straight on
  {
    V const r      = V::make( 0.0, 0.0, 1.0);
    V const p      = V::make( 0.0, 0.0,-3.0);

    geometry::Ray<V> const ray = geometry::make_ray(p, r);

    T       length = VT::zero();
    V       q      = V::zero();

    bool hit = geometry::compute_raycast_sphere(ray, sphere, q, length);

    BOOST_CHECK( hit );

    BOOST_CHECK_CLOSE( length, 2.0, 0.01);
    BOOST_CHECK_CLOSE( q(0),  0.0, 0.01);
    BOOST_CHECK_CLOSE( q(1),  0.0, 0.01);
    BOOST_CHECK_CLOSE( q(2), -1.0, 0.01);
  }

  // ray shooting staright away
  {
    V const r      = V::make( 0.0, 0.0,-1.0);
    V const p      = V::make( 0.0, 0.0,-3.0);

    geometry::Ray<V> const ray = geometry::make_ray(p, r);

    T       length = VT::zero();
    V       q      = V::zero();

    bool hit = geometry::compute_raycast_sphere(ray, sphere, q, length);

    BOOST_CHECK( !hit );

  }

  // ray starts inside
  {
    V const r      = V::make( 0.0, 0.0,-1.0);
    V const p      = V::make( 0.0, 0.0, 0.5);

    geometry::Ray<V> const ray = geometry::make_ray(p, r);

    T       length = VT::zero();
    V       q      = V::zero();

    bool hit = geometry::compute_raycast_sphere(ray, sphere, q, length);

    BOOST_CHECK( !hit );

  }

  // ray shooting pass sphere
  {
    V const r      = V::make( 4.0, 0.0, 1.0);
    V const p      = V::make( 0.0, 0.0,-3.0);

    geometry::Ray<V> const ray = geometry::make_ray(p, r);

    T       length = VT::zero();
    V       q      = V::zero();

    bool hit = geometry::compute_raycast_sphere(ray, sphere, q, length);

    BOOST_CHECK( !hit );
  }

}

BOOST_AUTO_TEST_SUITE_END();


#include <iostream>
#include <ctime>
#include <iomanip>
#include <map>
#include <tuple>
#include "date.h"
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/program_options.hpp>

namespace pt = boost::property_tree;
namespace po = boost::program_options;
using namespace std;

int main(int argc, char *argv[])
{
    if (argc < 2)
    {
        cerr<< "enter json file";
        return 1;
    }
    string filename = argv[1];
    cout << "рассчет кредита" << endl;
    pt::ptree root;
    pt::read_json(filename, root);
    map<Date, double> precents;
    for (auto& item: root.get_child("precents"))
    {
        auto& item0 = item.second.front();
        auto d = stringToDate(item0.first);
        precents[d] = item0.second.get_value<double>();
    }

    map<Date, double> pays;
    for (auto& item: root.get_child("pays"))
    {
        auto& item0 = item.second.front();
        auto d = stringToDate(item0.first);
        pays[d] = item0.second.get_value<int>();
    }

    auto currentDatestr = root.get<string>("currentDate", "");
    auto currentDate = stringToDate(currentDatestr);

    int planedPay = root.get<int>("planedPay", 0);
    double balance = root.get<int>("debt", 0);
    int durationYears = root.get<int>("duration", 0);

    for (int i = 0 ; i < 20 && planedPay != 0; ++i)
    {
        pays[currentDate] = planedPay;
        currentDate.monthInc();
    }

    cout << "current " << currentDatestr << endl;
    Date currentdate = begin(precents)->first;
    double percent = begin(precents)->second;
    int duration = durationYears * 12;
    double debt = balance / duration;
    double pay;
    int overpay = 0;
    while (balance > 0)
    {
        currentdate.monthInc();
        auto currentPercent = precents.find(currentdate);
        percent = currentPercent != end(precents) ? currentPercent->second : percent;
        double percentages = balance * (percent/100/12);

        overpay += percentages;

        pay = debt + percentages;

        auto currentPay = pays.find(currentdate);
        pay = currentPay != end(pays) ? currentPay->second : pay;

        pay = pay < (balance + percentages)? pay : (balance + percentages);
        balance = balance - (pay - percentages);
        auto t = currentdate.toTm();
        cout << "период: " << put_time(&t, "%B %t %Y");
        cout << " выплата: " << pay;
        cout << " долг: " << (pay - percentages);
        cout << " проценты: " << percentages;
        cout << " остаток: " << balance;
        cout << " процент: " << percent;
        cout << " переплата: " << overpay;
        cout << endl;
    }
    return 0;
}


//==============================================================================
//         Copyright 2014          LRI    UMR 8623 CNRS/Univ Paris Sud XI
//         Copyright 2014          NumScale SAS
//
//          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 BOOST_NUMERIC_ODEINT_EXTERNAL_NT2_NT2_NORM_INF_HPP_INCLUDED
#define BOOST_NUMERIC_ODEINT_EXTERNAL_NT2_NT2_NORM_INF_HPP_INCLUDED

#include <nt2/core/container/table/table.hpp>
#include <nt2/include/functions/globalmax.hpp>
#include <nt2/include/functions/abs.hpp>

#include <boost/numeric/odeint/algebra/vector_space_algebra.hpp>

namespace boost { namespace numeric { namespace odeint
{
  template<typename T, typename S>
  struct vector_space_norm_inf<nt2::container::table<T,S> >
  {
    typedef T result_type;
    result_type operator()(const nt2::container::table<T,S> &v1) const
    {
      return nt2::globalmax(nt2::abs(v1));
    }
  };
} } }

#endif


#ifndef GRADIENT_PSV_H_
#define GRADIENT_PSV_H_

#include "gradient.hpp"
#include <Eigen/Dense>
#include <memory>
#include <vector>

using VecMat4cd =
    std::vector<Eigen::Matrix4cd, Eigen::aligned_allocator<Eigen::Matrix4cd>>;
using VecMat24cd = std::vector<
    Eigen::Matrix<std::complex<double>, 2, 4>,
    Eigen::aligned_allocator<Eigen::Matrix<std::complex<double>, 2, 4>>>;

namespace grad_psv {
class GRTCoeff {
  friend class IntegralLayer;

public:
  EIGEN_MAKE_ALIGNED_OPERATOR_NEW
  GRTCoeff(const Eigen::Ref<const Eigen::ArrayXXd> model, const double freq,
           const double c);

private:
  void initialize_gamma();
  void initialize_nv();
  void initialize_E();
  Eigen::Matrix2cd get_Ad(const double z, const int ind_layer) const;
  Eigen::Matrix2cd get_Au(const double z, const int ind_layer) const;
  Eigen::Matrix2cd get_Ad_der(const double z, const int ind_layer) const;
  Eigen::Matrix2cd get_Au_der(const double z, const int ind_layer) const;

  void compute_rtc();
  void compute_grtc();
  void compute_CdCu();

  const Eigen::ArrayXd z_, rho_, beta_, alpha_, mu_;
  const int nl_;
  const std::complex<double> angfreq_;
  const double c_;

  Eigen::ArrayXcd gamma_, nv_;
  std::vector<Eigen::Matrix2cd, Eigen::aligned_allocator<Eigen::Matrix2cd>>
      e11_, e12_, e21_, e22_,   //
      t_d_, r_ud_, r_du_, t_u_, //
      gt_d_, gr_ud_, gr_du_, gt_u_;
  std::vector<Eigen::Vector2cd, Eigen::aligned_allocator<Eigen::Vector2cd>> Cd_,
      Cu_;

  const Eigen::Matrix2cd matI_ = Eigen::Matrix2cd::Identity();
};

class IntegralLayer {
public:
  EIGEN_MAKE_ALIGNED_OPERATOR_NEW
  IntegralLayer(const Eigen::Ref<const Eigen::ArrayXXd> model,
                const double freq, const double c);

  double compute_I1();
  double compute_I2();
  double compute_I3();
  Eigen::ArrayXd compute_kvs();

private:
  void initialize_P();
  void initialize_sigma();

  double intker_us2_top(int id_layer);
  double intker_ur2_top(int id_layer);
  double intker_dus2_top(int id_layer);
  double intker_dur2_top(int id_layer);
  double intker_urdus_top(int id_layer);
  double intker_usdur_top(int id_layer);

  double intker_us2_bottom(int id_layer);
  double intker_ur2_bottom(int id_layer);
  double intker_dus2_bottom(int id_layer);
  double intker_dur2_bottom(int id_layer);
  double intker_urdus_bottom(int id_layer);
  double intker_usdur_bottom(int id_layer);

  void integrate_us2();
  void integrate_ur2();
  void integrate_dus2();
  void integrate_dur2();
  void integrate_usdur();
  void integrate_urdus();

  const std::unique_ptr<GRTCoeff> grtc_;
  const int nl_;
  const double k_;
  const double pvel_;
  Eigen::ArrayXd z_, alpha_, beta_, rho_, mu_, lamb_;
  Eigen::ArrayXcd gamma_, nv_;
  Eigen::ArrayXcd thickness_;

  std::vector<Eigen::Vector2cd, Eigen::aligned_allocator<Eigen::Vector2cd>> Cd_,
      Cu_;
  VecMat24cd matE_;
  VecMat4cd matP_u_u_;
  VecMat4cd matP_uc_u_;
  VecMat4cd matP_uc_uc_;
  VecMat4cd matP_du_du_;
  VecMat4cd matP_duc_du_;
  VecMat4cd matP_duc_duc_;
  VecMat4cd matP_u_du_;
  VecMat4cd matP_uc_du_;
  VecMat4cd matP_u_duc_;
  VecMat4cd matP_uc_duc_;
  // top is the upper limit of integral
  VecMat4cd sigma_x_sigma_top_;
  VecMat4cd sigmac_x_sigma_top_;
  VecMat4cd sigma_x_sigmac_top_;
  VecMat4cd sigmac_x_sigmac_top_;
  // bottom is the lower limit
  VecMat4cd sigma_x_sigma_bottom_;
  VecMat4cd sigmac_x_sigma_bottom_;
  VecMat4cd sigma_x_sigmac_bottom_;
  VecMat4cd sigmac_x_sigmac_bottom_;

  Eigen::ArrayXd int_us2_;
  Eigen::ArrayXd int_ur2_;
  Eigen::ArrayXd int_dus2_;
  Eigen::ArrayXd int_dur2_;
  Eigen::ArrayXd int_urdus_;
  Eigen::ArrayXd int_usdur_;
};

class GradientPSV : public Gradient {
public:
  using Gradient::Gradient;
  ~GradientPSV();
  Eigen::ArrayXd compute(const double freq, const double c) const override;
};

} // namespace grad_psv

#endif


/*
Copyright 2012, 2013 Rogier van Dalen.

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.
*/

/** \internal \file
Arithmetic operations on values stored as logarithms.
These also take Boost.Math policies with regard to error handling into account.
*/

#ifndef MATH_DETAIL_LOG_FLOAT_ARITHMETIC_HPP
#define MATH_DETAIL_LOG_FLOAT_ARITHMETIC_HPP

#include <cmath>
#include <cassert>
#include <limits>

#include <boost/static_assert.hpp>
#include <boost/math/policies/policy.hpp>
#include <boost/math/tools/promotion.hpp>
#include <boost/math/special_functions/fpclassify.hpp>

#include "./log-float_fwd.hpp"

namespace math { namespace detail {

    /*
    std::log1p gives exactly the behaviour we need: log1p (-1) should
    evaluate to -infinity, which indicates a stored value of 0.
    A policy could be used with boost::math::log1p, but this is slower.
    */
    using std::log1p;
    using std::exp;
    using std::log;

    /**
    Multiply two numbers represented as their logs.
    This is equivalent to adding them.
    However, this takes care to produce the correct errors where applicable.
    */
    template <class RealType1, class RealType2, class Policy,
            class OverflowPolicy, class IndeterminateResultPolicy>
        inline typename promote_args <RealType1, RealType2>::type
            multiply_log_float (RealType1 const & loga, RealType2 const & logb,
            Policy const & policy,
            OverflowPolicy const &, IndeterminateResultPolicy const &)
    {
        static char const * function_name = "multiplication of log_float<%1%>";
        typedef typename promote_args <RealType1, RealType2>::type
            result_type;
        static constexpr RealType1 a_infinity =
            std::numeric_limits <RealType1>::infinity();
        static constexpr RealType2 b_infinity =
            std::numeric_limits <RealType2>::infinity();
        static constexpr result_type result_infinity =
            std::numeric_limits <result_type>::infinity();
        static constexpr result_type result_nan =
            std::numeric_limits <result_type>::quiet_NaN();

        // This explicitly provides behaviour that IEEE standard floating
        // point numbers should provide.
        if (isnan (loga) || isnan (logb))
            return result_nan;
        else if (isinf (loga) || isinf (logb))
        {
            if (isinf (loga) && isinf (logb))
            {
                if (loga == a_infinity && logb == b_infinity) {
                    // +inf * +inf = +inf.
                    // Any overflow has happened before this operation.
                    return result_infinity;
                } else if (loga == -a_infinity && logb == -b_infinity) {
                    // 0 * 0 = 0.
                    // Any underflow has happened before this operation.
                    return - result_infinity;
                } else {
                    // loga and logb are of opposite signs.
                    return raise_indeterminate_result_error (
                        function_name, "%1% * inf is undefined",
                        result_type(), result_nan, Policy());
                }
            } else {
                // Either loga or logb is infinity,
                // either positive or negative.
                if (loga == -a_infinity || logb == -b_infinity)
                    // 0 * (finite value) = 0
                    return - result_infinity;
                else {
                    assert (loga == a_infinity || logb == b_infinity);
                    // +inf * (finite value) = +inf
                    return result_infinity;
                }
            }
        } else {
            result_type result = loga + logb;
            if (isinf (result)) {
                if (result == -result_infinity) {
                    raise_underflow_error <result_type> (function_name,
                        "Result of multiplication has underflowed",
                        Policy());
                } else {
                    assert (result == result_infinity);
                    raise_overflow_error <result_type> (function_name,
                        "Result of multiplication has overflowed",
                        Policy());
                }
            }
            return result;
        }
    }

    /**
    Multiply two numbers represented as their logs.
    This is equivalent to adding them.
    This implementation is used only when errors are ignored.
    It may be faster than the explicit implementation.
    */
    template <class RealType1, class RealType2, class Policy>
        inline typename promote_args <RealType1, RealType2>::type
            multiply_log_float (RealType1 const & loga, RealType2 const & logb,
                Policy const &,
                boost::math::policies::overflow_error<
                    boost::math::policies::ignore_error> const &,
                boost::math::policies::indeterminate_result_error<
                    boost::math::policies::ignore_error> const &)
    {
        return loga + logb;
    }

    /**
    Multiply two numbers represented as their logs.
    */
    template <class RealType1, class RealType2, class Policy>
        inline typename promote_args <RealType1, RealType2>::type
            multiply_log_float (RealType1 const & loga, RealType2 const & logb,
                Policy const & policy)
    {
        // Forward to an appropriate implementation depending on the
        // error policy.
        return multiply_log_float (loga, logb, policy,
            typename Policy::overflow_error_type(),
            typename Policy::indeterminate_result_error_type());
    }

    /**
    Divide two numbers represented as their logs.
    This is equivalent to subtraction.
    However, this takes care to produce the correct errors where applicable.
    */
    template <class NumeratorType, class DenominatorType, class Policy,
            class OverflowPolicy, class UnderflowPolicy,
            class IndeterminateResultPolicy>
        inline typename promote_args <NumeratorType, DenominatorType>::type
            divide_log_float (NumeratorType const & log_numerator,
                DenominatorType const & log_denominator,
                Policy const & policy,
                OverflowPolicy const &, UnderflowPolicy const &,
                IndeterminateResultPolicy const &)
    {
        static char const * function_name = "division of log_float<%1%>";
        typedef typename promote_args <NumeratorType, DenominatorType>::type
            result_type;
        static constexpr NumeratorType numerator_infinity =
            std::numeric_limits <NumeratorType>::infinity();
        static constexpr DenominatorType denominator_infinity =
            std::numeric_limits <DenominatorType>::infinity();
        static constexpr result_type result_infinity =
            std::numeric_limits <result_type>::infinity();
        static constexpr result_type result_nan =
            std::numeric_limits <result_type>::quiet_NaN();

        // This explicitly provides behaviour that IEEE standard floating
        // point numbers should provide.
        if (isnan (log_numerator) || isnan (log_denominator))
            return result_nan;
        else if (isinf (log_numerator) || isinf (log_denominator)) {
            if (log_denominator == -denominator_infinity) {
                if (log_numerator == -numerator_infinity) {
                    return raise_indeterminate_result_error (
                        function_name, "%1% / 0 is undefined",
                        result_type(), result_nan, Policy());
                } else {
                    if (log_numerator == numerator_infinity)
                        // inf / 0 = inf: not really overflow,
                        // just propagation of infinity.
                        return result_infinity;
                    else
                        return raise_overflow_error <result_type> (
                            function_name, "division by zero causes overflow",
                            Policy());
                }
            } else if (log_denominator == denominator_infinity) {
                if (log_numerator == numerator_infinity) {
                    return raise_indeterminate_result_error (
                        function_name, "%1% / %1% is undefined",
                        result_type (log_numerator), result_nan, Policy());
                } else {
                    // (finite value) / inf = 0.
                    // The overflow has already happened, so this is not
                    // classified as underflow.
                    return -result_infinity;
                }
            } else {
                assert (!isinf (log_denominator));
                assert (isinf (log_numerator));
                if (log_numerator == -numerator_infinity)
                    return -result_infinity;
                else {
                    assert (log_numerator == numerator_infinity);
                    return result_infinity;
                }
            }
        } else {
            result_type result = log_numerator - log_denominator;
            if (isinf (result)) {
                if (result == -result_infinity
                        && log_numerator != -numerator_infinity) {
                    raise_underflow_error <result_type> (function_name,
                        "Result of division has underflowed",
                        Policy());
                } else {
                    assert (result == result_infinity);
                    if (log_numerator != numerator_infinity)
                        raise_overflow_error <result_type> (function_name,
                            "Result of division has overflowed",
                            Policy());
                }
            }
            return result;
        }
    }

    /**
    Divide two numbers represented as their logs.
    This is equivalent to subtraction.
    This implementation is used only when errors are ignored.
    It may be faster than the explicity implementation.
    */
    template <class NumeratorType, class DenominatorType, class Policy>
        inline typename promote_args <NumeratorType, DenominatorType>::type
            divide_log_float (NumeratorType const & log_numerator,
                DenominatorType const & log_denominator,
                Policy const &,
                boost::math::policies::overflow_error<
                    boost::math::policies::ignore_error> const &,
                boost::math::policies::underflow_error<
                    boost::math::policies::ignore_error> const &,
                boost::math::policies::indeterminate_result_error<
                    boost::math::policies::ignore_error> const &)
    { return log_numerator - log_denominator; }

    /**
    Divide two numbers represented as their logs.
    This is equivalent to subtraction.
    */
    template <class NumeratorType, class DenominatorType, class Policy>
        inline typename promote_args <NumeratorType, DenominatorType>::type
            divide_log_float (NumeratorType const & log_numerator,
                DenominatorType const & log_denominator,
                Policy const & policy)
    {
        // Forward to an appropriate implementation depending on the
        // error policy.
        return divide_log_float (log_numerator, log_denominator, policy,
            typename Policy::overflow_error_type(),
            typename Policy::underflow_error_type(),
            typename Policy::indeterminate_result_error_type());
    }

    /**
    Add two numbers represented as their logs.
    This takes care to produce the correct errors where applicable.
    */
    template <class RealType1, class RealType2,
            class Policy, class OverflowPolicy>
        inline typename promote_args <RealType1, RealType2>::type
            add_log_float (RealType1 const & loga, RealType2 const & logb,
            Policy const & policy, OverflowPolicy const &)
    {
        static char const * function_name = "addition of log_float<%1%>";
        typedef typename promote_args <RealType1, RealType2>::type
            result_type;
        static constexpr RealType1 a_infinity =
            std::numeric_limits <RealType1>::infinity();
        static constexpr RealType2 b_infinity =
            std::numeric_limits <RealType2>::infinity();
        static constexpr result_type result_infinity =
            std::numeric_limits <result_type>::infinity();
        static constexpr result_type result_nan =
            std::numeric_limits <result_type>::quiet_NaN();

        // This explicitly provides behaviour that IEEE standard floating
        // point numbers should provide.
        if (isnan (loga) || isnan (logb))
            return result_nan;
        else if (isinf (loga) && isinf (logb)) {
            if (loga == -a_infinity && logb == -b_infinity)
                return -result_infinity;
            else {
                assert (loga == a_infinity || logb == b_infinity);
                // 0 + inf = inf
                // inf + inf = inf
                return result_infinity;
            }
        } else {
            result_type result;
            if (loga > logb)
                result = loga + log1p (exp (logb - loga));
            else
                result = logb + log1p (exp (loga - logb));

            if (result == result_infinity &&
                    loga != a_infinity && logb != b_infinity) {
                raise_overflow_error <result_type> (function_name,
                    "Result of addition has overflowed",
                    Policy());
            }
            return result;
        }
    }

    /**
    Add two numbers represented as their logs.
    This implementation, for policies that ignore errors, is somewhat
    faster.
    */
    template <class RealType1, class RealType2, class Policy>
        inline typename promote_args <RealType1, RealType2>::type
            add_log_float (RealType1 const & loga, RealType2 const & logb,
                Policy const & policy,
                boost::math::policies::overflow_error <
                    boost::math::policies::ignore_error> const &)
    {
        typedef typename promote_args <RealType1, RealType2>::type
            result_type;

        /*
        A normal implementation of addition in the log-domain uses,
        (assuming, without loss of generalisation, that loga >= logb):
        1)  log (a + b) = loga + log (1 + exp (logb - loga))
            where implementing log (1 + ...) with log1p yields marginal
            improvements.
        Corner cases are NaNs and infinities.
        The following implementation aims to use as few cases as possible
        by allowing most corner cases to be dealt with by the IEEE floating
        point operations:
        2)  isnan (loga) || isnan (logb):
            Any computation that involves both loga and logb, such as the
            one for case (1), returns NaN, which is correct.
        Infinities (assume again that loga >= logb):
        3)  If (loga == logb), either both +infinity, or both -infinity:
            logb - loga = NaN
            and the result will be NaN, which is incorrect.
            This requires a special case.
            A solution is to return loga or logb.
        4)  Otherwise, (loga == +infinity || logb == -infinity):
            The default implementation returns loga:
                logb - loga = -infinity
                exp (logb - loga) = 0
                log1p (0) = 0
                loga + 0 = loga
            which is correct.
            No special handling is required.
        Note, however, that for loga >= logb, (3) and (4) are handled by
            return loga
        and vice versa.
        */
        using std::abs;
        using std::max;

        if (!::boost::math::isinf (loga)) {
            result_type difference = -abs (logb - loga);
            result_type greatest = (max) (loga, logb);
            return greatest + log1p (exp (difference));
        } else {
            if (!(loga >= logb))
                return result_type (logb);
            else
                return result_type (loga);
        }
    }

    /**
    Add two numbers represented as their logs.
    */
    template <class RealType1, class RealType2, class Policy>
        inline typename promote_args <RealType1, RealType2>::type
            add_log_float (RealType1 const & loga, RealType2 const & logb,
            Policy const & policy)
    {
        return add_log_float (loga, logb, policy,
            typename Policy::overflow_error_type());
    }

    /**
    \pre !(loga < logb)
        (This is not the same as loga >= logb in the face of NaNs.)
    */
    template <class RealType1, class RealType2, class Policy,
            class UnderflowPolicy, class IndeterminateResultPolicy>
        inline typename promote_args <RealType1, RealType2>::type
            subtract_log_float (RealType1 const & loga, RealType2 const & logb,
            Policy const & policy,
            UnderflowPolicy const &, IndeterminateResultPolicy const &)
    {
        assert (!(loga < logb));
        static char const * function_name = "subtraction of log_float<%1%>";
        typedef typename promote_args <RealType1, RealType2>::type
            result_type;
        static constexpr RealType1 a_infinity =
            std::numeric_limits <RealType1>::infinity();
        static constexpr RealType2 b_infinity =
            std::numeric_limits <RealType2>::infinity();
        static constexpr result_type result_infinity =
            std::numeric_limits <result_type>::infinity();
        static constexpr result_type result_nan =
            std::numeric_limits <result_type>::quiet_NaN();

        // This explicitly provides behaviour that IEEE standard floating
        // point numbers should provide.
        if (isnan (loga) || isnan (logb))
            return result_nan;
        else if (loga == a_infinity) {
            if (logb == b_infinity) {
                return raise_indeterminate_result_error (
                    function_name, "%1% - inf is undefined",
                    result_infinity, result_nan, policy);
            } else
                // Any overflow has taken place already.
                return result_infinity;
        } else if (loga == logb) {
            return -result_infinity;
        } else {
            result_type result;
            result = loga + log1p (-exp (logb - loga));

            if (result == - result_infinity) {
                raise_underflow_error <result_type> (function_name,
                    "Result of subtraction has underflowed",
                    Policy());
            }
            return result;
        }
    }

    template <class RealType1, class RealType2, class Policy>
        inline typename promote_args <RealType1, RealType2>::type
            subtract_log_float (RealType1 const & loga, RealType2 const & logb,
            Policy const & policy,
                boost::math::policies::underflow_error<
                    boost::math::policies::ignore_error> const &,
                boost::math::policies::indeterminate_result_error<
                    boost::math::policies::ignore_error> const &)
    {
        assert (!(loga < logb));
        if (loga == -std::numeric_limits <RealType1>::infinity())
            // logb == -infinity or nan.
            return loga + logb;
        else
            return loga + log1p (-exp (logb - loga));
    }

    /**
    \pre !(loga < logb)
        (This is not the same as loga >= logb in the face of NaNs.)
    */
    template <class RealType1, class RealType2, class Policy>
        inline typename promote_args <RealType1, RealType2>::type
            subtract_log_float (RealType1 const & loga, RealType2 const & logb,
            Policy const & policy)
    {
        return subtract_log_float (loga, logb, policy,
            typename Policy::underflow_error_type(),
            typename Policy::indeterminate_result_error_type());
    }

    template <class RealType1, class RealType2, class Policy>
        inline std::pair <
            typename promote_args <RealType1, RealType2>::type, int>
        add_signed_log_float (RealType1 const & loga, int sign_a,
            RealType2 const & logb, int sign_b,
            Policy const & policy)
    {
        if (sign_a == sign_b)
            return std::make_pair (add_log_float (loga, logb, policy), sign_a);
        else {
            if (loga >= logb)
                return std::make_pair (
                    subtract_log_float (loga, logb, policy), sign_a);
            else
                return std::make_pair (
                    subtract_log_float (logb, loga, policy), sign_b);
        }
    }

}} // namespace math::detail

#endif  // MATH_DETAIL_LOG_FLOAT_ARITHMETIC_HPP


#include <boost/numeric/mtl/mtl.hpp>

int main(int, char**)
{
    using namespace mtl; using namespace mtl::mat;

    const unsigned n= 20;
    dense2D<double>                            A(n, n), B(n, n);
    morton_dense<double, doppled_64_row_mask>  C(n, n);

    hessian_setup(A, 3.0); hessian_setup(B, 1.0);
    hessian_setup(C, 2.0);

    // Corresponds to A= B * B;
    mult(B, B, A);

    A= B * B;   // use BLAS
    A= B * C;   // use recursion + tiling from MTL4

    A+= B * C;  // Increment A by the product of B and C
    A-= B * C;  // Likewise with decrement

    return 0;
}


// Boost.Geometry (aka GGL, Generic Geometry Library)
// This file is manually converted from PROJ4

// Copyright (c) 2008-2012 Barend Gehrels, Amsterdam, the Netherlands.

// This file was modified by Oracle on 2018.
// Modifications copyright (c) 2018, Oracle and/or its affiliates.
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle

// Use, modification and distribution is 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)

// This file is converted from PROJ4, http://trac.osgeo.org/proj
// PROJ4 is originally written by Gerald Evenden (then of the USGS)
// PROJ4 is maintained by Frank Warmerdam
// PROJ4 is converted to Geometry Library by Barend Gehrels (Geodan, Amsterdam)

// Original copyright notice:

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

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

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

#ifndef BOOST_GEOMETRY_PROJECTIONS_IMPL_AASINCOS_HPP
#define BOOST_GEOMETRY_PROJECTIONS_IMPL_AASINCOS_HPP

#include <cmath>

#include <boost/geometry/srs/projections/exception.hpp>
#include <boost/geometry/srs/projections/impl/pj_strerrno.hpp>
#include <boost/geometry/util/math.hpp>

namespace boost { namespace geometry { namespace projections
{

namespace detail
{

namespace aasincos
{
    template <typename T>
    inline T ONE_TOL() { return 1.00000000000001; }
    //template <typename T>
    //inline T TOL() { return 0.000000001; }
    template <typename T>
    inline T ATOL() { return 1e-50; }
}

template <typename T>
inline T aasin(T const& v)
{
    T av = 0;

    if ((av = geometry::math::abs(v)) >= 1.0)
    {
        if (av > aasincos::ONE_TOL<T>())
        {
            BOOST_THROW_EXCEPTION( projection_exception(error_acos_asin_arg_too_large) );
        }
        return (v < 0.0 ? -geometry::math::half_pi<T>() : geometry::math::half_pi<T>());
    }

    return asin(v);
}

template <typename T>
inline T aacos(T const& v)
{
    T av = 0;

    if ((av = geometry::math::abs(v)) >= 1.0)
    {
        if (av > aasincos::ONE_TOL<T>())
        {
            BOOST_THROW_EXCEPTION( projection_exception(error_acos_asin_arg_too_large) );
        }
        return (v < 0.0 ? geometry::math::pi<T>() : 0.0);
    }

    return acos(v);
}

template <typename T>
inline T asqrt(T const& v)
{
    return ((v <= 0) ? 0 : sqrt(v));
}

template <typename T>
inline T aatan2(T const& n, T const& d)
{
    return ((geometry::math::abs(n) < aasincos::ATOL<T>()
        && geometry::math::abs(d) < aasincos::ATOL<T>()) ? 0.0 : atan2(n, d));
}

} // namespace detail

}}} // namespace boost::geometry::projections

#endif // BOOST_GEOMETRY_PROJECTIONS_IMPL_AASINCOS_HPP


#ifndef __QUADROTOR_UKF_HH__
#define __QUADROTOR_UKF_HH__

#include <list>
#include <cmath>
#include <vector>
#include <string>
#include <iostream>
#include <algorithm>
#include <armadillo>

#include <ros/ros.h>

#include <pose_utils.h>

using namespace arma;

class QuadrotorUKF
{
	private:

		// State History and Covariance
		list<colvec>    xHist;
		list<colvec>    uHist;
		list<ros::Time> xTimeHist;
		mat P;

		// Process Covariance Matrix
		mat Rv;

		// Instance sigma points
		mat Xa;
		mat Va;

		// Initial process update indicator
		bool initMeasure;
		bool initGravity;

		// Dimemsions
		int stateCnt;
		int procNoiseCnt;
		int measNoiseSLAMCnt;
		int measNoiseGPSCnt;
		int L;

		// Gravity
		double g;

		// UKF Parameters
		double alpha;
		double beta;
		double kappa;
		double lambda;
		double gamma;
		// UKF Weights
		rowvec wm;
		rowvec wc;

		// Private functions
		void GenerateWeights();
		void GenerateSigmaPoints();
		colvec ProcessModel(const colvec& x, const colvec& u, const colvec& v, double dt);
		mat MeasurementModelSLAM();
		mat MeasurementModelGPS();
		void PropagateAprioriCovariance(const ros::Time time, list<colvec>::iterator& kx, list<colvec>::iterator& ku, list<ros::Time>::iterator& kt);
		void PropagateAposterioriState(list<colvec>::iterator kx, list<colvec>::iterator ku, list<ros::Time>::iterator kt);

	public:

		QuadrotorUKF();
		~QuadrotorUKF();

		bool      isInitialized();
		colvec    GetState();
		ros::Time GetStateTime();
		mat       GetStateCovariance();

		void SetGravity(double _g);
		void SetImuCovariance(const mat& _Rv);
		void SetUKFParameters(double _alpha, double _beta, double _kappa);
		void SetInitPose(colvec p, ros::Time time);

		bool ProcessUpdate(colvec u, ros::Time time);
		bool MeasurementUpdateSLAM(colvec z, mat RnSLAM, ros::Time time);
		bool MeasurementUpdateGPS(colvec z, mat RnGPS, ros::Time time);
};

#endif


//
// Created by asem on 01/01/19.
//

#ifndef MARKOVIAN_FEATURES_AAINDEXCLUSTERING_HPP
#define MARKOVIAN_FEATURES_AAINDEXCLUSTERING_HPP

#include "dlib_utilities.hpp"
#include <dlib/svm.h>

#include "SimilarityMetrics.hpp"
#include "LabeledEntry.hpp"
#include "AAIndexDBGET.hpp"
#include "common.hpp"
#include "LUT.hpp"

namespace aaindex {

class AAIndexClustering
{
    using SampleType = dlib::matrix<double, 0, 0>;

public:
    explicit AAIndexClustering(
            const std::vector<AAIndex1> &index,
            size_t nClusters
    )
            : _index( index ),
              _dims( index.size()),
              _nClusters( nClusters )
    {}

    virtual void runClustering()
    {

        auto samplesVector = _samples();

        {
            std::vector<SampleType> centroids;

            dlib::pick_initial_centers( _nClusters, centroids, samplesVector );
            dlib::find_clusters_using_kmeans( samplesVector, centroids );

            _centroids.emplace( std::move( centroids ));
        }

        _clusters.emplace( LUT<char, long>::makeLUT(
                [this]( char aa ) -> long {
                    if ( auto point = getPoint( aa ); point )
                        return _closestIdx( _centroids.value(), point.value());
                    else return -1;
                } ));
    }

    std::optional<SampleType> getPoint( char aa ) const
    {
        using namespace dlib_utilities;
        std::vector<double> point;
        for (auto &index : _index)
        {
            if ( auto component = index.normalizedIndex( aa ); component )
                point.push_back( component.value());
            else return std::nullopt;
        }
        return vector_to_column_matrix_like( std::move( point ));
    }

    std::optional<size_t> getCluster( char aa ) const
    {
        assert( _centroids.has_value() && _clusters.has_value());
        if ( auto cluster = _clusters->at( aa ); cluster >= 0 )
            return size_t( cluster );
        else return std::nullopt;
    }

protected:

    long _closestIdx(
            const std::vector<SampleType> &centroids,
            const SampleType &point
    )
    {
        static auto euclidean = Euclidean::template similarityFunctor<SampleType>();

        auto minIt = std::min_element(
                centroids.cbegin(), centroids.cend(),
                [&](
                        SampleType x,
                        SampleType y
                ) {
                    return euclidean( x, point ) < euclidean( y, point );
                } );
        return std::distance( centroids.cbegin(), minIt );
    }

    std::vector<SampleType> _samples() const
    {
        std::vector<SampleType> samples;
        for (auto aa : AMINO_ACIDS20)
        {
            auto point = getPoint( aa );
            assert( point.has_value());
            samples.emplace_back( getPoint( aa ).value());
        }
        return samples;
    }

private:
    const std::vector<AAIndex1> _index;
    const size_t _nClusters;
    const size_t _dims;

    std::optional<std::vector<SampleType>> _centroids;
    std::optional<LUT<char, long>> _clusters;
};

}

#endif //MARKOVIAN_FEATURES_AAINDEXCLUSTERING_HPP


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

/*
  Copyright (C) 2008, 2009, 2014 Klaus Spanderen
  Copyright (C) 2014 Johannes Göttker-Schnetmann

 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.
*/

#include "fdheston.hpp"
#include "utilities.hpp"

#include <ql/math/functional.hpp>
#include <ql/quotes/simplequote.hpp>
#include <ql/time/calendars/target.hpp>
#include <ql/time/daycounters/actual360.hpp>
#include <ql/time/daycounters/actualactual.hpp>
#include <ql/time/daycounters/actual365fixed.hpp>
#include <ql/instruments/barrieroption.hpp>
#include <ql/instruments/dividendvanillaoption.hpp>
#include <ql/models/equity/hestonmodel.hpp>
#include <ql/termstructures/yield/zerocurve.hpp>
#include <ql/termstructures/yield/flatforward.hpp>
#include <ql/termstructures/volatility/equityfx/localconstantvol.hpp>
#include <ql/methods/finitedifferences/meshers/fdmhestonvariancemesher.hpp>
#include <ql/pricingengines/barrier/analyticbarrierengine.hpp>
#include <ql/pricingengines/vanilla/analytichestonengine.hpp>
#include <ql/pricingengines/vanilla/analyticeuropeanengine.hpp>
#include <ql/pricingengines/barrier/fdhestonbarrierengine.hpp>
#include <ql/pricingengines/vanilla/fdhestonvanillaengine.hpp>
#include <ql/pricingengines/barrier/fdblackscholesbarrierengine.hpp>
#include <ql/pricingengines/vanilla/fdblackscholesvanillaengine.hpp>

#include <boost/assign/std/vector.hpp>
#include <ql/tuple.hpp>

using namespace QuantLib;
using namespace boost::assign;
using boost::unit_test_framework::test_suite;

namespace fd_heston_test {
    struct NewBarrierOptionData {
        Barrier::Type barrierType;
        Real barrier;
        Real rebate;
        Option::Type type;
        Real strike;
        Real s;        // spot
        Rate q;        // dividend
        Rate r;        // risk-free rate
        Time t;        // time to maturity
        Volatility v;  // volatility
    };

    class ParableLocalVolatility : public LocalVolTermStructure {
      public:
        ParableLocalVolatility(
            const Date& referenceDate,
            Real s0,
            Real alpha,
            const DayCounter& dayCounter)
        : LocalVolTermStructure(
              referenceDate, NullCalendar(), Following, dayCounter),
          referenceDate_(referenceDate),
          s0_(s0),
          alpha_(alpha) {}

        Date maxDate() const override { return Date::maxDate(); }
        Real minStrike() const override { return 0.0; }
        Real maxStrike() const override { return std::numeric_limits<Real>::max(); }

      protected:
        Volatility localVolImpl(Time t, Real s) const override {
            return alpha_*(square<Real>()(s0_ - s) + 25.0);
        }

      private:
        const Date referenceDate_;
        const Real s0_, alpha_;
    };
}

void FdHestonTest::testFdmHestonVarianceMesher() {
    BOOST_TEST_MESSAGE("Testing FDM Heston variance mesher...");

    using namespace fd_heston_test;

    SavedSettings backup;

    const Date today = Date(22, February, 2018);
    const DayCounter dc = Actual365Fixed();
    Settings::instance().evaluationDate() = today;

    const ext::shared_ptr<HestonProcess> process(
        ext::make_shared<HestonProcess>(
            Handle<YieldTermStructure>(flatRate(0.02, dc)),
            Handle<YieldTermStructure>(flatRate(0.02, dc)),
            Handle<Quote>(ext::make_shared<SimpleQuote>(100.0)),
            0.09, 1.0, 0.09, 0.2, -0.5));

    const ext::shared_ptr<FdmHestonVarianceMesher> mesher
        = ext::make_shared<FdmHestonVarianceMesher>(5, process, 1.0);

    const std::vector<Real> locations = mesher->locations();

    const Real expected[] = {
        0.0, 6.652314e-02, 9.000000e-02, 1.095781e-01, 2.563610e-01
    };

    const Real tol = 1e-6;
    for (Size i=0; i < locations.size(); ++i) {
        const Real diff = std::fabs(expected[i] - locations[i]);

        if (diff > tol) {
            BOOST_ERROR("Failed to reproduce Heston variance mesh"
                        << "\n    calculated: " << locations[i]
                        << "\n    expected:   " << expected[i]
                        << std::scientific
                        << "\n    difference  " << diff
                        << "\n    tolerance:  " << tol);
        }
    }

    const ext::shared_ptr<LocalVolTermStructure> lVol =
        ext::make_shared<LocalConstantVol>(today, 2.5, dc);

    const ext::shared_ptr<FdmHestonLocalVolatilityVarianceMesher> constSlvMesher
        = ext::make_shared<FdmHestonLocalVolatilityVarianceMesher>
              (5, process, lVol, 1.0);

    const Real expectedVol = 2.5 * mesher->volaEstimate();
    const Real calculatedVol = constSlvMesher->volaEstimate();

    const Real diff = std::fabs(calculatedVol - expectedVol);
    if (diff > tol) {
        BOOST_ERROR("Failed to reproduce Heston local volatility "
                "variance estimate"
                    << "\n    calculated: " << calculatedVol
                    << "\n    expected:   " << expectedVol
                    << std::scientific
                    << "\n    difference  " << diff
                    << "\n    tolerance:  " << tol);
    }

    const Real alpha = 0.01;
    const ext::shared_ptr<LocalVolTermStructure> leverageFct
        = ext::make_shared<ParableLocalVolatility>(today, 100.0, alpha, dc);

    const ext::shared_ptr<FdmHestonLocalVolatilityVarianceMesher> slvMesher
        = ext::make_shared<FdmHestonLocalVolatilityVarianceMesher>(
              5, process, leverageFct, 0.5, 1, 0.01);

    const Real initialVolEstimate =
        ext::make_shared<FdmHestonVarianceMesher>(5, process, 0.5, 1, 0.01)->
            volaEstimate();

    // const Real vEst = leverageFct->localVol(0, 100) * initialVolEstimate;
    // Mathematica solution
    //    N[Integrate[
    //      alpha*((100*Exp[vEst*x*Sqrt[0.5]] - 100)^2 + 25)*
    //       PDF[NormalDistribution[0, 1], x], {x ,
    //       InverseCDF[NormalDistribution[0, 1], 0.01],
    //       InverseCDF[NormalDistribution[0, 1], 0.99]}]]

    const Real leverageAvg = 0.455881 / (1-0.02);

    const Real volaEstExpected =
        0.5*(leverageAvg + leverageFct->localVol(0, 100)) * initialVolEstimate;

    const Real volaEstCalculated = slvMesher->volaEstimate();

    if (std::fabs(volaEstExpected - volaEstCalculated) > 0.001) {
        BOOST_ERROR("Failed to reproduce Heston local volatility "
                "variance estimate"
                    << "\n    calculated: " << calculatedVol
                    << "\n    expected:   " << expectedVol
                    << std::scientific
                    << "\n    difference  " << std::fabs(volaEstExpected - volaEstCalculated)
                    << "\n    tolerance:  " << tol);
    }
}

void FdHestonTest::testFdmHestonBarrierVsBlackScholes() {

    BOOST_TEST_MESSAGE("Testing FDM with barrier option in Heston model...");

    using namespace fd_heston_test;

    SavedSettings backup;

    NewBarrierOptionData values[] = {
        /* The data below are from
          "Option pricing formulas", E.G. Haug, McGraw-Hill 1998 pag. 72
        */
        //     barrierType, barrier, rebate,         type, strike,     s,    q,    r,    t,    v
        { Barrier::DownOut,    95.0,    3.0, Option::Call,     90, 100.0, 0.04, 0.08, 0.50, 0.25},
        { Barrier::DownOut,    95.0,    3.0, Option::Call,    100, 100.0, 0.00, 0.08, 1.00, 0.30},
        { Barrier::DownOut,    95.0,    3.0, Option::Call,    110, 100.0, 0.04, 0.08, 0.50, 0.25},
        { Barrier::DownOut,   100.0,    3.0, Option::Call,     90, 100.0, 0.00, 0.08, 0.25, 0.25},
        { Barrier::DownOut,   100.0,    3.0, Option::Call,    100, 100.0, 0.04, 0.08, 0.50, 0.25},
        { Barrier::DownOut,   100.0,    3.0, Option::Call,    110, 100.0, 0.04, 0.08, 0.50, 0.25},
        { Barrier::UpOut,     105.0,    3.0, Option::Call,     90, 100.0, 0.04, 0.08, 0.50, 0.25},
        { Barrier::UpOut,     105.0,    3.0, Option::Call,    100, 100.0, 0.04, 0.08, 0.50, 0.25},
        { Barrier::UpOut,     105.0,    3.0, Option::Call,    110, 100.0, 0.04, 0.08, 0.50, 0.25},

        { Barrier::DownIn,     95.0,    3.0, Option::Call,    90, 100.0, 0.04, 0.08, 0.50, 0.25},
        { Barrier::DownIn,     95.0,    3.0, Option::Call,   100, 100.0, 0.04, 0.08, 0.50, 0.25},
        { Barrier::DownIn,     95.0,    3.0, Option::Call,   110, 100.0, 0.04, 0.08, 0.50, 0.25},
        { Barrier::DownIn,    100.0,    3.0, Option::Call,    90, 100.0, 0.00, 0.08, 0.25, 0.25},
        { Barrier::DownIn,    100.0,    3.0, Option::Call,   100, 100.0, 0.04, 0.08, 0.50, 0.25},
        { Barrier::DownIn,    100.0,    3.0, Option::Call,   110, 100.0, 0.04, 0.08, 0.50, 0.25},
        { Barrier::UpIn,      105.0,    3.0, Option::Call,    90, 100.0, 0.04, 0.08, 0.50, 0.25},
        { Barrier::UpIn,      105.0,    3.0, Option::Call,   100, 100.0, 0.00, 0.08, 0.40, 0.25},
        { Barrier::UpIn,      105.0,    3.0, Option::Call,   110, 100.0, 0.04, 0.08, 0.50, 0.15},

        { Barrier::DownOut,    95.0,    3.0, Option::Call,    90, 100.0, 0.04, 0.08, 0.50, 0.30},
        { Barrier::DownOut,    95.0,    3.0, Option::Call,   100, 100.0, 0.00, 0.08, 0.40, 0.35},
        { Barrier::DownOut,    95.0,    3.0, Option::Call,   110, 100.0, 0.04, 0.08, 0.50, 0.30},
        { Barrier::DownOut,   100.0,    3.0, Option::Call,    90, 100.0, 0.04, 0.08, 0.50, 0.15},
        { Barrier::DownOut,   100.0,    3.0, Option::Call,   100, 100.0, 0.04, 0.08, 0.50, 0.30},
        { Barrier::DownOut,   100.0,    3.0, Option::Call,   110, 100.0, 0.00, 0.00, 1.00, 0.20},
        { Barrier::UpOut,     105.0,    3.0, Option::Call,    90, 100.0, 0.04, 0.08, 0.50, 0.30},
        { Barrier::UpOut,     105.0,    3.0, Option::Call,   100, 100.0, 0.04, 0.08, 0.50, 0.30},
        { Barrier::UpOut,     105.0,    3.0, Option::Call,   110, 100.0, 0.04, 0.08, 0.50, 0.30},

        { Barrier::DownIn,     95.0,    3.0, Option::Call,    90, 100.0, 0.04, 0.08, 0.50, 0.30},
        { Barrier::DownIn,     95.0,    3.0, Option::Call,   100, 100.0, 0.04, 0.08, 0.50, 0.30},
        { Barrier::DownIn,     95.0,    3.0, Option::Call,   110, 100.0, 0.00, 0.08, 1.00, 0.30},
        { Barrier::DownIn,    100.0,    3.0, Option::Call,    90, 100.0, 0.04, 0.08, 0.50, 0.30},
        { Barrier::DownIn,    100.0,    3.0, Option::Call,   100, 100.0, 0.04, 0.08, 0.50, 0.30},
        { Barrier::DownIn,    100.0,    3.0, Option::Call,   110, 100.0, 0.04, 0.08, 0.50, 0.30},
        { Barrier::UpIn,      105.0,    3.0, Option::Call,    90, 100.0, 0.04, 0.08, 0.50, 0.30},
        { Barrier::UpIn,      105.0,    3.0, Option::Call,   100, 100.0, 0.04, 0.08, 0.50, 0.30},
        { Barrier::UpIn,      105.0,    3.0, Option::Call,   110, 100.0, 0.04, 0.08, 0.50, 0.30},

        { Barrier::DownOut,    95.0,    3.0,  Option::Put,    90, 100.0, 0.04, 0.08, 0.50, 0.25},
        { Barrier::DownOut,    95.0,    3.0,  Option::Put,   100, 100.0, 0.04, 0.08, 0.50, 0.25},
        { Barrier::DownOut,    95.0,    3.0,  Option::Put,   110, 100.0, 0.04, 0.08, 0.50, 0.25},
        { Barrier::DownOut,   100.0,    3.0,  Option::Put,    90, 100.0, 0.04, 0.08, 0.50, 0.25},
        { Barrier::DownOut,   100.0,    3.0,  Option::Put,   100, 100.0, 0.04, 0.08, 0.50, 0.25},
        { Barrier::DownOut,   100.0,    3.0,  Option::Put,   110, 100.0, 0.04, 0.08, 0.50, 0.25},
        { Barrier::UpOut,     105.0,    3.0,  Option::Put,    90, 100.0, 0.04, 0.08, 0.50, 0.25},
        { Barrier::UpOut,     105.0,    3.0,  Option::Put,   100, 100.0, 0.04, 0.08, 0.50, 0.25},
        { Barrier::UpOut,     105.0,    3.0,  Option::Put,   110, 100.0, 0.04, 0.08, 0.50, 0.25},

        { Barrier::DownIn,     95.0,    3.0,  Option::Put,    90, 100.0, 0.04, 0.08, 0.50, 0.25},
        { Barrier::DownIn,     95.0,    3.0,  Option::Put,   100, 100.0, 0.04, 0.08, 0.50, 0.25},
        { Barrier::DownIn,     95.0,    3.0,  Option::Put,   110, 100.0, 0.04, 0.08, 0.50, 0.25},
        { Barrier::DownIn,    100.0,    3.0,  Option::Put,    90, 100.0, 0.04, 0.08, 0.50, 0.25},
        { Barrier::DownIn,    100.0,    3.0,  Option::Put,   100, 100.0, 0.04, 0.08, 0.50, 0.25},
        { Barrier::DownIn,    100.0,    3.0,  Option::Put,   110, 100.0, 0.04, 0.08, 0.50, 0.25},
        { Barrier::UpIn,      105.0,    3.0,  Option::Put,    90, 100.0, 0.04, 0.08, 0.50, 0.25},
        { Barrier::UpIn,      105.0,    3.0,  Option::Put,   100, 100.0, 0.04, 0.08, 0.50, 0.25},
        { Barrier::UpIn,      105.0,    3.0,  Option::Put,   110, 100.0, 0.00, 0.04, 1.00, 0.15},

        { Barrier::DownOut,    95.0,    3.0,  Option::Put,    90, 100.0, 0.04, 0.08, 0.50, 0.30},
        { Barrier::DownOut,    95.0,    3.0,  Option::Put,   100, 100.0, 0.04, 0.08, 0.50, 0.30},
        { Barrier::DownOut,    95.0,    3.0,  Option::Put,   110, 100.0, 0.04, 0.08, 0.50, 0.30},
        { Barrier::DownOut,   100.0,    3.0,  Option::Put,    90, 100.0, 0.04, 0.08, 0.50, 0.30},
        { Barrier::DownOut,   100.0,    3.0,  Option::Put,   100, 100.0, 0.04, 0.08, 0.50, 0.30},
        { Barrier::DownOut,   100.0,    3.0,  Option::Put,   110, 100.0, 0.04, 0.08, 0.50, 0.30},
        { Barrier::UpOut,     105.0,    3.0,  Option::Put,    90, 100.0, 0.04, 0.08, 0.50, 0.30},
        { Barrier::UpOut,     105.0,    3.0,  Option::Put,   100, 100.0, 0.04, 0.08, 0.50, 0.30},
        { Barrier::UpOut,     105.0,    3.0,  Option::Put,   110, 100.0, 0.04, 0.08, 0.50, 0.30},

        { Barrier::DownIn,     95.0,    3.0,  Option::Put,    90, 100.0, 0.04, 0.08, 0.50, 0.30},
        { Barrier::DownIn,     95.0,    3.0,  Option::Put,   100, 100.0, 0.04, 0.08, 0.50, 0.30},
        { Barrier::DownIn,     95.0,    3.0,  Option::Put,   110, 100.0, 0.04, 0.08, 0.50, 0.30},
        { Barrier::DownIn,    100.0,    3.0,  Option::Put,    90, 100.0, 0.04, 0.08, 0.50, 0.30},
        { Barrier::DownIn,    100.0,    3.0,  Option::Put,   100, 100.0, 0.04, 0.08, 0.50, 0.30},
        { Barrier::DownIn,    100.0,    3.0,  Option::Put,   110, 100.0, 0.04, 0.08, 1.00, 0.15},
        { Barrier::UpIn,      105.0,    3.0,  Option::Put,    90, 100.0, 0.04, 0.08, 0.50, 0.30},
        { Barrier::UpIn,      105.0,    3.0,  Option::Put,   100, 100.0, 0.04, 0.08, 0.50, 0.30},
        { Barrier::UpIn,      105.0,    3.0,  Option::Put,   110, 100.0, 0.04, 0.08, 0.50, 0.30}
    };

    const DayCounter dc = Actual365Fixed();
    const Date todaysDate(28, March, 2004);
    const Date exerciseDate(28, March, 2005);
    Settings::instance().evaluationDate() = todaysDate;

    Handle<Quote> spot(
            ext::shared_ptr<Quote>(new SimpleQuote(0.0)));
    ext::shared_ptr<SimpleQuote> qRate(new SimpleQuote(0.0));
    Handle<YieldTermStructure> qTS(flatRate(qRate, dc));
    ext::shared_ptr<SimpleQuote> rRate(new SimpleQuote(0.0));
    Handle<YieldTermStructure> rTS(flatRate(rRate, dc));
    ext::shared_ptr<SimpleQuote> vol(new SimpleQuote(0.0));
    Handle<BlackVolTermStructure> volTS(flatVol(vol, dc));

    ext::shared_ptr<BlackScholesMertonProcess> bsProcess(
                      new BlackScholesMertonProcess(spot, qTS, rTS, volTS));

    ext::shared_ptr<PricingEngine> analyticEngine(
                                        new AnalyticBarrierEngine(bsProcess));

    for (auto& value : values) {
        Date exDate = todaysDate + timeToDays(value.t, 365);
        ext::shared_ptr<Exercise> exercise(new EuropeanExercise(exDate));

        ext::dynamic_pointer_cast<SimpleQuote>(spot.currentLink())->setValue(value.s);
        qRate->setValue(value.q);
        rRate->setValue(value.r);
        vol->setValue(value.v);

        ext::shared_ptr<StrikedTypePayoff> payoff(new PlainVanillaPayoff(value.type, value.strike));

        BarrierOption barrierOption(value.barrierType, value.barrier, value.rebate, payoff,
                                    exercise);

        const Real v0 = vol->value()*vol->value();
        ext::shared_ptr<HestonProcess> hestonProcess(
             new HestonProcess(rTS, qTS, spot, v0, 1.0, v0, 0.005, 0.0));

        barrierOption.setPricingEngine(ext::shared_ptr<PricingEngine>(
            new FdHestonBarrierEngine(ext::make_shared<HestonModel>(
                              hestonProcess), 200, 101, 3)));

        const Real calculatedHE = barrierOption.NPV();

        barrierOption.setPricingEngine(analyticEngine);
        const Real expected = barrierOption.NPV();

        const Real tol = 0.0025;
        if (std::fabs(calculatedHE - expected)/expected > tol) {
            BOOST_ERROR("Failed to reproduce expected Heston npv"
                        << "\n    calculated: " << calculatedHE
                        << "\n    expected:   " << expected
                        << "\n    tolerance:  " << tol);
        }
    }
}

void FdHestonTest::testFdmHestonBarrier() {

    BOOST_TEST_MESSAGE("Testing FDM with barrier option for Heston model vs "
                       "Black-Scholes model...");

    SavedSettings backup;

    Handle<Quote> s0(ext::shared_ptr<Quote>(new SimpleQuote(100.0)));

    Handle<YieldTermStructure> rTS(flatRate(0.05, Actual365Fixed()));
    Handle<YieldTermStructure> qTS(flatRate(0.0 , Actual365Fixed()));

    ext::shared_ptr<HestonProcess> hestonProcess(
        new HestonProcess(rTS, qTS, s0, 0.04, 2.5, 0.04, 0.66, -0.8));

    Settings::instance().evaluationDate() = Date(28, March, 2004);
    Date exerciseDate(28, March, 2005);

    ext::shared_ptr<Exercise> exercise(new EuropeanExercise(exerciseDate));

    ext::shared_ptr<StrikedTypePayoff> payoff(new
                                      PlainVanillaPayoff(Option::Call, 100));

    BarrierOption barrierOption(Barrier::UpOut, 135, 0.0, payoff, exercise);

    barrierOption.setPricingEngine(ext::shared_ptr<PricingEngine>(
            new FdHestonBarrierEngine(ext::make_shared<HestonModel>(
                              hestonProcess), 50, 400, 100)));

    const Real tol = 0.01;
    const Real npvExpected   =  9.1530;
    const Real deltaExpected =  0.5218;
    const Real gammaExpected = -0.0354;

    if (std::fabs(barrierOption.NPV() - npvExpected) > tol) {
        BOOST_ERROR("Failed to reproduce expected npv"
                    << "\n    calculated: " << barrierOption.NPV()
                    << "\n    expected:   " << npvExpected
                    << "\n    tolerance:  " << tol);
    }
    if (std::fabs(barrierOption.delta() - deltaExpected) > tol) {
        BOOST_ERROR("Failed to reproduce expected delta"
                    << "\n    calculated: " << barrierOption.delta()
                    << "\n    expected:   " << deltaExpected
                    << "\n    tolerance:  " << tol);
    }
    if (std::fabs(barrierOption.gamma() - gammaExpected) > tol) {
        BOOST_ERROR("Failed to reproduce expected gamma"
                    << "\n    calculated: " << barrierOption.gamma()
                    << "\n    expected:   " << gammaExpected
                    << "\n    tolerance:  " << tol);
    }
}

void FdHestonTest::testFdmHestonAmerican() {

    BOOST_TEST_MESSAGE("Testing FDM with American option in Heston model...");

    SavedSettings backup;

    Handle<Quote> s0(ext::shared_ptr<Quote>(new SimpleQuote(100.0)));

    Handle<YieldTermStructure> rTS(flatRate(0.05, Actual365Fixed()));
    Handle<YieldTermStructure> qTS(flatRate(0.0 , Actual365Fixed()));

    ext::shared_ptr<HestonProcess> hestonProcess(
        new HestonProcess(rTS, qTS, s0, 0.04, 2.5, 0.04, 0.66, -0.8));

    Settings::instance().evaluationDate() = Date(28, March, 2004);
    Date exerciseDate(28, March, 2005);

    ext::shared_ptr<Exercise> exercise(new AmericanExercise(exerciseDate));

    ext::shared_ptr<StrikedTypePayoff> payoff(new
                                      PlainVanillaPayoff(Option::Put, 100));

    VanillaOption option(payoff, exercise);
    ext::shared_ptr<PricingEngine> engine(
         new FdHestonVanillaEngine(ext::make_shared<HestonModel>(
                             hestonProcess), 200, 100, 50));
    option.setPricingEngine(engine);

    const Real tol = 0.01;
    const Real npvExpected   =  5.66032;
    const Real deltaExpected = -0.30065;
    const Real gammaExpected =  0.02202;

    if (std::fabs(option.NPV() - npvExpected) > tol) {
        BOOST_ERROR("Failed to reproduce expected npv"
                    << "\n    calculated: " << option.NPV()
                    << "\n    expected:   " << npvExpected
                    << "\n    tolerance:  " << tol);
    }
    if (std::fabs(option.delta() - deltaExpected) > tol) {
        BOOST_ERROR("Failed to reproduce expected delta"
                    << "\n    calculated: " << option.delta()
                    << "\n    expected:   " << deltaExpected
                    << "\n    tolerance:  " << tol);
    }
    if (std::fabs(option.gamma() - gammaExpected) > tol) {
        BOOST_ERROR("Failed to reproduce expected gamma"
                    << "\n    calculated: " << option.gamma()
                    << "\n    expected:   " << gammaExpected
                    << "\n    tolerance:  " << tol);
    }
}

void FdHestonTest::testFdmHestonIkonenToivanen() {

    BOOST_TEST_MESSAGE("Testing FDM Heston for Ikonen and Toivanen tests...");

    /* check prices of american puts as given in:
       From Efficient numerical methods for pricing American options under
       stochastic volatility, Samuli Ikonen, Jari Toivanen,
       http://users.jyu.fi/~tene/papers/reportB12-05.pdf
    */
    SavedSettings backup;

    Handle<YieldTermStructure> rTS(flatRate(0.10, Actual360()));
    Handle<YieldTermStructure> qTS(flatRate(0.0 , Actual360()));

    Settings::instance().evaluationDate() = Date(28, March, 2004);
    Date exerciseDate(26, June, 2004);

    ext::shared_ptr<Exercise> exercise(new AmericanExercise(exerciseDate));

    ext::shared_ptr<StrikedTypePayoff> payoff(new
                                      PlainVanillaPayoff(Option::Put, 10));

    VanillaOption option(payoff, exercise);

    Real strikes[]  = { 8, 9, 10, 11, 12 };
    Real expected[] = { 2.00000, 1.10763, 0.520038, 0.213681, 0.082046 };
    const Real tol = 0.001;

    for (Size i=0; i < LENGTH(strikes); ++i) {
        Handle<Quote> s0(ext::shared_ptr<Quote>(new SimpleQuote(strikes[i])));
        ext::shared_ptr<HestonProcess> hestonProcess(
            new HestonProcess(rTS, qTS, s0, 0.0625, 5, 0.16, 0.9, 0.1));

        ext::shared_ptr<PricingEngine> engine(
             new FdHestonVanillaEngine(ext::make_shared<HestonModel>(
                                 hestonProcess), 100, 400));
        option.setPricingEngine(engine);

        Real calculated = option.NPV();
        if (std::fabs(calculated - expected[i]) > tol) {
            BOOST_ERROR("Failed to reproduce expected npv"
                        << "\n    strike:     " << strikes[i]
                        << "\n    calculated: " << calculated
                        << "\n    expected:   " << expected[i]
                        << "\n    tolerance:  " << tol);
        }
    }
}

void FdHestonTest::testFdmHestonBlackScholes() {

    BOOST_TEST_MESSAGE("Testing FDM Heston with Black Scholes model...");

    SavedSettings backup;

    Settings::instance().evaluationDate() = Date(28, March, 2004);
    Date exerciseDate(26, June, 2004);

    Handle<YieldTermStructure> rTS(flatRate(0.10, Actual360()));
    Handle<YieldTermStructure> qTS(flatRate(0.0 , Actual360()));
    Handle<BlackVolTermStructure> volTS(
                    flatVol(rTS->referenceDate(), 0.25, rTS->dayCounter()));

    ext::shared_ptr<Exercise> exercise(new EuropeanExercise(exerciseDate));

    ext::shared_ptr<StrikedTypePayoff> payoff(new
                                      PlainVanillaPayoff(Option::Put, 10));

    VanillaOption option(payoff, exercise);

    Real strikes[]  = { 8, 9, 10, 11, 12 };
    const Real tol = 0.0001;

    for (double& strike : strikes) {
        Handle<Quote> s0(ext::shared_ptr<Quote>(new SimpleQuote(strike)));

        ext::shared_ptr<GeneralizedBlackScholesProcess> bsProcess(
                       new GeneralizedBlackScholesProcess(s0, qTS, rTS, volTS));

        option.setPricingEngine(ext::shared_ptr<PricingEngine>(
                                        new AnalyticEuropeanEngine(bsProcess)));

        const Real expected = option.NPV();

        ext::shared_ptr<HestonProcess> hestonProcess(
            new HestonProcess(rTS, qTS, s0, 0.0625, 1, 0.0625, 0.0001, 0.0));

        // Hundsdorfer scheme
        option.setPricingEngine(ext::shared_ptr<PricingEngine>(
             new FdHestonVanillaEngine(ext::make_shared<HestonModel>(
                                           hestonProcess),
                                       100, 400, 3)));

        Real calculated = option.NPV();
        if (std::fabs(calculated - expected) > tol) {
            BOOST_ERROR("Failed to reproduce expected npv"
                        << "\n    strike:     " << strike << "\n    calculated: " << calculated
                        << "\n    expected:   " << expected << "\n    tolerance:  " << tol);
        }

        // Explicit scheme
        option.setPricingEngine(ext::shared_ptr<PricingEngine>(
             new FdHestonVanillaEngine(ext::make_shared<HestonModel>(
                                           hestonProcess),
                                       4000, 400, 3, 0,
                                       FdmSchemeDesc::ExplicitEuler())));

        calculated = option.NPV();
        if (std::fabs(calculated - expected) > tol) {
            BOOST_ERROR("Failed to reproduce expected npv"
                        << "\n    strike:     " << strike << "\n    calculated: " << calculated
                        << "\n    expected:   " << expected << "\n    tolerance:  " << tol);
        }
    }
}

void FdHestonTest::testFdmHestonEuropeanWithDividends() {

    BOOST_TEST_MESSAGE("Testing FDM with European option with dividends"
                       " in Heston model...");

    SavedSettings backup;

    Handle<Quote> s0(ext::shared_ptr<Quote>(new SimpleQuote(100.0)));

    Handle<YieldTermStructure> rTS(flatRate(0.05, Actual365Fixed()));
    Handle<YieldTermStructure> qTS(flatRate(0.0 , Actual365Fixed()));

    ext::shared_ptr<HestonProcess> hestonProcess(
        new HestonProcess(rTS, qTS, s0, 0.04, 2.5, 0.04, 0.66, -0.8));

    Settings::instance().evaluationDate() = Date(28, March, 2004);
    Date exerciseDate(28, March, 2005);

    ext::shared_ptr<Exercise> exercise(new AmericanExercise(exerciseDate));

    ext::shared_ptr<StrikedTypePayoff> payoff(new
                                      PlainVanillaPayoff(Option::Put, 100));

    const std::vector<Real> dividends(1, 5);
    const std::vector<Date> dividendDates(1, Date(28, September, 2004));

    DividendVanillaOption option(payoff, exercise, dividendDates, dividends);
    ext::shared_ptr<PricingEngine> engine(
         new FdHestonVanillaEngine(ext::make_shared<HestonModel>(
                             hestonProcess), 50, 100, 50));
    option.setPricingEngine(engine);

    const Real tol = 0.01;
    const Real gammaTol = 0.001;
    const Real npvExpected   =  7.365075;
    const Real deltaExpected = -0.396678;
    const Real gammaExpected =  0.027681;

    if (std::fabs(option.NPV() - npvExpected) > tol) {
        BOOST_ERROR("Failed to reproduce expected npv"
                    << "\n    calculated: " << option.NPV()
                    << "\n    expected:   " << npvExpected
                    << "\n    tolerance:  " << tol);
    }
    if (std::fabs(option.delta() - deltaExpected) > tol) {
        BOOST_ERROR("Failed to reproduce expected delta"
                    << "\n    calculated: " << option.delta()
                    << "\n    expected:   " << deltaExpected
                    << "\n    tolerance:  " << tol);
    }
    if (std::fabs(option.gamma() - gammaExpected) > gammaTol) {
        BOOST_ERROR("Failed to reproduce expected gamma"
                    << "\n    calculated: " << option.gamma()
                    << "\n    expected:   " << gammaExpected
                    << "\n    tolerance:  " << tol);
    }
}

namespace {
    struct HestonTestData {
        Real kappa;
        Real theta;
        Real sigma;
        Real rho;
        Real r;
        Real q;
        Real T;
        Real K;
    };
}

void FdHestonTest::testFdmHestonConvergence() {

    /* convergence tests based on
       ADI finite difference schemes for option pricing in the
       Heston model with correlation, K.J. in t'Hout and S. Foulon
    */

    BOOST_TEST_MESSAGE("Testing FDM Heston convergence...");

    SavedSettings backup;

    HestonTestData values[] = {
        { 1.5   , 0.04  , 0.3   , -0.9   , 0.025 , 0.0   , 1.0 , 100 },
        { 3.0   , 0.12  , 0.04  , 0.6    , 0.01  , 0.04  , 1.0 , 100 },
        { 0.6067, 0.0707, 0.2928, -0.7571, 0.03  , 0.0   , 3.0 , 100 },
        { 2.5   , 0.06  , 0.5   , -0.1   , 0.0507, 0.0469, 0.25, 100 }
    };

    FdmSchemeDesc schemes[] = {
        FdmSchemeDesc::Hundsdorfer(),
        FdmSchemeDesc::ModifiedCraigSneyd(),
        FdmSchemeDesc::ModifiedHundsdorfer(),
        FdmSchemeDesc::CraigSneyd(),
        FdmSchemeDesc::TrBDF2(),
        FdmSchemeDesc::CrankNicolson(),
    };

    Size tn[] = { 60 };
    Real v0[] = { 0.04 };

    const Date todaysDate(28, March, 2004);
    Settings::instance().evaluationDate() = todaysDate;

    Handle<Quote> s0(ext::shared_ptr<Quote>(new SimpleQuote(75.0)));

    for (const auto& scheme : schemes) {
        for (auto& value : values) {
            for (unsigned long j : tn) {
                for (double k : v0) {
                    Handle<YieldTermStructure> rTS(flatRate(value.r, Actual365Fixed()));
                    Handle<YieldTermStructure> qTS(flatRate(value.q, Actual365Fixed()));

                    ext::shared_ptr<HestonProcess> hestonProcess(new HestonProcess(
                        rTS, qTS, s0, k, value.kappa, value.theta, value.sigma, value.rho));

                    Date exerciseDate =
                        todaysDate + Period(static_cast<Integer>(value.T * 365), Days);
                    ext::shared_ptr<Exercise> exercise(
                                          new EuropeanExercise(exerciseDate));

                    ext::shared_ptr<StrikedTypePayoff> payoff(
                        new PlainVanillaPayoff(Option::Call, value.K));

                    VanillaOption option(payoff, exercise);
                    ext::shared_ptr<PricingEngine> engine(new FdHestonVanillaEngine(
                        ext::make_shared<HestonModel>(hestonProcess), j, 101, 51, 0, scheme));
                    option.setPricingEngine(engine);

                    const Real calculated = option.NPV();

                    ext::shared_ptr<PricingEngine> analyticEngine(
                        new AnalyticHestonEngine(
                            ext::make_shared<HestonModel>(
                                hestonProcess), 144));

                    option.setPricingEngine(analyticEngine);
                    const Real expected = option.NPV();
                    if (   std::fabs(expected - calculated)/expected > 0.02
                        && std::fabs(expected - calculated) > 0.002) {
                        BOOST_ERROR("Failed to reproduce expected npv"
                                    << "\n    calculated: " << calculated
                                    << "\n    expected:   " << expected
                                    << "\n    tolerance:  " << 0.01);
                    }
                }
            }
        }
    }
}

void FdHestonTest::testFdmHestonIntradayPricing() {
#ifdef QL_HIGH_RESOLUTION_DATE

    BOOST_TEST_MESSAGE("Testing FDM Heston intraday pricing ...");

    SavedSettings backup;

    const Option::Type type(Option::Put);
    const Real underlying = 36;
    const Real strike = underlying;
    const Spread dividendYield = 0.00;
    const Rate riskFreeRate = 0.06;
    const Real v0    = 0.2;
    const Real kappa = 1.0;
    const Real theta = v0;
    const Real sigma = 0.0065;
    const Real rho   = -0.75;
    const DayCounter dayCounter = Actual365Fixed();

    const Date maturity(17, May, 2014, 17, 30, 0);

    const ext::shared_ptr<Exercise> europeanExercise(
        new EuropeanExercise(maturity));
    const ext::shared_ptr<StrikedTypePayoff> payoff(
        new PlainVanillaPayoff(type, strike));
    VanillaOption option(payoff, europeanExercise);

    const Handle<Quote> s0(
         ext::shared_ptr<Quote>(new SimpleQuote(underlying)));
    RelinkableHandle<BlackVolTermStructure> flatVolTS;
    RelinkableHandle<YieldTermStructure> flatTermStructure, flatDividendTS;
    const ext::shared_ptr<HestonProcess> process(
        new HestonProcess(flatTermStructure, flatDividendTS, s0,
              v0, kappa, theta, sigma, rho));
    const ext::shared_ptr<HestonModel> model(new HestonModel(process));
    const ext::shared_ptr<PricingEngine> fdm(
        new FdHestonVanillaEngine(model, 20, 100, 26, 0));
    option.setPricingEngine(fdm);

    const Real gammaExpected[] = {
        1.46757, 1.54696, 1.6408, 1.75409, 1.89464,
        2.07548, 2.32046, 2.67944, 3.28164, 4.64096  };

    for (Size i = 0; i < 10; ++i) {
        const Date now(17, May, 2014, 15, i*15, 0);
        Settings::instance().evaluationDate() = now;

        flatTermStructure.linkTo(ext::shared_ptr<YieldTermStructure>(
            new FlatForward(now, riskFreeRate, dayCounter)));
        flatDividendTS.linkTo(ext::shared_ptr<YieldTermStructure>(
            new FlatForward(now, dividendYield, dayCounter)));

        const Real gammaCalculated = option.gamma();
        if (std::fabs(gammaCalculated - gammaExpected[i]) > 1e-4) {
            BOOST_ERROR("unable to reproduce intraday gamma values at time "
                        << "\n   timestamp : " << io::iso_datetime(now)
                        << "\n   expiry    : " << io::iso_datetime(maturity)
                        << "\n   expected  : " << gammaExpected[i]
                        << "\n   calculated: "<<  gammaCalculated);
        }
    }
#endif
}

void FdHestonTest::testMethodOfLinesAndCN() {
    BOOST_TEST_MESSAGE("Testing method of lines to solve Heston PDEs...");

    SavedSettings backup;

    const DayCounter dc = Actual365Fixed();
    const Date today = Date(21, February, 2018);

    Settings::instance().evaluationDate() = today;

    const Handle<Quote> spot(ext::make_shared<SimpleQuote>(100.0));
    const Handle<YieldTermStructure> qTS(flatRate(today, 0.0, dc));
    const Handle<YieldTermStructure> rTS(flatRate(today, 0.0, dc));

    const Real v0    = 0.09;
    const Real kappa = 1.0;
    const Real theta = v0;
    const Real sigma = 0.4;
    const Real rho   = -0.75;

    const Date maturity = today + Period(3, Months);

    const ext::shared_ptr<HestonModel> model(
        ext::make_shared<HestonModel>(
            ext::make_shared<HestonProcess>(
                rTS, qTS, spot, v0, kappa, theta, sigma, rho)));

    const Size xGrid = 21;
    const Size vGrid = 7;

    const ext::shared_ptr<PricingEngine> fdmDefault(
        ext::make_shared<FdHestonVanillaEngine>(model, 10, xGrid, vGrid, 0));

    const ext::shared_ptr<PricingEngine> fdmMol(
        ext::make_shared<FdHestonVanillaEngine>(
            model, 10, xGrid, vGrid, 0, FdmSchemeDesc::MethodOfLines()));

    const ext::shared_ptr<PlainVanillaPayoff> payoff =
        ext::make_shared<PlainVanillaPayoff>(Option::Put, spot->value());

    VanillaOption option(
        payoff, ext::make_shared<AmericanExercise>(maturity));

    option.setPricingEngine(fdmMol);
    const Real calculatedMoL = option.NPV();

    option.setPricingEngine(fdmDefault);
    const Real expected = option.NPV();

    const Real tol = 0.005;
    const Real diffMoL = std::fabs(expected - calculatedMoL);

    if (diffMoL > tol) {
        BOOST_FAIL("Failed to reproduce european option values with MOL"
                   << "\n    calculated: " << calculatedMoL
                   << "\n    expected:   " << expected
                   << "\n    difference: " << diffMoL
                   << "\n    tolerance:  " << tol);
    }

    const ext::shared_ptr<PricingEngine> fdmCN(
        ext::make_shared<FdHestonVanillaEngine>(
            model, 10, xGrid, vGrid, 0, FdmSchemeDesc::CrankNicolson()));
    option.setPricingEngine(fdmCN);

    const Real calculatedCN = option.NPV();
    const Real diffCN = std::fabs(expected - calculatedCN);

    if (diffCN > tol) {
        BOOST_FAIL("Failed to reproduce european option values with Crank-Nicolson"
                   << "\n    calculated: " << calculatedCN
                   << "\n    expected:   " << expected
                   << "\n    difference: " << diffCN
                   << "\n    tolerance:  " << tol);
    }

    BarrierOption barrierOption(
        Barrier::DownOut, 85.0, 10.0,
        payoff, ext::make_shared<EuropeanExercise>(maturity));

    barrierOption.setPricingEngine(
        ext::make_shared<FdHestonBarrierEngine>(model, 100, 31, 11));

    const Real expectedBarrier = barrierOption.NPV();

    barrierOption.setPricingEngine(
        ext::make_shared<FdHestonBarrierEngine>(model, 100, 31, 11, 0,
            FdmSchemeDesc::MethodOfLines()));

    const Real calculatedBarrierMoL = barrierOption.NPV();

    const Real barrierTol = 0.01;
    const Real barrierDiffMoL = std::fabs(expectedBarrier - calculatedBarrierMoL);

    if (barrierDiffMoL > barrierTol) {
        BOOST_FAIL("Failed to reproduce barrier option values with MOL"
                   << "\n    calculated: " << calculatedBarrierMoL
                   << "\n    expected:   " << expectedBarrier
                   << "\n    difference: " << barrierDiffMoL
                   << "\n    tolerance:  " << barrierTol);
    }

    barrierOption.setPricingEngine(
        ext::make_shared<FdHestonBarrierEngine>(model, 100, 31, 11, 0,
            FdmSchemeDesc::CrankNicolson()));

    const Real calculatedBarrierCN = barrierOption.NPV();
    const Real barrierDiffCN = std::fabs(expectedBarrier - calculatedBarrierCN);

    if (barrierDiffCN > barrierTol) {
        BOOST_FAIL("Failed to reproduce barrier option values with Crank-Nicolson"
                   << "\n    calculated: " << calculatedBarrierCN
                   << "\n    expected:   " << expectedBarrier
                   << "\n    difference: " << barrierDiffCN
                   << "\n    tolerance:  " << barrierTol);
    }
}

void FdHestonTest::testSpuriousOscillations() {
    BOOST_TEST_MESSAGE("Testing for spurious oscillations when "
            "solving the Heston PDEs...");

    SavedSettings backup;

    const DayCounter dc = Actual365Fixed();
    const Date today = Date(7, June, 2018);

    Settings::instance().evaluationDate() = today;

    const Handle<Quote> spot(ext::make_shared<SimpleQuote>(100.0));
    const Handle<YieldTermStructure> qTS(flatRate(today, 0.1, dc));
    const Handle<YieldTermStructure> rTS(flatRate(today, 0.0, dc));

    const Real v0    = 0.005;
    const Real kappa = 1.0;
    const Real theta = 0.005;
    const Real sigma = 0.4;
    const Real rho   = -0.75;

    const Date maturity = today + Period(1, Years);

    const ext::shared_ptr<HestonProcess> process =
        ext::make_shared<HestonProcess>(
            rTS, qTS, spot, v0, kappa, theta, sigma, rho);

    const ext::shared_ptr<HestonModel> model =
        ext::make_shared<HestonModel>(process);

    const ext::shared_ptr<FdHestonVanillaEngine> hestonEngine(
        ext::make_shared<FdHestonVanillaEngine>(
            model, 6, 200, 13, 0, FdmSchemeDesc::TrBDF2()));

    VanillaOption option(
        ext::make_shared<PlainVanillaPayoff>(Option::Call, spot->value()),
        ext::make_shared<EuropeanExercise>(maturity));

    option.setupArguments(hestonEngine->getArguments());

    const ext::tuple<FdmSchemeDesc, std::string, bool> descs[] = {
        ext::make_tuple(FdmSchemeDesc::CraigSneyd(), "Craig-Sneyd", true),
        ext::make_tuple(FdmSchemeDesc::Hundsdorfer(), "Hundsdorfer", true),
        ext::make_tuple(
           FdmSchemeDesc::ModifiedHundsdorfer(), "Mod. Hundsdorfer", true),
        ext::make_tuple(FdmSchemeDesc::Douglas(), "Douglas", true),
        ext::make_tuple(FdmSchemeDesc::CrankNicolson(), "Crank-Nicolson", true),
        ext::make_tuple(FdmSchemeDesc::ImplicitEuler(), "Implicit", false),
        ext::make_tuple(FdmSchemeDesc::TrBDF2(), "TR-BDF2", false)
    };

    for (const auto& desc : descs) {
        const ext::shared_ptr<FdmHestonSolver> solver = ext::make_shared<FdmHestonSolver>(
            Handle<HestonProcess>(process), hestonEngine->getSolverDesc(1.0), ext::get<0>(desc));

        std::vector<Real> gammas;
        for (Real x=99; x < 101.001; x+=0.1) {
            gammas.push_back(solver->gammaAt(x, v0));
        }

        Real maximum = QL_MIN_REAL;
        for (Size i=1; i < gammas.size(); ++i) {
            const Real diff = std::fabs(gammas[i] - gammas[i-1]);
            if (diff > maximum)
                maximum = diff;
        }

        const Real tol = 0.01;
        const bool hasSpuriousOscillations = maximum > tol;

        if (hasSpuriousOscillations != ext::get<2>(desc)) {
            BOOST_ERROR("unable to reproduce spurious oscillation behaviour "
                        << "\n   scheme name          : " << ext::get<1>(desc)
                        << "\n   oscillations observed: " << hasSpuriousOscillations
                        << "\n   oscillations expected: " << ext::get<2>(desc));
        }
    }
}

test_suite* FdHestonTest::suite(SpeedLevel speed) {
    auto* suite = BOOST_TEST_SUITE("Finite Difference Heston tests");

    suite->add(QUANTLIB_TEST_CASE(&FdHestonTest::testFdmHestonVarianceMesher));
    suite->add(QUANTLIB_TEST_CASE(&FdHestonTest::testFdmHestonBarrier));
    suite->add(QUANTLIB_TEST_CASE(&FdHestonTest::testFdmHestonAmerican));
    suite->add(QUANTLIB_TEST_CASE(&FdHestonTest::testFdmHestonIkonenToivanen));
    suite->add(QUANTLIB_TEST_CASE(
        &FdHestonTest::testFdmHestonEuropeanWithDividends));
    suite->add(QUANTLIB_TEST_CASE(
        &FdHestonTest::testFdmHestonIntradayPricing));
    suite->add(QUANTLIB_TEST_CASE(&FdHestonTest::testMethodOfLinesAndCN));
    suite->add(QUANTLIB_TEST_CASE(&FdHestonTest::testSpuriousOscillations));

    if (speed <= Fast) {
        suite->add(QUANTLIB_TEST_CASE(
            &FdHestonTest::testFdmHestonBlackScholes));
        suite->add(QUANTLIB_TEST_CASE(
            &FdHestonTest::testFdmHestonConvergence));
    }

    if (speed == Slow) {
        suite->add(QUANTLIB_TEST_CASE(
            &FdHestonTest::testFdmHestonBarrierVsBlackScholes));
    }

    return suite;
}


/**
 * \ file OneMinusFilter.cpp
 */

#include <ATK/Tools/OneMinusFilter.h>

#include <ATK/Core/InPointerFilter.h>
#include <ATK/Core/OutPointerFilter.h>

#include <gtest/gtest.h>

#include <boost/math/constants/constants.hpp>

constexpr gsl::index PROCESSSIZE = 1024;

TEST(OneMinusFilter, sinus_test)
{
  std::array<double, PROCESSSIZE> data;
  for(gsl::index i = 0; i < PROCESSSIZE; ++i)
  {
    data[i] = std::sin(2 * boost::math::constants::pi<double>() * (i+1.)/48000 * 1000);
  }

  ATK::InPointerFilter<double> generator(data.data(), 1, PROCESSSIZE, false);
  generator.set_output_sampling_rate(48000);

  std::array<double, PROCESSSIZE> outdata;

  ATK::OneMinusFilter<double> filter(1);
  filter.set_input_sampling_rate(48000);
  filter.set_input_port(0, &generator, 0);

  ATK::OutPointerFilter<double> output(outdata.data(), 1, PROCESSSIZE, false);
  output.set_input_sampling_rate(48000);
  output.set_input_port(0, &filter, 0);

  output.process(PROCESSSIZE);

  for(gsl::index i = 0; i < PROCESSSIZE; ++i)
  {
    ASSERT_NEAR(data[i] + outdata[i], 1., 0.0001);
  }
}