#ifndef ABSTRACT_TRANSFORMER_HPP
#define ABSTRACT_TRANSFORMER_HPP

/*
   Implementation of the abstract transfer functions by reducing them
   to abstract domain operations.

   These are the main Crab statements for which we define their abstract
   transfer functions:

   ARITHMETIC and BOOLEAN
     x := y bin_op z;
     x := y;
     assume (cst)
     assert (cst);
     x := select (cond, y, z);

   ARRAYS
     a[i] = v;    (v can be either an integer or pointer)
     v = a[i];
     a := b       (a and b arrays)

   POINTERS
     *p = q;
     p = *q;
     p := q+n
     p := &obj;
     p := &fun
     p := null;

   FUNCTIONS
     x := foo(arg1,...,argn);
     return r;

   havoc (x);

 */

#include <boost/optional.hpp>
#include <boost/range/algorithm/set_algorithm.hpp>
#include <boost/noncopyable.hpp>

#include <crab/common/debug.hpp>
#include <crab/common/stats.hpp>
#include <crab/cfg/cfg.hpp>
#include <crab/domains/domain_traits.hpp>
#include <crab/domains/linear_constraints.hpp>

namespace crab {

  namespace analyzer {

  //! API abstract transformer
  template<typename Number, typename VariableName>
  class abs_transformer_api: public cfg::statement_visitor <Number, VariableName>
  {
   public:

    typedef Number number_t;
    typedef VariableName varname_t;

    typedef ikos::variable <number_t, VariableName > var_t;
    typedef ikos::linear_expression<number_t, VariableName > lin_exp_t;
    typedef ikos::linear_constraint<number_t, VariableName > lin_cst_t;
    typedef ikos::linear_constraint_system<number_t, VariableName > lin_cst_sys_t;

    typedef cfg::havoc_stmt<number_t,VariableName>        havoc_t;
    typedef cfg::unreachable_stmt<number_t,VariableName>  unreach_t;

    typedef cfg::binary_op<number_t,VariableName>        bin_op_t;
    typedef cfg::assignment<number_t,VariableName>       assign_t;
    typedef cfg::assume_stmt<number_t,VariableName>      assume_t;
    typedef cfg::select_stmt<number_t,VariableName>      select_t;
    typedef cfg::assert_stmt<number_t,VariableName>      assert_t;

    typedef cfg::callsite_stmt<number_t,VariableName>     callsite_t;
    typedef cfg::return_stmt<number_t,VariableName>       return_t;

    typedef cfg::array_assume_stmt<number_t,VariableName> arr_assume_t;
    typedef cfg::array_store_stmt<number_t,VariableName>  arr_store_t;
    typedef cfg::array_load_stmt<number_t,VariableName>   arr_load_t;
    typedef cfg::array_assign_stmt<number_t,VariableName> arr_assign_t;
    typedef cfg::ptr_store_stmt<number_t,VariableName>    ptr_store_t;
    typedef cfg::ptr_load_stmt<number_t,VariableName>     ptr_load_t;

    typedef cfg::ptr_assign_stmt<number_t,VariableName>   ptr_assign_t;
    typedef cfg::ptr_object_stmt<number_t,VariableName>   ptr_object_t;
    typedef cfg::ptr_function_stmt<number_t,VariableName> ptr_function_t;
    typedef cfg::ptr_null_stmt<number_t,VariableName>     ptr_null_t;
    typedef cfg::ptr_assume_stmt<number_t,VariableName>   ptr_assume_t;
    typedef cfg::ptr_assert_stmt<number_t,VariableName>   ptr_assert_t;

    typedef cfg::bool_binary_op<number_t,VariableName>   bool_bin_op_t;
    typedef cfg::bool_assign_cst<number_t,VariableName>  bool_assign_cst_t;
    typedef cfg::bool_assign_var<number_t,VariableName>  bool_assign_var_t;
    typedef cfg::bool_assume_stmt<number_t,VariableName> bool_assume_t;
    typedef cfg::bool_select_stmt<number_t,VariableName> bool_select_t;
    typedef cfg::bool_assert_stmt<number_t,VariableName> bool_assert_t;

   protected:

    virtual void exec (havoc_t&) { }
    virtual void exec (unreach_t&) { }
    virtual void exec (bin_op_t&)  { }
    virtual void exec (assign_t&) { }
    virtual void exec (assume_t&) { }
    virtual void exec (select_t&) { }
    virtual void exec (assert_t&) { }
    virtual void exec (callsite_t&) { }
    virtual void exec (return_t&) { }
    virtual void exec (arr_assume_t&) { }
    virtual void exec (arr_store_t&) { }
    virtual void exec (arr_load_t&) { }
    virtual void exec (arr_assign_t&) { }
    virtual void exec (ptr_store_t&) { }
    virtual void exec (ptr_load_t&) { }
    virtual void exec (ptr_assign_t&) { }
    virtual void exec (ptr_object_t&) { }
    virtual void exec (ptr_function_t&) { }
    virtual void exec (ptr_null_t&) { }
    virtual void exec (ptr_assume_t&) { }
    virtual void exec (ptr_assert_t&) { }
    virtual void exec (bool_bin_op_t&)  { }
    virtual void exec (bool_assign_cst_t&) { }
    virtual void exec (bool_assign_var_t&) { }
    virtual void exec (bool_assume_t&) { }
    virtual void exec (bool_select_t&) { }
    virtual void exec (bool_assert_t&) { }

   public: /* visitor api */

    void visit (havoc_t &s) { exec (s); }
    void visit (unreach_t &s) { exec (s); }
    void visit (bin_op_t &s) { exec (s); }
    void visit (assign_t &s) { exec (s); }
    void visit (assume_t &s) { exec (s); }
    void visit (select_t &s) { exec (s); }
    void visit (assert_t &s) { exec (s); }
    void visit (callsite_t &s) { exec (s); }
    void visit (return_t &s) { exec (s); }
    void visit (arr_assume_t &s) { exec (s); }
    void visit (arr_store_t &s) { exec (s); }
    void visit (arr_load_t &s) { exec (s); }
    void visit (arr_assign_t &s) { exec (s); }
    void visit (ptr_store_t &s) { exec (s); }
    void visit (ptr_load_t &s) { exec (s); }
    void visit (ptr_assign_t &s) { exec (s); }
    void visit (ptr_object_t &s) { exec (s); }
    void visit (ptr_function_t &s) { exec (s); }
    void visit (ptr_null_t &s) { exec (s); }
    void visit (ptr_assume_t &s) { exec (s); }
    void visit (ptr_assert_t &s) { exec (s); }
    void visit (bool_bin_op_t &s) { exec (s); }
    void visit (bool_assign_cst_t &s) { exec (s); }
    void visit (bool_assign_var_t &s) { exec (s); }
    void visit (bool_assume_t &s) { exec (s); }
    void visit (bool_select_t &s) { exec (s); }
    void visit (bool_assert_t &s) { exec (s); }
  };

  //! Abstract transformer for all statements except function calls
  //! which are ingnored in a sound manner.
  template<class AbsD>
  class intra_abs_transformer:
      public abs_transformer_api <typename AbsD::number_t, typename AbsD::varname_t>,
      public boost::noncopyable
  {

   public:
    typedef AbsD abs_dom_t;
    typedef typename abs_dom_t::number_t number_t;
    typedef typename abs_dom_t::varname_t varname_t;

   public:
    typedef abs_transformer_api <number_t, varname_t> abs_transform_api_t;
    using typename abs_transform_api_t::var_t;
    using typename abs_transform_api_t::lin_exp_t;
    using typename abs_transform_api_t::lin_cst_t;
    using typename abs_transform_api_t::lin_cst_sys_t;
    using typename abs_transform_api_t::havoc_t;
    using typename abs_transform_api_t::unreach_t;
    using typename abs_transform_api_t::bin_op_t;
    using typename abs_transform_api_t::assign_t;
    using typename abs_transform_api_t::assume_t;
    using typename abs_transform_api_t::select_t;
    using typename abs_transform_api_t::assert_t;
    using typename abs_transform_api_t::callsite_t;
    using typename abs_transform_api_t::arr_assume_t;
    using typename abs_transform_api_t::arr_load_t;
    using typename abs_transform_api_t::arr_store_t;
    using typename abs_transform_api_t::arr_assign_t;
    using typename abs_transform_api_t::ptr_load_t;
    using typename abs_transform_api_t::ptr_store_t;
    using typename abs_transform_api_t::ptr_assign_t;
    using typename abs_transform_api_t::ptr_object_t;
    using typename abs_transform_api_t::ptr_function_t;
    using typename abs_transform_api_t::ptr_null_t;
    using typename abs_transform_api_t::ptr_assume_t;
    using typename abs_transform_api_t::ptr_assert_t;
    using typename abs_transform_api_t::bool_bin_op_t;
    using typename abs_transform_api_t::bool_assign_cst_t;
    using typename abs_transform_api_t::bool_assign_var_t;
    using typename abs_transform_api_t::bool_assume_t;
    using typename abs_transform_api_t::bool_select_t;
    using typename abs_transform_api_t::bool_assert_t;

   protected:
    /// XXX: the transformer does not own m_inv and it does never copy
    ///      its content.
    abs_dom_t *m_inv;

   private:

    template <typename NumOrVar>
    void apply (abs_dom_t &inv, binary_operation_t op,
		varname_t x, varname_t y, NumOrVar z)
    {
      auto op1 = conv_op<ikos::operation_t> (op);
      auto op2 = conv_op<ikos::div_operation_t> (op);
      auto op3 = conv_op<ikos::bitwise_operation_t> (op);
      if (op1)      inv.apply (*op1, x, y, z);
      else if (op2) inv.apply (*op2, x, y, z);
      else if (op3) inv.apply (*op3, x, y, z);
      else CRAB_ERROR("unsupported binary operator", op);
    }

   public:

    intra_abs_transformer (abs_dom_t* inv):
      m_inv (inv) {
      if (!m_inv)
	CRAB_ERROR ("Invariant passed to transformer cannot be null!");
    }

    virtual ~intra_abs_transformer () { }

    void set (abs_dom_t& inv) { m_inv = &inv;}
    abs_dom_t& inv () { assert(m_inv); return *m_inv; }

    void exec (bin_op_t& stmt)
    {
      assert(m_inv);

      auto op1 = stmt.left ();
      auto op2 = stmt.right ();
      if (op1.get_variable () && op2.get_variable ())
      {
        apply (*m_inv, stmt.op (),
               stmt.lhs ().name(),
               (*op1.get_variable ()).name(),
               (*op2.get_variable ()).name());
      }
      else
      {
        assert (op1.get_variable () && op2.is_constant ());
        apply (*m_inv, stmt.op (),
               stmt.lhs ().name (),
               (*op1.get_variable ()).name(),
               op2.constant ());
      }
    }

    void exec (select_t& stmt)
    {
      assert(m_inv);

      abs_dom_t inv1 (*m_inv);
      abs_dom_t inv2 (*m_inv);
      inv1 += stmt.cond ();
      inv2 += stmt.cond ().negate ();
      if (inv2.is_bottom()) {
        inv1.assign (stmt.lhs().name (),stmt.left());
        *m_inv = inv1;
      }
      else if (inv1.is_bottom ()) {
        inv2.assign (stmt.lhs().name (),stmt.right());
        *m_inv = inv2;
      }
      else {
        inv1.assign (stmt.lhs().name (),stmt.left());
        inv2.assign (stmt.lhs().name (),stmt.right());
        *m_inv = inv1 | inv2;
      }
    }

    void exec (assign_t& stmt)
    {
      assert(m_inv);

      m_inv->assign (stmt.lhs().name (), lin_exp_t (stmt.rhs()));

    }

    void exec (assume_t& stmt)
    {
      assert(m_inv);

      *m_inv += stmt.constraint();
    }

    void exec (assert_t& stmt)
    {
      assert(m_inv);

      abs_dom_t cst;
      cst += stmt.constraint();
      abs_dom_t meet = cst & *m_inv;
      if (meet.is_bottom ())
        *m_inv = abs_dom_t::bottom (); // assertion does not definitely hold.
      else
	*m_inv += stmt.constraint ();
    }

    void exec (bool_assign_cst_t& stmt)
    {
      assert(m_inv);
      (*m_inv).assign_bool_cst (stmt.lhs (), stmt.rhs ());
    }

    void exec (bool_assign_var_t& stmt)
    {
      assert(m_inv);
      (*m_inv).assign_bool_var (stmt.lhs (), stmt.rhs ());
    }

    void exec (bool_bin_op_t& stmt)
    {
      assert(m_inv);
      if (auto op = conv_op<domains::bool_operation_t> (stmt.op()))
	(*m_inv).apply_binary_bool (*op, stmt.lhs(), stmt.left(), stmt.right ());
    }

    void exec (bool_assume_t& stmt)
    {
      assert(m_inv);
      (*m_inv).assume_bool (stmt.cond(), stmt.is_negated ());
    }

    void exec (bool_select_t& stmt)
    {
      assert(m_inv);
      CRAB_WARN ("TODO: transformer for bool select");
    }

    void exec (bool_assert_t& stmt)
    {
      assert(m_inv);
      abs_dom_t inv;
      inv.assume_bool (stmt.cond (), false);
      abs_dom_t meet = inv & *m_inv;
      if (meet.is_bottom ())
        *m_inv = abs_dom_t::bottom (); // assertion does not definitely hold.
      else
	(*m_inv).assume_bool (stmt.cond(), false);
    }

    void exec (havoc_t& stmt)
    { assert(m_inv); (*m_inv) -= stmt.variable(); }

    void exec (unreach_t& stmt)
    { assert(m_inv); *m_inv = abs_dom_t::bottom (); }

    void exec (arr_assume_t &stmt)
    { assert(m_inv);
      m_inv->array_assume (stmt.array (), stmt.array_type (),
			   stmt.lb_index (), stmt.ub_index (),
			   stmt.var ());
    }

    void exec (arr_store_t &stmt)
    { assert(m_inv);
      m_inv->array_store (stmt.array(), stmt.array_type (),
			  stmt.index (), stmt.value (),
			  stmt.elem_size(), stmt.is_singleton ());
    }

    void exec (arr_load_t  &stmt)
    { assert(m_inv);
      m_inv->array_load (stmt.lhs (),
			 stmt.array (), stmt.array_type (),
			 stmt.index(), stmt.elem_size());
    }

    void exec (arr_assign_t  &stmt)
    { assert(m_inv);
      m_inv->array_assign (stmt.lhs (), stmt.rhs (), stmt.array_type ());
    }

    void exec (ptr_null_t & stmt)
    { assert(m_inv); m_inv->pointer_mk_null (stmt.lhs ()); }

    void exec (ptr_object_t & stmt)
    { assert(m_inv); m_inv->pointer_mk_obj (stmt.lhs (), stmt.rhs()); }

    void exec (ptr_assign_t & stmt)
    { assert(m_inv); m_inv->pointer_assign (stmt.lhs (), stmt.rhs (), stmt.offset ()); }

    void exec (ptr_function_t & stmt)
    { assert(m_inv); m_inv->pointer_function (stmt.lhs (), stmt.rhs ()); }

    void exec (ptr_load_t & stmt)
    { assert(m_inv); m_inv->pointer_load (stmt.lhs (), stmt.rhs ()); }

    void exec (ptr_store_t & stmt)
    { assert(m_inv); m_inv->pointer_store (stmt.lhs (), stmt.rhs ()); }

    void exec (ptr_assume_t& stmt)
    { assert(m_inv); m_inv->pointer_assume (stmt.constraint ()); }

    void exec (ptr_assert_t& stmt)
    { assert(m_inv); m_inv->pointer_assert (stmt.constraint ()); }

    virtual void exec (callsite_t &cs)
    {
      assert(m_inv);
      for (auto vt: cs.get_lhs())
	(*m_inv) -= vt.first;  // havoc
    }
  };

  // Helper for function calls.
  template <typename AbsDom>
  static void unify (AbsDom &inv, variable_type ty,
		     typename AbsDom::varname_t lhs, typename AbsDom::varname_t rhs)
  {
    typedef typename AbsDom::linear_expression_t linear_expression_t;
    typedef typename AbsDom::number_t number_t;
    switch (ty)
    {
      case BOOL_TYPE:
	CRAB_WARN ("TODO: unify bool types");
	break;
      case INT_TYPE:
	inv.assign (lhs, linear_expression_t (rhs));
	break;
      case PTR_TYPE:
	inv.pointer_assign (lhs, rhs, number_t (0));
	break;
      case ARR_INT_TYPE:
      case ARR_PTR_TYPE:
	inv.array_assign (lhs, rhs, ty);
	break;
      default:
	CRAB_ERROR ("unsuported type");
    }
  }

  //! Transformer specialized for computing summaries.
  //  class SumTable stores the summaries
  template<class SumTable>
  class bu_summ_abs_transformer:
      public intra_abs_transformer <typename SumTable::abs_domain_t> {

   public:

    typedef typename SumTable::abs_domain_t summ_abs_domain_t;
    typedef summ_abs_domain_t abs_dom_t;
    typedef typename abs_dom_t::number_t number_t;

   private:

    typedef intra_abs_transformer<abs_dom_t> intra_abs_transform_t;
    typedef typename intra_abs_transform_t::abs_transform_api_t abs_transform_api_t;
    typedef typename abs_dom_t::varname_t varname_t;
    typedef typename abs_dom_t::linear_expression_t linear_expression_t;

    SumTable* m_sum_tbl;

  public:

    using typename abs_transform_api_t::callsite_t;

    static void reuse_summary (abs_dom_t& caller,
                               const callsite_t& cs,
                               const typename SumTable::Summary& summ) {

      // error if cs and function declaration associated with summ are
      // not type consistent
      summ.check_type_consistency (cs);

      CRAB_LOG("inter",
               crab::outs () << "    Reuse summary at " << cs << "\n";
               crab::outs () << "    Summary:";  summ.write(crab::outs ());
               crab::outs () << "\n");

      std::set<varname_t> actuals, formals;
      // --- matching formal and actual parameters
      auto pars = summ.get_params ();
      unsigned i=0;
      // XXX: propagating down
      for (auto p : pars) {
        auto a = cs.get_arg_name (i);
        if (!(a == p)) {
          CRAB_LOG ("inter",
                    crab::outs () << "\t\tPropagate from caller to callee "
                                  << p << ":=" << a << "\n");
	  unify (caller, cs.get_arg_type(i), p, a);
        }
        ++i;
        actuals.insert (a); formals.insert (p);
      }

      // --- meet caller's inv with summ
      auto sum_inv = summ.get_sum ();
      caller = caller & sum_inv;
      CRAB_LOG("inter",
	       crab::outs() << "\t\tAfter meet caller and summary: "
	                    <<  caller << "\n");

      // --- matching callsite's lhs and callee's return value
      // XXX: propagate from the return values in the callee to the
      // lhs variables of the callsite in the caller.
      auto const &caller_vts = cs.get_lhs ();
      auto const &callee_rs = summ.get_ret_vals ();
      assert (caller_vts.size () == callee_rs.size ());

      auto caller_it = caller_vts.begin();
      auto caller_et = caller_vts.end();
      auto callee_it = callee_rs.begin();

      // XXX: propagating up
      for (; caller_it != caller_et; ++caller_it, ++callee_it){
        auto vt = *caller_it;
        auto r = *callee_it;
        CRAB_LOG ("inter",
                  crab::outs () << "\t\tPropagate from callee to caller "
		                << vt.first << ":=" << r << "\n");
	unify (caller, vt.second, vt.first, r);
        actuals.insert (vt.first); formals.insert (r);
      }

      // --- remove from caller only formal parameters so we can keep
      //     as much context from the caller as possible
      std::set<varname_t> vs;
      boost::set_difference (formals, actuals, std::inserter(vs, vs.end ()));
      domains::domain_traits<abs_dom_t>::forget (caller, vs.begin (), vs.end ());

      CRAB_LOG ("inter",
                crab::outs() << "\t\tAfter forgetting formal parameters {";
                for (auto v: vs) crab::outs () << v << ";";
                crab::outs () << "}=" <<  caller << "\n");
    }

   public:

    bu_summ_abs_transformer(abs_dom_t* inv, SumTable* sum_tbl)
        : intra_abs_transform_t(inv), m_sum_tbl (sum_tbl) { }

    virtual void exec (callsite_t &cs) override
    {
      if (!m_sum_tbl)
        CRAB_WARN ("The summary table is empty: ignored analysis of callsite");
      else
      {
	if (m_sum_tbl->hasSummary (cs)) {
	  auto &summ = m_sum_tbl->get (cs);
	  reuse_summary (*(this->m_inv), cs, summ);
	}
	else
	{
	  CRAB_LOG("inter",
		   crab::outs() << "\tSummary not found for " << cs << "\n");
	  for (auto vt: cs.get_lhs())
	    *(this->m_inv) -= vt.first;  // havoc
	}
      }
    }
  };

  /// Conversion between domains
  template<typename Domain>
  inline void convert_domains (Domain from, Domain& to) { to = from; }

  template<typename Domain1, typename Domain2>
  inline void convert_domains (Domain1 from, Domain2& to) {
    CRAB_WARN("Converting from " + Domain1::getDomainName () + " to " +
              Domain2::getDomainName () + " might lose precision " +
              "if " + Domain1::getDomainName () + " is an abstraction of " +
              Domain2::getDomainName ());
    for (auto cst : from.to_linear_constraint_system ())
    { to += cst; }
  }

  // Transformer specialized for performing top-down forward traversal
  // while reusing summaries at the callsites.
  // class CallCtxTable stores the calling context.
  template<class SumTable, class CallCtxTable>
  class td_summ_abs_transformer:
      public intra_abs_transformer<typename CallCtxTable::abs_domain_t> {

   public:

    typedef typename SumTable::abs_domain_t     summ_abs_domain_t;
    typedef typename CallCtxTable::abs_domain_t call_abs_domain_t;
    typedef call_abs_domain_t abs_dom_t;
    typedef typename abs_dom_t::number_t number_t;

   private:

    typedef intra_abs_transformer<abs_dom_t> intra_abs_transform_t;

   public:

    typedef typename intra_abs_transform_t::abs_transform_api_t abs_transform_api_t;
    using typename abs_transform_api_t::callsite_t;

   private:

    SumTable* m_sum_tbl;
    CallCtxTable* m_call_tbl;

   public:

    td_summ_abs_transformer (abs_dom_t* inv, SumTable* sum_tbl, CallCtxTable* call_tbl)
        : intra_abs_transform_t(inv),
          m_sum_tbl (sum_tbl), m_call_tbl (call_tbl) { }

    virtual void exec (callsite_t &cs) override
    {
      if (!m_sum_tbl) {
        CRAB_WARN ("The summary table is empty");
      } else if (m_sum_tbl->hasSummary (cs)) {
        auto &summ = m_sum_tbl->get (cs);

        // error if cs and function declaration associated with summ
        // are not type consistent
        summ.check_type_consistency (cs);

        CRAB_LOG ("inter",
                  crab::outs () << "    Pluging caller context into callee\n");
        ///////
        /// Generate the callee context and store it.
        ///////
	typedef typename abs_dom_t::varname_t varname_t;

        abs_dom_t callee_ctx_inv (*(this->m_inv));
        // --- matching formal and actual parameters
        // XXX: propagating down
        unsigned i=0;
        auto pars = summ.get_params ();
        for (varname_t p : pars) {
          varname_t a = cs.get_arg_name (i);
          if (!(a == p))
	    unify (callee_ctx_inv, cs.get_arg_type(i), p, a);
          ++i;
        }

        // --- project only onto formal parameters
        domains::domain_traits<abs_dom_t>::project (callee_ctx_inv,
                                                    pars.begin (),
                                                    pars.end ());
        // --- store the callee context
        CRAB_LOG ("inter",
                  crab::outs() << "\t\tCallee context stored: "
                               << callee_ctx_inv << "\n");
        m_call_tbl->insert (cs, callee_ctx_inv);

        /////
        // Generate the continuation at the caller
        /////

        // --- convert this->m_inv to the language of summ_abs_dom_t (summ)
        summ_abs_domain_t caller_ctx_inv;
        convert_domains(*(this->m_inv), caller_ctx_inv);

        CRAB_LOG ("inter",
                  crab::outs() << "\t\tCaller context: " <<  caller_ctx_inv << "\n");

        // --- reuse summary to do the continuation
        bu_summ_abs_transformer<SumTable>::
	  reuse_summary (caller_ctx_inv, cs, summ);
        CRAB_LOG ("inter",
                  crab::outs() << "\t\tCaller context after plugin summary: "
                               << caller_ctx_inv << "\n");

        // --- convert back inv to the language of abs_dom_t
        convert_domains(caller_ctx_inv, *(this->m_inv));
        CRAB_LOG ("inter",
                  crab::outs() << "\t\tCaller continuation after "
  		               <<  cs << "=" <<  *(this->m_inv) << "\n");
        return;
      } else {
        // --- no summary found: do nothing
      }

      // We could not reuse a summary so we just havoc lhs of the call
      // site.
      for (auto vt: cs.get_lhs())
        *(this->m_inv) -= vt.first;
    }
  };

  } // end namespace
} // end namespace
#endif


#ifndef BOOST_NUMERIC_CHECKED_RESULT_OPERATIONS
#define BOOST_NUMERIC_CHECKED_RESULT_OPERATIONS

//  Copyright (c) 2012 Robert Ramey
//
// 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)

// Implemenation of arithmetic on "extended" integers.
// Extended integers are defined in terms of C++ primitive integers as
//     a) an interger range
//     b) extra elements +inf, -inf, indeterminate
//
// Integer operations are closed on the set of extended integers
// but operations are not necessarily associative when they result in the
// extensions +inf, -inf, and indeterminate
//
// in this code, the type "checked_result<T>" where T is some
// integer type is an "extended" integer.

#include <cassert>

#include <boost/logic/tribool.hpp>

#include "checked_result.hpp"
#include "checked_integer.hpp"

//////////////////////////////////////////////////////////////////////////
// the following idea of "value_type" is used by several of the operations
// defined by checked_result arithmetic.

namespace boost {
namespace safe_numerics {

template<typename T>
constexpr inline void display(const boost::safe_numerics::checked_result<T> & c){
    switch(c.m_e){
    case safe_numerics_error::success:
        std::terminate();
    case safe_numerics_error::positive_overflow_error:    // result is above representational maximum
        std::terminate();
    case safe_numerics_error::negative_overflow_error:    // result is below representational minimum
        std::terminate();
    case safe_numerics_error::domain_error:               // one operand is out of valid range
        std::terminate();
    case safe_numerics_error::range_error:                // result cannot be produced for this operation
        std::terminate();
    case safe_numerics_error::precision_overflow_error:   // result lost precision
        std::terminate();
    case safe_numerics_error::underflow_error:            // result is too small to be represented
        std::terminate();
    case safe_numerics_error::negative_value_shift:       // negative value in shift operator
        std::terminate();
    case safe_numerics_error::negative_shift:             // shift a negative value
        std::terminate();
    case safe_numerics_error::shift_too_large:            // l/r shift exceeds variable size
        std::terminate();
    case safe_numerics_error::uninitialized_value:        // creating of uninitialized value
        std::terminate();
    }
}

//////////////////////////////////////////////////////////////////////////
// implement C++ operators for check_result<T>

struct sum_value_type {
    // characterization of various values
    const enum flag {
        known_value = 0,
        less_than_min,
        greater_than_max,
        indeterminate,
        count
    } m_flag;
    template<class T>
    constexpr flag to_flag(const checked_result<T> & t) const {
        switch(static_cast<safe_numerics_error>(t)){
        case safe_numerics_error::success:
            return known_value;
        case safe_numerics_error::negative_overflow_error:
            // result is below representational minimum
            return less_than_min;
        case safe_numerics_error::positive_overflow_error:
            // result is above representational maximum
            return greater_than_max;
        default:
            return indeterminate;
        }
    }
    template<class T>
    constexpr sum_value_type(const checked_result<T> & t) :
        m_flag(to_flag(t))
    {}
    constexpr operator std::uint8_t () const {
        return static_cast<std::uint8_t>(m_flag);
    }
};

// integers addition
template<class T>
typename std::enable_if<
    std::is_integral<T>::value,
    checked_result<T>
>::type
constexpr inline operator+(
    const checked_result<T> & t,
    const checked_result<T> & u
){
    using value_type = sum_value_type;
    const std::uint8_t order = static_cast<std::uint8_t>(value_type::count);

    // note major pain.  Clang constexpr multi-dimensional array is fine.
    // but gcc doesn't permit a multi-dimensional array to be be constexpr.
    // so we need to some ugly gymnastics to make our system work for all
    // all systems.
    const enum safe_numerics_error result[order * order] = {
        // t == known_value
        //{
            // u == ...
            safe_numerics_error::success,                   // known_value,
            safe_numerics_error::negative_overflow_error,   // less_than_min,
            safe_numerics_error::positive_overflow_error,   // greater_than_max,
            safe_numerics_error::range_error,               // indeterminate,
        //},
        // t == less_than_min,
        //{
            // u == ...
            safe_numerics_error::negative_overflow_error,   // known_value,
            safe_numerics_error::negative_overflow_error,   // less_than_min,
            safe_numerics_error::range_error,               // greater_than_max,
            safe_numerics_error::range_error,               // indeterminate,
        //},
        // t == greater_than_max,
        //{
            // u == ...
            safe_numerics_error::positive_overflow_error,   // known_value,
            safe_numerics_error::range_error,               // less_than_min,
            safe_numerics_error::positive_overflow_error,   // greater_than_max,
            safe_numerics_error::range_error,               // indeterminate,
        //},
        // t == indeterminate,
        //{
            // u == ...
            safe_numerics_error::range_error,      // known_value,
            safe_numerics_error::range_error,      // less_than_min,
            safe_numerics_error::range_error,      // greater_than_max,
            safe_numerics_error::range_error,      // indeterminate,
        //},
    };

    const value_type tx(t);
    const value_type ux(u);

    const safe_numerics_error e = result[tx * order + ux];
    if(safe_numerics_error::success == e)
        return checked::add<T>(t, u);
    return checked_result<T>(e, "addition result");
}

// unary +
template<class T>
typename std::enable_if<
    std::is_integral<T>::value,
    checked_result<T>
>::type
constexpr inline operator+(
    const checked_result<T> & t
){
    return t;
}

// integers subtraction
template<class T>
typename std::enable_if<
    std::is_integral<T>::value,
    checked_result<T>
>::type
constexpr inline operator-(
    const checked_result<T> & t,
    const checked_result<T> & u
){
    using value_type = sum_value_type;
    constexpr const std::uint8_t order = static_cast<std::uint8_t>(value_type::count);

    constexpr const enum safe_numerics_error result[order * order] = {
        // t == known_value
        //{
            // u == ...
            safe_numerics_error::success,                   // known_value,
            safe_numerics_error::positive_overflow_error,   // less_than_min,
            safe_numerics_error::negative_overflow_error,   // greater_than_max,
            safe_numerics_error::range_error,               // indeterminate,
        //},
        // t == less_than_min,
        //{
            // u == ...
            safe_numerics_error::negative_overflow_error,   // known_value,
            safe_numerics_error::range_error,               // less_than_min,
            safe_numerics_error::negative_overflow_error,   // greater_than_max,
            safe_numerics_error::range_error,               // indeterminate,
        //},
        // t == greater_than_max,
        //{
            // u == ...
            safe_numerics_error::positive_overflow_error,   // known_value,
            safe_numerics_error::positive_overflow_error,   // less_than_min,
            safe_numerics_error::range_error,               // greater_than_max,
            safe_numerics_error::range_error,               // indeterminate,
        //},
        // t == indeterminate,
        //{
            // u == ...
            safe_numerics_error::range_error,               // known_value,
            safe_numerics_error::range_error,               // less_than_min,
            safe_numerics_error::range_error,               // greater_than_max,
            safe_numerics_error::range_error,               // indeterminate,
        //},
    };

    const value_type tx(t);
    const value_type ux(u);

    const safe_numerics_error e = result[tx * order + ux];
    if(safe_numerics_error::success == e)
        return checked::subtract<T>(t, u);
    return checked_result<T>(e, "subtraction result");
}

// unary -
template<class T>
typename std::enable_if<
    std::is_integral<T>::value,
    checked_result<T>
>::type
constexpr inline operator-(
    const checked_result<T> & t
){
//    assert(false);
    return checked_result<T>(0) - t;
}

struct product_value_type {
    // characterization of various values
    const enum flag {
        less_than_min = 0,
        less_than_zero,
        zero,
        greater_than_zero,
        greater_than_max,
        indeterminate,
        // count of number of cases for values
        count,
        // temporary values for special cases
        t_value,
        u_value,
        z_value
    } m_flag;
    template<class T>
    constexpr flag to_flag(const checked_result<T> & t) const {
        switch(static_cast<safe_numerics_error>(t)){
        case safe_numerics_error::success:
            return (t < checked_result<T>(0))
                ? less_than_zero
                : (t > checked_result<T>(0))
                ? greater_than_zero
                : zero;
        case safe_numerics_error::negative_overflow_error:
            // result is below representational minimum
            return less_than_min;
        case safe_numerics_error::positive_overflow_error:
            // result is above representational maximum
            return greater_than_max;
        default:
            return indeterminate;
        }
    }
    template<class T>
    constexpr product_value_type(const checked_result<T> & t) :
        m_flag(to_flag(t))
    {}
    constexpr operator std::uint8_t () const {
        return static_cast<std::uint8_t>(m_flag);
    }
};

// integers multiplication
template<class T>
typename std::enable_if<
    std::is_integral<T>::value,
    checked_result<T>
>::type
constexpr inline operator*(
    const checked_result<T> & t,
    const checked_result<T> & u
){
    using value_type = product_value_type;
    const std::uint8_t order = static_cast<std::uint8_t>(value_type::count);

    constexpr const enum value_type::flag result[order * order] = {
        // t == less_than_min
        //{
            // u == ...
            value_type::greater_than_max,   // less_than_min,
            value_type::greater_than_max,   // less_than_zero,
            value_type::zero,               // zero,
            value_type::less_than_min,      // greater_than_zero,
            value_type::less_than_min,      // greater than max,
            value_type::indeterminate,      // indeterminate,
        //},
        // t == less_than_zero,
        //{
            // u == ...
            value_type::greater_than_max,   // less_than_min,
            value_type::greater_than_zero,  // less_than_zero,
            value_type::zero,               // zero,
            value_type::less_than_zero,     // greater_than_zero,
            value_type::less_than_min,      // greater than max,
            value_type::indeterminate,      // indeterminate,
        //},
        // t == zero,
        //{
            // u == ...
            value_type::zero,               // less_than_min,
            value_type::zero,               // less_than_zero,
            value_type::zero,               // zero,
            value_type::zero,               // greater_than_zero,
            value_type::zero,               // greater than max,
            value_type::indeterminate,      // indeterminate,
        //},
        // t == greater_than_zero,
        //{
            // u == ...
            value_type::less_than_min,      // less_than_min,
            value_type::less_than_zero,     // less_than_zero,
            value_type::zero,               // zero,
            value_type::greater_than_zero,  // greater_than_zero,
            value_type::greater_than_max,   // greater than max,
            value_type::indeterminate,      // indeterminate,
        //},
        // t == greater_than_max
        //{
            value_type::less_than_min,      // less_than_min,
            value_type::less_than_min,      // less_than_zero,
            value_type::zero,               // zero,
            value_type::greater_than_max,   // greater_than_zero,
            value_type::greater_than_max,   // greater than max,
            value_type::indeterminate,      // indeterminate,
        //},
        // t == indeterminate
        //{
            value_type::indeterminate,      // less_than_min,
            value_type::indeterminate,      // less_than_zero,
            value_type::indeterminate,      // zero,
            value_type::indeterminate,      // greater_than_zero,
            value_type::indeterminate,      // greater than max,
            value_type::indeterminate,      // indeterminate,
        //}
    };

    const value_type tx(t);
    const value_type ux(u);

    switch(result[tx * order + ux]){
        case value_type::less_than_min:
            return safe_numerics_error::negative_overflow_error;
        case value_type::zero:
            return T(0);
        case value_type::greater_than_max:
            return safe_numerics_error::positive_overflow_error;
        case value_type::less_than_zero:
        case value_type::greater_than_zero:
            return checked::multiply<T>(t, u);
        case value_type::indeterminate:
            return safe_numerics_error::range_error;
        default:
            assert(false);
        }
    return checked_result<T>(0); // to suppress msvc warning
}

// integers division
template<class T>
typename std::enable_if<
    std::is_integral<T>::value,
    checked_result<T>
>::type
constexpr inline operator/(
    const checked_result<T> & t,
    const checked_result<T> & u
){
    using value_type = product_value_type;
    const std::uint8_t order = static_cast<std::uint8_t>(value_type::count);

    constexpr const enum value_type::flag result[order * order] = {
        // t == less_than_min
        //{
            // u == ...
            value_type::indeterminate,   // less_than_min,
            value_type::greater_than_max,   // less_than_zero,
            value_type::less_than_min,      // zero,
            value_type::less_than_min,      // greater_than_zero,
            value_type::less_than_min,      // greater than max,
            value_type::indeterminate,      // indeterminate,
        //},
        // t == less_than_zero,
        //{
            // u == ...
            value_type::zero,               // less_than_min,
            value_type::greater_than_zero,  // less_than_zero,
            value_type::less_than_min,      // zero,
            value_type::less_than_zero,     // greater_than_zero,
            value_type::zero,               // greater than max,
            value_type::indeterminate,      // indeterminate,
        //},
        // t == zero,
        //{
            // u == ...
            value_type::zero,               // less_than_min,
            value_type::zero,               // less_than_zero,
            value_type::indeterminate,      // zero,
            value_type::zero,               // greater_than_zero,
            value_type::zero,               // greater than max,
            value_type::indeterminate,               // indeterminate,
        //},
        // t == greater_than_zero,
        //{
            // u == ...
            value_type::zero,               // less_than_min,
            value_type::less_than_zero,     // less_than_zero,
            value_type::greater_than_max,   // zero,
            value_type::greater_than_zero,  // greater_than_zero,
            value_type::zero,               // greater than max,
            value_type::indeterminate,      // indeterminate,
        //},
        // t == greater_than_max
        //{
            value_type::less_than_min,      // less_than_min,
            value_type::less_than_min,      // less_than_zero,
            value_type::greater_than_max,   // zero,
            value_type::greater_than_max,   // greater_than_zero,
            value_type::indeterminate,   // greater than max,
            value_type::indeterminate,      // indeterminate,
        //},
        // t == indeterminate
        //{
            value_type::indeterminate,      // less_than_min,
            value_type::indeterminate,      // less_than_zero,
            value_type::indeterminate,      // zero,
            value_type::indeterminate,      // greater_than_zero,
            value_type::indeterminate,      // greater than max,
            value_type::indeterminate,      // indeterminate,
        //}
    };

    const value_type tx(t);
    const value_type ux(u);

    switch(result[tx * order + ux]){
        case value_type::less_than_min:
            return safe_numerics_error::negative_overflow_error;
        case value_type::zero:
            return 0;
        case value_type::greater_than_max:
            return safe_numerics_error::positive_overflow_error;
        case value_type::less_than_zero:
        case value_type::greater_than_zero:
            return checked::divide<T>(t, u);
        case value_type::indeterminate:
            return safe_numerics_error::range_error;
        default:
            assert(false);
    }
    return checked_result<T>(0); // to suppress msvc warning
}

// integers modulus
template<class T>
typename std::enable_if<
    std::is_integral<T>::value,
    checked_result<T>
>::type
constexpr inline operator%(
    const checked_result<T> & t,
    const checked_result<T> & u
){
    using value_type = product_value_type;
    const std::uint8_t order = static_cast<std::uint8_t>(value_type::count);

    constexpr const enum value_type::flag result[order * order] = {
        // t == less_than_min
        //{
            // u == ...
            value_type::indeterminate,      // less_than_min,
            value_type::z_value,            // less_than_zero,
            value_type::indeterminate,      // zero,
            value_type::z_value,            // greater_than_zero,
            value_type::indeterminate,      // greater than max,
            value_type::indeterminate,      // indeterminate,
        //},
        // t == less_than_zero,
        //{
            // u == ...
            value_type::t_value,            // less_than_min,
            value_type::greater_than_zero,  // less_than_zero,
            value_type::indeterminate,      // zero,
            value_type::less_than_zero,     // greater_than_zero,
            value_type::t_value,            // greater than max,
            value_type::indeterminate,      // indeterminate,
        //},
        // t == zero,
        //{
            // u == ...
            value_type::zero,               // less_than_min,
            value_type::zero,               // less_than_zero,
            value_type::indeterminate,      // zero,
            value_type::zero,               // greater_than_zero,
            value_type::zero,               // greater than max,
            value_type::indeterminate,      // indeterminate,
        //},
        // t == greater_than_zero,
        //{
            // u == ...
            value_type::t_value,            // less_than_min,
            value_type::less_than_zero,     // less_than_zero,
            value_type::indeterminate,      // zero,
            value_type::greater_than_zero,  // greater_than_zero,
            value_type::t_value,            // greater than max,
            value_type::indeterminate,      // indeterminate,
        //},
        // t == greater_than_max
        //{
            value_type::indeterminate,      // less_than_min,
            value_type::u_value,            // less_than_zero,
            value_type::indeterminate,      // zero,
            value_type::u_value,            // greater_than_zero,
            value_type::indeterminate,      // greater than max,
            value_type::indeterminate,      // indeterminate,
        //},
        // t == indeterminate
        //{
            value_type::indeterminate,      // less_than_min,
            value_type::indeterminate,      // less_than_zero,
            value_type::indeterminate,      // zero,
            value_type::indeterminate,      // greater_than_zero,
            value_type::indeterminate,      // greater than max,
            value_type::indeterminate,      // indeterminate,
        //}
    };

    const value_type tx(t);
    const value_type ux(u);

    switch(result[tx * order + ux]){
        case value_type::zero:
            return 0;
        case value_type::less_than_zero:
        case value_type::greater_than_zero:
            return checked::modulus<T>(t, u);
        case value_type::indeterminate:
            return safe_numerics_error::range_error;
        case value_type::t_value:
            return t;
        case value_type::u_value:
            return checked::subtract<T>(u, 1);
        case value_type::z_value:
            return checked::subtract<T>(1, u);
        case value_type::greater_than_max:
        case value_type::less_than_min:
        default:
            assert(false);
    }
    // suppress msvc warning
    return checked_result<T>(0);
}

// comparison operators

template<class T>
constexpr boost::logic::tribool operator<(
    const checked_result<T> & t,
    const checked_result<T> & u
){
    using value_type = sum_value_type;
    constexpr const std::uint8_t order = static_cast<std::uint8_t>(value_type::count);

    // the question arises about how to order values of type greater_than_min.
    // that is: what should greater_than_min < greater_than_min return.
    //
    // a) return indeterminate because we're talking about the "true" values for
    //    which greater_than_min is a placholder.
    //
    // b) return false because the two values are "equal"
    //
    // for our purposes, a) seems the better interpretation.

    enum class result_type : std::uint8_t {
        runtime,
        false_value,
        true_value,
        indeterminate,
    };
    constexpr const result_type resultx[order * order]{
        // t == known_value
        //{
            // u == ...
            result_type::runtime,       // known_value,
            result_type::false_value,   // less_than_min,
            result_type::true_value,    // greater_than_max,
            result_type::indeterminate, // indeterminate,
        //},
        // t == less_than_min
        //{
            // u == ...
            result_type::true_value,    // known_value,
            result_type::indeterminate, // less_than_min, see above argument
            result_type::true_value,    // greater_than_max,
            result_type::indeterminate, // indeterminate,
        //},
        // t == greater_than_max
        //{
            // u == ...
            result_type::false_value,   // known_value,
            result_type::false_value,   // less_than_min,
            result_type::indeterminate, // greater_than_max, see above argument
            result_type::indeterminate, // indeterminate,
        //},
        // t == indeterminate
        //{
            // u == ...
            result_type::indeterminate, // known_value,
            result_type::indeterminate, // less_than_min,
            result_type::indeterminate, // greater_than_max,
            result_type::indeterminate, // indeterminate,
        //},
    };

    const value_type tx(t);
    const value_type ux(u);

    switch(resultx[tx * order + ux]){
    case result_type::runtime:
        return static_cast<const T &>(t) < static_cast<const T &>(u);
    case result_type::false_value:
        return false;
    case result_type::true_value:
        return true;
    case result_type::indeterminate:
        return boost::logic::indeterminate;
    default:
        assert(false);
    }
    return true;
}

template<class T>
constexpr boost::logic::tribool
operator>=(
    const checked_result<T> & t,
    const checked_result<T> & u
){
    return !(t < u);
}

template<class T>
constexpr boost::logic::tribool
operator>(
    const checked_result<T> & t,
    const checked_result<T> & u
){
    return u < t;
}

template<class T>
constexpr boost::logic::tribool
operator<=(
    const checked_result<T> & t,
    const checked_result<T> & u
){
    return !(u < t);
}

template<class T>
constexpr boost::logic::tribool
operator==(
    const checked_result<T> & t,
    const checked_result<T> & u
){
    using value_type = sum_value_type;
    constexpr const std::uint8_t order = static_cast<std::uint8_t>(value_type::count);

    enum class result_type : std::uint8_t {
        runtime,
        false_value,
        true_value,
        indeterminate,
    };

    constexpr const result_type result[order * order]{
        // t == known_value
        //{
            // u == ...
            result_type::runtime,       // known_value,
            result_type::false_value,   // less_than_min,
            result_type::false_value,   // greater_than_max,
            result_type::indeterminate, // indeterminate,
        //},
        // t == less_than_min
        //{
            // u == ...
            result_type::false_value,   // known_value,
            result_type::indeterminate, // less_than_min,
            result_type::false_value,   // greater_than_max,
            result_type::indeterminate, // indeterminate,
        //},
        // t == greater_than_max
        //{
            // u == ...
            result_type::false_value,   // known_value,
            result_type::false_value,   // less_than_min,
            result_type::indeterminate, // greater_than_max,
            result_type::indeterminate, // indeterminate,
        //},
        // t == indeterminate
        //{
            // u == ...
            result_type::indeterminate, // known_value,
            result_type::indeterminate, // less_than_min,
            result_type::indeterminate, // greater_than_max,
            result_type::indeterminate, // indeterminate,
        //},
    };

    const value_type tx(t);
    const value_type ux(u);

    switch(result[tx * order + ux]){
    case result_type::runtime:
        return static_cast<const T &>(t) == static_cast<const T &>(u);
    case result_type::false_value:
        return false;
    case result_type::true_value:
        return true;
    case result_type::indeterminate:
        return boost::logic::indeterminate;
    default:
        assert(false);
    }
    // suppress msvc warning - not all control paths return a value
    return false;
}

template<class T>
constexpr boost::logic::tribool
operator!=(
    const checked_result<T> & t,
    const checked_result<T> & u
){
    return ! (t == u);
}

template<class T>
typename std::enable_if<
    std::is_integral<T>::value,
    checked_result<T>
>::type
constexpr inline operator>>(
    const checked_result<T> & t,
    const checked_result<T> & u
);

template<class T>
typename std::enable_if<
    std::is_integral<T>::value,
    checked_result<T>
>::type
constexpr inline operator~(
    const checked_result<T> & t
){
//    assert(false);
    return ~t.m_r;
}

template<class T>
typename std::enable_if<
    std::is_integral<T>::value,
    checked_result<T>
>::type
constexpr inline operator<<(
    const checked_result<T> & t,
    const checked_result<T> & u
){
    using value_type = product_value_type;
    const std::uint8_t order = static_cast<std::uint8_t>(value_type::count);

    constexpr const std::uint8_t result[order * order] = {
        // t == less_than_min
        //{
            // u == ...
            1, // -1,                                           // less_than_min,
            2, // safe_numerics_error::negative_overflow_error, // less_than_zero,
            2, // safe_numerics_error::negative_overflow_error, // zero,
            2, // safe_numerics_error::negative_overflow_error, // greater_than_zero,
            2, // safe_numerics_error::negative_overflow_error, // greater than max,
            1, // safe_numerics_error::range_error,             // indeterminate,
        //},
        // t == less_than_zero,
        //{
            // u == ...
            3, // -1,                                           // less_than_min,
            4, // - (-t >> -u),                                 // less_than_zero,
            5, // safe_numerics_error::negative_overflow_error, // zero,
            6, // - (-t << u),                                  // greater_than_zero,
            2, // safe_numerics_error::negative_overflow_error, // greater than max,
            1, // safe_numerics_error::range_error,             // indeterminate,
        //},
        // t == zero,
        //{
            // u == ...
            3, // 0     // less_than_min,
            3, // 0     // less_than_zero,
            3, // 0,    // zero,
            3, // 0,    // greater_than_zero,
            3, // 0,    // greater than max,
            3, // safe_numerics_error::range_error,    // indeterminate,
        //},
        // t == greater_than_zero,
        //{
            // u == ...
            3, // 0,                                            // less_than_min,
            7, // t << -u,                                      // less_than_zero,
            5, // t,                                            // zero,
            8, // t << u                                        // greater_than_zero,
            9, // safe_numerics_error::positive_overflow_error, // greater than max,
            1, // safe_numerics_error::range_error,             // indeterminate,
        //},
        // t == greater_than_max
        //{
            // u == ...
            1, // safe_numerics_error::range_error,               // less_than_min,
            9, // safe_numerics_error::positive_overflow_error),  // less_than_zero,
            9, // safe_numerics_error::positive_overflow_error,   // zero,
            9, // safe_numerics_error::positive_overflow_error),  // greater_than_zero,
            9, // safe_numerics_error::positive_overflow_error,   // greater than max,
            1, // safe_numerics_error::range_error,               // indeterminate,
        //},
        // t == indeterminate
        //{
            1, // safe_numerics_error::range_error,    // indeterminate,
            1, // safe_numerics_error::range_error,    // indeterminate,
            1, // safe_numerics_error::range_error,    // indeterminate,
            1, // safe_numerics_error::range_error,    // indeterminate,
            1, // safe_numerics_error::range_error,    // indeterminate,
            1, // safe_numerics_error::range_error,    // indeterminate,
        //}
    };

    const value_type tx(t);
    const value_type ux(u);
    assert(tx * order + ux < order * order);

    // I had a switch(i) statment here - but it results in an ICE
    // on multiple versions of gcc.  So make the equivalent in
    // nested if statments - should be the same (more or less)
    // performancewise.
    const unsigned int i = result[tx * order + ux];
    assert(i <= 9);
    if(1 == i){
        return safe_numerics_error::range_error;
    }
    else
    if(2 == i){
        return safe_numerics_error::negative_overflow_error;
    }
    else
    if(3 == i){
        return checked_result<T>(0);
    // the following gymnastics are to handle the case where
    // a value is changed from a negative to a positive number.
    // For example, and 8 bit number t == -128.  Then -t also
    // equals -128 since 128 cannot be held in an 8 bit signed
    // integer.
    }
    else
    if(4 == i){ // - (-t >> -u)
        assert(static_cast<bool>(t < checked_result<T>(0)));
        assert(static_cast<bool>(u < checked_result<T>(0)));
        return t >> -u;
    }
    else
    if(5 == i){
        return t;
    }
    else
    if(6 == i){ // - (-t << u)
        assert(static_cast<bool>(t < checked_result<T>(0)));
        assert(static_cast<bool>(u > checked_result<T>(0)));
        const checked_result<T> temp_t = t * checked_result<T>(2);
        const checked_result<T> temp_u = u - checked_result<T>(1);
        return  - (-temp_t << temp_u);
    }
    else
    if(7 == i){  // t >> -u
        assert(static_cast<bool>(t > checked_result<T>(0)));
        assert(static_cast<bool>(u < checked_result<T>(0)));
        return t >> -u;
    }
    else
    if(8 == i){ // t << u
        assert(static_cast<bool>(t > checked_result<T>(0)));
        assert(static_cast<bool>(u > checked_result<T>(0)));
        checked_result<T> r = checked::left_shift<T>(t, u);
        return (r.m_e == safe_numerics_error::shift_too_large)
        ? checked_result<T>(safe_numerics_error::positive_overflow_error)
        : r;
    }
    else
    if(9 == i){
        return safe_numerics_error::positive_overflow_error;
    }
    else{
        assert(false);
    };
    return checked_result<T>(0); // to suppress msvc warning
}

template<class T>
typename std::enable_if<
    std::is_integral<T>::value,
    checked_result<T>
>::type
constexpr inline operator>>(
    const checked_result<T> & t,
    const checked_result<T> & u
){
    using value_type = product_value_type;
    const std::uint8_t order = static_cast<std::uint8_t>(value_type::count);

    const std::uint8_t result[order * order] = {
        // t == less_than_min
        //{
            // u == ...
            2, // safe_numerics_error::negative_overflow_error, // less_than_min,
            2, // safe_numerics_error::negative_overflow_error, // less_than_zero,
            2, // safe_numerics_error::negative_overflow_error, // zero,
            2, // safe_numerics_error::negative_overflow_error, // greater_than_zero,
            1, // safe_numerics_error::range_error,             // greater than max,
            1, // safe_numerics_error::range_error,             // indeterminate,
        //},
        // t == less_than_zero,
        //{
            // u == ...
            2, // safe_numerics_error::negative_overflow_error  // less_than_min,
            4, // - (-t << -u),                                 // less_than_zero,
            5, // safe_numerics_error::negative_overflow_error. // zero,
            6, // - (-t >> u),                                  // greater_than_zero,
            3, // 0, ? or -1                                    // greater than max,
            1, // safe_numerics_error::range_error,             // indeterminate,
        //},
        // t == zero,
        //{
            // u == ...
            3, // 0     // less_than_min,
            3, // 0     // less_than_zero,
            3, // 0,    // zero,
            3, // 0,    // greater_than_zero,
            3, // 0,    // greater than max,
            3, // safe_numerics_error::range_error,    // indeterminate,
        //},
        // t == greater_than_zero,
        //{
            // u == ...
            9, // safe_numerics_error::positive_overflow_error  // less_than_min,
            7, // t << -u,                                      // less_than_zero,
            5, // t,                                            // zero,
            8, // t >> u                                        // greater_than_zero,
            3, // 0,                                            // greater than max,
            1, // safe_numerics_error::range_error,             // indeterminate,
        //},
        // t == greater_than_max
        //{
            // u == ...
            9, // safe_numerics_error::positive_overflow_error, // less_than_min,
            9, // safe_numerics_error::positive_overflow_error, // less_than_zero,
            9, // safe_numerics_error::positive_overflow_error, // zero,
            9, // safe_numerics_error::positive_overflow_error, // greater_than_zero,
            1, // safe_numerics_error::range_error,             // greater than max,
            1, // safe_numerics_error::range_error,             // indeterminate,
        //},
        // t == indeterminate
        //{
            1, // safe_numerics_error::range_error,    // indeterminate,
            1, // safe_numerics_error::range_error,    // indeterminate,
            1, // safe_numerics_error::range_error,    // indeterminate,
            1, // safe_numerics_error::range_error,    // indeterminate,
            1, // safe_numerics_error::range_error,    // indeterminate,
            1, // safe_numerics_error::range_error,    // indeterminate,
        //}
    };

    const value_type tx(t);
    const value_type ux(u);
    assert(tx * order + ux < order * order);

    // I had a switch(i) statment here - but it results in an ICE
    // on multiple versions of gcc.  So make the equivalent in
    // nested if statments - should be the same (more or less)
    // performancewise.
    const unsigned int i = result[tx * order + ux];
    assert(i <= 9);
    if(1 == i){
        return safe_numerics_error::range_error;
    }
    else
    if(2 == i){
        return safe_numerics_error::negative_overflow_error;
    }
    else
    if(3 == i){
        return checked_result<T>(0);
    }
    else
    if(4 == i){ // - (-t << -u)
        assert(static_cast<bool>(t < checked_result<T>(0)));
        assert(static_cast<bool>(u < checked_result<T>(0)));
        return t << -u;
    }
    else
    if(5 == i){
        return t;
    }
    else
    if(6 == i){ //  - (-t >> u)
        assert(static_cast<bool>(t < checked_result<T>(0)));
        assert(static_cast<bool>(u > checked_result<T>(0)));
        const checked_result<T> temp_t = t / checked_result<T>(2);
        const checked_result<T> temp_u = u - checked_result<T>(1);
        return  - (-temp_t >> temp_u);
    }
    else
    if(7 == i){  // t << -u,
        assert(static_cast<bool>(t > checked_result<T>(0)));
        assert(static_cast<bool>(u < checked_result<T>(0)));
        return t << -u;
    }
    else
    if(8 == i){ // t >> u
        assert(static_cast<bool>(t > checked_result<T>(0)));
        assert(static_cast<bool>(u > checked_result<T>(0)));
        checked_result<T> r = checked::right_shift<T>(t, u);
        return (r.m_e == safe_numerics_error::shift_too_large)
        ? checked_result<T>(0)
        : r;
    }
    else
    if(9 == i){
        return safe_numerics_error::positive_overflow_error;
    }
    else{
        assert(false);
    };
    return checked_result<T>(0); // to suppress msvc warning
}

template<class T>
typename std::enable_if<
    std::is_integral<T>::value,
    checked_result<T>
>::type
constexpr inline operator|(
    const checked_result<T> & t,
    const checked_result<T> & u
){
    return
        t.exception() || u.exception()
        ? checked_result<T>(safe_numerics_error::range_error)
        : checked::bitwise_or<T>(
            static_cast<T>(t),
            static_cast<T>(u)
        );
}
template<class T>
typename std::enable_if<
    std::is_integral<T>::value,
    checked_result<T>
>::type
constexpr inline operator^(
    const checked_result<T> & t,
    const checked_result<T> & u
){
    return
        t.exception() || u.exception()
        ? checked_result<T>(safe_numerics_error::range_error)
        : checked::bitwise_xor<T>(
            static_cast<T>(t),
            static_cast<T>(u)
        );
}

template<class T>
typename std::enable_if<
    std::is_integral<T>::value,
    checked_result<T>
>::type
constexpr inline operator&(
    const checked_result<T> & t,
    const checked_result<T> & u
){
    return
        t.exception() || u.exception()
        ? checked_result<T>(safe_numerics_error::range_error)
        : checked::bitwise_and<T>(
            static_cast<T>(t),
            static_cast<T>(u)
        );
}

} // safe_numerics
} // boost

#include <iosfwd>

namespace std {

template<typename CharT, typename Traits, typename R>
inline std::basic_ostream<CharT, Traits> & operator<<(
    std::basic_ostream<CharT, Traits> & os,
    const boost::safe_numerics::checked_result<R> & r
){
    bool e = r.exception();
    os << e;
    if(!e)
        os << static_cast<R>(r);
    else
        os << std::error_code(r.m_e).message() << ':' << static_cast<char const *>(r);
    return os;
}

template<typename CharT, typename Traits>
inline std::basic_ostream<CharT, Traits> & operator<<(
    std::basic_ostream<CharT, Traits> & os,
    const boost::safe_numerics::checked_result<signed char> & r
){
    bool e = r.exception();
    os << e;
    if(! e)
        os << static_cast<std::int16_t>(r);
    else
        os << std::error_code(r.m_e).message() << ':' << static_cast<char const *>(r);
    return os;
}

template<typename CharT, typename Traits, typename R>
inline std::basic_istream<CharT, Traits> & operator>>(
    std::basic_istream<CharT, Traits> & is,
    boost::safe_numerics::checked_result<R> & r
){
    bool e;
    is >> e;
    if(!e)
        is >> static_cast<R>(r);
    else
        is >> std::error_code(r.m_e).message() >> ':' >> static_cast<char const *>(r);
    return is;
}

template<typename CharT, typename Traits>
inline std::basic_istream<CharT, Traits> & operator>>(
    std::basic_istream<CharT, Traits> & is,
    boost::safe_numerics::checked_result<signed char> & r
){
    bool e;
    is >> e;
    if(!e){
        std::int16_t i;
        is >> i;
        r.m_contents.m_r = static_cast<signed char>(i);
    }
    else
        is >> std::error_code(r.m_e).message() >> ':' >> static_cast<char const *>(r);
    return is;
}

} // std

/////////////////////////////////////////////////////////////////
// numeric limits for checked<R>

#include <limits>

namespace std {

template<class R>
class numeric_limits<boost::safe_numerics::checked_result<R> >
    : public std::numeric_limits<R>
{
    using this_type = boost::safe_numerics::checked_result<R>;
public:
    constexpr static this_type min() noexcept {
        return this_type(std::numeric_limits<R>::min());
    }
    constexpr static this_type max() noexcept {
        return this_type(std::numeric_limits<R>::max());
    }
};

} // std

#endif  // BOOST_NUMERIC_CHECKED_RESULT_OPERATIONS


#include <boost/shared_ptr.hpp>
#include <iostream>
#include <time.h>

#include "fastsim.hpp"
#include "mlp.hpp"

int main(int argc, char* argv[])
{
    using namespace fastsim;

    if (argc < 7) {
        std::cerr << "This program cannot run: you need to provide a XML file (e.g.," << argv[0] << " worlds/example.xml), the number of time steps, a task ID, boolean flag whether to use goal sensors, a number of hidden neurons, debug flag, and a number of weights" << std::endl;
        exit(1);
    }

    int fcnParms = 8;
    fastsim::Settings settings(argv[1]);
    boost::shared_ptr<Map> map = settings.map();
    boost::shared_ptr<Robot> robot = settings.robot();
    std::string tmpdir = boost::lexical_cast<std::string>(argv[2]);
    int timesteps = boost::lexical_cast<int>(argv[3]);
    std::string coreID = boost::lexical_cast<std::string>(argv[4]);
    bool useGoalFinders = boost::lexical_cast<bool>(argv[5]);
    int numHiddenNeurons = boost::lexical_cast<int>(argv[6]);
    int debug = boost::lexical_cast<bool>(argv[7]);
    int numInputs = 3;
    if (useGoalFinders) {
        numInputs = 7;
    }
    int numLayers = 2;

    using namespace nn;
    Mlp<Neuron<PfWSum<>, AfTanhNoBias<> >, Connection<> > nn(numInputs, numHiddenNeurons, 2);
    std::vector<float> in(numInputs);

    // weights
    std::vector<float> w(nn.get_nb_connections());
    if (debug) std::cout<<"argc - fcnParms :" << argc - fcnParms << " --- nb connections:" << nn.get_nb_connections() << std::endl;

    assert(argc - fcnParms == nn.get_nb_connections());
    for (size_t i = fcnParms; i < argc; ++i)
      w[i - fcnParms] = boost::lexical_cast<float>(argv[i]);
    nn.set_all_weights(w);
    nn.init();

    boost::shared_ptr<Display> d;
    if (settings.display())
        d = boost::shared_ptr<Display>(new Display(map, *robot));

    // Trajectory output is saved in filename dependent on the coreID to allow multiple runs
    std::string trajFilename = tmpdir + "/traj.dat" + coreID;
    std::ofstream traj(trajFilename.c_str());

    for (int k = 0; k < timesteps; ++k) {
        if (debug) std::cout << "Time step: " << k << "/" << timesteps << std::endl;
        // lasers
        if (debug) std::cout << "Number of lasers: " << robot->get_lasers().size() << std::endl;
        for (size_t j = 0; j < robot->get_lasers().size(); ++j) {
            float d = robot->get_lasers()[j].get_dist();
            float range = robot->get_lasers()[j].get_range();
            in[j] = (d == -1 ? -1 : 1 - 2 * d / range);
        }
        if (debug) std::cout << "Laser values read" << std::endl;
        if (debug) std::cout << useGoalFinders << std::endl;

         // radar
        if (useGoalFinders) {
            int numSlices = robot->get_radars()[0].get_nb_slices();
            if (debug) std::cout << "Radar vector size: " << numSlices << std::endl;
            for (size_t j = 0; j < numSlices; ++j) {
                in[robot->get_lasers().size() + j] = -1.0f;
            }
            if (debug) std::cout << "Reading radar values" << std::endl;
            int s = robot->get_radars()[0].get_activated_slice();
            if (debug) std::cout << "Radar values read, activated slice: " << s << std::endl;
            in[robot->get_lasers().size() + s] = 1.0f;
            if (debug) std::cout << "Radar values assigned" << std::endl;
        } else {
            if (debug) std::cout << "Radar values selected to be ignored" << std::endl;
        }

        if (debug) std::cout << "Stepping network" << std::endl;
        if (debug) std::cout << "Input: " << in << std::endl;
        for (int j = 0; j < numLayers; ++j){
            nn.step(in);
            if (debug) std::cout << nn.get_outf(0) << std::endl;
        }

        if (debug) std::cout << "Stepping network done" << std::endl;

        // move the robot
        float out1 = nn.get_outf(0);
        float out2 = nn.get_outf(1);
        robot->move(out1 * 2, out2 * 2, map);
        if (debug) std::cout << "Moved robot" << std::endl;

        traj << robot->get_pos().x() << " "
             << robot->get_pos().y() << " "
             << robot->get_pos().theta() << std::endl;
        // update the display
        if (settings.display())
            d->update();

    }
    return 0;
}


#include <boost/math/special_functions/next.hpp>
#include <boost/random.hpp>

#include <limits>

#include "caffe/common.hpp"
#include "caffe/quantizer.hpp"
#include "caffe/util/math_functions.hpp"
#include "caffe/util/rng.hpp"

namespace caffe {

// Integer quantized types
template<typename Dtype>
typename std::enable_if<unsigned_integer_is_same<Dtype>::value, void>::type
caffe_gemv(const CBLAS_TRANSPOSE trans_A,
           const int_tp M, const int_tp N, const Dtype alpha,
           const Dtype* a, const Dtype* x,
           const Dtype beta, Dtype* y,
           const QuantizerValues* const alpha_quant,
           const QuantizerValues* const a_quant,
           const QuantizerValues* const x_quant,
           const QuantizerValues* const beta_quant,
           const QuantizerValues* const y_quant) {

  typedef typename std::conditional<sizeof(Dtype) == 1, int16_t,
          typename std::conditional<sizeof(Dtype) == 2, int32_t,
                                    int64_t>::type>::type Difftype;
  typedef typename std::conditional<sizeof(Dtype) == 1,
                                    int32_t, int64_t>::type Acctype;

  int8_t shift_bits = (32/sizeof(Dtype)) - 1;

  int32_t mult;
  int8_t shift;
  int32_t alpha_mult;
  int8_t alpha_shift;
  int32_t beta_mult;
  int8_t beta_shift;
  Acctype y_max = y_quant->get_max<Acctype>();
  Acctype y_min = y_quant->get_min<Acctype>();
  Dtype lhs_off = a_quant->get_zero<Dtype>();
  Dtype rhs_off = x_quant->get_zero<Dtype>();
  Dtype alpha_off = alpha_quant ? alpha_quant->get_zero<Dtype>() : Dtype(0);
  Dtype beta_off = beta_quant ? beta_quant->get_zero<Dtype>() : Dtype(0);
  const Acctype result_off = y_quant->get_zero<Acctype>();

  QuantizerBase::template MultiplicativeQuantVals<int32_t>(
      a_quant, x_quant, y_quant, &mult, &shift, shift_bits);
  QuantizerBase::template MultiplicativeQuantVals<int32_t>(
      y_quant, alpha_quant, y_quant, &alpha_mult, &alpha_shift, shift_bits);
  QuantizerBase::template MultiplicativeQuantVals<int32_t>(
      y_quant, beta_quant, y_quant, &beta_mult, &beta_shift, shift_bits);

  int_tp a_inc = (trans_A == CblasNoTrans) ? 1 : N;
  int_tp y_cnt = (trans_A == CblasNoTrans) ? M : N;
  int_tp x_cnt = (trans_A == CblasNoTrans) ? N : M;
#pragma omp parallel for
  for (int_tp m = 0; m < y_cnt; m++) {
    int_tp a_index = (trans_A == CblasNoTrans) ? m * N : m;
    Acctype acc = 0;
    for (int_tp n = 0; n < x_cnt; n++) {
      Difftype a_diff = a[a_index] - lhs_off;
      Difftype x_diff = x[n] - rhs_off;
      acc += static_cast<Acctype>(a_diff) * static_cast<Acctype>(x_diff);
      a_index += a_inc;
    }
    Acctype reg = acc * (alpha_quant ? Acctype(1) : alpha);
    reg = static_cast<Acctype>((static_cast<int64_t>(reg) *
                           static_cast<int64_t>(mult)) / (1ll << shift_bits));
    if (shift >= 0) {
      reg = reg >> shift;
    } else {
      reg = reg << -shift;
    }
    if (alpha_quant) {
      Difftype alpha_diff = alpha - alpha_off;
      reg = static_cast<Acctype>(alpha_diff) * static_cast<Acctype>(reg);
      reg = static_cast<Acctype>((static_cast<int64_t>(reg) *
                     static_cast<int64_t>(alpha_mult)) / (1ll << shift_bits));
      if (alpha_shift >= 0) {
        reg = reg >> alpha_shift;
      } else {
        reg = reg << -alpha_shift;
      }
    }
    if (beta_quant) {
      Difftype beta_diff = beta - beta_off;
      Difftype c_diff = y[m] - static_cast<Difftype>(result_off);
      Acctype creg = static_cast<Acctype>(beta_diff)
                   * static_cast<Acctype>(c_diff);
      creg = static_cast<Acctype>((static_cast<int64_t>(creg) *
                      static_cast<int64_t>(beta_mult)) / (1ll << shift_bits));
      if (beta_shift >= 0) {
        creg = creg >> beta_shift;
      } else {
        creg = creg << -beta_shift;
      }
      reg = reg + creg;
    } else if (beta == Dtype(1)) {
      reg = reg + (y[m] - result_off);
    }
    reg = reg + result_off;
    y[m] = static_cast<Dtype>(std::min(std::max(reg, y_min), y_max));
  }
}

// Half precision
template<typename Dtype>
typename std::enable_if<float_is_same<Dtype>::value, void>::type
caffe_gemv(const CBLAS_TRANSPOSE trans_A,
           const int_tp M, const int_tp N, const Dtype alpha,
           const Dtype* a, const Dtype* x,
           const Dtype beta, Dtype* y,
           const QuantizerValues* const alpha_quant,
           const QuantizerValues* const a_quant,
           const QuantizerValues* const x_quant,
           const QuantizerValues* const beta_quant,
           const QuantizerValues* const y_quant) {
  int_tp a_inc = (trans_A == CblasNoTrans) ? 1 : N;
  int_tp y_cnt = (trans_A == CblasNoTrans) ? M : N;
  int_tp x_cnt = (trans_A == CblasNoTrans) ? N : M;
  for (int_tp m = 0; m < y_cnt; m++) {
    int_tp a_index = (trans_A == CblasNoTrans) ? m * N : m;
    Dtype acc = 0;
    for (int_tp n = 0; n < x_cnt; n++) {
      acc += a[a_index] * x[n];
      a_index += a_inc;
    }
    if (beta == 0)
      y[m] = acc * alpha;
    else
      y[m] = acc * alpha + beta * y[m];
  }
}

template
typename std::enable_if<float_is_same<half_fp>::value, void>::type
caffe_gemv<half_fp>(const CBLAS_TRANSPOSE trans_A,
                    const int_tp M, const int_tp N,
                    const half_fp alpha,
                    const half_fp* a, const half_fp* x,
                    const half_fp beta, half_fp* y,
                    const QuantizerValues* const alpha_quant,
                    const QuantizerValues* const a_quant,
                    const QuantizerValues* const x_quant,
                    const QuantizerValues* const beta_quant,
                    const QuantizerValues* const y_quant);
template
typename std::enable_if<unsigned_integer_is_same<uint8_t>::value, void>::type
caffe_gemv<uint8_t>(const CBLAS_TRANSPOSE trans_A,
                    const int_tp M, const int_tp N,
                    const uint8_t alpha,
                    const uint8_t* a, const uint8_t* x,
                    const uint8_t beta, uint8_t* y,
                    const QuantizerValues* const alpha_quant,
                    const QuantizerValues* const a_quant,
                    const QuantizerValues* const x_quant,
                    const QuantizerValues* const beta_quant,
                    const QuantizerValues* const y_quant);
template
typename std::enable_if<unsigned_integer_is_same<uint16_t>::value, void>::type
caffe_gemv<uint16_t>(const CBLAS_TRANSPOSE trans_A,
                     const int_tp M, const int_tp N,
                     const uint16_t alpha,
                     const uint16_t* a, const uint16_t* x,
                     const uint16_t beta, uint16_t* y,
                     const QuantizerValues* const alpha_quant,
                     const QuantizerValues* const a_quant,
                     const QuantizerValues* const x_quant,
                     const QuantizerValues* const beta_quant,
                     const QuantizerValues* const y_quant);
template
typename std::enable_if<unsigned_integer_is_same<uint32_t>::value, void>::type
caffe_gemv<uint32_t>(const CBLAS_TRANSPOSE trans_A,
                     const int_tp M, const int_tp N,
                     const uint32_t alpha,
                     const uint32_t* a, const uint32_t* x,
                     const uint32_t beta, uint32_t* y,
                     const QuantizerValues* const alpha_quant,
                     const QuantizerValues* const a_quant,
                     const QuantizerValues* const x_quant,
                     const QuantizerValues* const beta_quant,
                     const QuantizerValues* const y_quant);

template
typename std::enable_if<unsigned_integer_is_same<uint64_t>::value, void>::type
caffe_gemv<uint64_t>(const CBLAS_TRANSPOSE trans_A,
                     const int_tp M, const int_tp N,
                     const uint64_t alpha,
                     const uint64_t* a, const uint64_t* x,
                     const uint64_t beta, uint64_t* y,
                     const QuantizerValues* const alpha_quant,
                     const QuantizerValues* const a_quant,
                     const QuantizerValues* const x_quant,
                     const QuantizerValues* const beta_quant,
                     const QuantizerValues* const y_quant);

template<>
void caffe_gemv<float>(const CBLAS_TRANSPOSE trans_A, const int_tp M,
                       const int_tp N, const float alpha, const float* A,
                       const float* x, const float beta, float* y,
                       const QuantizerValues* const alpha_quant,
                       const QuantizerValues* const a_quant,
                       const QuantizerValues* const x_quant,
                       const QuantizerValues* const beta_quant,
                       const QuantizerValues* const y_quant) {
  cblas_sgemv(CblasRowMajor, trans_A, M, N, alpha, A, N, x, 1, beta, y, 1);
}

template<>
void caffe_gemv<double>(const CBLAS_TRANSPOSE trans_A, const int_tp M,
                        const int_tp N, const double alpha, const double* A,
                        const double* x, const double beta, double* y,
                        const QuantizerValues* const alpha_quant,
                        const QuantizerValues* const a_quant,
                        const QuantizerValues* const x_quant,
                        const QuantizerValues* const beta_quant,
                        const QuantizerValues* const y_quant) {
  cblas_dgemv(CblasRowMajor, trans_A, M, N, alpha, A, N, x, 1, beta, y, 1);
}

}  // namespace caffe


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

#include <stdexcept>

#include "UncertainParameter.hh"

template class GaussianParameter<double>;
template class Variable<double>;
template class Parameter<double>;
template class ParameterWrapper<double>;
template class UniformAngleParameter<double>;
#ifdef PROVIDE_SINGLE_PRECISION
  template class GaussianParameter<float>;
  template class Variable<float>;
  template class Parameter<float>;
  template class ParameterWrapper<float>;
  template class UniformAngleParameter<float>;
#endif