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

#ifndef BOOST_MATH_MP_TOMMATH_BACKEND_HPP
#define BOOST_MATH_MP_TOMMATH_BACKEND_HPP

#include <boost/cstdint.hpp>
#include <boost/functional/hash_fwd.hpp>
#include <boost/math/special_functions/fpclassify.hpp>
#include <boost/multiprecision/detail/integer_ops.hpp>
#include <boost/multiprecision/number.hpp>
#include <boost/multiprecision/rational_adaptor.hpp>
#include <boost/scoped_array.hpp>
#include <cctype>
#include <climits>
#include <cmath>
#include <limits>
#include <tommath.h>

namespace boost {
namespace multiprecision {
namespace backends {

namespace detail {

inline void check_tommath_result(unsigned v) {
  if (v != MP_OKAY) {
    BOOST_THROW_EXCEPTION(std::runtime_error(mp_error_to_string(v)));
  }
}

} // namespace detail

struct tommath_int;

void eval_multiply(tommath_int &t, const tommath_int &o);
void eval_add(tommath_int &t, const tommath_int &o);

struct tommath_int {
  typedef mpl::list<boost::int32_t, boost::long_long_type> signed_types;
  typedef mpl::list<boost::uint32_t, boost::ulong_long_type> unsigned_types;
  typedef mpl::list<long double> float_types;

  tommath_int() { detail::check_tommath_result(mp_init(&m_data)); }
  tommath_int(const tommath_int &o) {
    detail::check_tommath_result(
        mp_init_copy(&m_data, const_cast<::mp_int *>(&o.m_data)));
  }
#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
  tommath_int(tommath_int &&o) BOOST_NOEXCEPT {
    m_data = o.m_data;
    o.m_data.dp = 0;
  }
  tommath_int &operator=(tommath_int &&o) {
    mp_exch(&m_data, &o.m_data);
    return *this;
  }
#endif
  tommath_int &operator=(const tommath_int &o) {
    if (m_data.dp == 0)
      detail::check_tommath_result(mp_init(&m_data));
    if (o.m_data.dp)
      detail::check_tommath_result(
          mp_copy(const_cast<::mp_int *>(&o.m_data), &m_data));
    return *this;
  }
  tommath_int &operator=(boost::ulong_long_type i) {
    if (m_data.dp == 0)
      detail::check_tommath_result(mp_init(&m_data));
    boost::ulong_long_type mask =
        ((1uLL << std::numeric_limits<unsigned>::digits) - 1);
    unsigned shift = 0;
    ::mp_int t;
    detail::check_tommath_result(mp_init(&t));
    mp_zero(&m_data);
    while (i) {
      detail::check_tommath_result(
          mp_set_int(&t, static_cast<unsigned>(i & mask)));
      if (shift)
        detail::check_tommath_result(mp_mul_2d(&t, shift, &t));
      detail::check_tommath_result((mp_add(&m_data, &t, &m_data)));
      shift += std::numeric_limits<unsigned>::digits;
      i >>= std::numeric_limits<unsigned>::digits;
    }
    mp_clear(&t);
    return *this;
  }
  tommath_int &operator=(boost::long_long_type i) {
    if (m_data.dp == 0)
      detail::check_tommath_result(mp_init(&m_data));
    bool neg = i < 0;
    *this = boost::multiprecision::detail::unsigned_abs(i);
    if (neg)
      detail::check_tommath_result(mp_neg(&m_data, &m_data));
    return *this;
  }
  //
  // Note that although mp_set_int takes an unsigned long as an argument
  // it only sets the first 32-bits to the result, and ignores the rest.
  // So use uint32_t as the largest type to pass to this function.
  //
  tommath_int &operator=(boost::uint32_t i) {
    if (m_data.dp == 0)
      detail::check_tommath_result(mp_init(&m_data));
    detail::check_tommath_result((mp_set_int(&m_data, i)));
    return *this;
  }
  tommath_int &operator=(boost::int32_t i) {
    if (m_data.dp == 0)
      detail::check_tommath_result(mp_init(&m_data));
    bool neg = i < 0;
    *this = boost::multiprecision::detail::unsigned_abs(i);
    if (neg)
      detail::check_tommath_result(mp_neg(&m_data, &m_data));
    return *this;
  }
  tommath_int &operator=(long double a) {
    using std::floor;
    using std::frexp;
    using std::ldexp;

    if (m_data.dp == 0)
      detail::check_tommath_result(mp_init(&m_data));

    if (a == 0) {
      detail::check_tommath_result(mp_set_int(&m_data, 0));
      return *this;
    }

    if (a == 1) {
      detail::check_tommath_result(mp_set_int(&m_data, 1));
      return *this;
    }

    BOOST_ASSERT(!(boost::math::isinf)(a));
    BOOST_ASSERT(!(boost::math::isnan)(a));

    int e;
    long double f, term;
    detail::check_tommath_result(mp_set_int(&m_data, 0u));
    ::mp_int t;
    detail::check_tommath_result(mp_init(&t));

    f = frexp(a, &e);

    static const int shift = std::numeric_limits<int>::digits - 1;

    while (f) {
      // extract int sized bits from f:
      f = ldexp(f, shift);
      term = floor(f);
      e -= shift;
      detail::check_tommath_result(mp_mul_2d(&m_data, shift, &m_data));
      if (term > 0) {
        detail::check_tommath_result(mp_set_int(&t, static_cast<int>(term)));
        detail::check_tommath_result(mp_add(&m_data, &t, &m_data));
      } else {
        detail::check_tommath_result(mp_set_int(&t, static_cast<int>(-term)));
        detail::check_tommath_result(mp_sub(&m_data, &t, &m_data));
      }
      f -= term;
    }
    if (e > 0)
      detail::check_tommath_result(mp_mul_2d(&m_data, e, &m_data));
    else if (e < 0) {
      tommath_int t2;
      detail::check_tommath_result(mp_div_2d(&m_data, -e, &m_data, &t2.data()));
    }
    mp_clear(&t);
    return *this;
  }
  tommath_int &operator=(const char *s) {
    //
    // We don't use libtommath's own routine because it doesn't error check the
    // input :-(
    //
    if (m_data.dp == 0)
      detail::check_tommath_result(mp_init(&m_data));
    std::size_t n = s ? std::strlen(s) : 0;
    *this = static_cast<boost::uint32_t>(0u);
    unsigned radix = 10;
    bool isneg = false;
    if (n && (*s == '-')) {
      --n;
      ++s;
      isneg = true;
    }
    if (n && (*s == '0')) {
      if ((n > 1) && ((s[1] == 'x') || (s[1] == 'X'))) {
        radix = 16;
        s += 2;
        n -= 2;
      } else {
        radix = 8;
        n -= 1;
      }
    }
    if (n) {
      if (radix == 8 || radix == 16) {
        unsigned shift = radix == 8 ? 3 : 4;
        unsigned block_count = DIGIT_BIT / shift;
        unsigned block_shift = shift * block_count;
        boost::ulong_long_type val, block;
        while (*s) {
          block = 0;
          for (unsigned i = 0; (i < block_count); ++i) {
            if (*s >= '0' && *s <= '9')
              val = *s - '0';
            else if (*s >= 'a' && *s <= 'f')
              val = 10 + *s - 'a';
            else if (*s >= 'A' && *s <= 'F')
              val = 10 + *s - 'A';
            else
              val = 400;
            if (val > radix) {
              BOOST_THROW_EXCEPTION(std::runtime_error(
                  "Unexpected content found while parsing character string."));
            }
            block <<= shift;
            block |= val;
            if (!*++s) {
              // final shift is different:
              block_shift = (i + 1) * shift;
              break;
            }
          }
          detail::check_tommath_result(
              mp_mul_2d(&data(), block_shift, &data()));
          if (data().used)
            data().dp[0] |= block;
          else
            *this = block;
        }
      } else {
        // Base 10, we extract blocks of size 10^9 at a time, that way
        // the number of multiplications is kept to a minimum:
        boost::uint32_t block_mult = 1000000000;
        while (*s) {
          boost::uint32_t block = 0;
          for (unsigned i = 0; i < 9; ++i) {
            boost::uint32_t val;
            if (*s >= '0' && *s <= '9')
              val = *s - '0';
            else
              BOOST_THROW_EXCEPTION(std::runtime_error(
                  "Unexpected character encountered in input."));
            block *= 10;
            block += val;
            if (!*++s) {
              static const boost::uint32_t block_multiplier[9] = {
                  10,      100,      1000,      10000,     100000,
                  1000000, 10000000, 100000000, 1000000000};
              block_mult = block_multiplier[i];
              break;
            }
          }
          tommath_int t;
          t = block_mult;
          eval_multiply(*this, t);
          t = block;
          eval_add(*this, t);
        }
      }
    }
    if (isneg)
      this->negate();
    return *this;
  }
  std::string str(std::streamsize /*digits*/, std::ios_base::fmtflags f) const {
    BOOST_ASSERT(m_data.dp);
    int base = 10;
    if ((f & std::ios_base::oct) == std::ios_base::oct)
      base = 8;
    else if ((f & std::ios_base::hex) == std::ios_base::hex)
      base = 16;
    //
    // sanity check, bases 8 and 16 are only available for positive numbers:
    //
    if ((base != 10) && m_data.sign)
      BOOST_THROW_EXCEPTION(
          std::runtime_error("Formatted output in bases 8 or 16 is only "
                             "available for positive numbers"));
    int s;
    detail::check_tommath_result(
        mp_radix_size(const_cast<::mp_int *>(&m_data), base, &s));
    boost::scoped_array<char> a(new char[s + 1]);
    detail::check_tommath_result(
        mp_toradix_n(const_cast<::mp_int *>(&m_data), a.get(), base, s + 1));
    std::string result = a.get();
    if (f & std::ios_base::uppercase)
      for (size_t i = 0; i < result.length(); ++i)
        result[i] = std::toupper(result[i]);
    if ((base != 10) && (f & std::ios_base::showbase)) {
      int pos = result[0] == '-' ? 1 : 0;
      const char *pp =
          base == 8 ? "0" : (f & std::ios_base::uppercase) ? "0X" : "0x";
      result.insert(static_cast<std::string::size_type>(pos), pp);
    }
    if ((f & std::ios_base::showpos) && (result[0] != '-'))
      result.insert(static_cast<std::string::size_type>(0), 1, '+');
    return result;
  }
  ~tommath_int() {
    if (m_data.dp)
      mp_clear(&m_data);
  }
  void negate() {
    BOOST_ASSERT(m_data.dp);
    mp_neg(&m_data, &m_data);
  }
  int compare(const tommath_int &o) const {
    BOOST_ASSERT(m_data.dp && o.m_data.dp);
    return mp_cmp(const_cast<::mp_int *>(&m_data),
                  const_cast<::mp_int *>(&o.m_data));
  }
  template <class V> int compare(V v) const {
    tommath_int d;
    tommath_int t(*this);
    detail::check_tommath_result(mp_shrink(&t.data()));
    d = v;
    return t.compare(d);
  }
  ::mp_int &data() {
    BOOST_ASSERT(m_data.dp);
    return m_data;
  }
  const ::mp_int &data() const {
    BOOST_ASSERT(m_data.dp);
    return m_data;
  }
  void swap(tommath_int &o) BOOST_NOEXCEPT { mp_exch(&m_data, &o.data()); }

protected:
  ::mp_int m_data;
};

#define BOOST_MP_TOMMATH_BIT_OP_CHECK(x)                                       \
  if (SIGN(&x.data()))                                                         \
  BOOST_THROW_EXCEPTION(std::runtime_error(                                    \
      "Bitwise operations on libtommath negative valued integers are "         \
      "disabled as they produce unpredictable results"))

int eval_get_sign(const tommath_int &val);

inline void eval_add(tommath_int &t, const tommath_int &o) {
  detail::check_tommath_result(
      mp_add(&t.data(), const_cast<::mp_int *>(&o.data()), &t.data()));
}
inline void eval_subtract(tommath_int &t, const tommath_int &o) {
  detail::check_tommath_result(
      mp_sub(&t.data(), const_cast<::mp_int *>(&o.data()), &t.data()));
}
inline void eval_multiply(tommath_int &t, const tommath_int &o) {
  detail::check_tommath_result(
      mp_mul(&t.data(), const_cast<::mp_int *>(&o.data()), &t.data()));
}
inline void eval_divide(tommath_int &t, const tommath_int &o) {
  using default_ops::eval_is_zero;
  tommath_int temp;
  if (eval_is_zero(o))
    BOOST_THROW_EXCEPTION(std::overflow_error("Integer division by zero"));
  detail::check_tommath_result(mp_div(
      &t.data(), const_cast<::mp_int *>(&o.data()), &t.data(), &temp.data()));
}
inline void eval_modulus(tommath_int &t, const tommath_int &o) {
  using default_ops::eval_is_zero;
  if (eval_is_zero(o))
    BOOST_THROW_EXCEPTION(std::overflow_error("Integer division by zero"));
  bool neg = eval_get_sign(t) < 0;
  bool neg2 = eval_get_sign(o) < 0;
  detail::check_tommath_result(
      mp_mod(&t.data(), const_cast<::mp_int *>(&o.data()), &t.data()));
  if ((neg != neg2) && (eval_get_sign(t) != 0)) {
    t.negate();
    detail::check_tommath_result(
        mp_add(&t.data(), const_cast<::mp_int *>(&o.data()), &t.data()));
    t.negate();
  } else if (neg && (t.compare(o) == 0)) {
    mp_zero(&t.data());
  }
}
template <class UI> inline void eval_left_shift(tommath_int &t, UI i) {
  detail::check_tommath_result(
      mp_mul_2d(&t.data(), static_cast<unsigned>(i), &t.data()));
}
template <class UI> inline void eval_right_shift(tommath_int &t, UI i) {
  using default_ops::eval_decrement;
  using default_ops::eval_increment;
  bool neg = eval_get_sign(t) < 0;
  tommath_int d;
  if (neg)
    eval_increment(t);
  detail::check_tommath_result(
      mp_div_2d(&t.data(), static_cast<unsigned>(i), &t.data(), &d.data()));
  if (neg)
    eval_decrement(t);
}
template <class UI>
inline void eval_left_shift(tommath_int &t, const tommath_int &v, UI i) {
  detail::check_tommath_result(mp_mul_2d(const_cast<::mp_int *>(&v.data()),
                                         static_cast<unsigned>(i), &t.data()));
}
/*
template <class UI>
inline void eval_right_shift(tommath_int& t, const tommath_int& v, UI i)
{
   tommath_int d;
   detail::check_tommath_result(mp_div_2d(const_cast< ::mp_int*>(&v.data()),
static_cast<unsigned long>(i), &t.data(), &d.data()));
}
*/
inline void eval_bitwise_and(tommath_int &result, const tommath_int &v) {
  BOOST_MP_TOMMATH_BIT_OP_CHECK(result);
  BOOST_MP_TOMMATH_BIT_OP_CHECK(v);
  detail::check_tommath_result(mp_and(
      &result.data(), const_cast<::mp_int *>(&v.data()), &result.data()));
}

inline void eval_bitwise_or(tommath_int &result, const tommath_int &v) {
  BOOST_MP_TOMMATH_BIT_OP_CHECK(result);
  BOOST_MP_TOMMATH_BIT_OP_CHECK(v);
  detail::check_tommath_result(
      mp_or(&result.data(), const_cast<::mp_int *>(&v.data()), &result.data()));
}

inline void eval_bitwise_xor(tommath_int &result, const tommath_int &v) {
  BOOST_MP_TOMMATH_BIT_OP_CHECK(result);
  BOOST_MP_TOMMATH_BIT_OP_CHECK(v);
  detail::check_tommath_result(mp_xor(
      &result.data(), const_cast<::mp_int *>(&v.data()), &result.data()));
}

inline void eval_add(tommath_int &t, const tommath_int &p,
                     const tommath_int &o) {
  detail::check_tommath_result(mp_add(const_cast<::mp_int *>(&p.data()),
                                      const_cast<::mp_int *>(&o.data()),
                                      &t.data()));
}
inline void eval_subtract(tommath_int &t, const tommath_int &p,
                          const tommath_int &o) {
  detail::check_tommath_result(mp_sub(const_cast<::mp_int *>(&p.data()),
                                      const_cast<::mp_int *>(&o.data()),
                                      &t.data()));
}
inline void eval_multiply(tommath_int &t, const tommath_int &p,
                          const tommath_int &o) {
  detail::check_tommath_result(mp_mul(const_cast<::mp_int *>(&p.data()),
                                      const_cast<::mp_int *>(&o.data()),
                                      &t.data()));
}
inline void eval_divide(tommath_int &t, const tommath_int &p,
                        const tommath_int &o) {
  using default_ops::eval_is_zero;
  tommath_int d;
  if (eval_is_zero(o))
    BOOST_THROW_EXCEPTION(std::overflow_error("Integer division by zero"));
  detail::check_tommath_result(mp_div(const_cast<::mp_int *>(&p.data()),
                                      const_cast<::mp_int *>(&o.data()),
                                      &t.data(), &d.data()));
}
inline void eval_modulus(tommath_int &t, const tommath_int &p,
                         const tommath_int &o) {
  using default_ops::eval_is_zero;
  if (eval_is_zero(o))
    BOOST_THROW_EXCEPTION(std::overflow_error("Integer division by zero"));
  bool neg = eval_get_sign(p) < 0;
  bool neg2 = eval_get_sign(o) < 0;
  detail::check_tommath_result(mp_mod(const_cast<::mp_int *>(&p.data()),
                                      const_cast<::mp_int *>(&o.data()),
                                      &t.data()));
  if ((neg != neg2) && (eval_get_sign(t) != 0)) {
    t.negate();
    detail::check_tommath_result(
        mp_add(&t.data(), const_cast<::mp_int *>(&o.data()), &t.data()));
    t.negate();
  } else if (neg && (t.compare(o) == 0)) {
    mp_zero(&t.data());
  }
}

inline void eval_bitwise_and(tommath_int &result, const tommath_int &u,
                             const tommath_int &v) {
  BOOST_MP_TOMMATH_BIT_OP_CHECK(u);
  BOOST_MP_TOMMATH_BIT_OP_CHECK(v);
  detail::check_tommath_result(mp_and(const_cast<::mp_int *>(&u.data()),
                                      const_cast<::mp_int *>(&v.data()),
                                      &result.data()));
}

inline void eval_bitwise_or(tommath_int &result, const tommath_int &u,
                            const tommath_int &v) {
  BOOST_MP_TOMMATH_BIT_OP_CHECK(u);
  BOOST_MP_TOMMATH_BIT_OP_CHECK(v);
  detail::check_tommath_result(mp_or(const_cast<::mp_int *>(&u.data()),
                                     const_cast<::mp_int *>(&v.data()),
                                     &result.data()));
}

inline void eval_bitwise_xor(tommath_int &result, const tommath_int &u,
                             const tommath_int &v) {
  BOOST_MP_TOMMATH_BIT_OP_CHECK(u);
  BOOST_MP_TOMMATH_BIT_OP_CHECK(v);
  detail::check_tommath_result(mp_xor(const_cast<::mp_int *>(&u.data()),
                                      const_cast<::mp_int *>(&v.data()),
                                      &result.data()));
}
/*
inline void eval_complement(tommath_int& result, const tommath_int& u)
{
   //
   // Although this code works, it doesn't really do what the user might
expect....
   // and it's hard to see how it ever could.  Disabled for now:
   //
   result = u;
   for(int i = 0; i < result.data().used; ++i)
   {
      result.data().dp[i] = MP_MASK & ~(result.data().dp[i]);
   }
   //
   // We now need to pad out the left of the value with 1's to round up to a
whole number of
   // CHAR_BIT * sizeof(mp_digit) units.  Otherwise we'll end up with a very
strange number of
   // bits set!
   //
   unsigned shift = result.data().used * DIGIT_BIT;    // How many bits we're
actually using
   // How many bits we actually need, reduced by one to account for a mythical
sign bit: int padding = result.data().used *
std::numeric_limits<mp_digit>::digits - shift - 1; while(padding >=
std::numeric_limits<mp_digit>::digits) padding -=
std::numeric_limits<mp_digit>::digits;

   // Create a mask providing the extra bits we need and add to result:
   tommath_int mask;
   mask = static_cast<boost::long_long_type>((1u << padding) - 1);
   eval_left_shift(mask, shift);
   add(result, mask);
}
*/
inline bool eval_is_zero(const tommath_int &val) {
  return mp_iszero(&val.data());
}
inline int eval_get_sign(const tommath_int &val) {
  return mp_iszero(&val.data()) ? 0 : SIGN(&val.data()) ? -1 : 1;
}
/*
template <class A>
inline void eval_convert_to(A* result, const tommath_int& val)
{
   *result = boost::lexical_cast<A>(val.str(0, std::ios_base::fmtflags(0)));
}
inline void eval_convert_to(char* result, const tommath_int& val)
{
   *result = static_cast<char>(boost::lexical_cast<int>(val.str(0,
std::ios_base::fmtflags(0))));
}
inline void eval_convert_to(unsigned char* result, const tommath_int& val)
{
   *result = static_cast<unsigned char>(boost::lexical_cast<unsigned>(val.str(0,
std::ios_base::fmtflags(0))));
}
inline void eval_convert_to(signed char* result, const tommath_int& val)
{
   *result = static_cast<signed char>(boost::lexical_cast<int>(val.str(0,
std::ios_base::fmtflags(0))));
}
*/
inline void eval_abs(tommath_int &result, const tommath_int &val) {
  detail::check_tommath_result(
      mp_abs(const_cast<::mp_int *>(&val.data()), &result.data()));
}
inline void eval_gcd(tommath_int &result, const tommath_int &a,
                     const tommath_int &b) {
  detail::check_tommath_result(mp_gcd(const_cast<::mp_int *>(&a.data()),
                                      const_cast<::mp_int *>(&b.data()),
                                      const_cast<::mp_int *>(&result.data())));
}
inline void eval_lcm(tommath_int &result, const tommath_int &a,
                     const tommath_int &b) {
  detail::check_tommath_result(mp_lcm(const_cast<::mp_int *>(&a.data()),
                                      const_cast<::mp_int *>(&b.data()),
                                      const_cast<::mp_int *>(&result.data())));
}
inline void eval_powm(tommath_int &result, const tommath_int &base,
                      const tommath_int &p, const tommath_int &m) {
  if (eval_get_sign(p) < 0) {
    BOOST_THROW_EXCEPTION(
        std::runtime_error("powm requires a positive exponent."));
  }
  detail::check_tommath_result(mp_exptmod(
      const_cast<::mp_int *>(&base.data()), const_cast<::mp_int *>(&p.data()),
      const_cast<::mp_int *>(&m.data()), &result.data()));
}

inline void eval_qr(const tommath_int &x, const tommath_int &y, tommath_int &q,
                    tommath_int &r) {
  detail::check_tommath_result(mp_div(const_cast<::mp_int *>(&x.data()),
                                      const_cast<::mp_int *>(&y.data()),
                                      &q.data(), &r.data()));
}

inline unsigned eval_lsb(const tommath_int &val) {
  int c = eval_get_sign(val);
  if (c == 0) {
    BOOST_THROW_EXCEPTION(std::range_error("No bits were set in the operand."));
  }
  if (c < 0) {
    BOOST_THROW_EXCEPTION(
        std::range_error("Testing individual bits in negative values is not "
                         "supported - results are undefined."));
  }
  return mp_cnt_lsb(const_cast<::mp_int *>(&val.data()));
}

inline unsigned eval_msb(const tommath_int &val) {
  int c = eval_get_sign(val);
  if (c == 0) {
    BOOST_THROW_EXCEPTION(std::range_error("No bits were set in the operand."));
  }
  if (c < 0) {
    BOOST_THROW_EXCEPTION(
        std::range_error("Testing individual bits in negative values is not "
                         "supported - results are undefined."));
  }
  return mp_count_bits(const_cast<::mp_int *>(&val.data())) - 1;
}

template <class Integer>
inline typename enable_if<is_unsigned<Integer>, Integer>::type
eval_integer_modulus(const tommath_int &x, Integer val) {
  static const mp_digit m = (static_cast<mp_digit>(1) << DIGIT_BIT) - 1;
  if (val <= m) {
    mp_digit d;
    detail::check_tommath_result(mp_mod_d(const_cast<::mp_int *>(&x.data()),
                                          static_cast<mp_digit>(val), &d));
    return d;
  } else {
    return default_ops::eval_integer_modulus(x, val);
  }
}
template <class Integer>
inline typename enable_if<is_signed<Integer>, Integer>::type
eval_integer_modulus(const tommath_int &x, Integer val) {
  return eval_integer_modulus(x,
                              boost::multiprecision::detail::unsigned_abs(val));
}

inline std::size_t hash_value(const tommath_int &val) {
  std::size_t result = 0;
  std::size_t len = val.data().used;
  for (std::size_t i = 0; i < len; ++i)
    boost::hash_combine(result, val.data().dp[i]);
  boost::hash_combine(result, val.data().sign);
  return result;
}

} // namespace backends

using boost::multiprecision::backends::tommath_int;

template <>
struct number_category<tommath_int> : public mpl::int_<number_kind_integer> {};

typedef number<tommath_int> tom_int;
typedef rational_adaptor<tommath_int> tommath_rational;
typedef number<tommath_rational> tom_rational;

} // namespace multiprecision
} // namespace boost

namespace std {

template <boost::multiprecision::expression_template_option ExpressionTemplates>
class numeric_limits<boost::multiprecision::number<
    boost::multiprecision::tommath_int, ExpressionTemplates>> {
  typedef boost::multiprecision::number<boost::multiprecision::tommath_int,
                                        ExpressionTemplates>
      number_type;

public:
  BOOST_STATIC_CONSTEXPR bool is_specialized = true;
  //
  // Largest and smallest numbers are bounded only by available memory, set
  // to zero:
  //
  static number_type(min)() { return number_type(); }
  static number_type(max)() { return number_type(); }
  static number_type lowest() { return (min)(); }
  BOOST_STATIC_CONSTEXPR int digits = INT_MAX;
  BOOST_STATIC_CONSTEXPR int digits10 = (INT_MAX / 1000) * 301L;
  BOOST_STATIC_CONSTEXPR int max_digits10 = digits10 + 3;
  BOOST_STATIC_CONSTEXPR bool is_signed = true;
  BOOST_STATIC_CONSTEXPR bool is_integer = true;
  BOOST_STATIC_CONSTEXPR bool is_exact = true;
  BOOST_STATIC_CONSTEXPR int radix = 2;
  static number_type epsilon() { return number_type(); }
  static number_type round_error() { return number_type(); }
  BOOST_STATIC_CONSTEXPR int min_exponent = 0;
  BOOST_STATIC_CONSTEXPR int min_exponent10 = 0;
  BOOST_STATIC_CONSTEXPR int max_exponent = 0;
  BOOST_STATIC_CONSTEXPR int max_exponent10 = 0;
  BOOST_STATIC_CONSTEXPR bool has_infinity = false;
  BOOST_STATIC_CONSTEXPR bool has_quiet_NaN = false;
  BOOST_STATIC_CONSTEXPR bool has_signaling_NaN = false;
  BOOST_STATIC_CONSTEXPR float_denorm_style has_denorm = denorm_absent;
  BOOST_STATIC_CONSTEXPR bool has_denorm_loss = false;
  static number_type infinity() { return number_type(); }
  static number_type quiet_NaN() { return number_type(); }
  static number_type signaling_NaN() { return number_type(); }
  static number_type denorm_min() { return number_type(); }
  BOOST_STATIC_CONSTEXPR bool is_iec559 = false;
  BOOST_STATIC_CONSTEXPR bool is_bounded = false;
  BOOST_STATIC_CONSTEXPR bool is_modulo = false;
  BOOST_STATIC_CONSTEXPR bool traps = false;
  BOOST_STATIC_CONSTEXPR bool tinyness_before = false;
  BOOST_STATIC_CONSTEXPR float_round_style round_style = round_toward_zero;
};

#ifndef BOOST_NO_INCLASS_MEMBER_INITIALIZATION

template <boost::multiprecision::expression_template_option ExpressionTemplates>
BOOST_CONSTEXPR_OR_CONST int numeric_limits<boost::multiprecision::number<
    boost::multiprecision::tommath_int, ExpressionTemplates>>::digits;
template <boost::multiprecision::expression_template_option ExpressionTemplates>
BOOST_CONSTEXPR_OR_CONST int numeric_limits<boost::multiprecision::number<
    boost::multiprecision::tommath_int, ExpressionTemplates>>::digits10;
template <boost::multiprecision::expression_template_option ExpressionTemplates>
BOOST_CONSTEXPR_OR_CONST int numeric_limits<boost::multiprecision::number<
    boost::multiprecision::tommath_int, ExpressionTemplates>>::max_digits10;
template <boost::multiprecision::expression_template_option ExpressionTemplates>
BOOST_CONSTEXPR_OR_CONST bool numeric_limits<boost::multiprecision::number<
    boost::multiprecision::tommath_int, ExpressionTemplates>>::is_signed;
template <boost::multiprecision::expression_template_option ExpressionTemplates>
BOOST_CONSTEXPR_OR_CONST bool numeric_limits<boost::multiprecision::number<
    boost::multiprecision::tommath_int, ExpressionTemplates>>::is_integer;
template <boost::multiprecision::expression_template_option ExpressionTemplates>
BOOST_CONSTEXPR_OR_CONST bool numeric_limits<boost::multiprecision::number<
    boost::multiprecision::tommath_int, ExpressionTemplates>>::is_exact;
template <boost::multiprecision::expression_template_option ExpressionTemplates>
BOOST_CONSTEXPR_OR_CONST int numeric_limits<boost::multiprecision::number<
    boost::multiprecision::tommath_int, ExpressionTemplates>>::radix;
template <boost::multiprecision::expression_template_option ExpressionTemplates>
BOOST_CONSTEXPR_OR_CONST int numeric_limits<boost::multiprecision::number<
    boost::multiprecision::tommath_int, ExpressionTemplates>>::min_exponent;
template <boost::multiprecision::expression_template_option ExpressionTemplates>
BOOST_CONSTEXPR_OR_CONST int numeric_limits<boost::multiprecision::number<
    boost::multiprecision::tommath_int, ExpressionTemplates>>::min_exponent10;
template <boost::multiprecision::expression_template_option ExpressionTemplates>
BOOST_CONSTEXPR_OR_CONST int numeric_limits<boost::multiprecision::number<
    boost::multiprecision::tommath_int, ExpressionTemplates>>::max_exponent;
template <boost::multiprecision::expression_template_option ExpressionTemplates>
BOOST_CONSTEXPR_OR_CONST int numeric_limits<boost::multiprecision::number<
    boost::multiprecision::tommath_int, ExpressionTemplates>>::max_exponent10;
template <boost::multiprecision::expression_template_option ExpressionTemplates>
BOOST_CONSTEXPR_OR_CONST bool numeric_limits<boost::multiprecision::number<
    boost::multiprecision::tommath_int, ExpressionTemplates>>::has_infinity;
template <boost::multiprecision::expression_template_option ExpressionTemplates>
BOOST_CONSTEXPR_OR_CONST bool numeric_limits<boost::multiprecision::number<
    boost::multiprecision::tommath_int, ExpressionTemplates>>::has_quiet_NaN;
template <boost::multiprecision::expression_template_option ExpressionTemplates>
BOOST_CONSTEXPR_OR_CONST bool numeric_limits<
    boost::multiprecision::number<boost::multiprecision::tommath_int,
                                  ExpressionTemplates>>::has_signaling_NaN;
template <boost::multiprecision::expression_template_option ExpressionTemplates>
BOOST_CONSTEXPR_OR_CONST float_denorm_style
    numeric_limits<boost::multiprecision::number<
        boost::multiprecision::tommath_int, ExpressionTemplates>>::has_denorm;
template <boost::multiprecision::expression_template_option ExpressionTemplates>
BOOST_CONSTEXPR_OR_CONST bool numeric_limits<boost::multiprecision::number<
    boost::multiprecision::tommath_int, ExpressionTemplates>>::has_denorm_loss;
template <boost::multiprecision::expression_template_option ExpressionTemplates>
BOOST_CONSTEXPR_OR_CONST bool numeric_limits<boost::multiprecision::number<
    boost::multiprecision::tommath_int, ExpressionTemplates>>::is_iec559;
template <boost::multiprecision::expression_template_option ExpressionTemplates>
BOOST_CONSTEXPR_OR_CONST bool numeric_limits<boost::multiprecision::number<
    boost::multiprecision::tommath_int, ExpressionTemplates>>::is_bounded;
template <boost::multiprecision::expression_template_option ExpressionTemplates>
BOOST_CONSTEXPR_OR_CONST bool numeric_limits<boost::multiprecision::number<
    boost::multiprecision::tommath_int, ExpressionTemplates>>::is_modulo;
template <boost::multiprecision::expression_template_option ExpressionTemplates>
BOOST_CONSTEXPR_OR_CONST bool numeric_limits<boost::multiprecision::number<
    boost::multiprecision::tommath_int, ExpressionTemplates>>::traps;
template <boost::multiprecision::expression_template_option ExpressionTemplates>
BOOST_CONSTEXPR_OR_CONST bool numeric_limits<boost::multiprecision::number<
    boost::multiprecision::tommath_int, ExpressionTemplates>>::tinyness_before;
template <boost::multiprecision::expression_template_option ExpressionTemplates>
BOOST_CONSTEXPR_OR_CONST float_round_style
    numeric_limits<boost::multiprecision::number<
        boost::multiprecision::tommath_int, ExpressionTemplates>>::round_style;

#endif
} // namespace std

#endif


// adaptation from diff_drive_controller/CanController

#include <cmath>
#include <tf/transform_datatypes.h>
#include <urdf_parser/urdf_parser.h>
#include <urdf/urdfdom_compatibility.h>
#include <boost/assign.hpp>
#include "pwm_driver/can_controller.h"

namespace pwm_driver{
	CanController::CanController()
		: name_("can_controller"), cmd_v(0), cmd_w(0), cmd_t(0), cmd_timeout_(0.2),
		v_scale_(1.0), w_scale_(1.0),
		wheel_separation_(1.0), wheel_radius_(1.0){}

	bool CanController::init(hardware_interface::VelocityJointInterface* hw,
			ros::NodeHandle & root_nh,
			ros::NodeHandle & controller_nh)
	{
		controller_nh.param("can_port", can_port_, can_port_);
		controller_nh.param("v_scale", v_scale_, v_scale_);
		controller_nh.param("w_scale", w_scale_, w_scale_);
		controller_nh.param("cmd_timeout", cmd_timeout_, cmd_timeout_);
		controller_nh.param("wheel_separation", wheel_separation_, wheel_separation_);
		controller_nh.param("wheel_radius", wheel_radius_, wheel_radius_);
		controller_nh.param("left_wheel", left_wheel_name_, left_wheel_name_);
		controller_nh.param("right_wheel", right_wheel_name_, right_wheel_name_);

		const std::string complete_ns = controller_nh.getNamespace();
		std::size_t id = complete_ns.find_last_of("/");
		name_ = complete_ns.substr(id + 1);

		// Get the joint object to use in the realtime loop
		left_wheel_joint_ = hw->getHandle(left_wheel_name_);  // throws on failure
		right_wheel_joint_ = hw->getHandle(right_wheel_name_);  // throws on failure
		can_read_thread_ = std::thread (&CanController::read, this);
		return true;
	}

	void CanController::read(){
		can_.reset(new ROSCan(can_port_.c_str()));

		if(!can_->init()){
			std::cout << "CAN Open Failed!" << std::endl;
			return;
		}

		can_->set_filter(0x02001100, 0x1FFFFFFF); // joy -100 ~ 100
		//can_->set_filter(0x02000200, 0x1FFFF0FF); // joy -speed ~ speed

		std::cout << "CAN Open Success!" << std::endl;
		cmd_t = ros::Time::now();

		fd_set rdfs;
		timeval tv;
		can_frame cf;
		tv.tv_sec = 0;
		tv.tv_usec = 10 * 1e3; // 10 ms

		while(true){
			FD_ZERO(&rdfs);
			FD_SET(can_->_s_h, &rdfs);

			int rc = select(can_->_s_h+1, &rdfs, NULL, NULL, &tv);
			if(rc && FD_ISSET(can_->_s_h, &rdfs)){
				can_->read(cf);
				int8_t cmd_x = (int8_t) cf.data[0];
				int8_t cmd_y = (int8_t) cf.data[1];

				cmd_v = cmd_y / 100.0 / v_scale_;
				cmd_w = - cmd_x / 100.0 / w_scale_;
				cmd_t = ros::Time::now();
				//std::cout << cmd_x << std::endl;
			}

		}

	}

	void CanController::update(const ros::Time& time, const ros::Duration& period)
	{
		const double ws  = wheel_separation_;
		const double wr = wheel_radius_;
		const double dt = (time - cmd_t).toSec();

		// Brake if cmd_vel has timeout:
		if (dt > cmd_timeout_)
		{
			cmd_v = cmd_w = 0.0;
		}

		// Compute wheels velocities:
		const double vel_left  = (cmd_v - cmd_w * ws / 2.0)/wr;
		const double vel_right = (cmd_v + cmd_w * ws / 2.0)/wr;

		// Set wheels velocities:
		left_wheel_joint_.setCommand(vel_left);
		right_wheel_joint_.setCommand(vel_right);
	}

	void CanController::starting(const ros::Time& time)
	{
		brake();
		// Register starting time used to keep fixed rate
	}

	void CanController::stopping(const ros::Time& /*time*/)
	{
		brake();
	}

	void CanController::brake()
	{
		const double vel = 0.0;
		left_wheel_joint_.setCommand(vel);
		right_wheel_joint_.setCommand(vel);
	}
}
// namespace diff_drive_controller


#pragma once

#include <boost/date_time/gregorian/gregorian_types.hpp>
#include <boost/multiprecision/cpp_int.hpp>

#include <atomic>
#include <cinttypes>
#include <future>
#include <string>
#include <type_traits>
#include <vector>

#include "vacationdb.hpp"

namespace Vacationdb {
	namespace _detail {
		using Date = boost::gregorian::date;
		using Number = boost::multiprecision::cpp_rational;

		struct Person {
			std::string name;
			Date start_date;
			Number percent_time;
			struct Extra_Time_t {
				Date begin;
				Date end;
				Number percent_time;
				bool valid = true;
			};
			std::vector<Extra_Time_t> extra_time;
			struct Day_Taken_t {
				Date day;
				Number value;
			};
			std::vector<std::vector<Day_Taken_t>> days_taken;
			bool valid = true;
		};

		static_assert(std::is_move_constructible<Person>::value, "Person must be move constructible");
		static_assert(std::is_move_assignable<Person>::value, "Person must be move assignable");

		struct Day {
			std::string name;
			Number rollover;
			Number yearly_bonus;
			struct Day_Rules_Data {
				uint32_t month_begin;
				Number days_per_year;
				bool valid = true;
			};
			std::vector<Day_Rules_Data> rules;
			bool valid = true;
		};

		static_assert(std::is_move_constructible<Day>::value, "Day must be move constructible");
		static_assert(std::is_move_assignable<Day>::value, "Day must be move assignable");

		VACATIONDB_SHARED Date create_date_safe(uint16_t start_year, uint16_t start_month, uint16_t start_day);
		VACATIONDB_SHARED Number create_number_safe(const char* value);

		class db_impl {
		public:
			db_impl() : io_lock(false) {};

			std::vector<Person> people;
			std::vector<Day> day_types;
			void validate(PersonID_t);
			void validate(PersonID_t, Extra_TimeID_t);
			void validate(DayID_t);
			void validate(DayID_t, RuleID_t);
			void add_day_to_people();
			void remove_day_from_people(size_t index);

			// File loading
			std::string current_file_name = "vdb.json";
			std::atomic<bool> io_lock;
			std::atomic<float> io_percentage;
			std::future<void> io_future;
			Vacationdb::IO_Status_t::Op_t io_curop = IO_Status_t::NOOP;
			void block_if_locked();
			void load_file();
			void save_file();
			void clear();
		};
	}
}


#include <windows.h>
#include <mmsystem.h>
#include "gv_base_type.h"
#include "gvt_array.h"
#include "gv_string.h"
#include "gv_time.h"
#include <boost/boost/date_time.hpp>
#include <boost/boost/date_time/gregorian/gregorian.hpp>
#include <boost/boost/date_time/posix_time/posix_time.hpp>
// use YYYY-MM-DD instead of  YYYY-mmm-DD
namespace gv
{
void gv_time::init()
{
// http://blogs.msdn.com/b/oldnewthing/archive/2008/09/08/8931563.aspx
#pragma GV_REMINDER( \
	"[MEMO]the QueryPerformanceFrequency is not reliable for timestamp!!!use the timegetime instead")

	LARGE_INTEGER freq, count;
	QueryPerformanceFrequency(&freq);
	QueryPerformanceCounter(&count);
	performance_counter_frequency = freq.QuadPart;
	start_performance_counter = count.QuadPart;

	FILETIME ft;
	GetSystemTimeAsFileTime(&ft);
	ULARGE_INTEGER epoch; // UNIX epoch (1970-01-01 00:00:00) expressed in Windows
						  // NT FILETIME
	epoch.LowPart = 0xD53E8000;
	epoch.HighPart = 0x019DB1DE;

	ULARGE_INTEGER ts;
	ts.LowPart = ft.dwLowDateTime;
	ts.HighPart = ft.dwHighDateTime;
	ts.QuadPart -= epoch.QuadPart;
	start_time_stamp = ts.QuadPart / 10;
	last_update_time_in_ms = timeGetTime();
	last_update_time_stamp = start_time_stamp - last_update_time_in_ms * 1000ull;
}

gv_ulong gv_get_performance_counter()
{
	LARGE_INTEGER count;
	QueryPerformanceCounter(&count);
	return count.QuadPart;
}

gv_ulong gv_time::get_performance_counter()
{
	return gv_get_performance_counter();
}

gv_ulong gv_time::get_millisec()
{
	return timeGetTime();
};
} // namespace gv


#define BOOST_TEST_MODULE TEST_RNN

#include <dynet/dynet.h>
#include <dynet/expr.h>
#include <dynet/exec.h>
#include <dynet/lstm.h>
#include <dynet/fast-lstm.h>
#include <dynet/gru.h>
#include <dynet/grad-check.h>
#include <dynet/param-init.h>
#include <boost/test/unit_test.hpp>
#include "test.h"
#include <stdexcept>
#include <fstream>

using namespace dynet;
using namespace std;

struct ExecTest {
  ExecTest() {
    // initialize if necessary
    if (default_device == nullptr) {
      for (auto x : {"ExecTest", "--dynet-seed", "10", "--dynet-mem", "10"}) {
        av.push_back(strdup(x));
      }
      ADD_EXTRA_ARGUMENTS(av)
      char **argv = &av[0];
      int argc = av.size();
      dynet::initialize(argc, argv);
    }
  }
  ~ExecTest() {
    // This was causing double deallocation errors?
    // for (auto x : av) free(x);
  }

  template <class T>
  std::string print_vec(const std::vector<T> vec) {
    ostringstream oss;
    if (vec.size()) oss << vec[0];
    for (size_t i = 1; i < vec.size(); i++)
      oss << ' ' << vec[i];
    return oss.str();
  }

  std::vector<char*> av;

};

// define the test suite
BOOST_FIXTURE_TEST_SUITE(exec_test, ExecTest);

BOOST_AUTO_TEST_CASE( autobatch_lstm_gradient ) {
  vector<float> results;
  dynet::ParameterCollection mod;
  dynet::VanillaLSTMBuilder lstm(2, 3, 10, mod);
  dynet::LookupParameter lp = mod.add_lookup_parameters(10, {3});
  for(size_t i = 0; i < 3; ++i) {
    dynet::autobatch_flag = i;
    dynet::ComputationGraph cg;
    lstm.new_graph(cg);
    vector<Expression> losses;
    for(size_t j = 0; j < 3; ++j) {
      lstm.start_new_sequence();
      for(size_t k = 0; k < 3; ++k) {
        Expression x = dynet::lookup(cg, lp, j*3 + k);
        lstm.add_input(x);
      }
      losses.push_back(squared_norm(lstm.final_h()[1]));
    }
    losses.push_back(losses[0] + losses[2]);
    Expression z = dynet::sum(losses);
    results.push_back(as_scalar(z.value()));
    BOOST_CHECK(check_grad(mod, z, 0));
  }
  for(size_t i = 1; i < results.size(); ++i)
    BOOST_CHECK_CLOSE(results[0], results[i], 0.0001);
}

// TODO: This is commented out because it inexplicably causes problems only when
//       performing manual install on mac on Travis CI, despite the fact that it
//       works in my local mac environment. Until it becomes possible to debug
//       on Travis, it will remain commented out.
// BOOST_AUTO_TEST_CASE( autobatch_big_sum ) {
//   dynet::ParameterCollection mod;
//   vector<float> f1(50), f2(30);
//   for(size_t i = 0; i < 50; ++i) f1[i] = i / 5000.0;
//   for(size_t i = 0; i < 30; ++i) f2[i] = i / 3000.0;
//   dynet::LookupParameter lp1 = mod.add_lookup_parameters(10, {5}, dynet::ParameterInitFromVector(f1));
//   dynet::LookupParameter lp2 = mod.add_lookup_parameters(10, {3}, dynet::ParameterInitFromVector(f2));
//   vector<float> results;
//   for(size_t i = 0; i < 3; ++i) {
//     dynet::autobatch_flag = i;
//     dynet::ComputationGraph cg;
//     vector<Expression> v1, v2;
//     for(size_t j = 0; j < 10000; ++j) {
//       v1.push_back(dynet::lookup(cg, lp1, (j*3 % 10)));
//       v2.push_back(dynet::lookup(cg, lp2, (j*3 % 10)));
//     }
//     Expression z = dynet::sum_elems(dynet::sum(v1)) + dynet::sum_elems(dynet::sum(v2));
//     results.push_back(as_scalar(z.value()));
//     BOOST_CHECK(check_grad(mod, z, 0));
//   }
//   for(size_t i = 1; i < results.size(); ++i)
//     BOOST_CHECK_CLOSE(results[0], results[i], 0.0001);
// }

BOOST_AUTO_TEST_CASE( param_after_node ) {
  auto autobatch_cache = dynet::autobatch_flag;
  for(size_t i = 0; i < 3; ++i) {
    dynet::autobatch_flag = i;
	  ComputationGraph cg;
	  ParameterCollection model;
	  Parameter param = model.add_parameters({ 1 });

	  Expression loss = zeroes(cg, { 1 });
	  parameter(cg, param);

	  cg.incremental_forward(loss);
	  cg.backward(loss);
  }
  dynet::autobatch_flag = autobatch_cache;
}

BOOST_AUTO_TEST_CASE( param_after_node_2 ) {
  auto autobatch_cache = dynet::autobatch_flag;
  for(size_t i = 0; i < 3; ++i) {
    dynet::autobatch_flag = i;
	  ComputationGraph cg;
	  ParameterCollection model;
	  LookupParameter param = model.add_lookup_parameters(10, { 1 });

	  lookup(cg, param, 1);
	  Expression loss = zeroes(cg, { 1 });

	  cg.incremental_forward(loss);
	  cg.backward(loss);
  }
  dynet::autobatch_flag = autobatch_cache;
}

BOOST_AUTO_TEST_CASE( loss_gradient ) {
  auto autobatch_cache = dynet::autobatch_flag;
  for(size_t i = 0; i < 2; ++i) {
    dynet::autobatch_flag = i;
	  ComputationGraph cg;
	  ParameterCollection model;
	  Parameter param = model.add_parameters({ 1 });

	  Expression loss = zeroes(cg, { 1 });
	  parameter(cg, param);

	  cg.incremental_forward(loss);
	  cg.backward(loss);
    BOOST_CHECK_EQUAL(as_scalar(loss.gradient()), 1.f);

  }
  dynet::autobatch_flag = autobatch_cache;
}

BOOST_AUTO_TEST_SUITE_END()


/*
 * Copyright 2014-2016 CyberVision, Inc.
 *
 * 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 KEYUTILS_HPP_
#define KEYUTILS_HPP_

#include <botan/botan.h>
#include <string>
#include <utility>
#include <cstdint>
#include <memory>
#include <boost/scoped_array.hpp>
#include "kaa/security/SecurityDefinitions.hpp"

namespace kaa {

class KeyUtils {
public:
    /**
     * Generates RSA private and public keys.
     *
     * @param length the length of the key in bits
     * @return the pair of keys. The first element of the pair is a public key and the second one - is a private key.
     */
    KeyPair generateKeyPair(std::size_t length);

    /**
     * Generates symmetric session key
     *
     * @param length the length of the key in bytes
     * @return the pair of key and initialization vector
     */
    SessionKey generateSessionKey(std::size_t length);

    /**
     * Checks consistency of the key pair.
     *
     * @param[in] keys     Target key pair
     * @retval    true     Keys are valid
     * @retval    false    Keys are invalid
     */
    bool checkKeyPair(const KeyPair &keys);

    static PublicKey loadPublicKey(const std::string& fileName);

    static PrivateKey loadPrivateKey(const std::string& fileName);

    static KeyPair loadKeyPair(const std::string& pubFileName, const std::string& privFileName);

    static void saveKeyPair(const KeyPair& pair, const std::string& pubFileName, const std::string& privFileName);

    static void savePublicKey(const PublicKey& key, const std::string& pubFileName);

    static void savePrivateKey(const PrivateKey& key, const std::string& privFileName);

private:
    static void readFile(const std::string& fileName, boost::scoped_array<char>& buf, std::size_t& len);

private:
    Botan::AutoSeeded_RNG     rng_;
};

}

#endif /* KEYUTILS_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 BOOST_SIMD_ARCH_COMMON_SCALAR_FUNCTION_INC_HPP_INCLUDED
#define BOOST_SIMD_ARCH_COMMON_SCALAR_FUNCTION_INC_HPP_INCLUDED

#include <boost/simd/constant/one.hpp>
#include <boost/simd/function/plus.hpp>
#include <boost/simd/function/inc.hpp>
#include <boost/simd/detail/dispatch/function/overload.hpp>
#include <boost/config.hpp>

namespace boost { namespace simd { namespace ext
{
  namespace bd = boost::dispatch;
  BOOST_DISPATCH_OVERLOAD ( inc_
                          , (typename A0)
                          , bd::cpu_
                          , bd::scalar_< bd::arithmetic_<A0> >
                          )
  {
    BOOST_FORCEINLINE A0 operator()(A0 a0) const BOOST_NOEXCEPT_IF_EXPR(a0+A0(1))
    {
      return a0+A0(1);
    }
  };
  BOOST_DISPATCH_OVERLOAD ( inc_
                          , (typename T)
                          ,  bd::cpu_
                          ,  bs::saturated_tag
                          ,  bd::scalar_<bd::fundamental_<T>>
                          )
  {
    BOOST_FORCEINLINE T operator()(const saturated_tag &, T a
                                  ) const BOOST_NOEXCEPT
    {
      return saturated_(inc)(a);
    }
  };
} } }

#include <boost/simd/arch/common/scalar/function/inc_s.hpp>

#endif


/*=============================================================================
    Copyright (c) 2001-2011 Joel de Guzman
    Copyright (c) 2006 Dan Marsden

    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)
==============================================================================*/
#if !defined(BOOST_FUSION_DISTANCE_IMPL_20060223_0814)
#define BOOST_FUSION_DISTANCE_IMPL_20060223_0814

#include <boost/mpl/minus.hpp>

namespace example
{
    struct example_struct_iterator_tag;
}

namespace boost { namespace fusion {

    namespace extension
    {
        template<typename Tag>
        struct distance_impl;

        template<>
        struct distance_impl<example::example_struct_iterator_tag>
        {
            template<typename First, typename Last>
            struct apply
                : mpl::minus<typename Last::index, typename First::index>
            {
                typedef apply<First, Last> self;

                static typename self::type
                call(First const& first, Last const& last)
                {
                    return typename self::type();
                }
            };
        };
    }
}}

#endif


#include "ftl/operators/detectandtrack.hpp"
#include "ftl/codecs/shapes.hpp"

#include <Eigen/Eigen>
#include <opencv2/core/eigen.hpp>
#include <opencv2/calib3d.hpp>

#define LOGURU_REPLACE_GLOG 1
#include <ftl/profiler.hpp>
#include <loguru.hpp>

using ftl::operators::ArUco;
using ftl::rgbd::Frame;

using ftl::codecs::Channel;
using ftl::codecs::Shape3D;

using cv::Mat;
using cv::Point2f;
using cv::Vec3d;

using std::vector;
using std::list;

static cv::Mat rmat(cv::Vec3d &rvec) {
	cv::Mat R(cv::Size(3, 3), CV_64FC1);
	cv::Rodrigues(rvec, R);
	return R;
}

static Eigen::Matrix4d matrix(cv::Vec3d &rvec, cv::Vec3d &tvec) {
	cv::Mat M = cv::Mat::eye(cv::Size(4, 4), CV_64FC1);
	rmat(rvec).copyTo(M(cv::Rect(0, 0, 3, 3)));
	M.at<double>(0, 3) = tvec[0];
	M.at<double>(1, 3) = tvec[1];
	M.at<double>(2, 3) = tvec[2];
	Eigen::Matrix4d r;
	cv::cv2eigen(M,r);
	return r;
}

ArUco::ArUco(ftl::operators::Graph *g, ftl::Configurable *cfg) : ftl::operators::Operator(g, cfg) {
	dictionary_ = cv::aruco::getPredefinedDictionary(
		cfg->value("dictionary", int(cv::aruco::DICT_4X4_50)));
	params_ = cv::aruco::DetectorParameters::create();
	params_->cornerRefinementMethod = cv::aruco::CORNER_REFINE_CONTOUR;
	params_->cornerRefinementMinAccuracy = 0.05;
	params_->cornerRefinementMaxIterations = 10;

	// default values 13, 23, 10, for speed just one thresholding window size
	params_->adaptiveThreshWinSizeMin = 13;
	params_->adaptiveThreshWinSizeMax = 23;
	params_->adaptiveThreshWinSizeStep = 10;

	channel_in_ = Channel::Colour;
	channel_out_ = Channel::Shapes3D;

	cfg->on("dictionary", [this,cfg]() {
		dictionary_ = cv::aruco::getPredefinedDictionary(
			cfg->value("dictionary", 0));
	});
}

bool ArUco::apply(Frame &in, Frame &out, cudaStream_t) {
	if (!in.hasChannel(channel_in_)) { return false; }

	estimate_pose_ = config()->value("estimate_pose", true);
	marker_size_ = config()->value("marker_size", 0.1f);

	job_ = ftl::pool.push([this, &in, &out](int) {
		std::vector<Vec3d> rvecs;
		std::vector<Vec3d> tvecs;
		std::vector<std::vector<cv::Point2f>> corners;
		std::vector<int> ids;

		{
			FTL_Profile("ArUco", 0.02);
			cv::cvtColor(in.get<cv::Mat>(channel_in_), tmp_, cv::COLOR_BGRA2GRAY);

			const Mat K = in.getLeftCamera().getCameraMatrix(tmp_.size());
			const Mat dist;

			cv::aruco::detectMarkers(tmp_, dictionary_,
									corners, ids, params_, cv::noArray(), K, dist);

			if (estimate_pose_) {
				cv::aruco::estimatePoseSingleMarkers(corners, marker_size_, K, dist, rvecs, tvecs);
			}
		}

		list<Shape3D> result;
		if (out.hasChannel(channel_out_)) {
			result = out.get<list<Shape3D>>(channel_out_);
		}

		for (size_t i = 0; i < rvecs.size(); i++) {
			if (estimate_pose_) {
				auto &t = result.emplace_back();
				t.id = ids[i];
				t.type = ftl::codecs::Shape3DType::ARUCO;
				t.pose = (in.getPose() * matrix(rvecs[i], tvecs[i])).cast<float>();
				t.size = Eigen::Vector3f(1.0f, 1.0f, 0.0f)*marker_size_;
				t.label = "Aruco-" + std::to_string(ids[i]);
			}
		}

		out.create<list<Shape3D>>(channel_out_).list = result;
	});
	return true;
}

void ArUco::wait(cudaStream_t) {
	if (job_.valid()) {
		job_.wait();
	}
}


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

#pragma once

#include <array>
#include <boost/functional/hash.hpp>  // IWYU pragma: keep
#include <cstddef>
#include <limits>
#include <string>
#include <type_traits>
#include <unordered_map>
#include <utility>

#include "DataStructures/DataBox/Prefixes.hpp"
#include "DataStructures/Tensor/Tensor.hpp"
#include "DataStructures/Variables.hpp"
#include "Domain/SizeOfElement.hpp"
#include "Domain/Structure/Element.hpp"  // IWYU pragma: keep
#include "Domain/Structure/OrientationMapHelpers.hpp"
#include "Domain/Tags.hpp"  // IWYU pragma: keep
#include "Evolution/DiscontinuousGalerkin/Limiters/HwenoImpl.hpp"
#include "Evolution/DiscontinuousGalerkin/Limiters/MinmodTci.hpp"
#include "Evolution/DiscontinuousGalerkin/Limiters/MinmodType.hpp"
#include "Evolution/DiscontinuousGalerkin/Limiters/SimpleWenoImpl.hpp"
#include "Evolution/DiscontinuousGalerkin/Limiters/WenoGridHelpers.hpp"
#include "Evolution/DiscontinuousGalerkin/Limiters/WenoHelpers.hpp"
#include "Evolution/DiscontinuousGalerkin/Limiters/WenoType.hpp"
#include "NumericalAlgorithms/Interpolation/RegularGridInterpolant.hpp"
#include "NumericalAlgorithms/LinearOperators/MeanValue.hpp"
#include "NumericalAlgorithms/Spectral/Mesh.hpp"
#include "Options/Options.hpp"
#include "Utilities/Algorithm.hpp"
#include "Utilities/ErrorHandling/Assert.hpp"
#include "Utilities/Gsl.hpp"
#include "Utilities/MakeArray.hpp"
#include "Utilities/TMPL.hpp"
#include "Utilities/TaggedTuple.hpp"

/// \cond
template <size_t VolumeDim>
class Direction;
template <size_t VolumeDim>
class ElementId;

namespace PUP {
class er;
}  // namespace PUP

namespace Limiters {
template <size_t VolumeDim, typename TagsToLimit>
class Weno;
}  // namespace Limiters
/// \endcond

namespace Limiters {
/// \ingroup LimitersGroup
/// \brief A compact-stencil WENO limiter for DG
///
/// Implements the simple WENO limiter of \cite Zhong2013 and the Hermite WENO
/// (HWENO) limiter of \cite Zhu2016 for an arbitrary set of tensors. These
/// limiters require communication only between nearest-neighbor elements, but
/// preserve the full order of the DG solution when the solution is smooth.
/// Full volume data is communicated between neighbors.
///
/// The limiter uses the minmod-based TVB troubled-cell indicator (TCI) of
/// \cite Cockburn1999 to identify elements that need limiting. The simple
/// WENO implementation follows the paper: it checks the TCI independently
/// to each tensor component, so that only certain tensor components may be
/// limited. The HWENO implementation checks the TCI for all tensor components,
/// and if any single component is troubled, then all components are limited.
/// Note that the HWENO paper, because it specializes the limiter to the
/// Newtonian Euler fluid system, uses a more sophisticated TCI that is adapted
/// to the particulars of the fluid system. We instead use the TVB indicator
/// because it is easily applied to a general set of tensors.
///
/// For each tensor component to limit, the new solution is obtained by WENO
/// reconstruction --- a linear combination of the local DG solution and a
/// "modified" solution from each neighbor element. For the simple WENO limiter,
/// the modified solution is obtained by simply extrapolating the neighbor
/// solution onto the troubled element. For the HWENO limiter, the modified
/// solution is obtained by a least-squares fit to the solution across multiple
/// neighboring elements.
///
/// To reconstruct the WENO solution from the local solution and the modified
/// neighbor solutions, the standard WENO procedure is followed. We use the
/// oscillation indicator of \cite Dumbser2007, modified for use on the
/// square/cube grids of SpECTRE. We favor this indicator because portions of
/// the work can be precomputed, leading to an oscillation measure that is
/// efficient to evaluate.
///
/// \warning
/// Limitations:
/// - Does not support non-Legendre bases; this is ASSERTed.
/// - Does not support h- or p-refinement; this is ASSERTed.
/// - Does not support curved elements; this is not enforced.
template <size_t VolumeDim, typename... Tags>
class Weno<VolumeDim, tmpl::list<Tags...>> {
 public:
  /// \brief The WenoType
  ///
  /// One of `Limiters::WenoType`. See the `Limiters::Weno`
  /// documentation for details.
  struct Type {
    using type = WenoType;
    static constexpr Options::String help = {"Type of WENO limiter"};
  };
  /// \brief The linear weight given to each neighbor
  ///
  /// This linear weight gets combined with the oscillation indicator to
  /// compute the weight for each WENO estimated solution. The standard value
  /// in the literature is 0.001; larger values may be better suited for
  /// problems with strong shocks, and smaller values may be better suited to
  /// smooth problems.
  struct NeighborWeight {
    using type = double;
    static type lower_bound() noexcept { return 1e-6; }
    static type upper_bound() noexcept { return 0.1; }
    static constexpr Options::String help = {
        "Linear weight for each neighbor element's solution"};
  };
  /// \brief The TVB constant for the minmod TCI
  ///
  /// See `Limiters::Minmod` documentation for details.
  struct TvbConstant {
    using type = double;
    static type lower_bound() noexcept { return 0.0; }
    static constexpr Options::String help = {"TVB constant 'm'"};
  };
  /// \brief Turn the limiter off
  ///
  /// This option exists to temporarily disable the limiter for debugging
  /// purposes. For problems where limiting is not needed, the preferred
  /// approach is to not compile the limiter into the executable.
  struct DisableForDebugging {
    using type = bool;
    static type suggested_value() noexcept { return false; }
    static constexpr Options::String help = {"Disable the limiter"};
  };
  using options =
      tmpl::list<Type, NeighborWeight, TvbConstant, DisableForDebugging>;
  static constexpr Options::String help = {"A WENO limiter for DG"};

  Weno(WenoType weno_type, double neighbor_linear_weight, double tvb_constant,
       bool disable_for_debugging = false) noexcept;

  Weno() noexcept = default;
  Weno(const Weno& /*rhs*/) = default;
  Weno& operator=(const Weno& /*rhs*/) = default;
  Weno(Weno&& /*rhs*/) noexcept = default;
  Weno& operator=(Weno&& /*rhs*/) noexcept = default;
  ~Weno() = default;

  // NOLINTNEXTLINE(google-runtime-references)
  void pup(PUP::er& p) noexcept;

  /// \brief Data to send to neighbor elements
  struct PackagedData {
    Variables<tmpl::list<Tags...>> volume_data;
    tuples::TaggedTuple<::Tags::Mean<Tags>...> means;
    Mesh<VolumeDim> mesh;
    std::array<double, VolumeDim> element_size =
        make_array<VolumeDim>(std::numeric_limits<double>::signaling_NaN());

    // NOLINTNEXTLINE(google-runtime-references)
    void pup(PUP::er& p) noexcept {
      p | volume_data;
      p | means;
      p | mesh;
      p | element_size;
    }
  };

  using package_argument_tags =
      tmpl::list<Tags..., domain::Tags::Mesh<VolumeDim>,
                 domain::Tags::SizeOfElement<VolumeDim>>;

  /// \brief Package data for sending to neighbor elements
  void package_data(gsl::not_null<PackagedData*> packaged_data,
                    const typename Tags::type&... tensors,
                    const Mesh<VolumeDim>& mesh,
                    const std::array<double, VolumeDim>& element_size,
                    const OrientationMap<VolumeDim>& orientation_map) const
      noexcept;

  using limit_tags = tmpl::list<Tags...>;
  using limit_argument_tags =
      tmpl::list<domain::Tags::Mesh<VolumeDim>,
                 domain::Tags::Element<VolumeDim>,
                 domain::Tags::SizeOfElement<VolumeDim>>;

  /// \brief Limit the solution on the element
  bool operator()(
      const gsl::not_null<std::add_pointer_t<typename Tags::type>>... tensors,
      const Mesh<VolumeDim>& mesh, const Element<VolumeDim>& element,
      const std::array<double, VolumeDim>& element_size,
      const std::unordered_map<
          std::pair<Direction<VolumeDim>, ElementId<VolumeDim>>, PackagedData,
          boost::hash<std::pair<Direction<VolumeDim>, ElementId<VolumeDim>>>>&
          neighbor_data) const noexcept;

 private:
  template <size_t LocalDim, typename LocalTagList>
  // NOLINTNEXTLINE(readability-redundant-declaration) false positive
  friend bool operator==(const Weno<LocalDim, LocalTagList>& lhs,
                         const Weno<LocalDim, LocalTagList>& rhs) noexcept;

  WenoType weno_type_;
  double neighbor_linear_weight_;
  double tvb_constant_;
  bool disable_for_debugging_;
};

template <size_t VolumeDim, typename... Tags>
Weno<VolumeDim, tmpl::list<Tags...>>::Weno(
    const WenoType weno_type, const double neighbor_linear_weight,
    const double tvb_constant, const bool disable_for_debugging) noexcept
    : weno_type_(weno_type),
      neighbor_linear_weight_(neighbor_linear_weight),
      tvb_constant_(tvb_constant),
      disable_for_debugging_(disable_for_debugging) {}

template <size_t VolumeDim, typename... Tags>
// NOLINTNEXTLINE(google-runtime-references)
void Weno<VolumeDim, tmpl::list<Tags...>>::pup(PUP::er& p) noexcept {
  p | weno_type_;
  p | neighbor_linear_weight_;
  p | tvb_constant_;
  p | disable_for_debugging_;
}

template <size_t VolumeDim, typename... Tags>
void Weno<VolumeDim, tmpl::list<Tags...>>::package_data(
    const gsl::not_null<PackagedData*> packaged_data,
    const typename Tags::type&... tensors, const Mesh<VolumeDim>& mesh,
    const std::array<double, VolumeDim>& element_size,
    const OrientationMap<VolumeDim>& orientation_map) const noexcept {
  // By always initializing the PackagedData Variables member, we avoid an
  // assertion that arises from having a default-constructed Variables in a
  // disabled limiter. There is a performance cost, because the package_data()
  // function does non-zero work even for a disabled limiter... but since the
  // limiter should never be disabled in a production simulation, this cost
  // should never matter.
  (packaged_data->volume_data).initialize(mesh.number_of_grid_points());

  if (UNLIKELY(disable_for_debugging_)) {
    // Do not initialize packaged_data
    // (except for the Variables member "volume_data", see above)
    return;
  }

  const auto wrap_compute_means = [&mesh, &packaged_data](
                                      auto tag, const auto tensor) noexcept {
    for (size_t i = 0; i < tensor.size(); ++i) {
      // Compute the mean using the local orientation of the tensor and mesh.
      get<::Tags::Mean<decltype(tag)>>(packaged_data->means)[i] =
          mean_value(tensor[i], mesh);
    }
    return '0';
  };
  expand_pack(wrap_compute_means(Tags{}, tensors)...);

  packaged_data->element_size =
      orientation_map.permute_from_neighbor(element_size);

  const auto wrap_copy_tensor = [&packaged_data](auto tag,
                                                 const auto tensor) noexcept {
    get<decltype(tag)>(packaged_data->volume_data) = tensor;
    return '0';
  };
  expand_pack(wrap_copy_tensor(Tags{}, tensors)...);
  packaged_data->volume_data = orient_variables(
      packaged_data->volume_data, mesh.extents(), orientation_map);

  packaged_data->mesh = orientation_map(mesh);
}

template <size_t VolumeDim, typename... Tags>
bool Weno<VolumeDim, tmpl::list<Tags...>>::operator()(
    const gsl::not_null<std::add_pointer_t<typename Tags::type>>... tensors,
    const Mesh<VolumeDim>& mesh, const Element<VolumeDim>& element,
    const std::array<double, VolumeDim>& element_size,
    const std::unordered_map<
        std::pair<Direction<VolumeDim>, ElementId<VolumeDim>>, PackagedData,
        boost::hash<std::pair<Direction<VolumeDim>, ElementId<VolumeDim>>>>&
        neighbor_data) const noexcept {
  if (UNLIKELY(disable_for_debugging_)) {
    // Do not modify input tensors
    return false;
  }

  // Check that basis is LGL or LG
  // A Legendre basis is assumed for the oscillation indicator (used in both
  // SimpleWeno and Hweno) and in the Hweno reconstruction.
  ASSERT(mesh.basis() == make_array<VolumeDim>(Spectral::Basis::Legendre),
         "Unsupported basis: " << mesh);
  ASSERT(mesh.quadrature() ==
                 make_array<VolumeDim>(Spectral::Quadrature::GaussLobatto) or
             mesh.quadrature() ==
                 make_array<VolumeDim>(Spectral::Quadrature::Gauss),
         "Unsupported quadrature: " << mesh);

  // Enforce restrictions on h-refinement, p-refinement
  if (UNLIKELY(alg::any_of(element.neighbors(),
                           [](const auto& direction_neighbors) noexcept {
                             return direction_neighbors.second.size() != 1;
                           }))) {
    ERROR("The Weno limiter does not yet support h-refinement");
    // Removing this limitation will require:
    // - Generalizing the computation of the modified neighbor solutions.
    // - Generalizing the WENO weighted sum for multiple neighbors in each
    //   direction.
  }
  alg::for_each(neighbor_data, [&mesh](const auto& neighbor_and_data) noexcept {
    if (UNLIKELY(neighbor_and_data.second.mesh != mesh)) {
      ERROR("The Weno limiter does not yet support p-refinement");
      // Removing this limitation will require generalizing the
      // computation of the modified neighbor solutions.
    }
  });

  if (weno_type_ == WenoType::Hweno) {
    // Troubled-cell detection for HWENO flags the element for limiting if any
    // component of any tensor needs limiting.
    const bool cell_is_troubled =
        Tci::tvb_minmod_indicator<VolumeDim, PackagedData, Tags...>(
            tvb_constant_, (*tensors)..., mesh, element, element_size,
            neighbor_data);
    if (not cell_is_troubled) {
      // No limiting is needed
      return false;
    }

    std::unordered_map<
        std::pair<Direction<VolumeDim>, ElementId<VolumeDim>>, DataVector,
        boost::hash<std::pair<Direction<VolumeDim>, ElementId<VolumeDim>>>>
        modified_neighbor_solution_buffer{};
    for (const auto& neighbor_and_data : neighbor_data) {
      const auto& neighbor = neighbor_and_data.first;
      modified_neighbor_solution_buffer.insert(
          make_pair(neighbor, DataVector(mesh.number_of_grid_points())));
    }

    EXPAND_PACK_LEFT_TO_RIGHT(Weno_detail::hweno_impl<Tags>(
        make_not_null(&modified_neighbor_solution_buffer), tensors,
        neighbor_linear_weight_, mesh, element, neighbor_data));
    return true;  // cell_is_troubled

  } else if (weno_type_ == WenoType::SimpleWeno) {
    // Buffers and pre-computations for TCI
    Minmod_detail::BufferWrapper<VolumeDim> tci_buffer(mesh);
    const auto effective_neighbor_sizes =
        Minmod_detail::compute_effective_neighbor_sizes(element, neighbor_data);

    // Buffers for simple WENO implementation
    std::unordered_map<
        std::pair<Direction<VolumeDim>, ElementId<VolumeDim>>,
        intrp::RegularGrid<VolumeDim>,
        boost::hash<std::pair<Direction<VolumeDim>, ElementId<VolumeDim>>>>
        interpolator_buffer{};
    std::unordered_map<
        std::pair<Direction<VolumeDim>, ElementId<VolumeDim>>, DataVector,
        boost::hash<std::pair<Direction<VolumeDim>, ElementId<VolumeDim>>>>
        modified_neighbor_solution_buffer{};

    bool some_component_was_limited = false;

    const auto wrap_minmod_tci_and_simple_weno_impl =
        [this, &some_component_was_limited, &tci_buffer, &interpolator_buffer,
         &modified_neighbor_solution_buffer, &mesh, &element, &element_size,
         &neighbor_data,
         &effective_neighbor_sizes](auto tag, const auto tensor) noexcept {
          for (size_t tensor_storage_index = 0;
               tensor_storage_index < tensor->size(); ++tensor_storage_index) {
            // Check TCI
            const auto effective_neighbor_means =
                Minmod_detail::compute_effective_neighbor_means<decltype(tag)>(
                    tensor_storage_index, element, neighbor_data);
            const bool component_needs_limiting = Tci::tvb_minmod_indicator(
                make_not_null(&tci_buffer), tvb_constant_,
                (*tensor)[tensor_storage_index], mesh, element, element_size,
                effective_neighbor_means, effective_neighbor_sizes);

            if (component_needs_limiting) {
              if (modified_neighbor_solution_buffer.empty()) {
                // Allocate the neighbor solution buffers only if the limiter is
                // triggered. This reduces allocation when no limiting occurs.
                for (const auto& neighbor_and_data : neighbor_data) {
                  const auto& neighbor = neighbor_and_data.first;
                  modified_neighbor_solution_buffer.insert(make_pair(
                      neighbor, DataVector(mesh.number_of_grid_points())));
                }
              }
              Weno_detail::simple_weno_impl<decltype(tag)>(
                  make_not_null(&interpolator_buffer),
                  make_not_null(&modified_neighbor_solution_buffer), tensor,
                  neighbor_linear_weight_, tensor_storage_index, mesh, element,
                  neighbor_data);
              some_component_was_limited = true;
            }
          }
          return '0';
        };
    expand_pack(wrap_minmod_tci_and_simple_weno_impl(Tags{}, tensors)...);
    return some_component_was_limited;  // cell_is_troubled
  } else {
    ERROR("WENO limiter not implemented for WenoType: " << weno_type_);
  }

  return false;  // cell_is_troubled
}

template <size_t LocalDim, typename LocalTagList>
bool operator==(const Weno<LocalDim, LocalTagList>& lhs,
                const Weno<LocalDim, LocalTagList>& rhs) noexcept {
  return lhs.weno_type_ == rhs.weno_type_ and
         lhs.neighbor_linear_weight_ == rhs.neighbor_linear_weight_ and
         lhs.tvb_constant_ == rhs.tvb_constant_ and
         lhs.disable_for_debugging_ == rhs.disable_for_debugging_;
}

template <size_t VolumeDim, typename TagList>
bool operator!=(const Weno<VolumeDim, TagList>& lhs,
                const Weno<VolumeDim, TagList>& rhs) noexcept {
  return not(lhs == rhs);
}

}  // namespace Limiters


#include <iostream>
#include <string.h>

#include <stb_image.h>
#include <stb_image_write.h>
#include <Eigen/Dense>

#include "Ray.h"

using namespace Eigen;

Vector3d ray_color(const Ray& ray)
{
    Vector3d unitDirection = ray.direction().normalized();
    double t = 0.5 * (unitDirection.y() + 1.0);
    return (1.0-t)*Vector3d(1.0, 1.0, 1.0) + t * Vector3d(0.5, 0.7, 1.0);
}

int main()
{
    // Image parameter.
    const double imageAspectRatio = 16.0/9.0;
    const int imageWidth = 400;
    const int imageHeight = static_cast<int>(imageWidth/imageAspectRatio);
    const int CHANNEL_NUM = 3;
    std::string imagePath;

    // Image path and data buffer.
    std::cout << "Please input the output image path." << std::endl;
    std::cin >> imagePath;
    std::cout << "output path is: " << imagePath << std::endl;
    const char* outputPath = imagePath.c_str();
    char* outputData = new char[imageWidth * imageHeight * CHANNEL_NUM];

    // Camera parameter.
    double viewportHeight = 2.0;
    double viewportWidth = imageAspectRatio * viewportHeight;
    double focalLength = 1.0;

    Vector3d origin(0, 0, 0);
    Vector3d horizontal(viewportWidth, 0, 0);
    Vector3d vertical(0, viewportHeight, 0);
    Vector3d lowerLeftCorner = origin - horizontal/2 -  vertical/2 - Vector3d(0, 0, focalLength);

    int index = 0;
    for(int j = imageHeight - 1; j >= 0; j--)
    {
        for(int i=0; i<imageWidth; i++)
        {
            double r = double(i) / (imageWidth - 1);
            double g = double(j) / (imageHeight - 1);
            double b = 0.25;

            outputData[index++] = static_cast<int>(255.999*r);
            outputData[index++] = static_cast<int>(255.999*g);
            outputData[index++] = static_cast<int>(255.999*b);
        }
    }

    Vector3f v;

    stbi_write_png(outputPath, imageWidth, imageHeight, 3, static_cast<void*>(outputData), CHANNEL_NUM*imageWidth);

    return 0;
}