//  Copyright 2014 Marco Guazzone (marco.guazzone@gmail.com)
//
//  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)
//
// This module implements the Hyper-Exponential distribution.
//
// References:
// - "Queueing Theory in Manufacturing Systems Analysis and Design" by H.T. Papadopolous, C. Heavey and J. Browne (Chapman & Hall/CRC, 1993)
// - http://reference.wolfram.com/language/ref/HyperexponentialDistribution.html
// - http://en.wikipedia.org/wiki/Hyperexponential_distribution
//

#ifndef BOOST_MATH_DISTRIBUTIONS_HYPEREXPONENTIAL_HPP
#define BOOST_MATH_DISTRIBUTIONS_HYPEREXPONENTIAL_HPP

#include <boost/config.hpp>
#include <boost/math/tools/cxx03_warn.hpp>
#include <boost/math/distributions/complement.hpp>
#include <boost/math/distributions/detail/common_error_handling.hpp>
#include <boost/math/distributions/exponential.hpp>
#include <boost/math/policies/policy.hpp>
#include <boost/math/special_functions/fpclassify.hpp>
#include <boost/math/tools/precision.hpp>
#include <boost/math/tools/roots.hpp>
#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>
#include <boost/range/size.hpp>
#include <boost/type_traits/has_pre_increment.hpp>
#include <cstddef>
#include <iterator>
#include <limits>
#include <numeric>
#include <utility>
#include <vector>

#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST)
# include <initializer_list>
#endif

#ifdef _MSC_VER
# pragma warning (push)
# pragma warning(disable:4127) // conditional expression is constant
# pragma warning(disable:4389) // '==' : signed/unsigned mismatch in test_tools
#endif // _MSC_VER

namespace boost { namespace math {

namespace detail {

template <typename Dist>
typename Dist::value_type generic_quantile(const Dist& dist, const typename Dist::value_type& p, const typename Dist::value_type& guess, bool comp, const char* function);

} // Namespace detail

template <typename RealT, typename PolicyT>
class hyperexponential_distribution;

namespace /*<unnamed>*/ { namespace hyperexp_detail {

template <typename T>
void normalize(std::vector<T>& v)
{
   if(!v.size())
      return;  // Our error handlers will get this later
    const T sum = std::accumulate(v.begin(), v.end(), static_cast<T>(0));
    T final_sum = 0;
    const typename std::vector<T>::iterator end = --v.end();
    for (typename std::vector<T>::iterator it = v.begin();
         it != end;
         ++it)
    {
        *it /= sum;
        final_sum += *it;
    }
    *end = 1 - final_sum;  // avoids round off errors, ensures the probs really do sum to 1.
}

template <typename RealT, typename PolicyT>
bool check_probabilities(char const* function, std::vector<RealT> const& probabilities, RealT* presult, PolicyT const& pol)
{
    BOOST_MATH_STD_USING
    const std::size_t n = probabilities.size();
    RealT sum = 0;
    for (std::size_t i = 0; i < n; ++i)
    {
        if (probabilities[i] < 0
            || probabilities[i] > 1
            || !(boost::math::isfinite)(probabilities[i]))
        {
            *presult = policies::raise_domain_error<RealT>(function,
                                                           "The elements of parameter \"probabilities\" must be >= 0 and <= 1, but at least one of them was: %1%.",
                                                           probabilities[i],
                                                           pol);
            return false;
        }
        sum += probabilities[i];
    }

    //
    // We try to keep phase probabilities correctly normalized in the distribution constructors,
    // however in practice we have to allow for a very slight divergence from a sum of exactly 1:
    //
    if (fabs(sum - 1) > tools::epsilon<RealT>() * 2)
    {
        *presult = policies::raise_domain_error<RealT>(function,
                                                       "The elements of parameter \"probabilities\" must sum to 1, but their sum is: %1%.",
                                                       sum,
                                                       pol);
        return false;
    }

    return true;
}

template <typename RealT, typename PolicyT>
bool check_rates(char const* function, std::vector<RealT> const& rates, RealT* presult, PolicyT const& pol)
{
    const std::size_t n = rates.size();
    for (std::size_t i = 0; i < n; ++i)
    {
        if (rates[i] <= 0
            || !(boost::math::isfinite)(rates[i]))
        {
            *presult = policies::raise_domain_error<RealT>(function,
                                                           "The elements of parameter \"rates\" must be > 0, but at least one of them is: %1%.",
                                                           rates[i],
                                                           pol);
            return false;
        }
    }
    return true;
}

template <typename RealT, typename PolicyT>
bool check_dist(char const* function, std::vector<RealT> const& probabilities, std::vector<RealT> const& rates, RealT* presult, PolicyT const& pol)
{
    BOOST_MATH_STD_USING
    if (probabilities.size() != rates.size())
    {
        *presult = policies::raise_domain_error<RealT>(function,
                                                       "The parameters \"probabilities\" and \"rates\" must have the same length, but their size differ by: %1%.",
                                                       fabs(static_cast<RealT>(probabilities.size())-static_cast<RealT>(rates.size())),
                                                       pol);
        return false;
    }

    return check_probabilities(function, probabilities, presult, pol)
           && check_rates(function, rates, presult, pol);
}

template <typename RealT, typename PolicyT>
bool check_x(char const* function, RealT x, RealT* presult, PolicyT const& pol)
{
    if (x < 0 || (boost::math::isnan)(x))
    {
        *presult = policies::raise_domain_error<RealT>(function, "The random variable must be >= 0, but is: %1%.", x, pol);
        return false;
    }
    return true;
}

template <typename RealT, typename PolicyT>
bool check_probability(char const* function, RealT p, RealT* presult, PolicyT const& pol)
{
    if (p < 0 || p > 1 || (boost::math::isnan)(p))
    {
        *presult = policies::raise_domain_error<RealT>(function, "The probability be >= 0 and <= 1, but is: %1%.", p, pol);
        return false;
    }
    return true;
}

template <typename RealT, typename PolicyT>
RealT quantile_impl(hyperexponential_distribution<RealT, PolicyT> const& dist, RealT const& p, bool comp)
{
    // Don't have a closed form so try to numerically solve the inverse CDF...

    typedef typename policies::evaluation<RealT, PolicyT>::type value_type;
    typedef typename policies::normalise<PolicyT,
                                         policies::promote_float<false>,
                                         policies::promote_double<false>,
                                         policies::discrete_quantile<>,
                                         policies::assert_undefined<> >::type forwarding_policy;

    static const char* function = comp ? "boost::math::quantile(const boost::math::complemented2_type<boost::math::hyperexponential_distribution<%1%>, %1%>&)"
                                       : "boost::math::quantile(const boost::math::hyperexponential_distribution<%1%>&, %1%)";

    RealT result = 0;

    if (!check_probability(function, p, &result, PolicyT()))
    {
        return result;
    }

    const std::size_t n = dist.num_phases();
    const std::vector<RealT> probs = dist.probabilities();
    const std::vector<RealT> rates = dist.rates();

    // A possible (but inaccurate) approximation is given below, where the
    // quantile is given by the weighted sum of exponential quantiles:
    RealT guess = 0;
    if (comp)
    {
        for (std::size_t i = 0; i < n; ++i)
        {
            const exponential_distribution<RealT,PolicyT> exp(rates[i]);

            guess += probs[i]*quantile(complement(exp, p));
        }
    }
    else
    {
        for (std::size_t i = 0; i < n; ++i)
        {
            const exponential_distribution<RealT,PolicyT> exp(rates[i]);

            guess += probs[i]*quantile(exp, p);
        }
    }

    // Fast return in case the Hyper-Exponential is essentially an Exponential
    if (n == 1)
    {
        return guess;
    }

    value_type q;
    q = detail::generic_quantile(hyperexponential_distribution<RealT,forwarding_policy>(probs, rates),
                                 p,
                                 guess,
                                 comp,
                                 function);

    result = policies::checked_narrowing_cast<RealT,forwarding_policy>(q, function);

    return result;
}

}} // Namespace <unnamed>::hyperexp_detail

template <typename RealT = double, typename PolicyT = policies::policy<> >
class hyperexponential_distribution
{
    public: typedef RealT value_type;
    public: typedef PolicyT policy_type;

    public: hyperexponential_distribution()
    : probs_(1, 1),
      rates_(1, 1)
    {
        RealT err;
        hyperexp_detail::check_dist("boost::math::hyperexponential_distribution<%1%>::hyperexponential_distribution",
                                    probs_,
                                    rates_,
                                    &err,
                                    PolicyT());
    }

    // Four arg constructor: no ambiguity here, the arguments must be two pairs of iterators:
    public: template <typename ProbIterT, typename RateIterT>
            hyperexponential_distribution(ProbIterT prob_first, ProbIterT prob_last,
                                          RateIterT rate_first, RateIterT rate_last)
    : probs_(prob_first, prob_last),
      rates_(rate_first, rate_last)
    {
        hyperexp_detail::normalize(probs_);
        RealT err;
        hyperexp_detail::check_dist("boost::math::hyperexponential_distribution<%1%>::hyperexponential_distribution",
                                    probs_,
                                    rates_,
                                    &err,
                                    PolicyT());
    }

    // Two arg constructor from 2 ranges, we SFINAE this out of existence if
    // either argument type is incrementable as in that case the type is
    // probably an iterator:
    public: template <typename ProbRangeT, typename RateRangeT>
            hyperexponential_distribution(ProbRangeT const& prob_range,
                                          RateRangeT const& rate_range,
                                          typename boost::disable_if_c<boost::has_pre_increment<ProbRangeT>::value || boost::has_pre_increment<RateRangeT>::value>::type* = 0)
    : probs_(boost::begin(prob_range), boost::end(prob_range)),
      rates_(boost::begin(rate_range), boost::end(rate_range))
    {
        hyperexp_detail::normalize(probs_);

        RealT err;
        hyperexp_detail::check_dist("boost::math::hyperexponential_distribution<%1%>::hyperexponential_distribution",
                                    probs_,
                                    rates_,
                                    &err,
                                    PolicyT());
    }

    // Two arg constructor for a pair of iterators: we SFINAE this out of
    // existence if neither argument types are incrementable.
    // Note that we allow different argument types here to allow for
    // construction from an array plus a pointer into that array.
    public: template <typename RateIterT, typename RateIterT2>
            hyperexponential_distribution(RateIterT const& rate_first,
                                          RateIterT2 const& rate_last,
                                          typename boost::enable_if_c<boost::has_pre_increment<RateIterT>::value || boost::has_pre_increment<RateIterT2>::value>::type* = 0)
    : probs_(std::distance(rate_first, rate_last), 1), // will be normalized below
      rates_(rate_first, rate_last)
    {
        hyperexp_detail::normalize(probs_);

        RealT err;
        hyperexp_detail::check_dist("boost::math::hyperexponential_distribution<%1%>::hyperexponential_distribution",
                                    probs_,
                                    rates_,
                                    &err,
                                    PolicyT());
    }

#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST)
      // Initializer list constructor: allows for construction from array literals:
public: hyperexponential_distribution(std::initializer_list<RealT> l1, std::initializer_list<RealT> l2)
      : probs_(l1.begin(), l1.end()),
        rates_(l2.begin(), l2.end())
      {
         hyperexp_detail::normalize(probs_);

         RealT err;
         hyperexp_detail::check_dist("boost::math::hyperexponential_distribution<%1%>::hyperexponential_distribution",
            probs_,
            rates_,
            &err,
            PolicyT());
      }

public: hyperexponential_distribution(std::initializer_list<RealT> l1)
      : probs_(l1.size(), 1),
        rates_(l1.begin(), l1.end())
      {
         hyperexp_detail::normalize(probs_);

         RealT err;
         hyperexp_detail::check_dist("boost::math::hyperexponential_distribution<%1%>::hyperexponential_distribution",
            probs_,
            rates_,
            &err,
            PolicyT());
      }
#endif // !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST)

    // Single argument constructor: argument must be a range.
    public: template <typename RateRangeT>
    hyperexponential_distribution(RateRangeT const& rate_range)
    : probs_(boost::size(rate_range), 1), // will be normalized below
      rates_(boost::begin(rate_range), boost::end(rate_range))
    {
        hyperexp_detail::normalize(probs_);

        RealT err;
        hyperexp_detail::check_dist("boost::math::hyperexponential_distribution<%1%>::hyperexponential_distribution",
                                    probs_,
                                    rates_,
                                    &err,
                                    PolicyT());
    }

    public: std::vector<RealT> probabilities() const
    {
        return probs_;
    }

    public: std::vector<RealT> rates() const
    {
        return rates_;
    }

    public: std::size_t num_phases() const
    {
        return rates_.size();
    }

    private: std::vector<RealT> probs_;
    private: std::vector<RealT> rates_;
}; // class hyperexponential_distribution

// Convenient type synonym for double.
typedef hyperexponential_distribution<double> hyperexponential;

// Range of permissible values for random variable x
template <typename RealT, typename PolicyT>
std::pair<RealT,RealT> range(hyperexponential_distribution<RealT,PolicyT> const&)
{
    if (std::numeric_limits<RealT>::has_infinity)
    {
        return std::make_pair(static_cast<RealT>(0), std::numeric_limits<RealT>::infinity()); // 0 to +inf.
    }

    return std::make_pair(static_cast<RealT>(0), tools::max_value<RealT>()); // 0 to +<max value>
}

// 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.
template <typename RealT, typename PolicyT>
std::pair<RealT,RealT> support(hyperexponential_distribution<RealT,PolicyT> const&)
{
    return std::make_pair(tools::min_value<RealT>(), tools::max_value<RealT>()); // <min value> to +<max value>.
}

template <typename RealT, typename PolicyT>
RealT pdf(hyperexponential_distribution<RealT, PolicyT> const& dist, RealT const& x)
{
    BOOST_MATH_STD_USING
    RealT result = 0;

    if (!hyperexp_detail::check_x("boost::math::pdf(const boost::math::hyperexponential_distribution<%1%>&, %1%)", x, &result, PolicyT()))
    {
        return result;
    }

    const std::size_t n = dist.num_phases();
    const std::vector<RealT> probs = dist.probabilities();
    const std::vector<RealT> rates = dist.rates();

    for (std::size_t i = 0; i < n; ++i)
    {
        const exponential_distribution<RealT,PolicyT> exp(rates[i]);

        result += probs[i]*pdf(exp, x);
        //result += probs[i]*rates[i]*exp(-rates[i]*x);
    }

    return result;
}

template <typename RealT, typename PolicyT>
RealT cdf(hyperexponential_distribution<RealT, PolicyT> const& dist, RealT const& x)
{
    RealT result = 0;

    if (!hyperexp_detail::check_x("boost::math::cdf(const boost::math::hyperexponential_distribution<%1%>&, %1%)", x, &result, PolicyT()))
    {
        return result;
    }

    const std::size_t n = dist.num_phases();
    const std::vector<RealT> probs = dist.probabilities();
    const std::vector<RealT> rates = dist.rates();

    for (std::size_t i = 0; i < n; ++i)
    {
        const exponential_distribution<RealT,PolicyT> exp(rates[i]);

        result += probs[i]*cdf(exp, x);
    }

    return result;
}

template <typename RealT, typename PolicyT>
RealT quantile(hyperexponential_distribution<RealT, PolicyT> const& dist, RealT const& p)
{
    return hyperexp_detail::quantile_impl(dist, p , false);
}

template <typename RealT, typename PolicyT>
RealT cdf(complemented2_type<hyperexponential_distribution<RealT,PolicyT>, RealT> const& c)
{
    RealT const& x = c.param;
    hyperexponential_distribution<RealT,PolicyT> const& dist = c.dist;

    RealT result = 0;

    if (!hyperexp_detail::check_x("boost::math::cdf(boost::math::complemented2_type<const boost::math::hyperexponential_distribution<%1%>&, %1%>)", x, &result, PolicyT()))
    {
        return result;
    }

    const std::size_t n = dist.num_phases();
    const std::vector<RealT> probs = dist.probabilities();
    const std::vector<RealT> rates = dist.rates();

    for (std::size_t i = 0; i < n; ++i)
    {
        const exponential_distribution<RealT,PolicyT> exp(rates[i]);

        result += probs[i]*cdf(complement(exp, x));
    }

    return result;
}

template <typename RealT, typename PolicyT>
RealT quantile(complemented2_type<hyperexponential_distribution<RealT, PolicyT>, RealT> const& c)
{
    RealT const& p = c.param;
    hyperexponential_distribution<RealT,PolicyT> const& dist = c.dist;

    return hyperexp_detail::quantile_impl(dist, p , true);
}

template <typename RealT, typename PolicyT>
RealT mean(hyperexponential_distribution<RealT, PolicyT> const& dist)
{
    RealT result = 0;

    const std::size_t n = dist.num_phases();
    const std::vector<RealT> probs = dist.probabilities();
    const std::vector<RealT> rates = dist.rates();

    for (std::size_t i = 0; i < n; ++i)
    {
        const exponential_distribution<RealT,PolicyT> exp(rates[i]);

        result += probs[i]*mean(exp);
    }

    return result;
}

template <typename RealT, typename PolicyT>
RealT variance(hyperexponential_distribution<RealT, PolicyT> const& dist)
{
    RealT result = 0;

    const std::size_t n = dist.num_phases();
    const std::vector<RealT> probs = dist.probabilities();
    const std::vector<RealT> rates = dist.rates();

    for (std::size_t i = 0; i < n; ++i)
    {
        result += probs[i]/(rates[i]*rates[i]);
    }

    const RealT mean = boost::math::mean(dist);

    result = 2*result-mean*mean;

    return result;
}

template <typename RealT, typename PolicyT>
RealT skewness(hyperexponential_distribution<RealT,PolicyT> const& dist)
{
    BOOST_MATH_STD_USING
    const std::size_t n = dist.num_phases();
    const std::vector<RealT> probs = dist.probabilities();
    const std::vector<RealT> rates = dist.rates();

    RealT s1 = 0; // \sum_{i=1}^n \frac{p_i}{\lambda_i}
    RealT s2 = 0; // \sum_{i=1}^n \frac{p_i}{\lambda_i^2}
    RealT s3 = 0; // \sum_{i=1}^n \frac{p_i}{\lambda_i^3}
    for (std::size_t i = 0; i < n; ++i)
    {
        const RealT p = probs[i];
        const RealT r = rates[i];
        const RealT r2 = r*r;
        const RealT r3 = r2*r;

        s1 += p/r;
        s2 += p/r2;
        s3 += p/r3;
    }

    const RealT s1s1 = s1*s1;

    const RealT num = (6*s3 - (3*(2*s2 - s1s1) + s1s1)*s1);
    const RealT den = (2*s2 - s1s1);

    return num / pow(den, static_cast<RealT>(1.5));
}

template <typename RealT, typename PolicyT>
RealT kurtosis(hyperexponential_distribution<RealT,PolicyT> const& dist)
{
    const std::size_t n = dist.num_phases();
    const std::vector<RealT> probs = dist.probabilities();
    const std::vector<RealT> rates = dist.rates();

    RealT s1 = 0; // \sum_{i=1}^n \frac{p_i}{\lambda_i}
    RealT s2 = 0; // \sum_{i=1}^n \frac{p_i}{\lambda_i^2}
    RealT s3 = 0; // \sum_{i=1}^n \frac{p_i}{\lambda_i^3}
    RealT s4 = 0; // \sum_{i=1}^n \frac{p_i}{\lambda_i^4}
    for (std::size_t i = 0; i < n; ++i)
    {
        const RealT p = probs[i];
        const RealT r = rates[i];
        const RealT r2 = r*r;
        const RealT r3 = r2*r;
        const RealT r4 = r3*r;

        s1 += p/r;
        s2 += p/r2;
        s3 += p/r3;
        s4 += p/r4;
    }

    const RealT s1s1 = s1*s1;

    const RealT num = (24*s4 - 24*s3*s1 + 3*(2*(2*s2 - s1s1) + s1s1)*s1s1);
    const RealT den = (2*s2 - s1s1);

    return num/(den*den);
}

template <typename RealT, typename PolicyT>
RealT kurtosis_excess(hyperexponential_distribution<RealT,PolicyT> const& dist)
{
    return kurtosis(dist) - 3;
}

template <typename RealT, typename PolicyT>
RealT mode(hyperexponential_distribution<RealT,PolicyT> const& /*dist*/)
{
    return 0;
}

}} // namespace boost::math

#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>
#include <boost/math/distributions/detail/generic_quantile.hpp>

#endif // BOOST_MATH_DISTRIBUTIONS_HYPEREXPONENTIAL


// The quad_float module is derived from the doubledouble
// library originally developed by Keith Briggs:
//    http://keithbriggs.info/doubledouble.html
// I attach the original copyright notice.

/*

Copyright (C) 1997 Keith Martin Briggs

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

*/

// The configure script tries to prevent this, but we
// double check here.  Note that while it is strongly
// discouraged, other parts of NTL probably work even with
// "fast math"; however, quad_float will definitely break.

#if (defined(__GNUC__) && __FAST_MATH__)
#error "do not compile quad_float.cpp with -ffast-math!!"
#endif

// The configure script should define NTL_FP_CONTRACT_OFF
// for icc via the NOCONTRACT variable
#ifdef NTL_FP_CONTRACT_OFF
#pragma fp_contract(off)
#endif

#if 0
// The configure script should ensure that all NTL files
// are compiled with --fp-model precise on icc.
#ifdef __INTEL_COMPILER
#pragma float_control(precise,on)
#endif
#endif

#include <NTL/quad_float.h>
#include <cfloat>

NTL_START_IMPL

#if (NTL_EXT_DOUBLE && defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)))

#if (!defined(NTL_X86_FIX) && !defined(NTL_NO_X86_FIX))

#define NTL_X86_FIX

#endif

#endif

#if (NTL_EXT_DOUBLE && !defined(NTL_X86_FIX))

#define DOUBLE volatile double

#else

#define DOUBLE double

#endif

#ifdef NTL_X86_FIX

#define START_FIX \
  unsigned short __old_cw, __new_cw; \
  __asm__ volatile ("fnstcw %0":"=m" (__old_cw)::"memory"); \
  __new_cw = (__old_cw & ~0x300) | 0x200; \
  __asm__ volatile ("fldcw %0"::"m" (__new_cw):"memory");

#define END_FIX  __asm__ volatile ("fldcw %0": :"m" (__old_cw));

// NOTE: "asm volatile" does not guarantee that the asm does
// not move.  However, the "memory" clobber makes these
// memory barriers that cannot move past a load/store

#define NO_INLINE __attribute__ ((noinline))
// to protect against LTO inlining which could break the memory
// barriers in START_FIX and END_FIX.  I've done some testing
// on gcc, clang, and icc.  The noinline attribute and the volatile
// asm together should ensure that the function gets called
// and doesn't get inlined during LTO.
// That said, I wouln't really recommend applying LTO to NTL...
// and especially to quad_float.cpp.

// NOTE: gcc 8.1 seems a bit buggy: it warns when overloading a function
// with different inline atrributes.  Earlier versions are fine.
// ICC and CLANG are fine.

// NOTE: starting with gcc 8.1, there is a function attribute called
// "noipa" which really does exactly what I want.  It would also be useful
// for ForceToMem, for example.

#else

#define START_FIX
#define END_FIX

#define NO_INLINE

#endif

NO_INLINE void quad_float_normalize(quad_float& z, const double& xhi, const double& xlo)
{
START_FIX
   DOUBLE u, v;

   u = xhi + xlo;
   v = xhi - u;
   v = v + xlo;

   z.hi = u;
   z.lo = v;
END_FIX
}

NO_INLINE void quad_float_in_place_add(quad_float& x, const quad_float& y ) {
START_FIX
        DOUBLE    H, h, T, t, S, s, e, f;
        DOUBLE    t1;

        S = x.hi + y.hi;
        T = x.lo + y.lo;
        e = S - x.hi;
        f = T - x.lo;

        t1 = S-e;
        t1 = x.hi-t1;
        s = y.hi-e;
        s = s + t1;

        t1 = T-f;
        t1 = x.lo-t1;
        t = y.lo-f;
        t = t + t1;

        s = s + T;
        H = S + s;
        h = S - H;
        h = h + s;

        h = h + t;
        e = H + h;
        f = H - e;
        f = f + h;

        x.hi = e;
        x.lo = f;
END_FIX
}

NO_INLINE void quad_float_in_place_sub(quad_float& x, const quad_float& y ) {
START_FIX
        DOUBLE    H, h, T, t, S, s, e, f;
        DOUBLE    t1, yhi, ylo;

        yhi = -y.hi;
        ylo = -y.lo;

        S = x.hi + yhi;
        T = x.lo + ylo;
        e = S - x.hi;
        f = T - x.lo;

        t1 = S-e;
        t1 = x.hi-t1;
        s = yhi-e;
        s = s + t1;

        t1 = T-f;
        t1 = x.lo-t1;
        t = ylo-f;
        t = t + t1;

        s = s + T;
        H = S + s;
        h = S - H;
        h = h + s;

        h = h + t;
        e = H + h;
        f = H - e;
        f = f + h;

        x.hi = e;
        x.lo = f;
END_FIX
}

NO_INLINE void quad_float_in_place_negate(quad_float& x)
{
START_FIX
   DOUBLE xhi, xlo, u, v;

   xhi = -x.hi;
   xlo = -x.lo;

   // it is a good idea to renormalize here, just in case
   // the rounding rule depends on sign, and thus we will
   // maintain the "normal form" for quad_float's.

   u = xhi + xlo;
   v = xhi - u;
   v = v + xlo;

   x.hi = u;
   x.lo = v;
END_FIX
}

#if (NTL_FMA_DETECTED && !defined(NTL_CONTRACTION_FIXED))

// The configure script should ensure that no FMA's are issued
// fo most compilers (at least gcc, clang, and icc), but if not,
// this is a last ditch effort to fix the problem (which seems to work).

double quad_float_zero = 0;

static inline
double Protect(double x) { return x + quad_float_zero; }

#else

static inline
double Protect(double x) { return x; }

#endif

NO_INLINE void quad_float_in_place_mul(quad_float& x,const quad_float& y ) {
START_FIX
  DOUBLE hx, tx, hy, ty, C, c;
  DOUBLE t1, t2;

  C = Protect(NTL_QUAD_FLOAT_SPLIT*x.hi);
  hx = C-x.hi;
  c = Protect(NTL_QUAD_FLOAT_SPLIT*y.hi);
  hx = C-hx;
  tx = x.hi-hx;
  hy = c-y.hi;
  C = Protect(x.hi*y.hi);
  hy = c-hy;
  ty = y.hi-hy;

  // c = ((((hx*hy-C)+hx*ty)+tx*hy)+tx*ty)+(x.hi*y.lo+x.lo*y.hi);

  t1 = Protect(hx*hy);
  t1 = t1-C;
  t2 = Protect(hx*ty);
  t1 = t1+t2;
  t2 = Protect(tx*hy);
  t1 = t1+t2;
  t2 = Protect(tx*ty);
  c = t1+t2;
  t1 = Protect(x.hi*y.lo);
  t2 = Protect(x.lo*y.hi);
  t1 = t1+t2;
  c = c + t1;

  hx = C+c;
  tx = C-hx;
  tx = tx+c;

  x.hi = hx;
  x.lo = tx;
END_FIX
}

NO_INLINE void quad_float_in_place_div(quad_float& x, const quad_float& y ) {
START_FIX
  DOUBLE hc, tc, hy, ty, C, c, U, u;
  DOUBLE t1;

  C = x.hi/y.hi;
  c = Protect(NTL_QUAD_FLOAT_SPLIT*C);
  hc = c-C;
  u = Protect(NTL_QUAD_FLOAT_SPLIT*y.hi);
  hc = c-hc;
  tc = C-hc;
  hy = u-y.hi;
  U = Protect(C * y.hi);
  hy = u-hy;
  ty = y.hi-hy;

  // u = (((hc*hy-U)+hc*ty)+tc*hy)+tc*ty;

  u = Protect(hc*hy);
  u = u-U;
  t1 = Protect(hc*ty);
  u = u+t1;
  t1 = Protect(tc*hy);
  u = u+t1;
  t1 = Protect(tc*ty);
  u = u+t1;

  // c = ((((x.hi-U)-u)+x.lo)-C*y.lo)/y.hi;

  c = x.hi-U;
  c = c-u;
  c = c+x.lo;
  t1 = Protect(C*y.lo);
  c = c - t1;
  c = c/y.hi;

  hy = C+c;
  ty = C-hy;
  ty = ty+c;

  x.hi = hy;
  x.lo = ty;
END_FIX
}

NO_INLINE void quad_float_in_place_sqrt(quad_float& y, double& c_ref) {
START_FIX
  DOUBLE c = c_ref;
  DOUBLE p,q,hx,tx,u,uu,cc;
  DOUBLE t1;

  p = Protect(NTL_QUAD_FLOAT_SPLIT*c);
  hx = (c-p);
  hx = hx+p;
  tx = c-hx;
  p = Protect(hx*hx);
  q = Protect(hx*tx);
  q = q+q;

  u = p+q;
  uu = p-u;
  uu = uu+q;
  t1 = Protect(tx*tx);
  uu = uu+t1;

  cc = y.hi-u;
  cc = cc-uu;
  cc = cc+y.lo;
  t1 = c+c;
  cc = cc/t1;

  hx = c+cc;
  tx = c-hx;
  tx = tx+cc;

  y.hi = hx;
  y.lo = tx;
END_FIX
}

NO_INLINE void quad_float_PrecisionOK(long& res, const double& one)
{
START_FIX
   long k;
   DOUBLE l1 = one;
   DOUBLE lh = one/double(2);
   DOUBLE epsilon;
   DOUBLE fudge, oldfudge;

   epsilon = l1;
   fudge = l1+l1;

   k = 0;

   do {
      k++;
      epsilon = epsilon * lh;
      oldfudge = fudge;
      fudge = l1 + epsilon;
   } while (fudge > l1 && fudge < oldfudge);

   res = (k == NTL_DOUBLE_PRECISION);
END_FIX
}

NTL_END_IMPL


#pragma once

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

#include <boost/mpl/vector.hpp>
#include <boost/proto/core.hpp>
#include <boost/variant.hpp>
#include <boost/any.hpp>
#include <boost/foreach.hpp>
#include <boost/tuple/tuple.hpp>

namespace metaSMT {
  namespace proto = boost::proto;
  namespace bvtags = ::metaSMT::logic::QF_BV::tag;
  namespace predtags = ::metaSMT::logic::tag;

  // Forward declaration
  struct addclause_cmd;
  namespace features
  {
    struct addclause_api;
  }

  template <typename PredicateSolver>
  struct BitBlast {
    typedef BitBlast<PredicateSolver> this_type;

    typedef typename PredicateSolver::result_type result_base;
    typedef std::vector< result_base >  bv_result;

    typedef typename boost::mpl::vector2<
      result_base, bv_result
    >::type result_types_vec;

    typedef typename boost::make_variant_over< result_types_vec >::type
      result_type;

    typedef boost::tuple<uint64_t, unsigned>  bvuint_tuple;
    typedef boost::tuple< int64_t, unsigned>  bvsint_tuple;

        void assertion( result_type e ) {
          _solver.assertion( boost::get<result_base>(e) );
        }

        void assumption( result_type e ) {
          _solver.assumption( boost::get<result_base>(e) );
        }

        unsigned get_bv_width( result_type const &e ) {
          try {
            return boost::get<bv_result>(e).size();
          } catch ( boost::bad_get ) {
            return 0;
          }
        }

        bool solve() {
          return _solver.solve();
        }

        result_type operator() (bvtags::var_tag var, boost::any arg ) {
          //printf("bitvec\n");
          bv_result ret(var.width);
          for (unsigned i = 0; i < var.width; ++i) {
            ret[i]= _solver(predtags::var_tag(), arg);
          }
          return ret;
        }

        result_type operator() ( bvtags::bvand_tag , result_type arg1, result_type arg2 )
        {
          //printf("bvand\n");
          bv_result a = boost::get<bv_result>(arg1);
          bv_result b = boost::get<bv_result>(arg2);
          assert(a.size()==b.size());
          bv_result ret(a.size());
          predtags::and_tag and_;

          for (unsigned i = 0; i < a.size(); ++i) {
            ret[i]= _solver(and_, a[i], b[i]);
          }
          return ret;
        }

        result_type operator() ( bvtags::bvnand_tag , result_type arg1, result_type arg2 )
        {
          //printf("bvnand\n");
          bv_result a = boost::get<bv_result>(arg1);
          bv_result b = boost::get<bv_result>(arg2);
          assert(a.size()==b.size());
          bv_result ret(a.size());
          predtags::nand_tag nand_;

          for (unsigned i = 0; i < a.size(); ++i) {
            ret[i]= _solver(nand_, a[i], b[i]);
          }
          return ret;
        }

        result_type operator() ( bvtags::bvor_tag , result_type arg1, result_type arg2 )
        {
          //printf("bvor\n");
          bv_result a = boost::get<bv_result>(arg1);
          bv_result b = boost::get<bv_result>(arg2);
          assert(a.size()==b.size());
          bv_result ret(a.size());
          predtags::or_tag or_;

          for (unsigned i = 0; i < a.size(); ++i) {
            ret[i]= _solver(or_, a[i], b[i]);
          }
          return ret;
        }

        result_type operator() ( bvtags::bvnor_tag , result_type arg1, result_type arg2 )
        {
          //printf("bvnor\n");
          bv_result a = boost::get<bv_result>(arg1);
          bv_result b = boost::get<bv_result>(arg2);
          assert(a.size()==b.size());
          bv_result ret(a.size());
          predtags::nor_tag tag_;

          for (unsigned i = 0; i < a.size(); ++i) {
            ret[i]= _solver(tag_, a[i], b[i]);
          }
          return ret;
        }

        result_type operator() ( bvtags::bvnot_tag , result_type arg1 )
        {
         //printf("bvnot\n");
          bv_result a = boost::get<bv_result>(arg1);
          bv_result ret(a.size());
          predtags::not_tag not_;

          for (unsigned i = 0; i < a.size(); ++i) {
            ret[i] = _solver(not_,a[i]);
          }
          return ret;
        }

       result_type operator() ( bvtags::bvxor_tag , result_type arg1, result_type arg2 )
       {
          //printf("bvxor\n");
          bv_result a = boost::get<bv_result>(arg1);
          bv_result b = boost::get<bv_result>(arg2);
          assert(a.size()==b.size());
          bv_result ret(a.size());
          predtags::xor_tag xor_;

          for (unsigned i = 0; i < a.size(); ++i) {
            ret[i]= _solver(xor_, a[i], b[i]);
          }
          return ret;
       }

       result_type operator() ( bvtags::bvxnor_tag , result_type arg1, result_type arg2 )
       {
          //printf("bvxnor\n");
          bv_result a = boost::get<bv_result>(arg1);
          bv_result b = boost::get<bv_result>(arg2);
          assert(a.size()==b.size());
          bv_result ret(a.size());
          predtags::xnor_tag xnor_;

          for (unsigned i = 0; i < a.size(); ++i) {
            ret[i]= _solver(xnor_, a[i], b[i]);
          }
          return ret;
        }

       result_type operator() (bvtags::bvult_tag, result_type arg1, result_type arg2)
       {
          bv_result a = boost::get<bv_result>(arg1);
          bv_result b = boost::get<bv_result>(arg2);
          assert(a.size()==b.size());
          assert(a.size()>0);

          typename bv_result::reverse_iterator ai, bi, end;
          ai = a.rbegin();
          bi = b.rbegin();
          end= a.rend();

          result_base not_a = _solver(predtags::not_tag(), *ai);
          result_base ret = _solver(predtags::and_tag(),not_a, *bi);
          result_base equal = _solver(predtags::xnor_tag(), *ai, *bi);

          for (++ai, ++bi ; ai != end; ++ai, ++bi)
          {
            not_a = _solver(predtags::not_tag(), *ai);
            result_base now_less = _solver(predtags::and_tag(),not_a, *bi);
            result_base now   = _solver(predtags::and_tag(), equal, now_less);

            result_base now_equal = _solver(predtags::xnor_tag(), *ai, *bi);
            equal = _solver(predtags::and_tag(), now_equal, equal);

            ret = _solver(predtags::or_tag(), ret, now);
          }
          return ret;
       }

       result_type operator() (bvtags::bvugt_tag, result_type arg1, result_type arg2)
       {
          bv_result a = boost::get<bv_result>(arg1);
          bv_result b = boost::get<bv_result>(arg2);
          assert(a.size()==b.size());
          assert(a.size()>0);

          typename bv_result::reverse_iterator ai, bi, end;
          ai = a.rbegin();
          bi = b.rbegin();
          end= a.rend();

          result_base not_b = _solver(predtags::not_tag(), *bi);
          result_base ret = _solver(predtags::and_tag(), *ai, not_b);
          result_base equal = _solver(predtags::xnor_tag(), *ai, *bi);

          for (++ai, ++bi ; ai != end; ++ai, ++bi)
          {
            not_b = _solver(predtags::not_tag(), *bi);
            result_base now_great = _solver(predtags::and_tag(),*ai, not_b);
            result_base now   = _solver(predtags::and_tag(), equal, now_great);

            result_base now_equal = _solver(predtags::xnor_tag(), *ai, *bi);
            equal = _solver(predtags::and_tag(), now_equal, equal);

            ret = _solver(predtags::or_tag(), ret, now);
          }
          return ret;
       }

       result_type operator() (bvtags::bvsgt_tag, result_type arg1, result_type arg2)
       {
          bv_result a = boost::get<bv_result>(arg1);
          bv_result b = boost::get<bv_result>(arg2);
          assert(a.size()==b.size());
          assert(a.size()>0);

          typename bv_result::reverse_iterator ai, bi, end;
          ai = a.rbegin();
          bi = b.rbegin();
          end= a.rend();

          result_base not_a = _solver(predtags::not_tag(), *ai);
          result_base not_b = _solver(predtags::not_tag(), *bi);
          result_base ret = _solver(predtags::and_tag(), not_a, *bi);
          result_base equal = _solver(predtags::xnor_tag(), *ai, *bi);

          for (++ai, ++bi ; ai != end; ++ai, ++bi)
          {
            not_b = _solver(predtags::not_tag(), *bi);

            result_base now_great = _solver(predtags::and_tag(),*ai, not_b);
            result_base now   = _solver(predtags::and_tag(), equal, now_great);

            result_base now_equal = _solver(predtags::xnor_tag(), *ai, *bi);
            equal = _solver(predtags::and_tag(), now_equal, equal);

            ret = _solver(predtags::or_tag(), ret, now);
          }
          return ret;
       }

        result_type operator() (bvtags::bvslt_tag, result_type arg1, result_type arg2)
       {
          bv_result a = boost::get<bv_result>(arg1);
          bv_result b = boost::get<bv_result>(arg2);
          assert(a.size()==b.size());
          assert(a.size()>0);

          typename bv_result::reverse_iterator ai, bi, end;
          ai = a.rbegin();
          bi = b.rbegin();
          end= a.rend();

          result_base not_a = _solver(predtags::not_tag(), *ai);
          result_base not_b = _solver(predtags::not_tag(), *bi);
          result_base ret = _solver(predtags::and_tag(), *ai, not_b);
          result_base equal = _solver(predtags::xnor_tag(), *ai, *bi);

          for (++ai, ++bi ; ai != end; ++ai, ++bi)
          {
            not_a = _solver(predtags::not_tag(), *ai);
            result_base now_less = _solver(predtags::and_tag(),not_a, *bi);
            result_base now   = _solver(predtags::and_tag(), equal, now_less);

            result_base now_equal = _solver(predtags::xnor_tag(), *ai, *bi);
            equal = _solver(predtags::and_tag(), now_equal, equal);

            ret = _solver(predtags::or_tag(), ret, now);
          }
          return ret;
       }

        result_type operator() (bvtags::bvule_tag, result_type arg1, result_type arg2)
        {
          bv_result a = boost::get<bv_result>(arg1);
          bv_result b = boost::get<bv_result>(arg2);
          assert(a.size()==b.size());
          assert(a.size()>0);

          typename bv_result::reverse_iterator ai, bi, end;
          ai = a.rbegin();
          bi = b.rbegin();
          end= a.rend();

          result_base not_a = _solver(predtags::not_tag(), *ai);
        //  result_base not_b = _solver(predtags::not_tag(), *bi);
          result_base less = _solver(predtags::and_tag(), not_a, *bi);
          result_base equal = _solver(predtags::xnor_tag(), *ai, *bi);
          result_base ret = less;

          for (++ai, ++bi ; ai != end; ++ai, ++bi)
          {
            not_a = _solver(predtags::not_tag(), *ai);
            result_base now_less = _solver(predtags::and_tag(),not_a, *bi);
            result_base now   = _solver(predtags::and_tag(), equal, now_less);

            result_base now_equal = _solver(predtags::xnor_tag(), *ai, *bi);
            equal = _solver(predtags::and_tag(), now_equal, equal);

            ret = _solver(predtags::or_tag(), ret, now);
          }
          ret = _solver(predtags::or_tag(), ret, equal);
          return ret;
        }

        result_type operator() (bvtags::bvuge_tag, result_type arg1, result_type arg2)
        {
          bv_result a = boost::get<bv_result>(arg1);
          bv_result b = boost::get<bv_result>(arg2);
          assert(a.size()==b.size());
          assert(a.size()>0);

          typename bv_result::reverse_iterator ai, bi, end;
          ai = a.rbegin();
          bi = b.rbegin();
          end= a.rend();

          result_base not_b = _solver(predtags::not_tag(), *bi);
        //  result_base not_b = _solver(predtags::not_tag(), *bi);
          result_base great = _solver(predtags::and_tag(), *ai, not_b);
          result_base equal = _solver(predtags::xnor_tag(), *ai, *bi);
          result_base ret = great;

          for (++ai, ++bi ; ai != end; ++ai, ++bi)
          {
            not_b = _solver(predtags::not_tag(), *bi);
            result_base now_great = _solver(predtags::and_tag(),*ai, not_b);
            result_base now   = _solver(predtags::and_tag(), equal, now_great);

            result_base now_equal = _solver(predtags::xnor_tag(), *ai, *bi);
            equal = _solver(predtags::and_tag(), now_equal, equal);

            ret = _solver(predtags::or_tag(), ret, now);
          }
          ret = _solver(predtags::or_tag(), ret, equal);
          return ret;
        }

       result_type operator() (bvtags::bvsge_tag, result_type arg1, result_type arg2)
       {
          bv_result a = boost::get<bv_result>(arg1);
          bv_result b = boost::get<bv_result>(arg2);
          assert(a.size()==b.size());
          assert(a.size()>0);

          typename bv_result::reverse_iterator ai, bi, end;
          ai = a.rbegin();
          bi = b.rbegin();
          end= a.rend();

          result_base not_a = _solver(predtags::not_tag(), *ai);
          result_base not_b = _solver(predtags::not_tag(), *bi);
          result_base great = _solver(predtags::and_tag(), not_a, *bi);
          result_base equal = _solver(predtags::xnor_tag(), *ai, *bi);
          result_base ret = great;

          for (++ai, ++bi ; ai != end; ++ai, ++bi)
          {
            not_b = _solver(predtags::not_tag(), *bi);
            result_base now_great = _solver(predtags::and_tag(),*ai, not_b);
            result_base now   = _solver(predtags::and_tag(), equal, now_great);

            result_base now_equal = _solver(predtags::xnor_tag(), *ai, *bi);
            equal = _solver(predtags::and_tag(), now_equal, equal);

            ret = _solver(predtags::or_tag(), ret, now);
          }
          ret = _solver(predtags::or_tag(), ret, equal);
          return ret;
       }

        result_type operator() (bvtags::bvsle_tag, result_type arg1, result_type arg2)
       {
          bv_result a = boost::get<bv_result>(arg1);
          bv_result b = boost::get<bv_result>(arg2);
          assert(a.size()==b.size());
          assert(a.size()>0);

          typename bv_result::reverse_iterator ai, bi, end;
          ai = a.rbegin();
          bi = b.rbegin();
          end= a.rend();

          result_base not_b = _solver(predtags::not_tag(), *bi);
          result_base less = _solver(predtags::and_tag(), *ai, not_b);
          result_base equal = _solver(predtags::xnor_tag(), *ai, *bi);
          result_base ret = less;

          for (++ai, ++bi ; ai != end; ++ai, ++bi)
          {
            result_base not_a = _solver(predtags::not_tag(), *ai);
            result_base now_less = _solver(predtags::and_tag(),not_a, *bi);
            result_base now   = _solver(predtags::and_tag(), equal, now_less);

            result_base now_equal = _solver(predtags::xnor_tag(), *ai, *bi);
            equal = _solver(predtags::and_tag(), now_equal, equal);

            ret = _solver(predtags::or_tag(), ret, now);
          }
          ret = _solver(predtags::or_tag(), ret, equal);
          return ret;
       }

        result_type operator() (bvtags::bvadd_tag, result_type arg1, result_type arg2)
       {
          bv_result a = boost::get<bv_result>(arg1);
          bv_result b = boost::get<bv_result>(arg2);
          assert(a.size()==b.size());

          bv_result ret(a.size());

          result_base carry = _solver(predtags::false_tag(), boost::any());

          result_base xor1, or1, and1, and2;

          for (unsigned i = 0; i < a.size(); ++i) {

              xor1 = _solver(predtags::xor_tag(), a[i], b[i]);
              ret[i] = _solver(predtags::xor_tag(), xor1, carry);

              // a&b | c&(a|b)
              and1 = _solver(predtags::and_tag(), a[i], b[i]);
              or1  = _solver(predtags::or_tag(),a[i],b[i]);
              and2 = _solver(predtags::and_tag(),carry, or1);
              carry  = _solver(predtags::or_tag(), and1, and2);

            }
          return ret;
       }

       result_type operator() (bvtags::bvmul_tag, result_type arg1, result_type arg2)
       {
          bv_result a = boost::get<bv_result>(arg1);
          bv_result b = boost::get<bv_result>(arg2);
          result_type ret = bv_result (a.size(), _solver( predtags::false_tag(), boost::any() ) );
          result_type tmp1;

          for(unsigned i = 0 ; i < a.size() ; ++i)
          {
            tmp1 = (*this)(bvtags::sign_extend_tag(),a.size()-1,bv_result(1,a[i]));
            tmp1 = (*this)(bvtags::bvand_tag(),arg2,tmp1);
            tmp1 = shiftL( boost::get<bv_result>(tmp1),i);

            ret = (*this)(bvtags::bvadd_tag(),ret,tmp1);
          }
          return ret;
       }

       result_type operator() ( bvtags::bvneg_tag, result_type arg1 )
       {

          bv_result a = boost::get<bv_result>(arg1);

          bv_result tmp1(a.size(),_solver(predtags::false_tag(),boost::any()));
          tmp1.front()= _solver(predtags::true_tag(), boost::any());
          result_type tmp2 = (*this)(bvtags::bvnot_tag(), arg1);

          return (*this)(bvtags::bvadd_tag(),tmp2,tmp1);
       }

       result_type operator() ( bvtags::bvudiv_tag, result_type arg1, result_type arg2 )
       {
            return uDivRem(arg1,arg2,true);
       }

       result_type operator() ( bvtags::bvsdiv_tag, result_type arg1, result_type arg2 )
       {
        return sDivRem(arg1,arg2,true);
       }

       result_type operator() ( bvtags::bvsrem_tag, result_type arg1, result_type arg2 )
       {
        return sDivRem(arg1,arg2,false);
       }

     result_type operator() ( bvtags::bvhex_tag , boost::any arg )
       {
               std::string str = boost::any_cast<std::string>(arg);
               result_base _0 = _solver(predtags::false_tag(),boost::any());
               result_base _1 = _solver(predtags::true_tag(),boost::any());
               bv_result ret(str.size()*4,_0);
               typename bv_result::iterator iter = ret.begin();

          BOOST_REVERSE_FOREACH( const char c, str) {
            switch ( c ) {
              case '0':
                *(iter++) = _0;
                *(iter++) = _0;
                *(iter++) = _0;
                *(iter++) = _0;
               break;
              case '1':
                *(iter++) = _1;
                *(iter++) = _0;
                *(iter++) = _0;
                *(iter++) = _0;
               break;
              case '2':
                *(iter++) = _0;
                *(iter++) = _1;
                *(iter++) = _0;
                *(iter++) = _0;
               break;
              case '3':
                *(iter++) = _1;
                *(iter++) = _1;
                *(iter++) = _0;
                *(iter++) = _0;
               break;
              case '4':
                *(iter++) = _0;
                *(iter++) = _0;
                *(iter++) = _1;
                *(iter++) = _0;
               break;
              case '5':
                *(iter++) = _1;
                *(iter++) = _0;
                *(iter++) = _1;
                *(iter++) = _0;
               break;
              case '6':
                *(iter++) = _0;
                *(iter++) = _1;
                *(iter++) = _1;
                *(iter++) = _0;
               break;
              case '7':
                *(iter++) = _1;
                *(iter++) = _1;
                *(iter++) = _1;
                *(iter++) = _0;
               break;
              case '8':
                *(iter++) = _0;
                *(iter++) = _0;
                *(iter++) = _0;
                *(iter++) = _1;
               break;
              case '9':
                *(iter++) = _1;
                *(iter++) = _0;
                *(iter++) = _0;
                *(iter++) = _1;
               break;
              case 'a':
              case 'A':
                *(iter++) = _0;
                *(iter++) = _1;
                *(iter++) = _0;
                *(iter++) = _1;
               break;
              case 'b':
              case 'B':
                *(iter++) = _1;
                *(iter++) = _1;
                *(iter++) = _0;
                *(iter++) = _1;
               break;
              case 'c':
              case 'C':
                *(iter++) = _0;
                *(iter++) = _0;
                *(iter++) = _1;
                *(iter++) = _1;
               break;
              case 'd':
              case 'D':
                *(iter++) = _1;
                *(iter++) = _0;
                *(iter++) = _1;
                *(iter++) = _1;
               break;
              case 'e':
              case 'E':
                *(iter++) = _0;
                *(iter++) = _1;
                *(iter++) = _1;
                *(iter++) = _1;
               break;
              case 'f':
              case 'F':
                *(iter++) = _1;
                *(iter++) = _1;
                *(iter++) = _1;
                *(iter++) = _1;
               break;
             }
          }

          return ret;

       }

       result_type operator() ( bvtags::bvurem_tag, result_type arg1, result_type arg2 )
       {
            return uDivRem(arg1,arg2, false);
       }

       result_type operator() ( bvtags::bvsub_tag, result_type arg1, result_type arg2 )
       {
          result_type tmp ((*this)(bvtags::bvneg_tag(), arg2));

           return (*this)(bvtags::bvadd_tag(), arg1,tmp);
       }

     result_type operator() ( bvtags::bvcomp_tag, result_type arg1, result_type arg2 )
       {
          result_type tmp = (*this)(predtags::equal_tag(), arg1,arg2);

          result_base ret = boost::get<result_base>(tmp);

          return bv_result(1,ret);
       }

       result_type operator() ( bvtags::zero_extend_tag, unsigned width, result_type arg1 )
       {
          bv_result a = boost::get<bv_result>(arg1);
          bv_result tmp(a.size()+width,_solver(predtags::false_tag(),boost::any()));

          std::copy(a.begin(), a.end(), tmp.begin());
          return tmp;
       }

       result_type operator() ( bvtags::sign_extend_tag, unsigned width, result_type arg1 )
       {
          bv_result a = boost::get<bv_result>(arg1);
          assert(!a.empty());
          bv_result tmp(a.size()+width, a.back());

          std::copy(a.begin(), a.end(), tmp.begin());
          return tmp;
       }

       result_type operator() ( predtags::equal_tag eq, result_type arg1, result_type arg2 )
       {
          result_base ret;
          //printf("try to compare bv\n");
          try {
            //printf("read arg1\n");
            bv_result a = boost::get<bv_result>(arg1);
            //printf("read arg2\n");
            bv_result b = boost::get<bv_result>(arg2);
            assert(a.size()==b.size());
            ret = _solver(predtags::true_tag(), boost::any());
            for (unsigned i = 0; i < a.size(); ++i) {
              result_base cur = _solver(eq, a[i], b[i]);
              ret = _solver(predtags::and_tag(), cur, ret);
            }
          } catch (boost::bad_get) {
            //printf("try to compare bool\n");
            //printf("read arg1\n");
            result_base a = boost::get<result_base>(arg1);
            //printf("read arg2\n");
            result_base b = boost::get<result_base>(arg2);
            ret = _solver(eq, a, b);
          }
          //printf("compare done\n");
          return ret;
       }

       result_type operator() ( predtags::nequal_tag neq, result_type arg1, result_type arg2 )
       {
          result_base ret;
          //printf("try to compare bv\n");
          try {
            //printf("read arg1\n");
            bv_result a = boost::get<bv_result>(arg1);
            //printf("read arg2\n");
            bv_result b = boost::get<bv_result>(arg2);
            assert(a.size()==b.size());
            ret = _solver(predtags::false_tag(), boost::any());
            for (unsigned i = 0; i < a.size(); ++i) {
              result_base cur = _solver(neq, a[i], b[i]);
              ret = _solver(predtags::or_tag(), cur, ret);
            }
          } catch (boost::bad_get) {
            //printf("try to compare bool\n");
            //printf("read arg1\n");
            result_base a = boost::get<result_base>(arg1);
            //printf("read arg2\n");
            result_base b = boost::get<result_base>(arg2);
            ret = _solver(neq, a, b);
          }
          //printf("compare done\n");
          return ret;
       }

        result_type operator() (bvtags::bvbin_tag , boost::any arg ) {
          //printf("bvbin\n");
          std::string value = boost::any_cast<std::string>(arg);
          bv_result ret (value.size());
          result_base one  = _solver(predtags::true_tag (), boost::any());
          result_base zero = _solver(predtags::false_tag(), boost::any());
          std::string::reverse_iterator vite = value.rbegin();
          typename bv_result::iterator rite = ret.begin();
          for (unsigned i = 0; i < value.size(); ++i) {
            *rite = (*vite)=='1' ? one : zero;
            ++rite;
            ++vite;
          }
          return ret;
        }

        result_type operator() (bvtags::bvuint_tag , boost::any arg ) {
          uint64_t value;
          unsigned width;
          boost::tie(value, width) = boost::any_cast<bvuint_tuple>(arg);

          bv_result ret (width);
          result_base one  = _solver(predtags::true_tag (), boost::any());
          result_base zero = _solver(predtags::false_tag(), boost::any());
          for (unsigned i = 0; i < width; ++i) {
            ret[i] = (value & 1) ? one : zero;
            value >>=1;
          }
          return ret;
        }

        result_type operator() (bvtags::bvsint_tag , boost::any arg ) {
          int64_t value;
          unsigned width;
          boost::tie(value, width) = boost::any_cast<bvsint_tuple>(arg);

          bv_result ret (width);
          result_base one  = _solver(predtags::true_tag (), boost::any());
          result_base zero = _solver(predtags::false_tag(), boost::any());
          for (unsigned i = 0; i < width; ++i) {
            ret[i] = (value & 1) ? one : zero;
            value >>=1;
          }
          return ret;
        }

        result_type operator() (bvtags::bit0_tag , boost::any arg ) {
          //printf("bit0\n");
          return bv_result(1,_solver(predtags::false_tag(), arg));
        }

        result_type operator() (bvtags::bit1_tag , boost::any arg ) {
          //printf("bit1\n");
          return bv_result(1,_solver(predtags::true_tag(), arg));
        }

        result_type operator() (bvtags::bvshr_tag, result_type arg1, result_type value) {
          bv_result a = boost::get<bv_result>(arg1);

          result_base zero = _solver(predtags::false_tag(),boost::any());
          result_type ret = bv_result(a.size(), zero);
          predtags:: ite_tag ite;

          for(unsigned i = 0; i < a.size(); ++i)
          {
            result_type index = (*this)(bvtags::bvuint_tag()
                ,boost::any(bvuint_tuple(i, a.size())));
            ret = (*this)(ite,
                (*this)(predtags::equal_tag(), value, index)
              , shiftR(a, i, zero)
              , ret
            );
          }

          return ret;
        }

        result_type operator() (bvtags::bvshl_tag, result_type arg1, result_type value ) {

          bv_result a = boost::get<bv_result>(arg1);

          result_type ret = bv_result(a.size(), _solver(predtags::false_tag(), boost::any()));
          predtags:: ite_tag ite;

          for(unsigned i = 0; i < a.size(); ++i)
          {
            result_type index = (*this)(bvtags::bvuint_tag()
                ,boost::any(bvuint_tuple(i, a.size())));
            ret = (*this)(ite,
                (*this)(predtags::equal_tag(), value, index)
              , shiftL(a, i)
              , ret
            );
          }
          return ret;
        }

        result_type operator() (bvtags::bvashr_tag, result_type arg1, result_type value ) {
        bv_result a = boost::get<bv_result>(arg1);

          result_type ret = bv_result(a.size(), a.back());
          predtags:: ite_tag ite;

          for(unsigned i = 0; i < a.size(); ++i)
          {
            result_type index = (*this)(bvtags::bvuint_tag()
                ,boost::any(bvuint_tuple(i, a.size())));
            ret = (*this)(ite,
                (*this)(predtags::equal_tag(), value, index)
              , shiftR(a, i, a.back())
              , ret
            );
          }

          return ret;
        }

        result_type operator() (predtags::ite_tag, result_type arg1, result_type arg2, result_type arg3 ) {

          result_base c = boost::get<result_base>(arg1);
          predtags::ite_tag ite;

          try {
           bv_result a = boost::get<bv_result>(arg2);
           bv_result b = boost::get<bv_result>(arg3);
           bv_result ret(a.size());
           assert(a.size()==b.size());

           for (unsigned i = 0; i < a.size(); ++i) {
               ret[i]= _solver(ite,c,a[i],b[i]);
           }

           return ret;
          }
           catch (boost::bad_get) {
           result_base a = boost::get<result_base>(arg2);
           result_base b = boost::get<result_base>(arg3);
            return _solver(ite,c,a,b);
          }

        }

        struct bv_getter : public boost::static_visitor<bv_result> {
          bv_result operator() (bv_result const & bv) const {
            return bv;
          }
          template <typename T>
          bv_result operator() (T) const {
            assert(false && "expected bitvector here.");
            return bv_result();
          }
        };

        result_type operator() (bvtags::extract_tag const &
            , unsigned upper, unsigned lower
            , result_type e
        ) {
          bv_result ret(upper-lower+1);
          bv_result bv = boost::apply_visitor(bv_getter(), e);
          std::copy(bv.begin()+lower, bv.begin()+upper+1, ret.begin());
          return ret;
        }

        result_type operator() (bvtags::concat_tag const &
            , result_type e1, result_type e2
        ) {
          bv_result bv1 = boost::apply_visitor(bv_getter(), e1);
          bv_result bv2 = boost::apply_visitor(bv_getter(), e2);
          bv_result ret(bv1.size()+bv2.size());
          std::copy(bv2.begin(), bv2.end(), ret.begin() );
          std::copy(bv1.begin(), bv1.end(), ret.begin() + bv2.size() );
          return ret;
        }

        result_wrapper read_value(result_type var)
        {
          try {
            return read_value(boost::get<result_base>(var));
          } catch ( boost::bad_get ) {
            return read_value(boost::get<bv_result>(var));
          }
        }

        result_wrapper read_value(result_base var)
        {
          return _solver.read_value(var);
        }

        result_wrapper read_value(bv_result const & vars)
        {
          std::vector<boost::logic::tribool> ret(vars.size());
          std::vector<boost::logic::tribool>::iterator it
            = ret.begin();

          for (unsigned i = 0; i < vars.size(); ++i, ++it) {
            *it = _solver.read_value( vars[i] );
          }

          return result_wrapper(ret);
        }

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

        template <typename TagT, typename Any>
        //boost::disable_if< boost::is_same(Any, bv_result)::type, result_type >::type
        result_type
        operator() (TagT tag, Any args ) {
          try {
            // std::cout << "operator " << tag << std::endl;
           return _solver(tag, args);
          } catch (boost::bad_get) {
//            std::cout << "Error bad_get in operator " << typeid(tag).name() << std::endl;
            throw;
          }
        }

        template <typename TagT>
        result_type operator() (TagT tag, result_type a ) {
          return _solver( tag
            , boost::get<result_base>(a)
          );
        }

        template <typename TagT>
        result_type operator() (TagT tag, result_type a, result_type b) {
          try {
          return _solver( tag
            , boost::get<result_base>(a)
            , boost::get<result_base>(b)
          );
          } catch (boost::bad_get) {
//            std::cout << "Error bad_get in operator " << typeid(tag).name() << std::endl;
            throw;
          }
        }

        template <typename TagT>
        result_type operator() (TagT tag, result_type a, result_type b, result_type c) {
          try {
          return _solver( tag
            , boost::get<result_base>(a)
            , boost::get<result_base>(b)
            , boost::get<result_base>(c)
          );
          } catch (boost::bad_get) {
//            std::cout << "Error bad_get in operator " << typeid(tag).name() << std::endl;
            throw;
          }
        }

      /* pseudo command */
      void command ( BitBlast<PredicateSolver> const & ) { };
      template<typename Command, typename Expr>
      void command ( Command const& cmd, Expr& expr )
      {
        _solver.command ( cmd, expr );
      }

    private:
      result_type sDivRem (result_type arg1, result_type arg2, bool value) {

        bv_result a = boost::get<bv_result>(arg1);
        bv_result b = boost::get<bv_result>(arg2);

        predtags:: ite_tag ite;
        predtags:: xor_tag xor_;
        bvtags:: bvneg_tag neg;

        result_type tmp1 = (*this)(neg,arg1);
        result_type tmp2 = (*this)(neg,arg2);

        result_type aneg = (*this)(ite, a.back(), (*this)(neg,arg1),arg1);
        result_type bneg = (*this)(ite, b.back(), (*this)(neg,arg2),arg2);

        result_type test = uDivRem(aneg,bneg,value);

        test = (*this)(ite, _solver(xor_, a.back(), b.back()), (*this)(neg,test),test);

        return test;
      }

    private:
      result_type uDivRem (result_type arg1, result_type arg2, bool value) {

          bv_result a = boost::get<bv_result>(arg1);
          bv_result b = boost::get<bv_result>(arg2);

          result_type divisor = arg2 ;

          result_base zero = _solver(predtags::false_tag(), boost::any());
          result_base one = _solver(predtags::true_tag(), boost::any());

          bv_result ret(a.size(),zero);
          result_type checker = zero;
          predtags::ite_tag ite;
         /*

          bvtags:: bvand_tag and_;
          bvtags:: bvneg_tag neg;

        result_type ret1 = (*this)(bvtags::bvuge_tag(), a, ret);
        result_type ret2 = (*this)(bvtags::bvuge_tag(), b, ret);
        result_type ret3 = (*this)(bvtags::bvslt_tag(), a, ret);
        result_type ret4 = (*this)(bvtags::bvslt_tag(), b, ret);

       result_type bneg = (*this)(ite, _solver(and_, ret1, ret4), (*this)(bvtags::bvneg_tag(),arg2),b);
       b = boost::get<bv_result>(bneg);

       result_type aneg = (*this)(ite, _solver(and_, ret3, ret2), (*this)(bvtags::bvneg_tag(),arg1),a);
       a = boost::get<bv_result>(aneg);

        result_type aeg = (*this)(ite, _solver(and_, ret3, ret4), (*this)(bvtags::bvneg_tag(),arg1),a);
        a = boost::get<bv_result>(aeg);

        result_type args1 = a;
        result_type args2 = b;
        */
           for(unsigned i = 0; i < a.size(); ++i)
          {
             result_type tmp = b.back();
             divisor = (*this)(ite,tmp, divisor, shiftL(b,1));
             b = boost::get<bv_result>(divisor);
          }

          for(unsigned i = 1; i <=a.size(); ++i)
          {
            result_type bef_dev = divisor;
            result_type do_devide = (*this)(bvtags::bvuge_tag(), arg1, divisor);
            result_type eq = (*this)(predtags::equal_tag(),bef_dev,arg2);

             arg1 = (*this)(ite, do_devide, (*this)(bvtags::bvsub_tag(), arg1, divisor),arg1);

            divisor = (*this)(ite, eq, divisor, shiftR(boost::get<bv_result>(divisor), 1, zero));

             do_devide = (*this)(ite,checker,zero, do_devide);
             ret[a.size()-i] = boost::get<result_base>(do_devide);

             result_type bo = (*this)(ite,checker,shiftR(ret,1, zero),ret);

             ret = boost::get<bv_result>(bo);

             checker = (*this)(ite, eq , one, checker);
         }

          if(value)
          {
            return ret;
          }

        return arg1;
       }

    private:
      result_type shiftR (bv_result a, unsigned value, result_base & x) {

        //bv_result a = boost::get<bv_result>(arg);
        bv_result ret(a.size());

        if(value == 0)
        {
          return a;
        }

        for(unsigned i= 0; i < a.size(); ++value,++i)
        {
          if(value < a.size())
          {
            ret[i] = a[value];
          } else
            ret[i] = x;
        }
       return ret;
      }

   private:
      result_type shiftL (bv_result a, unsigned value) {

        //bv_result a = boost::get<bv_result>(arg);
        bv_result ret(a.size());

        if(value == 0)
        {
          return a;
        }

        for(unsigned i= 0; i < a.size(); ++i)
        {
          if( i < value)
          {
             ret[i] = _solver(predtags::false_tag(),boost::any());
          } else
             ret[i] = a[i-value];
        }
       return ret;
      }

    private:
        PredicateSolver _solver;

  };

  namespace features {
    /* Stack supports stack api */
    template<typename Context>
    struct supports< BitBlast<Context>, features::addclause_api>
    : boost::mpl::true_ {};

    /* Forward all other supported operations */
    template<typename Context, typename Feature>
    struct supports< BitBlast<Context>, Feature>
    : supports<Context, Feature>::type {};
  }

} // namespace metaSMT

//  vim: ft=cpp:ts=2:sw=2:expandtab


// Copyright 2015-2022 The ALMA Project Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.

/// @file
/// Definitions corresponding to vasp_io.hpp.

#include <utility>
#include <iostream>
#include <fstream>
#include <boost/format.hpp>
#include <vasp_io.hpp>
#include <utilities.hpp>
#include <exceptions.hpp>

namespace alma {
std::unique_ptr<Crystal_structure> load_POSCAR(const char* filename) {
    std::ifstream f(filename);

    if (!f) {
        throw value_error("could not open file");
    }
    // The first line in the file can be either a description or an
    // element list.
    std::string firstline;
    std::getline(f, firstline);
    // The next line contains a multiplicative factor.
    double factor;
    f >> factor;
    factor /= 10.;
    // The next three lines contain the lattice vectors, one per
    // line.
    Eigen::Matrix3d lattvec;

    for (auto i = 0; i < 3; ++i)
        for (auto j = 0; j < 3; ++j)
            f >> lattvec(j, i);
    lattvec *= factor;
    // The next line can either contain an element list or a list of
    // integers.
    std::string line;
    flush_istream(f);
    std::getline(f, line);
    std::vector<std::string> elements;
    std::vector<int> numbers;
    try {
        numbers = tokenize_homogeneous_line<int>(line);
        // The list of elements must have been in the first line.
        elements = tokenize_homogeneous_line<std::string>(firstline);
    }
    catch (boost::bad_lexical_cast& e) {
        elements = tokenize_homogeneous_line<std::string>(line);
        // The list of integers must come next.
        std::getline(f, line);
        numbers = tokenize_homogeneous_line<int>(line);
    }
    // Next we can have "Selective dynamics", which we ignore.
    std::getline(f, line);

    if (starts_with_character(line, "Ss"))
        std::getline(f, line);
    // The next line tells us about the type of coordinates contained
    // in the file: Direct or Cartesian.
    auto cartesian = starts_with_character(line, "CcKk");
    // Finally, read the coordinates.
    auto natoms = std::accumulate(numbers.begin(), numbers.end(), 0);
    Eigen::Matrix<double, 3, Eigen::Dynamic> positions(3, natoms);

    for (int i = 0; i < natoms; ++i)
        for (auto j = 0; j < 3; ++j)
            f >> positions(j, i);

    // Make sure that we store lattice coordinates.
    if (cartesian)
        positions = lattvec.colPivHouseholderQr().solve(positions);

    // The code expects direct coordinates to lie in the
    // [0.,1.) range.
    for (int i = 0; i < natoms; ++i)
        for (auto j = 0; j < 3; ++j) {
            positions(j, i) = std::fmod(positions(j, i), 1.);

            if (positions(j, i) < 0.)
                positions(j, i) += 1.;
        }
    // Build and return the object.
    return std::make_unique<Crystal_structure>(
        lattvec, positions, elements, numbers);
}

std::unique_ptr<Harmonic_ifcs> load_FORCE_CONSTANTS(
    const char* filename,
    const Crystal_structure& cell,
    const int na,
    const int nb,
    const int nc) {
    if (std::min({na, nb, nc}) <= 0)
        throw value_error("na, nb and nc must be positive");
    std::ifstream f(filename);

    if (!f) {
        throw value_error("could not open file");
    }
    // The first line of the file contains the number of atoms in the
    // supercell.
    int ntot;
    f >> ntot;
    auto natoms = cell.get_natoms();
    auto ndof = 3 * natoms;
    auto nexpected = na * nb * nc * natoms;

    if (ntot != nexpected)
        throw value_error(boost::str(
            boost::format("expected %1% atoms, got %2%") % nexpected % ntot));
    // Then comes one block for each atom pair.
    // This means that the file is either highly redundant or
    // inconsistent. Here we assume the former.
    int field;
    std::string line;
    Triple_int_map<Eigen::MatrixXd> matrices;
    auto builder = Supercell_index_builder(na, nb, nc, natoms);

    for (auto i = 0; i < ntot; ++i) {
        for (auto j = 0; j < ntot; ++j) {
            f >> field;
            auto index1 = builder.create_index_safely(field - 1);
            f >> field;
            auto index2 = builder.create_index_safely(field - 1);

            if ((index1.index != i) || (index2.index != j))
                throw input_error("unexpected cell indices");
            // If there is anything else in the line, discard it.
            std::string tmp;
            std::getline(f, tmp);

            if ((index1.ia == 0) && (index1.ib == 0) && (index1.ic == 0)) {
                // The first unit cell is (0, 0, 0). Read and store
                // the data.
                auto key = index2.get_pos();

                if (matrices.find(key) == matrices.end())
                    // If the submatrix does not exist, create it.
                    matrices[key] = Eigen::MatrixXd(ndof, ndof);

                for (auto k = 0; k < 3; ++k)
                    for (auto l = 0; l < 3; ++l)
                        f >> matrices[key](3 * index1.iatom + k,
                                           3 * index2.iatom + l);
            }
            else {
                // Redundant block. Skip those lines.
                flush_istream(f);

                for (auto k = 0; k < 3; ++k)
                    std::getline(f, line);
            }
        }
    }
    // Put the results into data structures that can be passed to the
    // constructor.
    std::vector<Triple_int> pos;
    std::vector<Eigen::MatrixXd> ifcs;
    std::tie(pos, ifcs) = split_keys_and_values(matrices);
    return std::make_unique<Harmonic_ifcs>(pos, ifcs, na, nb, nc);
}

Eigen::ArrayXXd load_FORCE_CONSTANTS_raw(const char* filename) {
    std::ifstream f(filename);

    if (!f) {
        throw value_error("could not open file");
    }
    std::size_t ntot;
    f >> ntot;
    std::size_t ndof = 3 * ntot;
    Eigen::ArrayXXd nruter{ndof, ndof};

    for (std::size_t i = 0; i < ntot; ++i) {
        for (std::size_t j = 0; j < ntot; ++j) {
            std::size_t index;
            f >> index;

            if (index - 1 != i)
                throw input_error("unexpected index");
            f >> index;

            if (index - 1 != j)
                throw input_error("unexpected index");
            std::string tmp;
            std::getline(f, tmp);

            for (auto k = 0; k < 3; ++k)
                for (auto l = 0; l < 3; ++l)
                    f >> nruter(3 * i + k, 3 * j + l);
        }
    }
    return nruter;
}

std::unique_ptr<Dielectric_parameters> load_BORN(const char* filename) {
    std::ifstream f(filename);

    if (!f) {
        throw value_error("could not open file");
    }
    // The first line of the file contains a unit conversion factor
    // employed by Phonopy. We ignore it.
    std::string line;
    std::getline(f, line);
    // Then come the nine components of the dielectric tensor.
    Eigen::Matrix3d epsilon;

    for (auto k = 0; k < 3; ++k)
        for (auto l = 0; l < 3; ++l)
            f >> epsilon(k, l);
    // And finally the born charge tensors for each atom.
    flush_istream(f);
    std::vector<Eigen::MatrixXd> born;

    do {
        std::getline(f, line);
        auto fields = tokenize_homogeneous_line<double>(line);

        if (fields.size() == 0) {
            continue;
        }
        else if (fields.size() == 9) {
            auto tensor = Eigen::MatrixXd(3, 3);

            for (auto k = 0; k < 3; ++k)
                for (auto l = 0; l < 3; ++l)
                    tensor(k, l) = fields[3 * k + l];
            born.emplace_back(tensor);
        }
        else {
            throw input_error("wrong number of fields in a line");
        }
    } while (!f.eof());
    return std::make_unique<Dielectric_parameters>(born, epsilon);
}

std::unique_ptr<std::vector<Thirdorder_ifcs>> load_FORCE_CONSTANTS_3RD(
    const char* filename,
    const Crystal_structure& cell) {
    std::ifstream f(filename);

    if (!f) {
        throw value_error("could not open file");
    }
    auto solver = cell.lattvec.colPivHouseholderQr();
    // The first line is the number of blocks in the file.
    std::size_t nblocks;
    f >> nblocks;
    auto nruter = std::make_unique<std::vector<Thirdorder_ifcs>>();
    nruter->reserve(nblocks);

    // For each block.
    for (decltype(nblocks) iblock = 0; iblock < nblocks; ++iblock) {
        std::size_t tmp;
        // Read the block number and complain if it does not
        // match our expectations.
        f >> tmp;

        if (tmp != iblock + 1)
            throw value_error("wrong block number");
        // Read the Cartesian coordinates of the second unit cell.
        Eigen::VectorXd rj(3);
        f >> rj(0) >> rj(1) >> rj(2);
        // Read the Cartesian coordinates of the third unit cell.
        Eigen::VectorXd rk(3);
        f >> rk(0) >> rk(1) >> rk(2);
        // Convert these coordinates to nm and
        // round each vector to the closest unit cell.
        Eigen::VectorXd solj = solver.solve(rj / 10.);
        Eigen::VectorXd solk = solver.solve(rk / 10.);

        for (auto ic = 0; ic < 3; ++ic) {
            solj(ic) = std::round(solj(ic));
            solk(ic) = std::round(solk(ic));
        }
        rj = cell.lattvec * solj;
        rk = cell.lattvec * solk;
        // The next line contains the three atom indices.
        std::size_t i;
        std::size_t j;
        std::size_t k;
        f >> i >> j >> k;
        // Create an empty Thirdorder_ifcs object.
        Thirdorder_ifcs block(rj, rk, i - 1, j - 1, k - 1);

        // And read the contents of the 27 remaining lines to fill
        // in the values of the ifcs.
        for (auto ic = 0; ic < 27; ++ic) {
            int p1;
            int p2;
            int p3;
            f >> p1 >> p2 >> p3;
            f >> block.ifc(p1 - 1, p2 - 1, p3 - 1);
        }
        // Add the new block to the vector.
        nruter->emplace_back(block);
    }
    return nruter;
}
} // namespace alma


// ====================================================================
// This file is part of FlexibleSUSY.
//
// FlexibleSUSY 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.
//
// FlexibleSUSY is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with FlexibleSUSY.  If not, see
// <http://www.gnu.org/licenses/>.
// ====================================================================

#ifndef COUPLING_MONITOR_H
#define COUPLING_MONITOR_H

#include <vector>
#include <cmath>
#include <algorithm>
#include <fstream>
#include <iostream>
#include <iomanip>

#include <Eigen/Dense>
#include "logger.hpp"
#include "error.hpp"

namespace flexiblesusy {

/**
 * @class Coupling_monitor
 * @brief stores model parameters at different scales
 *
 * Template arguments are the model type and the parameter getter
 * type.  The paramameter getter has to provide two functions:
 * get_parameters() which returns the values of the parameters, and
 * get_parameter_names() wich returns the names of the parameters.
 *
 * Example:
 * @code
 * class MSSM_parameter_getter {
 * public:
 *    Eigen::ArrayXd get_parameters(const MSSM& model) {
 *       return model.get();
 *    }
 *    std::vector<std::string> get_parameter_names() const {
 *       return ...;
 *    }
 * };
 *
 * MSSM model;
 * MSSM_parameter_getter getter;
 * Coupling_monitor<MSSM, MSSM_parameter_getter> cm(model, getter);
 *
 * const double start_scale = 100.;
 * const double stop_scale = 1.0e12;
 * const unsigned number_of_points = 50;
 * const bool include_endpoint = true;
 *
 * cm.run(start_scale, stop_scale, number_of_points, include_endpoint);
 * cm.write_to_file("running_coupling.dat");
 * @endcode
 */
template <class Model, class DataGetter>
class Coupling_monitor {
public:
   typedef std::pair<double, Eigen::ArrayXd> TTouple;///< touple of scale and couplings

   Coupling_monitor(const Model&, const DataGetter&);
   ~Coupling_monitor() {}

   /// get couplings at all scales
   void run(double, double, unsigned int number_of_steps = 20, bool include_endpoint = false);
   /// get maximum scale
   TTouple get_max_scale() const;
   /// delete all saved couplings
   void clear();
   /// write couplings to file
   void write_to_file(const std::string&, bool overwrite = true) const;

private:
   typedef std::vector<TTouple> TData; ///< container for the scales and couplings
   struct TDataComp {
      bool operator() (const TData::value_type& i,const TData::value_type& j) {
         return i.first < j.first;
      }
   };

   TData couplings;        ///< all couplings at all scales
   Model model;            ///< the model
   DataGetter data_getter; ///< hepler class which extracts the model parameters
   unsigned width;         ///< width of columns in output table

   /// write line with parameter names
   void write_parameter_names_line(std::ofstream&) const;
   /// write a comment line
   void write_comment_line(std::ofstream&) const;
};

template <class Model, class DataGetter>
Coupling_monitor<Model,DataGetter>::Coupling_monitor(const Model& model_, const DataGetter& data_getter_)
   : couplings(TData())
   , model(model_)
   , data_getter(data_getter_)
   , width(16)
{
}

/**
 * Get the couplings at the largest scale
 *
 * @return a pair with the scale and a Eigen::ArrayXd which contains the
 * couplings at this scale
 */
template <class Model, class DataGetter>
typename Coupling_monitor<Model,DataGetter>::TTouple Coupling_monitor<Model,DataGetter>::get_max_scale() const
{
   if (couplings.empty()) {
      ERROR("Data container is empty!");
      return TTouple(0.0, Eigen::ArrayXd(1));
   }

   // find gauge couplings at the greatest scale
   TData::const_iterator maxScale
      = max_element(couplings.begin(), couplings.end(), TDataComp());

   return *maxScale;
}

/**
 * Delete all internal couplings.
 */
template <class Model, class DataGetter>
void Coupling_monitor<Model,DataGetter>::clear()
{
   couplings.clear();
}

/**
 * write line with parameter names
 *
 * @param fout output stream
 */
template <class Model, class DataGetter>
void Coupling_monitor<Model,DataGetter>::write_parameter_names_line(std::ofstream& fout) const
{
   if (!fout.good() || couplings.empty())
      return;

   const std::size_t number_of_couplings = couplings.front().second.size();
   const std::vector<std::string> parameter_names(data_getter.get_parameter_names());

   if (number_of_couplings != parameter_names.size()) {
      ERROR("number of couplings != length of list of parameter names");
   }

   fout << std::left << std::setw(width) << "scale";

   for (std::size_t i = 0; i < number_of_couplings; ++i)
      fout << std::left << std::setw(width) << parameter_names[i];

   fout << '\n';
}

/**
 * write help line which describes the written data
 *
 * @param fout output stream
 */
template <class Model, class DataGetter>
void Coupling_monitor<Model,DataGetter>::write_comment_line(std::ofstream& fout) const
{
   if (!fout.good() || couplings.empty())
      return;

   const std::size_t number_of_couplings = couplings.front().second.size();
   const std::vector<std::string> parameter_names(data_getter.get_parameter_names());

   if (number_of_couplings != parameter_names.size()) {
      ERROR("number of couplings != length of list of parameter names");
   }

   fout << std::left << std::setw(width) << "# [1] scale";

   for (std::size_t i = 0; i < number_of_couplings; ++i) {
      std::ostringstream parameter;
      parameter << '[' << (i+2) << "] " << parameter_names[i];

      fout << std::left << std::setw(width) << parameter.str();
   }

   fout << '\n';
}

/**
 * Write all couplings to a text file.
 *
 * @param file_name name of file to write the data to
 * @param overwrite if true, file is overwritten, otherwise content is appended
 */
template <class Model, class DataGetter>
void Coupling_monitor<Model,DataGetter>::write_to_file(const std::string& file_name, bool overwrite) const
{
   if (couplings.empty())
      return;

   const std::ios_base::openmode openmode
      = (overwrite ? std::ios::out : std::ios::app);

   std::ofstream filestr(file_name.c_str(), openmode);
   VERBOSE_MSG("Coupling_monitor<>::write_to_file: opening file: "
               << file_name.c_str());
   if (filestr.fail()) {
      ERROR("can't open file " << file_name
            << " for writing running couplings");
      return;
   }

   write_comment_line(filestr);
   write_parameter_names_line(filestr);

   // write data
   for (TData::const_iterator it = couplings.begin();
        it != couplings.end(); ++it) {
      if (!filestr.good()) {
         ERROR("file " << file_name << " is corrupted");
         break;
      }

      filestr << std::left << std::setw(width) << it->first;

      // write all gauge couplings in order
      for (int i = 0; i < it->second.size(); ++i) {
         filestr << std::left << std::setw(width) << it->second(i);
      }

      filestr << '\n';
   }

   filestr.close();
   VERBOSE_MSG("Coupling_monitor<>::write_to_file: file written: "
               << file_name.c_str());
}

/**
 * Add running couplings between scale q1 and q2.
 *
 * @param q1 scale to start at
 * @param q2 end scale
 * @param number_of_steps number of steps
 * @param include_endpoint include the endpoint q2 in the running
 *        (false by default)
 */
template <class Model, class DataGetter>
void Coupling_monitor<Model,DataGetter>::run(double q1, double q2,
                                             unsigned int number_of_steps, bool include_endpoint)
{
   if (q1 <= 0.0 || q2 <= 0.0) {
      ERROR("negative scales are not allowed: q1=" << q1 << ", q2=" << q2);
      return;
   }

   if (number_of_steps < 1)
      number_of_steps = 1;

   // if the endpoint should be included, the scale loop must run from
   // (n == 0) to (n == number_of_steps); otherwise it runs from (n == 0) to (n
   // == number_of_steps - 1)
   const unsigned int endpoint_offset = include_endpoint ? 1 : 0;

   // run from q1 to q2
   for (unsigned int n = 0; n < number_of_steps + endpoint_offset; ++n) {
      const double scale = exp(log(q1) + n * (log(q2) - log(q1)) / number_of_steps);
      try {
         model.run_to(scale);
      } catch (const Error&) {
         ERROR("Coupling_monitor::run: run to scale "
               << scale << " failed");
         break;
      }
      couplings.push_back(TData::value_type(scale, data_getter.get_parameters(model)));
   }

   std::sort(couplings.begin(), couplings.end(), TDataComp());
}

}

#endif


/*
 * This is part of the fl library, a C++ Bayesian filtering library
 * (https://github.com/filtering-library)
 *
 * Copyright (c) 2015 Max Planck Society,
 * 				 Autonomous Motion Department,
 * 			     Institute for Intelligent Systems
 *
 * This Source Code Form is subject to the terms of the MIT License (MIT).
 * A copy of the license can be found in the LICENSE file distributed with this
 * source code.
 */

/**
 * \file traits.hpp
 * \date October 2014
 * \author Jan Issac (jan.issac@gmail.com)
 */

#pragma once

#include <Eigen/Dense>

#include "types.hpp"
#include "profiling.hpp"

namespace fl
{

/**
 * \ingroup traits
 * \def from_traits
 * \brief Helper macro to import typedefs from the traits of a class
 *
 * typedef from_traits(SomeTypeName);
 *
 * is the short hand for
 *
 * typedef typename Traits<This>::SomeTypeName SomeTypeName;
 *
 * \note from_this(.) requires the typedef \c This of the current class
 */
#define from_traits(TypeName) typename Traits<This>::TypeName TypeName

#if defined(__GXX_EXPERIMENTAL_CXX0X__)
    #define override
#endif

/**
 * \ingroup traits
 * \brief Generic trait template
 *
 * Filters, models and distributions may specify a \c Traits specialization
 */
template <typename> struct Traits { };

/**
 * \ingroup traits
 *
 * \brief \c IsDynamic<int> trait for static dynamic-size checks.
 *
 * Generic IsDynamic<int> definition which evaluates to false.
 *
 * Examples
 *
 * static_assert(IsDynamic<MyEigenMatrixType::SizeAtCompileTime>(), "");
 *
 * if (IsDynamic<MyEigenMatrixType::SizeAtCompileTime>::value) ...
 */
template <int Size> struct IsDynamic
{
    static_assert(Size > Eigen::Dynamic, "Invalid static size");

    static constexpr bool value = false;
    constexpr operator bool () const { return value; }
};

/**
 * \ingroup traits
 * \brief \c IsDynamic<-1> or \c IsDynamic<Eigen::Dynamic> trait for static
 * dynamic-size checks.
 *
 * A specialization IsDynamic<Eigen::Dynamic> definition which evaluates to
 * true.
 *
 * Examples
 *
 * // overloaded operator bool ()
 * static_assert(!IsDynamic<MyEigenMatrixType::SizeAtCompileTime>(), "");
 *
 * if (!IsDynamic<MyEigenMatrixType::SizeAtCompileTime>::value) ...
 */
template <> struct IsDynamic<Eigen::Dynamic>
{
    static constexpr bool value = true;
    constexpr operator bool () const { return value; }
};

/**
 * \ingroup traits
 *
 * \brief \c IsFixed<int> trait for static fixed-size checks.
 *
 * Generic IsFixed<int> definition which evaluates to true. This traits is the
 * opposite of IsDynamic.
 *
 * Examples
 *
 * static_assert(IsFixed<MyEigenMatrixType::SizeAtCompileTime>::value, "");
 *
 * // overloaded operator bool ()
 * if (IsFixed<MyEigenMatrixType::SizeAtCompileTime>()) ...
 */
template <int Size> struct IsFixed
{
    static_assert(Size > Eigen::Dynamic, "Invalid static size");

    static constexpr bool value = true;
    constexpr operator bool () const { return value; }
};

/**
 * \ingroup traits
 * \brief \c IsFixed<-1> or \c IsFixed<Eigen::Dynamic> trait for static
 * fixed-size checks.
 *
 * A specialization IsFixed<Eigen::Dynamic> definition which evaluates to
 * false. This traits is the opposite of IsDynamic<Eigen::Dynamic>.
 *
 * Examples
 *
 * static_assert(!IsFixed<MyEigenMatrixType::SizeAtCompileTime>(), "");
 *
 * if (!IsFixed<MyEigenMatrixType::SizeAtCompileTime>::value) ...
 */
template <> struct IsFixed<Eigen::Dynamic>
{
    static constexpr bool value = false;
    constexpr operator bool () const { return value; }
};

/**
 * \ingroup traits
 *
 * \brief Mapps Eigen::Dynamic onto 0.
 *
 * For any type matrix or column vector the dimension is the number of rows,
 * i.e. Matrix::RowsAtCompileTime. If the Matrix::SizeAtCompileTime enum is not
 * equal Eigen::Dynamic (-1) the dimension is set to Matrix::RowsAtCompileTime.
 * Otherwise, 0 is returned.
 *
 * Examples
 *
 * static_assert(DimensionOf<MyEigenMatrixType>() > 0, "Dim must be greater 0");
 *
 * static_assert(DimensionOf<MyEigenMatrixType>::value > 0, "Dim must be .. 0");
 *
 * Eigen::VectorXd vector(DimensionOf<MyEigenMatrixType>());
 */
template <typename Matrix> struct DimensionOf
{
    enum : signed int { Value = IsFixed<Matrix::SizeAtCompileTime>()
                                    ? Matrix::RowsAtCompileTime
                                    : 0 };

    constexpr operator int () const { return Value; }
};

/**
 * \ingroup traits
 * \brief Returns the compile time size of a matrix or vector.
 *
 * SizeOf<M>() is shorthand for M::SizeAtCompileTime
 */
template <typename Matrix> struct SizeOf
{
    enum : signed int { Value = Matrix::SizeAtCompileTime };

    constexpr operator int () const { return Value; }
};

/**
 * \ingroup traits
 *
 */
template <int Dimension> struct ToDimension
{
    enum : signed int { Value = Dimension == Eigen::Dynamic ? 0 : Dimension };

    constexpr operator int () const { return Value; }
};

/**
 * \ingroup traits
 *
 * \brief Returns simple the max integer of A and B
 */
template <int A, int B> struct MaxOf
{
    enum : signed int { Value = (A > B) ? A : B };
    static constexpr int value = (A > B) ? A : B;
    constexpr operator int () const { return Value; }
};

/**
 * \ingroup traits
 *
 * \brief Returns simple the min integer of A and B
 */
template <int A, int B> struct MinOf
{
    enum : signed int { Value = (A < B) ? A : B };
    static constexpr int value = (A < B) ? A : B;
    constexpr operator int () const { return Value; }
};

/**
 * \ingroup traits
 *
 * Defines the second moment type of a given variate
 */
template <typename Variate>
struct SecondMomentOf
{
    enum: signed int { Dimension = SizeOf<Variate>::Value };
    typedef Eigen::Matrix<Real, Dimension, Dimension> Type;
};

/**
 * \ingroup traits
 */
template <typename Variate>
struct FirstMomentOf
{
    enum: signed int { Dimension = SizeOf<Variate>::Value };
    typedef Eigen::Matrix<Real, Dimension, 1> Type;
};

/**
 * \ingroup traits
 *
 * Defines the second moment type of a given variate
 */
template <typename Variate>
struct DiagonalSecondMomentOf
{
    enum: signed int { Dimension = SizeOf<Variate>::Value };
    typedef Eigen::DiagonalMatrix<Real, Dimension> Type;
};

/**
 * \ingroup traits
 *
 * Defines the second moment type of a given variate
 */
template <>
struct SecondMomentOf<Real>
{
    typedef Real Type;
};

/**
 * \ingroup traits
 */
template <>
struct FirstMomentOf<Real>
{
    typedef Real Type;
};

/**
 * \ingroup traits
 * Defines a Real variate of the specified size
 */
template <int Size>
struct VariateOfSize
{
    typedef Eigen::Matrix<Real, Size, 1> Type;
};

/**
 * \ingroup traits
 */
template <typename Model> struct IsAdditive
{
    enum: bool
    {
        Value = std::is_base_of<internal::AdditiveNoiseModelType, Model>::value
    };
};

/**
 * \ingroup traits
 */
template <typename Model> struct IsAdditiveUncorrelated
{
    enum: bool
    {
        Value = std::is_base_of<
                    internal::AdditiveUncorrelatedNoiseModelType,
                    Model
                >::value
    };
};

/**
 * \ingroup traits
 */
template <typename Model> struct IsNonAdditive
{
    enum: bool
    {
        Value = std::is_base_of<internal::NonAdditiveNoiseModelType, Model>::value
    };
};

/**
 * \internal
 * \ingroup traits
 */
template <typename Model, typename ...> struct AdditivityOf;

/**
 * \internal
 * \ingroup traits
 */
template <typename Model>
struct AdditivityOf<Model, internal::AdditiveNoiseModelType>
{
    typedef Additive<Model> Type;
};

/**
 * \internal
 * \ingroup traits
 */
template <typename Model>
struct AdditivityOf<Model, internal::AdditiveUncorrelatedNoiseModelType>
{
    typedef AdditiveUncorrelated<Model> Type;
};

/**
 * \internal
 * \ingroup traits
 */
template <typename Model>
struct AdditivityOf<Model, internal::NonAdditiveNoiseModelType>
{
    typedef NonAdditive<Model> Type;
};

/**
 * \internal
 * \ingroup traits
 */
template <typename Model>
struct AdditivityOf<Additive<Model>>
{
    typedef Additive<Model> Type;
};

/**
 * \internal
 * \ingroup traits
 */
template <typename Model>
struct AdditivityOf<AdditiveUncorrelated<Model>>
{
    typedef AdditiveUncorrelated<Model> Type;
};

/**
 * \internal
 * \ingroup traits
 */
template <typename Model>
struct AdditivityOf<NonAdditive<Model>>
{
    typedef NonAdditive<Model> Type;
};

/**
 * \ingroup traits
 * \brief Provides access to the type of the model
 *
 * The model type will be one of the following
 *  - \c NonAdditive (Noise)
 *  - \c Additive (Noise)
 *  - \c AdditiveUncorrelated (Noise)
 */
template <typename Model> struct AdditivityOf<Model>
{
    typedef typename AdditivityOf<Model, typename Model::Type>::Type Type;
};

/**
 * \internal
 * \ingroup traits
 */
template <typename Model> struct RemoveAdditivityOf
{
    typedef Model Type;
};

template <typename Model>
struct RemoveAdditivityOf<Additive<Model>>
{
    typedef Model Type;
};

template <typename Model>
struct RemoveAdditivityOf<AdditiveUncorrelated<Model>>
{
    typedef Model Type;
};

template <typename Model>
struct RemoveAdditivityOf<NonAdditive<Model>>
{
    typedef Model Type;
};

/**
 * \internal
 *
 * \ingroup traits
 */
template <typename Model>
struct ForwardLinearModelOnly
{
    static_assert(std::is_base_of<internal::LinearModelType, Model>::value,
                  "The specified model is not linear!");

    typedef Model Type;
};

}