///////////////////////////////////////////////////////////////////
//  Copyright Christopher Kormanyos 2019 - 2022.                 //
//  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 UINTWIDE_T_BACKEND_2019_12_15_HPP
  #define UINTWIDE_T_BACKEND_2019_12_15_HPP

  #include <cstdint>
  #include <limits>
  #include <string>
  #include <type_traits>

  #if defined(__GNUC__)
  #pragma GCC diagnostic push
  #pragma GCC diagnostic ignored "-Wconversion"
  #pragma GCC diagnostic push
  #pragma GCC diagnostic ignored "-Wsign-conversion"
  #pragma GCC diagnostic push
  #pragma GCC diagnostic ignored "-Wunused-parameter"
  #endif

  #if (defined(__clang__) && (__clang_major__ > 9)) && !defined(__APPLE__)
  #pragma GCC diagnostic push
  #pragma GCC diagnostic ignored "-Wdeprecated-copy"
  #endif

  #include <boost/config.hpp>
  #include <boost/multiprecision/number.hpp>
  #include <boost/version.hpp>

  #include <math/wide_integer/uintwide_t.h>

  #if defined(WIDE_INTEGER_NAMESPACE)
  #if (~(~WIDE_INTEGER_NAMESPACE + 0) == 0 && ~(~WIDE_INTEGER_NAMESPACE + 1) == 1)
  #else
  using namespace WIDE_INTEGER_NAMESPACE;
  #endif
  #endif

  namespace boost { namespace multiprecision {

  // Forward declaration of the uintwide_t_backend multiple precision
  // class. This class binds native ::math::wide_integer::uintwide_t
  // to boost::multiprecsion::uintwide_t_backend.

  template<const ::math::wide_integer::size_t MyWidth2,
           typename MyLimbType>
  class uintwide_t_backend;

  // Define the number category as an integer number kind
  // for the uintwide_t_backend. This is needed for properly
  // interacting as a backend with boost::muliprecision.
  #if (defined(BOOST_VERSION) && (BOOST_VERSION <= 107200))
  template<const ::math::wide_integer::size_t MyWidth2,
           typename MyLimbType>
  struct number_category<uintwide_t_backend<MyWidth2, MyLimbType>>
    : public boost::mpl::int_<number_kind_integer> { };
  #elif (defined(BOOST_VERSION) && (BOOST_VERSION <= 107500))
  template<const ::math::wide_integer::size_t MyWidth2,
           typename MyLimbType>
  struct number_category<uintwide_t_backend<MyWidth2, MyLimbType>>
    : public boost::integral_constant<int, number_kind_integer> { };
  #else
  template<const ::math::wide_integer::size_t MyWidth2,
           typename MyLimbType>
  struct number_category<uintwide_t_backend<MyWidth2, MyLimbType>>
    : public std::integral_constant<int, number_kind_integer> { };
  #endif

  // This is the uintwide_t_backend multiple precision class.
  template<const ::math::wide_integer::size_t MyWidth2,
           typename MyLimbType = std::uint32_t>
  class uintwide_t_backend // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions)
  {
  public:
    using representation_type = ::math::wide_integer::uintwide_t<MyWidth2, MyLimbType>;

    #if (defined(BOOST_VERSION) && (BOOST_VERSION <= 107500))
    using signed_types   = mpl::list<std::int64_t>;
    using unsigned_types = mpl::list<std::uint64_t>;
    using float_types    = mpl::list<long double>;
    #else
    using   signed_types = std::tuple<  signed char,   signed short,   signed int,   signed long,   signed long long, std::intmax_t>;  // NOLINT(google-runtime-int)
    using unsigned_types = std::tuple<unsigned char, unsigned short, unsigned int, unsigned long, unsigned long long, std::uintmax_t>; // NOLINT(google-runtime-int)
    using float_types    = std::tuple<float, double, long double>;
    #endif

    constexpr uintwide_t_backend() : m_value() { }

    explicit constexpr uintwide_t_backend(const representation_type& rep) : m_value(rep) { }

    constexpr uintwide_t_backend(const uintwide_t_backend& other) : m_value(other.m_value) { }

    template<typename UnsignedIntegralType,
             typename std::enable_if<(   (std::is_integral<UnsignedIntegralType>::value == true)
                                      && (std::is_unsigned<UnsignedIntegralType>::value == true))>::type const* = nullptr>
    constexpr uintwide_t_backend(UnsignedIntegralType u) : m_value(representation_type(std::uint64_t(u))) { } // NOLINT(google-explicit-constructor,hicpp-explicit-conversions)

    template<typename SignedIntegralType,
             typename std::enable_if<(   (std::is_integral<SignedIntegralType>::value == true)
                                      && (std::is_signed  <SignedIntegralType>::value == true))>::type const* = nullptr>
    constexpr uintwide_t_backend(SignedIntegralType n) : m_value(representation_type(std::int64_t(n))) { } // NOLINT(google-explicit-constructor,hicpp-explicit-conversions)

    template<typename FloatingPointType,
             typename std::enable_if<std::is_floating_point<FloatingPointType>::value == true>::type const* = nullptr>
    constexpr uintwide_t_backend(FloatingPointType f) : m_value(representation_type(static_cast<long double>(f))) { } // NOLINT(google-explicit-constructor,hicpp-explicit-conversions)

    constexpr uintwide_t_backend(const char* c) : m_value(c) { } // NOLINT(google-explicit-constructor,hicpp-explicit-conversions)

    constexpr uintwide_t_backend(const std::string& str) : m_value(str) { } // NOLINT(google-explicit-constructor,hicpp-explicit-conversions)

    //~uintwide_t_backend() { }

    WIDE_INTEGER_CONSTEXPR auto operator=(const uintwide_t_backend& other) -> uintwide_t_backend&
    {
      if(this != &other)
      {
        m_value.representation() = other.m_value.crepresentation();
      }

      return *this;
    }

    template<typename ArithmeticType,
             typename std::enable_if<std::is_arithmetic<ArithmeticType>::value == true>::type const* = nullptr>
    WIDE_INTEGER_CONSTEXPR auto operator=(const ArithmeticType& x) -> uintwide_t_backend&
    {
      m_value = representation_type(x);

      return *this;
    }

    WIDE_INTEGER_CONSTEXPR auto operator=(const std::string& str_rep)  -> uintwide_t_backend& { m_value = representation_type(str_rep);  return *this; }
    WIDE_INTEGER_CONSTEXPR auto operator=(const char*        char_ptr) -> uintwide_t_backend& { m_value = representation_type(char_ptr); return *this; }

    WIDE_INTEGER_CONSTEXPR void swap(uintwide_t_backend& other_mp_cpp_backend)
    {
      m_value.representation().swap(other_mp_cpp_backend.m_value.representation());
    }

    WIDE_INTEGER_CONSTEXPR auto  representation()       ->       representation_type& { return m_value; }
    WIDE_INTEGER_CONSTEXPR auto  representation() const -> const representation_type& { return m_value; }
    WIDE_INTEGER_CONSTEXPR auto crepresentation() const -> const representation_type& { return m_value; }

    auto str(std::streamsize number_of_digits, const std::ios::fmtflags format_flags) const -> std::string
    {
      (void) number_of_digits;

      std::array<char, representation_type::wr_string_max_buffer_size_dec> pstr { };

      const std::uint_fast8_t base_rep     = (((format_flags & std::ios::hex)       != 0) ? 16U : 10U);
      const bool              show_base    = ( (format_flags & std::ios::showbase)  != 0);
      const bool              show_pos     = ( (format_flags & std::ios::showpos)   != 0);
      const bool              is_uppercase = ( (format_flags & std::ios::uppercase) != 0);

      const bool wr_string_is_ok = m_value.wr_string(pstr.data(), base_rep, show_base, show_pos, is_uppercase);

      std::string str_result = (wr_string_is_ok ? std::string(pstr.data()) : std::string());

      return str_result;
    }

    WIDE_INTEGER_CONSTEXPR void negate()
    {
      m_value.negate();
    }

    constexpr auto compare(const uintwide_t_backend& other_mp_cpp_backend) const -> int
    {
      return static_cast<int>(m_value.compare(other_mp_cpp_backend.crepresentation()));
    }

    template<typename ArithmeticType,
             typename std::enable_if<std::is_arithmetic<ArithmeticType>::value == true>::type const* = nullptr>
    constexpr auto compare(ArithmeticType x) const -> int
    {
      return static_cast<int>(m_value.compare(representation_type(x)));
    }

    auto operator=(const representation_type&) -> uintwide_t_backend& = delete;

  private:
    representation_type m_value;
  };

  template<const ::math::wide_integer::size_t MyWidth2,
           typename MyLimbType>
  WIDE_INTEGER_CONSTEXPR void eval_add(uintwide_t_backend<MyWidth2, MyLimbType>& result, const uintwide_t_backend<MyWidth2, MyLimbType>& x)
  {
    result.representation() += x.crepresentation();
  }

  template<const ::math::wide_integer::size_t MyWidth2,
           typename MyLimbType>
  WIDE_INTEGER_CONSTEXPR void eval_subtract(uintwide_t_backend<MyWidth2, MyLimbType>& result, const uintwide_t_backend<MyWidth2, MyLimbType>& x)
  {
    result.representation() -= x.crepresentation();
  }

  template<const ::math::wide_integer::size_t MyWidth2,
           typename MyLimbType>
  WIDE_INTEGER_CONSTEXPR void eval_multiply(uintwide_t_backend<MyWidth2, MyLimbType>& result, const uintwide_t_backend<MyWidth2, MyLimbType>& x)
  {
    result.representation() *= x.crepresentation();
  }

  template<const ::math::wide_integer::size_t MyWidth2,
           typename MyLimbType,
           typename IntegralType,
           typename std::enable_if<(std::is_integral<IntegralType>::value == true)>::type const* = nullptr>
  WIDE_INTEGER_CONSTEXPR void eval_multiply(uintwide_t_backend<MyWidth2, MyLimbType>& result, const IntegralType& n)
  {
    result.representation() *= n;
  }

  template<const ::math::wide_integer::size_t MyWidth2,
           typename MyLimbType>
  WIDE_INTEGER_CONSTEXPR void eval_divide(uintwide_t_backend<MyWidth2, MyLimbType>& result, const uintwide_t_backend<MyWidth2, MyLimbType>& x)
  {
    result.representation() /= x.crepresentation();
  }

  template<const ::math::wide_integer::size_t MyWidth2,
           typename MyLimbType,
           typename IntegralType,
           typename std::enable_if<(   (std::is_fundamental<IntegralType>::value == true)
                                    && (std::is_integral   <IntegralType>::value == true)
                                    && (std::is_unsigned   <IntegralType>::value == true)
                                    && (std::numeric_limits<IntegralType>::digits <= std::numeric_limits<MyLimbType>::digits))>::type const* = nullptr>
  WIDE_INTEGER_CONSTEXPR void eval_divide(uintwide_t_backend<MyWidth2, MyLimbType>& result, const IntegralType& n)
  {
    using local_wide_integer_type = typename uintwide_t_backend<MyWidth2, MyLimbType>::representation_type;

    using local_limb_type = typename local_wide_integer_type::limb_type;

    result.representation().eval_divide_by_single_limb(static_cast<local_limb_type>(n), 0U, nullptr);
  }

  template<const ::math::wide_integer::size_t MyWidth2,
           typename MyLimbType,
           typename IntegralType,
           typename std::enable_if<(   (std::is_fundamental<IntegralType>::value == true)
                                    && (std::is_integral   <IntegralType>::value == true)
                                    && (std::is_unsigned   <IntegralType>::value == true)
                                    && (std::numeric_limits<IntegralType>::digits) > std::numeric_limits<MyLimbType>::digits)>::type const* = nullptr>
  WIDE_INTEGER_CONSTEXPR void eval_divide(uintwide_t_backend<MyWidth2, MyLimbType>& result, const IntegralType& n)
  {
    result.representation() /= n;
  }

  template<const ::math::wide_integer::size_t MyWidth2,
           typename MyLimbType>
  WIDE_INTEGER_CONSTEXPR void eval_modulus(uintwide_t_backend<MyWidth2, MyLimbType>& result, const uintwide_t_backend<MyWidth2, MyLimbType>& x)
  {
    result.representation() %= x.crepresentation();
  }

  template<const ::math::wide_integer::size_t MyWidth2,
           typename MyLimbType,
           typename IntegralType,
           typename std::enable_if<(   (std::is_fundamental<IntegralType>::value == true)
                                    && (std::is_integral   <IntegralType>::value == true)
                                    && (std::is_unsigned   <IntegralType>::value == true)
                                    && (std::numeric_limits<IntegralType>::digits <= std::numeric_limits<MyLimbType>::digits))>::type const* = nullptr>
  WIDE_INTEGER_CONSTEXPR auto eval_integer_modulus(uintwide_t_backend<MyWidth2, MyLimbType>& x, const IntegralType& n) -> IntegralType
  {
    using local_wide_integer_type = typename uintwide_t_backend<MyWidth2, MyLimbType>::representation_type;

    typename uintwide_t_backend<MyWidth2, MyLimbType>::representation_type rem;

    local_wide_integer_type(x.crepresentation()).eval_divide_by_single_limb(n, 0U, &rem);

    return static_cast<IntegralType>(rem);
  }

  template<const ::math::wide_integer::size_t MyWidth2,
           typename MyLimbType,
           typename IntegralType,
           typename std::enable_if<(   (std::is_fundamental<IntegralType>::value == true)
                                    && (std::is_integral   <IntegralType>::value == true)
                                    && (std::is_unsigned   <IntegralType>::value == true)
                                    && (std::numeric_limits<IntegralType>::digits) > std::numeric_limits<MyLimbType>::digits)>::type const* = nullptr>
  WIDE_INTEGER_CONSTEXPR auto eval_integer_modulus(uintwide_t_backend<MyWidth2, MyLimbType>& x, const IntegralType& n) -> IntegralType
  {
    const uintwide_t_backend<MyWidth2, MyLimbType> rem = x.crepresentation() % uintwide_t_backend<MyWidth2, MyLimbType>(n);

    return static_cast<IntegralType>(rem);
  }

  template<const ::math::wide_integer::size_t MyWidth2,
           typename MyLimbType>
  WIDE_INTEGER_CONSTEXPR void eval_bitwise_and(uintwide_t_backend<MyWidth2, MyLimbType>& result, const uintwide_t_backend<MyWidth2, MyLimbType>& x)
  {
    result.representation() &= x.crepresentation();
  }

  template<const ::math::wide_integer::size_t MyWidth2,
           typename MyLimbType>
  WIDE_INTEGER_CONSTEXPR void eval_bitwise_or(uintwide_t_backend<MyWidth2, MyLimbType>& result, const uintwide_t_backend<MyWidth2, MyLimbType>& x)
  {
    result.representation() |= x.crepresentation();
  }

  template<const ::math::wide_integer::size_t MyWidth2,
           typename MyLimbType>
  WIDE_INTEGER_CONSTEXPR void eval_bitwise_xor(uintwide_t_backend<MyWidth2, MyLimbType>& result, const uintwide_t_backend<MyWidth2, MyLimbType>& x)
  {
    result.representation() ^= x.crepresentation();
  }

  template<const ::math::wide_integer::size_t MyWidth2,
           typename MyLimbType>
  WIDE_INTEGER_CONSTEXPR void eval_complement(uintwide_t_backend<MyWidth2, MyLimbType>& result, const uintwide_t_backend<MyWidth2, MyLimbType>& x)
  {
    for(auto i = 0U; i < std::tuple_size<typename uintwide_t_backend<MyWidth2, MyLimbType>::representation_type>::value; ++i)
    {
      using local_limb_type = typename uintwide_t_backend<MyWidth2, MyLimbType>::limb_type;

      result.representation().representation()[i] = static_cast<local_limb_type>(~x.crepresentation().crepresentation()[i]);
    }
  }

  template<const ::math::wide_integer::size_t MyWidth2,
           typename MyLimbType>
  WIDE_INTEGER_CONSTEXPR void eval_powm(      uintwide_t_backend<MyWidth2, MyLimbType>& result,
                                        const uintwide_t_backend<MyWidth2, MyLimbType>& b,
                                        const uintwide_t_backend<MyWidth2, MyLimbType>& p,
                                        const uintwide_t_backend<MyWidth2, MyLimbType>& m)
  {
    result.representation() = powm(b.crepresentation(),
                                   p.crepresentation(),
                                   m.crepresentation());
  }

  template<const ::math::wide_integer::size_t MyWidth2,
           typename MyLimbType,
           typename OtherIntegralTypeM,
           typename std::enable_if<(   (std::is_integral   <OtherIntegralTypeM>::value == true)
                                    && (std::is_fundamental<OtherIntegralTypeM>::value == true))>::type const* = nullptr>
  WIDE_INTEGER_CONSTEXPR void eval_powm(      uintwide_t_backend<MyWidth2, MyLimbType>& result,
                                        const uintwide_t_backend<MyWidth2, MyLimbType>& b,
                                        const uintwide_t_backend<MyWidth2, MyLimbType>& p,
                                        const OtherIntegralTypeM                         m)
  {
    result.representation() = powm(b.crepresentation(),
                                   p.crepresentation(),
                                   m);
  }

  template<const ::math::wide_integer::size_t MyWidth2,
           typename MyLimbType,
           typename OtherIntegralTypeP,
           typename std::enable_if<(   (std::is_integral   <OtherIntegralTypeP>::value == true)
                                    && (std::is_fundamental<OtherIntegralTypeP>::value == true))>::type const* = nullptr>
  WIDE_INTEGER_CONSTEXPR void eval_powm(      uintwide_t_backend<MyWidth2, MyLimbType>& result,
                                        const uintwide_t_backend<MyWidth2, MyLimbType>& b,
                                        const OtherIntegralTypeP                         p,
                                        const uintwide_t_backend<MyWidth2, MyLimbType>& m)
  {
    result.representation() = powm(b.crepresentation(),
                                   p,
                                   m.crepresentation());
  }

  template<const ::math::wide_integer::size_t MyWidth2,
           typename MyLimbType,
           typename IntegralType,
           typename std::enable_if<(   (std::is_integral   <IntegralType>::value == true)
                                    && (std::is_fundamental<IntegralType>::value == true))>::type const* = nullptr>
  WIDE_INTEGER_CONSTEXPR void eval_left_shift(uintwide_t_backend<MyWidth2, MyLimbType>& result, const IntegralType& n)
  {
    result.representation() <<= n;
  }

  template<const ::math::wide_integer::size_t MyWidth2,
           typename MyLimbType,
           typename IntegralType,
           typename std::enable_if<(   (std::is_integral   <IntegralType>::value == true)
                                    && (std::is_fundamental<IntegralType>::value == true))>::type const* = nullptr>
  WIDE_INTEGER_CONSTEXPR void eval_right_shift(uintwide_t_backend<MyWidth2, MyLimbType>& result, const IntegralType& n)
  {
    result.representation() >>= n;
  }

  template<const ::math::wide_integer::size_t MyWidth2,
           typename MyLimbType>
  WIDE_INTEGER_CONSTEXPR auto eval_lsb(const uintwide_t_backend<MyWidth2, MyLimbType>& a) -> unsigned
  {
    return static_cast<unsigned>(lsb(a.crepresentation()));
  }

  template<const ::math::wide_integer::size_t MyWidth2,
           typename MyLimbType>
  WIDE_INTEGER_CONSTEXPR auto eval_msb(const uintwide_t_backend<MyWidth2, MyLimbType>& a) -> unsigned
  {
    return static_cast<unsigned>(msb(a.crepresentation()));
  }

  template<const ::math::wide_integer::size_t MyWidth2,
           typename MyLimbType>
  WIDE_INTEGER_CONSTEXPR auto eval_eq(const uintwide_t_backend<MyWidth2, MyLimbType>& a, const uintwide_t_backend<MyWidth2, MyLimbType>& b) -> bool
  {
    return (a.compare(b) == 0);
  }

  template<const ::math::wide_integer::size_t MyWidth2,
           typename MyLimbType,
           typename ArithmeticType,
           typename std::enable_if<(   (std::is_arithmetic <ArithmeticType>::value == true)
                                    && (std::is_fundamental<ArithmeticType>::value == true))>::type const* = nullptr>
  WIDE_INTEGER_CONSTEXPR auto eval_eq(const uintwide_t_backend<MyWidth2, MyLimbType>& a, const ArithmeticType& b) -> bool
  {
    return (a.compare(b) == 0);
  }

  template<const ::math::wide_integer::size_t MyWidth2,
           typename MyLimbType,
           typename ArithmeticType,
           typename std::enable_if<(   (std::is_arithmetic <ArithmeticType>::value == true)
                                    && (std::is_fundamental<ArithmeticType>::value == true))>::type const* = nullptr>
  WIDE_INTEGER_CONSTEXPR auto eval_eq(const ArithmeticType& a, const uintwide_t_backend<MyWidth2, MyLimbType>& b) -> bool
  {
    return (uintwide_t_backend<MyWidth2, MyLimbType>(a).compare(b) == 0);
  }

  template<const ::math::wide_integer::size_t MyWidth2,
           typename MyLimbType>
  WIDE_INTEGER_CONSTEXPR auto eval_gt(const uintwide_t_backend<MyWidth2, MyLimbType>& a, const uintwide_t_backend<MyWidth2, MyLimbType>& b) -> bool
  {
    return (a.compare(b) == 1);
  }

  template<const ::math::wide_integer::size_t MyWidth2,
           typename MyLimbType,
           typename ArithmeticType,
           typename std::enable_if<(   (std::is_arithmetic <ArithmeticType>::value == true)
                                    && (std::is_fundamental<ArithmeticType>::value == true))>::type const* = nullptr>
  WIDE_INTEGER_CONSTEXPR auto eval_gt(const uintwide_t_backend<MyWidth2, MyLimbType>& a, const ArithmeticType& b) -> bool
  {
    return (a.compare(b) == 1);
  }

  template<const ::math::wide_integer::size_t MyWidth2,
           typename MyLimbType,
           typename ArithmeticType,
           typename std::enable_if<(   (std::is_arithmetic <ArithmeticType>::value == true)
                                    && (std::is_fundamental<ArithmeticType>::value == true))>::type const* = nullptr>
  WIDE_INTEGER_CONSTEXPR auto eval_gt(const ArithmeticType& a, const uintwide_t_backend<MyWidth2, MyLimbType>& b) -> bool
  {
    return (uintwide_t_backend<MyWidth2, MyLimbType>(a).compare(b) == 1);
  }

  template<const ::math::wide_integer::size_t MyWidth2,
           typename MyLimbType>
  WIDE_INTEGER_CONSTEXPR auto eval_lt(const uintwide_t_backend<MyWidth2, MyLimbType>& a, const uintwide_t_backend<MyWidth2, MyLimbType>& b) -> bool
  {
    return (a.compare(b) == -1);
  }

  template<const ::math::wide_integer::size_t MyWidth2,
           typename MyLimbType,
           typename ArithmeticType,
           typename std::enable_if<(   (std::is_arithmetic <ArithmeticType>::value == true)
                                    && (std::is_fundamental<ArithmeticType>::value == true))>::type const* = nullptr>
  WIDE_INTEGER_CONSTEXPR auto eval_lt(const uintwide_t_backend<MyWidth2, MyLimbType>& a, const ArithmeticType& b) -> bool
  {
    return (a.compare(b) == -1);
  }

  template<const ::math::wide_integer::size_t MyWidth2,
           typename MyLimbType,
           typename ArithmeticType,
           typename std::enable_if<(   (std::is_arithmetic <ArithmeticType>::value == true)
                                    && (std::is_fundamental<ArithmeticType>::value == true))>::type const* = nullptr>
  WIDE_INTEGER_CONSTEXPR auto eval_lt(const ArithmeticType& a, const uintwide_t_backend<MyWidth2, MyLimbType>& b) -> bool
  {
    return (uintwide_t_backend<MyWidth2, MyLimbType>(a).compare(b) == -1);
  }

  template<const ::math::wide_integer::size_t MyWidth2,
           typename MyLimbType>
  WIDE_INTEGER_CONSTEXPR auto eval_is_zero(const uintwide_t_backend<MyWidth2, MyLimbType>& x) -> bool
  {
    return (x.crepresentation() == 0U);
  }

  template<const ::math::wide_integer::size_t MyWidth2,
           typename MyLimbType>
  WIDE_INTEGER_CONSTEXPR auto eval_get_sign(const uintwide_t_backend<MyWidth2, MyLimbType>& x) -> int
  {
    int n_result { };

    if  (x.crepresentation() == 0U) { n_result = 0; }
    else                            { n_result = 1; }

    return n_result;
  }

  template<const ::math::wide_integer::size_t MyWidth2,
           typename MyLimbType>
  WIDE_INTEGER_CONSTEXPR void eval_convert_to(unsigned long long* result, // NOLINT(google-runtime-int)
                                              const uintwide_t_backend<MyWidth2, MyLimbType>& val)
  {
    *result = static_cast<unsigned long long>(val.crepresentation()); // NOLINT(google-runtime-int)
  }

  template<const ::math::wide_integer::size_t MyWidth2,
           typename MyLimbType>
  WIDE_INTEGER_CONSTEXPR void eval_convert_to(signed long long* result, // NOLINT(google-runtime-int)
                                              const uintwide_t_backend<MyWidth2, MyLimbType>& val)
  {
    *result = static_cast<signed long long>(val.crepresentation()); // NOLINT(google-runtime-int)
  }

  template<const ::math::wide_integer::size_t MyWidth2,
           typename MyLimbType>
  WIDE_INTEGER_CONSTEXPR void eval_convert_to(long double* result,
                                              const uintwide_t_backend<MyWidth2, MyLimbType>& val)
  {
    *result = static_cast<long double>(val.crepresentation());
  }

  } // namespace multiprecision
  } // namespace boost

  namespace boost { namespace math { namespace policies {

  // Specialization of the precision structure.
  template<const ::math::wide_integer::size_t MyWidth2,
           typename MyLimbType,
           typename ThisPolicy,
           const boost::multiprecision::expression_template_option ExpressionTemplatesOptions>
  struct precision<boost::multiprecision::number<boost::multiprecision::uintwide_t_backend<MyWidth2, MyLimbType>,
                                                 ExpressionTemplatesOptions>,
                   ThisPolicy>
  {
    using precision_type = typename ThisPolicy::precision_type;

    using local_digits_2 = digits2<MyWidth2>;

    #if (BOOST_VERSION <= 107500)
    using type = typename mpl::if_c       <((local_digits_2::value <= precision_type::value) || (precision_type::value <= 0)),
                                             local_digits_2,
                                             precision_type>::type;
    #else
    using type = typename std::conditional<((local_digits_2::value <= precision_type::value) || (precision_type::value <= 0)),
                                             local_digits_2,
                                             precision_type>::type;
    #endif
  };

  } // namespace policies
  } // namespace math
  } // namespace boost

  namespace std // NOLINT(cert-dcl58-cpp)
  {
    template<const ::math::wide_integer::size_t MyWidth2,
             typename MyLimbType,
             const boost::multiprecision::expression_template_option ExpressionTemplatesOptions>
    class numeric_limits<boost::multiprecision::number<boost::multiprecision::uintwide_t_backend<MyWidth2, MyLimbType>,
                                                       ExpressionTemplatesOptions>>
    {
    public:
      static constexpr bool is_specialized = true;
      static constexpr bool is_signed      = false;
      static constexpr bool is_integer     = true;
      static constexpr bool is_exact       = true;
      static constexpr bool is_bounded     = true;
      static constexpr bool is_modulo      = false;
      static constexpr bool is_iec559      = false;
      static constexpr int  digits         = MyWidth2;
      static constexpr int  digits10       = static_cast<int>((MyWidth2 * 301LL) / 1000LL);
      static constexpr int  max_digits10   = static_cast<int>((MyWidth2 * 301LL) / 1000LL);

      static constexpr int max_exponent   = std::numeric_limits<::math::wide_integer::uintwide_t<MyWidth2, MyLimbType>>::max_exponent;
      static constexpr int max_exponent10 = std::numeric_limits<::math::wide_integer::uintwide_t<MyWidth2, MyLimbType>>::max_exponent10;
      static constexpr int min_exponent   = std::numeric_limits<::math::wide_integer::uintwide_t<MyWidth2, MyLimbType>>::min_exponent;
      static constexpr int min_exponent10 = std::numeric_limits<::math::wide_integer::uintwide_t<MyWidth2, MyLimbType>>::min_exponent10;

      static constexpr int                     radix             = 2;
      static constexpr std::float_round_style  round_style       = std::round_to_nearest;
      static constexpr bool                    has_infinity      = false;
      static constexpr bool                    has_quiet_NaN     = false;
      static constexpr bool                    has_signaling_NaN = false;
      static constexpr std::float_denorm_style has_denorm        = std::denorm_absent;
      static constexpr bool                    has_denorm_loss   = false;
      static constexpr bool                    traps             = false;
      static constexpr bool                    tinyness_before   = false;

      static WIDE_INTEGER_CONSTEXPR auto (min)        () -> boost::multiprecision::number<boost::multiprecision::uintwide_t_backend<MyWidth2, MyLimbType>, ExpressionTemplatesOptions> { return boost::multiprecision::uintwide_t_backend<MyWidth2, MyLimbType>((std::numeric_limits<::math::wide_integer::uintwide_t<MyWidth2, MyLimbType>>::min)()       ); }
      static WIDE_INTEGER_CONSTEXPR auto (max)        () -> boost::multiprecision::number<boost::multiprecision::uintwide_t_backend<MyWidth2, MyLimbType>, ExpressionTemplatesOptions> { return boost::multiprecision::uintwide_t_backend<MyWidth2, MyLimbType>((std::numeric_limits<::math::wide_integer::uintwide_t<MyWidth2, MyLimbType>>::max)()       ); }
      static WIDE_INTEGER_CONSTEXPR auto lowest       () -> boost::multiprecision::number<boost::multiprecision::uintwide_t_backend<MyWidth2, MyLimbType>, ExpressionTemplatesOptions> { return boost::multiprecision::uintwide_t_backend<MyWidth2, MyLimbType> (std::numeric_limits<::math::wide_integer::uintwide_t<MyWidth2, MyLimbType>>::lowest       ); }
      static WIDE_INTEGER_CONSTEXPR auto epsilon      () -> boost::multiprecision::number<boost::multiprecision::uintwide_t_backend<MyWidth2, MyLimbType>, ExpressionTemplatesOptions> { return boost::multiprecision::uintwide_t_backend<MyWidth2, MyLimbType> (std::numeric_limits<::math::wide_integer::uintwide_t<MyWidth2, MyLimbType>>::epsilon      ); }
      static WIDE_INTEGER_CONSTEXPR auto round_error  () -> boost::multiprecision::number<boost::multiprecision::uintwide_t_backend<MyWidth2, MyLimbType>, ExpressionTemplatesOptions> { return boost::multiprecision::uintwide_t_backend<MyWidth2, MyLimbType> (std::numeric_limits<::math::wide_integer::uintwide_t<MyWidth2, MyLimbType>>::round_error  ); }
      static WIDE_INTEGER_CONSTEXPR auto infinity     () -> boost::multiprecision::number<boost::multiprecision::uintwide_t_backend<MyWidth2, MyLimbType>, ExpressionTemplatesOptions> { return boost::multiprecision::uintwide_t_backend<MyWidth2, MyLimbType> (std::numeric_limits<::math::wide_integer::uintwide_t<MyWidth2, MyLimbType>>::infinity     ); }
      static WIDE_INTEGER_CONSTEXPR auto quiet_NaN    () -> boost::multiprecision::number<boost::multiprecision::uintwide_t_backend<MyWidth2, MyLimbType>, ExpressionTemplatesOptions> { return boost::multiprecision::uintwide_t_backend<MyWidth2, MyLimbType> (std::numeric_limits<::math::wide_integer::uintwide_t<MyWidth2, MyLimbType>>::quiet_NaN    ); }
      static WIDE_INTEGER_CONSTEXPR auto signaling_NaN() -> boost::multiprecision::number<boost::multiprecision::uintwide_t_backend<MyWidth2, MyLimbType>, ExpressionTemplatesOptions> { return boost::multiprecision::uintwide_t_backend<MyWidth2, MyLimbType> (std::numeric_limits<::math::wide_integer::uintwide_t<MyWidth2, MyLimbType>>::signaling_NaN); }
      static WIDE_INTEGER_CONSTEXPR auto denorm_min   () -> boost::multiprecision::number<boost::multiprecision::uintwide_t_backend<MyWidth2, MyLimbType>, ExpressionTemplatesOptions> { return boost::multiprecision::uintwide_t_backend<MyWidth2, MyLimbType> (std::numeric_limits<::math::wide_integer::uintwide_t<MyWidth2, MyLimbType>>::denorm_min   ); }
    };

    #ifndef BOOST_NO_INCLASS_MEMBER_INITIALIZATION

    template<const ::math::wide_integer::size_t MyWidth2, typename MyLimbType, const boost::multiprecision::expression_template_option ExpressionTemplatesOptions> constexpr bool std::numeric_limits<boost::multiprecision::number<boost::multiprecision::uintwide_t_backend<MyWidth2, MyLimbType>, ExpressionTemplatesOptions>>::is_specialized;
    template<const ::math::wide_integer::size_t MyWidth2, typename MyLimbType, const boost::multiprecision::expression_template_option ExpressionTemplatesOptions> constexpr bool std::numeric_limits<boost::multiprecision::number<boost::multiprecision::uintwide_t_backend<MyWidth2, MyLimbType>, ExpressionTemplatesOptions>>::is_signed;
    template<const ::math::wide_integer::size_t MyWidth2, typename MyLimbType, const boost::multiprecision::expression_template_option ExpressionTemplatesOptions> constexpr bool std::numeric_limits<boost::multiprecision::number<boost::multiprecision::uintwide_t_backend<MyWidth2, MyLimbType>, ExpressionTemplatesOptions>>::is_integer;
    template<const ::math::wide_integer::size_t MyWidth2, typename MyLimbType, const boost::multiprecision::expression_template_option ExpressionTemplatesOptions> constexpr bool std::numeric_limits<boost::multiprecision::number<boost::multiprecision::uintwide_t_backend<MyWidth2, MyLimbType>, ExpressionTemplatesOptions>>::is_exact;
    template<const ::math::wide_integer::size_t MyWidth2, typename MyLimbType, const boost::multiprecision::expression_template_option ExpressionTemplatesOptions> constexpr bool std::numeric_limits<boost::multiprecision::number<boost::multiprecision::uintwide_t_backend<MyWidth2, MyLimbType>, ExpressionTemplatesOptions>>::is_bounded;
    template<const ::math::wide_integer::size_t MyWidth2, typename MyLimbType, const boost::multiprecision::expression_template_option ExpressionTemplatesOptions> constexpr bool std::numeric_limits<boost::multiprecision::number<boost::multiprecision::uintwide_t_backend<MyWidth2, MyLimbType>, ExpressionTemplatesOptions>>::is_modulo;
    template<const ::math::wide_integer::size_t MyWidth2, typename MyLimbType, const boost::multiprecision::expression_template_option ExpressionTemplatesOptions> constexpr bool std::numeric_limits<boost::multiprecision::number<boost::multiprecision::uintwide_t_backend<MyWidth2, MyLimbType>, ExpressionTemplatesOptions>>::is_iec559;
    template<const ::math::wide_integer::size_t MyWidth2, typename MyLimbType, const boost::multiprecision::expression_template_option ExpressionTemplatesOptions> constexpr int  std::numeric_limits<boost::multiprecision::number<boost::multiprecision::uintwide_t_backend<MyWidth2, MyLimbType>, ExpressionTemplatesOptions>>::digits;
    template<const ::math::wide_integer::size_t MyWidth2, typename MyLimbType, const boost::multiprecision::expression_template_option ExpressionTemplatesOptions> constexpr int  std::numeric_limits<boost::multiprecision::number<boost::multiprecision::uintwide_t_backend<MyWidth2, MyLimbType>, ExpressionTemplatesOptions>>::digits10;
    template<const ::math::wide_integer::size_t MyWidth2, typename MyLimbType, const boost::multiprecision::expression_template_option ExpressionTemplatesOptions> constexpr int  std::numeric_limits<boost::multiprecision::number<boost::multiprecision::uintwide_t_backend<MyWidth2, MyLimbType>, ExpressionTemplatesOptions>>::max_digits10;

    template<const ::math::wide_integer::size_t MyWidth2, typename MyLimbType, const boost::multiprecision::expression_template_option ExpressionTemplatesOptions> constexpr int std::numeric_limits<boost::multiprecision::number<boost::multiprecision::uintwide_t_backend<MyWidth2, MyLimbType>, ExpressionTemplatesOptions>>::max_exponent;
    template<const ::math::wide_integer::size_t MyWidth2, typename MyLimbType, const boost::multiprecision::expression_template_option ExpressionTemplatesOptions> constexpr int std::numeric_limits<boost::multiprecision::number<boost::multiprecision::uintwide_t_backend<MyWidth2, MyLimbType>, ExpressionTemplatesOptions>>::max_exponent10;
    template<const ::math::wide_integer::size_t MyWidth2, typename MyLimbType, const boost::multiprecision::expression_template_option ExpressionTemplatesOptions> constexpr int std::numeric_limits<boost::multiprecision::number<boost::multiprecision::uintwide_t_backend<MyWidth2, MyLimbType>, ExpressionTemplatesOptions>>::min_exponent;
    template<const ::math::wide_integer::size_t MyWidth2, typename MyLimbType, const boost::multiprecision::expression_template_option ExpressionTemplatesOptions> constexpr int std::numeric_limits<boost::multiprecision::number<boost::multiprecision::uintwide_t_backend<MyWidth2, MyLimbType>, ExpressionTemplatesOptions>>::min_exponent10;

    template<const ::math::wide_integer::size_t MyWidth2, typename MyLimbType, const boost::multiprecision::expression_template_option ExpressionTemplatesOptions> constexpr int                     std::numeric_limits<boost::multiprecision::number<boost::multiprecision::uintwide_t_backend<MyWidth2, MyLimbType>, ExpressionTemplatesOptions>>::radix;
    template<const ::math::wide_integer::size_t MyWidth2, typename MyLimbType, const boost::multiprecision::expression_template_option ExpressionTemplatesOptions> constexpr std::float_round_style  std::numeric_limits<boost::multiprecision::number<boost::multiprecision::uintwide_t_backend<MyWidth2, MyLimbType>, ExpressionTemplatesOptions>>::round_style;
    template<const ::math::wide_integer::size_t MyWidth2, typename MyLimbType, const boost::multiprecision::expression_template_option ExpressionTemplatesOptions> constexpr bool                    std::numeric_limits<boost::multiprecision::number<boost::multiprecision::uintwide_t_backend<MyWidth2, MyLimbType>, ExpressionTemplatesOptions>>::has_infinity;
    template<const ::math::wide_integer::size_t MyWidth2, typename MyLimbType, const boost::multiprecision::expression_template_option ExpressionTemplatesOptions> constexpr bool                    std::numeric_limits<boost::multiprecision::number<boost::multiprecision::uintwide_t_backend<MyWidth2, MyLimbType>, ExpressionTemplatesOptions>>::has_quiet_NaN;
    template<const ::math::wide_integer::size_t MyWidth2, typename MyLimbType, const boost::multiprecision::expression_template_option ExpressionTemplatesOptions> constexpr bool                    std::numeric_limits<boost::multiprecision::number<boost::multiprecision::uintwide_t_backend<MyWidth2, MyLimbType>, ExpressionTemplatesOptions>>::has_signaling_NaN;
    template<const ::math::wide_integer::size_t MyWidth2, typename MyLimbType, const boost::multiprecision::expression_template_option ExpressionTemplatesOptions> constexpr std::float_denorm_style std::numeric_limits<boost::multiprecision::number<boost::multiprecision::uintwide_t_backend<MyWidth2, MyLimbType>, ExpressionTemplatesOptions>>::has_denorm;
    template<const ::math::wide_integer::size_t MyWidth2, typename MyLimbType, const boost::multiprecision::expression_template_option ExpressionTemplatesOptions> constexpr bool                    std::numeric_limits<boost::multiprecision::number<boost::multiprecision::uintwide_t_backend<MyWidth2, MyLimbType>, ExpressionTemplatesOptions>>::has_denorm_loss;
    template<const ::math::wide_integer::size_t MyWidth2, typename MyLimbType, const boost::multiprecision::expression_template_option ExpressionTemplatesOptions> constexpr bool                    std::numeric_limits<boost::multiprecision::number<boost::multiprecision::uintwide_t_backend<MyWidth2, MyLimbType>, ExpressionTemplatesOptions>>::traps;
    template<const ::math::wide_integer::size_t MyWidth2, typename MyLimbType, const boost::multiprecision::expression_template_option ExpressionTemplatesOptions> constexpr bool                    std::numeric_limits<boost::multiprecision::number<boost::multiprecision::uintwide_t_backend<MyWidth2, MyLimbType>, ExpressionTemplatesOptions>>::tinyness_before;

    #endif // !BOOST_NO_INCLASS_MEMBER_INITIALIZATION

  } // namespace std

  #if (defined(__clang__) && (__clang_major__ > 9)) && !defined(__APPLE__)
  #pragma GCC diagnostic pop
  #endif

  #if defined(__GNUC__)
  #pragma GCC diagnostic pop
  #pragma GCC diagnostic pop
  #pragma GCC diagnostic pop
  #endif

#endif // UINTWIDE_T_BACKEND_2019_12_15_HPP


/**
 * @file random_point_selection.hpp
 * @author Ryan Curtin
 *
 * Randomly select the next point for DBSCAN.
 *
 * mlpack is free software; you may redistribute it and/or modify it under the
 * terms of the 3-clause BSD license.  You should have received a copy of the
 * 3-clause BSD license along with mlpack.  If not, see
 * http://www.opensource.org/licenses/BSD-3-Clause for more information.
 */
#ifndef MLPACK_METHODS_DBSCAN_RANDOM_POINT_SELECTION_HPP
#define MLPACK_METHODS_DBSCAN_RANDOM_POINT_SELECTION_HPP

#include <mlpack/prereqs.hpp>
#include <boost/dynamic_bitset.hpp>

namespace mlpack {
namespace dbscan {

/**
 * This class can be used to randomly select the next point to use for DBSCAN.
 */
class RandomPointSelection
{
 public:
  /**
   * Select the next point to use, randomly.
   *
   * @param unvisited Bitset indicating which points are unvisited.
   * @param data Unused data.
   */
  template<typename MatType>
  static size_t Select(const boost::dynamic_bitset<>& unvisited,
                       const MatType& /* data */)
  {
    const size_t max = unvisited.count();
    const size_t index = math::RandInt(max);

    // Select the index'th unvisited point.
    size_t found = 0;
    for (size_t i = 0; i < unvisited.size(); ++i)
    {
      if (unvisited[i])
        ++found;

      if (found > index)
        return i;
    }

    return 0; // Not sure if it is possible to get here.
  }
};

} // namespace dbscan
} // namespace mlpack

#endif


#ifdef __APPLE__
#include <GLUT/glut.h>
#else
#include <GL/freeglut.h>
#endif

#include <vector>
#include <Eigen/Eigen>
#include <iostream>
#include <iomanip>
#include <fstream>

struct ScanPose{
	Eigen::Affine3d m;
	std::vector<Eigen::Vector3d> pc;
	std::string scan_file_name;
};

std::vector<ScanPose> scan_poses;

const unsigned int window_width = 1920;
const unsigned int window_height = 1080;
int mouse_old_x, mouse_old_y;
int mouse_buttons = 0;
float rotate_x = 0.0, rotate_y = 0.0;
float translate_z = -50.0;
float translate_x, translate_y = 0.0;

bool initGL(int *argc, char **argv);
void display();
void keyboard(unsigned char key, int x, int y);
void mouse(int button, int state, int x, int y);
void motion(int x, int y);
void reshape(int w, int h);
void printHelp();

void split(std::string &str, char delim, std::vector<std::string> &out)
{
	size_t start;
	size_t end = 0;

	while ((start = str.find_first_not_of(delim, end)) != std::string::npos)
	{
		end = str.find(delim, start);
		out.push_back(str.substr(start, end - start));
	}
}

int main(int argc, char *argv[]){
	if (false == initGL(&argc, argv)) {
		return 4;
	}

	std::ifstream f;
	f.open("../data/poses.csv");
	if(f.good()) {
		std::string s;
		while(!f.eof())	{
			getline(f,s);
			std::vector<std::string> strs;
			split(s, ',' , strs);
			ScanPose sp;
			if(strs.size() >= 13){
				sp.m = Eigen::Affine3d::Identity();
				std::istringstream(strs[0]) >> sp.scan_file_name;
				std::istringstream(strs[1]) >> sp.m(0,0);
				std::istringstream(strs[2]) >> sp.m(1,0);
				std::istringstream(strs[3]) >> sp.m(2,0);
				std::istringstream(strs[4]) >> sp.m(0,1);
				std::istringstream(strs[5]) >> sp.m(1,1);
				std::istringstream(strs[6]) >> sp.m(2,1);
				std::istringstream(strs[7]) >> sp.m(0,2);
				std::istringstream(strs[8]) >> sp.m(1,2);
				std::istringstream(strs[9]) >> sp.m(2,2);
				std::istringstream(strs[10]) >> sp.m(0,3);
				std::istringstream(strs[11]) >> sp.m(1,3);
				std::istringstream(strs[12]) >> sp.m(2,3);
				std::cout << sp.m(0,3) << " " << sp.m(1,3) << " " << sp.m(2,3) << std::endl;
				scan_poses.push_back(sp);
			}

		}
	}else{
		std::cout << "problem with opening file: ../data/poses.csv" << std::endl;
	}
	f.close();

	for(size_t i = 0; i < scan_poses.size(); i++){
		std::cout << scan_poses[i].scan_file_name << std::endl;
		std::ifstream f;
		f.open("../data/" + scan_poses[i].scan_file_name);
		if(f.good()) {
			std::string s;
			while(!f.eof())	{
				getline(f,s);
				std::vector<std::string> strs;
				split(s, ',' , strs);
				Eigen::Vector3d point;
				if(strs.size() == 3){
					std::istringstream(strs[0]) >> point.x();
					std::istringstream(strs[1]) >> point.y();
					std::istringstream(strs[2]) >> point.z();
					scan_poses[i].pc.push_back(point);
				}
			}
		}else{
			std::cout << "problem with opening file: ../data/poses.csv" << std::endl;
		}
		f.close();
	}

	std::cout << "loading done" << std::endl;

	printHelp();
	glutDisplayFunc(display);
	glutKeyboardFunc(keyboard);
	glutMouseFunc(mouse);
	glutMotionFunc(motion);
	glutMainLoop();

	return 0;
}

bool initGL(int *argc, char **argv) {
	glutInit(argc, argv);
	glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
	glutInitWindowSize(window_width, window_height);
	glutCreateWindow("particle_filer_demo");
	glutDisplayFunc(display);
	glutKeyboardFunc(keyboard);
	glutMotionFunc(motion);

	// default initialization
	glClearColor(1.0, 1.0, 1.0, 1.0);
	glEnable(GL_DEPTH_TEST);

	// viewport
	glViewport(0, 0, window_width, window_height);

	// projection
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(60.0, (GLfloat) window_width / (GLfloat) window_height, 0.01,
			10000.0);
	glutReshapeFunc(reshape);

	return true;
}

void display() {
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glTranslatef(translate_x, translate_y, translate_z);
	glRotatef(rotate_x, 1.0, 0.0, 0.0);
	glRotatef(rotate_y, 0.0, 0.0, 1.0);

	glBegin(GL_LINES);
		glColor3f(1.0f, 0.0f, 0.0f);
		glVertex3f(0.0f, 0.0f, 0.0f);
		glVertex3f(1.0f, 0.0f, 0.0f);

		glColor3f(0.0f, 1.0f, 0.0f);
		glVertex3f(0.0f, 0.0f, 0.0f);
		glVertex3f(0.0f, 1.0f, 0.0f);

		glColor3f(0.0f, 0.0f, 1.0f);
		glVertex3f(0.0f, 0.0f, 0.0f);
		glVertex3f(0.0f, 0.0f, 1.0f);
	glEnd();

	glColor3f(1,0,0);
	glBegin(GL_POINTS);
	for(size_t i = 0 ; i < scan_poses.size(); i++){
		for(size_t j = 0 ; j < scan_poses[i].pc.size(); j++){
			Eigen::Vector3d p = scan_poses[i].m * scan_poses[i].pc[j];
			glVertex3f(p.x(), p.y(), p.z());
		}
	}
	glEnd();
	glutSwapBuffers();
}

void keyboard(unsigned char key, int /*x*/, int /*y*/) {
	switch (key) {
		case (27): {
			glutDestroyWindow(glutGetWindow());
			return;
		}
	}
	printHelp();
	glutPostRedisplay();
}

void mouse(int button, int state, int x, int y) {
	if (state == GLUT_DOWN) {
		mouse_buttons |= 1 << button;
	} else if (state == GLUT_UP) {
		mouse_buttons = 0;
	}

	mouse_old_x = x;
	mouse_old_y = y;
}

void motion(int x, int y) {
	float dx, dy;
	dx = (float) (x - mouse_old_x);
	dy = (float) (y - mouse_old_y);

	if (mouse_buttons & 1) {
		rotate_x += dy * 0.2f;
		rotate_y += dx * 0.2f;

	} else if (mouse_buttons & 4) {
		translate_z += dy * 0.05f;
	} else if (mouse_buttons & 3) {
		translate_x += dx * 0.05f;
		translate_y -= dy * 0.05f;
	}

	mouse_old_x = x;
	mouse_old_y = y;

	glutPostRedisplay();
}

void reshape(int w, int h) {
	glViewport(0, 0, (GLsizei) w, (GLsizei) h);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(60.0, (GLfloat) w / (GLfloat) h, 0.01, 10000.0);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
}

void printHelp() {
	std::cout << "-------help-------" << std::endl;

}


//
// Created by Armando Herrera on 2019-07-22.
//

#include <array>
#include <iostream>
#include <algorithm>
#include <opencv2/opencv.hpp>
#include <boost/program_options.hpp>
#include <boost/filesystem.hpp>
#include <EasyTFLite.h>

namespace po = boost::program_options;
namespace fs = boost::filesystem;

int main(int argc, char **argv) {
    // Init google logging
    google::InitGoogleLogging(argv[0]);

    // Get paths
    fs::path project_path(fs::current_path().parent_path());
    fs::path source(project_path.string() + "/examples/galaxyclassification/110887.jpg");
    fs::path model_path(project_path.string() + "/examples/galaxyclassification/galaxmobilenet.tflite");

    // Image scale function
    std::function<float(unsigned char)> scale_func = [](unsigned char x) -> float {
        return static_cast<float>(x) / 255.0f;
    };

    // Get and parse arguments
    po::options_description description("An EasyTFLite Image Classification example");
    description.add_options()
            ("model", po::value<fs::path>(&model_path)->default_value(model_path),
             "Path to tensorFlow lite flatbuffer model")
            ("image", po::value<fs::path>(&source)->default_value(source),
             "Path to galaxy image to classify")
            ("help", "Produce help message");
    po::variables_map vm;
    po::store(po::parse_command_line(argc, argv, description), vm);
    if (vm.count("help")) {
        std::cout << description << "\n";
        return 0;
    }

    // Show selected arguments
    std::cout << "Model path: " << model_path << std::endl;
    std::cout << "Image path: " << source << std::endl;

    // Get image
    cv::Mat image = cv::imread(source.string());

    // Init model
    EasyTFLite model(model_path);

    // Run inference
    float *raw_output = model.run_inference_ptrs(image, scale_func)[0];

    // Get Data - model's output is 37 floats between 0 and 1
    std::array<float, 37> output = {0};
    std::copy(raw_output, raw_output + 37, output.begin());

    // Interpret model output
    // The first float indicates the potential of the galaxy being rounded
    // The second float indicates the potential of the galaxy being a disk
    // The third float indicates the potential of the galaxy being a artifact
    // Other floats - TODO
    //  https://www.kaggle.com/c/galaxy-zoo-the-galaxy-challenge/overview/the-galaxy-zoo-decision-tree
    std::cout << "The galaxy is ";
    if (output[2] > std::max(output[0], output[1])) {
        std::cout << "an artifact\n";
        return 0;
    } else if (output[0] > output[1]) {
        std::cout << "rounded ";
    } else {
        std::cout << "disk shaped ";
    }

    std::cout << '\n';

    return 0;
}


#ifndef __AERODYNAMICS_HH__
#define __AERODYNAMICS_HH__
/********************************* TRICK HEADER *******************************
PURPOSE:
      (Describe the AERODYNAMICS Module On Board)
LIBRARY DEPENDENCY:
      ((../src/Aerodynamics.cpp))
PROGRAMMERS:
      (((Chun-Hsu Lai) () () () ))
*******************************************************************************/
#include <armadillo>
#include <functional>
#include "aux.hh"
#include "cadac_constants.hh"
#include "datadeck.hh"
#include "numerical_constants.hh"
#include "vehicle.hh"
class AeroDynamics : public FH_module
{
    TRICK_INTERFACE(AeroDynamics);

public:
    explicit AeroDynamics();

    void load_aerotable(const char *filename);

    virtual void init(LaunchVehicle *VehicleIn){};
    virtual void algorithm(LaunchVehicle *VehicleIn);

private:
    Datadeck aerotable; /* ** (--) Aero Deck */
};

#endif  // __AERODYNAMICS_HH__


#include <boost/random/uniform_int_distribution.hpp>
#include <boost/random/random_device.hpp>
#include <limits>
#include "random.hpp"
#include "fast_hash.hpp"

namespace prologcoin { namespace common {

bool random::for_testing_ = false;
static fast_hash testing_rnd_;

static boost::random::random_device random_;

void random::set_for_testing(bool for_testing)
{
    for_testing_ = for_testing;
}

class for_testing_random {
public:
    uint32_t operator () () {
	testing_rnd_ << static_cast<int>(static_cast<uint32_t>(testing_rnd_));
	return static_cast<uint32_t>(testing_rnd_);
    }

    inline const uint32_t min() {
	return std::numeric_limits<uint32_t>::min();
    }
    inline const uint32_t max() {
	return std::numeric_limits<uint32_t>::max();
    }

    typedef uint32_t result_type;

private:
    static fast_hash random_;
};

static for_testing_random for_testing_random_;

std::string random::next(size_t entropy_bits)
{
    std::string chars("abcdefghijklmnopqrstuvwxyz"
		      "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
		      "1234567890");

    boost::random::uniform_int_distribution<> index_dist(0, chars.size() - 1);

    size_t n = (entropy_bits * 1000 + 5953) / 5954;
    std::string s(n, ' ');
    for (size_t i = 0; i < n; ++i) {
	if (for_testing_) {
	    s[i] = chars[index_dist(for_testing_random_)];
	} else {
	    s[i] = chars[index_dist(random_)];
	}
    }
    return s;
}

int random::next_int(int max)
{
    if (for_testing_) {
	return static_cast<int>(for_testing_random_() % max);
    } else {
	return static_cast<int>(static_cast<unsigned int>(random_()) % max);
    }
}

uint64_t random::next_int(uint64_t max)
{
    if (for_testing_) {
	return static_cast<uint64_t>(for_testing_random_() % max);
    } else {
	return static_cast<uint64_t>(static_cast<unsigned int>(random_()) % max);
    }
}

void random::next_bytes(uint8_t *bytes, size_t n)
{
    for (size_t i = 0; i < n; i++) {
	bytes[i] = next_int(256);
    }
}

}}


#ifndef MOCHIMOCHI_MAROW_HPP_
#define MOCHIMOCHI_MAROW_HPP_

#include <algorithm>
#include <unordered_map>
#include <boost/range/irange.hpp>
#include "../binary/arow.hpp"

class MAROW {
private:
  const std::size_t kClass;

private:
  std::unordered_map<std::size_t, AROW> _arows;

public:
  MAROW(const std::size_t dim, const std::size_t n_class, const double r)
    : kClass(n_class) {
    static_assert(std::numeric_limits<decltype(n_class)>::max() > 2, "Class range Error. (n_class > 2)");

    for (const auto i : boost::irange<std::size_t>(1, kClass + 1)) {
      _arows.insert(std::pair<std::size_t, AROW>(i, AROW(dim, r)) );
    }
  }

  virtual ~MAROW() { }

public:
  void update(const Eigen::VectorXd& feature, const std::size_t label) {
    for(auto& arow : _arows) {
      const auto t = (arow.first == label) ? 1 : -1;
      arow.second.update(feature, t);
    }
  }

  std::size_t predict(const Eigen::VectorXd& feature) const {
    return std::max_element(_arows.begin(), _arows.end(),
                            [&](const auto& p1, const auto& p2) {
                              return p1.second.get_means().dot(feature) < p2.second.get_means().dot(feature);
                            })->first;
  }

};

#endif //MOCHIMOCHI_MAROW_HPP_


//
// Created by vlad on 03.12.16.
//

#pragma once

#include <route_solver/structs/data_structs.hpp>
#include <route_solver/abc/abc.hpp>
#include <route_solver/utils/common.hpp>
#include <route_solver/solution/currentroutestate.hpp>
#include <route_solver/solution/fail_statistics.hpp>

#include <boost/function.hpp>
#include <Eigen/Core>

#include <vector>
#include <unordered_map>

namespace rs {

struct PointStat;

struct Route
{
	Route() = default;

	Route(Route&& o) = default;
	Route(const Route& o) = default;

	Route& operator=(const Route& o) = default;

	std::int32_t vehicleId = 0;
	bool solved = false;
	std::vector<std::int32_t> nodes;

	bool operator== (const Route& o) const
	{
		return nodes == o.nodes && vehicleId == o.vehicleId;
	}
};

}

namespace std {
template<> struct hash<rs::Route>
{
	std::size_t  operator()(rs::Route const& r) const
	{
		std::size_t seed = boost::hash_range(r.nodes.begin(), r.nodes.end());
		boost::hash_combine(seed, r.vehicleId);
		return seed;
	}
};
}

namespace rs {

struct RoutesCheckResult
{
	double fitness = 0;
	double totalDistance = 0;
	std::size_t totalTime = 0;
	bool allSolved = false;
};

class TruckSolution
	: public rs::Solution<TruckSolution> {
public:
	TruckSolution(
		std::vector<Route>&& routes,
		RoutesCheckResult&& stat
	)
		: m_stat(std::move(stat))
		, m_routes(std::move(routes))
	{}

	double fitnessImpl() const;

	const std::vector<Route>& getRoutes() const;

	const RoutesCheckResult& getStat() const;

private:
	RoutesCheckResult m_stat;
	std::vector<Route> m_routes;
};

struct TaskWithStat
{
	std::uint32_t nodeId;
	std::size_t timeStart;
	std::size_t timeEnd;
	double workloadDiff;
	double volumeDiff;
	double distanceDiff;
	int tasksCountDiff;
};

struct RouteWithStat
{
	double length;
	std::uint32_t vehicleId;
	std::size_t timeStart;
	std::size_t timeEnd;
	std::vector<TaskWithStat> tasks;

};

struct ProdSolutionWithStat
{
	std::vector<RouteWithStat> routes;
};

class TruckSolutionProducer
	: public rs::SolutionProducer<TruckSolutionProducer, TruckSolution> {
public:
	TruckSolutionProducer(
		const Eigen::MatrixXd& destTimeTable,
		const Eigen::MatrixXd& destTable,
		const Eigen::MatrixXd& vehicleToTaskValid,
		const std::vector<rs::Vehicle>& vehicles,
		const std::vector<rs::Task>& tasks,
		bool partialSolutionResultEnabled
	);

	TruckSolution generateRandomImpl();

	TruckSolution produceNeighborhoodImpl(const TruckSolution& solution);

	ProdSolutionWithStat getResultStats(const TruckSolution& solution);

public:
	struct SingleRouteStatistics
	{
		double cost = std::numeric_limits<double>::max();
		double distanse = 0;
		std::size_t totalTime = 0;
		std::size_t vipCount = 0;
	};

	void mergeFails();

	static FailedStatisticsCollector getFailsCollector();

private:

	RoutesCheckResult calculateFitnessOfRoutes(std::vector<Route>& routes);

	SingleRouteStatistics checkSingleRoute(
		Route& r,
		std::vector<PointStat>* stats = 0
	);

	const Eigen::MatrixXd& m_destTimeTable;
	const Eigen::MatrixXd& m_destTable;
	const Eigen::MatrixXd& m_vehicleToTaskValid;
	const std::vector<rs::Vehicle>& m_vehicles;
	const std::vector<rs::Task>& m_tasks;
	bool m_partialSolutionResultEnabled;

	std::unique_ptr<std::random_device> m_rd;
	std::unique_ptr<std::mt19937> m_g;
	std::unordered_map<Route, SingleRouteStatistics> m_routes_cache;

	FailedStatisticsCollector m_failsCollector;
};

}


#ifndef DEMO
#define DEMO

#include <Eigen/Dense>
#include <algorithm>
#include <iostream>
#include "verify.h"
#include "MiniDnn/layer.h"
#include "MiniDnn/layer/conv.h"
#include "MiniDnn/layer/fully_connected.h"
#include "MiniDnn/layer/ave_pooling.h"
#include "MiniDnn/layer/max_pooling.h"
#include "MiniDnn/layer/relu.h"
#include "MiniDnn/layer/sigmoid.h"
#include "MiniDnn/layer/softmax.h"
#include "MiniDnn/loss.h"
#include "MiniDnn/loss/mse_loss.h"
#include "MiniDnn/loss/cross_entropy_loss.h"
#include "MiniDnn/mnist.h"
#include "MiniDnn/network.h"
#include "MiniDnn/optimizer.h"
#include "MiniDnn/optimizer/sgd.h"
#include "Enclave.h"
#include "Enclave_t.h"  /* print_string */

#include <vector>
#include <string>
//#include <sstream>
//using namespace std;

extern uint32_t seed;
unsigned int prf(unsigned int next)
{
    next = next * 1103515245 + 12345;
    return (unsigned int) (next / 65536) % 60000;
}
float minus_abs(float a, float b)
{
    float temp = a - b;
    if (temp < 0)
        temp = temp * -1.0;
    return temp;
}
int judge(std::vector<std::vector<float> > vnet, int ith_batch)
{
    int error_number = 0;
    float *p_conv1, *p_conv2, *p_fc1, *p_fc2, *p_fc3;
    sgx_get_parameters(ith_batch, &p_conv1, &p_conv2, &p_fc1, &p_fc2, &p_fc3);
    std::vector<float> v1,v2,v3,v4,v5;
    for(int i = 0; i < CONV1_SIZE; i++)
        v1.push_back(p_conv1[i]);
    for(int i = 0; i < CONV2_SIZE; i++)
        v2.push_back(p_conv2[i]);
    for(int i = 0; i < FC1_SIZE; i++)
        v3.push_back(p_fc1[i]);
    for(int i = 0; i < FC2_SIZE; i++)
        v4.push_back(p_fc2[i]);
    for(int i = 0; i < FC3_SIZE; i++)
        v5.push_back(p_fc3[i]);
    sgx_free_parameters(p_conv1, p_conv2, p_fc1, p_fc2, p_fc3);
    std::vector<float> all;
    std::vector<float> all2;
    all.insert(all.end(), v1.begin(), v1.end());
    all.insert(all.end(), v2.begin(), v2.end());
    all.insert(all.end(), v3.begin(), v3.end());
    all.insert(all.end(), v4.begin(), v4.end());
    all.insert(all.end(), v5.begin(), v5.end());
    int index = 0;
    for (int i = 0; i < vnet.size(); i++)
        for (int j = 0; j < vnet[i].size(); j++)
            all2.push_back(vnet[i][j]);
    if (all.size() != all2.size())
        return 0;
    for (int i = 0; i < all.size(); i++)
    {
        if (minus_abs(all[i], all2[i])>0.00001)
            error_number++;
        if (error_number > 0)
            return 0;
    }
    return 1;
}

#define FILE_NAME "README.md"
void ecall_ml_vgg16() {
    //const int ith_batch = 120;
    const int proof_number = 5;
    const int batch_size = 128;
    const int total_rounds = 200;
    // read data
    printf("[Enclave]: Start loading data... \n");
    MNIST dataset("./Training/");
    dataset.read();
    int n_train = dataset.train_data.cols();
    int dim_in = dataset.train_data.rows();
    printf("[Enclave]: mnist train number:%d\n", n_train);

    std::vector<int> result;
    std::vector<int> proof;
    printf("[Enclave]: Round number:");
    for(int i = 0; i < proof_number; i++){
        unsigned int temp = 0;
        sgx_read_rand((uint8_t*)&temp, 32);
        temp = temp % total_rounds;
        while(std::find(proof.begin(), proof.end(), temp) != proof.end()){
            sgx_read_rand((uint8_t*)&temp, 32);
            temp = temp % total_rounds;
        }
        proof.push_back((int)temp);
        printf("%d, ", temp);
    }
    printf(" are going to be verified...\n");
    for (int number = 0; number < proof.size(); number++){
        int ith_batch = proof[number];
        Network dnn;
        Layer *conv1 = new Conv(1, 28, 28, 6, 5, 5, 1, 2, 2);
        Layer *pool1 = new MaxPooling(6, 28, 28, 2, 2, 2);
        Layer *conv2 = new Conv(6, 14, 14, 16, 5, 5, 1, 0, 0);
        Layer *pool2 = new MaxPooling(16, 10, 10, 2, 2, 2);
        Layer *fc3 = new FullyConnected(pool2->output_dim(), 120);
        Layer *fc4 = new FullyConnected(120, 84);
        Layer *fc5 = new FullyConnected(84, 10);
        Layer *relu1 = new ReLU;
        Layer *relu2 = new ReLU;
        Layer *relu3 = new ReLU;
        Layer *relu4 = new ReLU;
        Layer *softmax = new Softmax;

        //loading data
        float *p_conv1, *p_conv2, *p_fc1, *p_fc2, *p_fc3;
        sgx_get_parameters(ith_batch, &p_conv1, &p_conv2, &p_fc1, &p_fc2, &p_fc3);
        std::vector<float> v1,v2,v3,v4,v5;
        for(int i = 0; i < CONV1_SIZE; i++) v1.push_back(p_conv1[i]);
        for(int i = 0; i < CONV2_SIZE; i++) v2.push_back(p_conv2[i]);
        for(int i = 0; i < FC1_SIZE; i++) v3.push_back(p_fc1[i]);
        for(int i = 0; i < FC2_SIZE; i++) v4.push_back(p_fc2[i]);
        for(int i = 0; i < FC3_SIZE; i++) v5.push_back(p_fc3[i]);

        if(0 == sgx_check_parameter(ith_batch, p_conv1, p_conv2, p_fc1, p_fc2, p_fc3))
            printf("[Enclave]: Round %d parameter checking succeeded!\n", ith_batch);
        else{
            printf("[Enclave]: Round %d parameter checking failed!\n", ith_batch);
            return;
        }
        sgx_free_parameters(p_conv1, p_conv2, p_fc1, p_fc2, p_fc3);

        conv1->set_parameters(v1);
        conv2->set_parameters(v2);
        fc3->set_parameters(v3);
        fc4->set_parameters(v4);
        fc5->set_parameters(v5);

        //apply
        dnn.add_layer(conv1);
        dnn.add_layer(relu1);
        dnn.add_layer(pool1);
        dnn.add_layer(conv2);
        dnn.add_layer(relu2);
        dnn.add_layer(pool2);
        dnn.add_layer(fc3);
        dnn.add_layer(relu3);
        dnn.add_layer(fc4);
        dnn.add_layer(relu4);
        dnn.add_layer(fc5);
        dnn.add_layer(softmax);

        // loss
        Loss *loss = new CrossEntropy;
        dnn.add_loss(loss);

        // train & test
        SGD opt(0.001);
        Matrix x_batch;
        Matrix label_batch;
        x_batch.resize(dim_in, batch_size);
        label_batch.resize(1, batch_size);
        for(int j = 0; j < batch_size; j++){ //confirm training data
            unsigned int index = prf(seed * ith_batch + j);
            uint8_t* p_data = (uint8_t*)malloc(784), label;
            int ret_val;
            ocall_get_data(&ret_val, index, p_data, &label);
            if(0 != sgx_check_data(index, p_data, label)){
                printf("[Enclave]: Round %d data check failed!\n", ith_batch);
                return;
            }
            for(int i = 0; i < dim_in; i++)
                x_batch(i, j) = dataset.train_data(i, index);  //opt. std::copy (cols first)
            label_batch(0, j) = dataset.train_labels(0, index);
        }
        printf("[Enclave]: Round %d data check succeeded!\n", ith_batch);
        //start training
        printf("[Enclave]: Verify round %d training...\n", ith_batch);
        Matrix target_batch = one_hot_encode(label_batch, 10);
        dnn.forward(x_batch);
        dnn.backward(x_batch, target_batch);
        // optimize
        dnn.update(opt);

        //jduge
        // printf("[Enclave] Judging...\n");
        std::vector<std::vector<float> > vnet = dnn.get_parameters();
        if (judge(vnet, ith_batch + 1)){
            result.push_back(ith_batch);
            printf("[Enclave]: Round %d matched\n", ith_batch);
        }
        else{
            printf("[Enclave]: Round %d not matched\n", ith_batch);
        }
    }
    if(proof.size()==result.size())
    	printf("\n[Enclave]: Congratulations! Your training has been verified successfully\n\n");
    else
    	printf("[Enclave]: Verification failed\n");
}

#endif


#ifndef BoostUnitDefinitions_Units_hpp
#define BoostUnitDefinitions_Units_hpp

#include <boost/units/base_units/angle/arcminute.hpp>
#include <boost/units/base_units/angle/arcsecond.hpp>
#include <boost/units/base_units/angle/degree.hpp>
#include <boost/units/base_units/imperial/foot.hpp>
#include <boost/units/base_units/imperial/inch.hpp>
#include <boost/units/base_units/imperial/yard.hpp>
#include <boost/units/base_units/metric/year.hpp>
#include <boost/units/cmath.hpp>
#include <boost/units/systems/cgs.hpp>
#include <boost/units/systems/cgs/io.hpp>
#include <boost/units/systems/si.hpp>
#include <boost/units/systems/si/io.hpp>

namespace boost
{
namespace units
{

/** @brief namespace for unit types */
namespace t
{
}
/** @brief namespace for unit instances*/
namespace i
{
}
}  // namespace units
}  // namespace boost

/* A preprocessor macro for defining new units. Creates a unit types in the 't'
 * namespace and an instance of it in the 'i' namespace.
 */
#define ADD_UNIT(NAME, ...)                   \
  namespace boost                             \
  {                                           \
  namespace units                             \
  {                                           \
  namespace t                                 \
  {                                           \
  typedef __VA_ARGS__ NAME;                   \
  }                                           \
  namespace i                                 \
  {                                           \
  BOOST_UNITS_STATIC_CONSTANT(NAME, t::NAME); \
  }                                           \
  }                                           \
  }

/* Create a set of base units with all of the SI prefixes. */
#define ADD_BASE_UNIT_SET(NAME, SYMB, BASE)                                    \
  ADD_UNIT(giga##NAME,                                                         \
           scaled_base_unit<BASE, scale<10, static_rational<9>>>::unit_type)   \
  ADD_UNIT(mega##NAME,                                                         \
           scaled_base_unit<BASE, scale<10, static_rational<6>>>::unit_type)   \
  ADD_UNIT(kilo##NAME,                                                         \
           scaled_base_unit<BASE, scale<10, static_rational<3>>>::unit_type)   \
  ADD_UNIT(NAME, BASE::unit_type)                                              \
  ADD_UNIT(centi##NAME,                                                        \
           scaled_base_unit<BASE, scale<10, static_rational<-2>>>::unit_type)  \
  ADD_UNIT(milli##NAME,                                                        \
           scaled_base_unit<BASE, scale<10, static_rational<-3>>>::unit_type)  \
  ADD_UNIT(micro##NAME,                                                        \
           scaled_base_unit<BASE, scale<10, static_rational<-6>>>::unit_type)  \
  ADD_UNIT(nano##NAME,                                                         \
           scaled_base_unit<BASE, scale<10, static_rational<-9>>>::unit_type)  \
  ADD_UNIT(pico##NAME,                                                         \
           scaled_base_unit<BASE, scale<10, static_rational<-12>>>::unit_type) \
  ADD_UNIT(femto##NAME,                                                        \
           scaled_base_unit<BASE, scale<10, static_rational<-15>>>::unit_type) \
  ADD_UNIT(G##SYMB,                                                            \
           scaled_base_unit<BASE, scale<10, static_rational<9>>>::unit_type)   \
  ADD_UNIT(M##SYMB,                                                            \
           scaled_base_unit<BASE, scale<10, static_rational<6>>>::unit_type)   \
  ADD_UNIT(k##SYMB,                                                            \
           scaled_base_unit<BASE, scale<10, static_rational<3>>>::unit_type)   \
  ADD_UNIT(SYMB, BASE::unit_type)                                              \
  ADD_UNIT(c##SYMB,                                                            \
           scaled_base_unit<BASE, scale<10, static_rational<-2>>>::unit_type)  \
  ADD_UNIT(m##SYMB,                                                            \
           scaled_base_unit<BASE, scale<10, static_rational<-3>>>::unit_type)  \
  ADD_UNIT(u##SYMB,                                                            \
           scaled_base_unit<BASE, scale<10, static_rational<-6>>>::unit_type)  \
  ADD_UNIT(n##SYMB,                                                            \
           scaled_base_unit<BASE, scale<10, static_rational<-9>>>::unit_type)  \
  ADD_UNIT(p##SYMB,                                                            \
           scaled_base_unit<BASE, scale<10, static_rational<-12>>>::unit_type) \
  ADD_UNIT(f##SYMB,                                                            \
           scaled_base_unit<BASE, scale<10, static_rational<-15>>>::unit_type)

/* Create a set of (not base) units with all of the SI prefixes. */
#define ADD_UNIT_SET(NAME, SYMB, UNIT)                                    \
  ADD_UNIT(giga##NAME,                                                    \
           make_scaled_unit<UNIT, scale<10, static_rational<9>>>::type)   \
  ADD_UNIT(mega##NAME,                                                    \
           make_scaled_unit<UNIT, scale<10, static_rational<6>>>::type)   \
  ADD_UNIT(kilo##NAME,                                                    \
           make_scaled_unit<UNIT, scale<10, static_rational<3>>>::type)   \
  ADD_UNIT(NAME, UNIT)                                                    \
  ADD_UNIT(centi##NAME,                                                   \
           make_scaled_unit<UNIT, scale<10, static_rational<-2>>>::type)  \
  ADD_UNIT(milli##NAME,                                                   \
           make_scaled_unit<UNIT, scale<10, static_rational<-3>>>::type)  \
  ADD_UNIT(micro##NAME,                                                   \
           make_scaled_unit<UNIT, scale<10, static_rational<-6>>>::type)  \
  ADD_UNIT(nano##NAME,                                                    \
           make_scaled_unit<UNIT, scale<10, static_rational<-9>>>::type)  \
  ADD_UNIT(pico##NAME,                                                    \
           make_scaled_unit<UNIT, scale<10, static_rational<-12>>>::type) \
  ADD_UNIT(femto##NAME,                                                   \
           make_scaled_unit<UNIT, scale<10, static_rational<-15>>>::type) \
  ADD_UNIT(G##SYMB,                                                       \
           make_scaled_unit<UNIT, scale<10, static_rational<9>>>::type)   \
  ADD_UNIT(M##SYMB,                                                       \
           make_scaled_unit<UNIT, scale<10, static_rational<6>>>::type)   \
  ADD_UNIT(k##SYMB,                                                       \
           make_scaled_unit<UNIT, scale<10, static_rational<3>>>::type)   \
  ADD_UNIT(SYMB, UNIT)                                                    \
  ADD_UNIT(c##SYMB,                                                       \
           make_scaled_unit<UNIT, scale<10, static_rational<-2>>>::type)  \
  ADD_UNIT(m##SYMB,                                                       \
           make_scaled_unit<UNIT, scale<10, static_rational<-3>>>::type)  \
  ADD_UNIT(u##SYMB,                                                       \
           make_scaled_unit<UNIT, scale<10, static_rational<-6>>>::type)  \
  ADD_UNIT(n##SYMB,                                                       \
           make_scaled_unit<UNIT, scale<10, static_rational<-9>>>::type)  \
  ADD_UNIT(p##SYMB,                                                       \
           make_scaled_unit<UNIT, scale<10, static_rational<-12>>>::type) \
  ADD_UNIT(f##SYMB,                                                       \
           make_scaled_unit<UNIT, scale<10, static_rational<-15>>>::type)

/* Add an output overload. I.e., set the string that will be output when the
 * user prints the unit.
 */
#define ADD_UNIT_IO(NAME, NSTR, SSTR)                            \
  namespace boost                                                \
  {                                                              \
  namespace units                                                \
  {                                                              \
  inline std::string name_string(const boost::units::t::NAME&)   \
  {                                                              \
    return NSTR;                                                 \
  }                                                              \
  inline std::string symbol_string(const boost::units::t::NAME&) \
  {                                                              \
    return SSTR;                                                 \
  }                                                              \
  }                                                              \
  }

// Aliases

ADD_UNIT(dimensionless, si::dimensionless)
ADD_UNIT(dimless, dimensionless)

// Base units
// these units are based on scaled base units, which is required to
// get the si unit prefixes to work correctly
ADD_BASE_UNIT_SET(meter, m, si::meter_base_unit)
ADD_BASE_UNIT_SET(gram, g, cgs::gram_base_unit)
ADD_BASE_UNIT_SET(second, s, si::second_base_unit)
ADD_BASE_UNIT_SET(kelvin, K, si::kelvin_base_unit)
ADD_BASE_UNIT_SET(ampere, A, si::ampere_base_unit)
ADD_BASE_UNIT_SET(mole, mol, si::mole_base_unit)
ADD_BASE_UNIT_SET(candela, cd, si::candela_base_unit)

// Alternate Base Units
// these units are alternate base units, i.e. they are
// units for a single dimension.

ADD_UNIT(year, metric::year_base_unit::unit_type)
ADD_UNIT(yr, year)

ADD_UNIT(degree, angle::degree_base_unit::unit_type)
ADD_UNIT(deg, degree)
ADD_UNIT(arcminute, angle::arcminute_base_unit::unit_type)
ADD_UNIT(arcmin, arcminute)
ADD_UNIT(arcsecond, angle::arcsecond_base_unit::unit_type)
ADD_UNIT(arcsec, arcsecond)

ADD_UNIT(percent,
         make_scaled_unit<dimensionless, scale<10, static_rational<-2>>>::type)
ADD_UNIT(perc, percent)

// Imperial Units
ADD_UNIT(inches, imperial::inch_base_unit::unit_type)
ADD_UNIT(inch, inches)
ADD_UNIT(in, inches)
ADD_UNIT(feet, imperial::foot_base_unit::unit_type)
ADD_UNIT(foot, feet)
ADD_UNIT(ft, foot)
ADD_UNIT(yards, imperial::yard_base_unit::unit_type)
ADD_UNIT(yard, yards)
ADD_UNIT(yd, yard)

// NOTE: These MUST be equivalent to the si:: units or the trig
// functions will not work. We CANNOT use si::radian_base_unit here.
ADD_UNIT_SET(radian, rad, si::plane_angle)
ADD_UNIT_SET(steradian, sr, si::solid_angle)

// Derived Units

ADD_UNIT(inverse_second, divide_typeof_helper<dimensionless, second>::type)
ADD_UNIT(s_n1, inverse_second)
ADD_UNIT_SET(hertz, Hz, inverse_second)
ADD_UNIT_SET(joule, J, si::energy)
ADD_UNIT_SET(watt, W, si::power)
ADD_UNIT_SET(newton, N, si::force)
ADD_UNIT_SET(coulomb, C, si::electric_charge)

// Inverse Length
ADD_UNIT(inverse_kilometer,
         divide_typeof_helper<dimensionless, kilometer>::type)
ADD_UNIT(inverse_meter, divide_typeof_helper<dimensionless, meter>::type)
ADD_UNIT(inverse_centimeter,
         divide_typeof_helper<dimensionless, centimeter>::type)
ADD_UNIT(inverse_millimeter,
         divide_typeof_helper<dimensionless, millimeter>::type)
ADD_UNIT(inverse_micrometer,
         divide_typeof_helper<dimensionless, micrometer>::type)
ADD_UNIT(inverse_nanometer,
         divide_typeof_helper<dimensionless, nanometer>::type)
ADD_UNIT(inverse_picometer,
         divide_typeof_helper<dimensionless, picometer>::type)
ADD_UNIT(inverse_femtometer,
         divide_typeof_helper<dimensionless, femtometer>::type)

ADD_UNIT(km_n1, inverse_kilometer)
ADD_UNIT(m_n1, inverse_meter)
ADD_UNIT(cm_n1, inverse_centimeter)
ADD_UNIT(mm_n1, inverse_millimeter)
ADD_UNIT(um_n1, inverse_micrometer)
ADD_UNIT(nm_n1, inverse_nanometer)
ADD_UNIT(pm_n1, inverse_picometer)
ADD_UNIT(fm_n1, inverse_femtometer)

// Area
ADD_UNIT(kilometer_squared, multiply_typeof_helper<kilometer, kilometer>::type)
ADD_UNIT(meter_squared, multiply_typeof_helper<meter, meter>::type)
ADD_UNIT(m_p2, meter_squared)
ADD_UNIT(centimeter_squared,
         multiply_typeof_helper<centimeter, centimeter>::type)
ADD_UNIT(cm_p2, centimeter_squared)
ADD_UNIT(millimeter_squared,
         multiply_typeof_helper<millimeter, millimeter>::type)
ADD_UNIT(mm_p2, millimeter_squared)

// Inverse Area

ADD_UNIT(inverse_kilometer_squared,
         divide_typeof_helper<dimensionless, kilometer_squared>::type)
ADD_UNIT(inverse_meter_squared,
         divide_typeof_helper<dimensionless, meter_squared>::type)
ADD_UNIT(inverse_centimeter_squared,
         divide_typeof_helper<dimensionless, centimeter_squared>::type)
ADD_UNIT(inverse_millimeter_squared,
         divide_typeof_helper<dimensionless, millimeter_squared>::type)

ADD_UNIT(km_n2, inverse_kilometer_squared)
ADD_UNIT(m_n2, inverse_meter_squared)
ADD_UNIT(cm_n2, inverse_centimeter_squared)
ADD_UNIT(mm_n2, inverse_millimeter_squared)

// Velocity
ADD_UNIT(meter_per_second, divide_typeof_helper<meter, second>::type)
ADD_UNIT(m_s_n2, divide_typeof_helper<m, s>::type)

// Radiant Exposure
ADD_UNIT(joule_per_centimeter_squared,
         divide_typeof_helper<joule, centimeter_squared>::type)
ADD_UNIT(millijoule_per_centimeter_squared,
         divide_typeof_helper<millijoule, centimeter_squared>::type)
ADD_UNIT(microjoule_per_centimeter_squared,
         divide_typeof_helper<microjoule, centimeter_squared>::type)
ADD_UNIT(nanojoule_per_centimeter_squared,
         divide_typeof_helper<nanojoule, centimeter_squared>::type)

ADD_UNIT(J_cm_n2, joule_per_centimeter_squared)
ADD_UNIT(mJ_cm_n2, millijoule_per_centimeter_squared)
ADD_UNIT(uJ_cm_n2, microjoule_per_centimeter_squared)
ADD_UNIT(nJ_cm_n2, nanojoule_per_centimeter_squared)

ADD_UNIT(joule_per_meter_squared,
         divide_typeof_helper<joule, meter_squared>::type)
ADD_UNIT(millijoule_per_meter_squared,
         divide_typeof_helper<millijoule, meter_squared>::type)
ADD_UNIT(microjoule_per_meter_squared,
         divide_typeof_helper<microjoule, meter_squared>::type)
ADD_UNIT(nanojoule_per_meter_squared,
         divide_typeof_helper<nanojoule, meter_squared>::type)

ADD_UNIT(J_m_n2, joule_per_meter_squared)
ADD_UNIT(mJ_m_n2, millijoule_per_meter_squared)
ADD_UNIT(uJ_m_n2, microjoule_per_meter_squared)
ADD_UNIT(nJ_m_n2, nanojoule_per_meter_squared)

ADD_UNIT_IO(joule_per_centimeter_squared, "joule per centimeter squared",
            "J cm^-2")
ADD_UNIT_IO(joule_per_meter_squared, "joule per meter squared", "J m^-2")

// Irradiance
ADD_UNIT(watt_per_centimeter_squared,
         divide_typeof_helper<watt, centimeter_squared>::type)
ADD_UNIT(milliwatt_per_centimeter_squared,
         divide_typeof_helper<milliwatt, centimeter_squared>::type)
ADD_UNIT(microwatt_per_centimeter_squared,
         divide_typeof_helper<microwatt, centimeter_squared>::type)
ADD_UNIT(nanowatt_per_centimeter_squared,
         divide_typeof_helper<nanowatt, centimeter_squared>::type)

ADD_UNIT(W_cm_n2, watt_per_centimeter_squared)
ADD_UNIT(mW_cm_n2, milliwatt_per_centimeter_squared)
ADD_UNIT(uW_cm_n2, microwatt_per_centimeter_squared)
ADD_UNIT(nW_cm_n2, nanowatt_per_centimeter_squared)

ADD_UNIT(watt_per_meter_squared,
         divide_typeof_helper<watt, meter_squared>::type)
ADD_UNIT(milliwatt_per_meter_squared,
         divide_typeof_helper<milliwatt, meter_squared>::type)
ADD_UNIT(microwatt_per_meter_squared,
         divide_typeof_helper<microwatt, meter_squared>::type)
ADD_UNIT(nanowatt_per_meter_squared,
         divide_typeof_helper<nanowatt, meter_squared>::type)

ADD_UNIT(W_m_n2, watt_per_meter_squared)
ADD_UNIT(mW_m_n2, milliwatt_per_meter_squared)
ADD_UNIT(uW_m_n2, microwatt_per_meter_squared)
ADD_UNIT(nW_m_n2, nanowatt_per_meter_squared)

ADD_UNIT_IO(watt_per_centimeter_squared, "watt per centimeter squared",
            "W cm^-2")
ADD_UNIT_IO(watt_per_meter_squared, "watt per meter squared", "W m^-2")

// Integrated Radiance
ADD_UNIT(joule_per_centimeter_squared_per_steradian,
         divide_typeof_helper<joule_per_centimeter_squared, steradian>::type)
ADD_UNIT(
    millijoule_per_centimeter_squared_per_steradian,
    divide_typeof_helper<millijoule_per_centimeter_squared, steradian>::type)
ADD_UNIT(
    microjoule_per_centimeter_squared_per_steradian,
    divide_typeof_helper<microjoule_per_centimeter_squared, steradian>::type)
ADD_UNIT(
    nanojoule_per_centimeter_squared_per_steradian,
    divide_typeof_helper<nanojoule_per_centimeter_squared, steradian>::type)

ADD_UNIT(J_cm_n2_sr_n1, joule_per_centimeter_squared_per_steradian)
ADD_UNIT(mJ_cm_n2_sr_n1, millijoule_per_centimeter_squared_per_steradian)
ADD_UNIT(uJ_cm_n2_sr_n1, microjoule_per_centimeter_squared_per_steradian)
ADD_UNIT(nJ_cm_n2_sr_n1, nanojoule_per_centimeter_squared_per_steradian)

ADD_UNIT(joule_per_meter_squared_per_steradian,
         divide_typeof_helper<joule_per_meter_squared, steradian>::type)
ADD_UNIT(millijoule_per_meter_squared_per_steradian,
         divide_typeof_helper<millijoule_per_meter_squared, steradian>::type)
ADD_UNIT(microjoule_per_meter_squared_per_steradian,
         divide_typeof_helper<microjoule_per_meter_squared, steradian>::type)
ADD_UNIT(nanojoule_per_meter_squared_per_steradian,
         divide_typeof_helper<nanojoule_per_meter_squared, steradian>::type)

ADD_UNIT(J_m_n2_sr_n1, joule_per_meter_squared_per_steradian)
ADD_UNIT(mJ_m_n2_sr_n1, millijoule_per_meter_squared_per_steradian)
ADD_UNIT(uJ_m_n2_sr_n1, microjoule_per_meter_squared_per_steradian)
ADD_UNIT(nJ_m_n2_sr_n1, nanojoule_per_meter_squared_per_steradian)

ADD_UNIT_IO(joule_per_centimeter_squared_per_steradian,
            "joule per centimeter squared per steradian", "J cm^-2 sr^-1")

// Radiance
ADD_UNIT(watt_per_centimeter_squared_per_steradian,
         divide_typeof_helper<watt_per_centimeter_squared, steradian>::type)
ADD_UNIT(
    milliwatt_per_centimeter_squared_per_steradian,
    divide_typeof_helper<milliwatt_per_centimeter_squared, steradian>::type)
ADD_UNIT(
    microwatt_per_centimeter_squared_per_steradian,
    divide_typeof_helper<microwatt_per_centimeter_squared, steradian>::type)
ADD_UNIT(nanowatt_per_centimeter_squared_per_steradian,
         divide_typeof_helper<nanowatt_per_centimeter_squared, steradian>::type)

ADD_UNIT(W_cm_n2_sr_n1, watt_per_centimeter_squared_per_steradian)
ADD_UNIT(mW_cm_n2_sr_n1, milliwatt_per_centimeter_squared_per_steradian)
ADD_UNIT(uW_cm_n2_sr_n1, microwatt_per_centimeter_squared_per_steradian)
ADD_UNIT(nW_cm_n2_sr_n1, nanowatt_per_centimeter_squared_per_steradian)

ADD_UNIT(watt_per_meter_squared_per_steradian,
         divide_typeof_helper<watt_per_meter_squared, steradian>::type)
ADD_UNIT(milliwatt_per_meter_squared_per_steradian,
         divide_typeof_helper<milliwatt_per_meter_squared, steradian>::type)
ADD_UNIT(microwatt_per_meter_squared_per_steradian,
         divide_typeof_helper<microwatt_per_meter_squared, steradian>::type)
ADD_UNIT(nanowatt_per_meter_squared_per_steradian,
         divide_typeof_helper<nanowatt_per_meter_squared, steradian>::type)

ADD_UNIT(W_m_n2_sr_n1, watt_per_meter_squared_per_steradian)
ADD_UNIT(mW_m_n2_sr_n1, milliwatt_per_meter_squared_per_steradian)
ADD_UNIT(uW_m_n2_sr_n1, microwatt_per_meter_squared_per_steradian)
ADD_UNIT(nW_m_n2_sr_n1, nanowatt_per_meter_squared_per_steradian)

ADD_UNIT_IO(watt_per_centimeter_squared_per_steradian,
            "watt per centimeter squared per steradian", "W cm^-2 sr^-1")

// Integrated Radiant Intensity
ADD_UNIT(joule_per_steradian, divide_typeof_helper<joule, steradian>::type)
ADD_UNIT(millijoule_per_steradian,
         divide_typeof_helper<millijoule, steradian>::type)
ADD_UNIT(microjoule_per_steradian,
         divide_typeof_helper<microjoule, steradian>::type)
ADD_UNIT(nanojoule_per_steradian,
         divide_typeof_helper<nanojoule, steradian>::type)

ADD_UNIT(J_sr_n1, joule_per_steradian)
ADD_UNIT(mJ_sr_n1, millijoule_per_steradian)
ADD_UNIT(uJ_sr_n1, microjoule_per_steradian)
ADD_UNIT(nJ_sr_n1, nanojoule_per_steradian)

ADD_UNIT_IO(joule_per_steradian, "joule per steradian", "J sr^-1")

// Radiant Intensity
ADD_UNIT(watt_per_steradian, divide_typeof_helper<watt, steradian>::type)
ADD_UNIT(milliwatt_per_steradian,
         divide_typeof_helper<milliwatt, steradian>::type)
ADD_UNIT(microwatt_per_steradian,
         divide_typeof_helper<microwatt, steradian>::type)
ADD_UNIT(nanowatt_per_steradian,
         divide_typeof_helper<nanowatt, steradian>::type)

ADD_UNIT(W_sr_n1, watt_per_steradian)
ADD_UNIT(mW_sr_n1, milliwatt_per_steradian)
ADD_UNIT(uW_sr_n1, microwatt_per_steradian)
ADD_UNIT(nW_sr_n1, nanowatt_per_steradian)

ADD_UNIT_IO(watt_per_steradian, "watt per steradian", "W sr^-1")

// Electric Potential
ADD_UNIT(volt, si::electric_potential)
ADD_UNIT(V, volt)

// Electric Field
ADD_UNIT(volt_per_meter, divide_typeof_helper<volt, meter>::type)
ADD_UNIT(V_m_n2, volt_per_meter)

ADD_UNIT(newton_per_coulomb, divide_typeof_helper<newton, coulomb>::type)
ADD_UNIT(N_C_n1, newton_per_coulomb)

#undef ADD_UNIT
#undef ADD_BASE_UNIT_SET
#undef ADD_UNIT_SET
#undef ADD_UNIT_IO

namespace boost
{
namespace units
{
namespace d
{
// define some dimensions
typedef t::meter::dimension_type Length;
typedef t::kilogram::dimension_type Mass;
typedef t::second::dimension_type Time;
typedef t::meter_squared::dimension_type Area;

typedef t::joule::dimension_type Energy;
typedef t::watt::dimension_type Power;
typedef t::joule_per_centimeter_squared::dimension_type RadiantExposure;
typedef t::watt_per_centimeter_squared::dimension_type Irradiance;
typedef t::joule_per_centimeter_squared_per_steradian::dimension_type
    IntegratedRadiance;
typedef t::watt_per_centimeter_squared_per_steradian::dimension_type Radiance;
typedef t::joule_per_steradian::dimension_type IntegratedRadiantIntensity;
typedef t::watt_per_steradian::dimension_type RadiantIntensity;
}  // namespace d

// function to convert from dimensionless to angle
template <typename U, typename T>
// 1. convert to dimensionless
// 2. cast to double
// 3. create radian by multiply
// 4. convert to return type
quantity<t::rad, T> d2a(quantity<U, T> a)
{
  return quantity<t::rad, T>(i::rad *
                             quantity_cast<T>(quantity<t::dimless, T>(a)));
}

// function to convert from angle to dimensionless
template <typename U, typename T>
// 1. convert to radian
// 2. cast to double
// 3. create dimensionless by multiply
// 4. convert to return type
quantity<t::dimless, T> a2d(quantity<U, T> a)
{
  return quantity<t::dimless, T>(i::dimless *
                                 quantity_cast<T>(quantity<t::radian, T>(a)));
}

}  // namespace units
}  // namespace boost

// we want to allow implicit conversions between
// units that are the same, but have differen types.
// for example, boost::units::t::radian is a scaled base unit based on
// angle::radian_base_unit, so it is not implicitly convertible to/from
// si::plane_angle, even through they represent the same unit. This is true for
// all of the base units as well. boost::units::t::second is based on
// si::second_base_unit, and cannot be implicitly converted to/from si::time.
// below are a list of conversions between types that represent the same unit
// that the user could reasonably expect to be the "same"
#define ADD_IMPLICITLY_CONVERTIBLE_UNITS(A, B)                \
  namespace boost                                             \
  {                                                           \
  namespace units                                             \
  {                                                           \
  template <>                                                 \
  struct is_implicitly_convertible<A, B> : boost::true_type { \
  };                                                          \
  template <>                                                 \
  struct is_implicitly_convertible<B, A> : boost::true_type { \
  };                                                          \
  }                                                           \
  }

// base units
ADD_IMPLICITLY_CONVERTIBLE_UNITS(boost::units::t::radian,
                                 angle::radian_base_unit)
ADD_IMPLICITLY_CONVERTIBLE_UNITS(boost::units::t::steradian,
                                 angle::steradian_base_unit)
ADD_IMPLICITLY_CONVERTIBLE_UNITS(boost::units::t::meter, si::meter_base_unit)
ADD_IMPLICITLY_CONVERTIBLE_UNITS(boost::units::t::gram, cgs::gram_base_unit)
ADD_IMPLICITLY_CONVERTIBLE_UNITS(boost::units::t::second, si::second_base_unit)
ADD_IMPLICITLY_CONVERTIBLE_UNITS(boost::units::t::kelvin, si::kelvin_base_unit)
ADD_IMPLICITLY_CONVERTIBLE_UNITS(boost::units::t::ampere, si::ampere_base_unit)

// si system
ADD_IMPLICITLY_CONVERTIBLE_UNITS(boost::units::t::meter, si::length)
ADD_IMPLICITLY_CONVERTIBLE_UNITS(boost::units::t::kilogram, si::mass)
ADD_IMPLICITLY_CONVERTIBLE_UNITS(boost::units::t::second, si::time)
ADD_IMPLICITLY_CONVERTIBLE_UNITS(boost::units::t::kelvin, si::temperature)
ADD_IMPLICITLY_CONVERTIBLE_UNITS(boost::units::t::ampere, si::current)
ADD_IMPLICITLY_CONVERTIBLE_UNITS(boost::units::t::hertz, si::frequency)
ADD_IMPLICITLY_CONVERTIBLE_UNITS(boost::units::t::meter_squared, si::area)

// cgs system
ADD_IMPLICITLY_CONVERTIBLE_UNITS(boost::units::t::centimeter, cgs::length)
ADD_IMPLICITLY_CONVERTIBLE_UNITS(boost::units::t::gram, cgs::mass)
ADD_IMPLICITLY_CONVERTIBLE_UNITS(boost::units::t::second, cgs::time)
ADD_IMPLICITLY_CONVERTIBLE_UNITS(boost::units::t::ampere, cgs::current)
ADD_IMPLICITLY_CONVERTIBLE_UNITS(boost::units::t::hertz, cgs::frequency)
ADD_IMPLICITLY_CONVERTIBLE_UNITS(boost::units::t::centimeter_squared,
                                 cgs::area)

// define multiplication between int's and quantity<unit>'s
// otherwise, we can't do this
// quantity<t::m> L(3.*m);
// quantity<t::m> L2 = 3*L; // error
// quantity<t::m> L2 = 3.*L; // OK
template <typename U, typename T>
boost::units::quantity<U, T> operator*(int a, boost::units::quantity<U, T> b)
{
  return static_cast<T>(a) * b;
}

template <typename U, typename T>
boost::units::quantity<U, T> operator*(boost::units::quantity<U, T> b, int a)
{
  return static_cast<T>(a) * b;
}

// define multiplication between int's and units, so that template type
// deduction will work.
// otherwise, we can't do this
//
// template<typename U>
// void Func( quantity<U> q );
// ...
// Func(3*m); // error
// Func(3.*m); // OK
template <typename U, typename T>
boost::units::quantity<boost::units::unit<U, T>> operator*(
    int a, boost::units::unit<U, T> u)
{
  return static_cast<double>(a) * u;
}

template <typename U, typename T>
boost::units::quantity<boost::units::unit<U, T>> operator*(
    boost::units::unit<U, T> u, int a)
{
  return static_cast<double>(a) * u;
}

// define division of quantity<unit>'s by ints
// otherwise, we can't do this
// quantity<t::m> L(3.*m);
// quantity<t::m> L2 = L/3; // error
// quantity<t::m> L2 = L/3.; // OK
template <typename U, typename T>
boost::units::quantity<U, T> operator/(boost::units::quantity<U, T> b, int a)
{
  return b / static_cast<T>(a);
}

// define division of int by quantity<unit>
// otherwise, we can't do this
// quantity<t::s> T(2.*s);
// quantity<t::Hz> L2 = 1/T; // error
// quantity<t::Hz> L2 = 1./T.; // OK
template <typename U, typename T>
boost::units::quantity<typename boost::units::divide_typeof_helper<
                           boost::units::t::dimensionless, U>::type,
                       T>
operator/(int a, boost::units::quantity<U, T> b)
{
  return static_cast<T>(a) / b;
}

#endif  // include protector


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

  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)
*/
//==================================================================================================
#include <boost/simd/function/scalar/bitwise_notand.hpp>
#include <scalar_test.hpp>
#include <boost/simd/detail/dispatch/meta/as_integer.hpp>
#include <boost/simd/constant/inf.hpp>
#include <boost/simd/constant/minf.hpp>
#include <boost/simd/constant/mone.hpp>
#include <boost/simd/constant/nan.hpp>
#include <boost/simd/constant/one.hpp>
#include <boost/simd/constant/zero.hpp>

STF_CASE_TPL (" bitwise_notand real",  STF_IEEE_TYPES)
{
  namespace bs = boost::simd;
  using bs::bitwise_notand;
  using r_t = decltype(bitwise_notand(T(), T()));

  // return type conformity test
  STF_EXPR_IS(bitwise_notand(T(), T()), T);

  // specific values tests
#ifndef BOOST_SIMD_NO_INVALIDS
  STF_EQUAL(bitwise_notand(bs::Inf<T>(), bs::Inf<T>()), bs::Zero<r_t>());
  STF_EQUAL(bitwise_notand(bs::Minf<T>(), bs::Minf<T>()), bs::Zero<r_t>());
  STF_EQUAL(bitwise_notand(bs::Nan<T>(), bs::Nan<T>()), bs::Zero<r_t>());
#endif
  STF_EQUAL(bitwise_notand(bs::Zero<T>(), bs::Zero<T>()), bs::Zero<r_t>());
  STF_EQUAL(bitwise_notand(bs::Zero<T>(),bs::One<T>()), bs::One<r_t>());
} // end of test for floating_

STF_CASE_TPL("bitwise_notand mix", STF_IEEE_TYPES)
{
  namespace bs = boost::simd;
  namespace bd = boost::dispatch;
  using bs::bitwise_notand;
  using siT = bd::as_integer_t<T>;
  using uiT = bd::as_integer_t<T, unsigned>;

  // return type conformity test
  STF_EXPR_IS(bitwise_notand(T(),uiT()), T);
  STF_EXPR_IS(bitwise_notand(T(),siT()), T);
  STF_EXPR_IS(bitwise_notand(uiT(), T()), uiT);
  STF_EXPR_IS(bitwise_notand(siT(), T()), siT);

  // specific values tests
  STF_IEEE_EQUAL(bitwise_notand(bs::Zero<T>(),bs::Valmax<uiT>()), bs::Nan<T>());
  STF_IEEE_EQUAL(bitwise_notand(bs::Zero<T>(), bs::Mone<siT>()), bs::Nan<T>());
  STF_EQUAL(bitwise_notand(bs::Zero<siT>(),bs::Nan<T>()), bs::Mone<siT>());
  STF_EQUAL(bitwise_notand(bs::Zero<uiT>(), bs::Nan<T>()), bs::Valmax<uiT>());
}