//==================================================================================================
/*!
  @file

  @copyright 2016 NumScale SAS

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================
#ifndef BOOST_SIMD_ARCH_COMMON_SIMD_FUNCTION_LDEXP_HPP_INCLUDED
#define BOOST_SIMD_ARCH_COMMON_SIMD_FUNCTION_LDEXP_HPP_INCLUDED
#include <boost/simd/detail/overload.hpp>

#include <boost/simd/meta/hierarchy/simd.hpp>
#include <boost/simd/meta/as_logical.hpp>
#include <boost/simd/config.hpp>
#include <boost/simd/detail/constant/limitexponent.hpp>
#include <boost/simd/detail/constant/maxexponent.hpp>
#include <boost/simd/constant/nbmantissabits.hpp>
#include <boost/simd/constant/one.hpp>
#include <boost/simd/function/bitwise_cast.hpp>
#include <boost/simd/function/is_equal.hpp>
#include <boost/simd/function/rshl.hpp>
#include <boost/simd/function/expocvt.hpp>
#include <boost/simd/function/if_plus.hpp>
#include <boost/simd/function/if_minus.hpp>
#include <boost/simd/function/is_flint.hpp>
#include <boost/simd/function/shift_left.hpp>
#include <boost/simd/function/toint.hpp>
#include <boost/simd/detail/dispatch/meta/as_integer.hpp>

#ifndef BOOST_SIMD_NO_DENORMALS
#include <boost/simd/detail/constant/minexponent.hpp>
#include <boost/simd/constant/smallestposval.hpp>
#include <boost/simd/function/if_else.hpp>
#include <boost/simd/function/is_less.hpp>
#include <boost/simd/function/if_minus.hpp>
#endif

namespace boost { namespace simd { namespace ext
{
   namespace bd = boost::dispatch;
   namespace bs = boost::simd;
   // a0 a1 integers
   BOOST_DISPATCH_OVERLOAD(ldexp_
                          , (typename A0, typename A1, typename X)
                          , bd::cpu_
                          , bs::pack_<bd::integer_<A0>, X>
                          , bs::pack_<bd::integer_<A1>, X>
                          )
   {
     BOOST_FORCEINLINE A0 operator()( const A0& a0, const  A1&  a1) const BOOST_NOEXCEPT
     {
       return rshl(a0, a1);
     }
   };

  BOOST_DISPATCH_OVERLOAD(ldexp_
                         , (typename A0, typename A1, typename X)
                         , bd::cpu_
                         , bs::pack_<bd::integer_<A0>, X>
                         , bd::scalar_<bd::integer_<A1>>
                         )
  {
    BOOST_FORCEINLINE A0 operator()( const A0& a0, A1  a1) const BOOST_NOEXCEPT
    {
      using sA0 = bd::scalar_of_t<A0>;
      using iA0 = bd::as_integer_t<sA0>;
      return rshl(a0, iA0(a1));
    }
  };

  // a0 floating
  BOOST_DISPATCH_OVERLOAD(ldexp_
                         , (typename A0, typename A1, typename X, typename Y)
                         , bd::cpu_
                         , bs::pedantic_tag
                         , bs::pack_<bd::floating_<A0>, X>
                         , bs::pack_<bd::integer_<A1>, Y>
                         )
  {
    BOOST_FORCEINLINE A0 operator()(const pedantic_tag &
                                   ,  const A0& a0, const  A1&  a1) const BOOST_NOEXCEPT
    {
      using iA0 = bd::as_integer_t<A0>;
      using sA0 = bd::scalar_of_t<A0>;
      iA0 e = a1;
      A0 f = One<A0>();
#ifndef BOOST_SIMD_NO_DENORMALS
      auto denormal =  is_less(e, Minexponent<A0>());
      e = if_minus(denormal, e, Minexponent<A0>());
      f = if_else(denormal, Smallestposval<A0>(), One<A0>());
#endif
      auto test = is_equal(e, Limitexponent<A0>());
      f = if_plus(test, f, One<A0>());
      e = if_minus(test, e, One<iA0>());
      e += Maxexponent<A0>();
      e = shift_left(e, Nbmantissabits<sA0>());
      return a0*bitwise_cast<A0>(e)*f;
    }
  };

   BOOST_DISPATCH_OVERLOAD(ldexp_
                          , (typename A0, typename A1, typename X)
                          , bd::cpu_
                          , bs::pedantic_tag
                          , bs::pack_<bd::floating_<A0>, X>
                          , bd::scalar_<bd::integer_<A1>>
                          )
   {
     BOOST_FORCEINLINE A0 operator()(const pedantic_tag &
                                    ,  const A0& a0, const  A1&  a1) const BOOST_NOEXCEPT
     {
       using iA0 = bd::as_integer_t<A0>;
       using sA0 =  bd::scalar_of_t<A0>;
       using siA0 = bd::scalar_of_t<iA0>;
       siA0 e = a1;
       A0 f = One<A0>();
#ifndef BOOST_SIMD_NO_DENORMALS
       auto denormal =  is_less(e, Minexponent<siA0>());
       e = if_minus(denormal, e, Minexponent<siA0>());
       f = if_else(denormal, Smallestposval<A0>(), One<A0>());
#endif
       if (is_equal(e, Limitexponent<siA0>()))
       {
         f+= One<sA0>();
         e-= One<siA0>();
       }
       e += Maxexponent<sA0>();
       e = shift_left(e, Nbmantissabits<sA0>());
       return a0*(bitwise_cast<sA0>(e)*f);
     }
   };

  BOOST_DISPATCH_OVERLOAD ( ldexp_
                          , (typename A0, typename A1, typename X, typename Y)
                          , bd::cpu_
                          , bs::pack_<bd::floating_<A0>, X>
                          , bs::pack_<bd::integer_<A1>, Y>
                          )
  {
    BOOST_FORCEINLINE A0 operator() (const A0& a0, const A1& a1) const BOOST_NOEXCEPT
    {
      using i_t = bd::as_integer_t<A0>;
      using sA0 = bd::scalar_of_t<A0>;
      i_t ik =  a1+Maxexponent<A0>();
      ik = shift_left(ik, Nbmantissabits<sA0>());
      return a0*bitwise_cast<A0>(ik);
    }
  };

  BOOST_DISPATCH_OVERLOAD ( ldexp_
                          , (typename A0, typename A1, typename X)
                          , bd::cpu_
                          , bs::pack_<bd::floating_<A0>, X>
                          , bd::scalar_<bd::integer_<A1>>
                          )
  {
    BOOST_FORCEINLINE A0 operator() (const A0& a0,  A1 a1) const BOOST_NOEXCEPT
    {
      using iA0 = bd::as_integer_t<A0>;
      using sA0 =  bd::scalar_of_t<A0>;
      using siA0 = bd::scalar_of_t<iA0>;
      siA0 ik =  a1+Maxexponent<sA0>();
      ik = shift_left(ik, Nbmantissabits<sA0>());
      return a0*A0(bitwise_cast<sA0>(ik));
    }
  };

  BOOST_DISPATCH_OVERLOAD ( ldexp_
                          , (typename A0, typename X)
                          , bd::cpu_
                          , bs::pack_<bd::floating_<A0>, X>
                          , bs::pack_<bd::floating_<A0>, X>
                          )
  {
    BOOST_FORCEINLINE A0 operator() ( const A0& a0, const A0& a1) const BOOST_NOEXCEPT
    {
      return ldexp(a0, toint(a1));
    }
  };

  BOOST_DISPATCH_OVERLOAD ( ldexp_
                          , (typename A0, typename X)
                          , bd::cpu_
                          , boost::simd::pedantic_tag
                          , bs::pack_<bd::floating_<A0>, X>
                          , bs::pack_<bd::floating_<A0>, X>
                          )
  {
    BOOST_FORCEINLINE A0 operator() ( const pedantic_tag &
                                    , const A0& a0, const A0& a1) const BOOST_NOEXCEPT
    {
      return pedantic_(ldexp)(a0, toint(a1));
    }
  };

  BOOST_DISPATCH_OVERLOAD ( ldexp_
                          , (typename A0, typename X)
                          , bd::cpu_
                          , bs::pack_<bd::double_<A0>, X>
                          , bs::pack_<bd::double_<A0>, X>
                          )
  {
    BOOST_FORCEINLINE A0 operator() (const A0& a0, const A0& a1) const BOOST_NOEXCEPT
    {
      return a0*expocvt(a1);
    }
  };

  BOOST_DISPATCH_OVERLOAD ( ldexp_
                          , (typename A0, typename A1, typename X)
                          , bd::cpu_
                          , bs::pedantic_tag
                          , bs::pack_<bd::single_<A0>, X>
                          , bd::scalar_<bd::single_<A1>>
                          )
  {
    BOOST_FORCEINLINE A0 operator() ( const pedantic_tag &
                                    , const A0& a0, const A1& a1) const BOOST_NOEXCEPT
    {
      return pedantic_(ldexp)(a0, toint(a1));
    }
  };

  BOOST_DISPATCH_OVERLOAD ( ldexp_
                          , (typename A0, typename A1, typename X)
                          , bd::cpu_
                          , bs::pedantic_tag
                          , bs::pack_<bd::double_<A0>, X>
                          , bd::scalar_<bd::double_<A1>>
                          )
  {
    BOOST_FORCEINLINE A0 operator() ( const pedantic_tag &
                                    , const A0& a0, const A1& a1) const BOOST_NOEXCEPT
    {
      return pedantic_(ldexp)(a0, toint(a1));
//     return a0*expocvt(a1);
    }
  };

  BOOST_DISPATCH_OVERLOAD ( ldexp_
                          , (typename A0, typename A1, typename X)
                          , bd::cpu_
                          , bs::pack_<bd::single_<A0>, X>
                          , bd::scalar_<bd::single_<A1>>
                          )
  {
    BOOST_FORCEINLINE A0 operator() (const A0& a0, const A1& a1) const BOOST_NOEXCEPT
    {
      return ldexp(a0, toint(a1));
    }
  };

  BOOST_DISPATCH_OVERLOAD ( ldexp_
                          , (typename A0, typename A1, typename X)
                          , bd::cpu_
                          , bs::pack_<bd::double_<A0>, X>
                          , bd::scalar_<bd::double_<A1>>
                          )
  {
    BOOST_FORCEINLINE A0 operator() ( const A0& a0, const A1& a1) const BOOST_NOEXCEPT
    {
      return a0*expocvt(a1);
    }
  };

} } }

#endif


/*
 * Copyright (c) 2013-2019 Masahide Kashiwagi (kashi@waseda.jp)
 */

#ifndef AFFINE_HPP
#define AFFINE_HPP

// Affine Arithmetic

#include <iostream>
#include <stdexcept>
#include <boost/numeric/ublas/vector.hpp>
#include <boost/numeric/ublas/matrix.hpp>
#include <boost/numeric/ublas/io.hpp>
#include <vector>
#include <algorithm>
#include <cmath>

#include <map> //@hylagi

#include <kv/interval.hpp>
#include <kv/rdouble.hpp>

#include <kv/convert.hpp>

/*
 * define simplicity of affine arithmetic
 *
 *   value of AFFINE_SIMPLE                   | 0 | 1 | 2
 *  ------------------------------------------+---+---+---
 *   add dummy epsilin on linear operation    | o | x | x
 *   add dummy epsilin on nonlinear operation | o | o | x
 *
 *  default: 1
 */

#ifndef AFFINE_SIMPLE
#define AFFINE_SIMPLE 1
#endif

/*
 * select the method for multiplication
 *
 *  0: Stolfi's simple method (default, O(n))
 *  1: better multiplication (give smaller extra epsilon but slow, O(n^2))
 *  2: best multiplication (give smallest extra epsilon but slow, O(n^2))
 */

#ifndef AFFINE_MULT
#define AFFINE_MULT 0
#endif

namespace kv {

namespace ub = boost::numeric::ublas;

template <class T> class affine;

template <class C, class T> struct acceptable_s<C, affine<T> > {
  static const bool value = boost::is_same<C, interval<T> >::value || boost::is_convertible<C, std::string>::value;
};

template <class C, class T> struct acceptable_n<C, affine<T> > {
  static const bool value = convertible<C, T>::value && (!acceptable_s<C, affine<T> >::value);
};

template <class C, class T> struct convertible<C, affine<T> > {
  static const bool value = acceptable_n<C, affine<T> >::value || acceptable_s<C, affine<T> >::value || boost::is_same<C, affine<T> >::value;
};

#if 0
template <class C, class T> struct acceptable_n<C, affine<T> > {
  static const bool value = convertible<C, T>::value && (!boost::is_same<C, interval<T> >::value) && (!boost::is_convertible<C, std::string>::value);
};

template <class C, class T> struct convertible<C, affine<T> > {
  static const bool value = convertible<C, T>::value || boost::is_same<C, interval<T> >::value || boost::is_convertible<C, std::string>::value || boost::is_same<C, affine<T> >::value;
};
#endif

template <class T> class affine {
  public:
  ub::vector<T> a;
  #if AFFINE_SIMPLE >= 1
  T er;
  #endif

  typedef T base_type;

  static int& maxnum() {
    static int m = 0;
    #pragma omp threadprivate(m)
    return m;
  }

  friend inline T rad(const affine& x) {
    int i, xs;
    T r(0.);

    xs = x.a.size();

    rop<T>::begin();
    for (i=1; i<xs; i++) {
      using std::abs;
      r = rop<T>::add_up(r, abs(x.a(i)));
    }
    #if AFFINE_SIMPLE >= 1
    r = rop<T>::add_up(r, x.er);
    #endif
    rop<T>::end();

    return r;
  }

  friend inline interval<T> to_interval(const affine& x) {
    T t1, t2, t3;

    t1 = rad(x);
    rop<T>::begin();
    t2 = rop<T>::sub_down(x.a(0), t1);
    t3 = rop<T>::add_up(x.a(0), t1);
    rop<T>::end();
    return interval<T>(t2, t3);
  }

  interval<T> as_interval() const{ // @hylagi modified
    T t1, t2, t3;
    t1 = rad(*this);
    rop<T>::begin();
    t2 = rop<T>::sub_down(a(0), t1);
    t3 = rop<T>::add_up(a(0), t1);
    rop<T>::end();
    return interval<T>(t2, t3);
  }

  affine() {
  }

  template <class C> explicit affine(const C& x, typename boost::enable_if_c< acceptable_n<C, affine>::value >::type* =0) {
    a.resize(1);
    a(0) = x;
    #if AFFINE_SIMPLE >= 1
    er = 0.;
    #endif
  }

  template <class C> explicit affine(const C& x, typename boost::enable_if_c< acceptable_s<C, affine>::value >::type* =0) {
    int i;
    interval<T> I(x);
    maxnum()++;
    a.resize(maxnum()+1);

    rop<T>::begin();
    a(0) = rop<T>::mul_up(rop<T>::add_up(I.upper(), I.lower()), T(0.5));
    a(maxnum()) = rop<T>::sub_up(a(0), I.lower());
    rop<T>::end();

    for (i=1; i<maxnum(); i++) a(i) = 0.;

    #if AFFINE_SIMPLE >= 1
    er = 0.;
    #endif
  }

  template <class C> typename boost::enable_if_c< acceptable_n<C, affine>::value, affine& >::type operator=(const C& x) {
    a.resize(1);
    a(0) = x;
    #if AFFINE_SIMPLE >= 1
    er = 0.;
    #endif

    return *this;
  }

  template <class C> typename boost::enable_if_c< acceptable_s<C, affine>::value, affine& >::type operator=(const C& x) {
    int i;
    interval<T> I(x);
    maxnum()++;
    a.resize(maxnum()+1);

    rop<T>::begin();
    a(0) = rop<T>::mul_up(rop<T>::add_up(I.upper(), I.lower()), T(0.5));
    a(maxnum()) = rop<T>::sub_up(a(0), I.lower());
    rop<T>::end();

    for (i=1; i<maxnum(); i++) a(i) = 0.;

    #if AFFINE_SIMPLE >= 1
    er = 0.;
    #endif

    return *this;
  }

  T get_coef (int i) const {
    if (i >= a.size()) return T(0.);
    else return a(i);
  }

  T get_mid() const {
    return a(0);
  }

  T get_err() const {
    #if AFFINE_SIMPLE >= 1
    return er;
    #else
    return T(0.);
    #endif
  }

  friend affine operator+(const affine& x, const affine& y) {
    affine r;
    int xs, ys, i;
    T err(0.);

    #if AFFINE_SIMPLE == 0
    maxnum()++;
    r.a.resize(maxnum()+1);
    #endif

    xs = x.a.size();
    ys = y.a.size();
    if (xs > ys) {
      #if AFFINE_SIMPLE >= 1
      r.a.resize(xs);
      #endif
      rop<T>::begin();
      for (i=0; i<ys; i++) {
        r.a(i) = rop<T>::add_down(x.a(i), y.a(i));
      }
      for (i=0; i<ys; i++) {
        err = rop<T>::add_up(err, rop<T>::sub_up(rop<T>::add_up(x.a(i), y.a(i)), r.a(i)));
      }
      rop<T>::end();

      for (i=ys; i<xs; i++) {
        r.a(i) = x.a(i);
      }
      #if AFFINE_SIMPLE == 0
      for (i=xs; i<maxnum(); i++) {
        r.a(i) = 0.;
      }
      #endif
    } else {
      #if AFFINE_SIMPLE >= 1
      r.a.resize(ys);
      #endif
      rop<T>::begin();
      for (i=0; i<xs; i++) {
        r.a(i) = rop<T>::add_down(x.a(i), y.a(i));
      }
      for (i=0; i<xs; i++) {
        err = rop<T>::add_up(err, rop<T>::sub_up(rop<T>::add_up(x.a(i), y.a(i)), r.a(i)));
      }
      rop<T>::end();

      for (i=xs; i<ys; i++) {
        r.a(i) = y.a(i);
      }
      #if AFFINE_SIMPLE == 0
      for (i=ys; i<maxnum(); i++) {
        r.a(i) = 0.;
      }
      #endif
    }
    #if AFFINE_SIMPLE >= 1
    rop<T>::begin();
    r.er = rop<T>::add_up(rop<T>::add_up(x.er, y.er), err);
    rop<T>::end();
    #else
    r.a(maxnum()) = err;
    #endif
    return r;
  }

  // same as operator+, but do not add extra epsilon.
  // This function can be used for adding affine variables
  // which have no common epsilons.

  friend affine append(const affine& x, const affine& y) {
    affine r;
    int xs, ys, i;

    xs = x.a.size();
    ys = y.a.size();

    if (xs > ys) {
      r.a.resize(xs);
      for (i=0; i<ys; i++) {
        r.a(i) = x.a(i) + y.a(i);
      }
      for (i=ys; i<xs; i++) {
        r.a(i) = x.a(i);
      }
    } else {
      r.a.resize(ys);
      for (i=0; i<xs; i++) {
        r.a(i) = x.a(i) + y.a(i);
      }
      for (i=xs; i<ys; i++) {
        r.a(i) = y.a(i);
      }
    }
    #if AFFINE_SIMPLE >= 1
    r.er = x.er + y.er;
    #endif
    return r;
  }

  template <class C> friend typename boost::enable_if_c< acceptable_n<C, affine>::value, affine >::type operator+(const affine& x, const C& y) {
    affine r;
    int xs, i;
    T err;

    xs = x.a.size();

    #if AFFINE_SIMPLE == 0
    maxnum()++;
    r.a.resize(maxnum()+1);
    #else
    r.a.resize(xs);
    #endif

    rop<T>::begin();
    r.a(0) = rop<T>::add_down(x.a(0), (T)y);
    err = rop<T>::sub_up(rop<T>::add_up(x.a(0), (T)y), r.a(0));
    rop<T>::end();

    for (i=1; i<xs; i++) {
      r.a(i) = x.a(i);
    }
    #if AFFINE_SIMPLE == 0
    for (i=xs; i<maxnum(); i++) {
      r.a(i) = 0.;
    }
    #endif
    #if AFFINE_SIMPLE >= 1
    rop<T>::begin();
    r.er = rop<T>::add_up(x.er, err);
    rop<T>::end();
    #else
    r.a(maxnum()) = err;
    #endif
    return r;
  }

  template <class C> friend typename boost::enable_if_c< acceptable_n<C, affine>::value, affine >::type operator+(const C& x, const affine& y) {
    affine r;
    int ys, i;
    T err;

    ys = y.a.size();

    #if AFFINE_SIMPLE == 0
    maxnum()++;
    r.a.resize(maxnum()+1);
    #else
    r.a.resize(ys);
    #endif

    rop<T>::begin();
    r.a(0) = rop<T>::add_down((T)x, y.a(0));
    err = rop<T>::sub_up(rop<T>::add_up((T)x, y.a(0)), r.a(0));
    rop<T>::end();

    for (i=1; i<ys; i++) {
      r.a(i) = y.a(i);
    }
    #if AFFINE_SIMPLE == 0
    for (i=ys; i<maxnum(); i++) {
      r.a(i) = 0.;
    }
    #endif
    #if AFFINE_SIMPLE >= 1
    rop<T>::begin();
    r.er = rop<T>::add_up(y.er, err);
    rop<T>::end();
    #else
    r.a(maxnum()) = err;
    #endif
    return r;
  }

  template <class C> friend typename boost::enable_if_c< acceptable_s<C, affine>::value, affine >::type operator+(const affine& x, const C& y) {
    return x + affine(y);
  }

  template <class C> friend typename boost::enable_if_c< acceptable_s<C, affine>::value, affine >::type operator+(const C& x, const affine& y) {
    return affine(x) + y;
  }

  friend affine& operator+=(affine& x, const affine& y) {
    x = x + y;
    return x;
  }

  template <class C> friend typename boost::enable_if_c< acceptable_n<C, affine>::value, affine& >::type operator+=(affine& x, const C& y) {
    x = x + y;
    return x;
  }

  template <class C> friend typename boost::enable_if_c< acceptable_s<C, affine>::value, affine& >::type operator+=(affine& x, const C& y) {
    x = x + y;
    return x;
  }

  friend affine operator-(const affine& x, const affine& y) {
    affine r;
    int xs, ys, i;
    T err(0.);

    #if AFFINE_SIMPLE == 0
    maxnum()++;
    r.a.resize(maxnum()+1);
    #endif

    xs = x.a.size();
    ys = y.a.size();
    if (xs > ys) {
      #if AFFINE_SIMPLE >= 1
      r.a.resize(xs);
      #endif
      rop<T>::begin();
      for (i=0; i<ys; i++) {
        r.a(i) = rop<T>::sub_down(x.a(i), y.a(i));
      }
      for (i=0; i<ys; i++) {
        err = rop<T>::add_up(err, rop<T>::sub_up(rop<T>::sub_up(x.a(i), y.a(i)), r.a(i)));
      }
      rop<T>::end();

      for (i=ys; i<xs; i++) {
        r.a(i) = x.a(i);
      }
      #if AFFINE_SIMPLE == 0
      for (i=xs; i<maxnum(); i++) {
        r.a(i) = 0.;
      }
      #endif
    } else {
      #if AFFINE_SIMPLE >= 1
      r.a.resize(ys);
      #endif
      rop<T>::begin();
      for (i=0; i<xs; i++) {
        r.a(i) = rop<T>::sub_down(x.a(i), y.a(i));
      }
      for (i=0; i<xs; i++) {
        err = rop<T>::add_up(err, rop<T>::sub_up(rop<T>::sub_up(x.a(i), y.a(i)), r.a(i)));
      }
      rop<T>::end();

      for (i=xs; i<ys; i++) {
        r.a(i) = - y.a(i);
      }
      #if AFFINE_SIMPLE == 0
      for (i=ys; i<maxnum(); i++) {
        r.a(i) = 0.;
      }
      #endif
    }
    #if AFFINE_SIMPLE >= 1
    rop<T>::begin();
    r.er = rop<T>::add_up(rop<T>::add_up(x.er, y.er), err);
    rop<T>::end();
    #else
    r.a(maxnum()) = err;
    #endif
    return r;
  }

  template <class C> friend typename boost::enable_if_c< acceptable_n<C, affine>::value, affine >::type operator-(const affine& x, const C& y) {
    affine r;
    int xs, i;
    T err;

    xs = x.a.size();

    #if AFFINE_SIMPLE == 0
    maxnum()++;
    r.a.resize(maxnum()+1);
    #else
    r.a.resize(xs);
    #endif

    rop<T>::begin();
    r.a(0) = rop<T>::sub_down(x.a(0), (T)y);
    err = rop<T>::sub_up(rop<T>::sub_up(x.a(0), (T)y), r.a(0));
    rop<T>::end();

    for (i=1; i<xs; i++) {
      r.a(i) = x.a(i);
    }
    #if AFFINE_SIMPLE == 0
    for (i=xs; i<maxnum(); i++) {
      r.a(i) = 0.;
    }
    #endif
    #if AFFINE_SIMPLE >= 1
    rop<T>::begin();
    r.er = rop<T>::add_up(x.er, err);
    rop<T>::end();
    #else
    r.a(maxnum()) = err;
    #endif
    return r;
  }

  template <class C> friend typename boost::enable_if_c< acceptable_n<C, affine>::value, affine >::type operator-(const C& x, const affine& y) {
    affine r;
    int ys, i;
    T err;

    ys = y.a.size();

    #if AFFINE_SIMPLE == 0
    maxnum()++;
    r.a.resize(maxnum()+1);
    #else
    r.a.resize(ys);
    #endif

    rop<T>::begin();
    r.a(0) = rop<T>::sub_down((T)x, y.a(0));
    err = rop<T>::sub_up(rop<T>::sub_up((T)x, y.a(0)), r.a(0));
    rop<T>::end();

    for (i=1; i<ys; i++) {
      r.a(i) = - y.a(i);
    }
    #if AFFINE_SIMPLE == 0
    for (i=ys; i<maxnum(); i++) {
      r.a(i) = 0.;
    }
    #endif
    #if AFFINE_SIMPLE >= 1
    rop<T>::begin();
    r.er = rop<T>::add_up(y.er, err);
    rop<T>::end();
    #else
    r.a(maxnum()) = err;
    #endif
    return r;
  }

  template <class C> friend typename boost::enable_if_c< acceptable_s<C, affine>::value, affine >::type operator-(const affine& x, const C& y) {
    return x - affine(y);
  }

  template <class C> friend typename boost::enable_if_c< acceptable_s<C, affine>::value, affine >::type operator-(const C& x, const affine& y) {
    return affine(x) - y;
  }

  friend affine& operator-=(affine& x, const affine& y) {
    x = x - y;
    return x;
  }

  template <class C> friend typename boost::enable_if_c< acceptable_n<C, affine>::value, affine& >::type operator-=(affine& x, const C& y) {
    x = x - y;
    return x;
  }

  template <class C> friend typename boost::enable_if_c< acceptable_s<C, affine>::value, affine& >::type operator-=(affine& x, const C& y) {
    x = x - y;
    return x;
  }

  friend affine operator-(const affine& x) {
    affine r;

    r.a = - x.a;
    #if AFFINE_SIMPLE >= 1
    r.er = x.er;
    #endif
    return r;
  }

  template <class C> friend typename boost::enable_if_c< acceptable_n<C, affine>::value, affine >::type operator*(const affine& x, const C& y) {
    affine r;
    int xs, i;
    T err(0.);

    xs = x.a.size();

    #if AFFINE_SIMPLE == 0
    maxnum()++;
    r.a.resize(maxnum()+1);
    #else
    r.a.resize(xs);
    #endif

    rop<T>::begin();
    for (i=0; i<xs; i++) {
      r.a(i) = rop<T>::mul_down(x.a(i), (T)y);
    }
    for (i=0; i<xs; i++) {
      err = rop<T>::add_up(err, rop<T>::sub_up(rop<T>::mul_up(x.a(i), (T)y), r.a(i)));
    }
    rop<T>::end();

    #if AFFINE_SIMPLE == 0
    for (i=xs; i<maxnum(); i++) r.a(i) = 0.;
    #endif
    #if AFFINE_SIMPLE >= 1
    rop<T>::begin();
    using std::abs;
    r.er = rop<T>::add_up(rop<T>::mul_up(x.er, T(abs(y))), err);
    rop<T>::end();
    #else
    r.a(maxnum()) = err;
    #endif

    return r;
  }

  template <class C> friend typename boost::enable_if_c< acceptable_n<C, affine>::value, affine >::type operator*(const C& x, const affine& y) {
    affine r;
    int ys, i;
    T err(0.);

    ys = y.a.size();

    #if AFFINE_SIMPLE == 0
    maxnum()++;
    r.a.resize(maxnum()+1);
    #else
    r.a.resize(ys);
    #endif

    rop<T>::begin();
    for (i=0; i<ys; i++) {
      r.a(i) = rop<T>::mul_down((T)x, y.a(i));
    }
    for (i=0; i<ys; i++) {
      err = rop<T>::add_up(err, rop<T>::sub_up(rop<T>::mul_up((T)x, y.a(i)), r.a(i)));
    }
    rop<T>::end();

    #if AFFINE_SIMPLE == 0
    for (i=ys; i<maxnum(); i++) r.a(i) = 0.;
    #endif
    #if AFFINE_SIMPLE >= 1
    rop<T>::begin();
    using std::abs;
    r.er = rop<T>::add_up(rop<T>::mul_up(y.er, T(abs(x))), err);
    rop<T>::end();
    #else
    r.a(maxnum()) = err;
    #endif

    return r;
  }

  template <class C> friend typename boost::enable_if_c< acceptable_s<C, affine>::value, affine >::type operator*(const affine& x, const C& y) {
    return x * affine(y);
  }

  template <class C> friend typename boost::enable_if_c< acceptable_s<C, affine>::value, affine >::type operator*(const C& x, const affine& y) {
    return affine(x) * y;
  }

  #if AFFINE_MULT >= 1

  static interval<T> bestmult_error(const affine& x, const affine& y)
  {
    int n, i, j, s;
    bool f1, f2;
    ub::vector<T> vx, vy;
    kv::interval<T> tmp, C, D, E, X, Y;

    n = std::max(x.a.size(), y.a.size()) - 1;

    #if AFFINE_SIMPLE >= 1
    vx.resize(n+3);
    vy.resize(n+3);
    #else
    vx.resize(n+1);
    vy.resize(n+1);
    #endif

    for (i=0; i<x.a.size(); i++) {
      vx(i) = x.a(i);
    }
    for (i=x.a.size(); i<=n; i++) {
      vx(i) = 0.;
    }
    for (i=0; i<y.a.size(); i++) {
      vy(i) = y.a(i);
    }
    for (i=y.a.size(); i<=n; i++) {
      vy(i) = 0.;
    }

    #if AFFINE_SIMPLE >= 1
    vx(n+1) = x.er;
    vy(n+1) = 0.;
    vx(n+2) = 0.;
    vy(n+2) = y.er;
    n += 2;
    #endif

    E = 0.;

    #if AFFINE_MULT == 1
    for (i=1; i<=n; i++) {
      X = vx(i);
      Y = vy(i);
      E += X * Y * interval<T>(0., 1.);
      for (j=i+1; j<=n; j++) {
        tmp = X * vy(j) + Y * vx(j);
        E += tmp * interval<T>(-1., 1.);
      }
    }
    #else // AFFINE_MULT == 1

    for (i=1; i<=n; i++) {
      X = vx(i);
      Y = vy(i);
      if (X == 0. && Y == 0.) continue;
      C = D = 0.;
      for (j=1; j<=n; j++) {
        if (j == i) continue;
        if (vy(j) == 0. && vx(j) == 0.) continue;
        tmp = X * vy(j) - Y * vx(j);
        if (tmp.lower() >= 0.) {
          C += vx(j);
          D += vy(j);
        } else if (tmp.upper() <= 0.) {
          C -= vx(j);
          D -= vy(j);
        } else {
          if ((X.upper() > 0. && vx(j) < 0.)
          || (X.lower() < 0. && vx(j) > 0.)) {
            X -= vx(j);
            Y -= vy(j);
          } else {
            X += vx(j);
            Y += vy(j);
          }
        }
      }
      E = interval<T>::hull(E, X * Y + (X * D + Y * C) + C * D);
      E = interval<T>::hull(E, X * Y - (X * D + Y * C) + C * D);
      if (!zero_in(X * Y) ){
        tmp = -0.5 * (X * D + Y * C) / (X * Y);
        if (overlap(tmp, interval<T>(-1., 1.))) {
          E = interval<T>::hull(E, X * Y * tmp * tmp + (X * D + Y * C) * tmp + C * D);
        }
      }
    }

    #endif // AFFINE_MULT == 1

    return E;
  }

  #endif // AFFINE_MULT >= 1

  friend affine operator*(const affine& x, const affine& y) {
    affine r;
    int i, j, xs, ys;
    T err;
    T tmp_u, tmp_l;

    // if (&x == &y) return square(x);

    xs = x.a.size();
    ys = y.a.size();

    #if AFFINE_SIMPLE != 2
    maxnum()++;
    r.a.resize(maxnum()+1);
    #else
    r.a.resize(std::max(xs, ys));
    #endif

    rop<T>::begin();
    r.a(0) = rop<T>::mul_down(x.a(0), y.a(0));
    err = rop<T>::sub_up(rop<T>::mul_up(x.a(0), y.a(0)), r.a(0));
    rop<T>::end();

    if (xs > ys) {
      rop<T>::begin();
      for (i=1; i<ys; i++) {
        r.a(i) = rop<T>::add_down(rop<T>::mul_down(y.a(0), x.a(i)), rop<T>::mul_down(x.a(0), y.a(i)));
      }
      for (i=ys; i<xs; i++) {
        r.a(i) = rop<T>::mul_down(y.a(0), x.a(i));
      }
      for (i=1; i<ys; i++) {
        err = rop<T>::add_up(err, rop<T>::sub_up(rop<T>::add_up(rop<T>::mul_up(y.a(0), x.a(i)), rop<T>::mul_up(x.a(0), y.a(i))), r.a(i)));
      }
      for (i=ys; i<xs; i++) {
        err = rop<T>::add_up(err, rop<T>::sub_up(rop<T>::mul_up(y.a(0), x.a(i)), r.a(i)));
      }
      rop<T>::end();
      #if AFFINE_SIMPLE != 2
      for (i=xs; i<maxnum(); i++) {
        r.a(i) = 0.;
      }
      #endif
    } else {
      rop<T>::begin();
      for (i=1; i<xs; i++) {
        r.a(i) = rop<T>::add_down(rop<T>::mul_down(y.a(0), x.a(i)), rop<T>::mul_down(x.a(0), y.a(i)));
      }
      for (i=xs; i<ys; i++) {
        r.a(i) = rop<T>::mul_down(x.a(0), y.a(i));
      }
      for (i=1; i<xs; i++) {
        err = rop<T>::add_up(err, rop<T>::sub_up(rop<T>::add_up(rop<T>::mul_up(y.a(0), x.a(i)), rop<T>::mul_up(x.a(0), y.a(i))), r.a(i)));
      }
      for (i=xs; i<ys; i++) {
        err = rop<T>::add_up(err, rop<T>::sub_up(rop<T>::mul_up(x.a(0), y.a(i)), r.a(i)));
      }
      rop<T>::end();
      #if AFFINE_SIMPLE != 2
      for (i=ys; i<maxnum(); i++) {
        r.a(i) = 0.;
      }
      #endif
    }

    #if AFFINE_MULT >= 1

    interval<T> E;
    E = r.a(0) + bestmult_error(x, y);
    rop<T>::begin();
    r.a(0) = rop<T>::mul_up(rop<T>::add_up(E.upper(), E.lower()), 0.5);
    err = rop<T>::add_up(err, rop<T>::sub_up(r.a(0), E.lower()));
    rop<T>::end();

    #else // AFFINE_MULT >= 1

    tmp_l = rad(x);
    tmp_u = rad(y);
    rop<T>::begin();
    err = rop<T>::add_up(err, rop<T>::mul_up(tmp_l, tmp_u));
    rop<T>::end();

    #endif // AFFINE_MULT >= 1

    #if AFFINE_SIMPLE >= 1
    rop<T>::begin();
    using std::abs;
    err = rop<T>::add_up(err, rop<T>::add_up(rop<T>::mul_up(abs(y.a(0)), x.er), rop<T>::mul_up(abs(x.a(0)), y.er)));
    rop<T>::end();
    #endif

    #if AFFINE_SIMPLE == 2
    r.er = err;
    #else
    r.a(maxnum()) = err;
    # if AFFINE_SIMPLE == 1
    r.er = 0.;
    # endif
    #endif

    return r;
  }

  friend affine& operator*=(affine& x, const affine& y) {
    x = x * y;
    return x;
  }

  template <class C> friend typename boost::enable_if_c< acceptable_n<C, affine>::value, affine& >::type operator*=(affine& x, const C& y) {
    x = x * y;
    return x;
  }

  template <class C> friend typename boost::enable_if_c< acceptable_s<C, affine>::value, affine& >::type operator*=(affine& x, const C& y) {
    x = x * y;
    return x;
  }

  friend affine inv(const affine& x) {
    affine r;
    T err;
    interval<T> I, tmp, range;
    T a, b, l, u;
    int i, xs;

    xs = x.a.size();

    #if AFFINE_SIMPLE == 2
    r.a.resize(xs);
    #else
    maxnum()++;
    r.a.resize(maxnum()+1);
    #endif

    I = to_interval(x);
    l = I.lower();
    u = I.upper();
    if (l < 0. && u > 0.) {
      throw std::domain_error("affine: division by 0");
    }

    a = -1. /(l * u);
    tmp = a; // tmp is used to force interval calculation
    if (u > 0.) {
      range = 2. * sqrt(-tmp);
    } else {
      range = -2. * sqrt(-tmp);
    }
    tmp = l;
    range = interval<T>::hull(range, 1./tmp - a * tmp);
    tmp = u;
    range = interval<T>::hull(range, 1./tmp - a * tmp);

    rop<T>::begin();
    b = rop<T>::mul_up(rop<T>::add_up(range.upper(), range.lower()), T(0.5));
    err = rop<T>::sub_up(b, range.lower());
    for (i=1; i<xs; i++) {
      r.a(i) = rop<T>::mul_down(x.a(i), a);
    }
    for (i=1; i<xs; i++) {
      err = rop<T>::add_up(err, rop<T>::sub_up(rop<T>::mul_up(x.a(i), a), r.a(i)));
    }
    #if AFFINE_SIMPLE != 2
    for (i=xs; i<maxnum(); i++) r.a(i) = 0.;
    #endif
    l = rop<T>::add_down(rop<T>::mul_down(x.a(0), a), b);
    u = rop<T>::add_up(rop<T>::mul_up(x.a(0), a), b);
    r.a(0) = rop<T>::mul_up(rop<T>::add_up(l, u), T(0.5));
    err = rop<T>::add_up(err, rop<T>::sub_up(r.a(0), l));
    #if AFFINE_SIMPLE >= 1
    // err += abs(a) * x.er;
    using std::abs;
    err = rop<T>::add_up(err, rop<T>::mul_up(abs(a), x.er));
    #endif
    rop<T>::end();

    #if AFFINE_SIMPLE == 2
    r.er = err;
    #else
    r.a(maxnum()) = err;
    # if AFFINE_SIMPLE == 1
    r.er = 0.;
    # endif
    #endif

    return r;
  }

  friend affine operator/(const affine& x, const affine& y) {
    return x * inv(y);
  }

  template <class C> friend typename boost::enable_if_c< acceptable_n<C, affine>::value, affine >::type operator/(const C& x, const affine& y) {
    return x * inv(y);
  }

  template <class C> friend typename boost::enable_if_c< acceptable_n<C, affine>::value, affine >::type operator/(const affine& x, const C& y) {
    affine r;
    int xs, i;
    T err(0.);

    xs = x.a.size();

    #if AFFINE_SIMPLE == 0
    maxnum()++;
    r.a.resize(maxnum()+1);
    #else
    r.a.resize(xs);
    #endif

    rop<T>::begin();
    for (i=0; i<xs; i++) {
      r.a(i) = rop<T>::div_down(x.a(i), (T)y);
    }
    for (i=0; i<xs; i++) {
      err = rop<T>::add_up(err, rop<T>::sub_up(rop<T>::div_up(x.a(i), (T)y), r.a(i)));
    }
    rop<T>::end();

    #if AFFINE_SIMPLE == 0
    for (i=xs; i<maxnum(); i++) r.a(i) = 0.;
    #endif
    #if AFFINE_SIMPLE >= 1
    // r.er = x.er / abs(y) + err;
    rop<T>::begin();
    using std::abs;
    r.er = rop<T>::add_up(err, rop<T>::div_up(x.er, T(abs(y))));
    rop<T>::end();
    #else
    r.a(maxnum()) = err;
    #endif

    return r;
  }

  template <class C> friend typename boost::enable_if_c< acceptable_s<C, affine>::value, affine >::type operator/(const affine& x, const C& y) {
    return x / affine(y);
  }

  template <class C> friend typename boost::enable_if_c< acceptable_s<C, affine>::value, affine >::type operator/(const C& x, const affine& y) {
    return affine(x) / y;
  }

  friend affine& operator/=(affine& x, const affine& y) {
    x = x / y;
    return x;
  }

  template <class C> friend typename boost::enable_if_c< acceptable_n<C, affine>::value, affine& >::type operator/=(affine& x, const C& y) {
    x = x / y;
    return x;
  }

  template <class C> friend typename boost::enable_if_c< acceptable_s<C, affine>::value, affine& >::type operator/=(affine& x, const C& y) {
    x = x / y;
    return x;
  }

  friend affine sqrt(const affine& x) {
    affine r;
    T err;
    interval<T> I, tmp, range;
    T a, b, l, u;
    int i, xs;

    xs = x.a.size();

    #if AFFINE_SIMPLE == 2
    r.a.resize(xs);
    #else
    maxnum()++;
    r.a.resize(maxnum()+1);
    #endif

    I = to_interval(x);
    l = I.lower();
    u = I.upper();
    if (l < 0.) {
      throw std::domain_error("affine: sqrt of negative value");

    }

    using std::sqrt;
    a = 1. /(sqrt(l) + sqrt(u));
    tmp = a;
    range = 1. / (4. * tmp);
    tmp = l;
    range = interval<T>::hull(range, sqrt(tmp) - a * tmp);
    tmp = u;
    range = interval<T>::hull(range, sqrt(tmp) - a * tmp);

    rop<T>::begin();
    b = rop<T>::mul_up(rop<T>::add_up(range.upper(), range.lower()), T(0.5));
    err = rop<T>::sub_up(b, range.lower());
    for (i=1; i<xs; i++) {
      r.a(i) = rop<T>::mul_down(x.a(i), a);
    }
    for (i=1; i<xs; i++) {
      err = rop<T>::add_up(err, rop<T>::sub_up(rop<T>::mul_up(x.a(i), a), r.a(i)));
    }
    #if AFFINE_SIMPLE != 2
    for (i=xs; i<maxnum(); i++) r.a(i) = 0.;
    #endif
    l = rop<T>::add_down(rop<T>::mul_down(x.a(0), a), b);
    u = rop<T>::add_up(rop<T>::mul_up(x.a(0), a), b);
    r.a(0) = rop<T>::mul_up(rop<T>::add_up(l, u), T(0.5));
    err = rop<T>::add_up(err, rop<T>::sub_up(r.a(0), l));
    #if AFFINE_SIMPLE >= 1
    // err += abs(a) * x.er;
    using std::abs;
    err = rop<T>::add_up(err, rop<T>::mul_up(abs(a), x.er));
    #endif
    rop<T>::end();

    #if AFFINE_SIMPLE == 2
    r.er = err;
    #else
    r.a(maxnum()) = err;
    # if AFFINE_SIMPLE == 1
    r.er = 0.;
    # endif
    #endif

    return r;
  }

  friend affine square(const affine& x) {
    affine r;
    T err;
    interval<T> I, tmp, range;
    T a, b, l, u;
    int i, xs;

    xs = x.a.size();

    #if AFFINE_SIMPLE == 2
    r.a.resize(xs);
    #else
    maxnum()++;
    r.a.resize(maxnum()+1);
    #endif

    I = to_interval(x);
    l = I.lower();
    u = I.upper();

    a = l + u;
    tmp = a;
    range = - tmp * tmp * 0.25;
    tmp = l;
    // range = hull(range, tmp * tmp - a * tmp);
    range = interval<T>::hull(range, tmp * (tmp - a));
    tmp = u;
    range = interval<T>::hull(range, tmp * (tmp - a));

    rop<T>::begin();
    b = rop<T>::mul_up(rop<T>::add_up(range.upper(), range.lower()), T(0.5));
    err = rop<T>::sub_up(b, range.lower());
    for (i=1; i<xs; i++) {
      r.a(i) = rop<T>::mul_down(x.a(i), a);
    }
    for (i=1; i<xs; i++) {
      err = rop<T>::add_up(err, rop<T>::sub_up(rop<T>::mul_up(x.a(i), a), r.a(i)));
    }
    #if AFFINE_SIMPLE != 2
    for (i=xs; i<maxnum(); i++) r.a(i) = 0.;
    #endif
    l = rop<T>::add_down(rop<T>::mul_down(x.a(0), a), b);
    u = rop<T>::add_up(rop<T>::mul_up(x.a(0), a), b);
    r.a(0) = rop<T>::mul_up(rop<T>::add_up(l, u), T(0.5));
    err = rop<T>::add_up(err, rop<T>::sub_up(r.a(0), l));
    #if AFFINE_SIMPLE >= 1
    // err += abs(a) * x.er;
    using std::abs;
    err = rop<T>::add_up(err, rop<T>::mul_up(abs(a), x.er));
    #endif
    rop<T>::end();

    #if AFFINE_SIMPLE == 2
    r.er = err;
    #else
    r.a(maxnum()) = err;
    # if AFFINE_SIMPLE == 1
    r.er = 0.;
    # endif
    #endif

    return r;
  }

  friend affine exp(const affine& x) {
    affine r;
    T err;
    interval<T> I, tmp, range;
    T a, b, l, u;
    int i, xs;

    xs = x.a.size();

    #if AFFINE_SIMPLE == 2
    r.a.resize(xs);
    #else
    maxnum()++;
    r.a.resize(maxnum()+1);
    #endif

    I = to_interval(x);
    l = I.lower();
    u = I.upper();

    using std::exp;
    if (u == l) a = exp(u);
    else a = (exp(u) - exp(l)) / (u - l);
    tmp = a;
    range = tmp * (1. - log(tmp));
    tmp = l;
    range = interval<T>::hull(range, exp(tmp) - a * tmp);
    tmp = u;
    range = interval<T>::hull(range, exp(tmp) - a * tmp);

    rop<T>::begin();
    b = rop<T>::mul_up(rop<T>::add_up(range.upper(), range.lower()), T(0.5));
    err = rop<T>::sub_up(b, range.lower());
    for (i=1; i<xs; i++) {
      r.a(i) = rop<T>::mul_down(x.a(i), a);
    }
    for (i=1; i<xs; i++) {
      err = rop<T>::add_up(err, rop<T>::sub_up(rop<T>::mul_up(x.a(i), a), r.a(i)));
    }
    #if AFFINE_SIMPLE != 2
    for (i=xs; i<maxnum(); i++) r.a(i) = 0.;
    #endif
    l = rop<T>::add_down(rop<T>::mul_down(x.a(0), a), b);
    u = rop<T>::add_up(rop<T>::mul_up(x.a(0), a), b);
    r.a(0) = rop<T>::mul_up(rop<T>::add_up(l, u), T(0.5));
    err = rop<T>::add_up(err, rop<T>::sub_up(r.a(0), l));
    #if AFFINE_SIMPLE >= 1
    // err += abs(a) * x.er;
    using std::abs;
    err = rop<T>::add_up(err, rop<T>::mul_up(abs(a), x.er));
    #endif
    rop<T>::end();

    #if AFFINE_SIMPLE == 2
    r.er = err;
    #else
    r.a(maxnum()) = err;
    # if AFFINE_SIMPLE == 1
    r.er = 0.;
    # endif
    #endif

    return r;
  }

  friend affine log(const affine& x) {
    affine r;
    T err;
    interval<T> I, tmp, range;
    T a, b, l, u;
    int i, xs;

    xs = x.a.size();

    #if AFFINE_SIMPLE == 2
    r.a.resize(xs);
    #else
    maxnum()++;
    r.a.resize(maxnum()+1);
    #endif

    I = to_interval(x);
    l = I.lower();
    u = I.upper();

    if (l <= 0.) {
      throw std::domain_error("affine: log of nagative value");
    }

    using std::log;
    if (u == l) a = 1. / u;
    else a = (log(u) - log(l)) / (u - l);
    tmp = a;
    range = log(1. / tmp) - 1.;
    tmp = l;
    range = interval<T>::hull(range, log(tmp) - a * tmp);
    tmp = u;
    range = interval<T>::hull(range, log(tmp) - a * tmp);

    rop<T>::begin();
    b = rop<T>::mul_up(rop<T>::add_up(range.upper(), range.lower()), T(0.5));
    err = rop<T>::sub_up(b, range.lower());
    for (i=1; i<xs; i++) {
      r.a(i) = rop<T>::mul_down(x.a(i), a);
    }
    for (i=1; i<xs; i++) {
      err = rop<T>::add_up(err, rop<T>::sub_up(rop<T>::mul_up(x.a(i), a), r.a(i)));
    }
    #if AFFINE_SIMPLE != 2
    for (i=xs; i<maxnum(); i++) r.a(i) = 0.;
    #endif
    l = rop<T>::add_down(rop<T>::mul_down(x.a(0), a), b);
    u = rop<T>::add_up(rop<T>::mul_up(x.a(0), a), b);
    r.a(0) = rop<T>::mul_up(rop<T>::add_up(l, u), T(0.5));
    err = rop<T>::add_up(err, rop<T>::sub_up(r.a(0), l));
    #if AFFINE_SIMPLE >= 1
    // err += abs(a) * x.er;
    using std::abs;
    err = rop<T>::add_up(err, rop<T>::mul_up(abs(a), x.er));
    #endif
    rop<T>::end();

    #if AFFINE_SIMPLE == 2
    r.er = err;
    #else
    r.a(maxnum()) = err;
    # if AFFINE_SIMPLE == 1
    r.er = 0.;
    # endif
    #endif

    return r;
  }

  friend affine abs(const affine& x) {
    affine r;
    T err;
    interval<T> I, range;
    T a, b, l, u;
    int i, xs;

    I = to_interval(x);
    l = I.lower();
    u = I.upper();

    if (l >= 0.) {
      return x;
    }
    if (u <= 0.) {
      return -x;
    }

    xs = x.a.size();

    #if AFFINE_SIMPLE == 2
    r.a.resize(xs);
    #else
    maxnum()++;
    r.a.resize(maxnum()+1);
    #endif

    a = (u + l) / (u - l);

    range = 0.;
    range = interval<T>::hull(range, u - a * u);
    range = interval<T>::hull(range, -l - a * l);

    rop<T>::begin();
    b = rop<T>::mul_up(rop<T>::add_up(range.upper(), range.lower()), T(0.5));
    err = rop<T>::sub_up(b, range.lower());
    for (i=1; i<xs; i++) {
      r.a(i) = rop<T>::mul_down(x.a(i), a);
    }
    for (i=1; i<xs; i++) {
      err = rop<T>::add_up(err, rop<T>::sub_up(rop<T>::mul_up(x.a(i), a), r.a(i)));
    }
    #if AFFINE_SIMPLE != 2
    for (i=xs; i<maxnum(); i++) r.a(i) = 0.;
    #endif
    l = rop<T>::add_down(rop<T>::mul_down(x.a(0), a), b);
    u = rop<T>::add_up(rop<T>::mul_up(x.a(0), a), b);
    r.a(0) = rop<T>::mul_up(rop<T>::add_up(l, u), T(0.5));
    err = rop<T>::add_up(err, rop<T>::sub_up(r.a(0), l));
    #if AFFINE_SIMPLE >= 1
    // err += abs(a) * x.er;
    using std::abs;
    err = rop<T>::add_up(err, rop<T>::mul_up(abs(a), x.er));
    #endif
    rop<T>::end();

    #if AFFINE_SIMPLE == 2
    r.er = err;
    #else
    r.a(maxnum()) = err;
    # if AFFINE_SIMPLE == 1
    r.er = 0.;
    # endif
    #endif

    return r;
  }

  // lazy implementation of integer pow
  friend affine pow(const affine& x, int y) {
    affine r, xp;
    int a, tmp;

    if (y == 0) return affine(1.);

    a = (y >= 0) ? y : -y;

    tmp = a;
    r = 1.;
    xp = x;
    while (tmp != 0) {
      if (tmp % 2 != 0) {
        r *= xp;
      }
      tmp /= 2;
      xp = xp * xp;
    }

    if (y < 0) {
      r = 1. / r;
    }

    return r;
  }

  friend affine pow(const affine& x, const affine& y) {
    return exp(y * log(x));
  }

  template <class C> friend typename boost::enable_if_c< acceptable_n<C, affine>::value && ! boost::is_integral<C>::value, affine >::type pow(const affine& x, const C& y) {
    return pow(x, affine(y));
  }

  template <class C> friend typename boost::enable_if_c< acceptable_s<C, affine>::value, affine >::type pow(const affine& x, const C& y) {
    return pow(x, affine(y));
  }

  template <class C> friend typename boost::enable_if_c< acceptable_n<C, affine>::value, affine >::type pow(const C& x, const affine& y) {
    return pow(affine(x), y);
  }

  template <class C> friend typename boost::enable_if_c< acceptable_s<C, affine>::value, affine >::type pow(const C& x, const affine& y) {
    return pow(affine(x), y);
  }

  // lazy implementation of sin
  friend affine sin(const affine& x) {
    // return sin((interval<T>)(x.a(0))) + cos(to_interval(x)) * (x - x.a(0));

    affine tmp;
    interval<T> r, r2;
    T m;

    tmp = x;
    tmp.a(0) = 0.;
    r = sin((interval<T>)(x.a(0)));
    r2 = cos(to_interval(x));
    m = mid(r2);
    r += (r2 - m) * to_interval(tmp);

    return r + m * tmp;
  }

  // lazy implementation of cos
  friend affine cos(const affine& x) {
    // return cos((interval<T>)(x.a(0))) - sin(to_interval(x)) * (x - x.a(0));

    affine tmp;
    interval<T> r, r2;
    T m;

    tmp = x;
    tmp.a(0) = 0.;
    r = cos((interval<T>)(x.a(0)));
    r2 = -sin(to_interval(x));
    m = mid(r2);
    r += (r2 - m) * to_interval(tmp);

    return r + m * tmp;
  }

  // lazy implementation of tan
  friend affine tan(const affine& x) {
    // return tan((interval<T>)(x.a(0))) + pow(cos(to_interval(x)), -2) * (x - x.a(0));

    affine tmp;
    interval<T> r, r2;
    T m;

    tmp = x;
    tmp.a(0) = 0.;
    r = tan((interval<T>)(x.a(0)));
    r2 = pow(cos(to_interval(x)), -2);
    m = mid(r2);
    r += (r2 - m) * to_interval(tmp);

    return r + m * tmp;
  }

  // lazy implementation of asin
  friend affine asin(const affine& x) {
    // return asin((interval<T>)(x.a(0))) + (1. / sqrt(1. - pow(to_interval(x), 2))) * (x - x.a(0));

    affine tmp;
    interval<T> r, r2;
    T m;

    tmp = x;
    tmp.a(0) = 0.;
    r = asin((interval<T>)(x.a(0)));
    r2 = 1. / sqrt(1. - pow(to_interval(x), 2));
    m = mid(r2);
    r += (r2 - m) * to_interval(tmp);

    return r + m * tmp;
  }

  // lazy implementation of acos
  friend affine acos(const affine& x) {
    // return acos((interval<T>)(x.a(0))) + (- 1. / sqrt(1. - pow(to_interval(x), 2))) * (x - x.a(0));

    affine tmp;
    interval<T> r, r2;
    T m;

    tmp = x;
    tmp.a(0) = 0.;
    r = acos((interval<T>)(x.a(0)));
    r2 = - 1. / sqrt(1. - pow(to_interval(x), 2));
    m = mid(r2);
    r += (r2 - m) * to_interval(tmp);

    return r + m * tmp;
  }

  // lazy implementation of atan
  friend affine atan(const affine& x) {
    // return atan((interval<T>)(x.a(0))) + (1. / sqrt(1. + pow(to_interval(x), 2))) * (x - x.a(0));

    affine tmp;
    interval<T> r, r2;
    T m;

    tmp = x;
    tmp.a(0) = 0.;
    r = atan((interval<T>)(x.a(0)));
    r2 = 1. / sqrt(1. + pow(to_interval(x), 2));
    m = mid(r2);
    r += (r2 - m) * to_interval(tmp);

    return r + m * tmp;
  }

  // lazy implementation of sinh
  friend affine sinh(const affine& x) {
    // return sinh((interval<T>)(x.a(0))) + cosh(to_interval(x)) * (x - x.a(0));

    affine tmp;
    interval<T> r, r2;
    T m;

    tmp = x;
    tmp.a(0) = 0.;
    r = sinh((interval<T>)(x.a(0)));
    r2 = cosh(to_interval(x));
    m = mid(r2);
    r += (r2 - m) * to_interval(tmp);

    return r + m * tmp;
  }

  // lazy implementation of cosh
  friend affine cosh(const affine& x) {
    // return cosh((interval<T>)(x.a(0))) + sinh(to_interval(x)) * (x - x.a(0));

    affine tmp;
    interval<T> r, r2;
    T m;

    tmp = x;
    tmp.a(0) = 0.;
    r = cosh((interval<T>)(x.a(0)));
    r2 = sinh(to_interval(x));
    m = mid(r2);
    r += (r2 - m) * to_interval(tmp);

    return r + m * tmp;
  }

  // lazy implementation of tanh
  friend affine tanh(const affine& x) {
    // return tanh((interval<T>)(x.a(0))) + pow(cosh(to_interval(x)), -2) * (x - x.a(0));

    affine tmp;
    interval<T> r, r2;
    T m;

    tmp = x;
    tmp.a(0) = 0.;
    r = tanh((interval<T>)(x.a(0)));
    r2 = pow(cosh(to_interval(x)), -2);
    m = mid(r2);
    r += (r2 - m) * to_interval(tmp);

    return r + m * tmp;
  }

  // lazy implementation of asinh
  friend affine asinh(const affine& x) {
    // return asinh((interval<T>)(x.a(0))) + (1 / sqrt(pow(to_interval(x), 2) + 1)) * (x - x.a(0));

    affine tmp;
    interval<T> r, r2;
    T m;

    tmp = x;
    tmp.a(0) = 0.;
    r = asinh((interval<T>)(x.a(0)));
    r2 = 1 / sqrt(pow(to_interval(x), 2) + 1);
    m = mid(r2);
    r += (r2 - m) * to_interval(tmp);

    return r + m * tmp;
  }

  // lazy implementation of acosh
  friend affine acosh(const affine& x) {
    // return acosh((interval<T>)(x.a(0))) + (1 / sqrt(pow(to_interval(x), 2) - 1)) * (x - x.a(0));

    affine tmp;
    interval<T> r, r2;
    T m;

    tmp = x;
    tmp.a(0) = 0.;
    r = acosh((interval<T>)(x.a(0)));
    r2 = 1 / sqrt(pow(to_interval(x), 2) - 1);
    m = mid(r2);
    r += (r2 - m) * to_interval(tmp);

    return r + m * tmp;
  }

  // lazy implementation of atanh
  friend affine atanh(const affine& x) {
    // return atanh((interval<T>)(x.a(0))) + (1 / (1 - pow(to_interval(x), 2))) * (x - x.a(0));

    affine tmp;
    interval<T> r, r2;
    T m;

    tmp = x;
    tmp.a(0) = 0.;
    r = atanh((interval<T>)(x.a(0)));
    r2 = 1 / (1 - pow(to_interval(x), 2));
    m = mid(r2);
    r += (r2 - m) * to_interval(tmp);

    return r + m * tmp;
  }

  friend std::ostream& operator<<(std::ostream& s, const affine& x) {
    int i;

    s << "[(" << x.a(0) << ")";
    for (i=1; i<x.a.size(); i++) {
      s << "+(" << x.a(i) << ")e" << i;
    }
    #if AFFINE_SIMPLE >= 1
    s << "+(" << x.er << ")er";
    #endif
    s << "]";

    return s;
  }

  friend inline void split(const affine& x, int n, affine& y, affine& z) {
    int i;
    int s = x.a.size();

    y.a.resize(s);
    z.a.resize(s);
    for (i=0; i<=n; i++) {
      y.a(i) = x.a(i);
      z.a(i) = 0.;
    }
    for (i=n+1; i<s; i++) {
      y.a(i) = 0.;
      z.a(i) = x.a(i);
    }
    #if AFFINE_SIMPLE >= 1
    y.er = 0.;
    z.er = x.er;
    #endif
  }

  void resize() {
    int i;
    ub::vector<T> r;

    r.resize(maxnum()+1);
    for (i=0; i<=maxnum(); i++) {
      r(i) = a(i);
    }

    a = r;
  }
};

template <class T> inline ub::vector< interval<T> > to_interval(const ub::vector< affine<T> >& x) {
  int s = x.size();
  ub::vector< interval<T> > r;
  int i;

  r.resize(s);
  for (i=0; i<s; i++) r(i) = to_interval(x(i));

  return r;
}

/*
 * epsilon_reduce(x, n, n_limit)
 *  x: vector of affine (overwrited)
 *  reduce the number of epsilons to n if the number of epsilons > n_limit.
 *
 *  s: size of x
 *  keep n-s "important" epsilons and convert other "non-important" epsilons
 *  to s-dimensional rectangular.
 */

// a class to store column vector and its "importance"

template <class T> class ep_reduce_v {
  public:
  ub::vector<T> v;
  T score;
  int index; // @hylagi to identify intervalized dummy variables
  void calc_score() {
    int s = v.size();
    int i, j;
    T m1, m2, tmp;
    using std::abs;
    if (s<2){score = abs(v(0)); return;} // @hylagi
    m1 = abs(v(0));
    if (s == 1) {
      score = m1;
      return;
    }
    m2 = abs(v(1));
    if (m2 > m1) {
      tmp = m2; m2 = m1; m1 = tmp;
    }
    for (i=2; i<s; i++) {
      tmp = abs(v(i));
      if (tmp > m1) {
        m2 = m1; m1 = tmp;
      } else if (tmp > m2) {
        m2 = tmp;
      }
    }
    if (m1 == 0.) score = 0.;
    else score = (m1*m2)/(m1+m2);
  }
};

// function to sort column vector by score

template <class T> inline bool ep_reduce_cmp(ep_reduce_v<T>* a, ep_reduce_v<T>* b) {
#ifdef EP_REDUCE_REVERSE
  return a->score < b->score;
#else
  return a->score > b->score;
#endif
}
//template <class T> inline void epsilon_reduce(ub::vector< affine<T> >& x, int n, int n_limit = 0) {
template <class T> inline std::map<int, int> epsilon_reduce(ub::vector< affine<T> >& x, int n, int n_limit = 0) { // @hylagi modified
  int s = x.size();
  int m = affine<T>::maxnum();
  int i, j;
  std::vector< ep_reduce_v<T> > a;
  std::vector< ep_reduce_v<T>* > pa;
  ub::vector< affine<T> > r;
  T tmp;
  std::map<int, int> result_map; // @hylagi

  if (n_limit < n) n_limit = n;

  // if (m <= n_limit) return;
  // if (n < s) return; // impossible
  // @hylagi modified
  if (m <= n_limit) return result_map;
  if (n < s) return result_map; // impossible

  a.resize(m);
  pa.resize(m);

  for (i=1; i<=m; i++) {
    a[i-1].v.resize(s);
    a[i-1].index = i; // @hylagi
    result_map.insert(std::make_pair(i, -1)); // @hylagi
    for (j=0; j<s; j++) {
      a[i-1].v(j) = (i < x(j).a.size()) ? x(j).a(i) : (T)0.;
    }
    a[i-1].calc_score();
    pa[i-1] = &(a[i-1]);
  }

#ifdef EP_REDUCE_REVERSE
  std::partial_sort(pa.begin(), pa.begin()+m-n+s, pa.end(), ep_reduce_cmp<T>);
#else
  std::partial_sort(pa.begin(), pa.begin()+n-s, pa.end(), ep_reduce_cmp<T>);
#endif

  r.resize(s);
  for (i=0; i<s; i++) {
    r(i).a.resize(n+1);
    r(i).a(0) = x(i).a(0);
    for (j=0; j<n-s; j++) {
#ifdef EP_REDUCE_REVERSE
      r(i).a(j+1) = pa[m-1-j]->v(i);
      result_map[pa[m-1-j]->index] = j+1; // @hylagi
#else
      r(i).a(j+1) = pa[j]->v(i);
#endif
    }
    tmp = 0.;
    rop<T>::begin();
    for (j=n-s; j<m; j++) {
      using std::abs;
#ifdef EP_REDUCE_REVERSE
      tmp = rop<T>::add_up(tmp, abs(pa[m-1-j]->v(i)));
#else
      tmp = rop<T>::add_up(tmp, abs(pa[j]->v(i)));
#endif
    }
    #if AFFINE_SIMPLE >= 1
    tmp = rop<T>::add_up(tmp, x(i).er);
    #endif
    rop<T>::end();
    for (j=n-s; j<n; j++) {
      r(i).a(j+1) = 0.;
    }
    r(i).a(n-s+i+1) = tmp;
    #if AFFINE_SIMPLE >= 1
    r(i).er = 0.;
    #endif
  }

  x = r;
  affine<T>::maxnum() = n;
  return result_map; //@hylagi
}

// simple version of epsilon_reduce
// keep ep_1...ep_n and "intervalize" epsilons newer than ep_n

template <class T> inline void epsilon_reduce2(ub::vector< affine<T> >& x, int n) {
  int s = x.size();
  int i, j;
  T tmp;

  for (i=0; i<s; i++) {
    tmp = 0.;
    rop<T>::begin();
    for (j=n+1; j<x(i).a.size(); j++) {
      using std::abs;
      tmp = rop<T>::add_up(tmp, abs(x(i).a(j)));
    }
    #if AFFINE_SIMPLE >= 1
    tmp = rop<T>::add_up(tmp, x(i).er);
    #endif
    rop<T>::end();

    x(i).a.resize(n+1+i+1, true);
    for (j=0; j<i; j++) {
      x(i).a(n+1+j) = 0.;
    }
    x(i).a(n+1+i) = tmp;
    #if AFFINE_SIMPLE >= 1
    x(i).er = 0.;
    #endif
  }

  affine<T>::maxnum() = n + s;
}

template <class T> struct constants< affine<T> > {
  static affine<T> pi() {
    static const affine<T> tmp(constants< interval<T> >::pi());
    return tmp;
  }

  static affine<T> e() {
    static const affine<T> tmp(constants< interval<T> >::e());
    return tmp;
  }

  static affine<T> ln2() {
    static const affine<T> tmp(constants< interval<T> >::ln2());
    return tmp;
  }
  static affine<T> str(const std::string& s) {
    return affine<T>(constants< interval<T> >::str(s));
  }
  static affine<T> str(const std::string& s1, const std::string& s2) {
    return affine<T>(constants< interval<T> >::str(s1, s2));
  }
};

} // namespace kv

#endif //AFFINE_HPP


//=======================================================================
// Copyright (c) 2014 Andrzej Pacuk
//
// 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)
//=======================================================================
/**
 * @file lsh_nearest_neighbors_regression_test.cpp
 * @brief
 * @author Andrzej Pacuk
 * @version 1.0
 * @date 2014-10-07
 */
#include "test_utils/logger.hpp"
#include "test_utils/get_test_dir.hpp"
#include "test_utils/serialization.hpp"

#include "paal/regression/lsh_nearest_neighbors_regression.hpp"
#include "paal/utils/irange.hpp"

#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/numeric/ublas/vector_sparse.hpp>
#include <boost/range/empty.hpp>
#include <boost/test/unit_test.hpp>

#include <iterator>
#include <vector>
#include <utility>
#include <string>

namespace {

using coordinate_t = int;
using result_t = double;
using point_coordinates_t = std::vector<coordinate_t>;
using point_sparse_coordinates_t = boost::numeric::ublas::compressed_vector<double>;

static result_t const EPSILON = 1e-9;
static unsigned const default_passes = 50;
static unsigned const default_hash_functions_per_row = 2;

static const std::string TMP_FILE = "tmp.bin";

struct jaccard_tag{};
struct hamming_tag{};

auto make_model(hamming_tag, std::vector<point_coordinates_t> const &training_points,
                std::vector<result_t> const &training_results,
                unsigned passes = default_passes,
                unsigned hash_functions_per_row
                    = default_hash_functions_per_row) {
    auto const dimensions = training_points.front().size();
    return paal::make_lsh_nearest_neighbors_regression_tuple_hash(
                training_points,
                training_results,
                passes,
                paal::lsh::hamming_hash_function_generator{dimensions},
                hash_functions_per_row);
}

auto make_model(jaccard_tag, std::vector<point_sparse_coordinates_t> const &training_points,
                std::vector<result_t> const &training_results,
                unsigned passes = default_passes,
                unsigned hash_functions_per_row
                    = default_hash_functions_per_row) {
    auto const dimensions = training_points.front().size();
    return paal::make_lsh_nearest_neighbors_regression_tuple_hash(
                training_points,
                training_results,
                passes,
                paal::lsh::min_hash_function_generator<>{dimensions},
                hash_functions_per_row);
}

template <typename HashFunctionGenerator>
auto make_lp(std::vector<point_sparse_coordinates_t> const &training_points,
             std::vector<result_t> const &training_results,
             unsigned passes = default_passes,
             unsigned hash_functions_per_row
                 = default_hash_functions_per_row) {
    auto const dimensions = training_points.front().size();
    int const W = 2;
    return paal::make_lsh_nearest_neighbors_regression_tuple_hash(
                training_points,
                training_results,
                passes,
                HashFunctionGenerator{dimensions, W},
                hash_functions_per_row);
}

template<typename Model>
void update(Model &&model,
            std::vector<point_coordinates_t> const &training_points,
            std::vector<result_t> const &training_results) {
    model.update(training_points, training_results);
}

template <typename Model, typename Row>
void test(Model &&model,
          std::vector<Row> const &test_points,
          std::vector<result_t> const &expected_results) {
    std::vector<result_t> results(test_points.size());
    model.test(test_points, results.begin());

    LOGLN("expected results");
    LOG_COPY_RANGE_DEL(expected_results, ",");
    LOGLN("");

    LOGLN("results");
    LOG_COPY_RANGE_DEL(results, ",");
    LOGLN("");

    for (auto i : paal::irange(test_points.size())) {
        BOOST_CHECK_CLOSE(expected_results[i], results[i], EPSILON);
    }
}

template <typename HashFunctionTag, typename Row>
void check_hamming_jaccard(HashFunctionTag tag, std::vector<Row> const &training_points,
                   std::vector<result_t> const &training_results,
                   std::vector<Row> const &test_points,
                   std::vector<result_t> const &expected_results,
                   unsigned passes = default_passes,
                   unsigned hash_functions_per_row
                       = default_hash_functions_per_row) {
    auto model = make_model(tag, training_points, training_results,
                                    passes, hash_functions_per_row);
    test(std::move(model), test_points, expected_results);
}

void check_hamming(std::vector<point_coordinates_t> const &training_points,
                   std::vector<result_t> const &training_results,
                   std::vector<point_coordinates_t> const &test_points,
                   std::vector<result_t> const &expected_results,
                   unsigned passes = default_passes,
                   unsigned hash_functions_per_row
                       = default_hash_functions_per_row) {
    check_hamming_jaccard(hamming_tag{}, training_points, training_results, test_points,
            expected_results, passes, hash_functions_per_row);
}

point_sparse_coordinates_t make_sparse_set_from_coordinates(std::vector<int> const &coordinates) {
    assert(!boost::empty(coordinates));
    point_sparse_coordinates_t point(*boost::max_element(coordinates) + 1);
    for (auto index : coordinates) point(index) = 1.0;
    return point;
};

std::vector<point_sparse_coordinates_t> make_jaccard_points(std::vector<point_coordinates_t> const &points) {
    std::vector<point_sparse_coordinates_t> converted;
    size_t max_size = 0;
    for (auto &coordinates : points) {
        auto converted_point = make_sparse_set_from_coordinates(coordinates);
        max_size = std::max(max_size, converted_point.size());
        converted.push_back(std::move(converted_point));
    }

    for (auto &point : converted) {
        point.resize(max_size);
    }
    return converted;
}

auto make_sparse(std::vector<double> const &list) {
    point_sparse_coordinates_t point(list.size());
    for (auto index : paal::irange(list.size())) {
        point[index] = list[index];
    }
    return point;
};

} //! unnamed

BOOST_AUTO_TEST_SUITE(lsh_nearest_neighbors_regression)

BOOST_AUTO_TEST_CASE(training_points_same_as_test_points) {
    // the more hash functions, the lower chance of accidental positive
    LOGLN("training_points_same_as_test_points");
    auto hash_functions_per_row = 50;
    for (auto passes : {1, 10, 50}) {
        check_hamming({{0, 0}, {1, 1}},
                      {0.0, 1.0},
                      {{0, 0}, {1, 1}},
                      {0.0, 1.0},
                      passes,
                      hash_functions_per_row);
    }
}

BOOST_AUTO_TEST_CASE(one_value) {
    //test always passes, since in case of missing all training points hash values
    //still average = 1.0
    LOGLN("one_value");
    check_hamming({{0, 0}, {3, -1}, {0, 2}, {4, 1}, {5, 1}, {2, 5}},
                  {1.0, 1.0, 1.0, 1.0, 1.0, 1.0},
                  {{0, -1}},
                  {1.0});
}

BOOST_AUTO_TEST_CASE(two_descent_values) {
    //test passes with probability 1 - 0.5**50
    LOGLN("two_descent_values");
    check_hamming({{0, 0}, {3, -1}, {0, 2}, {4, 1}, {5, 1}, {2, 5}},
                  {0.0, 0.0, 0.0, 1.0, 1.0, 1.0},
                  //first point projections matches projections of
                  //0.0-result training point and 1.0-result for second point
                  {{0, -1}, {2, 1}},
                  {0.0, 1.0});
}

BOOST_AUTO_TEST_CASE(two_descent_values_ints_results) {
    //test passes with probability 1 - 0.5**50
    LOGLN("two_descent_values_ints_results");
    check_hamming({{0, 0}, {3, -1}, {0, 2}, {4, 1}, {5, 1}, {2, 5}},
                  {0, 0, 0, 1, 1, 1},
                  //first point projections matches projections of
                  //0.0-result training point and 1.0-result for second point
                  {{0, -1}, {2, 1}},
                  {0, 1});
}

BOOST_AUTO_TEST_CASE(two_descent_values_ints_results_min) {
    LOGLN("two_descent_values_ints_results min hashing");
    check_hamming_jaccard(jaccard_tag{},
                make_jaccard_points({{0, 1}, {1}, {0}, {2, 3}, {2}, {3}}),
                {0, 0, 0, 1, 1, 1},
                make_jaccard_points({{0, 1}, {3, 2}}),
                {0, 1});
}

BOOST_AUTO_TEST_CASE(no_neighbors_return_average) {
    LOGLN("no_neighbors_return_average");
    check_hamming({{0, 0}, {3, -1}, {0, 2}, {4, 1}, {5, 1}, {2, 5}},
                  {0.0, 0.0, 0.0, 1.0, 1.0, 1.0},
                  //coordinates different from training points coordinates
                  {{14, -7}},
                  {0.5});
}

BOOST_AUTO_TEST_CASE(no_hit_return_average) {
    LOGLN("no_hit_return_average");
    using puu = std::pair<unsigned, unsigned>;
    for (auto const &params : {puu(0, 50), puu(50, 0), puu(0, 0)}) {
        auto passes = params.first;
        auto hash_functions_per_row = params.second;
        LOGLN("passes: " << passes);
        LOGLN("hash_functions_per_row: " << hash_functions_per_row);
        check_hamming({{0, 0}, {3, -1}},
                      {0.0, 1.0},
                      {{14, -7}},
                      {0.5},
                      passes,
                      hash_functions_per_row);
    }
}

BOOST_AUTO_TEST_CASE(update_changes_model) {
    LOGLN("update_changes_model");
    auto model = make_model(hamming_tag{}, {{0, 1}, {2, 3}},
                                    {0.0, 0.2});
    //point distant from training points
    LOGLN("test");
    std::vector<point_coordinates_t> test_points = {{17, 18}};
    test(model, test_points, {0.1});
    LOGLN("update");
    update(model, {{4, 5}}, {0.4});
    LOGLN("test");
    test(model, test_points, {0.2});
    LOGLN("end");
}

BOOST_AUTO_TEST_CASE(serialization) {
    LOGLN("serialize");
    serialize_test(make_model(hamming_tag{}, {{0, 1}, {2, 3}}, {0.0, 0.2}), TMP_FILE);
    serialize_test(make_model(jaccard_tag{},
                         make_jaccard_points({{0, 1}, {2, 3}}),
                         {0.0, 0.2}), TMP_FILE);

    point_sparse_coordinates_t p1{make_sparse({0.0, 1.0})}, p2{make_sparse({2.0, 3.0})};
    serialize_test(make_lp<paal::lsh::l_1_hash_function_generator<>>({p1, p2}, {0.0, 0.2}), TMP_FILE);
    serialize_test(make_lp<paal::lsh::l_2_hash_function_generator<>>({p1, p2}, {0.0, 0.2}), TMP_FILE);
}

BOOST_AUTO_TEST_SUITE_END()


#include <fstream>
#include <json/json.h>
#include <armadillo>

#include "ema/Ema.h"

#include "settings.h"

int main(int argc, char* argv[]) {

  Settings settings(argc, argv);

  Ema ema;
  ema.read().from(settings.input);

  Json::Value root(Json::arrayValue);

  for(const double& timeStamp: settings.timeStamps) {

    const arma::vec position = ema.coil(settings.coil).interpolate().position_at_time(timeStamp);

    Json::Value entry(Json::objectValue);

    entry["x"] = position(0);
    entry["y"] = position(1);
    entry["z"] = position(2);

    root.append(entry);

  }

  std::ofstream outFile(settings.output);
  outFile << root << std::endl;
  outFile.close();

  return 0;

}


/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * Written (W) 2013 Fernando J. Iglesias Garcia
 * Copyright (C) 2013 Fernando J. Iglesias Garcia
 */

#ifdef HAVE_EIGEN3

#include <shogun/distance/CustomMahalanobisDistance.h>
#include <Eigen/Dense>

using namespace shogun;
using namespace Eigen;

CCustomMahalanobisDistance::CCustomMahalanobisDistance() : CRealDistance()
{
	register_params();
}

CCustomMahalanobisDistance::CCustomMahalanobisDistance(CFeatures* l, CFeatures* r, SGMatrix<float64_t> m)
: CRealDistance()
{
	register_params();
	CRealDistance::init(l, r);
	m_mahalanobis_matrix = m;
}

void CCustomMahalanobisDistance::register_params()
{
	SG_ADD(&m_mahalanobis_matrix, "m_mahalanobis_matrix", "Mahalanobis matrix", MS_NOT_AVAILABLE)
}

CCustomMahalanobisDistance::~CCustomMahalanobisDistance()
{
	cleanup();
}

void CCustomMahalanobisDistance::cleanup()
{
}

const char* CCustomMahalanobisDistance::get_name() const
{
	return "CustomMahalanobisDistance";
}

EDistanceType CCustomMahalanobisDistance::get_distance_type()
{
	return D_CUSTOMMAHALANOBIS;
}

float64_t CCustomMahalanobisDistance::compute(int32_t idx_a, int32_t idx_b)
{
	// Get feature vectors that will be used to compute the distance; casts
	// are safe, features are checked to be dense in DenseDistance::init
	SGVector<float64_t> avec = static_cast<CDenseFeatures<float64_t>*>(lhs)->get_feature_vector(idx_a);
	SGVector<float64_t> bvec = static_cast<CDenseFeatures<float64_t>*>(rhs)->get_feature_vector(idx_b);

	REQUIRE(avec.vlen == bvec.vlen, "In CCustomMahalanobisDistance::compute the "
			"feature vectors must have the same number of elements")

	// Compute the distance between the feature vectors

	// Compute the difference vector and wrap in Eigen vector
	const VectorXd dvec = Map<const VectorXd>(avec, avec.vlen) - Map<const VectorXd>(bvec, bvec.vlen);
	// Wrap Mahalanobis distance in Eigen matrix
	Map<const MatrixXd> M(m_mahalanobis_matrix.matrix, m_mahalanobis_matrix.num_rows,
			m_mahalanobis_matrix.num_cols);

	return dvec.transpose()*M*dvec;
}

#endif /* HAVE_EIGEN3 */


#define BOOST_TEST_MODULE InstructionTest

#include "instruction.h"
#include "addressMode.h"
#include "cpu6502.h"
#include "registers.h"

#include <cinttypes>
#include <string>
#include <vector>
#include <boost/test/unit_test.hpp>

/*
 * These tests test the actual instructions with the associated permutations
 * of addressing modes. Notably, the translation of op code to instruction/mode
 * is skipped.
 */

class CpuTester {
public:

  CpuTester() : cpu(nullptr, nullptr) {}

  std::string print(const std::string& op_name, uint8_t op) {
    return '\n' + op_name + '\n' + cpu.print_registers(op);
  }

  void run() {
    cpu.power(true);
  }
  void queue(Instruction6502 *instruction) {
    // Push an instruction onto the instruction queue and power on the CPU.
    cpu.q->push(instruction);
  }

  registers& data() {
    return cpu.reg;
  }

  Cpu6502 cpu;
};

class Immediate : public AddressMode {
public:
  Immediate(uint8_t o) :
    operand(o) {}
  uint8_t fetch(registers&) { return operand; }

private:
  uint8_t operand;
};

// Verification funtions.

static void
verify_acc(CpuTester& c, uint8_t expected, const std::string& op_name="")
{
  BOOST_CHECK_MESSAGE(c.data().acc == expected, c.print(op_name, expected));
}

static void
verify_status_all(CpuTester& c, uint8_t expected, const std::string& op_name="")
{
  BOOST_CHECK_MESSAGE(c.data().p == expected, c.print(op_name, expected));
}

static void
verify_status_one(CpuTester& c, uint8_t expected,const std::string& op_name="")
{
  BOOST_CHECK_MESSAGE((c.data().p & expected) > 0, c.print(op_name, expected));
}

/*
 * Declares an instruction and subsequently executes it.  It then prints the output.
 * We assume that the addressing mode has been determined and so feed it directly to
 * the instructions.
 *
 * INST        = The Instruction name.
 * ADDRESSMODE = The addressing mode.
 * OP          = The Operand on which the instruction operates.
 */

template<typename INSTRUCTION>
static void run(CpuTester& c, uint8_t operand)
{
  Immediate mode(operand);
  INSTRUCTION instruction(c.cpu, c.data(), &mode);
  c.queue(&instruction);
  c.run();
}

template<typename INSTRUCTION>
static void run(CpuTester& c, std::vector<uint8_t> operands)
{
  std::vector<Immediate *> storage;
  std::vector<INSTRUCTION *> storage2;
  for (uint8_t operand : operands) {
    Immediate *mode = new Immediate(operand);
    storage.push_back(mode);
    INSTRUCTION *instruction = new INSTRUCTION(c.cpu, c.data(), mode);
    storage2.push_back(instruction);
    c.queue(instruction);
  }
  c.run();
}

BOOST_AUTO_TEST_CASE(_adc_)
{
  // Test customs.
  // Simple case.

  {
    CpuTester c;
    run<ADC>(c, 0);
    verify_acc(c, 0, "0");
  }
  {
    CpuTester c;
    run<ADC>(c, 69);
    verify_acc(c, 69, "General");
  }

  {
    CpuTester c;
    std::vector<uint8_t> operands = { 1, 127 };
    run<ADC>(c, operands);
    verify_acc(c, 128, "1 + 127 = 128");
  }

  {
    CpuTester c;
    std::vector<uint8_t> operands = { 1, 255 };
    run<ADC>(c, operands);
    verify_acc(c, 0, "1 + 255 = 0");
    verify_status_one(c, Registers::CARRY_FLAG, "Carry FLAG");
  }
  {
    CpuTester c;
    std::vector<uint8_t> operands = { 255, 255 };
    run<ADC>(c, operands);
    verify_acc(c, 254, "255 + 255 = 254");
    verify_status_one(c, Registers::CARRY_FLAG, "Carry FLAG");
    verify_status_one(c, Registers::OVERFLOW_FLAG, "Overflow FLAG");
  }
  {
    CpuTester c;
    std::vector<uint8_t> operands = { 255, 255, 0 };
    run<ADC>(c, operands);
    verify_acc(c, 255, "255 + 255 = 254 + carry = 255");
  }
}

BOOST_AUTO_TEST_CASE(_sbc_)
{
  CpuTester c;
  // run<SBC>(c, 0);
}


#include <iostream>
#include <mutex>
#include <queue>
#include <thread>

#include <boost/math/constants/constants.hpp>

#include <eigen3/Eigen/Geometry>

#include <MadgwickAHRS.h>

#include <opencv2/calib3d/calib3d.hpp>
#include <opencv2/core/eigen.hpp>
#include <opencv2/viz/vizcore.hpp>

#include <sensor_reader/mpu9250.hpp>
#include <sensor_reader/sensor_reader.hpp>
#include <sensor_reader/string_line_reader.hpp>

using a4wd2::sensor_reader::sensor_reader;
using a4wd2::sensor_reader::serial_string_line_reader;
namespace sensors = a4wd2::sensor_reader::sensors;

using boost::math::constants::pi;

using namespace cv;

static constexpr float FONT_SIZE = 12.f;

static constexpr float DEG2RAD = 2. * pi<float>() / 360.;

int main(int argc, char** argv)
{
    if (argc < 2)
    {
        std::cerr << "read_serial <port_name>" << std::endl;
        return EXIT_FAILURE;
    }

    const char* port_name = argv[1];
    serial_string_line_reader serial_reader(port_name, 9600);

    // Data vector shared between the window thread and serial thread
    std::mutex data_mutex;
    std::queue<sensors::mpu9250::data_t> imu_data;

    auto sensor_processor = [&]() {
        sensor_reader reader{serial_reader.get_stream()};

        reader.add_sensor<sensors::mpu9250>([&](const auto& data) {
            std::lock_guard<std::mutex> lock(data_mutex);
            imu_data.push(data);
        });
        reader.read_all();
    };
    std::thread sensor_thread(sensor_processor);

    // Visualisation
    viz::Viz3d window("Coordinate Frame");

    window.showWidget("Coordinate Widget", viz::WCoordinateSystem());

    viz::WText sensor_text("IMU raw data: waiting for data...", cv::Point(10, 10),
                           FONT_SIZE);
    window.showWidget("sensor_text", sensor_text);

    Madgwick madgwick;
    madgwick.begin(1000.);
    window.showWidget("sensor_coordinate_frame", viz::WCoordinateSystem());

    while (!window.wasStopped())
    {
        {
            std::lock_guard<std::mutex> lock(data_mutex);

            while (imu_data.size() > 1)
            {
                imu_data.pop();
            }
        }

        if (!imu_data.empty())
        {
            auto data = imu_data.front();
            imu_data.pop();

            for (int i = 0; i < 50; ++i)
            {
                madgwick.update(-data.g.y, -data.g.x, -data.g.z, data.a.y, data.a.x,
                                data.a.z, data.m.x, data.m.y, -data.m.z);
            }

            std::stringstream ss;
            ss << "IMU accel: " << data.a << '\n';
            ss << "IMU gyro : " << data.g << '\n';
            ss << "IMU mag  : " << data.m << '\n';
            ss << "Madgwick : roll: " << std::setw(7) << std::fixed
               << std::setprecision(3) << madgwick.getRoll()
               << ", pitch: " << std::setw(7) << std::fixed << std::setprecision(3)
               << madgwick.getPitch() << ", yaw: " << std::setw(7) << std::fixed
               << std::setprecision(3) << madgwick.getYaw() << '\n';
            sensor_text.setText(ss.str());

            Eigen::Matrix3f rotation_matrix;
            rotation_matrix = Eigen::AngleAxisf(-madgwick.getYaw() * DEG2RAD,
                                                Eigen::Vector3f::UnitZ()) *
                              Eigen::AngleAxisf(madgwick.getPitch() * DEG2RAD,
                                                Eigen::Vector3f::UnitX()) *
                              Eigen::AngleAxisf(madgwick.getRoll() * DEG2RAD,
                                                Eigen::Vector3f::UnitY());
            cv::Mat cv_rotation_matrix;
            cv::eigen2cv(rotation_matrix, cv_rotation_matrix);

            window.setWidgetPose("sensor_coordinate_frame",
                                 cv::Affine3f(cv_rotation_matrix, cv::Vec3f(.5, .5, .5)));
        }

        window.spinOnce(10, true);
    }

    serial_reader.shutdown();
    sensor_thread.join();

    return 0;
}


// Copyright (c) 2017-2018 Steven Watanabe
//
// Distributed under the Boost Software License Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_UNITS2_TEMPERATURE_HPP_INCLUDED
#define BOOST_UNITS2_TEMPERATURE_HPP_INCLUDED

#include <boost/units2/absolute.hpp>
#include <boost/units2/si.hpp>

namespace boost {
namespace units2 {
namespace temperature {

constexpr auto kelvin = absolute_unit(si::kelvin);
constexpr auto celsius = absolute_unit(kelvin, std::ratio<27315,100>());
constexpr auto fahrenheit = absolute_unit(celsius * std::ratio<5,9>(), std::ratio<-32>());

}
}
}

#endif


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

  Copyright 2015 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/two_prod.hpp>
#include <simd_test.hpp>
#include <boost/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>
#include <boost/simd/constant/eps.hpp>
#include <boost/simd/constant/half.hpp>
#include <utility>

STF_CASE_TPL(" two_prod", STF_IEEE_TYPES)
{
  namespace bs = boost::simd;
  namespace bd = boost::dispatch;
  using bs::two_prod;

  STF_EXPR_IS( (two_prod(T(), T()))
                  , (std::pair<T,T>)
                  );

  T inf_    = bs::Inf<T>();
  T zero_   = bs::Zero<T>();
  T one_    = bs::One<T>();
  T eps_    = bs::Eps<T>();
  T meps2_   = -eps_*eps_;

  {
    T s,r;

    two_prod(inf_,one_, s, r);
    STF_EQUAL(s, inf_);
    STF_EQUAL(r, zero_);

    two_prod(one_, inf_, s, r);
    STF_EQUAL(s, inf_);
    STF_EQUAL(r, zero_);

    two_prod(one_ + eps_, one_ - eps_, s, r);
    STF_EQUAL(s, one_);
    STF_EQUAL(r, meps2_);

    two_prod(one_ - eps_,one_ + eps_, s, r);
    STF_EQUAL(s, one_);
    STF_EQUAL(r, meps2_);
  }

  {
    T s,r;

    s = two_prod(inf_,one_, r);
    STF_EQUAL(s, inf_);
    STF_EQUAL(r, zero_);

    s = two_prod(one_, inf_, r);
    STF_EQUAL(s, inf_);
    STF_EQUAL(r, zero_);

    s = two_prod(one_ + eps_, one_ - eps_, r);
    STF_EQUAL(s, one_);
    STF_EQUAL(r, meps2_);

    s = two_prod(one_ - eps_,one_ + eps_, r);
    STF_EQUAL(s, one_);
    STF_EQUAL(r, meps2_);
  }

  {
    std::pair<T,T> p;

    p = two_prod(inf_,one_);
    STF_EQUAL(p.first, inf_);
    STF_EQUAL(p.second, zero_);

    p = two_prod(one_, inf_);
    STF_EQUAL(p.first, inf_);
    STF_EQUAL(p.second, zero_);

    p = two_prod(one_ + eps_, one_ - eps_);
    STF_EQUAL(p.first, one_);
    STF_EQUAL(p.second, meps2_);

    p = two_prod(one_ - eps_,one_ + eps_);
    STF_EQUAL(p.first, one_);
    STF_EQUAL(p.second, meps2_);
  }
}


#pragma once

#include <Eigen/Dense>
#include <boost/multi_array.hpp>
#include <vector>

/**
 *
 * @brief Helper to store (cache) \f$ b_i(x_j) \$
 * where \f$ b_i \f$ is supposed to be a polynomial of degree i.
 *
 */
template <typename NUMERIC>
class PolyBase
{
 public:
  typedef NUMERIC numeric_t;
  typedef Eigen::Array<numeric_t, Eigen::Dynamic, 1> array_t;
  typedef Eigen::Map<const array_t> marray_t;

 public:
  PolyBase(unsigned int n);

  /**
   * @brief
   *
   * @param c coefficients
   * @param y
   */
  void evaluate(const std::vector<numeric_t> &c, std::vector<numeric_t> &y);

  /**
   * @brief returns max_degree + 1
   *
   */
  unsigned int npoly() const;

  /**
   * @brief returns a pointer to the begin of b_i
   *
   * @param n  (degree)
   *
   * @return numeric_t*
   */
  const numeric_t *get(unsigned int n) const;

  /**
   * @brief returns an Eigen::Array of b_i(x),
   *
   *
   * @param n
   *
   * @return Array of length m = size(x)
   */
  marray_t get_array(unsigned int n) const;

  unsigned int get_degree() const { return n_; }
  unsigned int get_npoints() const { return Y_.shape()[1]; }

 protected:
  /// max degree
  unsigned int n_;
  /// b_i(x_j) (i: row index, j: column index)
  boost::multi_array<NUMERIC, 2> Y_;
  bool is_initialized_;
};

// ----------------------------------------------------------------------
template <typename NUMERIC>
PolyBase<NUMERIC>::PolyBase(unsigned int n)
    : n_(n)
    , is_initialized_(false)
{
  /* empty */
}

// ----------------------------------------------------------------------
template <typename NUMERIC>
void
PolyBase<NUMERIC>::evaluate(const std::vector<numeric_t> &c, std::vector<numeric_t> &y)
{
  const unsigned int npts = Y_.shape()[1];
  const unsigned int L = Y_.shape()[0];

  assert(L == c.size());
  assert(npts == y.size());
  assert(is_initialized_);

  for (unsigned int xi = 0; xi < npts; ++xi) {
    y[xi] = 0;
  }

  for (unsigned int l = 0; l < L; ++l) {
    for (unsigned int xi = 0; xi < npts; ++xi) {
      y[xi] += Y_[l][xi] * c[l];
    }
  }
}

// ----------------------------------------------------------------------
template <typename NUMERIC>
const NUMERIC *
PolyBase<NUMERIC>::get(unsigned int n) const
{
  assert(n < Y_.shape()[0]);
  return Y_[n].origin();
}

template <typename NUMERIC>
typename PolyBase<NUMERIC>::marray_t
PolyBase<NUMERIC>::get_array(unsigned int n) const
{
  assert(n < Y_.shape()[0]);
  return marray_t(Y_[n].origin(), Y_.shape()[1]);
}

// ----------------------------------------------------------------------
template <typename NUMERIC>
unsigned int
PolyBase<NUMERIC>::npoly() const
{
  return Y_.shape()[0];
}


//  Copyright John Maddock 2006.
//  Copyright Paul A. Bristow 2006.
//  Use, modification and distribution are subject to the
//  Boost Software License, Version 1.0. (See accompanying file
//  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_STATS_STUDENTS_T_HPP
#define BOOST_STATS_STUDENTS_T_HPP

// http://en.wikipedia.org/wiki/Student%27s_t_distribution
// http://www.itl.nist.gov/div898/handbook/eda/section3/eda3664.htm

#include <boost/math/distributions/fwd.hpp>
#include <boost/math/special_functions/beta.hpp> // for ibeta(a, b, x).
#include <boost/math/distributions/complement.hpp>
#include <boost/math/distributions/detail/common_error_handling.hpp>

#include <utility>

#ifdef BOOST_MSVC
# pragma warning(push)
# pragma warning(disable: 4702) // unreachable code (return after domain_error throw).
#endif

namespace boost{ namespace math{

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

   students_t_distribution(RealType i) : m_df(i)
   { // Constructor.
      RealType result;
      detail::check_df(
         "boost::math::students_t_distribution<%1%>::students_t_distribution", m_df, &result, Policy());
   } // students_t_distribution

   RealType degrees_of_freedom()const
   {
      return m_df;
   }

   // Parameter estimation:
   static RealType find_degrees_of_freedom(
      RealType difference_from_mean,
      RealType alpha,
      RealType beta,
      RealType sd,
      RealType hint = 100);

private:
   //
   // Data members:
   //
   RealType m_df;  // degrees of freedom are a real number.
};

typedef students_t_distribution<double> students_t;

template <class RealType, class Policy>
inline const std::pair<RealType, RealType> range(const students_t_distribution<RealType, Policy>& /*dist*/)
{ // Range of permissible values for random variable x.
   using boost::math::tools::max_value;
   return std::pair<RealType, RealType>(-max_value<RealType>(), max_value<RealType>());
}

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

template <class RealType, class Policy>
inline RealType pdf(const students_t_distribution<RealType, Policy>& dist, const RealType& t)
{
   BOOST_FPU_EXCEPTION_GUARD
   BOOST_MATH_STD_USING  // for ADL of std functions

   RealType degrees_of_freedom = dist.degrees_of_freedom();
   // Error check:
   RealType error_result;
   if(false == detail::check_df(
      "boost::math::pdf(const students_t_distribution<%1%>&, %1%)", degrees_of_freedom, &error_result, Policy()))
      return error_result;
   // Might conceivably permit df = +infinity and use normal distribution.
   RealType result;
   RealType basem1 = t * t / degrees_of_freedom;
   if(basem1 < 0.125)
   {
      result = exp(-boost::math::log1p(basem1, Policy()) * (1+degrees_of_freedom) / 2);
   }
   else
   {
      result = pow(1 / (1 + basem1), (degrees_of_freedom + 1) / 2);
   }
   result /= sqrt(degrees_of_freedom) * boost::math::beta(degrees_of_freedom / 2, RealType(0.5f), Policy());
   return result;
} // pdf

template <class RealType, class Policy>
inline RealType cdf(const students_t_distribution<RealType, Policy>& dist, const RealType& t)
{
   RealType degrees_of_freedom = dist.degrees_of_freedom();
   // Error check:
   RealType error_result;
   if(false == detail::check_df(
      "boost::math::cdf(const students_t_distribution<%1%>&, %1%)", degrees_of_freedom, &error_result, Policy()))
      return error_result;

   if (t == 0)
   {
     return 0.5;
   }
   //
   // Calculate probability of Student's t using the incomplete beta function.
   // probability = ibeta(degrees_of_freedom / 2, 1/2, degrees_of_freedom / (degrees_of_freedom + t*t))
   //
   // However when t is small compared to the degrees of freedom, that formula
   // suffers from rounding error, use the identity formula to work around
   // the problem:
   //
   // I[x](a,b) = 1 - I[1-x](b,a)
   //
   // and:
   //
   //     x = df / (df + t^2)
   //
   // so:
   //
   // 1 - x = t^2 / (df + t^2)
   //
   RealType t2 = t * t;
   RealType probability;
   if(degrees_of_freedom > 2 * t2)
   {
      RealType z = t2 / (degrees_of_freedom + t2);
      probability = ibetac(static_cast<RealType>(0.5), degrees_of_freedom / 2, z, Policy()) / 2;
   }
   else
   {
      RealType z = degrees_of_freedom / (degrees_of_freedom + t2);
      probability = ibeta(degrees_of_freedom / 2, static_cast<RealType>(0.5), z, Policy()) / 2;
   }
   return (t > 0 ? 1   - probability : probability);
} // cdf

template <class RealType, class Policy>
inline RealType quantile(const students_t_distribution<RealType, Policy>& dist, const RealType& p)
{
   BOOST_MATH_STD_USING // for ADL of std functions
   //
   // Obtain parameters:
   //
   RealType degrees_of_freedom = dist.degrees_of_freedom();
   RealType probability = p;
   //
   // Check for domain errors:
   //
   static const char* function = "boost::math::quantile(const students_t_distribution<%1%>&, %1%)";
   RealType error_result;
   if(false == detail::check_df(
      function, degrees_of_freedom, &error_result, Policy())
         && detail::check_probability(function, probability, &error_result, Policy()))
      return error_result;

   // Special cases, regardless of degrees_of_freedom.
   if (probability == 0)
      return -policies::raise_overflow_error<RealType>(function, 0, Policy());
   if (probability == 1)
     return policies::raise_overflow_error<RealType>(function, 0, Policy());
   if (probability == static_cast<RealType>(0.5))
     return 0;
   //
   // This next block is disabled in favour of a faster method than
   // incomplete beta inverse, code retained for future reference:
   //
#if 0
   //
   // Calculate quantile of Student's t using the incomplete beta function inverse:
   //
   probability = (probability > 0.5) ? 1 - probability : probability;
   RealType t, x, y;
   x = ibeta_inv(degrees_of_freedom / 2, RealType(0.5), 2 * probability, &y);
   if(degrees_of_freedom * y > tools::max_value<RealType>() * x)
      t = tools::overflow_error<RealType>(function);
   else
      t = sqrt(degrees_of_freedom * y / x);
   //
   // Figure out sign based on the size of p:
   //
   if(p < 0.5)
      t = -t;

   return t;
#endif
   //
   // Depending on how many digits RealType has, this may forward
   // to the incomplete beta inverse as above.  Otherwise uses a
   // faster method that is accurate to ~15 digits everywhere
   // and a couple of epsilon at double precision and in the central
   // region where most use cases will occur...
   //
   return boost::math::detail::fast_students_t_quantile(degrees_of_freedom, probability, Policy());
} // quantile

template <class RealType, class Policy>
inline RealType cdf(const complemented2_type<students_t_distribution<RealType, Policy>, RealType>& c)
{
   return cdf(c.dist, -c.param);
}

template <class RealType, class Policy>
inline RealType quantile(const complemented2_type<students_t_distribution<RealType, Policy>, RealType>& c)
{
   return -quantile(c.dist, c.param);
}

//
// Parameter estimation follows:
//
namespace detail{
//
// Functors for finding degrees of freedom:
//
template <class RealType, class Policy>
struct sample_size_func
{
   sample_size_func(RealType a, RealType b, RealType s, RealType d)
      : alpha(a), beta(b), ratio(s*s/(d*d)) {}

   RealType operator()(const RealType& df)
   {
      if(df <= tools::min_value<RealType>())
         return 1;
      students_t_distribution<RealType, Policy> t(df);
      RealType qa = quantile(complement(t, alpha));
      RealType qb = quantile(complement(t, beta));
      qa += qb;
      qa *= qa;
      qa *= ratio;
      qa -= (df + 1);
      return qa;
   }
   RealType alpha, beta, ratio;
};

}  // namespace detail

template <class RealType, class Policy>
RealType students_t_distribution<RealType, Policy>::find_degrees_of_freedom(
      RealType difference_from_mean,
      RealType alpha,
      RealType beta,
      RealType sd,
      RealType hint)
{
   static const char* function = "boost::math::students_t_distribution<%1%>::find_degrees_of_freedom";
   //
   // Check for domain errors:
   //
   RealType error_result;
   if(false == detail::check_probability(
      function, alpha, &error_result, Policy())
         && detail::check_probability(function, beta, &error_result, Policy()))
      return error_result;

   if(hint <= 0)
      hint = 1;

   detail::sample_size_func<RealType, Policy> f(alpha, beta, sd, difference_from_mean);
   tools::eps_tolerance<RealType> tol(policies::digits<RealType, Policy>());
   boost::uintmax_t max_iter = policies::get_max_root_iterations<Policy>();
   std::pair<RealType, RealType> r = tools::bracket_and_solve_root(f, hint, RealType(2), false, tol, max_iter, Policy());
   RealType result = r.first + (r.second - r.first) / 2;
   if(max_iter == policies::get_max_root_iterations<Policy>())
   {
      policies::raise_evaluation_error<RealType>(function, "Unable to locate solution in a reasonable time:"
         " either there is no answer to how many degrees of freedom are required"
         " or the answer is infinite.  Current best guess is %1%", result, Policy());
   }
   return result;
}

template <class RealType, class Policy>
inline RealType mean(const students_t_distribution<RealType, Policy>& )
{
   return 0;
}

template <class RealType, class Policy>
inline RealType variance(const students_t_distribution<RealType, Policy>& dist)
{
   // Error check:
   RealType error_result;
   if(false == detail::check_df(
      "boost::math::variance(students_t_distribution<%1%> const&, %1%)", dist.degrees_of_freedom(), &error_result, Policy()))
      return error_result;

   RealType v = dist.degrees_of_freedom();
   return v / (v - 2);
}

template <class RealType, class Policy>
inline RealType mode(const students_t_distribution<RealType, Policy>& /*dist*/)
{
   return 0;
}

template <class RealType, class Policy>
inline RealType median(const students_t_distribution<RealType, Policy>& /*dist*/)
{
   return 0;
}

template <class RealType, class Policy>
inline RealType skewness(const students_t_distribution<RealType, Policy>& dist)
{
   if(dist.degrees_of_freedom() <= 3)
   {
      policies::raise_domain_error<RealType>(
         "boost::math::skewness(students_t_distribution<%1%> const&, %1%)",
         "Skewness is undefined for degrees of freedom <= 3, but got %1%.",
         dist.degrees_of_freedom(), Policy());
   }
   return 0;
}

template <class RealType, class Policy>
inline RealType kurtosis(const students_t_distribution<RealType, Policy>& dist)
{
   RealType df = dist.degrees_of_freedom();
   if(df <= 3)
   {
      policies::raise_domain_error<RealType>(
         "boost::math::kurtosis(students_t_distribution<%1%> const&, %1%)",
         "Skewness is undefined for degrees of freedom <= 3, but got %1%.",
         df, Policy());
   }
   return 3 * (df - 2) / (df - 4);
}

template <class RealType, class Policy>
inline RealType kurtosis_excess(const students_t_distribution<RealType, Policy>& dist)
{
   // see http://mathworld.wolfram.com/Kurtosis.html
   RealType df = dist.degrees_of_freedom();
   if(df <= 3)
   {
      policies::raise_domain_error<RealType>(
         "boost::math::kurtosis_excess(students_t_distribution<%1%> const&, %1%)",
         "Skewness is undefined for degrees of freedom <= 3, but got %1%.",
         df, Policy());
   }
   return 6 / (df - 4);
}

} // namespace math
} // namespace boost

#ifdef BOOST_MSVC
# pragma warning(pop)
#endif

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

#endif // BOOST_STATS_STUDENTS_T_HPP


/*
@copyright Louis Dionne 2014
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/hana/detail/assert.hpp>
#include <boost/hana/integral.hpp>
#include <boost/hana/range.hpp>
using namespace boost::hana;
using namespace literals;

//! [main]
BOOST_HANA_CONSTANT_ASSERT(head(range(0_c, 5_c)) == 0_c);
BOOST_HANA_CONSTANT_ASSERT(last(range(0_c, 5_c)) == 4_c);
BOOST_HANA_CONSTANT_ASSERT(tail(range(0_c, 5_c)) == range(1_c, 5_c));
//! [main]

int main() { }