// boost\math\distributions\non_central_chi_squared.hpp

// Copyright John Maddock 2008.

// 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)

#ifndef BOOST_MATH_SPECIAL_NON_CENTRAL_CHI_SQUARE_HPP
#define BOOST_MATH_SPECIAL_NON_CENTRAL_CHI_SQUARE_HPP

#include <boost/math/distributions/fwd.hpp>
#include <boost/math/special_functions/gamma.hpp> // for incomplete gamma. gamma_q
#include <boost/math/special_functions/bessel.hpp> // for cyl_bessel_i
#include <boost/math/special_functions/round.hpp> // for iround
#include <boost/math/distributions/complement.hpp> // complements
#include <boost/math/distributions/chi_squared.hpp> // central distribution
#include <boost/math/distributions/detail/common_error_handling.hpp> // error checks
#include <boost/math/special_functions/fpclassify.hpp> // isnan.
#include <boost/math/tools/roots.hpp> // for root finding.
#include <boost/math/distributions/detail/generic_mode.hpp>
#include <boost/math/distributions/detail/generic_quantile.hpp>

namespace boost
{
   namespace math
   {

      template <class RealType, class Policy>
      class non_central_chi_squared_distribution;

      namespace detail{

         template <class T, class Policy>
         T non_central_chi_square_q(T x, T f, T theta, const Policy& pol, T init_sum = 0)
         {
            //
            // Computes the complement of the Non-Central Chi-Square
            // Distribution CDF by summing a weighted sum of complements
            // of the central-distributions.  The weighting factor is
            // a Poisson Distribution.
            //
            // This is an application of the technique described in:
            //
            // Computing discrete mixtures of continuous
            // distributions: noncentral chisquare, noncentral t
            // and the distribution of the square of the sample
            // multiple correlation coeficient.
            // D. Benton, K. Krishnamoorthy.
            // Computational Statistics & Data Analysis 43 (2003) 249 - 267
            //
            BOOST_MATH_STD_USING

            // Special case:
            if(x == 0)
               return 1;

            //
            // Initialize the variables we'll be using:
            //
            T lambda = theta / 2;
            T del = f / 2;
            T y = x / 2;
            boost::uintmax_t max_iter = policies::get_max_series_iterations<Policy>();
            T errtol = boost::math::policies::get_epsilon<T, Policy>();
            T sum = init_sum;
            //
            // k is the starting location for iteration, we'll
            // move both forwards and backwards from this point.
            // k is chosen as the peek of the Poisson weights, which
            // will occur *before* the largest term.
            //
            int k = iround(lambda, pol);
            // Forwards and backwards Poisson weights:
            T poisf = boost::math::gamma_p_derivative(1 + k, lambda, pol);
            T poisb = poisf * k / lambda;
            // Initial forwards central chi squared term:
            T gamf = boost::math::gamma_q(del + k, y, pol);
            // Forwards and backwards recursion terms on the central chi squared:
            T xtermf = boost::math::gamma_p_derivative(del + 1 + k, y, pol);
            T xtermb = xtermf * (del + k) / y;
            // Initial backwards central chi squared term:
            T gamb = gamf - xtermb;

            //
            // Forwards iteration first, this is the
            // stable direction for the gamma function
            // recurrences:
            //
            int i;
            for(i = k; static_cast<boost::uintmax_t>(i-k) < max_iter; ++i)
            {
               T term = poisf * gamf;
               sum += term;
               poisf *= lambda / (i + 1);
               gamf += xtermf;
               xtermf *= y / (del + i + 1);
               if(((sum == 0) || (fabs(term / sum) < errtol)) && (term >= poisf * gamf))
                  break;
            }
            //Error check:
            if(static_cast<boost::uintmax_t>(i-k) >= max_iter)
               policies::raise_evaluation_error(
                  "cdf(non_central_chi_squared_distribution<%1%>, %1%)",
                  "Series did not converge, closest value was %1%", sum, pol);
            //
            // Now backwards iteration: the gamma
            // function recurrences are unstable in this
            // direction, we rely on the terms deminishing in size
            // faster than we introduce cancellation errors.
            // For this reason it's very important that we start
            // *before* the largest term so that backwards iteration
            // is strictly converging.
            //
            for(i = k - 1; i >= 0; --i)
            {
               T term = poisb * gamb;
               sum += term;
               poisb *= i / lambda;
               xtermb *= (del + i) / y;
               gamb -= xtermb;
               if((sum == 0) || (fabs(term / sum) < errtol))
                  break;
            }

            return sum;
         }

         template <class T, class Policy>
         T non_central_chi_square_p_ding(T x, T f, T theta, const Policy& pol, T init_sum = 0)
         {
            //
            // This is an implementation of:
            //
            // Algorithm AS 275:
            // Computing the Non-Central #2 Distribution Function
            // Cherng G. Ding
            // Applied Statistics, Vol. 41, No. 2. (1992), pp. 478-482.
            //
            // This uses a stable forward iteration to sum the
            // CDF, unfortunately this can not be used for large
            // values of the non-centrality parameter because:
            // * The first term may underfow to zero.
            // * We may need an extra-ordinary number of terms
            //   before we reach the first *significant* term.
            //
            BOOST_MATH_STD_USING
            // Special case:
            if(x == 0)
               return 0;
            T tk = boost::math::gamma_p_derivative(f/2 + 1, x/2, pol);
            T lambda = theta / 2;
            T vk = exp(-lambda);
            T uk = vk;
            T sum = init_sum + tk * vk;
            if(sum == 0)
               return sum;

            boost::uintmax_t max_iter = policies::get_max_series_iterations<Policy>();
            T errtol = boost::math::policies::get_epsilon<T, Policy>();

            int i;
            T lterm(0), term(0);
            for(i = 1; static_cast<boost::uintmax_t>(i) < max_iter; ++i)
            {
               tk = tk * x / (f + 2 * i);
               uk = uk * lambda / i;
               vk = vk + uk;
               lterm = term;
               term = vk * tk;
               sum += term;
               if((fabs(term / sum) < errtol) && (term <= lterm))
                  break;
            }
            //Error check:
            if(static_cast<boost::uintmax_t>(i) >= max_iter)
               policies::raise_evaluation_error(
                  "cdf(non_central_chi_squared_distribution<%1%>, %1%)",
                  "Series did not converge, closest value was %1%", sum, pol);
            return sum;
         }

         template <class T, class Policy>
         T non_central_chi_square_p(T y, T n, T lambda, const Policy& pol, T init_sum)
         {
            //
            // This is taken more or less directly from:
            //
            // Computing discrete mixtures of continuous
            // distributions: noncentral chisquare, noncentral t
            // and the distribution of the square of the sample
            // multiple correlation coeficient.
            // D. Benton, K. Krishnamoorthy.
            // Computational Statistics & Data Analysis 43 (2003) 249 - 267
            //
            // We're summing a Poisson weighting term multiplied by
            // a central chi squared distribution.
            //
            BOOST_MATH_STD_USING
            // Special case:
            if(y == 0)
               return 0;
            boost::uintmax_t max_iter = policies::get_max_series_iterations<Policy>();
            T errtol = boost::math::policies::get_epsilon<T, Policy>();
            T errorf(0), errorb(0);

            T x = y / 2;
            T del = lambda / 2;
            //
            // Starting location for the iteration, we'll iterate
            // both forwards and backwards from this point.  The
            // location chosen is the maximum of the Poisson weight
            // function, which ocurrs *after* the largest term in the
            // sum.
            //
            int k = iround(del, pol);
            T a = n / 2 + k;
            // Central chi squared term for forward iteration:
            T gamkf = boost::math::gamma_p(a, x, pol);

            if(lambda == 0)
               return gamkf;
            // Central chi squared term for backward iteration:
            T gamkb = gamkf;
            // Forwards Poisson weight:
            T poiskf = gamma_p_derivative(k+1, del, pol);
            // Backwards Poisson weight:
            T poiskb = poiskf;
            // Forwards gamma function recursion term:
            T xtermf = boost::math::gamma_p_derivative(a, x, pol);
            // Backwards gamma function recursion term:
            T xtermb = xtermf * x / a;
            T sum = init_sum + poiskf * gamkf;
            if(sum == 0)
               return sum;
            int i = 1;
            //
            // Backwards recursion first, this is the stable
            // direction for gamma function recurrences:
            //
            while(i <= k)
            {
               xtermb *= (a - i + 1) / x;
               gamkb += xtermb;
               poiskb = poiskb * (k - i + 1) / del;
               errorf = errorb;
               errorb = gamkb * poiskb;
               sum += errorb;
               if((fabs(errorb / sum) < errtol) && (errorb <= errorf))
                  break;
               ++i;
            }
            i = 1;
            //
            // Now forwards recursion, the gamma function
            // recurrence relation is unstable in this direction,
            // so we rely on the magnitude of successive terms
            // decreasing faster than we introduce cancellation error.
            // For this reason it's vital that k is chosen to be *after*
            // the largest term, so that successive forward iterations
            // are strictly (and rapidly) converging.
            //
            do
            {
               xtermf = xtermf * x / (a + i - 1);
               gamkf = gamkf - xtermf;
               poiskf = poiskf * del / (k + i);
               errorf = poiskf * gamkf;
               sum += errorf;
               ++i;
            }while((fabs(errorf / sum) > errtol) && (static_cast<boost::uintmax_t>(i) < max_iter));

            //Error check:
            if(static_cast<boost::uintmax_t>(i) >= max_iter)
               policies::raise_evaluation_error(
                  "cdf(non_central_chi_squared_distribution<%1%>, %1%)",
                  "Series did not converge, closest value was %1%", sum, pol);

            return sum;
         }

         template <class T, class Policy>
         T non_central_chi_square_pdf(T x, T n, T lambda, const Policy& pol)
         {
            //
            // As above but for the PDF:
            //
            BOOST_MATH_STD_USING
            boost::uintmax_t max_iter = policies::get_max_series_iterations<Policy>();
            T errtol = boost::math::policies::get_epsilon<T, Policy>();
            T x2 = x / 2;
            T n2 = n / 2;
            T l2 = lambda / 2;
            T sum = 0;
            int k = itrunc(l2);
            T pois = gamma_p_derivative(k + 1, l2, pol) * gamma_p_derivative(n2 + k, x2);
            if(pois == 0)
               return 0;
            T poisb = pois;
            for(int i = k; ; ++i)
            {
               sum += pois;
               if(pois / sum < errtol)
                  break;
               if(static_cast<boost::uintmax_t>(i - k) >= max_iter)
                  return policies::raise_evaluation_error(
                     "pdf(non_central_chi_squared_distribution<%1%>, %1%)",
                     "Series did not converge, closest value was %1%", sum, pol);
               pois *= l2 * x2 / ((i + 1) * (n2 + i));
            }
            for(int i = k - 1; i >= 0; --i)
            {
               poisb *= (i + 1) * (n2 + i) / (l2 * x2);
               sum += poisb;
               if(poisb / sum < errtol)
                  break;
            }
            return sum / 2;
         }

         template <class RealType, class Policy>
         inline RealType non_central_chi_squared_cdf(RealType x, RealType k, RealType l, bool invert, const Policy&)
         {
            typedef typename policies::evaluation<RealType, Policy>::type value_type;
            typedef typename policies::normalise<
               Policy,
               policies::promote_float<false>,
               policies::promote_double<false>,
               policies::discrete_quantile<>,
               policies::assert_undefined<> >::type forwarding_policy;

            BOOST_MATH_STD_USING
            value_type result;
            if(l == 0)
               result = cdf(boost::math::chi_squared_distribution<RealType, Policy>(k), x);
            else if(x > k + l)
            {
               // Complement is the smaller of the two:
               result = detail::non_central_chi_square_q(
                  static_cast<value_type>(x),
                  static_cast<value_type>(k),
                  static_cast<value_type>(l),
                  forwarding_policy(),
                  static_cast<value_type>(invert ? 0 : -1));
               invert = !invert;
            }
            else if(l < 200)
            {
               // For small values of the non-centrality parameter
               // we can use Ding's method:
               result = detail::non_central_chi_square_p_ding(
                  static_cast<value_type>(x),
                  static_cast<value_type>(k),
                  static_cast<value_type>(l),
                  forwarding_policy(),
                  static_cast<value_type>(invert ? -1 : 0));
            }
            else
            {
               // For largers values of the non-centrality
               // parameter Ding's method will consume an
               // extra-ordinary number of terms, and worse
               // may return zero when the result is in fact
               // finite, use Krishnamoorthy's method instead:
               result = detail::non_central_chi_square_p(
                  static_cast<value_type>(x),
                  static_cast<value_type>(k),
                  static_cast<value_type>(l),
                  forwarding_policy(),
                  static_cast<value_type>(invert ? -1 : 0));
            }
            if(invert)
               result = -result;
            return policies::checked_narrowing_cast<RealType, forwarding_policy>(
               result,
               "boost::math::non_central_chi_squared_cdf<%1%>(%1%, %1%, %1%)");
         }

         template <class T, class Policy>
         struct nccs_quantile_functor
         {
            nccs_quantile_functor(const non_central_chi_squared_distribution<T,Policy>& d, T t, bool c)
               : dist(d), target(t), comp(c) {}

            T operator()(const T& x)
            {
               return comp ?
                  target - cdf(complement(dist, x))
                  : cdf(dist, x) - target;
            }

         private:
            non_central_chi_squared_distribution<T,Policy> dist;
            T target;
            bool comp;
         };

         template <class RealType, class Policy>
         RealType nccs_quantile(const non_central_chi_squared_distribution<RealType, Policy>& dist, const RealType& p, bool comp)
         {
            static const char* function = "quantile(non_central_chi_squared_distribution<%1%>, %1%)";
            typedef typename policies::evaluation<RealType, Policy>::type value_type;
            typedef typename policies::normalise<
               Policy,
               policies::promote_float<false>,
               policies::promote_double<false>,
               policies::discrete_quantile<>,
               policies::assert_undefined<> >::type forwarding_policy;

            value_type k = dist.degrees_of_freedom();
            value_type l = dist.non_centrality();
            value_type r;
            if(!detail::check_df(
               function,
               k, &r, Policy())
               ||
            !detail::check_non_centrality(
               function,
               l,
               &r,
               Policy())
               ||
            !detail::check_probability(
               function,
               static_cast<value_type>(p),
               &r,
               Policy()))
                  return (RealType)r;

            value_type b = (l * l) / (k + 3 * l);
            value_type c = (k + 3 * l) / (k + 2 * l);
            value_type ff = (k + 2 * l) / (c * c);
            value_type guess;
            if(comp)
               guess = b + c * quantile(complement(chi_squared_distribution<value_type, forwarding_policy>(ff), p));
            else
               guess = b + c * quantile(chi_squared_distribution<value_type, forwarding_policy>(ff), p);

            if(guess < 0)
               guess = tools::min_value<value_type>();

            value_type result = detail::generic_quantile(
               non_central_chi_squared_distribution<value_type, forwarding_policy>(k, l),
               p,
               guess,
               comp,
               function);
            return policies::checked_narrowing_cast<RealType, forwarding_policy>(
               result,
               function);
         }

         template <class RealType, class Policy>
         RealType nccs_pdf(const non_central_chi_squared_distribution<RealType, Policy>& dist, const RealType& x)
         {
            BOOST_MATH_STD_USING
            static const char* function = "pdf(non_central_chi_squared_distribution<%1%>, %1%)";
            typedef typename policies::evaluation<RealType, Policy>::type value_type;
            typedef typename policies::normalise<
               Policy,
               policies::promote_float<false>,
               policies::promote_double<false>,
               policies::discrete_quantile<>,
               policies::assert_undefined<> >::type forwarding_policy;

            value_type k = dist.degrees_of_freedom();
            value_type l = dist.non_centrality();
            value_type r;
            if(!detail::check_df(
               function,
               k, &r, Policy())
               ||
            !detail::check_non_centrality(
               function,
               l,
               &r,
               Policy())
               ||
            !detail::check_positive_x(
               function,
               (value_type)x,
               &r,
               Policy()))
                  return (RealType)r;

         if(l == 0)
            return pdf(boost::math::chi_squared_distribution<RealType, forwarding_policy>(dist.degrees_of_freedom()), x);

         // Special case:
         if(x == 0)
            return 0;
         if(l > 50)
         {
            r = non_central_chi_square_pdf(static_cast<value_type>(x), k, l, forwarding_policy());
         }
         else
         {
            r = log(x / l) * (k / 4 - 0.5f) - (x + l) / 2;
            if(fabs(r) >= tools::log_max_value<RealType>() / 4)
            {
               r = non_central_chi_square_pdf(static_cast<value_type>(x), k, l, forwarding_policy());
            }
            else
            {
               r = exp(r);
               r = 0.5f * r
                  * boost::math::cyl_bessel_i(k/2 - 1, sqrt(l * x), forwarding_policy());
            }
         }
         return policies::checked_narrowing_cast<RealType, forwarding_policy>(
               r,
               function);
         }

         template <class RealType, class Policy>
         struct degrees_of_freedom_finder
         {
            degrees_of_freedom_finder(
               RealType lam_, RealType x_, RealType p_, bool c)
               : lam(lam_), x(x_), p(p_), comp(c) {}

            RealType operator()(const RealType& v)
            {
               non_central_chi_squared_distribution<RealType, Policy> d(v, lam);
               return comp ?
                  RealType(p - cdf(complement(d, x)))
                  : RealType(cdf(d, x) - p);
            }
         private:
            RealType lam;
            RealType x;
            RealType p;
            bool comp;
         };

         template <class RealType, class Policy>
         inline RealType find_degrees_of_freedom(
            RealType lam, RealType x, RealType p, RealType q, const Policy& pol)
         {
            const char* function = "non_central_chi_squared<%1%>::find_degrees_of_freedom";
            if((p == 0) || (q == 0))
            {
               //
               // Can't a thing if one of p and q is zero:
               //
               return policies::raise_evaluation_error<RealType>(function,
                  "Can't find degrees of freedom when the probability is 0 or 1, only possible answer is %1%",
                  RealType(std::numeric_limits<RealType>::quiet_NaN()), Policy());
            }
            degrees_of_freedom_finder<RealType, Policy> f(lam, x, p < q ? p : q, p < q ? false : true);
            tools::eps_tolerance<RealType> tol(policies::digits<RealType, Policy>());
            boost::uintmax_t max_iter = policies::get_max_root_iterations<Policy>();
            //
            // Pick an initial guess that we know will give us a probability
            // right around 0.5.
            //
            RealType guess = x - lam;
            if(guess < 1)
               guess = 1;
            std::pair<RealType, RealType> ir = tools::bracket_and_solve_root(
               f, guess, RealType(2), false, tol, max_iter, pol);
            RealType result = ir.first + (ir.second - ir.first) / 2;
            if(max_iter >= policies::get_max_root_iterations<Policy>())
            {
               policies::raise_evaluation_error<RealType>(function, "Unable to locate solution in a reasonable time:"
                  " or there is no answer to problem.  Current best guess is %1%", result, Policy());
            }
            return result;
         }

         template <class RealType, class Policy>
         struct non_centrality_finder
         {
            non_centrality_finder(
               RealType v_, RealType x_, RealType p_, bool c)
               : v(v_), x(x_), p(p_), comp(c) {}

            RealType operator()(const RealType& lam)
            {
               non_central_chi_squared_distribution<RealType, Policy> d(v, lam);
               return comp ?
                  RealType(p - cdf(complement(d, x)))
                  : RealType(cdf(d, x) - p);
            }
         private:
            RealType v;
            RealType x;
            RealType p;
            bool comp;
         };

         template <class RealType, class Policy>
         inline RealType find_non_centrality(
            RealType v, RealType x, RealType p, RealType q, const Policy& pol)
         {
            const char* function = "non_central_chi_squared<%1%>::find_non_centrality";
            if((p == 0) || (q == 0))
            {
               //
               // Can't do a thing if one of p and q is zero:
               //
               return policies::raise_evaluation_error<RealType>(function,
                  "Can't find non centrality parameter when the probability is 0 or 1, only possible answer is %1%",
                  RealType(std::numeric_limits<RealType>::quiet_NaN()), Policy());
            }
            non_centrality_finder<RealType, Policy> f(v, x, p < q ? p : q, p < q ? false : true);
            tools::eps_tolerance<RealType> tol(policies::digits<RealType, Policy>());
            boost::uintmax_t max_iter = policies::get_max_root_iterations<Policy>();
            //
            // Pick an initial guess that we know will give us a probability
            // right around 0.5.
            //
            RealType guess = x - v;
            if(guess < 1)
               guess = 1;
            std::pair<RealType, RealType> ir = tools::bracket_and_solve_root(
               f, guess, RealType(2), false, tol, max_iter, pol);
            RealType result = ir.first + (ir.second - ir.first) / 2;
            if(max_iter >= policies::get_max_root_iterations<Policy>())
            {
               policies::raise_evaluation_error<RealType>(function, "Unable to locate solution in a reasonable time:"
                  " or there is no answer to problem.  Current best guess is %1%", result, Policy());
            }
            return result;
         }

      }

      template <class RealType = double, class Policy = policies::policy<> >
      class non_central_chi_squared_distribution
      {
      public:
         typedef RealType value_type;
         typedef Policy policy_type;

         non_central_chi_squared_distribution(RealType df_, RealType lambda) : df(df_), ncp(lambda)
         {
            const char* function = "boost::math::non_central_chi_squared_distribution<%1%>::non_central_chi_squared_distribution(%1%,%1%)";
            RealType r;
            detail::check_df(
               function,
               df, &r, Policy());
            detail::check_non_centrality(
               function,
               ncp,
               &r,
               Policy());
         } // non_central_chi_squared_distribution constructor.

         RealType degrees_of_freedom() const
         { // Private data getter function.
            return df;
         }
         RealType non_centrality() const
         { // Private data getter function.
            return ncp;
         }
         static RealType find_degrees_of_freedom(RealType lam, RealType x, RealType p)
         {
            const char* function = "non_central_chi_squared<%1%>::find_degrees_of_freedom";
            typedef typename policies::evaluation<RealType, Policy>::type value_type;
            typedef typename policies::normalise<
               Policy,
               policies::promote_float<false>,
               policies::promote_double<false>,
               policies::discrete_quantile<>,
               policies::assert_undefined<> >::type forwarding_policy;
            value_type result = detail::find_degrees_of_freedom(
               static_cast<value_type>(lam),
               static_cast<value_type>(x),
               static_cast<value_type>(p),
               static_cast<value_type>(1-p),
               forwarding_policy());
            return policies::checked_narrowing_cast<RealType, forwarding_policy>(
               result,
               function);
         }
         template <class A, class B, class C>
         static RealType find_degrees_of_freedom(const complemented3_type<A,B,C>& c)
         {
            const char* function = "non_central_chi_squared<%1%>::find_degrees_of_freedom";
            typedef typename policies::evaluation<RealType, Policy>::type value_type;
            typedef typename policies::normalise<
               Policy,
               policies::promote_float<false>,
               policies::promote_double<false>,
               policies::discrete_quantile<>,
               policies::assert_undefined<> >::type forwarding_policy;
            value_type result = detail::find_degrees_of_freedom(
               static_cast<value_type>(c.dist),
               static_cast<value_type>(c.param1),
               static_cast<value_type>(1-c.param2),
               static_cast<value_type>(c.param2),
               forwarding_policy());
            return policies::checked_narrowing_cast<RealType, forwarding_policy>(
               result,
               function);
         }
         static RealType find_non_centrality(RealType v, RealType x, RealType p)
         {
            const char* function = "non_central_chi_squared<%1%>::find_non_centrality";
            typedef typename policies::evaluation<RealType, Policy>::type value_type;
            typedef typename policies::normalise<
               Policy,
               policies::promote_float<false>,
               policies::promote_double<false>,
               policies::discrete_quantile<>,
               policies::assert_undefined<> >::type forwarding_policy;
            value_type result = detail::find_non_centrality(
               static_cast<value_type>(v),
               static_cast<value_type>(x),
               static_cast<value_type>(p),
               static_cast<value_type>(1-p),
               forwarding_policy());
            return policies::checked_narrowing_cast<RealType, forwarding_policy>(
               result,
               function);
         }
         template <class A, class B, class C>
         static RealType find_non_centrality(const complemented3_type<A,B,C>& c)
         {
            const char* function = "non_central_chi_squared<%1%>::find_non_centrality";
            typedef typename policies::evaluation<RealType, Policy>::type value_type;
            typedef typename policies::normalise<
               Policy,
               policies::promote_float<false>,
               policies::promote_double<false>,
               policies::discrete_quantile<>,
               policies::assert_undefined<> >::type forwarding_policy;
            value_type result = detail::find_non_centrality(
               static_cast<value_type>(c.dist),
               static_cast<value_type>(c.param1),
               static_cast<value_type>(1-c.param2),
               static_cast<value_type>(c.param2),
               forwarding_policy());
            return policies::checked_narrowing_cast<RealType, forwarding_policy>(
               result,
               function);
         }
      private:
         // Data member, initialized by constructor.
         RealType df; // degrees of freedom.
         RealType ncp; // non-centrality parameter
      }; // template <class RealType, class Policy> class non_central_chi_squared_distribution

      typedef non_central_chi_squared_distribution<double> non_central_chi_squared; // Reserved name of type double.

      // Non-member functions to give properties of the distribution.

      template <class RealType, class Policy>
      inline const std::pair<RealType, RealType> range(const non_central_chi_squared_distribution<RealType, Policy>& /* dist */)
      { // Range of permissible values for random variable k.
         using boost::math::tools::max_value;
         return std::pair<RealType, RealType>(static_cast<RealType>(0), max_value<RealType>()); // Max integer?
      }

      template <class RealType, class Policy>
      inline const std::pair<RealType, RealType> support(const non_central_chi_squared_distribution<RealType, Policy>& /* dist */)
      { // Range of supported values for random variable k.
         // This is range where cdf rises from 0 to 1, and outside it, the pdf is zero.
         using boost::math::tools::max_value;
         return std::pair<RealType, RealType>(static_cast<RealType>(0),  max_value<RealType>());
      }

      template <class RealType, class Policy>
      inline RealType mean(const non_central_chi_squared_distribution<RealType, Policy>& dist)
      { // Mean of poisson distribution = lambda.
         const char* function = "boost::math::non_central_chi_squared_distribution<%1%>::mean()";
         RealType k = dist.degrees_of_freedom();
         RealType l = dist.non_centrality();
         RealType r;
         if(!detail::check_df(
            function,
            k, &r, Policy())
            ||
         !detail::check_non_centrality(
            function,
            l,
            &r,
            Policy()))
               return r;
         return k + l;
      } // mean

      template <class RealType, class Policy>
      inline RealType mode(const non_central_chi_squared_distribution<RealType, Policy>& dist)
      { // mode.
         static const char* function = "mode(non_central_chi_squared_distribution<%1%> const&)";

         RealType k = dist.degrees_of_freedom();
         RealType l = dist.non_centrality();
         RealType r;
         if(!detail::check_df(
            function,
            k, &r, Policy())
            ||
         !detail::check_non_centrality(
            function,
            l,
            &r,
            Policy()))
               return (RealType)r;
         return detail::generic_find_mode(dist, 1 + k, function);
      }

      template <class RealType, class Policy>
      inline RealType variance(const non_central_chi_squared_distribution<RealType, Policy>& dist)
      { // variance.
         const char* function = "boost::math::non_central_chi_squared_distribution<%1%>::variance()";
         RealType k = dist.degrees_of_freedom();
         RealType l = dist.non_centrality();
         RealType r;
         if(!detail::check_df(
            function,
            k, &r, Policy())
            ||
         !detail::check_non_centrality(
            function,
            l,
            &r,
            Policy()))
               return r;
         return 2 * (2 * l + k);
      }

      // RealType standard_deviation(const non_central_chi_squared_distribution<RealType, Policy>& dist)
      // standard_deviation provided by derived accessors.

      template <class RealType, class Policy>
      inline RealType skewness(const non_central_chi_squared_distribution<RealType, Policy>& dist)
      { // skewness = sqrt(l).
         const char* function = "boost::math::non_central_chi_squared_distribution<%1%>::skewness()";
         RealType k = dist.degrees_of_freedom();
         RealType l = dist.non_centrality();
         RealType r;
         if(!detail::check_df(
            function,
            k, &r, Policy())
            ||
         !detail::check_non_centrality(
            function,
            l,
            &r,
            Policy()))
               return r;
         BOOST_MATH_STD_USING
            return pow(2 / (k + 2 * l), RealType(3)/2) * (k + 3 * l);
      }

      template <class RealType, class Policy>
      inline RealType kurtosis_excess(const non_central_chi_squared_distribution<RealType, Policy>& dist)
      {
         const char* function = "boost::math::non_central_chi_squared_distribution<%1%>::kurtosis_excess()";
         RealType k = dist.degrees_of_freedom();
         RealType l = dist.non_centrality();
         RealType r;
         if(!detail::check_df(
            function,
            k, &r, Policy())
            ||
         !detail::check_non_centrality(
            function,
            l,
            &r,
            Policy()))
               return r;
         return 12 * (k + 4 * l) / ((k + 2 * l) * (k + 2 * l));
      } // kurtosis_excess

      template <class RealType, class Policy>
      inline RealType kurtosis(const non_central_chi_squared_distribution<RealType, Policy>& dist)
      {
         return kurtosis_excess(dist) + 3;
      }

      template <class RealType, class Policy>
      inline RealType pdf(const non_central_chi_squared_distribution<RealType, Policy>& dist, const RealType& x)
      { // Probability Density/Mass Function.
         return detail::nccs_pdf(dist, x);
      } // pdf

      template <class RealType, class Policy>
      RealType cdf(const non_central_chi_squared_distribution<RealType, Policy>& dist, const RealType& x)
      {
         const char* function = "boost::math::non_central_chi_squared_distribution<%1%>::cdf(%1%)";
         RealType k = dist.degrees_of_freedom();
         RealType l = dist.non_centrality();
         RealType r;
         if(!detail::check_df(
            function,
            k, &r, Policy())
            ||
         !detail::check_non_centrality(
            function,
            l,
            &r,
            Policy())
            ||
         !detail::check_positive_x(
            function,
            x,
            &r,
            Policy()))
               return r;

         return detail::non_central_chi_squared_cdf(x, k, l, false, Policy());
      } // cdf

      template <class RealType, class Policy>
      RealType cdf(const complemented2_type<non_central_chi_squared_distribution<RealType, Policy>, RealType>& c)
      { // Complemented Cumulative Distribution Function
         const char* function = "boost::math::non_central_chi_squared_distribution<%1%>::cdf(%1%)";
         non_central_chi_squared_distribution<RealType, Policy> const& dist = c.dist;
         RealType x = c.param;
         RealType k = dist.degrees_of_freedom();
         RealType l = dist.non_centrality();
         RealType r;
         if(!detail::check_df(
            function,
            k, &r, Policy())
            ||
         !detail::check_non_centrality(
            function,
            l,
            &r,
            Policy())
            ||
         !detail::check_positive_x(
            function,
            x,
            &r,
            Policy()))
               return r;

         return detail::non_central_chi_squared_cdf(x, k, l, true, Policy());
      } // ccdf

      template <class RealType, class Policy>
      inline RealType quantile(const non_central_chi_squared_distribution<RealType, Policy>& dist, const RealType& p)
      { // Quantile (or Percent Point) function.
         return detail::nccs_quantile(dist, p, false);
      } // quantile

      template <class RealType, class Policy>
      inline RealType quantile(const complemented2_type<non_central_chi_squared_distribution<RealType, Policy>, RealType>& c)
      { // Quantile (or Percent Point) function.
         return detail::nccs_quantile(c.dist, c.param, true);
      } // quantile complement.

   } // namespace math
} // namespace boost

// This include must be at the end, *after* the accessors
// for this distribution have been defined, in order to
// keep compilers that support two-phase lookup happy.
#include <boost/math/distributions/detail/derived_accessors.hpp>

#endif // BOOST_MATH_SPECIAL_NON_CENTRAL_CHI_SQUARE_HPP


#include <iostream>
#include <cassert>
#include <vector>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/depth_first_search.hpp>

const int debug_level = 0;

#define DEBUG(min_level, x)      \
  if (debug_level >= min_level)  \
  {                              \
    std::cerr << x << std::endl; \
  }

typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS> Graph;

const long undefined_diff = std::numeric_limits<int>::max();

class DFSVisitor : public boost::default_dfs_visitor
{
public:
  DFSVisitor(
      std::size_t n,
      std::size_t m,
      std::vector<long> &window_diff_by_start,
      std::function<long(int)> get_value) : n(n),
                                            m(m),
                                            window_diff_by_start(window_diff_by_start),
                                            get_value(get_value)
  {
    assert(m > 0);
  }

  void discover_vertex(int vertex, const Graph &G)
  {
    const long added_value = get_value(vertex);
    DEBUG(3, "discover_vertex " << vertex << " adding " << added_value);
    values_on_path.push_back(added_value);
    vertices_on_path.push_back(vertex);
    values_in_window.insert(added_value);
    if (values_on_path.size() > m)
    {
      const long removed_value = values_on_path.at(values_on_path.size() - m - 1);
      DEBUG(3, "discover_vertex " << vertex << " removing " << removed_value);
      values_in_window.erase(values_in_window.find(removed_value));
    }
    try_save_diff();
  }

  void finish_vertex(int vertex, const Graph &G)
  {
    assert(vertices_on_path.back() == vertex);
    vertices_on_path.pop_back();
    const long removed_value = values_on_path.back();
    DEBUG(3, "finish_vertex " << vertex << " removing " << removed_value);
    values_on_path.pop_back();
    values_in_window.erase(values_in_window.find(removed_value));
    if (values_on_path.size() >= m)
    {
      const long added_value = values_on_path.at(values_on_path.size() - m);
      DEBUG(3, "finish_vertex " << vertex << " adding " << added_value);
      values_in_window.insert(added_value);
    }
  }

private:
  std::size_t n, m;
  std::vector<long> &window_diff_by_start;
  std::function<long(int)> get_value;
  std::multiset<long> values_in_window;
  std::vector<long> values_on_path;
  std::vector<int> vertices_on_path;

  void try_save_diff()
  {
    if (values_in_window.size() < m)
    {
      return;
    }
    assert(values_in_window.size() == m);
    const int start_vertex = vertices_on_path.at(vertices_on_path.size() - m);
    const long diff_in_window = *values_in_window.rbegin() - *values_in_window.begin();
    DEBUG(3, "try_save_diff " << start_vertex << " " << diff_in_window);
    long &saved_diff = window_diff_by_start.at(start_vertex);
    saved_diff = std::min(saved_diff, diff_in_window);
  }
};

void testcase()
{
  int n, m, k;
  std::cin >> n >> m >> k;
  assert(n >= 1 && n <= 1e5 && m >= 2 && m <= 1e4 && k >= 0 && k <= 1e4);

  std::vector<long> temperature_by_node(n);
  for (long &t : temperature_by_node)
  {
    std::cin >> t;
    assert(t >= 0 && t < (1L << 31));
  }

  Graph G(n);
  for (int i = 0; i < n - 1; i++)
  {
    int u, v;
    std::cin >> u >> v;
    assert(u >= 0 && u < n && v >= 0 && v < n);
    assert(u != v); // not sure if this holds
    boost::add_edge(u, v, G);
  }

  std::vector<long> window_diff_by_start(n, undefined_diff);
  DFSVisitor visitor(n, m, window_diff_by_start, [&temperature_by_node](int i) { return temperature_by_node.at(i); });
  // WARNING visitors are copied
  boost::depth_first_search(G, boost::visitor(visitor));

  bool mission_is_possible = false;
  for (int i = 0; i < n; i++)
  {
    const long diff = window_diff_by_start.at(i);
    DEBUG(2, "i " << i << " diff " << diff);
    if (diff == undefined_diff)
    {
      continue;
    }
    assert(diff >= 0);
    if (diff <= k)
    {
      if (mission_is_possible)
      {
        std::cout << " ";
      }
      else
      {
        mission_is_possible = true;
      }
      std::cout << i;
    }
  }
  std::cout << (mission_is_possible ? "\n" : "Abort mission\n");
}

int main()
{
  std::ios_base::sync_with_stdio(false);

  int t;
  std::cin >> t;
  for (int i = 0; i < t; i++)
  {
    testcase();
    DEBUG(1, "");
  }

  return 0;
}


// smooth_feedback: Control theory on Lie groups
// https://github.com/pettni/smooth_feedback
//
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
//
// Copyright (c) 2021 Petter Nilsson, John B. Mains
//
// 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 SMOOTH__FEEDBACK__QP_HPP_
#define SMOOTH__FEEDBACK__QP_HPP_

/**
 * @file
 * @brief Quadratic Program definition.
 */

#include <Eigen/Dense>
#include <Eigen/Sparse>

namespace smooth::feedback {

/**
 * @brief Quadratic program definition.
 *
 * @tparam M number of constraints
 * @tparam N number of variables
 *
 * The quadratic program is on the form
 * \f[
 * \begin{cases}
 *  \min_{x} & \frac{1}{2} x^T P x + q^T x, \\
 *  \text{s.t.} & l \leq A x \leq u,
 * \end{cases}
 * \f]
 * where \f$ P \in \mathbb{R}^{n \times n}, q \in \mathbb{R}^n, l, u \in \mathbb{R}^m, A \in
 * \mathbb{R}^{m \times n} \f$.
 */
template<Eigen::Index M, Eigen::Index N, typename Scalar = double>
struct QuadraticProgram
{
  /// Positive semi-definite square cost (only upper triangular part is used)
  Eigen::Matrix<Scalar, N, N> P;
  /// Linear cost
  Eigen::Matrix<Scalar, N, 1> q;

  /// Inequality matrix
  Eigen::Matrix<Scalar, M, N> A;
  /// Inequality lower bound
  Eigen::Matrix<Scalar, M, 1> l;
  /// Inequality upper bound
  Eigen::Matrix<Scalar, M, 1> u;
};

/**
 * @brief Sparse quadratic program definition.
 *
 * The quadratic program is on the form
 * \f[
 * \begin{cases}
 *  \min_{x} & \frac{1}{2} x^T P x + q^T x, \\
 *  \text{s.t.} & l \leq A x \leq u,
 * \end{cases}
 * \f]
 * where \f$ P \in \mathbb{R}^{n \times n}, q \in \mathbb{R}^n, l, u \in \mathbb{R}^m, A \in
 * \mathbb{R}^{m \times n} \f$.
 */
template<typename Scalar = double>
struct QuadraticProgramSparse
{
  /// Positive semi-definite square cost (only upper trianglular part is used)
  Eigen::SparseMatrix<Scalar> P;
  /// Linear cost
  Eigen::Matrix<Scalar, -1, 1> q;

  /**
   * @brief Inequality matrix
   *
   * @note The constraint matrix is stored in row-major format,
   * i.e. coefficients for each constraint are contiguous in memory
   */
  Eigen::SparseMatrix<Scalar, Eigen::RowMajor> A;
  /// Inequality lower bound
  Eigen::Matrix<Scalar, -1, 1> l;
  /// Inequality upper bound
  Eigen::Matrix<Scalar, -1, 1> u;
};

/// @brief Solver exit codes
enum class QPSolutionStatus {
  Optimal,           /// @brief Solution satisifes optimality condition. Solution is polished if
                     /// `QPSolverParams::polish = true`.
  PolishFailed,      /// @brief Solution satisfies optimality condition but is not polished
  PrimalInfeasible,  /// @brief A certificate of primal infeasibility was found, no solution
                     /// returned
  DualInfeasible,    /// @brief A certificate of dual infeasibility was found, no solution returned
  MaxIterations,  /// @brief Max number of iterations was reached, returned solution is not optimal
  MaxTime,        /// @brief Max time was reached, returned solution is not optimal
  Unknown         /// @brief Solution is useless because of other reasons, no solution returned
};

/// Solver solution
template<Eigen::Index M, Eigen::Index N, typename Scalar = double>
struct QPSolution
{
  /// Exit code
  QPSolutionStatus code = QPSolutionStatus::Unknown;
  /// Number of iterations
  uint32_t iter;
  /// Primal vector
  Eigen::Matrix<Scalar, N, 1> primal;
  /// Dual vector
  Eigen::Matrix<Scalar, M, 1> dual;
  /// Solution objective value
  Scalar objective{0.};
};

}  // namespace smooth::feedback

#endif  // SMOOTH__FEEDBACK__QP_HPP_


#pragma once

#include <algorithm>
#include <array>
#include <cassert>
#include <cmath>
#include <cstdint>
#include <cstdlib>
#include <vector>
#include <utility>

#include <Eigen/Core>

#include <pcl/common/transforms.h>
#include <pcl/io/pcd_io.h>
#include <pcl/common/common.h>
#include <pcl/point_cloud.h>

class PointCloudDim2D;
struct TransformXYTheta;

typedef Eigen::Matrix<std::uint8_t, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> RMatrixXui8;

void writeBGM(const std::string& kSavePath, const RMatrixXui8& kMat);

void writeScanInfo(const std::string& KInfofilename, const std::vector<std::string>& kVelonames,
                   const std::vector<TransformXYTheta>& kTFs,
                   const std::pair<std::uint32_t, std::uint32_t>& kMapImgSize,
                   const float kResolution);

inline
std::array<int, 2> computePixelLoc(
    int imgHeight,
    int imgWidth,
    float x,
    float y,
    float resolution)
{
    assert(imgHeight > 0 && imgWidth > 0);
    assert(x >= 0.f && y >= 0.f);
    assert(resolution > 0.f);

    return std::array<int, 2>{
        std::min(imgHeight - static_cast<int>(floor(x / resolution)), imgHeight - 1),
        std::min( imgWidth - static_cast<int>(floor(y / resolution)),  imgWidth - 1)
    };
}

template<typename T>
inline T eucddist(const T& x0, const T& y0, const T& x1, const T& y1)
{
    return std::sqrt(std::pow(x0 - x1, 2) + std::pow(y0 - y1, 2));
}

template <typename PointT>
void aggregatePointClouds(
    const std::vector<pcl::PointCloud<PointT>>& kIndivClouds,
    pcl::PointCloud<PointT>& aggCloud)
{
    for (auto it = kIndivClouds.begin(); it != kIndivClouds.end(); ++it)
        aggCloud += *it;
}

template<typename PointT>
RMatrixXui8 buildBEVFromCloud(
    const pcl::PointCloud<PointT>& kCloud,
    const float kXdiff,
    const float kYdiff,
    const float outRes,
    bool useZvals = false,
    float minZ = 0.f)
{
    // Sanity checks.
    assert(kXdiff > 0.f && kYdiff > 0.f && outRes > 0.f);

    // Compute the image size.
    auto imgHeight = static_cast<int>(ceil(kXdiff / outRes));
    auto imgWidth = static_cast<int>(ceil(kYdiff / outRes));

    // Create the bird's eye matrix (image) and iteratively fill the matrix.
    RMatrixXui8 bev = RMatrixXui8::Zero(imgHeight, imgWidth);
    for (std::size_t i = 0; i < kCloud.size(); i++)
    {
        const PointT& pt = kCloud[i];
        float pixelValue = 0.0f;
        // max / min from zlen
        if (useZvals)
        {
            //pixelValue = (pt.z - kMinPt.z) / (kMaxPt.z - kMinPt.z);
            pixelValue = (pt.z + minZ) / (minZ * 2);
        }
        else
        {
            pixelValue = pt.intensity;
        }
        pixelValue = std::min(std::max(pixelValue * 255.0f, 0.0f), 255.0f);

        auto pxLoc = computePixelLoc(imgHeight, imgWidth, pt.x, pt.y, outRes);
        auto row = pxLoc[0];
        auto col = pxLoc[1];

        bev(row, col) = std::max(static_cast<std::uint8_t>(pixelValue), bev(row, col));
    }
    return bev;
}

template<typename PointT>
void extractPointCloudROI(
    const pcl::PointCloud<PointT>& kOriginalPC,
    const float kXlen,
    const float kYlen,
    pcl::PointCloud<PointT>& extractedPC)
{
    // Sanity checks.
    assert(kOriginalPC.size() > 0);
    assert(kXlen > 0.f && kYlen > 0.f);

    float x = 0.f, y = 0.f;
    for (auto pt_it = kOriginalPC.points.begin(); pt_it != kOriginalPC.points.end(); ++pt_it)
    {
        x = pt_it->x;
        y = pt_it->y;
        if (-kXlen < x && x < kXlen && -kYlen < y && y < kYlen)
            extractedPC.push_back(*pt_it);
    }
}

template<typename PointT>
void extractPointCloudROI(
    const pcl::PointCloud<PointT>& kOriginalPC,
    const float kRadius,
    pcl::PointCloud<PointT>& extractedPC)
{
    // Sanity checks.
    assert(kOriginalPC.size() > 0);
    assert(kRadius > 0.f);

    float d = 0.f;
    for (auto pt_it = kOriginalPC.points.begin(); pt_it != kOriginalPC.points.end(); ++pt_it)
    {
        d = std::sqrt(std::pow(pt_it->x, 2) + std::pow(pt_it->y, 2));
        if (d < kRadius)
            extractedPC.push_back(*pt_it);
    }
}

template<typename PointT>
void translateDataXY(
    pcl::PointCloud<PointT>& cloud,
    const Eigen::Vector2f& kShift)
{
    // Sanity check.
    assert(cloud.size() > 0);

    const float dx = -kShift[0], dy = -kShift[1];
    for (auto it = cloud.points.begin(); it != cloud.points.end(); ++it)
    {
        it->x += dx;
        it->y += dy;
    }
}

template<typename T>
void translateDataXY(
    std::vector<T>& data,
    const Eigen::Vector2f& kShift)
{
    // Sanity check.
    assert(data.size() > 0);

    const float dx = -kShift[0], dy = -kShift[1];
    for (auto it = data.begin(); it != data.end(); ++it)
    {
        it->x += dx;
        it->y += dy;
    }
}


#include <boost/test/unit_test.hpp>
#include "expression/Parser.hpp"
#include "expression/Ast.hpp"
#include "expression/Lexer.hpp"
#include "expression/Scope.hpp"
#include "types/Number.hpp"
#include "Error.hpp"

using namespace slim;
using namespace slim::expr;
BOOST_AUTO_TEST_SUITE(TestNumber)

std::string eval(const std::string &str, Scope &scope)
{
    Lexer lexer(str);
    expr::LocalVarNames vars;
    for (auto x : scope) vars.add(x.first->str());
    Parser parser(vars, lexer);
    auto expr = parser.full_expression();
    auto result = expr->eval(scope);
    return result->inspect();
}
std::string eval(const std::string &str)
{
    Scope scope(create_view_model());
    return eval(str, scope);
}

BOOST_AUTO_TEST_CASE(basic_methods)
{
    BOOST_CHECK_EQUAL("inf", make_value(INFINITY)->to_string());
    BOOST_CHECK_EQUAL("-inf", make_value(-INFINITY)->to_string());
    Scope scope(create_view_model());
    scope.set("inf", make_value(INFINITY));
    scope.set("ninf", make_value(-INFINITY));
    scope.set("min", make_value(-std::numeric_limits<double>::max()));
    scope.set("max", make_value(std::numeric_limits<double>::max()));

    std::string min_s = make_value(-std::numeric_limits<double>::max())->to_string();
    std::string max_s = make_value(std::numeric_limits<double>::max())->to_string();

    BOOST_CHECK_EQUAL("0", eval("0.abs"));
    BOOST_CHECK_EQUAL("5", eval("5.0.abs"));
    BOOST_CHECK_EQUAL("5.5", eval("5.5.abs"));
    BOOST_CHECK_EQUAL("5", eval("-5.abs"));
    BOOST_CHECK_EQUAL("5", eval("-5.0.abs"));
    BOOST_CHECK_EQUAL("5.5", eval("-5.5.abs"));
    BOOST_CHECK_EQUAL("inf", eval("inf.abs", scope));
    BOOST_CHECK_EQUAL("inf", eval("ninf.abs", scope));

    BOOST_CHECK_EQUAL("inf", eval("inf.next_float", scope));
    BOOST_CHECK_EQUAL("inf", eval("max.next_float", scope));
    BOOST_CHECK_EQUAL(min_s, eval("ninf.next_float", scope));

    BOOST_CHECK_EQUAL(max_s, eval("inf.prev_float", scope));
    BOOST_CHECK_EQUAL("-inf", eval("ninf.prev_float", scope));
    BOOST_CHECK_EQUAL("-inf", eval("min.prev_float", scope));

    BOOST_CHECK_EQUAL("true", eval("0.finite?"));
    BOOST_CHECK_EQUAL("true", eval("5.5.finite?"));
    BOOST_CHECK_EQUAL("false", eval("(0.0/0.0).finite?"));
    BOOST_CHECK_EQUAL("false", eval("(1.0/0.0).finite?"));

    BOOST_CHECK_EQUAL("nil", eval("0.infinite?"));
    BOOST_CHECK_EQUAL("nil", eval("(0.0/0.0).infinite?"));
    BOOST_CHECK_EQUAL("1", eval("(1.0/0.0).infinite?"));
    BOOST_CHECK_EQUAL("-1", eval("(-1.0/0.0).infinite?"));

    BOOST_CHECK_EQUAL("false", eval("1.nan?"));
    BOOST_CHECK_EQUAL("true", eval("(0.0/0.0).nan?"));
    BOOST_CHECK_EQUAL("false", eval("(1.0/0.0).nan?"));
    BOOST_CHECK_EQUAL("false", eval("(-1.0/0.0).nan?"));

    BOOST_CHECK_EQUAL("true", eval("0.zero?", scope));
    BOOST_CHECK_EQUAL("true", eval("-0.zero?", scope));
    BOOST_CHECK_EQUAL("false", eval("5.zero?", scope));
}

BOOST_AUTO_TEST_CASE(operators)
{
    BOOST_CHECK_EQUAL("5", eval("+5"));
    BOOST_CHECK_EQUAL("-5", eval("-5"));
    BOOST_CHECK_EQUAL("false", eval("!5"));

    BOOST_CHECK_EQUAL("5", eval("1 + 4"));
    BOOST_CHECK_EQUAL("5", eval("6 - 1"));
    BOOST_CHECK_EQUAL("6", eval("2 * 3"));
    BOOST_CHECK_EQUAL("6", eval("24 / 4"));
    BOOST_CHECK_EQUAL("6", eval("16 % 10"));
    BOOST_CHECK_EQUAL("9", eval("3 ** 2"));

    BOOST_CHECK_EQUAL("8", eval("2 << 2"));
    BOOST_CHECK_EQUAL("1", eval("2 >> 1"));
    BOOST_CHECK_EQUAL("0", eval("2 >> 3"));
    BOOST_CHECK_EQUAL("2", eval("7 & 2"));
    BOOST_CHECK_EQUAL("7", eval("6 | 3"));
    BOOST_CHECK_EQUAL("10", eval("8 ^ 2"));
    BOOST_CHECK_EQUAL("8", eval("10 ^ 2"));
    BOOST_CHECK_EQUAL("-9", eval("~8"));
}

BOOST_AUTO_TEST_CASE(rounding)
{
    BOOST_CHECK_EQUAL("0", eval("0.ceil"));
    BOOST_CHECK_EQUAL("6", eval("5.4.ceil"));
    BOOST_CHECK_EQUAL("6", eval("5.6.ceil"));
    BOOST_CHECK_EQUAL("-5", eval("-5.3.ceil"));
    BOOST_CHECK_EQUAL("-5", eval("-5.6.ceil"));

    BOOST_CHECK_EQUAL("0", eval("0.floor"));
    BOOST_CHECK_EQUAL("5", eval("5.4.floor"));
    BOOST_CHECK_EQUAL("5", eval("5.6.floor"));
    BOOST_CHECK_EQUAL("-6", eval("-5.3.floor"));
    BOOST_CHECK_EQUAL("-6", eval("-5.6.floor"));

    BOOST_CHECK_EQUAL("0", eval("0.round"));
    BOOST_CHECK_EQUAL("5", eval("5.4.round"));
    BOOST_CHECK_EQUAL("6", eval("5.6.round"));
    BOOST_CHECK_EQUAL("-5", eval("-5.3.round"));
    BOOST_CHECK_EQUAL("-6", eval("-5.6.round"));

    BOOST_CHECK_EQUAL("0", eval("0.round(2)"));
    BOOST_CHECK_EQUAL("5.4", eval("5.4.round(2)"));
    BOOST_CHECK_EQUAL("5.6", eval("5.63.round(2)"));
    BOOST_CHECK_EQUAL("5.7", eval("5.66.round(2)"));
    BOOST_CHECK_EQUAL("-5.3", eval("-5.3.round(2)"));
    BOOST_CHECK_EQUAL("-5.6", eval("-5.63.round(2)"));
    BOOST_CHECK_EQUAL("-5.7", eval("-5.66.round(2)"));

    BOOST_CHECK_EQUAL("0", eval("0.round(-1)"));
    BOOST_CHECK_EQUAL("5", eval("5.4.round(-1)"));
    BOOST_CHECK_EQUAL("6", eval("5.63.round(-1)"));
    BOOST_CHECK_EQUAL("6", eval("5.66.round(-1)"));
    BOOST_CHECK_EQUAL("-5", eval("-5.3.round(-1)"));
    BOOST_CHECK_EQUAL("-6", eval("-5.63.round(-1)"));
    BOOST_CHECK_EQUAL("-6", eval("-5.66.round(-1)"));

    BOOST_CHECK_EQUAL("0", eval("0.round(-1)"));
    BOOST_CHECK_EQUAL("50", eval("54.round(-1)"));
    BOOST_CHECK_EQUAL("500", eval("543.round(-1)"));
    BOOST_CHECK_EQUAL("600", eval("566.round(-1)"));
    BOOST_CHECK_EQUAL("-500", eval("-539.round(-1)"));
    BOOST_CHECK_EQUAL("-500", eval("-543.round(-1)"));
    BOOST_CHECK_EQUAL("-600", eval("-566.round(-1)"));
}

BOOST_AUTO_TEST_SUITE_END()


/**
 * @file lin_alg_test.cpp
 * @author Ryan Curtin
 *
 * Simple tests for things in the linalg__private namespace.
 * Partly so I can be sure that my changes are working.
 * Move to boost unit testing framework at some point.
 *
 * mlpack is free software; you may redistribute it and/or modify it under the
 * terms of the 3-clause BSD license.  You should have received a copy of the
 * 3-clause BSD license along with mlpack.  If not, see
 * http://www.opensource.org/licenses/BSD-3-Clause for more information.
 */
#include <mlpack/core.hpp>
#include <mlpack/core/math/lin_alg.hpp>

#include <boost/test/unit_test.hpp>
#include "test_tools.hpp"

using namespace arma;
using namespace mlpack;
using namespace mlpack::math;

BOOST_AUTO_TEST_SUITE(LinAlgTest);

/**
 * Test for linalg__private::Center().  There are no edge cases here, so we'll
 * just try it once for now.
 */
BOOST_AUTO_TEST_CASE(TestCenterA)
{
  mat tmp(5, 5);
  // [[0  0  0  0  0]
  //  [1  2  3  4  5]
  //  [2  4  6  8  10]
  //  [3  6  9  12 15]
  //  [4  8  12 16 20]]
  for (int row = 0; row < 5; row++)
    for (int col = 0; col < 5; col++)
      tmp(row, col) = row * (col + 1);

  mat tmp_out;
  Center(tmp, tmp_out);

  // average should be
  // [[0 3 6 9 12]]'
  // so result should be
  // [[ 0  0  0  0  0]
  //  [-2 -1  0  1  2 ]
  //  [-4 -2  0  2  4 ]
  //  [-6 -3  0  3  6 ]
  //  [-8 -4  0  4  8]]
  for (int row = 0; row < 5; row++)
    for (int col = 0; col < 5; col++)
      BOOST_REQUIRE_CLOSE(tmp_out(row, col), (double) (col - 2) * row, 1e-5);
}

BOOST_AUTO_TEST_CASE(TestCenterB)
{
  mat tmp(5, 6);
  for (int row = 0; row < 5; row++)
    for (int col = 0; col < 6; col++)
      tmp(row, col) = row * (col + 1);

  mat tmp_out;
  Center(tmp, tmp_out);

  // average should be
  // [[0 3.5 7 10.5 14]]'
  // so result should be
  // [[ 0    0    0   0   0   0  ]
  //  [-2.5 -1.5 -0.5 0.5 1.5 2.5]
  //  [-5   -3   -1   1   3   5  ]
  //  [-7.5 -4.5 -1.5 1.5 1.5 4.5]
  //  [-10  -6   -2   2   6   10 ]]
  for (int row = 0; row < 5; row++)
    for (int col = 0; col < 6; col++)
      BOOST_REQUIRE_CLOSE(tmp_out(row, col), (double) (col - 2.5) * row, 1e-5);
}

BOOST_AUTO_TEST_CASE(TestWhitenUsingEig)
{
  // After whitening using eigendecomposition, the covariance of
  // our matrix will be I (or something very close to that).
  // We are loading a matrix from an external file... bad choice.
  mat tmp, tmp_centered, whitened, whitening_matrix;

  data::Load("trainSet.csv", tmp);
  Center(tmp, tmp_centered);
  WhitenUsingEig(tmp_centered, whitened, whitening_matrix);

  mat newcov = ccov(whitened);
  for (int row = 0; row < 5; row++)
  {
    for (int col = 0; col < 5; col++)
    {
      if (row == col)
      {
        // diagonal will be 0 in the case of any zero-valued eigenvalues
        // (rank-deficient covariance case)
        if (std::abs(newcov(row, col)) > 1e-10)
          BOOST_REQUIRE_CLOSE(newcov(row, col), 1.0, 1e-10);
      }
      else
      {
        BOOST_REQUIRE_SMALL(newcov(row, col), 1e-10);
      }
    }
  }
}

BOOST_AUTO_TEST_CASE(TestOrthogonalize)
{
  // Generate a random matrix; then, orthogonalize it and test if it's
  // orthogonal.
  mat tmp, orth;
  data::Load("fake.csv", tmp);
  Orthogonalize(tmp, orth);

  // test orthogonality
  mat test = ccov(orth);
  double ival = test(0, 0);
  for (size_t row = 0; row < test.n_rows; row++)
  {
    for (size_t col = 0; col < test.n_cols; col++)
    {
      if (row == col)
      {
        if (std::abs(test(row, col)) > 1e-10)
          BOOST_REQUIRE_CLOSE(test(row, col), ival, 1e-10);
      }
      else
      {
        BOOST_REQUIRE_SMALL(test(row, col), 1e-10);
      }
    }
  }
}

// Test RemoveRows().
BOOST_AUTO_TEST_CASE(TestRemoveRows)
{
  // Run this test several times.
  for (size_t run = 0; run < 10; ++run)
  {
    arma::mat input;
    input.randu(200, 200);

    // Now pick some random numbers.
    std::vector<size_t> rowsToRemove;
    size_t row = 0;
    while (row < 200)
    {
      row += RandInt(1, (2 * (run + 1) + 1));
      if (row < 200)
      {
        rowsToRemove.push_back(row);
      }
    }

    // Ensure we're not about to remove every single row.
    if (rowsToRemove.size() == 10)
    {
      rowsToRemove.erase(rowsToRemove.begin() + 4); // Random choice to remove.
    }

    arma::mat output;
    RemoveRows(input, rowsToRemove, output);

    // Now check that the output is right.
    size_t outputRow = 0;
    size_t skipIndex = 0;

    for (row = 0; row < 200; ++row)
    {
      // Was this row supposed to be removed?  If so skip it.
      if ((skipIndex < rowsToRemove.size()) && (rowsToRemove[skipIndex] == row))
      {
        ++skipIndex;
      }
      else
      {
        // Compare.
        BOOST_REQUIRE_EQUAL(accu(input.row(row) == output.row(outputRow)), 200);

        // Increment output row counter.
        ++outputRow;
      }
    }
  }
}

BOOST_AUTO_TEST_CASE(TestSvecSmat)
{
  arma::mat X(3, 3);
  X(0, 0) = 0; X(0, 1) = 1, X(0, 2) = 2;
  X(1, 0) = 1; X(1, 1) = 3, X(1, 2) = 4;
  X(2, 0) = 2; X(2, 1) = 4, X(2, 2) = 5;

  arma::vec sx;
  Svec(X, sx);
  BOOST_REQUIRE_CLOSE(sx(0), 0, 1e-7);
  BOOST_REQUIRE_CLOSE(sx(1), M_SQRT2 * 1., 1e-7);
  BOOST_REQUIRE_CLOSE(sx(2), M_SQRT2 * 2., 1e-7);
  BOOST_REQUIRE_CLOSE(sx(3), 3., 1e-7);
  BOOST_REQUIRE_CLOSE(sx(4), M_SQRT2 * 4., 1e-7);
  BOOST_REQUIRE_CLOSE(sx(5), 5., 1e-7);

  arma::mat Xtest;
  Smat(sx, Xtest);
  BOOST_REQUIRE_EQUAL(Xtest.n_rows, 3);
  BOOST_REQUIRE_EQUAL(Xtest.n_cols, 3);
  for (size_t i = 0; i < 3; i++)
    for (size_t j = 0; j < 3; j++)
      BOOST_REQUIRE_CLOSE(X(i, j), Xtest(i, j), 1e-7);
}

BOOST_AUTO_TEST_CASE(TestSparseSvec)
{
  arma::sp_mat X;
  X.zeros(3, 3);
  X(1, 0) = X(0, 1) = 1;

  arma::sp_vec sx;
  Svec(X, sx);

  const double v0 = sx(0);
  const double v1 = sx(1);
  const double v2 = sx(2);
  const double v3 = sx(3);
  const double v4 = sx(4);
  const double v5 = sx(5);

  BOOST_REQUIRE_CLOSE(v0, 0, 1e-7);
  BOOST_REQUIRE_CLOSE(v1, M_SQRT2 * 1., 1e-7);
  BOOST_REQUIRE_CLOSE(v2, 0, 1e-7);
  BOOST_REQUIRE_CLOSE(v3, 0, 1e-7);
  BOOST_REQUIRE_CLOSE(v4, 0, 1e-7);
  BOOST_REQUIRE_CLOSE(v5, 0, 1e-7);
}

BOOST_AUTO_TEST_CASE(TestSymKronIdSimple)
{
  arma::mat A(3, 3);
  A(0, 0) = 1; A(0, 1) = 2, A(0, 2) = 3;
  A(1, 0) = 2; A(1, 1) = 4, A(1, 2) = 5;
  A(2, 0) = 3; A(2, 1) = 5, A(2, 2) = 6;
  arma::mat Op;
  SymKronId(A, Op);

  const arma::mat X = A + arma::ones<arma::mat>(3, 3);
  arma::vec sx;
  Svec(X, sx);

  const arma::vec lhs = Op * sx;
  const arma::mat Rhs = 0.5 * (A * X + X * A);
  arma::vec rhs;
  Svec(Rhs, rhs);

  BOOST_REQUIRE_EQUAL(lhs.n_elem, rhs.n_elem);
  for (size_t j = 0; j < lhs.n_elem; j++)
    BOOST_REQUIRE_CLOSE(lhs(j), rhs(j), 1e-5);
}

BOOST_AUTO_TEST_CASE(TestSymKronId)
{
  const size_t n = 10;
  arma::mat A = arma::randu<arma::mat>(n, n);
  A += A.t();

  arma::mat Op;
  SymKronId(A, Op);

  for (size_t i = 0; i < 5; i++)
  {
    arma::mat X = arma::randu<arma::mat>(n, n);
    X += X.t();
    arma::vec sx;
    Svec(X, sx);

    const arma::vec lhs = Op * sx;
    const arma::mat Rhs = 0.5 * (A * X + X * A);
    arma::vec rhs;
    Svec(Rhs, rhs);

    BOOST_REQUIRE_EQUAL(lhs.n_elem, rhs.n_elem);
    for (size_t j = 0; j < lhs.n_elem; j++)
      BOOST_REQUIRE_CLOSE(lhs(j), rhs(j), 1e-5);
  }
}

BOOST_AUTO_TEST_SUITE_END();


#include <iostream>
#include "gtest/gtest.h"
#include <Eigen/Core>
#include "mean_curvature_solver.h"
#include "uniform_lb_operator.h"
#include "cotangent_lb_operator.h"

class MeanCurvatureSolverTest : public ::testing::Test {
protected:
    MeanCurvatureSolverTest() : mUnifromMCS(mcurv::uniformLBOperatorStrategy),
                                mCotangentMCS(mcurv::cotangentLBOperatorStrategy),
                                mBunnyPath("./res/bunny.off") {}

    Eigen::MatrixXd mSolution;
    const std::string mBunnyPath;
    mcurv::MeanCurvatureSolver mUnifromMCS;
    mcurv::MeanCurvatureSolver mCotangentMCS;
};

TEST_F(MeanCurvatureSolverTest, WrongPathForExecute) {
    ASSERT_THROW(mUnifromMCS.Execute(mSolution, "wrong_path"), std::runtime_error);
    ASSERT_THROW(mCotangentMCS.Execute(mSolution, "wrong_path"), std::runtime_error);
}

TEST_F(MeanCurvatureSolverTest, ExecUniform) {
    mUnifromMCS.Execute(mSolution, mBunnyPath);

    EXPECT_NEAR(mSolution(0,0), 0.0065769, 0.001);
    EXPECT_NEAR(mSolution(1,0), -0.00329001, 0.001);
    EXPECT_NEAR(mSolution(2,0), -0.00750467, 0.001);
}

TEST_F(MeanCurvatureSolverTest, ExecCotangent) {
    mCotangentMCS.Execute(mSolution, mBunnyPath);

    EXPECT_NEAR(mSolution(0,0), 66.2739, 0.001);
    EXPECT_NEAR(mSolution(1,0), -555.572, 0.001);
    EXPECT_NEAR(mSolution(2,0), -76.9057, 0.001);
}


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

/*
 Copyright (C) 2015 Peter Caspers

 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 "lgm.hpp"
#include "utilities.hpp"
#include <ql/indexes/ibor/euribor.hpp>
#include <ql/instruments/vanillaoption.hpp>
#include <ql/models/shortrate/onefactormodels/gsr.hpp>
#include <ql/models/shortrate/calibrationhelpers/swaptionhelper.hpp>
#include <ql/math/statistics/incrementalstatistics.hpp>
#include <ql/math/optimization/levenbergmarquardt.hpp>
#include <ql/math/randomnumbers/rngtraits.hpp>
#include <ql/methods/montecarlo/multipathgenerator.hpp>
#include <ql/methods/montecarlo/pathgenerator.hpp>
#include <ql/pricingengines/swaption/gaussian1dswaptionengine.hpp>
#include <ql/termstructures/yield/flatforward.hpp>
#include <ql/time/calendars/target.hpp>
#include <ql/time/daycounters/actual360.hpp>
#include <ql/time/daycounters/thirty360.hpp>
#include <ql/quotes/simplequote.hpp>
#include <ql/experimental/models/lgm1.hpp>
#include <ql/experimental/models/cclgm1.hpp>
#include <ql/experimental/models/cclgmanalyticfxoptionengine.hpp>
#include <ql/experimental/models/fxoptionhelper.hpp>

#include <boost/make_shared.hpp>
#include <boost/accumulators/accumulators.hpp>
#include <boost/accumulators/statistics/mean.hpp>
#include <boost/accumulators/statistics/stats.hpp>
#include <boost/accumulators/statistics/error_of_mean.hpp>

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

void LgmTest::testBermudanLgm1fGsr() {

    BOOST_TEST_MESSAGE("Testing consistency of Bermudan swaption pricing in "
                       "LGM1F and GSR models...");

    // for kappa (LGM) = reversion (GSR) = 0.0
    // we have alpha (LGM) = sigma (GSR), so
    // we should get equal Bermudan swaption prices

    SavedSettings backup;

    Date evalDate(12, January, 2015);
    Settings::instance().evaluationDate() = evalDate;
    Handle<YieldTermStructure> yts(
        boost::make_shared<FlatForward>(evalDate, 0.02, Actual365Fixed()));
    boost::shared_ptr<IborIndex> euribor6m =
        boost::make_shared<Euribor>(6 * Months, yts);

    Date effectiveDate = TARGET().advance(evalDate, 2 * Days);
    Date startDate = TARGET().advance(effectiveDate, 1 * Years);
    Date maturityDate = TARGET().advance(startDate, 9 * Years);

    Schedule fixedSchedule(startDate, maturityDate, 1 * Years, TARGET(),
                           ModifiedFollowing, ModifiedFollowing,
                           DateGeneration::Forward, false);
    Schedule floatingSchedule(startDate, maturityDate, 6 * Months, TARGET(),
                              ModifiedFollowing, ModifiedFollowing,
                              DateGeneration::Forward, false);
    boost::shared_ptr<VanillaSwap> underlying = boost::make_shared<VanillaSwap>(
        VanillaSwap(VanillaSwap::Payer, 1.0, fixedSchedule, 0.02, Thirty360(),
                    floatingSchedule, euribor6m, 0.0, Actual360()));

    std::vector<Date> exerciseDates;
    for (Size i = 0; i < 9; ++i) {
        exerciseDates.push_back(TARGET().advance(fixedSchedule[i], -2 * Days));
    }
    boost::shared_ptr<Exercise> exercise =
        boost::make_shared<BermudanExercise>(exerciseDates, false);

    boost::shared_ptr<Swaption> swaption =
        boost::make_shared<Swaption>(underlying, exercise);

    std::vector<Date> stepDates(exerciseDates.begin(), exerciseDates.end() - 1);
    std::vector<Real> sigmas(stepDates.size() + 1);
    for (Size i = 0; i < sigmas.size(); ++i) {
        sigmas[i] = 0.0050 +
                    (0.0080 - 0.0050) * std::exp(-0.2 * static_cast<double>(i));
    }

    Real reversion = 0.0;

    // fix any T forward measure
    boost::shared_ptr<Gsr> gsr =
        boost::make_shared<Gsr>(yts, stepDates, sigmas, reversion, 50.0);

    boost::shared_ptr<Lgm1> lgm =
        boost::make_shared<Lgm1>(yts, stepDates, sigmas, reversion);

    boost::shared_ptr<PricingEngine> swaptionEngineGsr =
        boost::make_shared<Gaussian1dSwaptionEngine>(gsr, 64, 7.0, true, false);

    boost::shared_ptr<PricingEngine> swaptionEngineLgm =
        boost::make_shared<Gaussian1dSwaptionEngine>(lgm, 64, 7.0, true, false);

    swaption->setPricingEngine(swaptionEngineGsr);
    Real npvGsr = swaption->NPV();
    swaption->setPricingEngine(swaptionEngineLgm);
    Real npvLgm = swaption->NPV();

    Real tol = 0.05E-4; // basis point tolerance

    if (std::fabs(npvGsr - npvLgm) > tol)
        BOOST_ERROR(
            "Failed to verify consistency of Bermudan swaption price in Lgm1f ("
            << npvLgm << ") and Gsr (" << npvGsr << ") models, tolerance is "
            << tol);
} // testBermudanLgm1fGsr

void LgmTest::testLgm1fCalibration() {

    BOOST_TEST_MESSAGE(
        "Testing calibration of LGM1F model against GSR parameters...");

    // for fixed kappa != 0.0 we calibrate alpha
    // and compare the effective Hull White parameters
    // with the calibration results for the Gsr model

    SavedSettings backup;

    Date evalDate(12, January, 2015);
    Settings::instance().evaluationDate() = evalDate;
    Handle<YieldTermStructure> yts(
        boost::make_shared<FlatForward>(evalDate, 0.02, Actual365Fixed()));
    boost::shared_ptr<IborIndex> euribor6m =
        boost::make_shared<Euribor>(6 * Months, yts);

    // coterminal basket 1y-9y, 2y-8y, ... 9y-1y

    std::vector<boost::shared_ptr<CalibrationHelper> > basket;
    Real impliedVols[] = {0.4, 0.39, 0.38, 0.35, 0.35, 0.34, 0.33, 0.32, 0.31};
    std::vector<Date> expiryDates;

    for (Size i = 0; i < 9; ++i) {
        boost::shared_ptr<CalibrationHelper> helper =
            boost::make_shared<SwaptionHelper>(
                (i + 1) * Years, (9 - i) * Years,
                Handle<Quote>(boost::make_shared<SimpleQuote>(impliedVols[i])),
                euribor6m, 1 * Years, Thirty360(), Actual360(), yts);
        basket.push_back(helper);
        expiryDates.push_back(boost::static_pointer_cast<SwaptionHelper>(helper)
                                  ->swaption()
                                  ->exercise()
                                  ->dates()
                                  .back());
    }

    std::vector<Date> stepDates(expiryDates.begin(), expiryDates.end() - 1);

    std::vector<Real> gsrInitialSigmas(stepDates.size() + 1, 0.0050);
    std::vector<Real> lgmInitialAlphas(stepDates.size() + 1, 0.0050);

    Real kappa = 0.05;

    // fix any T forward measure
    boost::shared_ptr<Gsr> gsr =
        boost::make_shared<Gsr>(yts, stepDates, gsrInitialSigmas, kappa, 50.0);

    boost::shared_ptr<Lgm1> lgm =
        boost::make_shared<Lgm1>(yts, stepDates, lgmInitialAlphas, kappa);

    boost::shared_ptr<PricingEngine> swaptionEngineGsr =
        boost::make_shared<Gaussian1dSwaptionEngine>(gsr, 64, 7.0, true, false);

    boost::shared_ptr<PricingEngine> swaptionEngineLgm =
        boost::make_shared<Gaussian1dSwaptionEngine>(lgm, 64, 7.0, true, false);

    // calibrate GSR

    LevenbergMarquardt lm(1E-8, 1E-8, 1E-8);
    EndCriteria ec(1000, 500, 1E-8, 1E-8, 1E-8);

    for (Size i = 0; i < basket.size(); ++i) {
        basket[i]->setPricingEngine(swaptionEngineGsr);
    }

    gsr->calibrateVolatilitiesIterative(basket, lm, ec);

    Array gsrSigmas = gsr->volatility();

    // calibrate LGM

    for (Size i = 0; i < basket.size(); ++i) {
        basket[i]->setPricingEngine(swaptionEngineLgm);
    }

    lgm->calibrateAlphasIterative(basket, lm, ec);

    std::vector<Real> lgmHwSigmas;
    std::vector<Real> lgmHwKappas;

    for (Size i = 0; i < gsrSigmas.size(); ++i) {
        lgmHwSigmas.push_back(
            lgm->hullWhiteSigma(static_cast<double>(i) + 0.5));
        lgmHwKappas.push_back(
            lgm->hullWhiteKappa(static_cast<double>(i) + 0.5));
    }

    Real tol0 = 1E-8;
    Real tol = 1E-4;

    for (Size i = 0; i < gsrSigmas.size(); ++i) {
        // check calibration itself, we should match the market prices
        // rather exactly
        if (std::fabs(basket[i]->modelValue() - basket[i]->marketValue()) >
            tol0)
            BOOST_ERROR("Failed to calibrate to market swaption #"
                        << i << ", market price is " << basket[i]->marketValue()
                        << " while model price is " << basket[i]->modelValue());
        // we can not directly compare the gsr model's sigma with
        // the lgm model's equivalent HW sigma (since the former
        // is piecewise constant, while the latter is not), but
        // we can do a rough check on the mid point of each interval
        if (std::fabs(gsrSigmas[i] - lgmHwSigmas[i]) > tol)
            BOOST_ERROR("Failed to verify LGM's equivalent Hull White sigma (#"
                        << i << "), which is " << lgmHwSigmas[i]
                        << " while GSR's sigma is " << gsrSigmas[i] << ")");
    }

} // testLgm1fCalibration

void LgmTest::testLgm3fForeignPayouts() {
    BOOST_TEST_MESSAGE("Testing pricing of foreign payouts under domestic "
                       "measure in LGM3F model...");

    SavedSettings backup;

    Date referenceDate(30, July, 2015);

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

    Handle<YieldTermStructure> eurYts(
        boost::make_shared<FlatForward>(referenceDate, 0.02, Actual365Fixed()));

    Handle<YieldTermStructure> usdYts(
        boost::make_shared<FlatForward>(referenceDate, 0.05, Actual365Fixed()));

    // use different grids for the EUR and USD  models and the FX volatility
    // process to test the piecewise numerical integration ...

    std::vector<Date> volstepdatesEur, volstepdatesUsd, volstepdatesFx;

    volstepdatesEur.push_back(Date(15, July, 2016));
    volstepdatesEur.push_back(Date(15, July, 2017));
    volstepdatesEur.push_back(Date(15, July, 2018));
    volstepdatesEur.push_back(Date(15, July, 2019));
    volstepdatesEur.push_back(Date(15, July, 2020));

    volstepdatesUsd.push_back(Date(13, April, 2016));
    volstepdatesUsd.push_back(Date(13, September, 2016));
    volstepdatesUsd.push_back(Date(13, April, 2017));
    volstepdatesUsd.push_back(Date(13, September, 2017));
    volstepdatesUsd.push_back(Date(13, April, 2018));
    volstepdatesUsd.push_back(Date(15, July, 2018)); // shared with EUR
    volstepdatesUsd.push_back(Date(13, April, 2019));
    volstepdatesUsd.push_back(Date(13, September, 2019));

    volstepdatesFx.push_back(Date(15, July, 2016)); // shared with EUR
    volstepdatesFx.push_back(Date(15, October, 2016));
    volstepdatesFx.push_back(Date(15, May, 2017));
    volstepdatesFx.push_back(Date(13, September, 2017)); // shared with USD
    volstepdatesFx.push_back(Date(15, July, 2018)); //  shared with EUR and USD

    std::vector<Real> eurVols, usdVols, fxSigmas;

    for (Size i = 0; i < volstepdatesEur.size() + 1; ++i) {
        eurVols.push_back(0.0050 +
                          (0.0080 - 0.0050) *
                              std::exp(-0.3 * static_cast<double>(i)));
    }
    for (Size i = 0; i < volstepdatesUsd.size() + 1; ++i) {
        usdVols.push_back(0.0030 +
                          (0.0110 - 0.0030) *
                              std::exp(-0.3 * static_cast<double>(i)));
    }
    for (Size i = 0; i < volstepdatesFx.size() + 1; ++i) {
        fxSigmas.push_back(
            0.15 + (0.20 - 0.15) * std::exp(-0.3 * static_cast<double>(i)));
    }

    boost::shared_ptr<Lgm1::model_type> eurLgm =
        boost::make_shared<Lgm1>(eurYts, volstepdatesEur, eurVols, 0.02);
    boost::shared_ptr<Lgm1::model_type> usdLgm =
        boost::make_shared<Lgm1>(usdYts, volstepdatesUsd, usdVols, 0.04);

    std::vector<boost::shared_ptr<Lgm1::model_type> > singleModels;
    singleModels.push_back(eurLgm);
    singleModels.push_back(usdLgm);

    std::vector<Handle<YieldTermStructure> > curves;
    curves.push_back(eurYts);
    curves.push_back(usdYts);

    std::vector<Handle<Quote> > fxSpots;
    fxSpots.push_back(Handle<Quote>(boost::make_shared<SimpleQuote>(
        std::log(0.90)))); // USD per EUR in log scale

    std::vector<std::vector<Real> > fxVolatilities;
    fxVolatilities.push_back(fxSigmas);

    Matrix c(3, 3);
    //  FX             EUR         USD
    c[0][0] = 1.0; c[0][1] = 0.8; c[0][2] = -0.5; // FX
    c[1][0] = 0.8; c[1][1] = 1.0; c[1][2] = -0.2; // EUR
    c[2][0] = -0.5; c[2][1] = -0.2; c[2][2] = 1.0; // USD

    boost::shared_ptr<CcLgm1> ccLgm = boost::make_shared<CcLgm1>(
        singleModels, fxSpots, volstepdatesFx, fxVolatilities, c, curves);

    boost::shared_ptr<StochasticProcess> process = ccLgm->stateProcess();

    boost::shared_ptr<StochasticProcess> usdProcess = usdLgm->stateProcess();

    // path generation

    Size n = 500000; // number of paths
    Size seed = 121; // seed
    // maturity of test payoffs
    Time T = 5.0;
    // take large steps, but not only one (since we are testing)
    Size steps = static_cast<Size>(T * 2.0);
    TimeGrid grid(T, steps);
    PseudoRandom::rsg_type sg =
        PseudoRandom::make_sequence_generator(3 * steps, seed);
    PseudoRandom::rsg_type sg2 =
        PseudoRandom::make_sequence_generator(steps, seed);

    MultiPathGenerator<PseudoRandom::rsg_type> pg(process, grid, sg, false);
    PathGenerator<PseudoRandom::rsg_type> pg2(usdProcess, grid, sg2, false);

    // test
    // 1 deterministic USD cashflow under EUR numeraire vs. price on USD curve
    // 2 zero bond option USD under EUR numeraire vs. USD numeraire
    // 3 fx option USD-EUR under EUR numeraire vs. analytical price

    accumulator_set<double, stats<tag::mean, tag::error_of<tag::mean> > > stat1,
        stat2a, stat2b, stat3;

    // same for paths2 since shared time grid
    for (Size j = 0; j < n; ++j) {
        Sample<MultiPath> path = pg.next();
        Sample<Path> path2 = pg2.next();
        Size l = path.value[0].length() - 1;
        Real fx = std::exp(path.value[0][l]);
        Real zeur = path.value[1][l];
        Real zusd = path.value[2][l];
        Real zusd2 = path2.value[l];
        Real yeur = (zeur - eurLgm->stateProcess()->expectation(0.0, 0.0, T)) /
                    eurLgm->stateProcess()->stdDeviation(0.0, 0.0, T);
        Real yusd = (zusd - usdLgm->stateProcess()->expectation(0.0, 0.0, T)) /
                    usdLgm->stateProcess()->stdDeviation(0.0, 0.0, T);
        Real yusd2 =
            (zusd2 - usdLgm->stateProcess()->expectation(0.0, 0.0, T)) /
            usdLgm->stateProcess()->stdDeviation(0.0, 0.0, T);

        // 1 USD paid at T deflated with EUR numeraire
        stat1(1.0 * fx / eurLgm->numeraire(T, yeur));

        // 2 USD zero bond option at T on P(T,T+10) strike 0.5 ...
        // ... under EUR numeraire ...
        Real zbOpt = std::max(usdLgm->zerobond(T + 10.0, T, yusd) - 0.5, 0.0);
        stat2a(zbOpt * fx / eurLgm->numeraire(T, yeur));
        // ... and under USD numeraire ...
        Real zbOpt2 = std::max(usdLgm->zerobond(T + 10.0, T, yusd2) - 0.5, 0.0);
        stat2b(zbOpt2 / usdLgm->numeraire(T, yusd2));

        // 3 USD-EUR fx option @0.9
        stat3(std::max(fx - 0.9, 0.0) / eurLgm->numeraire(T, yeur));
    }

    boost::shared_ptr<VanillaOption> fxOption =
        boost::make_shared<VanillaOption>(
            boost::make_shared<PlainVanillaPayoff>(Option::Call, 0.9),
            boost::make_shared<EuropeanExercise>(referenceDate + 5 * 365));

    boost::shared_ptr<PricingEngine> ccLgmFxOptionEngine =
        boost::make_shared<CcLgmAnalyticFxOptionEngine<
            CcLgm1::cclgm_model_type, CcLgm1::lgmfx_model_type,
            CcLgm1::lgm_model_type> >(ccLgm, 0);

    fxOption->setPricingEngine(ccLgmFxOptionEngine);

    Real npv1 = mean(stat1);
    Real error1 = error_of<tag::mean>(stat1);
    Real expected1 = usdYts->discount(5.0) * std::exp(fxSpots[0]->value());
    Real npv2a = mean(stat2a);
    Real error2a = error_of<tag::mean>(stat2a);
    Real npv2b = mean(stat2b) * std::exp(fxSpots[0]->value());
    Real error2b = error_of<tag::mean>(stat2b) * std::exp(fxSpots[0]->value());
    Real npv3 = mean(stat3);
    Real error3 = error_of<tag::mean>(stat3);

    // accept this relative difference in error estimates
    Real tolError = 0.2;
    // accept tolErrEst*errorEstimate as absolute difference
    Real tolErrEst = 1.0;

    if (std::fabs((error1 - 4E-4) / 4E-4) > tolError)
        BOOST_ERROR("error estimate deterministic "
                    "cashflow pricing can not be "
                    "reproduced, is "
                    << error1
                    << ", expected 4E-4, relative tolerance "
                    << tolError);
    if (std::fabs((error2a - 1E-4) / 1E-4) > tolError)
        BOOST_ERROR("error estimate zero bond "
                    "option pricing (foreign measure) can "
                    "not be reproduced, is "
                    << error2a
                    << ", expected 1E-4, relative tolerance "
                    << tolError);
    if (std::fabs((error2b - 7E-5) / 7E-5) > tolError)
        BOOST_ERROR("error estimate zero bond "
                    "option pricing (domestic measure) can "
                    "not be reproduced, is "
                    << error2b
                    << ", expected 7E-5, relative tolerance "
                    << tolError);
    if (std::fabs((error3 - 2.7E-4) / 2.7E-4) > tolError)
        BOOST_ERROR(
            "error estimate fx option pricing can not be reproduced, is "
            << error3 << ", expected 2.7E-4, relative tolerance " << tolError);

    if (std::fabs(npv1 - expected1) > tolErrEst * error1)
        BOOST_ERROR("can no reproduce deterministic cashflow pricing, is "
                    << npv1 << ", expected " << expected1 << ", tolerance "
                    << tolErrEst << "*" << error1);

    if (std::fabs(npv2a - npv2b) >
        tolErrEst * std::sqrt(error2a * error2a + error2b * error2b))
        BOOST_ERROR("can no reproduce zero bond option pricing, domestic "
                    "measure result is "
                    << npv2a
                    << ", foreign measure result is "
                    << npv2b
                    << ", tolerance "
                    << tolErrEst
                    << "*"
                    << std::sqrt(error2a * error2a + error2b * error2b));

    if (std::fabs(npv3 - fxOption->NPV()) > tolErrEst * std::sqrt(error3))
        BOOST_ERROR("can no reproduce fx option pricing, monte carlo result is "
                    << npv3 << ", analytical pricing result is "
                    << fxOption->NPV() << ", tolerance is " << tolErrEst << "*"
                    << error3);

} // testLgm3fForeignPayouts

void LgmTest::testLgm4fAndFxCalibration() {
    BOOST_TEST_MESSAGE("Testing LGM4F model and fx calibration...");

    SavedSettings backup;

    Date referenceDate(30, July, 2015);

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

    Handle<YieldTermStructure> eurYts(
        boost::make_shared<FlatForward>(referenceDate, 0.02, Actual365Fixed()));
    Handle<YieldTermStructure> usdYts(
        boost::make_shared<FlatForward>(referenceDate, 0.05, Actual365Fixed()));
    Handle<YieldTermStructure> gbpYts(
        boost::make_shared<FlatForward>(referenceDate, 0.04, Actual365Fixed()));

    std::vector<Date> volstepdates, volstepdatesFx;

    volstepdates.push_back(Date(15, July, 2016));
    volstepdates.push_back(Date(15, July, 2017));
    volstepdates.push_back(Date(15, July, 2018));
    volstepdates.push_back(Date(15, July, 2019));
    volstepdates.push_back(Date(15, July, 2020));

    volstepdatesFx.push_back(Date(15, July, 2016));
    volstepdatesFx.push_back(Date(15, October, 2016));
    volstepdatesFx.push_back(Date(15, May, 2017));
    volstepdatesFx.push_back(Date(13, September, 2017));
    volstepdatesFx.push_back(Date(15, July, 2018));

    std::vector<Real> eurVols, usdVols, gbpVols, fxSigmasUsd, fxSigmasGbp;

    for (Size i = 0; i < volstepdates.size() + 1; ++i) {
        eurVols.push_back(0.0050 +
                          (0.0080 - 0.0050) *
                              std::exp(-0.3 * static_cast<double>(i)));
    }
    for (Size i = 0; i < volstepdates.size() + 1; ++i) {
        usdVols.push_back(0.0030 +
                          (0.0110 - 0.0030) *
                              std::exp(-0.3 * static_cast<double>(i)));
    }
    for (Size i = 0; i < volstepdates.size() + 1; ++i) {
        gbpVols.push_back(0.0070 +
                          (0.0095 - 0.0070) *
                              std::exp(-0.3 * static_cast<double>(i)));
    }
    for (Size i = 0; i < volstepdatesFx.size() + 1; ++i) {
        fxSigmasUsd.push_back(
            0.15 + (0.20 - 0.15) * std::exp(-0.3 * static_cast<double>(i)));
    }
    for (Size i = 0; i < volstepdatesFx.size() + 1; ++i) {
        fxSigmasGbp.push_back(
            0.10 + (0.15 - 0.10) * std::exp(-0.3 * static_cast<double>(i)));
    }

    boost::shared_ptr<Lgm1::model_type> eurLgm =
        boost::make_shared<Lgm1>(eurYts, volstepdates, eurVols, 0.02);
    boost::shared_ptr<Lgm1::model_type> usdLgm =
        boost::make_shared<Lgm1>(usdYts, volstepdates, usdVols, 0.03);
    boost::shared_ptr<Lgm1::model_type> gbpLgm =
        boost::make_shared<Lgm1>(usdYts, volstepdates, gbpVols, 0.04);

    std::vector<boost::shared_ptr<Lgm1::model_type> > singleModels;
    singleModels.push_back(eurLgm);
    singleModels.push_back(usdLgm);
    singleModels.push_back(gbpLgm);

    // we test the 4f model against the 3f model eur-gbp
    std::vector<boost::shared_ptr<Lgm1::model_type> > singleModelsProjected;
    singleModelsProjected.push_back(eurLgm);
    singleModelsProjected.push_back(gbpLgm);

    std::vector<Handle<YieldTermStructure> > curves;
    curves.push_back(eurYts);
    curves.push_back(usdYts);
    curves.push_back(gbpYts);

    std::vector<Handle<YieldTermStructure> > curvesProjected;
    curvesProjected.push_back(eurYts);
    curvesProjected.push_back(gbpYts);

    std::vector<Handle<Quote> > fxSpots;
    fxSpots.push_back(Handle<Quote>(boost::make_shared<SimpleQuote>(
        std::log(0.90)))); // EUR per one unit of USD in log scale
    fxSpots.push_back(Handle<Quote>(boost::make_shared<SimpleQuote>(
        std::log(1.35)))); // EUR per one unit of GBP in log scale

    std::vector<Handle<Quote> > fxSpotsProjected;
    fxSpotsProjected.push_back(fxSpots[1]);

    std::vector<std::vector<Real> > fxVolatilities;
    fxVolatilities.push_back(fxSigmasUsd);
    fxVolatilities.push_back(fxSigmasGbp);

    std::vector<std::vector<Real> > fxVolatilitiesProjected;
    fxVolatilitiesProjected.push_back(fxSigmasGbp);

    Matrix c(5, 5);
    //  FX USD-EUR      FX GBP-EUR     EUR           USD             GBP
    c[0][0] = 1.0;  c[0][1]= 0.3;   c[0][2] = 0.2; c[0][3] = -0.2; c[0][4]=0.0;  // FX USD-EUR
    c[1][0] = 0.3;  c[1][1]= 1.0;   c[1][2] = 0.3; c[1][3] = -0.1; c[1][4]=0.1;  // FX GBP-EUR
    c[2][0] = 0.2;  c[2][1]= 0.3;   c[2][2] = 1.0; c[2][3] = 0.6;  c[2][4]=0.3;  // EUR
    c[3][0] = -0.2; c[3][1]=-0.1;   c[3][2] = 0.6; c[3][3] = 1.0;  c[3][4]=0.1;  // USD
    c[4][0] = 0.0;  c[4][1]= 0.1;   c[4][2] = 0.3; c[4][3] = 0.1;  c[4][4]=1.0;  // GBP

    Matrix cProjected(3, 3);
    for (Size i = 0, ii = 0; i < 5; ++i) {
        if (i != 0 && i != 3) {
            for (Size j = 0, jj = 0; j < 5; ++j) {
                if (j != 0 && j != 3)
                    cProjected[ii][jj++] = c[i][j];
            }
            ++ii;
        }
    }

    boost::shared_ptr<CcLgm1> ccLgm = boost::make_shared<CcLgm1>(
        singleModels, fxSpots, volstepdatesFx, fxVolatilities, c, curves);

    boost::shared_ptr<CcLgm1> ccLgmProjected = boost::make_shared<CcLgm1>(
        singleModelsProjected, fxSpotsProjected, volstepdatesFx,
        fxVolatilitiesProjected, cProjected, curvesProjected);

    boost::shared_ptr<PricingEngine> ccLgmFxOptionEngineUsd =
        boost::make_shared<CcLgmAnalyticFxOptionEngine<
            CcLgm1::cclgm_model_type, CcLgm1::lgmfx_model_type,
            CcLgm1::lgm_model_type> >(ccLgm, 0);

    boost::shared_ptr<PricingEngine> ccLgmFxOptionEngineGbp =
        boost::make_shared<CcLgmAnalyticFxOptionEngine<
            CcLgm1::cclgm_model_type, CcLgm1::lgmfx_model_type,
            CcLgm1::lgm_model_type> >(ccLgm, 1);

    boost::shared_ptr<PricingEngine> ccLgmProjectedFxOptionEngineGbp =
        boost::make_shared<CcLgmAnalyticFxOptionEngine<
            CcLgm1::cclgm_model_type, CcLgm1::lgmfx_model_type,
            CcLgm1::lgm_model_type> >(ccLgmProjected, 0);

    // while the initial fx vol starts at 0.2 for usd and 0.15 for gbp
    // we calibrate to helpers with 0.15 and 0.2 target implied vol
    std::vector<boost::shared_ptr<CalibrationHelper> > helpersUsd, helpersGbp;
    for (Size i = 0; i <= volstepdatesFx.size(); ++i) {
        boost::shared_ptr<CalibrationHelper> tmpUsd =
            boost::make_shared<FxOptionHelper>(
                i < volstepdatesFx.size() ? volstepdatesFx[i]
                                          : volstepdatesFx.back() + 365,
                0.90, Handle<Quote>(boost::make_shared<SimpleQuote>(
                          std::exp(fxSpots[0]->value()))),
                Handle<Quote>(boost::make_shared<SimpleQuote>(0.15)),
                ccLgm->termStructure(0), ccLgm->termStructure(1));
        boost::shared_ptr<CalibrationHelper> tmpGbp =
            boost::make_shared<FxOptionHelper>(
                i < volstepdatesFx.size() ? volstepdatesFx[i]
                                          : volstepdatesFx.back() + 365,
                1.35, Handle<Quote>(boost::make_shared<SimpleQuote>(
                          std::exp(fxSpots[1]->value()))),
                Handle<Quote>(boost::make_shared<SimpleQuote>(0.20)),
                ccLgm->termStructure(0), ccLgm->termStructure(2));
        tmpUsd->setPricingEngine(ccLgmFxOptionEngineUsd);
        tmpGbp->setPricingEngine(ccLgmFxOptionEngineGbp);
        helpersUsd.push_back(tmpUsd);
        helpersGbp.push_back(tmpGbp);
    }

    LevenbergMarquardt lm(1E-8, 1E-8, 1E-8);
    EndCriteria ec(1000, 500, 1E-8, 1E-8, 1E-8);

    // calibrate USD-EUR FX volatility
    ccLgm->calibrateFxVolatilitiesIterative(0, helpersUsd, lm, ec);
    // calibrate GBP-EUR FX volatility
    ccLgm->calibrateFxVolatilitiesIterative(1, helpersGbp, lm, ec);

    Real tol = 1E-6;
    for (Size i = 0; i < helpersUsd.size(); ++i) {
        Real market = helpersUsd[i]->marketValue();
        Real model = helpersUsd[i]->modelValue();
        Real calibratedVol = ccLgm->fxVolatility(0)[i];
        if (std::fabs(market - model) > tol)
            BOOST_ERROR("calibration for fx option helper #"
                        << i << " (USD) failed, market premium is " << market
                        << " while model premium is " << model);
        // the stochastic rates produce some noise, but do not have a huge
        // impact on the effective volatility, so we check that they are
        // in line with a cached example (note that the analytic fx option
        // pricing engine was checked against MC in another test case)
        if (std::fabs(calibratedVol - 0.143) > 0.01)
            BOOST_ERROR(
                "calibrated fx volatility #"
                << i
                << " (USD) seems off, expected to be 0.143 +- 0.01, but is "
                << calibratedVol);
    }
    for (Size i = 0; i < helpersGbp.size(); ++i) {
        Real market = helpersGbp[i]->marketValue();
        Real model = helpersGbp[i]->modelValue();
        Real calibratedVol = ccLgm->fxVolatility(1)[i];
        if (std::fabs(market - model) > tol)
            BOOST_ERROR("calibration for fx option helper #"
                        << i << " (GBP) failed, market premium is " << market
                        << " while model premium is " << model);
        // see above
        if (std::fabs(calibratedVol - 0.193) > 0.01)
            BOOST_ERROR(
                "calibrated fx volatility #"
                << i << " (USD) seems off, expected to be 0.193 +- 0.01, but is "
                << calibratedVol);
    }

    // calibrate the projected model

    for (Size i = 0; i < helpersGbp.size(); ++i) {
        helpersGbp[i]->setPricingEngine(ccLgmProjectedFxOptionEngineGbp);
    }

    ccLgmProjected->calibrateFxVolatilitiesIterative(0, helpersGbp, lm, ec);

    for (Size i = 0; i < helpersGbp.size(); ++i) {
        Real fullModelVol = ccLgm->fxVolatility(1)[i];
        Real projectedModelVol = ccLgmProjected->fxVolatility(0)[i];
        if (std::fabs(fullModelVol - projectedModelVol) > tol)
            BOOST_ERROR(
                "calibrated fx volatility of full model @"
                << i << " (" << fullModelVol
                << ") is inconsistent with that of the projected model ("
                << projectedModelVol << ")");
    }

} // testLgm4fAndFxCalibration

test_suite *LgmTest::suite() {
    test_suite *suite = BOOST_TEST_SUITE("LGM model tests");
    suite->add(QUANTLIB_TEST_CASE(&LgmTest::testBermudanLgm1fGsr));
    suite->add(QUANTLIB_TEST_CASE(&LgmTest::testLgm1fCalibration));
    suite->add(QUANTLIB_TEST_CASE(&LgmTest::testLgm3fForeignPayouts));
    suite->add(QUANTLIB_TEST_CASE(&LgmTest::testLgm4fAndFxCalibration));
    return suite;
}


#include <CGAL/boost/graph/graph_traits_Surface_mesh.h>
#include <CGAL/Polygon_mesh_processing/remesh.h>
#include <CGAL/Polygon_mesh_processing/border.h>
#include <boost/function_output_iterator.hpp>
#include "booleanmesh.h"
#include "isotropicremesh.h"

typedef boost::graph_traits<CgalMesh>::halfedge_descriptor halfedge_descriptor;
typedef boost::graph_traits<CgalMesh>::edge_descriptor     edge_descriptor;
namespace PMP = CGAL::Polygon_mesh_processing;

struct halfedge2edge
{
    halfedge2edge(const CgalMesh& m, std::vector<edge_descriptor>& edges)
        : m_mesh(m), m_edges(edges)
    {}
    void operator()(const halfedge_descriptor& h) const
    {
        m_edges.push_back(edge(h, m_mesh));
    }
    const CgalMesh& m_mesh;
    std::vector<edge_descriptor>& m_edges;
};

void isotropicRemesh(const std::vector<QVector3D> &inputVertices,
        std::vector<std::vector<size_t>> &inputTriangles,
        std::vector<QVector3D> &outputVertices,
        std::vector<std::vector<size_t>> &outputTriangles,
        float targetEdgeLength,
        unsigned int iterationNum)
{
    CgalMesh *mesh = buildCgalMesh<CgalKernel>(inputVertices, inputTriangles);
    if (nullptr == mesh)
        return;

    std::vector<edge_descriptor> border;
    PMP::border_halfedges(faces(*mesh),
        *mesh,
        boost::make_function_output_iterator(halfedge2edge(*mesh, border)));
    PMP::split_long_edges(border, targetEdgeLength, *mesh);

    PMP::isotropic_remeshing(faces(*mesh),
        targetEdgeLength,
        *mesh,
        PMP::parameters::number_of_iterations(iterationNum)
        .protect_constraints(true));

    outputVertices.clear();
    outputTriangles.clear();
    fetchFromCgalMesh<CgalKernel>(mesh, outputVertices, outputTriangles);

    delete mesh;
}


//==================================================================================================
/*!
  @file
  @copyright 2016 NumScale SAS
  @copyright 2016 J.T. Lapreste

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

#if defined(DOXYGEN_ONLY)
namespace boost { namespace simd
{

 /*!
  @ingroup group-euler
    Function object implementing gamma capabilities

   Computes the gamma function:
   \f$\displaystyle \int_0^{\infty} t^{x-1}e^{-t}\mbox{d}t\f$

    @par Semantic:

    For every parameter of floating type T

    @code
    T r = gamma(x);
    @endcode

    @see gammaln

  **/
  const boost::dispatch::functor<tag::gamma_> gamma = {};
} }
#endif

#include <boost/simd/function/scalar/gamma.hpp>
//#include <boost/simd/function/simd/gamma.hpp>

#endif