#include <mdw/formula/StandardTypes.hpp>
#include <mdw/formula/Grammar.hpp>
#include <mdw/formula/Operator.hpp>
#include <mdw/formula/Repeated.hpp>
#include <mdw/formula/Parser.hpp>
#include <mdw/formula/Expression.hpp>
#include <mdw/formula/Casts.hpp>
#include <mdw/formula/Facts.hpp>
#include <mdw/formula/ValueException.hpp>
#include <functional>
#include <string>
#include <boost/mem_fn.hpp>

namespace mdw { namespace formula {

  class LogicalOrOperator :
    public SymmetricTypedOperator<bool, bool, std::logical_or<bool> >
  {
    public:
      LogicalOrOperator(const TypedExpression<bool>& iLeft,
                        const TypedExpression<bool>& iRight,
                        const Grammar& iGrammar,
                        const std::string& iSymbol) :
        SymmetricTypedOperator<bool, bool, std::logical_or<bool> >(iLeft, iRight, iGrammar, iSymbol)
      {}

      // PTR#12761056
      // Let's first evaluate the left operand on his own : the goal is to handle the cases where
      // the left operand throws a ValueException or flags the context as NaN in which cases,
      // since we are in an OR statement, we should ignore the exception / rollback the NaN flag
      // not to invalidate the right operand evaluation upfront
      SymmetricTypedOperator<bool, bool, std::logical_or<bool> >::ReturnType evaluate(
          IContext& ioContext) const
      {
        // Better safe than sorry
        if(ioContext.isNaN())
        {
          return false;
        }

        bool aLeftEvaluate;
        try
        {
          aLeftEvaluate = _left.evaluate(ioContext);
        }
        catch(const ValueException& aValueException)
        {
          aLeftEvaluate = false;
        }

        if(ioContext.isNaN())
        {
          ioContext.ignoreNaN();
          aLeftEvaluate = false;
        }

        return aLeftEvaluate || _right.evaluate(ioContext);
      }
  };

  Expression& StandardUnary::instantiate(ArenaAllocator& ioAllocator,
                                         const Grammar& iGrammar,
                                         const std::string& iSymbol,
                                         const Expression& iChild) const
  {
    switch (iChild.getType()) {
    case kExprInt:
      {
        if (iSymbol == "-")
        {
          return ioAllocator.create<TypedUnaryOperator<int, int, std::negate<int64_t> > >(iChild.getInt(), iGrammar, iSymbol);
        } else if (iSymbol == "(double)") {
          return ioAllocator.create<ExpressionCast<int, double> >(iChild.getInt(), iGrammar, iSymbol);
        } else if (iSymbol == "(bool)") {
          return ioAllocator.create<ExpressionCast<int, bool> >(iChild.getInt(), iGrammar, iSymbol);
        } else if (iSymbol == "(string)") {
          return ioAllocator.create<ExpressionCast<int, std::string> >(iChild.getInt(), iGrammar, iSymbol);
        } else {
          throw mdw::UnknownException("Unary operator not supported on int: " + iSymbol);
        }
        break;
      }
    case kExprDouble:
      {
        if (iSymbol == "-")
        {
          return ioAllocator.create<TypedUnaryOperator<double, double, std::negate<double> > >(iChild.getDouble(), iGrammar, iSymbol);
        } else if (iSymbol == "(int)") {
          return ioAllocator.create<ExpressionCast<double, int> >(iChild.getDouble(), iGrammar, iSymbol);
        } else if (iSymbol == "(string)") {
          return ioAllocator.create<ExpressionCast<double, std::string> >(iChild.getDouble(), iGrammar, iSymbol);
        } else {
          throw mdw::UnknownException("Unary operator not supported on double: " + iSymbol);
        }
        break;
      }
    case kExprBool:
      {
        if (iSymbol == "!") {
          return ioAllocator.create<TypedUnaryOperator<bool, bool, std::logical_not<bool> > >(iChild.getBool(), iGrammar, iSymbol);
        } else if (iSymbol == "(int)") {
          return ioAllocator.create<ExpressionCast<bool, int> >(iChild.getBool(), iGrammar, iSymbol);
        } else {
          throw mdw::UnknownException("Unary operator not supported on bool: " + iSymbol);
        }
        break;
      }
    case kExprString:
      {
        if (iSymbol == "(int)") {
          return ioAllocator.create<ExpressionCast<std::string, int> >(iChild.getString(), iGrammar, iSymbol);
        } else if (iSymbol == "(double)") {
          return ioAllocator.create<ExpressionCast<std::string, double> >(iChild.getString(), iGrammar, iSymbol);
        } else {
          throw mdw::UnknownException("Unary operator not supported on string: " + iSymbol);
        }
        break;
      }
    default:
      throw mdw::UnknownException("Unsupported unary operator: " + iSymbol + " on type " +
                                  std::string(iChild.getTypeAsString()));
    }
  }

  void StandardUnary::RegisterMe(Grammar& ioGrammar, ArenaAllocator& ioAllocator)
  {
    StandardUnary& aUnaryInstantiator = ioAllocator.create<StandardUnary>();
    ioGrammar.registerUnaryOperator(kExprInt, kExprInt, "-", aUnaryInstantiator);
    ioGrammar.registerUnaryOperator(kExprDouble, kExprDouble, "-", aUnaryInstantiator);
    ioGrammar.registerUnaryOperator(kExprBool, kExprBool, "!", aUnaryInstantiator);

    ioGrammar.registerUnaryOperator(kExprInt, kExprDouble, "(double)", aUnaryInstantiator);
    ioGrammar.registerUnaryOperator(kExprInt, kExprBool, "(bool)", aUnaryInstantiator);
    ioGrammar.registerUnaryOperator(kExprInt, kExprString, "(string)", aUnaryInstantiator);
    ioGrammar.registerUnaryOperator(kExprDouble, kExprInt, "(int)", aUnaryInstantiator);
    ioGrammar.registerUnaryOperator(kExprDouble, kExprString, "(string)", aUnaryInstantiator);
    ioGrammar.registerUnaryOperator(kExprBool, kExprInt, "(int)", aUnaryInstantiator);
    ioGrammar.registerUnaryOperator(kExprString, kExprInt, "(int)", aUnaryInstantiator);
    ioGrammar.registerUnaryOperator(kExprString, kExprDouble, "(double)", aUnaryInstantiator);
  }

  Expression& StandardBinary::instantiate(ArenaAllocator& ioAllocator,
                                          const Grammar& iGrammar,
                                          const std::string& iSymbol,
                                          const Expression& iLeft,
                                          const Expression& iRight) const
  {
    switch (iLeft.getType()) {
    case kExprInt:
      {
        if (iRight.getType() == kExprInt)
        {
          if (iSymbol == "+")
          {
            return ioAllocator.create<SymmetricTypedOperator<int, int, std::plus<int64_t> > >(iLeft.getInt(), iRight.getInt(), iGrammar, iSymbol);
          } else if (iSymbol == "-") {
            return ioAllocator.create<SymmetricTypedOperator<int, int, std::minus<int64_t> > >(iLeft.getInt(), iRight.getInt(), iGrammar, iSymbol);
          } else if (iSymbol == "*") {
            return ioAllocator.create<SymmetricTypedOperator<int, int, std::multiplies<int64_t> > >(iLeft.getInt(), iRight.getInt(), iGrammar, iSymbol);
          } else if (iSymbol == "/") {
            return ioAllocator.create<SymmetricTypedOperator<int, int, std::divides<int64_t> > >(iLeft.getInt(), iRight.getInt(), iGrammar, iSymbol);
          } else if (iSymbol == "%") {
            return ioAllocator.create<SymmetricTypedOperator<int, int, std::modulus<int64_t> > >(iLeft.getInt(), iRight.getInt(), iGrammar, iSymbol);
          } else if (iSymbol == ">") {
            return ioAllocator.create<SymmetricTypedOperator<int, bool, std::greater<int64_t> > >(iLeft.getInt(), iRight.getInt(), iGrammar, iSymbol);
          } else if (iSymbol == ">=") {
            return ioAllocator.create<SymmetricTypedOperator<int, bool, std::greater_equal<int64_t> > >(iLeft.getInt(), iRight.getInt(), iGrammar, iSymbol);
          } else if (iSymbol == "<") {
            return ioAllocator.create<SymmetricTypedOperator<int, bool, std::less<int64_t> > >(iLeft.getInt(), iRight.getInt(), iGrammar, iSymbol);
          } else if (iSymbol == "<=") {
            return ioAllocator.create<SymmetricTypedOperator<int, bool, std::less_equal<int64_t> > >(iLeft.getInt(), iRight.getInt(), iGrammar, iSymbol);
          } else if (iSymbol == "!=") {
            return ioAllocator.create<SymmetricTypedOperator<int, bool, std::not_equal_to<int64_t> > >(iLeft.getInt(), iRight.getInt(), iGrammar, iSymbol);
          } else if (iSymbol == "==") {
            return ioAllocator.create<SymmetricTypedOperator<int, bool, std::equal_to<int64_t> > >(iLeft.getInt(), iRight.getInt(), iGrammar, iSymbol);
          } else {
            throw mdw::UnknownException("Binary operator not supported on int: " + iSymbol);
          }
        }
        break;
      }
    case kExprDouble:
      {
        if (iRight.getType() == kExprDouble)
        {
          if (iSymbol == "+")
          {
            return ioAllocator.create<SymmetricTypedOperator<double, double, std::plus<double> > >(iLeft.getDouble(), iRight.getDouble(), iGrammar, iSymbol);
          } else if (iSymbol == "-") {
            return ioAllocator.create<SymmetricTypedOperator<double, double, std::minus<double> > >(iLeft.getDouble(), iRight.getDouble(), iGrammar, iSymbol);
          } else if (iSymbol == "*") {
            return ioAllocator.create<SymmetricTypedOperator<double, double, std::multiplies<double> > >(iLeft.getDouble(), iRight.getDouble(), iGrammar, iSymbol);
          } else if (iSymbol == "/") {
            return ioAllocator.create<SymmetricTypedOperator<double, double, std::divides<double> > >(iLeft.getDouble(), iRight.getDouble(), iGrammar, iSymbol);
          } else if (iSymbol == ">") {
            return ioAllocator.create<SymmetricTypedOperator<double, bool, std::greater<double> > >(iLeft.getDouble(), iRight.getDouble(), iGrammar, iSymbol);
          } else if (iSymbol == ">=") {
            return ioAllocator.create<SymmetricTypedOperator<double, bool, std::greater_equal<double> > >(iLeft.getDouble(), iRight.getDouble(), iGrammar, iSymbol);
          } else if (iSymbol == "<") {
            return ioAllocator.create<SymmetricTypedOperator<double, bool, std::less<double> > >(iLeft.getDouble(), iRight.getDouble(), iGrammar, iSymbol);
          } else if (iSymbol == "<=") {
            return ioAllocator.create<SymmetricTypedOperator<double, bool, std::less_equal<double> > >(iLeft.getDouble(), iRight.getDouble(), iGrammar, iSymbol);
          } else if (iSymbol == "!=") {
            return ioAllocator.create<SymmetricTypedOperator<double, bool, std::not_equal_to<double> > >(iLeft.getDouble(), iRight.getDouble(), iGrammar, iSymbol);
          } else if (iSymbol == "==") {
            return ioAllocator.create<SymmetricTypedOperator<double, bool, std::equal_to<double> > >(iLeft.getDouble(), iRight.getDouble(), iGrammar, iSymbol);
          } else {
            throw mdw::UnknownException("Binary operator not supported on double: " + iSymbol);
          }
        }
        break;
      }
    case kExprBool:
      {
        if (iRight.getType() == kExprBool)
        {
          if (iSymbol == "&&")
          {
            return ioAllocator.create<SymmetricTypedOperator<bool, bool, std::logical_and<bool> > >(iLeft.getBool(), iRight.getBool(), iGrammar, iSymbol);
          } else if (iSymbol == "||") {
            return ioAllocator.create<LogicalOrOperator>(iLeft.getBool(), iRight.getBool(), iGrammar, iSymbol);
          } else if (iSymbol == "==") {
            return ioAllocator.create<SymmetricTypedOperator<bool, bool, std::equal_to<bool> > >(iLeft.getBool(), iRight.getBool(), iGrammar, iSymbol);
          } else if (iSymbol == "!=") {
            return ioAllocator.create<SymmetricTypedOperator<bool, bool, std::not_equal_to<bool> > >(iLeft.getBool(), iRight.getBool(), iGrammar, iSymbol);
          } else {
            throw mdw::UnknownException("Binary operator not supported on bool: " + iSymbol);
          }
        }
        break;
      }
    case kExprString:
      {
        if (iRight.getType() == kExprString)
        {
          if (iSymbol == "==")
          {
            return ioAllocator.create<SymmetricTypedOperator<std::string, bool, std::equal_to<std::string> > >(iLeft.getString(), iRight.getString(), iGrammar, iSymbol);
          } else if (iSymbol == "!=") {
            return ioAllocator.create<SymmetricTypedOperator<std::string, bool, std::not_equal_to<std::string> > >(iLeft.getString(), iRight.getString(), iGrammar, iSymbol);
          } else if (iSymbol == ">") {
            return ioAllocator.create<SymmetricTypedOperator<std::string, bool, std::greater<std::string> > >(iLeft.getString(), iRight.getString(), iGrammar, iSymbol);
          } else if (iSymbol == ">=") {
            return ioAllocator.create<SymmetricTypedOperator<std::string, bool, std::greater_equal<std::string> > >(iLeft.getString(), iRight.getString(), iGrammar, iSymbol);
          } else if (iSymbol == "<") {
            return ioAllocator.create<SymmetricTypedOperator<std::string, bool, std::less<std::string> > >(iLeft.getString(), iRight.getString(), iGrammar, iSymbol);
          } else if (iSymbol == "<=") {
            return ioAllocator.create<SymmetricTypedOperator<std::string, bool, std::less_equal<std::string> > >(iLeft.getString(), iRight.getString(), iGrammar, iSymbol);
          } else {
            throw mdw::UnknownException("Binary operator not supported on string: " + iSymbol);
          }
        } else if (iRight.getType() == kExprInt) {
          if (iSymbol == "[]")
          {
            SquareOp<size_t, std::string> anOp;
            return ioAllocator.create<SquareOperator<SquareOp<size_t, std::string> > >(iLeft.getString(),
                                                                                iRight.getLong(),
                                                                                iGrammar,
                                                                                anOp);
          } else {
            throw mdw::UnknownException("Binary operator not supported on string: " + iSymbol);
          }
          break;
        }
      }
    default:
      throw mdw::UnknownException("Unsupported binary operator: " + iSymbol + " on types " +
                                  std::string(iLeft.getTypeAsString()) + " and " +
                                  std::string(iRight.getTypeAsString()));
    }
    throw mdw::UnknownException("Unsupported binary operator: " + iSymbol + " on types " +
                                std::string(iLeft.getTypeAsString()) + " and " +
                                std::string(iRight.getTypeAsString()));
  }

  void StandardBinary::RegisterMe(Grammar& ioGrammar, ArenaAllocator& ioAllocator)
  {
    StandardBinary& aBinaryInstantiator = ioAllocator.create<StandardBinary>();
    ioGrammar.registerBinaryOperator(kExprInt, kExprInt, kExprInt, "+", aBinaryInstantiator);
    ioGrammar.registerBinaryOperator(kExprInt, kExprInt, kExprInt, "-", aBinaryInstantiator);
    ioGrammar.registerBinaryOperator(kExprInt, kExprInt, kExprInt, "*", aBinaryInstantiator);
    ioGrammar.registerBinaryOperator(kExprInt, kExprInt, kExprInt, "/", aBinaryInstantiator);
    ioGrammar.registerBinaryOperator(kExprInt, kExprInt, kExprInt, "%", aBinaryInstantiator);

    ioGrammar.registerBinaryOperator(kExprInt, kExprInt, kExprBool, ">", aBinaryInstantiator);
    ioGrammar.registerBinaryOperator(kExprInt, kExprInt, kExprBool, ">=", aBinaryInstantiator);
    ioGrammar.registerBinaryOperator(kExprInt, kExprInt, kExprBool, "<", aBinaryInstantiator);
    ioGrammar.registerBinaryOperator(kExprInt, kExprInt, kExprBool, "<=", aBinaryInstantiator);
    ioGrammar.registerBinaryOperator(kExprInt, kExprInt, kExprBool, "!=", aBinaryInstantiator);
    ioGrammar.registerBinaryOperator(kExprInt, kExprInt, kExprBool, "==", aBinaryInstantiator);

    ioGrammar.registerBinaryOperator(kExprDouble, kExprDouble, kExprDouble, "+",
                                     aBinaryInstantiator);
    ioGrammar.registerBinaryOperator(kExprDouble, kExprDouble, kExprDouble, "-",
                                     aBinaryInstantiator);
    ioGrammar.registerBinaryOperator(kExprDouble, kExprDouble, kExprDouble, "*",
                                     aBinaryInstantiator);
    ioGrammar.registerBinaryOperator(kExprDouble, kExprDouble, kExprDouble, "/",
                                     aBinaryInstantiator);

    ioGrammar.registerBinaryOperator(kExprDouble, kExprDouble, kExprBool, ">", aBinaryInstantiator);
    ioGrammar.registerBinaryOperator(kExprDouble, kExprDouble, kExprBool, ">=",
                                     aBinaryInstantiator);
    ioGrammar.registerBinaryOperator(kExprDouble, kExprDouble, kExprBool, "<", aBinaryInstantiator);
    ioGrammar.registerBinaryOperator(kExprDouble, kExprDouble, kExprBool, "<=",
                                     aBinaryInstantiator);
    ioGrammar.registerBinaryOperator(kExprDouble, kExprDouble, kExprBool, "!=",
                                     aBinaryInstantiator);
    ioGrammar.registerBinaryOperator(kExprDouble, kExprDouble, kExprBool, "==",
                                     aBinaryInstantiator);

    ioGrammar.registerBinaryOperator(kExprBool, kExprBool, kExprBool, "&&", aBinaryInstantiator);
    ioGrammar.registerBinaryOperator(kExprBool, kExprBool, kExprBool, "||", aBinaryInstantiator);
    ioGrammar.registerBinaryOperator(kExprBool, kExprBool, kExprBool, "==", aBinaryInstantiator);
    ioGrammar.registerBinaryOperator(kExprBool, kExprBool, kExprBool, "!=", aBinaryInstantiator);

    ioGrammar.registerBinaryOperator(kExprString, kExprString, kExprBool, "==",
                                     aBinaryInstantiator);
    ioGrammar.registerBinaryOperator(kExprString, kExprString, kExprBool, "!=",
                                     aBinaryInstantiator);
    ioGrammar.registerBinaryOperator(kExprString, kExprString, kExprBool, ">", aBinaryInstantiator);
    ioGrammar.registerBinaryOperator(kExprString, kExprString, kExprBool, ">=",
                                     aBinaryInstantiator);
    ioGrammar.registerBinaryOperator(kExprString, kExprString, kExprBool, "<", aBinaryInstantiator);
    ioGrammar.registerBinaryOperator(kExprString, kExprString, kExprBool, "<=",
                                     aBinaryInstantiator);
    ioGrammar.registerBinaryOperator(kExprString, kExprInt, kExprInt, "[]", aBinaryInstantiator);
  }

}}


// Distributed under the MIT License.
// See LICENSE.txt for details.

#pragma once

#include <algorithm>
#include <array>
#include <complex>
#include <cstddef>

#include "DataStructures/BoostMultiArray.hpp"
#include "DataStructures/ComplexDataVector.hpp"
#include "DataStructures/ComplexModalVector.hpp"
#include "DataStructures/DataVector.hpp"
#include "DataStructures/ModalVector.hpp"
#include "DataStructures/VectorImpl.hpp"

// Boost MultiArray is used internally in odeint, so odeint must be included
// later
#include <boost/numeric/odeint.hpp>

namespace detail {
struct vector_impl_algebra : boost::numeric::odeint::vector_space_algebra {
  template <typename VectorType>
  static double norm_inf(const VectorType& vector) noexcept {
    return max(abs(vector));
  }
};

struct vector_impl_array_algebra : boost::numeric::odeint::array_algebra {
  template <typename VectorType, size_t Size>
  static double norm_inf(
      const std::array<VectorType, Size>& vector_array) noexcept {
    return std::accumulate(
        vector_array.begin(), vector_array.end(), 0.0,
        [](const double maximum_so_far, const VectorType& element) noexcept {
          return std::max(maximum_so_far, max(abs(element)));
        });
  }
};
}  // namespace detail

namespace boost {
namespace numeric {
namespace odeint {

namespace detail {
template <typename V>
struct set_unit_value_impl<ComplexDataVector, V, void> {
  static void set_value(ComplexDataVector& t, const V& v) noexcept {  // NOLINT
    // this ensures that the blaze expression template resolve the result to a
    // complex vector
    t = std::complex<double>(1.0, 0.0) * v;
  }
};
template <typename V>
struct set_unit_value_impl<ComplexModalVector, V, void> {
  static void set_value(ComplexModalVector& t, const V& v) noexcept {  // NOLINT
    // this ensures that the blaze expression template resolve the result to a
    // complex vector
    t = std::complex<double>(1.0, 0.0) * v;
  }
};
}  // namespace detail

// In some integration contexts, boost requires the ability to resize the
// integration arguments in preparation for writing to output buffers passed by
// reference. These specializations make the resize work correctly with spectre
// vector types.
template <class VectorType1, class VectorType2>
struct resize_impl_sfinae<VectorType1, VectorType2,
                          typename boost::enable_if_c<
                              is_derived_of_vector_impl_v<VectorType1> and
                              is_derived_of_vector_impl_v<VectorType2>>::type> {
  static void resize(VectorType1& x1, const VectorType2& x2) noexcept {
    x1.destructive_resize(x2.size());
  }
};

template <class VectorType>
struct is_resizeable_sfinae<
    VectorType,
    typename boost::enable_if_c<is_derived_of_vector_impl_v<VectorType>>::type>
    : boost::true_type {};

template <>
struct algebra_dispatcher<DataVector> {
  using algebra_type = ::detail::vector_impl_algebra;
};

template <>
struct algebra_dispatcher<ComplexDataVector> {
  using algebra_type = ::detail::vector_impl_algebra;
};

template <>
struct algebra_dispatcher<ModalVector> {
  using algebra_type = ::detail::vector_impl_algebra;
};

template <>
struct algebra_dispatcher<ComplexModalVector> {
  using algebra_type = ::detail::vector_impl_algebra;
};

template <size_t Size>
struct algebra_dispatcher<std::array<DataVector, Size>> {
  using algebra_type = ::detail::vector_impl_array_algebra;
};

template <size_t Size>
struct algebra_dispatcher<std::array<ComplexDataVector, Size>> {
  using algebra_type = ::detail::vector_impl_array_algebra;
};

template <size_t Size>
struct algebra_dispatcher<std::array<ModalVector, Size>> {
  using algebra_type = ::detail::vector_impl_array_algebra;
};

template <size_t Size>
struct algebra_dispatcher<std::array<ComplexModalVector, Size>> {
  using algebra_type = ::detail::vector_impl_array_algebra;
};
}  // namespace odeint
}  // namespace numeric
}  // namespace boost

/*!
 * \ingroup NumericalAlgorithmsGroup
 * \brief For ODE integration, we suggest using the boost libraries whenever
 * possible.
 *
 * \details Here we describe briefly the suggested setup to use a boost ODE
 * integrator in SpECTRE. The boost utilities have a number of more elaborate
 * features, including features that may result in simpler function calls than
 * the specific recipes below. For full documentation, see the Boost odeint
 * documentation:
 * https://www.boost.org/doc/libs/1_72_0/libs/numeric/odeint/doc/html/boost_numeric_odeint/
 *
 * The integration methods can be used largely as in the boost documentation. We
 * summarize the salient points necessary to get an example running. The main
 * steps in using a boost integrator are:
 * - define the system
 * - construct the stepper
 * - initialize (might be performed implicitly, depending on the stepper)
 * - perform steps
 * - (compute dense output, for dense-output steppers)
 *
 * For most cases the Dormand-Prince fifth order controlled, dense-output
 * stepper is recommended. That stepper is used in the section "SpECTRE vectors
 * or `std::array` thereof in boost integrators" below.
 *
 * #### Fundamental types or `std::array` thereof in fixed-step integrators
 *
 * Let us consider a simple oscillator system, which we'll declare as a lambda:
 *
 * \snippet Test_OdeIntegration.cpp explicit_fundamental_array_system
 *
 * Note that the first argument is a `const` lvalue reference to the state type,
 * `std::array<double, 2>`, the second argument is an lvalue reference for the
 * time derivatives that is written to by the system function, and the final
 * argument is the current time.
 *
 * The construction and initialization of the stepper is simple:
 *
 * \snippet Test_OdeIntegration.cpp explicit_fundamental_stepper_construction
 *
 * Finally, we can perform the steps and examine the output,
 *
 * \snippet Test_OdeIntegration.cpp explicit_fundamental_stepper_use
 *
 * #### Fundamental types or `std::array` thereof in Dense-output integrators
 *
 * The dense-output and controlled-step-size ODE integrators in boost comply
 * with a somewhat different interface, as significantly more state information
 * must be managed. The result is a somewhat simpler, but more opaque, interface
 * to the user code.
 *
 * Once again, we start by constructing the system we'd like to integrate,
 *
 * \snippet Test_OdeIntegration.cpp dense_output_fundamental_system
 *
 * The constructor for dense steppers takes optional arguments for tolerance,
 * and dense steppers need to be initialized.
 *
 * \snippet Test_OdeIntegration.cpp dense_output_fundamental_construction
 *
 * It is important to take note of the tolerance arguments, as the defaults are
 * `1.0e-6`, which is often looser than we want for calculations in SpECTRE.
 *
 * We then perform the step supplying the system function, and can retrieve the
 * dense output state with `calc_state`, which returns by reference by the
 * second argument.
 *
 * \snippet Test_OdeIntegration.cpp dense_output_fundamental_stepper_use
 *
 * #### SpECTRE vectors or `std::array` thereof in boost integrators
 *
 * The additional template specializations present in the `OdeIntegration.hpp`
 * file ensure that the boost ODE methods work transparently for SpECTRE vectors
 * as well. We'll run through a brief example to emphasize the functionality.
 *
 * \snippet Test_OdeIntegration.cpp dense_output_vector_stepper
 */
namespace OdeIntegration {}


#pragma once

#include "../tags/QF_BV.hpp"
#include "../result_wrapper.hpp"

#include <cvc4/cvc4.h>

#include <boost/mpl/map/map40.hpp>
#include <boost/any.hpp>
#include <boost/tuple/tuple.hpp>
#include <list>

namespace metaSMT {
  namespace solver {
    namespace predtags = ::metaSMT::logic::tag;
    namespace bvtags = ::metaSMT::logic::QF_BV::tag;

    /**
     * @ingroup Backend
     * @class CVC4 CVC4.hpp metaSMT/backend/CVC4.hpp
     * @brief The CVC4 backend
     */
    class CVC4 {
    public:
      typedef ::CVC4::Expr result_type;
      typedef std::list< ::CVC4::Expr > Exprs;

      CVC4()
        : exprManager_( )
        , engine_( &exprManager_ )
        , isPushed_( false )
      {
        engine_.setOption("incremental", true);
        engine_.setOption("produce-models", true);
      }

      ~CVC4() {
      }

      void assertion( result_type e ) {
        assertions_.push_back( e );
      }

      void assumption( result_type e ) {
        assumptions_.push_back( e );
      }

      bool solve() {
        removeOldAssumptions();
        pushAssertions();
        pushAssumptions();

        return engine_.checkSat().isSat();
      }

      result_wrapper read_value(result_type var) {

        ::CVC4::Expr value = engine_.getValue(var);
        ::CVC4::Type type = value.getType();

        if (type.isBoolean()) {
          return value.getConst<bool>();

        } else if (type.isBitVector()) {
          ::CVC4::BitVector bvValue = value.getConst< ::CVC4::BitVector >();
          return bvValue.toString();
        }
        return result_wrapper( false );
      }

      // predtags
      result_type operator()( predtags::var_tag const & var, boost::any args ) {
        return exprManager_.mkVar(exprManager_.booleanType());
      }

      result_type operator()( predtags::false_tag , boost::any arg ) {
        return exprManager_.mkConst(false);
      }

      result_type operator()( predtags::true_tag , boost::any arg ) {
        return exprManager_.mkConst(true);
      }

      result_type operator()( predtags::not_tag , result_type e ) {
        return exprManager_.mkExpr(::CVC4::kind::NOT, e);
      }

      result_type operator()( predtags::ite_tag tag
                              , result_type a, result_type b, result_type c ) {
        return exprManager_.mkExpr(::CVC4::kind::ITE, a, b, c);
      }

      // bvtags
      result_type operator()( bvtags::var_tag const & var, boost::any args ) {
        assert ( var.width != 0 );
        ::CVC4::Type bv_ty = exprManager_.mkBitVectorType(var.width);
        return exprManager_.mkVar(bv_ty);
      }

      result_type operator()( bvtags::bit0_tag , boost::any arg ) {
        return exprManager_.mkConst(::CVC4::BitVector(1u, 0u));
      }

      result_type operator()( bvtags::bit1_tag , boost::any arg ) {
        return exprManager_.mkConst(::CVC4::BitVector(1u, 1u));
      }

      result_type operator()( bvtags::bvuint_tag , boost::any arg ) {
        typedef boost::tuple<unsigned long, unsigned long> Tuple;
        Tuple tuple = boost::any_cast<Tuple>(arg);
        unsigned long value = boost::get<0>(tuple);
        unsigned long width = boost::get<1>(tuple);

        return exprManager_.mkConst(::CVC4::BitVector(width, value));
      }

      result_type operator()( bvtags::bvsint_tag , boost::any arg ) {
        typedef boost::tuple<long, unsigned long> Tuple;
        Tuple tuple = boost::any_cast<Tuple>(arg);
        long value = boost::get<0>(tuple);
        unsigned long width = boost::get<1>(tuple);

        ::CVC4::BitVector bvValue (width, ::CVC4::Integer(value));
        return exprManager_.mkConst(bvValue);
      }

      result_type operator()( bvtags::bvbin_tag , boost::any arg ) {
        std::string val = boost::any_cast<std::string>(arg);
        return exprManager_.mkConst(::CVC4::BitVector(val));
      }

      result_type operator()( bvtags::bvhex_tag , boost::any arg ) {
        std::string hex = boost::any_cast<std::string>(arg);
        return exprManager_.mkConst(::CVC4::BitVector(hex, 16));
      }

      result_type operator()( bvtags::bvnot_tag , result_type e ) {
        return exprManager_.mkExpr(::CVC4::kind::BITVECTOR_NOT, e);
      }

      result_type operator()( bvtags::bvneg_tag , result_type e ) {
        return exprManager_.mkExpr(::CVC4::kind::BITVECTOR_NEG, e);
      }

      result_type operator()( bvtags::extract_tag const &
        , unsigned long upper, unsigned long lower
        , result_type e)
      {
        ::CVC4::BitVectorExtract bvOp (upper, lower);
        ::CVC4::Expr op = exprManager_.mkConst(bvOp);
        return exprManager_.mkExpr(op, e);
      }

      result_type operator()( bvtags::zero_extend_tag const &
        , unsigned long width
        , result_type e)
      {
        ::CVC4::BitVectorZeroExtend bvOp (width);
        ::CVC4::Expr op = exprManager_.mkConst(bvOp);
        return exprManager_.mkExpr(op, e);
      }

      result_type operator()( bvtags::sign_extend_tag const &
        , unsigned long width
        , result_type e)
      {
        ::CVC4::BitVectorSignExtend bvOp (width);
        ::CVC4::Expr op = exprManager_.mkConst(bvOp);
        return exprManager_.mkExpr(op, e);
      }

      result_type operator()( predtags::equal_tag const &
                             , result_type a
                             , result_type b) {
        if (a.getType().isBoolean() && b.getType().isBoolean() ) {
          return exprManager_.mkExpr(::CVC4::kind::IFF, a, b);
        } else {
          return exprManager_.mkExpr(::CVC4::kind::EQUAL, a, b);
        }
      }

      result_type operator()( predtags::nequal_tag const &
                             , result_type a
                             , result_type b) {
        return exprManager_.mkExpr(::CVC4::kind::DISTINCT, a, b);
      }

      result_type operator()( predtags::distinct_tag const &
                             , result_type a
                             , result_type b) {
        return exprManager_.mkExpr(::CVC4::kind::DISTINCT, a, b);
      }

      ////////////////////////
      // Fallback operators //
      ////////////////////////

      template <typename TagT>
      result_type operator() (TagT tag, boost::any args ) {
        assert( false );
        return exprManager_.mkConst(false);
      }

      template< ::CVC4::kind::Kind_t KIND_ >
      struct Op2 {
        static result_type exec(::CVC4::ExprManager& em, ::CVC4::Expr x,
          ::CVC4::Expr y)
        {
          return em.mkExpr(KIND_, x, y);
        }
      };

      template< ::CVC4::kind::Kind_t KIND_ >
      struct NotOp2 {
        static result_type exec(::CVC4::ExprManager& em, ::CVC4::Expr x,
          ::CVC4::Expr y)
        {
          return em.mkExpr(::CVC4::kind::NOT, em.mkExpr(KIND_, x, y));
        }
      };

      template <typename TagT>
      result_type operator() (TagT tag, result_type a, result_type b) {
        namespace mpl = boost::mpl;
        using namespace ::CVC4::kind;

        typedef mpl::map33<
          // binary Logic tags
          mpl::pair<predtags::and_tag,     Op2<AND> >
        , mpl::pair<predtags::nand_tag,    NotOp2<AND> >
        , mpl::pair<predtags::or_tag,      Op2<OR> >
        , mpl::pair<predtags::nor_tag,     NotOp2<OR> >
        , mpl::pair<predtags::xor_tag,     Op2<XOR> >
        , mpl::pair<predtags::xnor_tag,    NotOp2<XOR> >
        , mpl::pair<predtags::implies_tag, Op2<IMPLIES> >
        // binary QF_BV tags
        , mpl::pair<bvtags::bvand_tag,     Op2<BITVECTOR_AND> >
        , mpl::pair<bvtags::bvnand_tag,    Op2<BITVECTOR_NAND> >
        , mpl::pair<bvtags::bvor_tag,      Op2<BITVECTOR_OR> >
        , mpl::pair<bvtags::bvnor_tag,     Op2<BITVECTOR_NOR> >
        , mpl::pair<bvtags::bvxor_tag,     Op2<BITVECTOR_XOR> >
        , mpl::pair<bvtags::bvxnor_tag,    Op2<BITVECTOR_XNOR> >
        , mpl::pair<bvtags::bvadd_tag,     Op2<BITVECTOR_PLUS> >
        , mpl::pair<bvtags::bvsub_tag,     Op2<BITVECTOR_SUB> >
        , mpl::pair<bvtags::bvmul_tag,     Op2<BITVECTOR_MULT> >
        , mpl::pair<bvtags::bvudiv_tag,    Op2<BITVECTOR_UDIV> >
        , mpl::pair<bvtags::bvurem_tag,    Op2<BITVECTOR_UREM> >
        , mpl::pair<bvtags::bvsdiv_tag,    Op2<BITVECTOR_SDIV> >
        , mpl::pair<bvtags::bvsrem_tag,    Op2<BITVECTOR_SREM> >
        , mpl::pair<bvtags::bvslt_tag,     Op2<BITVECTOR_SLT> >
        , mpl::pair<bvtags::bvsle_tag,     Op2<BITVECTOR_SLE> >
        , mpl::pair<bvtags::bvsgt_tag,     Op2<BITVECTOR_SGT> >
        , mpl::pair<bvtags::bvsge_tag,     Op2<BITVECTOR_SGE> >
        , mpl::pair<bvtags::bvult_tag,     Op2<BITVECTOR_ULT> >
        , mpl::pair<bvtags::bvule_tag,     Op2<BITVECTOR_ULE> >
        , mpl::pair<bvtags::bvugt_tag,     Op2<BITVECTOR_UGT> >
        , mpl::pair<bvtags::bvuge_tag,     Op2<BITVECTOR_UGE> >
        , mpl::pair<bvtags::concat_tag,    Op2<BITVECTOR_CONCAT> >
        , mpl::pair<bvtags::bvcomp_tag,    Op2<BITVECTOR_COMP> >
        , mpl::pair<bvtags::bvshl_tag,     Op2<BITVECTOR_SHL> >
        , mpl::pair<bvtags::bvshr_tag,     Op2<BITVECTOR_LSHR> >
        , mpl::pair<bvtags::bvashr_tag,    Op2<BITVECTOR_ASHR> >
        > Opcode_Map;

        typedef
          typename mpl::has_key< Opcode_Map, TagT >::type
          has_key;

        if (has_key::value ) {
          typedef typename mpl::eval_if<
            has_key
          , mpl::at< Opcode_Map, TagT >
          , mpl::identity< Op2<UNDEFINED_KIND> >
          >::type opcode;
          return opcode::exec(exprManager_, a, b);
        }
        else {
          // std::cerr << "unknown operator: " << tag << std::endl;
          assert(false && "unknown operator");
        }
        return exprManager_.mkConst(false);
      }

      template <typename TagT>
      result_type operator() (TagT tag, result_type a, result_type b, result_type c) {
        assert( false );
        return exprManager_.mkConst(false);
      }

      // pseudo command
      void command ( CVC4 const & ) { };

    private:
      void removeOldAssumptions() {
        if (isPushed_) {
          engine_.pop();
          isPushed_ = false;
        }
      }

      void pushAssumptions() {
        engine_.push();
        isPushed_ = true;

        applyAssertions(assumptions_);
        assumptions_.clear();
      }

      void pushAssertions() {
        applyAssertions(assertions_);
        assertions_.clear();
      }

      void applyAssertions(Exprs const& expressions) {
        for ( Exprs::const_iterator it = expressions.begin(),
              ie = expressions.end(); it != ie; ++it ) {
          engine_.assertFormula(*it);
        }
      }

    private:
      ::CVC4::ExprManager exprManager_;
      ::CVC4::SmtEngine engine_;
      Exprs assumptions_;
      Exprs assertions_;
      bool isPushed_;
    }; // class CVC4

  } // solver
} // metaSMT


// Boost.Geometry

// Copyright (c) 2020-2021, Oracle and/or its affiliates.

// Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle

// Licensed under the Boost Software License version 1.0.
// http://www.boost.org/users/license.html

#ifndef BOOST_GEOMETRY_STRATEGIES_CONVEX_HULL_GEOGRAPHIC_HPP
#define BOOST_GEOMETRY_STRATEGIES_CONVEX_HULL_GEOGRAPHIC_HPP

#include <boost/geometry/strategies/convex_hull/services.hpp>
#include <boost/geometry/strategies/detail.hpp>
#include <boost/geometry/strategies/geographic/side.hpp>
#include <boost/geometry/strategies/spherical/point_in_point.hpp>
#include <boost/geometry/util/type_traits.hpp>

namespace boost { namespace geometry
{

namespace strategies { namespace convex_hull
{

template
<
    typename FormulaPolicy = strategy::andoyer,
    typename Spheroid = srs::spheroid<double>,
    typename CalculationType = void
>
class geographic : public strategies::detail::geographic_base<Spheroid>
{
    using base_t = strategies::detail::geographic_base<Spheroid>;

public:
    geographic() = default;

    explicit geographic(Spheroid const& spheroid)
        : base_t(spheroid)
    {}

    template <typename Geometry1, typename Geometry2>
    static auto relate(Geometry1 const&, Geometry2 const&,
                       std::enable_if_t
                            <
                                util::is_pointlike<Geometry1>::value
                             && util::is_pointlike<Geometry2>::value
                            > * = nullptr)
    {
        return strategy::within::spherical_point_point();
    }

    auto side() const
    {
        return strategy::side::geographic
            <
                FormulaPolicy,
                Spheroid,
                CalculationType
            >(base_t::m_spheroid);
    }
};

namespace services
{

template <typename Geometry>
struct default_strategy<Geometry, geographic_tag>
{
    using type = strategies::convex_hull::geographic<>;
};

} // namespace services

}} // namespace strategies::convex_hull

}} // namespace boost::geometry

#endif // BOOST_GEOMETRY_STRATEGIES_CONVEX_HULL_GEOGRAPHIC_HPP


/*
 * Copyright 2020 Robert Bosch GmbH
 *
 * 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.
 *
 * SPDX-License-Identifier: Apache-2.0
 */
/**
 * \file osi_sensor_component.cpp
 * \see  osi_sensor_component.hpp
 */

#include "osi_sensor_component.hpp"

#include <sstream>  // for istringstream
#include <string>   // for string

#include <boost/math/constants/constants.hpp>  // for pi
#include <boost/property_tree/ptree.hpp>       // for ptree
#include <boost/property_tree/xml_parser.hpp>  // for read_xml

#include <cloe/simulator.hpp>  // for ModelError

#include "osi_omni_sensor.hpp"  // for SensorMockTarget
#include "vtd_conf.hpp"         // for VtdSensorConfig

namespace vtd {

void VtdOsiSensor::configure(const VtdSensorConfig& cfg) {
  boost::property_tree::ptree pt;
  std::istringstream is(cfg.xml);
  boost::property_tree::read_xml(is, pt);
  boost::property_tree::ptree child;

  // In VTD v2.2, the sensor mounting position is not provided as OSI message.
  // Hence, the coordinate reference frame transformations rely on the
  // configuration input stored in vtd_mnt_pos_dxyz_ and vtd_mnt_ori_drpy_.
  child = pt.get_child("Sensor.Position");
  vtd_mnt_pos_dxyz_(0) = child.get<double>("<xmlattr>.dx");
  vtd_mnt_pos_dxyz_(1) = child.get<double>("<xmlattr>.dy");
  vtd_mnt_pos_dxyz_(2) = child.get<double>("<xmlattr>.dz");

  auto deg_to_rad = [](const double deg) {
    return deg / 180 * boost::math::constants::pi<double>();
  };
  vtd_mnt_ori_drpy_(0) = deg_to_rad(child.get<double>("<xmlattr>.drDeg"));
  vtd_mnt_ori_drpy_(1) = deg_to_rad(child.get<double>("<xmlattr>.dpDeg"));
  vtd_mnt_ori_drpy_(2) = deg_to_rad(child.get<double>("<xmlattr>.dhDeg"));

  child = pt.get_child("Sensor.Frustum");
  frustum_.clip_near = child.get<double>("<xmlattr>.near");
  frustum_.clip_far = child.get<double>("<xmlattr>.far");
  double ang_deg = child.get<double>("<xmlattr>.left");
  double fov_deg = ang_deg + child.get<double>("<xmlattr>.right");
  frustum_.fov_h = deg_to_rad(fov_deg);
  frustum_.offset_h = deg_to_rad(ang_deg) - 0.5 * frustum_.fov_h;
  ang_deg = child.get<double>("<xmlattr>.bottom");
  fov_deg = ang_deg + child.get<double>("<xmlattr>.top");
  frustum_.fov_v = deg_to_rad(fov_deg);
  frustum_.offset_v = deg_to_rad(ang_deg) - 0.5 * frustum_.fov_v;

  child = pt.get_child("Sensor.Origin");
  if (child.get<std::string>("<xmlattr>.type") != "sensor") {
    throw cloe::ModelError(
        "OSI interface only supports object detection in sensor coordinate reference frame "
        "(Sensor.Origin type=sensor)");
  }

  set_mock_conf(cfg.sensor_mock_conf);
}

}  // namespace vtd


#include <chrono>
#include <cmath>
#include <iostream>
#include <boost/exception/diagnostic_information.hpp>
#include <g3log/logworker.hpp>
#include "bdm/device/demultiplexer.hxx"
#include "bdm/device/mcp3004.hxx"
#include "bdm/device/gpio/gpio_wrapper.hxx"
#include "bdm/device/spi/spidev.hxx"
#include "bdm/task/delay_estimation.hxx"
#include "bdm/task/position_estimation.hxx"
#include "bdm/task/sound_buffer.hxx"
#include "bdmbin/logger.hxx"

namespace {
	constexpr std::uint8_t gpio_button = 14;
	constexpr std::uint8_t gpio_demux_enabler = 23;
	constexpr std::uint8_t gpio_demux_selector[] = {17, 21, 22};

	constexpr bdm::device::spi::config get_spi_conf() {
		bdm::device::spi::config spi_conf{};
		spi_conf.bits_per_word = 8;
		spi_conf.max_speed = 1000'0000;
		spi_conf.spi_mode = bdm::device::spi::mode::DEFAULT;
		return spi_conf;
	}

	class runner {
	public:
		runner();

		void loop();
	private:
		bdm::device::gpio::gpio_wrapper _g;
		bdm::device::demultiplexer _demux;
		bdm::device::spi::spidev _sp;
		bdm::device::mcp3004 _mc;
		bdm::task::sound_buffer _buf;
		bdm::task::delay_estimation _de;
		bdm::task::position_estimation _pe;
	};

	runner::runner()
			: _demux(_g, gpio_demux_enabler, gpio_demux_selector),
			  _sp(bdm::unique_fd::open("/dev/spidev0.0", O_RDWR), get_spi_conf()),
			  _mc(_sp),
			  _buf(_mc) {
		_g.set_pin_mode(gpio_button, bdm::device::gpio::io_mode::input);
	}

	void runner::loop() {
		if (!_g.read(gpio_button)) {
			std::this_thread::sleep_for(std::chrono::milliseconds(250));
			return;
		}
		LOG(INFO) << "loop start";
		auto start = std::chrono::system_clock::now();
		_buf.fill();
		auto end = std::chrono::system_clock::now();

		using sec_type = std::chrono::duration<double, std::chrono::milliseconds::period>;
		auto sample_time = sec_type(end - start).count() / _buf.buf_size;

		LOG(DEBUG) << "sample time: " << sample_time;
		double t1 = _de.get_delay(_buf.buf_size, _buf.get_buf(0), _buf.get_buf(1)) * sample_time;
		double t2 = _de.get_delay(_buf.buf_size, _buf.get_buf(0), _buf.get_buf(2)) * sample_time;
		double t3 = _de.get_delay(_buf.buf_size, _buf.get_buf(0), _buf.get_buf(3)) * sample_time;
		LOG(INFO) << "estimated delay: " << t1 << ' ' << t2 << ' ' << t3;

		double p[3];
		_pe.estimate_position(0, t1, t2, t3, p);
		LOG(INFO) << "estimated position: " << p[0] << ' ' << p[1] << ' ' << p[2];

		_demux.disable();
		std::uint8_t led;
		if (p[0] >= 0) {
			if (p[1] >= 0) {
				led = 1;
			} else {
				led = 4;
			}
		} else {
			if (p[1] >= 0) {
				led = 2;
			} else {
				led = 3;
			}
		}
		LOG(INFO) << "led: " << static_cast<int>(led);

		_demux.select(led);
		_demux.enable();
	}
}

int main(int, char**) {
	bdmbin::logger logger;
	logger.install_logger();
	try {
		runner r;
		while (true) {
			r.loop();
		}
	} catch (boost::exception& ex) {
		LOG(ERROR) << "uncaught exception";
		std::exception* stdex = dynamic_cast<std::exception*>(&ex);
		if (stdex) {
			LOG(ERROR) << "what: " << stdex->what();
		}
		LOG(ERROR) << boost::diagnostic_information_what(ex);
		return 1;
	} catch (std::exception& ex) {
		LOG(ERROR) << "uncaught exception";
		LOG(ERROR) << "what: " << ex.what();
		return 1;
	}
}


/*
 Copyright (C) 2016 Quaternion Risk Management Ltd
 All rights reserved.

 This file is part of ORE, a free-software/open-source library
 for transparent pricing and risk analysis - http://opensourcerisk.org

 ORE is free software: you can redistribute it and/or modify it
 under the terms of the Modified BSD License.  You should have received a
 copy of the license along with this program.
 The license is also available online at <http://opensourcerisk.org>

 This program is distributed on the basis that it will form a useful
 contribution to risk analytics and model standardisation, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
*/

#include <boost/make_shared.hpp>
#include <ored/portfolio/builders/equityoption.hpp>
#include <ored/portfolio/enginefactory.hpp>
#include <ored/portfolio/equityoption.hpp>
#include <ored/portfolio/referencedata.hpp>
#include <ored/utilities/currencycheck.hpp>
#include <ored/utilities/log.hpp>
#include <ql/errors.hpp>
#include <ql/exercise.hpp>
#include <ql/instruments/compositeinstrument.hpp>
#include <ql/instruments/vanillaoption.hpp>

using namespace QuantLib;

namespace ore {
namespace data {

void EquityOption::build(const boost::shared_ptr<EngineFactory>& engineFactory) {

    // Set the assetName_ as it may have changed after lookup
    assetName_ = equityName();

    // Populate the index_ in case the option is automatic exercise.
    const boost::shared_ptr<Market>& market = engineFactory->market();
    index_ = *market->equityCurve(assetName_, engineFactory->configuration(MarketContext::pricing));

    // check the equity currency
    Currency equityCurrency =
        market->equityCurve(assetName_, engineFactory->configuration(MarketContext::pricing))->currency();
    QL_REQUIRE(!equityCurrency.empty(), "No equity currency in equityCurve for equity " << assetName_ << ".");

    // Set the strike currency - if we have a minor currency, convert the strike
    if (!strikeCurrency_.empty()) {
        QL_REQUIRE(parseCurrencyWithMinors(strikeCurrency_) == equityCurrency,
                   "Strike currency " << strikeCurrency_ << " does not match equity currency " << equityCurrency
                                      << " for trade " << id() << ".");
        strike_ = convertMinorToMajorCurrency(strikeCurrency_, localStrike_);
    } else {
        // If payoff currency and underlying currency are equivalent (and payoff currency could be a minor currency)
        if (parseCurrencyWithMinors(localCurrency_) == equityCurrency) {
            strike_ = convertMinorToMajorCurrency(localCurrency_, localStrike_);
            TLOG("Setting strike currency to payoff currency " << localCurrency_ << " for trade " << id() << ".");
            strikeCurrency_ = localCurrency_;
        } else {
            // If quanto payoff, then strike currency must be populated to avoid confusion over what the
            // currency of the strike payoff is: can be either underlying currency or payoff currency
            QL_FAIL("Strike currency must be specified for a quanto payoff for trade " << id() << ".");
        }
    }

    // Quanto payoff condition, i.e. currency_ != underlyingCurrency_, will be checked in VanillaOptionTrade::build()
    currency_ = parseCurrencyWithMinors(localCurrency_).code();
    underlyingCurrency_ = equityCurrency.code();

    // Build the trade using the shared functionality in the base class.
    VanillaOptionTrade::build(engineFactory);

    // LOG the volatility if the trade expiry date is in the future.
    if (expiryDate_ > Settings::instance().evaluationDate()) {
        DLOG("Implied vol for " << tradeType_ << " on " << assetName_ << " with expiry " << expiryDate_
                                << " and strike " << strike_ << " is "
                                << market->equityVol(assetName_)->blackVol(expiryDate_, strike_));
    }

    additionalData_["quantity"] = quantity_;
    additionalData_["strike"] = localStrike_;
    additionalData_["strikeCurrency"] = strikeCurrency_;
}

void EquityOption::fromXML(XMLNode* node) {
    VanillaOptionTrade::fromXML(node);
    XMLNode* eqNode = XMLUtils::getChildNode(node, "EquityOptionData");
    QL_REQUIRE(eqNode, "No EquityOptionData Node");
    option_.fromXML(XMLUtils::getChildNode(eqNode, "OptionData"));
    XMLNode* tmp = XMLUtils::getChildNode(eqNode, "Underlying");
    if (!tmp)
        tmp = XMLUtils::getChildNode(eqNode, "Name");
    equityUnderlying_.fromXML(tmp);
    localCurrency_ = XMLUtils::getChildValue(eqNode, "Currency", true);
    localStrike_ = XMLUtils::getChildValueAsDouble(eqNode, "Strike", true);
    strikeCurrency_ = XMLUtils::getChildValue(eqNode, "StrikeCurrency", false);
    quantity_ = XMLUtils::getChildValueAsDouble(eqNode, "Quantity", true);
}

XMLNode* EquityOption::toXML(XMLDocument& doc) {
    XMLNode* node = VanillaOptionTrade::toXML(doc);
    XMLNode* eqNode = doc.allocNode("EquityOptionData");
    XMLUtils::appendNode(node, eqNode);

    XMLUtils::appendNode(eqNode, option_.toXML(doc));
    XMLUtils::appendNode(eqNode, equityUnderlying_.toXML(doc));
    XMLUtils::addChild(doc, eqNode, "Currency", localCurrency_);
    XMLUtils::addChild(doc, eqNode, "Strike", localStrike_);

    Currency ccy = parseCurrencyWithMinors(localCurrency_);
    Currency strikeCcy = parseCurrencyWithMinors(strikeCurrency_);
    if (!strikeCurrency_.empty() && ccy != strikeCcy)
        XMLUtils::addChild(doc, eqNode, "StrikeCurrency", strikeCurrency_);

    XMLUtils::addChild(doc, eqNode, "Quantity", quantity_);

    return node;
}

std::map<AssetClass, std::set<std::string>> EquityOption::underlyingIndices(const boost::shared_ptr<ReferenceDataManager>& referenceDataManager) const {
    return {{AssetClass::EQ, std::set<std::string>({equityName()})}};
}

} // namespace data
} // namespace ore


#include "dense-reconstruction/stereo-matcher.h"

#include <memory>

#include <Eigen/Dense>
#include <aslam/cameras/camera.h>
#include <aslam/common/pose-types.h>
#include <gflags/gflags.h>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/opencv.hpp>

#include "dense-reconstruction/aslam-cv-interface.h"
#include "dense-reconstruction/disparity-conversion-utils.h"
#include "dense-reconstruction/stereo-camera-utils.h"

DEFINE_double(
    dense_stereo_downscaling_factor, 1.0,
    "Downscaling factor applied to the images prior to stereo matching.");

DEFINE_bool(dense_stereo_use_sgbm, true, "Use SGBM if enabled, BM otherwise.");

DEFINE_int32(dense_stereo_sgbm_min_disparity, 0, "");
DEFINE_int32(dense_stereo_sgbm_num_disparities, 128, "");
DEFINE_int32(dense_stereo_sgbm_sad_window_size, 3, "");
DEFINE_int32(dense_stereo_sgbm_p1, 72, "");
DEFINE_int32(dense_stereo_sgbm_p2, 288, "");
DEFINE_int32(dense_stereo_sgbm_disp12_max_diff, 1, "");
DEFINE_int32(dense_stereo_sgbm_pre_filter_cap, 31, "");
DEFINE_int32(dense_stereo_sgbm_uniqueness_ratio, 10, "");
DEFINE_int32(dense_stereo_sgbm_speckle_window_size, 500, "");
DEFINE_int32(dense_stereo_sgbm_speckle_range, 3, "");
DEFINE_int32(
    dense_stereo_sgbm_mode, cv::StereoSGBM::MODE_SGBM,
    "MODE_SGBM = 0, MODE_HH = 1, MODE_SGBM_3WAY = 2, MODE_HH4 = 3");

DEFINE_int32(dense_stereo_bm_pre_filter_size, 9, "");
DEFINE_int32(dense_stereo_bm_pre_filter_cap, 31, "");
DEFINE_string(dense_stereo_bm_prefilter_type, "xsobel", "");
DEFINE_int32(dense_stereo_bm_texture_threshold, 10, "");
DEFINE_int32(dense_stereo_bm_uniqueness_ratio, 15, "");
DEFINE_int32(dense_stereo_bm_sad_window_size, 9, "");
DEFINE_int32(dense_stereo_bm_min_disparity, 0, "");
DEFINE_int32(dense_stereo_bm_num_disparities, 128, "");
DEFINE_int32(dense_stereo_bm_speckle_window_size, 500, "");
DEFINE_int32(dense_stereo_bm_speckle_range, 3, "");
DEFINE_int32(dense_stereo_bm_disp12_max_diff, 1, "");

namespace dense_reconstruction {
namespace stereo {

void StereoMatcherConfig::adaptParamsBasedOnImageSize(
    const size_t image_width) {
  // Source:
  // https://github.com/opencv/opencv/blob/master/samples/cpp/stereo_match.cpp

  const int sgbm_old_num_disparities = sgbm_num_disparities;
  const int bm_old_num_disparities = bm_num_disparities;
  sgbm_num_disparities = ((image_width / 8) + 15) & -16;
  bm_num_disparities = sgbm_num_disparities;
  LOG(INFO)
      << "Adapted the number of disparities based on the image width from "
      << sgbm_old_num_disparities << "/" << bm_old_num_disparities << " to "
      << sgbm_num_disparities;

  const int old_p1 = sgbm_p1;
  const int old_p2 = sgbm_p2;
  sgbm_p1 = 8 * sgbm_sad_window_size * sgbm_sad_window_size;
  sgbm_p2 = 32 * sgbm_sad_window_size * sgbm_sad_window_size;

  LOG(INFO) << "Adapted the SGBM params p1/p2 based on sad window size from "
            << old_p1 << "/" << old_p2 << " to " << sgbm_p1 << "/" << sgbm_p2;
}

StereoMatcherConfig StereoMatcherConfig::getFromGflags() {
  StereoMatcherConfig config;

  // General config:
  config.downscaling_factor = FLAGS_dense_stereo_downscaling_factor;
  config.use_sgbm = FLAGS_dense_stereo_use_sgbm;

  // SGBM config:
  config.sgbm_min_disparity = FLAGS_dense_stereo_sgbm_min_disparity;
  config.sgbm_num_disparities = FLAGS_dense_stereo_sgbm_num_disparities;
  config.sgbm_sad_window_size = FLAGS_dense_stereo_sgbm_sad_window_size;
  config.sgbm_p1 = FLAGS_dense_stereo_sgbm_p1;
  config.sgbm_p2 = FLAGS_dense_stereo_sgbm_p2;
  config.sgbm_disp12_max_diff = FLAGS_dense_stereo_sgbm_disp12_max_diff;
  config.sgbm_pre_filter_cap = FLAGS_dense_stereo_sgbm_pre_filter_cap;
  config.sgbm_uniqueness_ratio = FLAGS_dense_stereo_sgbm_uniqueness_ratio;
  config.sgbm_speckle_window_size = FLAGS_dense_stereo_sgbm_speckle_window_size;
  config.sgbm_speckle_range = FLAGS_dense_stereo_sgbm_speckle_range;
  config.sgbm_mode = FLAGS_dense_stereo_sgbm_mode;

  // BM config:
  config.bm_pre_filter_size = FLAGS_dense_stereo_bm_pre_filter_size;
  config.bm_pre_filter_cap = FLAGS_dense_stereo_bm_pre_filter_cap;
  config.bm_prefilter_type = FLAGS_dense_stereo_bm_prefilter_type;
  config.bm_texture_threshold = FLAGS_dense_stereo_bm_texture_threshold;
  config.bm_uniqueness_ratio = FLAGS_dense_stereo_bm_uniqueness_ratio;
  config.bm_sad_window_size = FLAGS_dense_stereo_bm_sad_window_size;
  config.bm_min_disparity = FLAGS_dense_stereo_bm_min_disparity;
  config.bm_num_disparities = FLAGS_dense_stereo_bm_num_disparities;
  config.bm_speckle_window_size = FLAGS_dense_stereo_bm_speckle_window_size;
  config.bm_speckle_range = FLAGS_dense_stereo_bm_speckle_range;
  config.bm_disp12_max_diff = FLAGS_dense_stereo_bm_disp12_max_diff;

  return config;
}

StereoMatcher::StereoMatcher(
    const aslam::Camera& first_camera, const aslam::Camera& second_camera,
    const aslam::Transformation& T_C2_C1, const StereoMatcherConfig& config)
    : config_(config),
      first_camera_(first_camera),
      second_camera_(second_camera),
      T_C2_C1_(T_C2_C1),
      stereo_camera_params_(config_.downscaling_factor),
      undistorter_first_(nullptr),
      undistorter_second_(nullptr) {
  getStereoPairFromAslamCvCameras(
      first_camera_, second_camera_, T_C2_C1_, config_.downscaling_factor,
      &stereo_camera_params_);

  undistorter_first_.reset(new Undistorter(stereo_camera_params_.getFirst()));
  undistorter_second_.reset(new Undistorter(stereo_camera_params_.getSecond()));

  if (config_.use_sgbm) {
    VLOG(1) << "Stereo matching algorithm used: SGBM";
    stereo_matcher_ = cv::StereoSGBM::create(
        config_.sgbm_min_disparity, config_.sgbm_num_disparities,
        config_.sgbm_sad_window_size, config_.sgbm_p1, config_.sgbm_p2,
        config_.sgbm_disp12_max_diff, config_.sgbm_pre_filter_cap,
        config_.sgbm_uniqueness_ratio, config_.sgbm_speckle_window_size,
        config_.sgbm_speckle_range, config_.sgbm_mode);

    min_disparity_ = config_.sgbm_min_disparity;
    num_disparities_ = config_.sgbm_num_disparities;
    sad_window_size_ = config_.sgbm_sad_window_size;
  } else {
    VLOG(1) << "Stereo matching algorithm used: BM";
    stereo_matcher_ = cv::StereoBM::create(
        config_.bm_num_disparities, config_.bm_sad_window_size);

    cv::StereoBM* bm_ptr = static_cast<cv::StereoBM*>(stereo_matcher_.get());
    bm_ptr->setPreFilterCap(config_.bm_pre_filter_cap);
    bm_ptr->setPreFilterSize(config_.bm_pre_filter_size);
    bm_ptr->setMinDisparity(config_.bm_min_disparity);
    bm_ptr->setTextureThreshold(config_.bm_texture_threshold);
    bm_ptr->setUniquenessRatio(config_.bm_uniqueness_ratio);
    bm_ptr->setSpeckleRange(config_.bm_speckle_range);
    bm_ptr->setSpeckleWindowSize(config_.bm_speckle_window_size);
    bm_ptr->setDisp12MaxDiff(config_.bm_disp12_max_diff);

    min_disparity_ = config_.bm_min_disparity;
    num_disparities_ = config_.bm_num_disparities;
    sad_window_size_ = config_.bm_sad_window_size;
  }

  // Cache some intrinsics values:
  const std::shared_ptr<OutputCameraParameters> left_params =
      undistorter_first_->getCameraParametersPair().getOutputPtr();
  const std::shared_ptr<OutputCameraParameters> right_params =
      undistorter_second_->getCameraParametersPair().getOutputPtr();

  focal_length_ = left_params->P()(0, 0);
  baseline_ = (right_params->P()(0, 3) - left_params->P()(0, 3)) /
              left_params->P()(0, 0);

  cx_ = left_params->P()(0, 2);
  const double cx_right = right_params->P()(0, 2);
  CHECK_EQ(cx_, cx_right)
      << "The undistortion should have made cx_left and cx_right identical!";

  cy_ = left_params->P()(1, 2);

  CHECK_GT(std::abs(baseline_), 1e-6);
}

void StereoMatcher::computeDisparityMap(
    const cv::Mat& first_image, const cv::Mat& second_image,
    cv::Mat* disparity_map, cv::Mat* first_image_undistorted,
    cv::Mat* second_image_undistorted) const {
  CHECK_NOTNULL(disparity_map);
  CHECK_NOTNULL(first_image_undistorted);
  CHECK_NOTNULL(second_image_undistorted);
  CHECK(undistorter_first_);
  CHECK(undistorter_second_);
  CHECK(stereo_matcher_);

  VLOG(5) << "Undistorting and rectifying images...";
  undistorter_first_->undistortImage(first_image, first_image_undistorted);
  undistorter_second_->undistortImage(second_image, second_image_undistorted);

  VLOG(5) << "Computing disparity map...";
  stereo_matcher_->compute(
      *first_image_undistorted, *second_image_undistorted, *disparity_map);
  VLOG(5) << "Done.";
}

void StereoMatcher::computePointCloud(
    const cv::Mat& first_image, const cv::Mat& second_image,
    resources::PointCloud* point_cloud) const {
  CHECK_NOTNULL(point_cloud);

  cv::Mat disparity_map, first_image_undistorted, second_image_undistorted;
  computeDisparityMap(
      first_image, second_image, &disparity_map, &first_image_undistorted,
      &second_image_undistorted);

  convertDisparityMapToPointCloud(
      disparity_map, first_image_undistorted, baseline_, focal_length_, cx_,
      cy_, sad_window_size_, min_disparity_, num_disparities_, point_cloud);
}

void StereoMatcher::computeDepthMapForOriginalCamera(
    const cv::Mat& first_image, const cv::Mat& second_image,
    cv::Mat* depth_map) const {
  CHECK_NOTNULL(depth_map);

  cv::Mat disparity_map, first_image_undistorted, second_image_undistorted;
  computeDisparityMap(
      first_image, second_image, &disparity_map, &first_image_undistorted,
      &second_image_undistorted);

  convertDisparityMapToDepthMap(
      disparity_map, first_image_undistorted, baseline_, focal_length_, cx_,
      cy_, sad_window_size_, min_disparity_, num_disparities_, first_camera_,
      depth_map);
}

}  // namespace stereo
}  // namespace dense_reconstruction


// Boost.Geometry (aka GGL, Generic Geometry Library)

// Copyright (c) 2014-2021, Oracle and/or its affiliates.

// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle

// Licensed under the Boost Software License version 1.0.
// http://www.boost.org/users/license.html

#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISTANCE_LINEAR_TO_LINEAR_HPP
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISTANCE_LINEAR_TO_LINEAR_HPP

#include <boost/geometry/algorithms/detail/distance/range_to_geometry_rtree.hpp>
#include <boost/geometry/algorithms/detail/distance/strategy_utils.hpp>
#include <boost/geometry/algorithms/dispatch/distance.hpp>
#include <boost/geometry/algorithms/num_points.hpp>
#include <boost/geometry/algorithms/num_segments.hpp>

#include <boost/geometry/core/point_type.hpp>

#include <boost/geometry/iterators/point_iterator.hpp>
#include <boost/geometry/iterators/segment_iterator.hpp>

#include <boost/geometry/strategies/distance.hpp>

namespace boost { namespace geometry
{

#ifndef DOXYGEN_NO_DETAIL
namespace detail { namespace distance
{

template <typename Linear1, typename Linear2, typename Strategies>
struct linear_to_linear
{
    typedef distance::return_t<Linear1, Linear2, Strategies> return_type;

    static inline return_type apply(Linear1 const& linear1,
                                    Linear2 const& linear2,
                                    Strategies const& strategies,
                                    bool = false)
    {
        if (geometry::num_points(linear1) == 1)
        {
            return dispatch::distance
                <
                    typename point_type<Linear1>::type,
                    Linear2,
                    Strategies
                >::apply(*points_begin(linear1), linear2, strategies);
        }

        if (geometry::num_points(linear2) == 1)
        {
            return dispatch::distance
                <
                    typename point_type<Linear2>::type,
                    Linear1,
                    Strategies
                >::apply(*points_begin(linear2), linear1, strategies);
        }

        if (geometry::num_segments(linear2) < geometry::num_segments(linear1))
        {
            return point_or_segment_range_to_geometry_rtree
                <
                    geometry::segment_iterator<Linear2 const>,
                    Linear1,
                    Strategies
                >::apply(geometry::segments_begin(linear2),
                         geometry::segments_end(linear2),
                         linear1,
                         strategies);

        }

        return point_or_segment_range_to_geometry_rtree
            <
                geometry::segment_iterator<Linear1 const>,
                Linear2,
                Strategies
            >::apply(geometry::segments_begin(linear1),
                     geometry::segments_end(linear1),
                     linear2,
                     strategies);
    }
};

}} // namespace detail::distance
#endif // DOXYGEN_NO_DETAIL

#ifndef DOXYGEN_NO_DISPATCH
namespace dispatch
{

template <typename Linear1, typename Linear2, typename Strategy, typename StrategyTag>
struct distance
    <
        Linear1, Linear2, Strategy,
        linear_tag, linear_tag,
        StrategyTag, false
    > : detail::distance::linear_to_linear
        <
            Linear1, Linear2, Strategy
        >
{};

} // namespace dispatch
#endif // DOXYGEN_NO_DISPATCH

}} // namespace boost::geometry

#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISTANCE_LINEAR_TO_LINEAR_HPP


// Copyright 2021-2022 Lawrence Livermore National Security, LLC and other
// krowkee Project Developers. See the top-level COPYRIGHT file for details.
//
// SPDX-License-Identifier: MIT

#ifndef _KROWKEE_SKETCH_INTERFACE_HPP
#define _KROWKEE_SKETCH_INTERFACE_HPP

#include <krowkee/hash/hash.hpp>

#include <krowkee/transform/CountSketch.hpp>
#include <krowkee/transform/FWHT.hpp>

#include <krowkee/sketch/Dense.hpp>
#include <krowkee/sketch/Promotable.hpp>
#include <krowkee/sketch/Sparse.hpp>

#include <krowkee/sketch/Sketch.hpp>

#if __has_include(<ygm/comm.hpp>)
#include <ygm/detail/ygm_ptr.hpp>
#endif

#if __has_include(<boost/container/flat_map.hpp>)
#include <boost/container/flat_map.hpp>
#endif

#include <map>
#include <memory>

////////////////////////////////////////////////////////////////////////////////
// Sketch Type Presets
////////////////////////////////////////////////////////////////////////////////

namespace krowkee {
namespace sketch {

template <template <typename, typename...> class SketchFunc,
          template <typename, typename> class ContainerType,
          template <typename> class MergeOp, typename RegType, typename... Args>
using LocalSketch = Sketch<SketchFunc, ContainerType, MergeOp, RegType,
                           std::shared_ptr, Args...>;

template <template <typename, typename> class ContainerType, typename RegType>
using LocalCountSketch =
    LocalSketch<krowkee::transform::CountSketchFunctor, ContainerType,
                std::plus, RegType, krowkee::hash::MulAddShift>;

template <typename RegType>
using LocalFWHT = LocalSketch<krowkee::transform::FWHTFunctor,
                              krowkee::sketch::Dense, std::plus, RegType>;

}  // namespace sketch
}  // namespace krowkee

#if __has_include(<ygm/comm.hpp>)
namespace krowkee {
namespace sketch {

template <template <typename, typename...> class SketchFunc,
          template <typename, typename> class ContainerType,
          template <typename> class MergeOp, typename RegType, typename... Args>
using CommunicableSketch =
    Sketch<SketchFunc, ContainerType, MergeOp, RegType, ygm::ygm_ptr, Args...>;

template <template <typename, typename> class ContainerType, typename RegType>
using CommunicableCountSketch =
    CommunicableSketch<krowkee::transform::CountSketchFunctor, ContainerType,
                       std::plus, RegType, krowkee::hash::MulAddShift>;

template <typename RegType>
using CommunicableFWHT =
    CommunicableSketch<krowkee::transform::FWHTFunctor, krowkee::sketch::Dense,
                       std::plus, RegType>;

}  // namespace sketch
}  // namespace krowkee
#endif

////////////////////////////////////////////////////////////////////////////////
// Sparse Container Presets
////////////////////////////////////////////////////////////////////////////////

namespace krowkee {
namespace sketch {

template <typename RegType, typename MergeOp, typename KeyType>
using MapSparse = Sparse<RegType, MergeOp, std::map, KeyType>;

template <typename RegType, typename MergeOp>
using MapSparse32 = MapSparse<RegType, MergeOp, std::uint32_t>;

#if __has_include(<boost/container/flat_map.hpp>)
template <typename RegType, typename MergeOp, typename KeyType>
using FlatMapSparse =
    Sparse<RegType, MergeOp, boost::container::flat_map, KeyType>;

template <typename RegType, typename MergeOp>
using FlatMapSparse32 = FlatMapSparse<RegType, MergeOp, std::uint32_t>;
#endif

}  // namespace sketch
}  // namespace krowkee

////////////////////////////////////////////////////////////////////////////////
// Promotable Container Presets
////////////////////////////////////////////////////////////////////////////////

namespace krowkee {
namespace sketch {

template <typename RegType, typename MergeOp, typename KeyType>
using MapPromotable = Promotable<RegType, MergeOp, std::map, KeyType>;

template <typename RegType, typename MergeOp>
using MapPromotable32 = MapPromotable<RegType, MergeOp, std::uint32_t>;

#if __has_include(<boost/container/flat_map.hpp>)
template <typename RegType, typename MergeOp, typename KeyType>
using FlatMapPromotable =
    Promotable<RegType, MergeOp, boost::container::flat_map, KeyType>;

template <typename RegType, typename MergeOp>
using FlatMapPromotable32 = FlatMapPromotable<RegType, MergeOp, std::uint32_t>;
#endif

}  // namespace sketch
}  // namespace krowkee

#endif


/*
 * Copyright (c) 2011-2022, The DART development contributors
 * All rights reserved.
 *
 * The list of contributors can be found at:
 *   https://github.com/dartsim/dart/blob/master/LICENSE
 *
 * This file is provided under the following "BSD-style" License:
 *   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 HOLDERS AND
 *   CONTRIBUTORS "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 OR
 *   CONTRIBUTORS 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.
 */

#ifndef DART_OPTIMIZER_POPULATION_HPP_
#define DART_OPTIMIZER_POPULATION_HPP_

#include <iostream>
#include <memory>
#include <random>

#include <Eigen/Dense>

#include "dart/optimizer/MultiObjectiveProblem.hpp"

namespace dart {
namespace optimizer {

class MultiObjectiveProblem;

class Population
{
public:
  /// Constructor
  ///
  /// Population stores a clone of a \p problem.
  Population(
      std::shared_ptr<MultiObjectiveProblem> problem,
      std::size_t populationSize = 0u);

  /// Returns the associated problem, which is always not null.
  MultiObjectiveProblem* getProblem();

  /// Returns the associated problem, which is always not null.
  const MultiObjectiveProblem* getProblem() const;

  /// Adds the decision vector at the back of the population. The corresponding
  /// fitness vector will be evaulated and set.
  void pushBack(const Eigen::VectorXd& x);

  /// Adds the decision vector and the fitness vector at the back of the
  /// population.
  void pushBack(const Eigen::VectorXd& x, const Eigen::VectorXd& f);

  /// Sets the decision vector. The corresponding fitness vector will be
  /// evaulated and set.
  void set(std::size_t index, const Eigen::VectorXd& x);

  /// Sets the decision vector and fitness vector at \c index in the population
  void set(
      std::size_t index, const Eigen::VectorXd& x, const Eigen::VectorXd& f);

  /// Returns the size of this population
  std::size_t getSize() const;

  /// Returns a decision vectors at \c index.
  Eigen::VectorXd getDecisionVector(std::size_t index) const;

  /// Returns a fitness vectors at \c index.
  Eigen::VectorXd getFitnessVector(std::size_t index) const;

  /// Prints information of this class to a stream.
  std::ostream& print(std::ostream& os) const;

protected:
  /// Multi-objective problem that this population is accosiated with. This is
  /// always not null.
  std::shared_ptr<MultiObjectiveProblem> mProblem;

  /// Decision vectors. A column represents a decision vector.
  Eigen::MatrixXd mPopulation;

  /// Fitness vectors. A column represents a fitness vector.
  Eigen::MatrixXd mFitness;
};

} // namespace optimizer
} // namespace dart

#endif // DART_OPTIMIZER_POPULATION_HPP_


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

#include <boost/simd/sdk/meta/real_of.hpp>
#include <boost/simd/sdk/meta/scalar_of.hpp>
#include <boost/dispatch/meta/factory_of.hpp>
#include <boost/mpl/apply.hpp>

namespace boost { namespace simd { namespace meta
{
  template<class T>
  struct as_real
   : boost::mpl::apply1< typename boost::dispatch::meta::factory_of<T, typename scalar_of<T>::type>::type
                       , typename real_of<T>::type
                       >
  {};
} } }

#endif


// This file is part of LatNet Builder.
//
// Copyright (C) 2012-2021  The LatNet Builder author's, supervised by Pierre L'Ecuyer, Universite de Montreal.
//
// 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.

#include "latbuilder/Parser/MeritFilter.h"
#include "latbuilder/Parser/LevelWeights.h"
#include "latbuilder/MeritFilter.h"
#include "latbuilder/Norm/Normalizer.h"
#include "latbuilder/Norm/PAlphaSL10.h"
#include "latbuilder/Norm/PAlphaDPW08.h"
#include "latbuilder/Norm/PAlphaTilde.h"
#include "latbuilder/Norm/IAAlpha.h"
#include "latbuilder/Norm/IB.h"
#include "latbuilder/Functor/LowPass.h"

#include <boost/lexical_cast.hpp>

namespace LatBuilder { namespace Parser {

namespace {

   template <LatticeType LR, class NORM>
   void setLevelWeights(
         Norm::Normalizer<LR, EmbeddingType::UNILEVEL, NORM>&,
         const std::string&,
         const LatBuilder::SizeParam<LR, EmbeddingType::UNILEVEL>&
         )
   {}

   template <LatticeType LR, class NORM>
   void setLevelWeights(
         Norm::Normalizer<LR, EmbeddingType::MULTILEVEL, NORM>& normalizer,
         const std::string& levelWeights,
         const LatBuilder::SizeParam<LR, EmbeddingType::MULTILEVEL>& sizeParam
         )
   { normalizer.setWeights(LevelWeights<LR>::parse(levelWeights, sizeParam)); }

   template <LatticeType LR, class NORM, EmbeddingType ET>
   std::unique_ptr<BasicMeritFilter<LR, ET>> createNormalizer(
         unsigned int alpha,
         const LatBuilder::SizeParam<LR, ET>& sizeParam,
         const LatticeTester::Weights& weights,
         Real normType,
         const std::string& levelWeights
         )
   {
      auto normalizer = new LatBuilder::Norm::Normalizer<LR, ET, NORM>(NORM(alpha, weights, normType));
      setLevelWeights(*normalizer, levelWeights, sizeParam);
      return std::unique_ptr<BasicMeritFilter<LR, ET>>(normalizer);
   }

   template <LatticeType LR, EmbeddingType ET> void correctLevelWeights(
         const LatBuilder::SizeParam<LR, ET>& sizeParam,
         const std::string& combiner,
         std::string& levelWeights);

   template <LatticeType LR>
   void correctLevelWeights(
         const LatBuilder::SizeParam<LR, EmbeddingType::UNILEVEL>& sizeParam,
         const std::string& combiner,
         std::string& levelWeights)
      {}

   template <LatticeType LR>
   void correctLevelWeights(
         const LatBuilder::SizeParam<LR, EmbeddingType::MULTILEVEL>& sizeParam,
         const std::string& combiner,
         std::string& levelWeights)
      {
            const auto strCombinerSplit = splitPair<>(combiner, ':');
            if (strCombinerSplit.first == "level") {
                  if (strCombinerSplit.second == "max"){
                        levelWeights = "select:" + std::to_string(sizeParam.maxLevel());
                  }
                  else {
                        try {
                              levelWeights = "select:" + std::to_string(boost::lexical_cast<Level>(strCombinerSplit.second)) + "," + std::to_string(boost::lexical_cast<Level>(strCombinerSplit.second));
                        }
                        catch (boost::bad_lexical_cast&) {}
                  }
            }
      }

   /**
    * Parses the bound PAlphaSL10.
    *
    * Example strings: \c P2-SL10, \c P4-SL10, \c P2-DPW08, \c P4-DPW08
    */
   template <LatticeType LR, EmbeddingType ET>
   std::unique_ptr<BasicMeritFilter<LR, ET>> parseNormalizer(
         const std::string& str,
         const std::string& figure,
         const LatBuilder::SizeParam<LR, ET>& sizeParam,
         const LatticeTester::Weights& weights,
         Real normType,
         std::string combiner
         )
   {
      auto figureSplit = splitPair(figure, ':');
      std::string figureString = (figureSplit.second == "") ? figure : figureSplit.second;
      std::set<std::string> admissibleNormalizations;
      if (figureString[0] == 'P') {
            admissibleNormalizations.insert("P" + figureString.substr(1));
            if (LR == LatticeType::ORDINARY)
            {
                  admissibleNormalizations.insert("P" + figureString.substr(1) + "-SL10");
                  admissibleNormalizations.insert("P" + figureString.substr(1) + "-DPW08");
            }
      }
      else if (figureString[0] == 'I')
      {
            if (figureString[1] == 'A')
            {
                  admissibleNormalizations.insert("IA" + figureString.substr(2));
            }
            else if (figureString[1] == 'B') {
                  admissibleNormalizations.insert("IB");
            }
      }

      std::string newNormString = str;
      if (figure != "" && admissibleNormalizations.size() == 0)
            throw BadFilter("No normalizations are available with figure " + figure + ".");

      if ( (str.size() >= 6 && str.substr(0,6)=="select") || str.size() == 0)
      {
            newNormString =  *admissibleNormalizations.begin() + ":" + newNormString;
      }

      auto args = splitPair(newNormString, ':');
      if (figure != "" && admissibleNormalizations.find(args.first) == admissibleNormalizations.end())
      {
            throw BadFilter("Normalization " + str + " is incompatible with figure" + figure + ( (LR == LatticeType::POLYNOMIAL) ? " with lattice type polynomial." : "."));
      }
      const auto strSplit = splitPair<>(args.first, '-');

      correctLevelWeights(sizeParam, combiner, args.second);

      try {
      if (strSplit.first[0] == 'P') {
            const auto alpha = boost::lexical_cast<unsigned int>(strSplit.first.substr(1));
            if (strSplit.second == "")
            {
                  if (LR == LatticeType::ORDINARY)
                  {
                        return createNormalizer<LR, LatBuilder::Norm::PAlphaSL10, ET>(alpha, sizeParam, weights, normType, args.second);
                  }
                  else if (LR == LatticeType::POLYNOMIAL)
                  {
                        return createNormalizer<LR, LatBuilder::Norm::PAlphaTilde, ET>(alpha, sizeParam, weights, normType, args.second);
                  }
            }
            if (strSplit.second == "SL10")
            return createNormalizer<LR, LatBuilder::Norm::PAlphaSL10, ET>(alpha, sizeParam, weights, normType, args.second);
            else if (strSplit.second == "DPW08")
            return createNormalizer<LR, LatBuilder::Norm::PAlphaDPW08, ET>(alpha, sizeParam, weights, normType, args.second);
      }
      if (strSplit.first[0] == 'I')
      {
            if (strSplit.first[1] == 'A')
            {
                  const auto alpha = boost::lexical_cast<unsigned int>(strSplit.first.substr(2));
                  return createNormalizer<LR, LatBuilder::Norm::IAAlpha, ET>(alpha, sizeParam, weights, normType, args.second);
            }
            if (strSplit.first[1] == 'B')
            {
                  return createNormalizer<LR, LatBuilder::Norm::IB, ET>(weights.interlacingFactor(), sizeParam, weights, normType, args.second);
            }
      }
      }
      catch (boost::bad_lexical_cast&) {}
      throw BadFilter("cannot parse norm: " + str);

   }
}

template <LatticeType LR, EmbeddingType ET>
std::unique_ptr<BasicMeritFilter<LR, ET>>
MeritFilter<LR,ET>::parse(
      const std::string& str,
      const std::string& figure,
      const LatBuilder::SizeParam<LR, ET>& sizeParam,
      const LatticeTester::Weights& weights,
      Real normType,
      std::string combiner
      )
{
   const auto x = splitPair(str, ':');
   if (x.first == "norm")
      return parseNormalizer(x.second, figure, sizeParam, weights, normType, combiner);
   else if (x.first == "low-pass") {
      auto threshold = boost::lexical_cast<Real>(x.second);
      return std::unique_ptr<BasicMeritFilter<LR, ET>>(new LatBuilder::MeritFilter<LR, ET>(Functor::LowPass<Real>(threshold), str));
   }
   throw BadFilter(x.first);
}

template struct LatBuilder::Parser::MeritFilter <LatticeType::ORDINARY, EmbeddingType::UNILEVEL> ;
template struct LatBuilder::Parser::MeritFilter <LatticeType::ORDINARY, EmbeddingType::MULTILEVEL> ;
template struct LatBuilder::Parser::MeritFilter <LatticeType::POLYNOMIAL, EmbeddingType::UNILEVEL> ;
template struct LatBuilder::Parser::MeritFilter <LatticeType::POLYNOMIAL, EmbeddingType::MULTILEVEL> ;

}}


// Copyright (c) 2014-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "chainparams.h"
#include "validation.h"
#include "net.h"

#include "test/test_bitcoin.h"

#include <boost/signals2/signal.hpp>
#include <boost/test/unit_test.hpp>

BOOST_FIXTURE_TEST_SUITE(main_tests, TestingSetup)

BOOST_AUTO_TEST_CASE(subsidy_limit_test)
{
    const Consensus::Params& consensusParams = Params(CBaseChainParams::MAIN).GetConsensus();
    CAmount nSum = 0;
    nSum += 5000000000;
    for (int nHeight = 1; nHeight < 1440; nHeight += 1439) {
        CAmount nSubsidy = GetBlockSubsidy(nHeight, consensusParams);
        BOOST_CHECK(nSubsidy <= 500 * COIN);
        nSum += nSubsidy * 1439;
        BOOST_CHECK(MoneyRange(nSum));
    }
    for (int nHeight = 1440; nHeight < 2880; nHeight += 1440) {
        CAmount nSubsidy = GetBlockSubsidy(nHeight, consensusParams);
        BOOST_CHECK(nSubsidy <= 100 * COIN);
        nSum += nSubsidy * 1440;
        BOOST_CHECK(MoneyRange(nSum));
    }
    for (int nHeight = 2880; nHeight < 4320; nHeight += 1440) {
        CAmount nSubsidy = GetBlockSubsidy(nHeight, consensusParams);
        BOOST_CHECK(nSubsidy <= 50 * COIN);
        nSum += nSubsidy * 1440;
        BOOST_CHECK(MoneyRange(nSum));
    }
    for (int nHeight = 4320; nHeight < 4536545; nHeight += 4532225) {
        CAmount nSubsidy = GetBlockSubsidy(nHeight, consensusParams);
        BOOST_CHECK(nSubsidy <= 2 * COIN);
        nSum += nSubsidy * 4532225;
        BOOST_CHECK(MoneyRange(nSum));
    }
    BOOST_CHECK_EQUAL(nSum, 1000000000000000ULL);
}

bool ReturnFalse() { return false; }
bool ReturnTrue() { return true; }

BOOST_AUTO_TEST_CASE(test_combiner_all)
{
    boost::signals2::signal<bool (), CombinerAll> Test;
    BOOST_CHECK(Test());
    Test.connect(&ReturnFalse);
    BOOST_CHECK(!Test());
    Test.connect(&ReturnTrue);
    BOOST_CHECK(!Test());
    Test.disconnect(&ReturnFalse);
    BOOST_CHECK(Test());
    Test.disconnect(&ReturnTrue);
    BOOST_CHECK(Test());
}
BOOST_AUTO_TEST_SUITE_END()