/*******************************************************************************
 * Reverse Polish Notation calculator.                                         *
 * Copyright (c) 2007-2009, Samuel Fredrickson <kinghajj@gmail.com>            *
 * All rights reserved.                                                        *
 *                                                                             *
 * Redistribution and use in source and binary forms, with or without          *
 * modification, are permitted provided that the following conditions are met: *
 *     * Redistributions of source code must retain the above copyright        *
 *       notice, this list of conditions and the following disclaimer.         *
 *     * Redistributions in binary form must reproduce the above copyright     *
 *       notice, this list of conditions and the following disclaimer in the   *
 *       documentation and/or other materials provided with the distribution.  *
 *                                                                             *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS *
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED           *
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE      *
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY        *
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES  *
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR          *
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER  *
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT          *
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY   *
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH *
 * DAMAGE.                                                                     *
 ******************************************************************************/

/*******************************************************************************
 * Commands.cpp - command Calculator methods.                                  *
 ******************************************************************************/

#include "rpn.h"
#include <boost/foreach.hpp>
#include <cmath>
using namespace std;
using namespace RPN;

#ifndef DOXYGEN_SKIP

template <class T>
static void printAnything(T t)
{
    Print(t);
}

template <class T>
static void printAnythingDetailed(T t)
{
    PrintDetailed(t);
}

template <class T>
static void printAnyList(list<T>& l, void (*printer)(T))
{
    Print("[ ");
    BOOST_FOREACH(T& item, l)
    {
        printer(item);
        Print(", ");
    }
    Print(']');
}

static void printValue(Value v)
{
    printAnything(v);
}

static void printValueDetailed(Value v)
{
    printAnythingDetailed(v);
}

void Calculator::dup(vector<string>&)
{
    if(HasStack() && StackSize() > 0)
    {
        Stack& stack(CurrentStack());
        stack.push_front(stack.front());
    }
}

void Calculator::exit(vector<string>&)
{
    status = Stop;
}

void Calculator::pop(vector<string>&)
{
    if(HasStack() && StackSize() > 0)
        CurrentStack().pop_front();
}

void Calculator::popHistory(vector<string>&)
{
    if(history.size() > 1)
        history.pop_front();
}

void Calculator::printHelp(vector<string>&)
{
    printHelpItems(helpItems);
}

void Calculator::printHistoryGeneric(void (*printer)(Value))
{
    Print('[');
    BOOST_FOREACH(Stack& item, history)
    {
        printAnyList(item, printer);
        Print(", ");
    }
    Print("]\n");
}

void Calculator::printHistory(vector<string>&)
{
    printHistoryGeneric(printValue);
}

void Calculator::printHistoryDetailed(vector<string>&)
{
    printHistoryGeneric(printValueDetailed);
}

void Calculator::printStack(vector<string>&)
{
    if(HasStack())
    {
        printAnyList(CurrentStack(), printValue);
        Print('\n');
    }
}

void Calculator::printStackDetailed(vector<string>&)
{
    if(HasStack())
    {
        printAnyList(CurrentStack(), printValueDetailed);
        Print('\n');
    }
}

void Calculator::printVariablesGeneric(void (*printer)(Value))
{
    Print("[ ");
    BOOST_FOREACH(Variables::value_type& v, variables)
    {
        Print(v.first);
        Print(" = ");
        printer(v.second);
        Print(", ");
    }
    Print("]\n");
}

void Calculator::printVariables(vector<string>&)
{
    printVariablesGeneric(printValue);
}

void Calculator::printVariablesDetailed(vector<string>&)
{
    printVariablesGeneric(printValueDetailed);
}

void Calculator::printVersion(vector<string>&)
{
    Port::Print("RPN %i.%i.%i.%i", VERSION_MAJOR, VERSION_MINOR,
                                   VERSION_REVIS, VERSION_BUILD);
    Port::Print(" (%s) ", getVersion());

#ifdef __GNUC__
    Port::Print("(GCC %i.%i.%i on %s at %s)", __GNUC__,
	                                          __GNUC_MINOR__,
	                                          __GNUC_PATCHLEVEL__,
	                                          __DATE__, __TIME__);
#endif
    Print("\nBy Sam Fredrickson <kinghajj@gmail.com>\n");
}

void Calculator::pushHistory(vector<string>&)
{
    if(HasStack())
        history.push_front(CurrentStack());
}

void Calculator::sqrtTop(vector<string>&)
{
    if(HasStack() && StackSize() > 0)
    {
        Stack& stack = CurrentStack();
        Value top = stack.front();
        stack.pop_front();
#ifdef RPN_DOUBLE
        stack.push_front(sqrt(top));
#elif  RPN_LONG_DOUBLE
        stack.push_front(sqrtl(top));
#endif
    }
}

void Calculator::swap(vector<string>&)
{
    if(HasStack() && StackSize() > 1)
    {
        Stack& stack = CurrentStack();
        Value a, b;
        a = stack.front();
        stack.pop_front();
        b = stack.front();
        stack.pop_front();
        stack.push_front(a);
        stack.push_front(b);
    }
}

void Calculator::unset(vector<string>& args)
{
    variables.erase(args.front());
}

#endif

Commands Calculator::defaultCommands()
{
    Commands ret;

    ret["dup"]   = Command(&Calculator::dup);
    ret["help"]  = Command(&Calculator::printHelp);
    ret["pop"]   = Command(&Calculator::pop);
    ret["poph"]  = Command(&Calculator::popHistory);
    ret["ph"]    = Command(&Calculator::printHistory);
    ret["phd"]   = Command(&Calculator::printHistoryDetailed);
    ret["ps"]    = Command(&Calculator::printStack);
    ret["psd"]   = Command(&Calculator::printStackDetailed);
    ret["pv"]    = Command(&Calculator::printVariables);
    ret["pvd"]   = Command(&Calculator::printVariablesDetailed);
    ret["pushh"] = Command(&Calculator::pushHistory);
    ret["sqrt"]  = Command(&Calculator::sqrtTop);
    ret["swap"]  = Command(&Calculator::swap);
    ret["unset"] = Command(&Calculator::unset, 1);
    ret["ver"]   = Command(&Calculator::printVersion);
    ret["x"]     = Command(&Calculator::exit);

    return ret;
}


/*!
@file
Defines `boost::hana::intersperse`.

@copyright Louis Dionne 2013-2017
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_HANA_INTERSPERSE_HPP
#define BOOST_HANA_INTERSPERSE_HPP

#include <boost/hana/fwd/intersperse.hpp>

#include <boost/hana/at.hpp>
#include <boost/hana/bool.hpp>
#include <boost/hana/concept/sequence.hpp>
#include <boost/hana/config.hpp>
#include <boost/hana/core/dispatch.hpp>
#include <boost/hana/core/make.hpp>
#include <boost/hana/length.hpp>

#include <cstddef>
#include <utility>

BOOST_HANA_NAMESPACE_BEGIN
    //! @cond
    template <typename Xs, typename Z>
    constexpr auto intersperse_t::operator()(Xs&& xs, Z&& z) const {
        using S = typename hana::tag_of<Xs>::type;
        using Intersperse = BOOST_HANA_DISPATCH_IF(intersperse_impl<S>,
            hana::Sequence<S>::value
        );

    #ifndef BOOST_HANA_CONFIG_DISABLE_CONCEPT_CHECKS
        static_assert(hana::Sequence<S>::value,
        "hana::intersperse(xs, z) requires 'xs' to be a Sequence");
    #endif

        return Intersperse::apply(static_cast<Xs&&>(xs), static_cast<Z&&>(z));
    }
    //! @endcond

    template <typename S, bool condition>
    struct intersperse_impl<S, when<condition>> : default_ {
        template <std::size_t i, typename Xs, typename Z>
        static constexpr decltype(auto)
        pick(Xs&&, Z&& z, hana::false_ /* odd index */)
        { return static_cast<Z&&>(z); }

        template <std::size_t i, typename Xs, typename Z>
        static constexpr decltype(auto)
        pick(Xs&& xs, Z&&, hana::true_ /* even index */)
        { return hana::at_c<(i + 1) / 2>(static_cast<Xs&&>(xs)); }

        template <typename Xs, typename Z, std::size_t ...i>
        static constexpr auto
        intersperse_helper(Xs&& xs, Z&& z, std::index_sequence<i...>) {
            return hana::make<S>(
                pick<i>(static_cast<Xs&&>(xs), static_cast<Z&&>(z),
                        hana::bool_c<(i % 2 == 0)>)...
            );
        }

        template <typename Xs, typename Z>
        static constexpr auto apply(Xs&& xs, Z&& z) {
            constexpr std::size_t size = decltype(hana::length(xs))::value;
            constexpr std::size_t new_size = size == 0 ? 0 : (size * 2) - 1;
            return intersperse_helper(static_cast<Xs&&>(xs), static_cast<Z&&>(z),
                                      std::make_index_sequence<new_size>{});
        }
    };
BOOST_HANA_NAMESPACE_END

#endif // !BOOST_HANA_INTERSPERSE_HPP


//==================================================================================================
/*!

  Copyright 2016 NumScale SAS

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

#define STF_CUSTOM_DRIVER_FUNCTION simd_test
#include <stf.hpp>

#include <boost/simd/pack.hpp>

// -------------------------------------------------------------------------------------------------
// adapt pack for STF testing
namespace stf { namespace ext
{
  // -----------------------------------------------------------------------------------------------
  // STF conformance for pack
  template<typename T, std::size_t N, typename A>
  struct equal<boost::simd::pack<T,N,A>,boost::simd::pack<T,N,A>>
  {
    using type_t = boost::simd::pack<T,N,A>;
    inline bool operator()(type_t const& l, type_t const& r) const
    {
      for(std::size_t i = 0; i < N; ++i)
        if(l[i] != r[i]) return false;
      return true;
    }
  };

  template<typename T, std::size_t N, typename A>
  struct ulpdist<boost::simd::pack<T,N,A>,boost::simd::pack<T,N,A>>
  {
    using type_t = boost::simd::pack<T,N,A>;
    inline double operator()(type_t const& l, type_t const& r) const
    {
      double max_ulp = 0;
      for(std::size_t i = 0; i < N; ++i)
        max_ulp = std::max( max_ulp, stf::ulpdist(T(l[i]),T(r[i])) );

      return max_ulp;
    }
  };

  template<typename T, std::size_t N, typename A>
  struct reldist<boost::simd::pack<T,N,A>,boost::simd::pack<T,N,A>>
  {
    using type_t = boost::simd::pack<T,N,A>;
    inline double operator()(type_t const& l, type_t const& r) const
    {
      double max_rel = 0;
      for(std::size_t i = 0; i < N; ++i)
        max_rel = std::max( max_rel, stf::reldist(T(l[i]),T(r[i])) );

      return max_rel;
    }
  };

  // -----------------------------------------------------------------------------------------------
  // STF conformance for pack_proxy
  template<typename S, typename T>
  struct equal<boost::simd::detail::pack_proxy<S>,T>
  {
    using type_t  = boost::simd::detail::pack_proxy<S>;
    using value_t = typename type_t::value_type;
    inline double operator()(type_t const& l, T const& r) const
    {
      return value_t(l) == r;
    }
  };

  template<typename S, typename T>
  struct equal<T,boost::simd::detail::pack_proxy<S>>
  {
    using type_t  = boost::simd::detail::pack_proxy<S>;
    using value_t = typename type_t::value_type;
    inline double operator()(T const& l, type_t const& r) const
    {
      return l == value_t(r);
    }
  };

  template<typename S>
  struct reldist<boost::simd::detail::pack_proxy<S>,boost::simd::detail::pack_proxy<S>>
  {
    using type_t  = boost::simd::detail::pack_proxy<S>;
    using value_t = typename type_t::value_type;
    inline double operator()(type_t const& l, type_t const& r) const
    {
      return stf::reldist(value_t(l),value_t(r));
    }
  };

  template<typename S>
  struct ulpdist<boost::simd::detail::pack_proxy<S>,boost::simd::detail::pack_proxy<S>>
  {
    using type_t  = boost::simd::detail::pack_proxy<S>;
    using value_t = typename type_t::value_type;
    inline double operator()(type_t const& l, type_t const& r) const
    {
      return stf::ulpdist(value_t(l),value_t(r));
    }
  };

  // -----------------------------------------------------------------------------------------------
  // STF conformance for logical
  template<typename T>
  struct reldist<boost::simd::logical<T>,boost::simd::logical<T>>
  {
    using type_t  = boost::simd::logical<T>;
    inline double operator()(type_t const& l, type_t const& r) const
    {
      return stf::reldist(bool(l),bool(r));
    }
  };

  template<typename T>
  struct ulpdist<boost::simd::logical<T>,boost::simd::logical<T>>
  {
    using type_t  = boost::simd::logical<T>;
    inline double operator()(type_t const& l, type_t const& r) const
    {
      return stf::ulpdist(bool(l),bool(r));
    }
  };
} }

// -------------------------------------------------------------------------------------------------
// Test driver entry point
int main(int argc, const char** argv)
{
  std::cout << "CTEST_FULL_OUTPUT\n";
  std::cout << "Compiled for: "
            << stf::type_id<BOOST_DISPATCH_DEFAULT_SITE>()
            << "\n";
  return simd_test(argc,argv);
}

#endif


// Copyright Abel Sinkovics (abel@sinkovics.hu) 2011.
// Distributed under the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE_1_0.txt or copy at
//          http://www.boost.org/LICENSE_1_0.txt)

#include <boost/metaparse/int_.hpp>
#include <boost/metaparse/is_error.hpp>
#include <boost/metaparse/start.hpp>
#include <boost/metaparse/get_result.hpp>

#include "common.hpp"

#include <boost/mpl/equal_to.hpp>
#include <boost/mpl/apply_wrap.hpp>
#include <boost/mpl/assert.hpp>

#include "test_case.hpp"

BOOST_METAPARSE_TEST_CASE(int)
{
  using boost::metaparse::is_error;
  using boost::metaparse::int_;
  using boost::metaparse::start;
  using boost::metaparse::get_result;

  using boost::mpl::apply_wrap2;
  using boost::mpl::equal_to;

  // test_with_text
  BOOST_MPL_ASSERT((is_error<apply_wrap2<int_, str_hello, start> >));

  // test_with_zero
  BOOST_MPL_ASSERT((
    equal_to<get_result<apply_wrap2<int_, str_0, start> >::type, int0>
  ));

  // test_with_one_digit
  BOOST_MPL_ASSERT((
    equal_to<get_result<apply_wrap2<int_, str_1, start> >::type, int1>
  ));

  // test_with_big_number
  BOOST_MPL_ASSERT((
    equal_to<
      get_result<apply_wrap2<int_, str_1983, start> >::type,
      boost::mpl::int_<1983>
    >
  ));

  // test_with_empty_string
  BOOST_MPL_ASSERT((is_error<apply_wrap2<int_, str_, start> >));
}


//==============================================================================
//         Copyright 2003 - 2012   LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2012   LRI    UMR 8623 CNRS/Univ Paris Sud XI
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
#include <nt2/include/functions/quadgk.hpp>
#include <nt2/integration/output.hpp>
#include <nt2/integration/options.hpp>
#include <nt2/integration/int_transforms.hpp>

#include <nt2/include/functions/sqr.hpp>
#include <nt2/include/functions/rowvect.hpp>
#include <nt2/include/functions/vertcat.hpp>
#include <nt2/include/functions/linspace.hpp>
#include <nt2/include/functions/exp.hpp>
#include <nt2/include/functions/sin.hpp>
#include <nt2/include/functions/if_else_zero.hpp>
#include <nt2/include/functions/rec.hpp>
#include <nt2/include/functions/log.hpp>
#include <nt2/include/functions/exp.hpp>
#include <nt2/include/functions/oneminus.hpp>
#include <nt2/include/functions/expm1.hpp>
#include <nt2/include/functions/sqr.hpp>
#include <nt2/include/functions/zeros.hpp>
#include <nt2/include/functions/globalsum.hpp>
#include <nt2/include/functions/globalmax.hpp>
#include <nt2/include/functions/mul_i.hpp>
#include <nt2/include/functions/linspace.hpp>
#include <nt2/include/functions/dist.hpp>
#include <nt2/include/functions/rec.hpp>
#include <nt2/include/functions/minusone.hpp>
#include <nt2/include/functions/oneminus.hpp>
#include <nt2/include/functions/horzcat.hpp>
#include <nt2/include/functions/ones.hpp>
#include <nt2/include/functions/fliplr.hpp>
#include <nt2/include/functions/real.hpp>
#include <nt2/include/functions/cos.hpp>
#include <nt2/include/constants/half.hpp>
#include <nt2/include/constants/eps.hpp>
#include <nt2/include/constants/sqrteps.hpp>
#include <nt2/include/constants/one.hpp>
#include <nt2/include/constants/two.hpp>
#include <nt2/include/constants/pi.hpp>
#include <nt2/include/constants/i.hpp>
#include <nt2/include/constants/sqrteps.hpp>
#include <nt2/include/constants/pio_2.hpp>
#include <nt2/table.hpp>
#include <boost/dispatch/meta/strip.hpp>

#include <nt2/sdk/unit/module.hpp>
#include <nt2/sdk/unit/tests.hpp>
#include <nt2/sdk/unit/tests/relation.hpp>

struct f1
{
  template<class Sig>
  struct result;

  template<class This, class X>
  struct result<This(X)>
       : boost::dispatch::meta::strip<X>
  {
  };

  template < class X > inline
  X operator()(const X & x ) const
  {
    return nt2::sin(x);
  }
};
struct f
{
  template<class Sig>
  struct result;

  template<class This, class X>
  struct result<This(X)>
       : nt2::meta::as_real< typename boost::dispatch::meta::strip<X>::type >
  {
  };

  template < class X > inline
  typename nt2::meta::as_real<X>::type
  operator()(const X & x ) const
  {
    return nt2::real(nt2::sin(x));
  }

};

struct ff
{
  template<class Sig>
  struct result;

  template<class This, class X>
  struct result<This(X)>
       : boost::dispatch::meta::strip<X>
  {
  };

  template < class X > inline
  X operator()(const X & x ) const
  {
    return nt2::exp(-sqr(x))*nt2::sqr(nt2::log(x));
  }
};
struct g
{
  template<class Sig>
  struct result;

  template<class This, class X>
  struct result<This(X)>
       : boost::dispatch::meta::strip<X>
  {
  };

  template < class X > inline
  X operator()(const X & x ) const
  {
    return nt2::oneminus(x)/nt2::log(x);
  }
};

struct h
{
  template<class Sig>
  struct result;

  template<class This, class X>
  struct result<This(X)>
       : nt2::meta::as_complex<typename boost::dispatch::meta::strip<X>::type>
  {
  };

  template < class X> inline
  typename nt2::meta::as_complex<X>::type
  operator()(const X & x ) const
  {
    return nt2::exp(nt2::mul_i(x));
  }
};
struct k
{
  template<class Sig>
  struct result;

  template<class This, class X>
  struct result<This(X)>
       : boost::dispatch::meta::strip<X>
  {
  };

  template < class X>
  X  operator()(const X & x ) const
  {
    return nt2::rec(nt2::minusone(x+x));
  }
};
struct l
{
  template<class Sig>
  struct result;

  template<class This, class X>
  struct result<This(X)>
       : boost::dispatch::meta::strip<X>
  {
  };

  template < class X> inline
  X operator()(const X & x ) const
  {
    return nt2::One<double >()+nt2::exp(nt2::mul_i(x));
  }
};
struct m
{
  template<class Sig>
  struct result;

  template<class This, class X>
  struct result<This(X)>
       : boost::dispatch::meta::strip<X>
  {
  };

  template < class X> inline
  X operator()(const X & x ) const
  {
    return nt2::exp(-x);
  }
};
struct n
{
  template<class Sig>
  struct result;

  template<class This, class X>
  struct result<This(X)>
       : boost::dispatch::meta::strip<X>
  {
  };

  template < class X> inline
  X operator()(const X & x ) const
  {
    return nt2::exp(x);
  }
};

struct o
{
  template<class Sig>
  struct result;

  template<class This, class X>
  struct result<This(X)>
       : boost::dispatch::meta::strip<X>
  {
  };

  template < class X> inline
  X operator()(const X & x ) const
  {
    typedef typename nt2::meta::scalar_of<X>::type S;
    return nt2::exp(-x*x)*nt2::Two<S>()/nt2::sqrt(nt2::Pi<S>());
  }
};

NT2_TEST_CASE_TPL( quadgk_functor__, NT2_REAL_TYPES )
{
  using nt2::quadgk;
  using nt2::options;
  using nt2::integration::output;
  typedef typename nt2::meta::as_complex<T>::type cT;

  BOOST_AUTO_TPL(res, (quadgk(f(), cT(0), cT(nt2::Pi<T>()))));
  NT2_TEST(res.successful);
}

NT2_TEST_CASE_TPL( quadgk_functor_fina_infb, NT2_REAL_TYPES )
{
  using nt2::quadgk;
  using nt2::options;
  using nt2::integration::output;

  BOOST_AUTO_TPL(res, (quadgk(m(), T(0), nt2::Inf<T>())));
  NT2_TEST(res.successful);
}
NT2_TEST_CASE_TPL( quadgk_functor_infa_finb, NT2_REAL_TYPES )
{
  using nt2::quadgk;
  using nt2::options;
  using nt2::integration::output;

  BOOST_AUTO_TPL(res, (quadgk(n(), nt2::Minf<T>(), T(0))));
  NT2_TEST(res.successful);
}
NT2_TEST_CASE_TPL( quadgk_functor_infa_infb, NT2_REAL_TYPES )
{
  using nt2::quadgk;
  using nt2::options;
  using nt2::integration::output;

  BOOST_AUTO_TPL(res, (quadgk(o(), nt2::Minf<T>(), nt2::Inf<T>())));
  NT2_TEST(res.successful);
}

NT2_TEST_CASE_TPL( quadgk_functor_r, NT2_REAL_TYPES )
{
  using nt2::quadgk;
  using nt2::options;
  using nt2::integration::output;
  BOOST_AUTO_TPL(res, (quadgk(f1(), T(0), nt2::Pi<T>()))); //, options [nt2::range::waypoints_ = nt2::linspace(T(0), T(1), 5)])));
  NT2_TEST(res.successful);
}
NT2_TEST_CASE_TPL( quadgk_cplx_out, NT2_REAL_TYPES )
{
  using nt2::quadgk;
  using nt2::options;
  using nt2::integration::output;
  typedef nt2::table<T> tab_t;
  tab_t x =  nt2::linspace(-nt2::Pio_2<T>(), nt2::Pio_2<T>(), 2);
  BOOST_AUTO_TPL(res, (quadgk(h(), nt2::Pio_2<T>(), -nt2::Pio_2<T>())));
  NT2_TEST(res.successful);
}

NT2_TEST_CASE_TPL( quadgk_cplx_inout, NT2_REAL_TYPES)
{
  using nt2::quadgk;
  using nt2::options;
  using nt2::integration::output;
  typedef typename nt2::meta::as_complex<T>::type cT;
  typedef nt2::table<cT> tab_t;
  cT cx[] = {std::complex<T>(1, 1),std::complex<T>(1, -1)};
  tab_t x(nt2::of_size(1, 2), &cx[0], &cx[2]);
  BOOST_AUTO_TPL(res, (quadgk(k(), cT(0, 0), cT(0, 0), options[nt2::range::waypoints_ =x])));
  NT2_TEST(res.successful);
}

NT2_TEST_CASE_TPL( quadgk_cplx_inout1, (double))//NT2_REAL_TYPES )
{
  using nt2::quadgk;
  using nt2::options;
  using nt2::integration::output;
  typedef typename nt2::meta::as_complex<T>::type cT;
  typedef nt2::table<cT> tab_t;
  cT cx[] = {std::complex<T>(1, 1),std::complex<T>(1, -1)};
  tab_t x(nt2::of_size(1, 2), &cx[0], &cx[2]);
  BOOST_AUTO_TPL(res, (quadgk(l(), cT(0, -1), cT(0, 1)))); //, options[nt2::range::waypoints_ =x])));
  NT2_TEST(res.successful);
}

NT2_TEST_CASE_TPL( quadgk_cplx_inout2, NT2_REAL_TYPES )
{
  using nt2::quadgk;
  using nt2::options;
  using nt2::integration::output;
  typedef typename nt2::meta::as_complex<T>::type cT;
  typedef nt2::table<cT> tab_t;
  cT cx[] = { std::complex<T>(0, 0), std::complex<T>(1, 1),std::complex<T>(1, -1),std::complex<T>(0, 0)};
  tab_t x(nt2::of_size(1, 4), &cx[0], &cx[4]);
  BOOST_AUTO_TPL(res, (quadgk(k(), std::complex<T>(0, 0), std::complex<T>(0, 0),
                                      options[nt2::range::waypoints_ =x
                                        ] )));
  NT2_TEST(res.successful);
}

NT2_TEST_CASE_TPL( quadgk_functor_cplx, NT2_REAL_TYPES )
{
  using nt2::quadgk;
  using nt2::options;
  using nt2::integration::output;
  typedef typename nt2::meta::as_complex<T>::type cT;
  typedef nt2::table<cT> tab_t ;
  cT cx[] = { cT(1, 1),cT(1, -1)};
  tab_t x(nt2::of_size(1, 2), &cx[0], &cx[2]);

  //output<tab_t,T>
  BOOST_AUTO_TPL(res, (quadgk(f1(), cT(0), cT(0), options [nt2::range::waypoints_ = x])));

  NT2_TEST(res.successful);
}

NT2_TEST_CASE_TPL( quadgk_functorb, NT2_REAL_TYPES )
{
  using nt2::quadgk;
  using nt2::options;
  using nt2::integration::output;
  BOOST_AUTO_TPL(res, (quadgk(f1(), T(0), T(1), options [ nt2::tolerance::abstol_ = T(1.0e-5)])));
  NT2_TEST_LESSER_EQUAL(nt2::dist(res.integrals(nt2::end_), nt2::oneminus(nt2::cos(T(1)))), T(1.0e-5));
  NT2_TEST(res.successful);
}

NT2_TEST_CASE_TPL( quadgk_functor0, NT2_REAL_TYPES )
{
  using nt2::quadgk;
  using nt2::options;
  using nt2::integration::output;
  typedef nt2::table<T> tab_t;
  tab_t x = nt2::_(T(0), T(1), T(1));
  output<tab_t,T> res =  (quadgk(g(), x, options [ nt2::tolerance::abstol_ = T(1.0e-6)]));
  NT2_TEST_LESSER_EQUAL(nt2::dist(res.integrals(nt2::end_), -nt2::log(T(2))), T(1.0e-6));
  NT2_TEST(res.successful);

}

NT2_TEST_CASE_TPL( quadgk_tag, NT2_REAL_TYPES )
{
  using nt2::quadgk;
  using nt2::options;
  using nt2::integration::output;
  typedef nt2::table<T> tab_t;
  tab_t x = nt2::_(T(0), T(5));
  BOOST_AUTO_TPL(res, (quadgk(nt2::functor<nt2::tag::exp_>(), x)));
  NT2_TEST(res.successful);
}

NT2_TEST_CASE_TPL( quadgk_tag_r, NT2_REAL_TYPES )
{
  using nt2::quadgk;
  using nt2::options;
  using nt2::integration::output;
  typedef nt2::table<T> tab_t;
  tab_t x = nt2::_(T(5), T(-1), T(4));
  BOOST_AUTO_TPL(res, (quadgk(nt2::functor<nt2::tag::exp_>(), T(5), T(4))));
  NT2_TEST(res.successful);
}

NT2_TEST_CASE_TPL( quadgk_tag_reverse, NT2_REAL_TYPES )
{
  using nt2::quadgk;
  using nt2::options;
  using nt2::integration::output;
  typedef nt2::table<T> tab_t;
  tab_t x = nt2::_(T(5), T(-1), T(0));
  BOOST_AUTO_TPL(res, (quadgk(nt2::functor<nt2::tag::exp_>(), x)));
  NT2_TEST(res.successful);
}


/**
 * @file cape.cpp
 *
 */

#include "cape.h"
#include "logger.h"
#include "numerical_functions.h"
#include "plugin_factory.h"
#include "util.h"
#include <boost/lexical_cast.hpp>
#include <boost/thread.hpp>
#include <future>

#include "fetcher.h"
#include "hitool.h"
#include "radon.h"

#include "cape.cuh"

const unsigned char FCAPE = (1 << 2);
const unsigned char FCAPE3km = (1 << 0);

using namespace std;
using namespace himan::plugin;

#ifdef DEBUG
#define DumpVector(A, B) himan::util::DumpVector(A, B)
#else
#define DumpVector(A, B)
#endif

extern mutex dimensionMutex;

const himan::param LCLTParam("LCL-K", 4, 0, 0, 0);
const himan::param LCLPParam("LCL-HPA", 4720, 0, 3, 0);
const himan::param LCLZParam("LCL-M", 4726, 0, 3, 6);
const himan::param LFCTParam("LFC-K", 4, 0, 0, 0);
const himan::param LFCPParam("LFC-HPA", 4721, 0, 3, 0);
const himan::param LFCZParam("LFC-M", 4727, 0, 3, 6);
const himan::param ELTParam("EL-K", 4, 0, 0, 0);
const himan::param ELPParam("EL-HPA", 4722, 0, 3, 0);
const himan::param ELZParam("EL-M", 4728, 0, 3, 6);
const himan::param CAPEParam("CAPE-JKG", 4723, 0, 7, 6);
const himan::param CAPE1040Param("CAPE1040-JKG", 4729, 0, 7, 6);
const himan::param CAPE3kmParam("CAPE3KM-JKG", 4724, 0, 7, 6);
const himan::param CINParam("CIN-JKG", 4725, 0, 7, 7);

const himan::level SURFACE(himan::kHeight, 0);
const himan::level M500(himan::kHeightLayer, 500, 0);
const himan::level UNSTABLE(himan::kMaximumThetaE, 0);

double Max(const vector<double>& vec)
{
	double ret = -1e38;

	for (const double& val : vec)
	{
		if (val != himan::kFloatMissing && val > ret) ret = val;
	}

	if (ret == -1e38) ret = himan::kFloatMissing;

	return ret;
}

void MultiplyWith(vector<double>& vec, double multiplier)
{
	for (double& val : vec)
	{
		if (val != himan::kFloatMissing) val *= multiplier;
	}
}

string PrintMean(const vector<double>& vec)
{
	double min = 1e38, max = -1e38, sum = 0;
	size_t count = 0, missing = 0;

	for (const double& val : vec)
	{
		if (val == himan::kFloatMissing)
		{
			missing++;
			continue;
		}

		min = (val < min) ? val : min;
		max = (val > max) ? val : max;
		count++;
		sum += val;
	}

	double mean = numeric_limits<double>::quiet_NaN();

	if (count > 0)
	{
		mean = sum / static_cast<double>(count);
	}

	string minstr = (min == 1e38) ? "nan" : to_string(static_cast<int>(min));
	string maxstr = (max == -1e38) ? "nan" : to_string(static_cast<int>(max));
	string meanstr = (mean != mean) ? "nan" : to_string(static_cast<int>(mean));

	return "min " + minstr + " max " + maxstr + " mean " + meanstr + " missing " + to_string(missing);
}

void MoistLift(const double* Piter, const double* Titer, const double* Penv, double* Tparcel, size_t size)
{
	// Split MoistLift (integration of a saturated air parcel upwards in atmosphere)
	// to several threads since it is very CPU intensive

	vector<future<void>> futures;

	size_t workers = 6;

	if (size % workers != 0)
	{
		workers = 4;
		if (size % workers != 0)
		{
			workers = 3;
			if (size % workers != 0)
			{
				workers = 1;
			}
		}
	}

	const size_t splitSize = static_cast<size_t>(floor(size / workers));

	for (size_t num = 0; num < workers; num++)
	{
		const size_t start = num * splitSize;
		futures.push_back(async(launch::async,
		                        [&](size_t start) {
			                        himan::metutil::MoistLiftA(&Piter[start], &Titer[start], &Penv[start],
			                                                   &Tparcel[start], splitSize);
			                    },
		                        start));
	}

	for (auto& future : futures)
	{
		future.get();
	}
}

cape::cape() : itsBottomLevel(kHybrid, kHPMissingInt)
{
	itsLogger = logger("cape");
}

void cape::Process(std::shared_ptr<const plugin_configuration> conf)
{
	compiled_plugin_base::Init(conf);

	auto r = GET_PLUGIN(radon);

	itsBottomLevel = level(kHybrid, stoi(r->RadonDB().GetProducerMetaData(itsConfiguration->SourceProducer().Id(),
	                                                                      "last hybrid level number")));

#ifdef HAVE_CUDA
	cape_cuda::itsBottomLevel = itsBottomLevel;
#endif

	vector<param> theParams;
	vector<string> sourceDatas;

	if (itsConfiguration->Exists("source_data"))
	{
		sourceDatas = itsConfiguration->GetValueList("source_data");
	}

	if (sourceDatas.size() == 0)
	{
		sourceDatas.push_back("surface");
		sourceDatas.push_back("500m mix");
		sourceDatas.push_back("most unstable");
	}

	theParams.push_back(LCLTParam);
	theParams.push_back(LCLPParam);
	theParams.push_back(LCLZParam);
	theParams.push_back(LFCTParam);
	theParams.push_back(LFCPParam);
	theParams.push_back(LFCZParam);
	theParams.push_back(ELTParam);
	theParams.push_back(ELPParam);
	theParams.push_back(ELZParam);
	theParams.push_back(CAPEParam);
	theParams.push_back(CAPE1040Param);
	theParams.push_back(CAPE3kmParam);
	theParams.push_back(CINParam);

	for (const auto& source : sourceDatas)
	{
		if (source == "surface")
		{
			itsSourceLevels.push_back(SURFACE);
		}
		else if (source == "500m mix")
		{
			itsSourceLevels.push_back(M500);
		}
		else if (source == "most unstable")
		{
			itsSourceLevels.push_back(UNSTABLE);
		}
	}

	// disregard the level information provided by user

	itsConfiguration->Info()->Levels(itsSourceLevels);

	SetParams(theParams);

	Start();
}

void cape::Calculate(shared_ptr<info> myTargetInfo, unsigned short threadIndex)
{
	/*
	 * Algorithm:
	 *
	 * 1) Find suitable T and TD values of an air parcel
	 *
	 * 2) Lift this air parcel to LCL height dry adiabatically
	 *
	 * 3) Continue lifting the particle to LFC height moist adiabatically.
	 *
	 * This is done by lifting the parcel a certain height at a time (500 Pa),
	 * and calculating the new temperature from the lifted height. If parcel
	 * temperature is larger than environment temperature, we have reached LFC.
	 * Environment temperature is therefore fetched every iteration of integration
	 * algorithm.
	 *
	 * 4) Integrate from LFC to EL
	 *
	 * 5) Integrate from surface to LFC to find CIN
	 */

	auto sourceLevel = myTargetInfo->Level();

	auto mySubThreadedLogger = logger("siThread#" + boost::lexical_cast<string>(threadIndex) + "Version" +
	                                  boost::lexical_cast<string>(static_cast<int>(sourceLevel.Type())));

	mySubThreadedLogger.Info("Calculating source level type " + HPLevelTypeToString.at(sourceLevel.Type()) +
	                         " for time " + static_cast<string>(myTargetInfo->Time().ValidDateTime()));

	// 1.

	timer aTimer;
	aTimer.Start();

	cape_source sourceValues;

	switch (sourceLevel.Type())
	{
		case kHeight:
			sourceValues = GetSurfaceValues(myTargetInfo);
			break;

		case kHeightLayer:
			sourceValues = Get500mMixingRatioValues(myTargetInfo);
			break;

		case kMaximumThetaE:
			sourceValues = GetHighestThetaEValues(myTargetInfo);
			break;

		default:
			throw runtime_error("Invalid source level: " + static_cast<std::string>(sourceLevel));
			break;
	}

	myTargetInfo->Level(sourceLevel);

	if (get<0>(sourceValues).empty()) return;

	aTimer.Stop();

	mySubThreadedLogger.Info("Source data calculated in " + boost::lexical_cast<string>(aTimer.GetTime()) + " ms");

	mySubThreadedLogger.Debug("Source temperature: " + ::PrintMean(get<0>(sourceValues)));
	mySubThreadedLogger.Debug("Source dewpoint: " + ::PrintMean(get<1>(sourceValues)));
	mySubThreadedLogger.Debug("Source pressure: " + ::PrintMean(get<2>(sourceValues)));

	// 2.

	aTimer.Start();

	auto LCL = GetLCL(myTargetInfo, sourceValues);

	aTimer.Stop();

	mySubThreadedLogger.Info("LCL calculated in " + boost::lexical_cast<string>(aTimer.GetTime()) + " ms");

	mySubThreadedLogger.Debug("LCL temperature: " + ::PrintMean(LCL.first));
	mySubThreadedLogger.Debug("LCL pressure: " + ::PrintMean(LCL.second));

	myTargetInfo->Param(LCLTParam);
	myTargetInfo->Data().Set(LCL.first);

	myTargetInfo->Param(LCLPParam);
	myTargetInfo->Data().Set(LCL.second);

	auto h = GET_PLUGIN(hitool);
	h->Configuration(itsConfiguration);
	h->Time(myTargetInfo->Time());
	h->ForecastType(myTargetInfo->ForecastType());
	h->HeightUnit(kHPa);

	auto height = h->VerticalValue(param("HL-M"), LCL.second);

	myTargetInfo->Param(LCLZParam);
	myTargetInfo->Data().Set(height);

	// 3.

	aTimer.Start();

	auto LFC = GetLFC(myTargetInfo, LCL.first, LCL.second);

	aTimer.Stop();

	mySubThreadedLogger.Info("LFC calculated in " + boost::lexical_cast<string>(aTimer.GetTime()) + " ms");

	if (LFC.first.empty())
	{
		return;
	}

	mySubThreadedLogger.Debug("LFC temperature: " + ::PrintMean(LFC.first));
	mySubThreadedLogger.Debug("LFC pressure: " + ::PrintMean(LFC.second));

	myTargetInfo->Param(LFCTParam);
	myTargetInfo->Data().Set(LFC.first);

	myTargetInfo->Param(LFCPParam);
	myTargetInfo->Data().Set(LFC.second);

	height = h->VerticalValue(param("HL-M"), LFC.second);

	myTargetInfo->Param(LFCZParam);
	myTargetInfo->Data().Set(height);

	// 4. & 5.

	aTimer.Start();

	auto capeInfo = make_shared<info>(*myTargetInfo);
	boost::thread t1(&cape::GetCAPE, this, boost::ref(capeInfo), LFC, ELTParam, ELPParam, ELZParam, CAPEParam,
	                 CAPE1040Param, CAPE3kmParam);

	auto cinInfo = make_shared<info>(*myTargetInfo);
	boost::thread t2(&cape::GetCIN, this, boost::ref(cinInfo), get<0>(sourceValues), get<2>(sourceValues), LCL.first,
	                 LCL.second, LFC.second, CINParam);

	t1.join();
	t2.join();

	aTimer.Stop();

	mySubThreadedLogger.Info("CAPE and CIN calculated in " + boost::lexical_cast<string>(aTimer.GetTime()) + " ms");

	// Sometimes CAPE area is infinitely small -- so that CAPE is zero but LFC is found. In this case set all derivative
	// parameters missing.

	capeInfo->Param(LFCZParam);
	auto& lfcz_ = VEC(capeInfo);
	capeInfo->Param(LFCPParam);
	auto& lfcp_ = VEC(capeInfo);
	capeInfo->Param(LFCTParam);
	auto& lfct_ = VEC(capeInfo);
	cinInfo->Param(CINParam);
	auto& cin_ = VEC(cinInfo);
	capeInfo->Param(ELZParam);
	auto elz_ = VEC(capeInfo);
	capeInfo->Param(CAPEParam);
	auto cape_ = VEC(capeInfo);

	for (size_t i = 0; i < lfcz_.size(); i++)
	{
		if (cape_[i] == 0 && elz_[i] == kFloatMissing && lfcz_[i] != kFloatMissing)
		{
			cin_[i] = 0;
			lfcz_[i] = kFloatMissing;
			lfcp_[i] = kFloatMissing;
			lfct_[i] = kFloatMissing;
		}
	}

#ifdef DEBUG
	assert(lfcz_.size() == elz_.size());
	assert(cape_.size() == elz_.size());
	assert(cin_.size() == elz_.size());

	for (size_t i = 0; i < lfcz_.size(); i++)
	{
		// Check:
		// * If LFC is missing, EL is missing
		// * If LFC is present, EL is present
		// * If both are present, LFC must be below EL
		// * CAPE must be zero or positive real value
		// * CIN must be zero or negative real value
		assert((lfcz_[i] == kFloatMissing && elz_[i] == kFloatMissing) ||
		       ((lfcz_[i] != kFloatMissing && elz_[i] != kFloatMissing) && (lfcz_[i] < elz_[i])));
		assert(cape_[i] >= 0);
		assert(cin_[i] <= 0);
	}
#endif

	// Do smoothening for CAPE & CIN parameters
	mySubThreadedLogger.Trace("Smoothening");

	himan::matrix<double> filter_kernel(3, 3, 1, kFloatMissing, 1. / 9.);

	capeInfo->Param(CAPEParam);
	himan::matrix<double> filtered = numerical_functions::Filter2D(capeInfo->Data(), filter_kernel);
	capeInfo->Grid()->Data(filtered);

	capeInfo->Param(CAPE1040Param);
	filtered = numerical_functions::Filter2D(capeInfo->Data(), filter_kernel);
	capeInfo->Grid()->Data(filtered);

	capeInfo->Param(CAPE3kmParam);
	filtered = numerical_functions::Filter2D(capeInfo->Data(), filter_kernel);
	capeInfo->Grid()->Data(filtered);

	capeInfo->Param(CINParam);
	filtered = numerical_functions::Filter2D(capeInfo->Data(), filter_kernel);
	capeInfo->Grid()->Data(filtered);

	capeInfo->Param(CAPEParam);
	mySubThreadedLogger.Debug("CAPE: " + ::PrintMean(VEC(capeInfo)));
	capeInfo->Param(CAPE1040Param);
	mySubThreadedLogger.Debug("CAPE1040: " + ::PrintMean(VEC(capeInfo)));
	capeInfo->Param(CAPE3kmParam);
	mySubThreadedLogger.Debug("CAPE3km: " + ::PrintMean(VEC(capeInfo)));
	cinInfo->Param(CINParam);
	mySubThreadedLogger.Debug("CIN: " + ::PrintMean(VEC(cinInfo)));
}

void cape::GetCIN(shared_ptr<info> myTargetInfo, const vector<double>& Tsource, const vector<double>& Psource,
                  const vector<double>& TLCL, const vector<double>& PLCL, const vector<double>& PLFC, param CINParam)
{
#ifdef HAVE_CUDA
	if (itsConfiguration->UseCuda())
	{
		cape_cuda::GetCINGPU(itsConfiguration, myTargetInfo, Tsource, Psource, TLCL, PLCL, PLFC, CINParam);
	}
	else
#endif
	{
		GetCINCPU(myTargetInfo, Tsource, Psource, TLCL, PLCL, PLFC, CINParam);
	}
}

void cape::GetCINCPU(shared_ptr<info> myTargetInfo, const vector<double>& Tsource, const vector<double>& Psource,
                     const vector<double>& TLCL, const vector<double>& PLCL, const vector<double>& PLFC, param CINParam)
{
	auto h = GET_PLUGIN(hitool);
	h->Configuration(itsConfiguration);
	h->Time(myTargetInfo->Time());
	h->ForecastType(myTargetInfo->ForecastType());
	h->HeightUnit(kHPa);

	vector<bool> found(Tsource.size(), false);

	for (size_t i = 0; i < found.size(); i++)
	{
		if (PLFC[i] == kFloatMissing)
		{
			found[i] = true;
		}
	}

	forecast_time ftime = myTargetInfo->Time();
	forecast_type ftype = myTargetInfo->ForecastType();

	/*
	 * Modus operandi:
	 *
	 * 1. Integrate from source level to LCL dry adiabatically
	 *
	 * This can be done always since LCL is known at all grid points
	 * (that have source data values defined).
	 *
	 * 2. Integrate from LCL to LFC moist adiabatically
	 *
	 * Note! For some points integration will fail (no LFC found)
	 *
	 * We stop integrating at first time CAPE area is found!
	 */

	// Get LCL and LFC heights in meters

	auto ZLCL = h->VerticalValue(param("HL-M"), PLCL);
	auto ZLFC = h->VerticalValue(param("HL-M"), PLFC);

	level curLevel = itsBottomLevel;

	auto prevZenvInfo = Fetch(ftime, curLevel, param("HL-M"), ftype, false);
	auto prevTenvInfo = Fetch(ftime, curLevel, param("T-K"), ftype, false);
	auto prevPenvInfo = Fetch(ftime, curLevel, param("P-HPA"), ftype, false);

	std::vector<double> cinh(PLCL.size(), 0);

	size_t foundCount = count(found.begin(), found.end(), true);

	auto Piter = Psource;
	::MultiplyWith(Piter, 100);

	auto PLCLPa = PLCL;
	::MultiplyWith(PLCLPa, 100);

	auto Titer = Tsource;
	auto prevTparcelVec = Tsource;

	curLevel.Value(curLevel.Value() - 1);

	auto hPa100 = h->LevelForHeight(myTargetInfo->Producer(), 100.);

	while (curLevel.Value() > hPa100.first.Value() && foundCount != found.size())
	{
		auto ZenvInfo = Fetch(ftime, curLevel, param("HL-M"), ftype, false);
		auto TenvInfo = Fetch(ftime, curLevel, param("T-K"), ftype, false);
		auto PenvInfo = Fetch(ftime, curLevel, param("P-HPA"), ftype, false);

		vector<double> TparcelVec(Piter.size(), kFloatMissing);

		// Convert pressure to Pa since metutil-library expects that
		auto PenvVec = PenvInfo->Data().Values();
		::MultiplyWith(PenvVec, 100);

		metutil::LiftLCL(&Piter[0], &Titer[0], &PLCLPa[0], &PenvVec[0], &TparcelVec[0], TparcelVec.size());

		int i = -1;

		auto& cinhref = cinh;

		for (auto&& tup : zip_range(cinhref, VEC(TenvInfo), VEC(prevTenvInfo), VEC(PenvInfo), VEC(prevPenvInfo),
		                            VEC(ZenvInfo), VEC(prevZenvInfo), TparcelVec, prevTparcelVec, Psource))
		{
			i++;

			if (found[i]) continue;

			double& cin = tup.get<0>();

			double Tenv = tup.get<1>();  // K
			assert(Tenv >= 100.);

			double prevTenv = tup.get<2>();

			double Penv = tup.get<3>();  // hPa
			assert(Penv < 1200.);

			double prevPenv = tup.get<4>();

			double Zenv = tup.get<5>();      // m
			double prevZenv = tup.get<6>();  // m

			double Tparcel = tup.get<7>();  // K
			assert(Tparcel >= 100.);

			double prevTparcel = tup.get<8>();  // K

			double Psrc = tup.get<9>();

			if (Penv > Psrc)
			{
				// Have not reached source level yet
				continue;
			}
			else if (Penv <= PLFC[i])
			{
				// reached max height

				found[i] = true;

				if (prevTparcel == kFloatMissing || prevPenv == kFloatMissing || prevTenv == kFloatMissing)
				{
					continue;
				}

				// Integrate the final piece from previous level to LFC level

				// First get LFC height in meters
				Zenv = himan::numerical_functions::interpolation::Linear(PLFC[i], prevPenv, Penv, prevZenv, Zenv);

				// LFC environment temperature value
				Tenv = himan::numerical_functions::interpolation::Linear(PLFC[i], prevPenv, Penv, prevTenv, Tenv);

				// LFC T parcel value
				Tparcel =
				    himan::numerical_functions::interpolation::Linear(PLFC[i], prevPenv, Penv, prevTparcel, Tparcel);

				Penv = PLFC[i];
				assert(Zenv > prevZenv);
			}

			if (Tparcel == kFloatMissing)
			{
				continue;
			}

			if (Penv < PLCL[i])
			{
				// Above LCL, switch to virtual temperature
				Tparcel = metutil::VirtualTemperature_(Tparcel, Penv * 100);
				Tenv = metutil::VirtualTemperature_(Tenv, Penv * 100);
			}

			cin += CAPE::CalcCIN(Tenv, prevTenv, Tparcel, prevTparcel, Penv, prevPenv, Zenv, prevZenv);
			assert(cin <= 0);
		}

		foundCount = count(found.begin(), found.end(), true);

		itsLogger.Trace("CIN read for " + boost::lexical_cast<string>(foundCount) + "/" +
		                boost::lexical_cast<string>(found.size()) + " gridpoints");

		curLevel.Value(curLevel.Value() - 1);

		prevZenvInfo = ZenvInfo;
		prevTenvInfo = TenvInfo;
		prevPenvInfo = PenvInfo;
		prevTparcelVec = TparcelVec;

		for (size_t i = 0; i < Titer.size(); i++)
		{
			if (TparcelVec[i] != kFloatMissing && PenvVec[i] != kFloatMissing)
			{
				Titer[i] = TparcelVec[i];
				Piter[i] = PenvVec[i];
			}

			if (found[i]) Titer[i] = kFloatMissing;  // by setting this we prevent MoistLift to integrate particle
		}
	}

	myTargetInfo->Param(CINParam);
	myTargetInfo->Data().Set(cinh);
}

void cape::GetCAPE(shared_ptr<info> myTargetInfo, const pair<vector<double>, vector<double>>& LFC, param ELTParam,
                   param ELPParam, param ELZParam, param CAPEParam, param CAPE1040Param, param CAPE3kmParam)
{
#ifdef HAVE_CUDA
	if (itsConfiguration->UseCuda())
	{
		cape_cuda::GetCAPEGPU(itsConfiguration, myTargetInfo, LFC.first, LFC.second, ELTParam, ELPParam, CAPEParam,
		                      CAPE1040Param, CAPE3kmParam);
	}
	else
#endif
	{
		GetCAPECPU(myTargetInfo, LFC.first, LFC.second, ELTParam, ELPParam, CAPEParam, CAPE1040Param, CAPE3kmParam);
	}

	auto h = GET_PLUGIN(hitool);

	h->Configuration(itsConfiguration);
	h->Time(myTargetInfo->Time());
	h->ForecastType(myTargetInfo->ForecastType());
	h->HeightUnit(kHPa);

	myTargetInfo->Param(ELPParam);
	auto height = h->VerticalValue(param("HL-M"), VEC(myTargetInfo));

	myTargetInfo->Param(ELZParam);
	myTargetInfo->Data().Set(height);
}

void cape::GetCAPECPU(shared_ptr<info> myTargetInfo, const vector<double>& T, const vector<double>& P, param ELTParam,
                      param ELPParam, param CAPEParam, param CAPE1040Param, param CAPE3kmParam)
{
	assert(T.size() == P.size());

	auto h = GET_PLUGIN(hitool);

	h->Configuration(itsConfiguration);
	h->Time(myTargetInfo->Time());
	h->ForecastType(myTargetInfo->ForecastType());
	h->HeightUnit(kHPa);

	// Found count determines if we have calculated all three CAPE variation for a single grid point
	vector<unsigned char> found(T.size(), 0);

	vector<double> CAPE(T.size(), 0);
	vector<double> CAPE1040(T.size(), 0);
	vector<double> CAPE3km(T.size(), 0);
	vector<double> ELT(T.size(), kFloatMissing);
	vector<double> ELP(T.size(), kFloatMissing);

	// Unlike LCL, LFC is *not* found for all grid points

	size_t foundCount = 0;

	for (size_t i = 0; i < P.size(); i++)
	{
		if (P[i] == kFloatMissing)
		{
			found[i] |= FCAPE;
			foundCount++;
		}
	}

	// For each grid point find the hybrid level that's below LFC and then pick the lowest level
	// among all grid points

	auto levels = h->LevelForHeight(myTargetInfo->Producer(), ::Max(P));

	level curLevel = levels.first;

	auto prevZenvInfo = Fetch(myTargetInfo->Time(), curLevel, param("HL-M"), myTargetInfo->ForecastType(), false);
	auto prevTenvInfo = Fetch(myTargetInfo->Time(), curLevel, param("T-K"), myTargetInfo->ForecastType(), false);
	auto prevPenvInfo = Fetch(myTargetInfo->Time(), curLevel, param("P-HPA"), myTargetInfo->ForecastType(), false);

	curLevel.Value(curLevel.Value());

	auto Piter = P, Titer = T;  // integration variables
	auto prevTparcelVec = Titer;

	// Convert pressure to Pa since metutil-library expects that
	::MultiplyWith(Piter, 100);

	info_t TenvInfo, PenvInfo, ZenvInfo;

	auto hPa100 = h->LevelForHeight(myTargetInfo->Producer(), 100.);

	while (curLevel.Value() > hPa100.first.Value() && foundCount != found.size())
	{
		// Get environment temperature, pressure and height values for this level
		PenvInfo = Fetch(myTargetInfo->Time(), curLevel, param("P-HPA"), myTargetInfo->ForecastType(), false);
		TenvInfo = Fetch(myTargetInfo->Time(), curLevel, param("T-K"), myTargetInfo->ForecastType(), false);
		ZenvInfo = Fetch(myTargetInfo->Time(), curLevel, param("HL-M"), myTargetInfo->ForecastType(), false);

		// Convert pressure to Pa since metutil-library expects that
		auto PenvVec = PenvInfo->Data().Values();
		::MultiplyWith(PenvVec, 100);

		vector<double> TparcelVec(P.size(), kFloatMissing);

		::MoistLift(&Piter[0], &Titer[0], &PenvVec[0], &TparcelVec[0], TparcelVec.size());

		int i = -1;
		for (auto&& tup : zip_range(VEC(PenvInfo), VEC(ZenvInfo), VEC(prevZenvInfo), VEC(prevTenvInfo),
		                            VEC(prevPenvInfo), VEC(TenvInfo), TparcelVec, prevTparcelVec))
		{
			i++;

			double Tenv = tup.get<5>();  // K
			assert(Tenv > 100.);

			double prevTenv = tup.get<3>();  // K
			assert(prevTenv > 100.);

			double Penv = tup.get<0>();  // hPa
			assert(Penv < 1200.);

			double prevPenv = tup.get<4>();  // hPa
			assert(prevPenv < 1200.);

			double Zenv = tup.get<1>();      // m
			double prevZenv = tup.get<2>();  // m

			double Tparcel = tup.get<6>();  // K
			assert(Tparcel > 100. || Tparcel == kFloatMissing);

			double prevTparcel = tup.get<7>();  // K
			assert(prevTparcel > 100. || Tparcel == kFloatMissing);

			if (found[i] & FCAPE)
			{
				continue;
			}
			else if (Penv == kFloatMissing || Tenv == kFloatMissing || Zenv == kFloatMissing ||
			         prevZenv == kFloatMissing || Tparcel == kFloatMissing || Penv > P[i])
			{
				// Missing data or current grid point is below LFC
				continue;
			}

			// When rising above LFC, get accurate value of Tenv at that level so that even small amounts of CAPE
			// (and EL!) values can be determined.

			if (prevTparcel == kFloatMissing && Tparcel != kFloatMissing)
			{
				prevTenv = himan::numerical_functions::interpolation::Linear(P[i], prevPenv, Penv, prevTenv, Tenv);
				prevZenv = himan::numerical_functions::interpolation::Linear(P[i], prevPenv, Penv, prevZenv, Zenv);
				prevPenv = P[i];     // LFC pressure
				prevTparcel = T[i];  // LFC temperature

				// If LFC was found close to lower hybrid level, the linear interpolation and moist lift will result
				// to same values. In this case CAPE integration fails as there is no area formed between environment
				// and parcel temperature. The result for this is that LFC is found but EL is not found. To prevent
				// this, warm the parcel value just slightly so that a miniscule CAPE area is formed and EL is found.

				if (fabs(prevTparcel - prevTenv) < 0.0001)
				{
					prevTparcel += 0.0001;
				}
			}

			if (curLevel.Value() < 85 && (Tenv - Tparcel) > 25.)
			{
				// Temperature gap between environment and parcel too large --> abort search.
				// Only for values higher in the atmosphere, to avoid the effects of inversion

				found[i] |= FCAPE;
				continue;
			}

			if (prevZenv >= 3000. && Zenv >= 3000.)
			{
				found[i] |= FCAPE3km;
			}

			if ((found[i] & FCAPE3km) == 0)
			{
				double C = CAPE::CalcCAPE3km(Tenv, prevTenv, Tparcel, prevTparcel, Penv, prevPenv, Zenv, prevZenv);

				CAPE3km[i] += C;

				assert(CAPE3km[i] < 3000.);  // 3000J/kg, not 3000m
				assert(CAPE3km[i] >= 0);
			}

			double C = CAPE::CalcCAPE1040(Tenv, prevTenv, Tparcel, prevTparcel, Penv, prevPenv, Zenv, prevZenv);

			CAPE1040[i] += C;

			assert(CAPE1040[i] < 5000.);
			assert(CAPE1040[i] >= 0);

			double CAPEval, ELTval, ELPval;

			CAPE::CalcCAPE(Tenv, prevTenv, Tparcel, prevTparcel, Penv, prevPenv, Zenv, prevZenv, CAPEval, ELTval,
			               ELPval);

			CAPE[i] += CAPEval;

			assert(CAPEval >= 0.);
			assert(CAPE[i] < 8000);

			if (ELTval != kFloatMissing)
			{
				ELT[i] = ELTval;
				ELP[i] = ELPval;
			}
		}

		curLevel.Value(curLevel.Value() - 1);

		foundCount = 0;
		for (auto& val : found)
		{
			if (val & FCAPE) foundCount++;
		}

		itsLogger.Trace("CAPE read for " + boost::lexical_cast<string>(foundCount) + "/" +
		                boost::lexical_cast<string>(found.size()) + " gridpoints");
		prevZenvInfo = ZenvInfo;
		prevTenvInfo = TenvInfo;
		prevPenvInfo = PenvInfo;
		prevTparcelVec = TparcelVec;
	}

	// If the CAPE area is continued all the way to level 60 and beyond, we don't have an EL for that
	// (since integration is forcefully stopped)
	// In this case level 60 = EL

	for (size_t i = 0; i < CAPE.size(); i++)
	{
		if (CAPE[i] > 0 && ELT[i] == kFloatMissing)
		{
			TenvInfo->LocationIndex(i);
			PenvInfo->LocationIndex(i);

			ELT[i] = TenvInfo->Value();
			ELP[i] = PenvInfo->Value();
		}
	}

	myTargetInfo->Param(ELTParam);
	myTargetInfo->Data().Set(ELT);

	myTargetInfo->Param(ELPParam);
	myTargetInfo->Data().Set(ELP);

	myTargetInfo->Param(CAPEParam);
	myTargetInfo->Data().Set(CAPE);

	myTargetInfo->Param(CAPE1040Param);
	myTargetInfo->Data().Set(CAPE1040);

	myTargetInfo->Param(CAPE3kmParam);
	myTargetInfo->Data().Set(CAPE3km);
}

pair<vector<double>, vector<double>> cape::GetLFC(shared_ptr<info> myTargetInfo, vector<double>& T, vector<double>& P)
{
	auto h = GET_PLUGIN(hitool);

	assert(T.size() == P.size());

	h->Configuration(itsConfiguration);
	h->Time(myTargetInfo->Time());
	h->ForecastType(myTargetInfo->ForecastType());
	h->HeightUnit(kHPa);

	itsLogger.Trace("Searching environment temperature for starting pressure");

	vector<double> TenvLCL;

	try
	{
		TenvLCL = h->VerticalValue(param("T-K"), P);
	}
	catch (const HPExceptionType& e)
	{
		if (e == kFileDataNotFound)
		{
			return make_pair(vector<double>(), vector<double>());
		}

		throw;
	}

#ifdef HAVE_CUDA
	if (itsConfiguration->UseCuda())
	{
		return cape_cuda::GetLFCGPU(itsConfiguration, myTargetInfo, T, P, TenvLCL);
	}
	else
#endif
	{
		return GetLFCCPU(myTargetInfo, T, P, TenvLCL);
	}
}

pair<vector<double>, vector<double>> cape::GetLFCCPU(shared_ptr<info> myTargetInfo, vector<double>& T,
                                                     vector<double>& P, vector<double>& TenvLCL)
{
	auto h = GET_PLUGIN(hitool);

	assert(T.size() == P.size());

	h->Configuration(itsConfiguration);
	h->Time(myTargetInfo->Time());
	h->ForecastType(myTargetInfo->ForecastType());
	h->HeightUnit(kHPa);

	auto Piter = P, Titer = T;  // integration variables

	// Convert pressure to Pa since metutil-library expects that
	::MultiplyWith(Piter, 100);

	vector<bool> found(T.size(), false);

	vector<double> LFCT(T.size(), kFloatMissing);
	vector<double> LFCP(T.size(), kFloatMissing);

	for (size_t i = 0; i < TenvLCL.size(); i++)
	{
		// Require dry lifted parcel to be just a fraction higher
		// than environment to be accepted as LFC level.
		// This requirement is important later when CAPE integration
		// starts.

		if ((T[i] - TenvLCL[i]) > 0.001)
		{
			found[i] = true;
			LFCT[i] = T[i];
			LFCP[i] = P[i];
			Piter[i] = kFloatMissing;
		}
	}

	size_t foundCount = count(found.begin(), found.end(), true);

	itsLogger.Debug("Found " + boost::lexical_cast<string>(foundCount) + " gridpoints that have LCL=LFC");

	// For each grid point find the hybrid level that's below LCL and then pick the lowest level
	// among all grid points; most commonly it's the lowest hybrid level

	auto levels = h->LevelForHeight(myTargetInfo->Producer(), ::Max(P));
	level curLevel = levels.first;

	auto prevPenvInfo = Fetch(myTargetInfo->Time(), curLevel, param("P-HPA"), myTargetInfo->ForecastType(), false);
	auto prevTenvInfo = Fetch(myTargetInfo->Time(), curLevel, param("T-K"), myTargetInfo->ForecastType(), false);

	curLevel.Value(curLevel.Value() - 1);

	auto hPa150 = h->LevelForHeight(myTargetInfo->Producer(), 150.);
	auto hPa450 = h->LevelForHeight(myTargetInfo->Producer(), 450.);
	vector<double> prevTparcelVec(P.size(), kFloatMissing);

	while (curLevel.Value() > hPa150.first.Value() && foundCount != found.size())
	{
		// Get environment temperature and pressure values for this level
		auto TenvInfo = Fetch(myTargetInfo->Time(), curLevel, param("T-K"), myTargetInfo->ForecastType(), false);
		auto PenvInfo = Fetch(myTargetInfo->Time(), curLevel, param("P-HPA"), myTargetInfo->ForecastType(), false);

		// Convert pressure to Pa since metutil-library expects that
		auto PenvVec = PenvInfo->Data().Values();
		::MultiplyWith(PenvVec, 100);

		// Lift the particle from previous level to this level. In the first revolution
		// of this loop the starting level is LCL. If target level level is below current level
		// (ie. we would be lowering the particle) missing value is returned.

		vector<double> TparcelVec(P.size(), kFloatMissing);

		::MoistLift(&Piter[0], &Titer[0], &PenvVec[0], &TparcelVec[0], TparcelVec.size());

		double scale = 1;
		if (prevPenvInfo->Param().Name() == "P-PA") scale = 0.01;

		int i = -1;
		for (auto&& tup : zip_range(VEC(TenvInfo), VEC(PenvInfo), VEC(prevPenvInfo), VEC(prevTenvInfo), TparcelVec,
		                            prevTparcelVec, LFCT, LFCP))
		{
			i++;

			if (found[i]) continue;

			double Tenv = tup.get<0>();  // K
			assert(Tenv > 100.);

			double Penv = tup.get<1>();  // hPa
			assert(Penv < 1200.);
			assert(P[i] < 1200.);

			double prevPenv = tup.get<2>() * scale;
			assert(prevPenv < 1200.);

			double prevTenv = tup.get<3>();  // K
			assert(prevTenv > 100.);

			double Tparcel = tup.get<4>();  // K
			assert(Tparcel > 100.);

			double prevTparcel = tup.get<5>();  // K
			assert(Tparcel > 100.);

			double& Tresult = tup.get<6>();
			double& Presult = tup.get<7>();

			if (Tparcel == kFloatMissing || Penv > P[i] + 30)
			{
				continue;
			}

			if (Tparcel > Tenv)
			{
				// Parcel is now warmer than environment, we have found LFC and entering CAPE zone

				found[i] = true;

				if (prevTparcel == kFloatMissing)
				{
					// Previous value is unknown: perhaps LFC is found very close to ground?
					// Use LCL for previous value.
					prevTparcel = T[i];
				}

				if (fabs(prevTparcel - prevTenv) < 0.0001)
				{
					Tresult = Tparcel;
					Presult = Penv;
				}
				else
				{
					auto intersection =
					    CAPE::GetPointOfIntersection(point(Tenv, Penv), point(prevTenv, prevPenv), point(Tparcel, Penv),
					                                 point(prevTparcel, prevPenv));
					Tresult = intersection.X();
					Presult = intersection.Y();

					if (Tresult == kFloatMissing)
					{
						// Intersection not found, use exact level value
						Tresult = Tenv;
						Presult = Penv;
					}
				}

				assert(Tresult != kFloatMissing);
				assert(Presult != kFloatMissing);
			}
			else if (curLevel.Value() < hPa450.first.Value() && (Tenv - Tparcel) > 30.)
			{
				// Temperature gap between environment and parcel too large --> abort search.
				// Only for values higher in the atmosphere, to avoid the effects of inversion

				found[i] = true;
			}
		}

		curLevel.Value(curLevel.Value() - 1);

		foundCount = count(found.begin(), found.end(), true);
		itsLogger.Trace("LFC processed for " + boost::lexical_cast<string>(foundCount) + "/" +
		                boost::lexical_cast<string>(found.size()) + " grid points");

		prevPenvInfo = PenvInfo;
		prevTenvInfo = TenvInfo;

		for (size_t i = 0; i < Titer.size(); i++)
		{
			if (found[i]) Titer[i] = kFloatMissing;  // by setting this we prevent MoistLift to integrate particle
		}

		prevTparcelVec = TparcelVec;
	}

	return make_pair(LFCT, LFCP);
}

pair<vector<double>, vector<double>> cape::GetLCL(shared_ptr<info> myTargetInfo, const cape_source& sourceValues)
{
	vector<double> TLCL(get<0>(sourceValues).size(), kFloatMissing);
	vector<double> PLCL = TLCL;

	// Need surface pressure

	double Pscale = 100.;  // P should be Pa

	for (auto&& tup : zip_range(get<0>(sourceValues), get<1>(sourceValues), get<2>(sourceValues), TLCL, PLCL))
	{
		double T = tup.get<0>();
		double TD = tup.get<1>();
		double P = tup.get<2>() * Pscale;  // Pa
		double& Tresult = tup.get<3>();
		double& Presult = tup.get<4>();

		auto lcl = metutil::LCLA_(P, T, TD);

		Tresult = lcl.T;  // K

		if (lcl.P != kFloatMissing)
		{
			Presult = 0.01 * ((lcl.P > P) ? P : lcl.P);  // hPa
		}
	}

	for (auto& val : PLCL)
	{
		val = fmax(val, 250.);
	}

	return make_pair(TLCL, PLCL);
}

cape_source cape::GetSurfaceValues(shared_ptr<info> myTargetInfo)
{
	/*
	 * 1. Get temperature and relative humidity from lowest hybrid level.
	 * 2. Calculate dewpoint
	 * 3. Return temperature and dewpoint
	 */

	auto TInfo = Fetch(myTargetInfo->Time(), itsBottomLevel, param("T-K"), myTargetInfo->ForecastType(), false);
	auto RHInfo = Fetch(myTargetInfo->Time(), itsBottomLevel, param("RH-PRCNT"), myTargetInfo->ForecastType(), false);
	auto PInfo = Fetch(myTargetInfo->Time(), itsBottomLevel, param("P-HPA"), myTargetInfo->ForecastType(), false);

	if (!TInfo || !RHInfo || !PInfo)
	{
		return make_tuple(vector<double>(), vector<double>(), vector<double>());
	}

	auto T = VEC(TInfo);
	auto RH = VEC(RHInfo);

	vector<double> TD(T.size(), kFloatMissing);

	for (size_t i = 0; i < TD.size(); i++)
	{
		if (T[i] != kFloatMissing && RH[i] != kFloatMissing)
		{
			TD[i] = metutil::DewPointFromRH_(T[i], RH[i]);
		}
	}

	return make_tuple(T, TD, VEC(PInfo));
}

cape_source cape::Get500mMixingRatioValues(shared_ptr<info> myTargetInfo)
{
/*
 * 1. Calculate potential temperature and mixing ratio for vertical profile
 *    0...500m for every 2 hPa
 * 2. Take an average from all values
 * 3. Calculate temperature from potential temperature, and dewpoint temperature
 *    from temperature and mixing ratio
 * 4. Return the two calculated values
 */

#ifdef HAVE_CUDA
	if (itsConfiguration->UseCuda())
	{
		return cape_cuda::Get500mMixingRatioValuesGPU(itsConfiguration, myTargetInfo);
	}
	else
#endif
	{
		return Get500mMixingRatioValuesCPU(myTargetInfo);
	}
}

cape_source cape::Get500mMixingRatioValuesCPU(shared_ptr<info> myTargetInfo)
{
	modifier_mean tp, mr;
	level curLevel = itsBottomLevel;

	auto h = GET_PLUGIN(hitool);

	h->Configuration(itsConfiguration);
	h->Time(myTargetInfo->Time());
	h->ForecastType(myTargetInfo->ForecastType());

	tp.HeightInMeters(false);
	mr.HeightInMeters(false);

	auto PInfo = Fetch(myTargetInfo->Time(), curLevel, param("P-HPA"), myTargetInfo->ForecastType(), false);

	if (!PInfo)
	{
		return make_tuple(vector<double>(), vector<double>(), vector<double>());
	}
	else
	{
		// Himan specialty: empty data grid

		size_t miss = 0;
		for (auto& val : VEC(PInfo))
		{
			if (val == kFloatMissing) miss++;
		}

		if (PInfo->Data().MissingCount() == PInfo->Data().Size())
		{
			return make_tuple(vector<double>(), vector<double>(), vector<double>());
		}
	}

	auto P = PInfo->Data().Values();

	auto P500m = h->VerticalValue(param("P-HPA"), 500.);

	h->HeightUnit(kHPa);

	tp.LowerHeight(P);
	mr.LowerHeight(P);

	tp.UpperHeight(P500m);
	mr.UpperHeight(P500m);

	vector<bool> found(myTargetInfo->Data().Size(), false);
	size_t foundCount = 0;

	while (foundCount != found.size())
	{
		auto T = h->VerticalValue(param("T-K"), P);
		auto RH = h->VerticalValue(param("RH-PRCNT"), P);

		vector<double> Tpot(T.size(), kFloatMissing);
		vector<double> MR(T.size(), kFloatMissing);

		for (size_t i = 0; i < T.size(); i++)
		{
			if (found[i]) continue;
			if (T[i] == kFloatMissing || P[i] == kFloatMissing || RH[i] == kFloatMissing) continue;

			assert(T[i] > 150 && T[i] < 350);
			assert(P[i] > 100 && P[i] < 1500);
			assert(RH[i] > 0 && RH[i] < 102);

			Tpot[i] = metutil::Theta_(T[i], 100 * P[i]);
			MR[i] = metutil::smarttool::MixingRatio_(T[i], RH[i], 100 * P[i]);
		}

		tp.Process(Tpot, P);
		mr.Process(MR, P);

		foundCount = tp.HeightsCrossed();

		assert(tp.HeightsCrossed() == mr.HeightsCrossed());

		itsLogger.Debug("Data read " + boost::lexical_cast<string>(foundCount) + "/" +
		                boost::lexical_cast<string>(found.size()) + " gridpoints");

		for (size_t i = 0; i < found.size(); i++)
		{
			assert((P[i] > 100 && P[i] < 1500) || P[i] == kFloatMissing);

			if (found[i])
			{
				P[i] = kFloatMissing;  // disable processing of this
			}
			else if (P[i] != kFloatMissing)
			{
				P[i] -= 2.0;
			}
		}
	}

	auto Tpot = tp.Result();
	auto MR = mr.Result();

	auto Psurf = Fetch(myTargetInfo->Time(), itsBottomLevel, param("P-HPA"), myTargetInfo->ForecastType(), false);
	P = Psurf->Data().Values();

	vector<double> T(Tpot.size(), kFloatMissing);

	for (size_t i = 0; i < Tpot.size(); i++)
	{
		assert((P[i] > 100 && P[i] < 1500) || P[i] == kFloatMissing);
		if (Tpot[i] != kFloatMissing && P[i] != kFloatMissing)
		{
			T[i] = Tpot[i] * pow((P[i] / 1000.), 0.2854);
		}
	}

	vector<double> TD(T.size(), kFloatMissing);

	for (size_t i = 0; i < MR.size(); i++)
	{
		if (T[i] != kFloatMissing && MR[i] != kFloatMissing && P[i] != kFloatMissing)
		{
			double Es = metutil::Es_(T[i]);  // Saturated water vapor pressure
			double E = metutil::E_(MR[i], 100 * P[i]);

			double RH = E / Es * 100;
			TD[i] = metutil::DewPointFromRH_(T[i], RH);
		}
	}

	return make_tuple(T, TD, P);
}

cape_source cape::GetHighestThetaEValues(shared_ptr<info> myTargetInfo)
{
/*
 * 1. Calculate equivalent potential temperature for all hybrid levels
 *    below 600hPa
 * 2. Take temperature and relative humidity from the level that had
 *    highest theta e
 * 3. Calculate dewpoint temperature from temperature and relative humidity.
 * 4. Return temperature and dewpoint
 */

#ifdef HAVE_CUDA
	if (itsConfiguration->UseCuda())
	{
		return cape_cuda::GetHighestThetaEValuesGPU(itsConfiguration, myTargetInfo);
	}
	else
#endif
	{
		return GetHighestThetaEValuesCPU(myTargetInfo);
	}
}

cape_source cape::GetHighestThetaEValuesCPU(shared_ptr<info> myTargetInfo)
{
	vector<bool> found(myTargetInfo->Data().Size(), false);

	vector<double> maxThetaE(myTargetInfo->Data().Size(), -1);
	vector<double> Ttheta(myTargetInfo->Data().Size(), kFloatMissing);
	auto TDtheta = Ttheta;
	auto Ptheta = Ttheta;

	level curLevel = itsBottomLevel;

	info_t prevTInfo, prevRHInfo, prevPInfo;

	while (true)
	{
		auto TInfo = Fetch(myTargetInfo->Time(), curLevel, param("T-K"), myTargetInfo->ForecastType(), false);
		auto RHInfo = Fetch(myTargetInfo->Time(), curLevel, param("RH-PRCNT"), myTargetInfo->ForecastType(), false);
		auto PInfo = Fetch(myTargetInfo->Time(), curLevel, param("P-HPA"), myTargetInfo->ForecastType(), false);

		if (!TInfo || !RHInfo || !PInfo)
		{
			return make_tuple(vector<double>(), vector<double>(), vector<double>());
		}

		int i = -1;

		for (auto&& tup : zip_range(VEC(TInfo), VEC(RHInfo), VEC(PInfo), maxThetaE, Ttheta, TDtheta, Ptheta))
		{
			i++;

			if (found[i]) continue;

			double T = tup.get<0>();
			double RH = tup.get<1>();
			double P = tup.get<2>();
			double& refThetaE = tup.get<3>();
			double& Tresult = tup.get<4>();
			double& TDresult = tup.get<5>();
			double& Presult = tup.get<6>();

			if (P == kFloatMissing)
			{
				found[i] = true;
				continue;
			}

			if (P < 600.)
			{
				found[i] = true;  // Make sure this is the last time we access this grid point

				if (!prevPInfo || !prevTInfo || !prevRHInfo)
				{
					// Lowest grid point located above 600hPa, hmm...
					continue;
				}

				// Cut search if reach level 600hPa
				prevPInfo->LocationIndex(i);
				prevTInfo->LocationIndex(i);
				prevRHInfo->LocationIndex(i);

				// Linearly interpolate temperature and humidity values to 600hPa, to check
				// if highest theta e is found there

				T = himan::numerical_functions::interpolation::Linear(600., P, prevPInfo->Value(), T,
				                                                      prevTInfo->Value());
				RH = himan::numerical_functions::interpolation::Linear(600., P, prevPInfo->Value(), RH,
				                                                       prevRHInfo->Value());

				P = 600.;
			}

			double TD = metutil::DewPointFromRH_(T, RH);
			double ThetaE = metutil::smarttool::ThetaE_(T, RH, P * 100);
			assert(ThetaE >= 0);

			if (ThetaE >= refThetaE)
			{
				refThetaE = ThetaE;
				Tresult = T;
				TDresult = TD;
				Presult = P;

				assert(TDresult > 100);
			}
		}

		size_t foundCount = count(found.begin(), found.end(), true);

		if (foundCount == found.size())
		{
			break;
		}

		itsLogger.Trace("Max ThetaE processed for " + boost::lexical_cast<string>(foundCount) + "/" +
		                boost::lexical_cast<string>(found.size()) + " grid points");

		curLevel.Value(curLevel.Value() - 1);

		prevPInfo = PInfo;
		prevTInfo = TInfo;
		prevRHInfo = RHInfo;
	}

	return make_tuple(Ttheta, TDtheta, Ptheta);
}


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

#pragma once

#include <boost/asio.hpp>
#include <iostream>
#include <memory>
#include <string>
#include <vector>

#include "client_util.hpp"
#include "seal/seal.h"
#include "tcp/tcp_client.hpp"
#include "tcp/tcp_message.hpp"

namespace ngraph {
namespace he {
class HESealClient {
 public:
  HESealClient(const std::string& hostname, const size_t port,
               const size_t batch_size, const std::vector<float>& inputs);

  ~HESealClient() = default;

  void set_seal_context();

  void handle_message(const ngraph::he::TCPMessage& message);

  void handle_relu_request(const ngraph::he::TCPMessage& message);

  inline void write_message(ngraph::he::TCPMessage&& message) {
    m_tcp_client->write_message(std::move(message));
  }

  inline bool is_done() { return m_is_done; }

  inline std::vector<float> get_results() { return m_results; }

  void close_connection();

  bool complex_packing() const { return m_complex_packing; }

  void decode_to_real_vec(const seal::Plaintext& plain,
                          std::vector<double>& output, bool complex);

 private:
  std::shared_ptr<TCPClient> m_tcp_client;
  seal::EncryptionParameters m_encryption_params{seal::scheme_type::CKKS};
  std::shared_ptr<seal::PublicKey> m_public_key;
  std::shared_ptr<seal::SecretKey> m_secret_key;
  std::shared_ptr<seal::SEALContext> m_context;
  std::shared_ptr<seal::Encryptor> m_encryptor;
  std::shared_ptr<seal::CKKSEncoder> m_ckks_encoder;
  std::shared_ptr<seal::Decryptor> m_decryptor;
  std::shared_ptr<seal::Evaluator> m_evaluator;
  std::shared_ptr<seal::KeyGenerator> m_keygen;
  std::shared_ptr<seal::RelinKeys> m_relin_keys;
  double m_scale;
  size_t m_batch_size;
  bool m_is_done;
  std::vector<float> m_inputs;   // Function inputs
  std::vector<float> m_results;  // Function outputs

  bool m_complex_packing{std::getenv("NGRAPH_COMPLEX_PACK") != nullptr};
};
}  // namespace he
}  // namespace ngraph


#include "hpp/ik/definitions.hh"
#include "hpp/ik/ik-constraint.hh"
#include "hpp/pinocchio/urdf/util.hh"
#include "hpp/pinocchio/frame.hh"

#include <vector>

#include <eigenpy/memory.hpp>
#include <eigenpy/eigenpy.hpp>

#include <boost/python.hpp>

/*** TEMPLATE SPECIALIZATION FOR PYTHON ****/
typedef hpp::ik::vector_t  vector_t;
typedef hpp::ik::vector3_t vector3_t;
typedef hpp::ik::quat_t quat_t;

/*** TEMPLATE SPECIALIZATION FOR PYTHON ****/

EIGENPY_DEFINE_STRUCT_ALLOCATOR_SPECIALIZATION(vector_t)
EIGENPY_DEFINE_STRUCT_ALLOCATOR_SPECIALIZATION(vector3_t)
EIGENPY_DEFINE_STRUCT_ALLOCATOR_SPECIALIZATION(quat_t)

using namespace hpp::ik;

namespace hppy_ik
{
using namespace boost::python;

IkHelper* wrapIkHelperConstructor(const std::string& robotName, const std::string& rootJointType,
                                  const std::string& packageName, const std::string& modelName,
                                  const std::string& urdfSuffix, const std::string& srdfSuffix)
{
    hpp::pinocchio::DevicePtr_t device = hpp::pinocchio::Device::create (robotName);
    hpp::pinocchio::urdf::loadRobotModel(device, rootJointType, packageName, modelName,
    urdfSuffix, srdfSuffix);
    return new IkHelper(device);
}

void clearProjector(IkHelper& helper)
{
    helper.proj_ = hpp::core::ConfigProjector::create(helper.device_, "proj",
                                                      helper.proj_->errorThreshold(), 40 );
}

void setConfig(IkHelper& helper, vector_t config )
{
    helper.device_->currentConfiguration(config);
    helper.device_->computeForwardKinematics();
}

vector_t getConfig(IkHelper& helper)
{
    return helper.device_->currentConfiguration();
}

bool project(IkHelper& helper)
{
    hpp::pinocchio::Configuration_t  conf = helper.device_->currentConfiguration();
    if (!helper.proj_->apply(conf))
        return false;
    setConfig(helper, conf);
    return true;
}

vector_t pos(FrameMarker& frameMarker)
{
    return frameMarker.frame_.currentTransformation().translation();
}

quat_t rot(FrameMarker& frameMarker)
{
    return quat_t(frameMarker.frame_.currentTransformation().rotation());
}

FrameMarker* warpFrameConstructor1(IkHelper& helper, const std::string& frameName)
{
    hpp::pinocchio::Frame frame = helper.device_->getFrameByName(frameName);
    return new FrameMarker(frame);
}

vector3_t com(IkHelper& helper)
{
    return helper.device_->positionCenterOfMass();
}

void    (*target6D)(IkHelper&, const FrameMarker&, const vector3_t&, const quat_t&) = &AddConstraint;
void    (*targetPosNorm)(IkHelper&, const FrameMarker&, const vector3_t&, const vector3_t&) = &AddConstraint;
void    (*maintain6D)(IkHelper&, const FrameMarker&) = &AddMaintainConstraint<MAINTAIN_6D>;
void    (*maintain3D)(IkHelper&, const FrameMarker&) = &AddMaintainConstraint<MAINTAIN_3D>;

void add3DConstraint(IkHelper& helper, const FrameMarker& frame, const vector3_t& pos)
{
    AddConstraint(helper,frame.frame_,pos);
}

BOOST_PYTHON_MODULE(hppy_ik)
{
    /** BEGIN eigenpy init**/
    eigenpy::enableEigenPy();

    /** END eigenpy init**/
    /** BEGIN struct**/

    class_<IkHelper>("IkHelper", no_init)
            .def("__init__", make_constructor(&wrapIkHelperConstructor))
            .def("clear", &clearProjector);

    class_<FrameMarker>("FrameMarker", no_init)
            .def("__init__", make_constructor(&warpFrameConstructor1))
            .def("pos", &pos)
            .def("rot", &rot);

    ;
    /** END struct**/

    /** BEGIN contraint methods **/
    def("target3D",  &add3DConstraint);
    def("target6D",  target6D);
    def("targetPosNorm",  targetPosNorm);
    def("maintain6D"   ,  maintain6D);
    def("maintain3D"   ,  maintain3D);
    def("targetCOM"    ,  &AddCOMConstraint);
    def("MaintainCOM"  ,  &AddMaintainCOMConstraint);
    /** END contraint methods **/

    /** BEGIN helper methods **/
    def("setConfig", &setConfig);
    def("getConfig", &getConfig);
    def("project"  , &project);
    def("clearProjector"  , &clearProjector);
    def("comPos"  , &com);
    /** END helper methods **/

    /** BEGIN enum types **/
    enum_<MaintainConstraintType>("MaintainConstraintType")
            .value("MAINTAIN_3D"    , MAINTAIN_3D)
            .value("MAINTAIN_6D"    , MAINTAIN_6D)
            .export_values();
    /** END enum types **/
}
} // namespace hppy_ik


//  Copyright 2015 Peter Dimov.
//
// Distributed under the Boost Software License, Version 1.0.
//
// See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt

#include <boost/mp11/integer_sequence.hpp>
#include <boost/core/lightweight_test_trait.hpp>
#include <type_traits>

int main()
{
    using boost::mp11::integer_sequence;
    using boost::mp11::make_integer_sequence;
    using boost::mp11::index_sequence;
    using boost::mp11::make_index_sequence;
    using boost::mp11::index_sequence_for;

    BOOST_TEST_TRAIT_TRUE((std::is_same<make_integer_sequence<int, 0>, integer_sequence<int>>));
    BOOST_TEST_TRAIT_TRUE((std::is_same<make_integer_sequence<int, 1>, integer_sequence<int, 0>>));
    BOOST_TEST_TRAIT_TRUE((std::is_same<make_integer_sequence<int, 2>, integer_sequence<int, 0, 1>>));
    BOOST_TEST_TRAIT_TRUE((std::is_same<make_integer_sequence<int, 3>, integer_sequence<int, 0, 1, 2>>));
    BOOST_TEST_TRAIT_TRUE((std::is_same<make_integer_sequence<int, 4>, integer_sequence<int, 0, 1, 2, 3>>));

    BOOST_TEST_TRAIT_TRUE((std::is_same<make_integer_sequence<char, 0>, integer_sequence<char>>));
    BOOST_TEST_TRAIT_TRUE((std::is_same<make_integer_sequence<char, 1>, integer_sequence<char, 0>>));
    BOOST_TEST_TRAIT_TRUE((std::is_same<make_integer_sequence<char, 2>, integer_sequence<char, 0, 1>>));
    BOOST_TEST_TRAIT_TRUE((std::is_same<make_integer_sequence<char, 3>, integer_sequence<char, 0, 1, 2>>));
    BOOST_TEST_TRAIT_TRUE((std::is_same<make_integer_sequence<char, 4>, integer_sequence<char, 0, 1, 2, 3>>));

    BOOST_TEST_TRAIT_TRUE((std::is_same<make_integer_sequence<std::size_t, 0>, integer_sequence<std::size_t>>));
    BOOST_TEST_TRAIT_TRUE((std::is_same<make_integer_sequence<std::size_t, 1>, integer_sequence<std::size_t, 0>>));
    BOOST_TEST_TRAIT_TRUE((std::is_same<make_integer_sequence<std::size_t, 2>, integer_sequence<std::size_t, 0, 1>>));
    BOOST_TEST_TRAIT_TRUE((std::is_same<make_integer_sequence<std::size_t, 3>, integer_sequence<std::size_t, 0, 1, 2>>));
    BOOST_TEST_TRAIT_TRUE((std::is_same<make_integer_sequence<std::size_t, 4>, integer_sequence<std::size_t, 0, 1, 2, 3>>));

    BOOST_TEST_TRAIT_TRUE((std::is_same<make_index_sequence<0>, integer_sequence<std::size_t>>));
    BOOST_TEST_TRAIT_TRUE((std::is_same<make_index_sequence<1>, integer_sequence<std::size_t, 0>>));
    BOOST_TEST_TRAIT_TRUE((std::is_same<make_index_sequence<2>, integer_sequence<std::size_t, 0, 1>>));
    BOOST_TEST_TRAIT_TRUE((std::is_same<make_index_sequence<3>, integer_sequence<std::size_t, 0, 1, 2>>));
    BOOST_TEST_TRAIT_TRUE((std::is_same<make_index_sequence<4>, integer_sequence<std::size_t, 0, 1, 2, 3>>));

    BOOST_TEST_TRAIT_TRUE((std::is_same<make_index_sequence<0>, index_sequence<>>));
    BOOST_TEST_TRAIT_TRUE((std::is_same<make_index_sequence<1>, index_sequence<0>>));
    BOOST_TEST_TRAIT_TRUE((std::is_same<make_index_sequence<2>, index_sequence<0, 1>>));
    BOOST_TEST_TRAIT_TRUE((std::is_same<make_index_sequence<3>, index_sequence<0, 1, 2>>));
    BOOST_TEST_TRAIT_TRUE((std::is_same<make_index_sequence<4>, index_sequence<0, 1, 2, 3>>));

    BOOST_TEST_TRAIT_TRUE((std::is_same<index_sequence_for<>, index_sequence<>>));
    BOOST_TEST_TRAIT_TRUE((std::is_same<index_sequence_for<void>, index_sequence<0>>));
    BOOST_TEST_TRAIT_TRUE((std::is_same<index_sequence_for<void, void>, index_sequence<0, 1>>));
    BOOST_TEST_TRAIT_TRUE((std::is_same<index_sequence_for<void, void, void>, index_sequence<0, 1, 2>>));
    BOOST_TEST_TRAIT_TRUE((std::is_same<index_sequence_for<void, void, void, void>, index_sequence<0, 1, 2, 3>>));

    return boost::report_errors();
}


#include <iomanip>
#include <memory>
#include <vector>

#include <Eigen/Core>
#include <aslam/common/memory.h>
#include <aslam/common/statistics/statistics.h>
#include <eigen-checks/gtest.h>
#include <glog/logging.h>
#include <gtest/gtest.h>
#include <maplab-common/feature-descriptor-ref.h>
#include <maplab-common/global-coordinate-tools.h>
#include <maplab-common/file-logger.h>
#include <maplab-common/test/testing-entrypoint.h>
#include <maplab-common/test/testing-predicates.h>

#include "simulation/generic-path-generator.h"
#include "simulation/gps-simulator.h"

namespace simulation {

namespace gps_simulator {

class GpsSimulatorTest : public ::testing::Test {
 public:
  EIGEN_MAKE_ALIGNED_OPERATOR_NEW
  virtual void SetUp() {
    settings_.mode = test_trajectory_gen::Path::kCircular;
    settings_.imu_sigmas.acc_bias_random_walk_noise_density = 0.1;
    settings_.imu_sigmas.acc_noise_density = 0.1;
    settings_.gravity_meter_by_second2 = 9.81;
    settings_.imu_sigmas.gyro_bias_random_walk_noise_density = 0.1;
    settings_.imu_sigmas.gyro_noise_density = 0.1;
    settings_.imu_noise_bias_seed = 10u;
    settings_.num_of_path_constraints = 8;
    settings_.landmark_seed = 5u;
    settings_.landmark_variance_meter = 0.25;
    settings_.num_of_landmarks = 1500;
    settings_.sampling_time_second = 0.1;
    settings_.distance_to_keypoints_meter = 7.5;

    gps_simulator_.reset(new GpsSimulator);

    if (kSaveGnssSimulationToFile) {
      const std::string filename = "/tmp/simulated_gnss.txt";
      logger_.reset(new common::FileLogger(filename));
    }
  }
  test_trajectory_gen::PathAndLandmarkSettings settings_;
  std::shared_ptr<test_trajectory_gen::GenericPathGenerator> path_generator_;
  std::unique_ptr<GpsSimulator> gps_simulator_;

  const bool kSaveGnssSimulationToFile = false;
  std::unique_ptr<common::FileLogger> logger_;
};

TEST_F(GpsSimulatorTest, testSimulateGps_noErrors) {
  path_generator_ =
      aligned_shared<test_trajectory_gen::GenericPathGenerator>(settings_);

  path_generator_->generatePath();

  aslam::TransformationVector T_G_Bs;
  path_generator_->getGroundTruthTransformations(&T_G_Bs);

  GpsSimulator::GpsSimulationSettings no_noise_settings;
  no_noise_settings.residual_zenith_ionospheric_delay_mean_s = 0.0;
  no_noise_settings.residual_zenith_ionospheric_delay_sigma_s = 0.0;
  no_noise_settings.receiver_clock_error_mean_s = 0.0;
  no_noise_settings.receiver_clock_error_sigma_s = 0.0;
  no_noise_settings.residual_satellite_clock_error_mean_s = 0.0;
  no_noise_settings.residual_satellite_clock_error_sigma_s = 0.0;
  no_noise_settings.receiver_pseudorange_noise_mean_m = 0.0;
  no_noise_settings.receiver_pseudorange_noise_sigma_m = 0.0;
  no_noise_settings.simulate_ionosphere = false;
  no_noise_settings.simulate_troposphere = false;
  gps_simulator_->setGpsSimulationSettings(no_noise_settings);
  if (kSaveGnssSimulationToFile) {
    // First block of the log contains the simulation settings.
    *logger_ << std::setprecision(15)
             << no_noise_settings.ned_origin_vector_in_llh[0] << " "
             << no_noise_settings.ned_origin_vector_in_llh[1] << " "
             << no_noise_settings.ned_origin_vector_in_llh[2] << " "
             << no_noise_settings.min_elevation_for_visibility_rad << " "
             << no_noise_settings.residual_zenith_ionospheric_delay_mean_s
             << " "
             << no_noise_settings.residual_zenith_ionospheric_delay_sigma_s
             << " " << no_noise_settings.receiver_clock_error_mean_s << " "
             << no_noise_settings.receiver_clock_error_sigma_s << " "
             << no_noise_settings.residual_satellite_clock_error_mean_s << " "
             << no_noise_settings.residual_satellite_clock_error_sigma_s << " "
             << no_noise_settings.receiver_pseudorange_noise_mean_m << " "
             << no_noise_settings.receiver_pseudorange_noise_sigma_m << " "
             << no_noise_settings.receiver_carrierphase_noise_mean_m << " "
             << no_noise_settings.receiver_carrierphase_noise_sigma_m << " "
             << no_noise_settings.rinex_navigation_file_path << " "
             << no_noise_settings.satellite_position_calculation_iterations
             << " "
             << no_noise_settings.satellite_position_calculation_threshold
             << " " << no_noise_settings.simulate_troposphere << " "
             << no_noise_settings.simulate_ionosphere << std::endl;
  }

  Aligned<std::vector, GpsMeasurement> measurements;
  NavigationData navigation_data;
  const Eigen::VectorXd timestamps = path_generator_->getTimestampsInSeconds();
  gps_simulator_->simulateMeasurements(
      timestamps, T_G_Bs, &measurements, &navigation_data);
  Eigen::Vector3d ned_origin_ecef, receiver_ecef;
  common::llhToEcef(
      no_noise_settings.ned_origin_vector_in_llh, &ned_origin_ecef);
  for (size_t i = 0u; i < measurements.size(); ++i) {
    Eigen::Vector4d estimated_position = Eigen::Vector4d::Zero();
    estimateReceiverPositionAndClockErrorFromPseudoRanges(
        measurements[i], navigation_data, &estimated_position);
    common::nedToEcef(T_G_Bs[i].getPosition(), ned_origin_ecef, &receiver_ecef);
    EXPECT_NEAR_EIGEN(receiver_ecef, estimated_position.head(3), 0.5);
    EXPECT_NEAR(
        estimated_position[3], 0.0, 0.01 * GpsSimulator::kSpeedOfLightMPerS);
    if (kSaveGnssSimulationToFile) {
      // Logging format for one measurement.
      // true_time true_receiver_x true_receiver_y true_receiver_z
      // estimated_receiver_x estimated_receiver_y estimated_receiver_z
      // estimated_receiver_clock_error number_of_satellites newline.
      // Number of satellite times: {satellite_id receiver_time
      // carrier_phase_cycles pseudo_range_m doppler_frequency_hz newline}.
      *logger_ << std::setprecision(15) << timestamps[i] << " "
               << receiver_ecef[0] << " " << receiver_ecef[1] << " "
               << receiver_ecef[2] << " " << estimated_position[0] << " "
               << estimated_position[1] << " " << estimated_position[2] << " "
               << estimated_position[3] << " "
               << measurements[i].measurements.size() << std::endl;
      for (std::vector<SatelliteMeasurement>::iterator it =
               measurements[i].measurements.begin();
           it != measurements[i].measurements.end(); ++it) {
        *logger_ << std::setprecision(15) << static_cast<int>(it->satellite_id)
                 << " " << it->time << " "
                 << it->carrier_phase_measurement_cycles[0] << " "
                 << it->pseudo_range_m[0] << " " << it->doppler_frequency_hz[0]
                 << std::endl;
      }
    }
  }
}

TEST_F(GpsSimulatorTest, testTroposphericDelay) {
  // Coordinates of the ETH tramstation in LLH.
  Eigen::Vector3d eth_llh(47.37669, 8.54866, 454.3);
  Eigen::Vector3d eth_ecef;
  common::llhToEcef(eth_llh, &eth_ecef);

  for (double angle_deg = 5.0; angle_deg < 180.0; angle_deg += 5.0) {
    SatelliteMeasurementError error =
        modelTroposphericDelays(eth_ecef, angle_deg * M_PI / 180.0);
    EXPECT_LT(error.carrier_phase_error_m, 30.0);
    EXPECT_LT(error.pseudo_range_error_m, 30.0);
  }
}

TEST_F(GpsSimulatorTest, testIonosphericDelay) {
  // Coordinates of the ETH tramstation in LLH.
  Eigen::Vector3d eth_llh(47.37669, 8.54866, 454.3);
  Eigen::Vector3d eth_ecef;
  common::llhToEcef(eth_llh, &eth_ecef);
  NavigationData navigation_data = gps_simulator_->getNavigationData();
  ros::Time receiver_gps_time =
      navigation_data.ephemeris[0].ephemeris_data_reference_time;

  for (std::vector<EphemerisData>::const_iterator it =
           navigation_data.ephemeris.begin();
       it != navigation_data.ephemeris.end(); ++it) {
    Eigen::Vector3d sat_ecef;
    getSatellitePositionAndVelocityEcef(
        *it, receiver_gps_time, &sat_ecef, nullptr, nullptr);
    // Check visibility.
    double elevation = getSatelliteElevationRad(sat_ecef, eth_ecef);
    if (elevation > 5.0 * M_PI / 180.0) {
      SatelliteMeasurementError error = modelL1IonosphericDelays(
          eth_ecef, sat_ecef, navigation_data, receiver_gps_time, 0.0);
      EXPECT_LT(fabs(error.carrier_phase_error_m), 15.0);
      EXPECT_LT(fabs(error.pseudo_range_error_m), 15.0);
    }
  }
}

}  // namespace gps_simulator
}  // namespace simulation

MAPLAB_UNITTEST_ENTRYPOINT


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

#ifndef NETKET_EXACT_SAMPLER_HPP
#define NETKET_EXACT_SAMPLER_HPP

#include <Eigen/Core>

#include "Sampler/abstract_sampler.hpp"
#include "Utils/messages.hpp"
#include "Utils/random_utils.hpp"
#include "common_types.hpp"

namespace netket {

// Exact sampling using heat bath, mostly for testing purposes on small
// systems
class ExactSampler : public AbstractSampler {
  // number of visible units
  const int nv_;
  Index sample_size_;

  // states of visible units
  RowMatrix<double> current_v_;
  VectorXcd current_log_psi_;
  std::vector<int> state_index_;

  const HilbertIndex& hilbert_index_;

  const int dim_;

  using RNG = default_random_engine;
  std::discrete_distribution<int> dist_;

  std::vector<Complex> all_log_psi_vals_;
  std::vector<double> probability_mass_;

  ExactSampler(AbstractMachine& psi, Index sample_size, std::true_type)
      : AbstractSampler(psi),
        nv_(psi.GetHilbert().Size()),
        sample_size_(sample_size),
        current_v_(sample_size, nv_),
        current_log_psi_(sample_size),
        state_index_(sample_size),
        hilbert_index_(psi.GetHilbert().GetIndex()),
        dim_(psi.GetHilbert().GetIndex().NStates()) {
    NETKET_CHECK(psi.GetHilbert().IsDiscrete(), InvalidInputError,
                 "Exact sampler works only for discrete "
                 "Hilbert spaces");
    Reset(true);
  }

 public:
  ExactSampler(AbstractMachine& psi, Index sample_size)
      : ExactSampler(psi, detail::CheckNChains("ExactSampler", sample_size),
                     {}) {}

  void Reset(bool initrandom) override {
    double logmax = -std::numeric_limits<double>::infinity();

    all_log_psi_vals_.resize(dim_);
    probability_mass_.resize(dim_);

    for (int i = 0; i < dim_; ++i) {
      auto v = hilbert_index_.NumberToState(i);
      all_log_psi_vals_[i] = GetMachine().LogValSingle(v);
      logmax = std::max(logmax, std::real(all_log_psi_vals_[i]));
    }

    for (int i = 0; i < dim_; ++i) {
      probability_mass_[i] =
          std::exp(GetMachinePow() * (all_log_psi_vals_[i] - logmax).real());
    }

    dist_ = std::discrete_distribution<int>(probability_mass_.begin(),
                                            probability_mass_.end());

    if (initrandom) {
      Sweep();
    }
  }

  void Sweep() override {
    for (Index i = 0; i < sample_size_; ++i) {
      const auto idx = dist_(GetRandomEngine());
      state_index_[i] = idx;
      current_log_psi_(i) = all_log_psi_vals_[idx];
      current_v_.row(i) = hilbert_index_.NumberToState(idx);
    }
  }

  std::pair<Eigen::Ref<const RowMatrix<double>>,
            Eigen::Ref<const Eigen::VectorXcd>>
  CurrentState() const override {
    return {current_v_, current_log_psi_};
  }

  void SetVisible(Eigen::Ref<const RowMatrix<double>> v) override {
    CheckShape(__FUNCTION__, "v", {v.rows(), v.cols()}, {sample_size_, nv_});
    current_v_ = v;
    for (Index i = 0; i < sample_size_; i++) {
      state_index_[i] = hilbert_index_.StateToNumber(current_v_.row(i));
    }
  }

  double Acceptance() const noexcept { return 1; }

  Index BatchSize() const noexcept override { return sample_size_; }

  Index NChains() const noexcept override { return sample_size_; }

  void SetMachinePow(double machine_pow) override {
    AbstractSampler::SetMachinePow(machine_pow);
    Reset(true);
  }
};

}  // namespace netket

#endif