#pragma once

#include <mbgl/annotation/annotation.hpp>
#include <mbgl/util/geo.hpp>

#include <string>

#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point.hpp>
#include <boost/geometry/geometries/box.hpp>
#include <boost/geometry/geometries/register/point.hpp>
#include <boost/geometry/geometries/register/box.hpp>
#include <boost/geometry/index/rtree.hpp>

namespace mbgl {

class AnnotationTileLayer;
class CanonicalTileID;

class SymbolAnnotationImpl {
public:
    SymbolAnnotationImpl(AnnotationID, SymbolAnnotation);

    void updateLayer(const CanonicalTileID&, AnnotationTileLayer&) const;

    const AnnotationID id;
    const SymbolAnnotation annotation;
};

} // namespace mbgl

namespace boost {
namespace geometry {

// Make Boost Geometry aware of our LatLng type
namespace traits {

template<> struct tag<mbgl::LatLng> { using type = point_tag; };
template<> struct dimension<mbgl::LatLng> : boost::mpl::int_<2> {};
template<> struct coordinate_type<mbgl::LatLng> { using type = double; };
template<> struct coordinate_system<mbgl::LatLng> { using type = boost::geometry::cs::cartesian; };

template<> struct access<mbgl::LatLng, 0> { static inline double get(mbgl::LatLng const& p) { return p.longitude(); } };
template<> struct access<mbgl::LatLng, 1> { static inline double get(mbgl::LatLng const& p) { return p.latitude(); } };

template<> struct tag<mbgl::LatLngBounds> { using type = box_tag; };
template<> struct point_type<mbgl::LatLngBounds> { using type = mbgl::LatLng; };

template <size_t D>
struct indexed_access<mbgl::LatLngBounds, min_corner, D>
{
    using ct = coordinate_type<mbgl::LatLng>::type;
    static inline ct get(mbgl::LatLngBounds const& b) { return geometry::get<D>(b.southwest()); }
    static inline void set(mbgl::LatLngBounds& b, ct const& value) { geometry::set<D>(b.southwest(), value); }
};

template <size_t D>
struct indexed_access<mbgl::LatLngBounds, max_corner, D>
{
    using ct = coordinate_type<mbgl::LatLng>::type;
    static inline ct get(mbgl::LatLngBounds const& b) { return geometry::get<D>(b.northeast());  }
    static inline void set(mbgl::LatLngBounds& b, ct const& value) { geometry::set<D>(b.northeast(), value); }
};

} // namespace traits

// Tell Boost Geometry how to access a std::shared_ptr<mbgl::SymbolAnnotation> object.
namespace index {

template <>
struct indexable<std::shared_ptr<const mbgl::SymbolAnnotationImpl>> {
    using result_type = mbgl::LatLng;
    mbgl::LatLng operator()(const std::shared_ptr<const mbgl::SymbolAnnotationImpl>& v) const {
        const mbgl::Point<double>& p = v->annotation.geometry;
        return {p.y, p.x};
    }
};

} // namespace index

} // namespace geometry
} // namespace boost


#pragma once

#include <cmath>

#include <boost/align/is_aligned.hpp>
#include <boost/simd/function/aligned_load.hpp>
#include <boost/simd/function/load.hpp>
#include <boost/simd/function/plus.hpp>
#include <boost/simd/function/store.hpp>
#include <boost/simd/pack.hpp>

typedef boost::simd::pack<double> pack_double;

struct sum_tag;
struct cum_sum_tag;
struct weighted_sum_tag;

template<typename tag>
struct source_1d;

template<typename T>
size_t static_size() {
    return sizeof(T) / sizeof(double);
}

template<>
struct source_1d<sum_tag>
{
    source_1d<sum_tag>(double const *src, std::size_t length)
        : src_(src), length_(length) {}

    template<typename T>
    void procure(T& container)
    {
        container += boost::simd::aligned_load<T>(src_);
        src_ += static_size<T>();
        length_ -= static_size<T>();
    }

    template<typename T>
    bool can_procure(int num, T const& item) const
    {
        return length_ >= num * static_size<T>();
    }

    bool is_aligned() const
    {
        return boost::alignment::is_aligned(src_, pack_double::alignment);
    }

private:
    double const *src_;
    std::size_t length_;
};

template<>
struct source_1d<weighted_sum_tag>
{
    source_1d<weighted_sum_tag>(double const *array,
                                           double const *weights,
                                           std::size_t length)
        : src_(array), weights_(weights), length_(length) {}

    template<typename T>
    void procure(T& container)
    {
        container += (boost::simd::aligned_load<T>(src_)
                      * boost::simd::load<T>(weights_));
        src_ += static_size<T>();
        weights_ += static_size<T>();
        length_ -= static_size<T>();
    }

    template<typename T>
    bool can_procure(int num, T const& item) const
    {
        return length_ >= num * static_size<T>();
    }

    bool is_aligned() const
    {
        return boost::alignment::is_aligned(src_, pack_double::alignment);
    }

private:
    double const *src_;
    double const *weights_;
    std::size_t length_;
};

template<>
struct source_1d<cum_sum_tag>
{
    source_1d<cum_sum_tag>(double const *src,
                                      double *dst,
                                      std::size_t length)
            : src_(src), dst_(dst), length_(length) {}

    template<typename T>
    void procure(T& container)
    {
        container = boost::simd::aligned_load<T>(src_);
        src_ += static_size<T>();
        length_ -= static_size<T>();
    }

    template<typename T>
    bool can_procure(int num, T const& item) const
    {
        return length_ >= num * static_size<T>();
    }

    bool is_aligned() const
    {
        return boost::alignment::is_aligned(src_, pack_double::alignment);
    }

    template<typename T>
    void feed(T const value)
    {
        // TODO: aligned_store?
        // TODO: error: 'T' does not refer to a value
        boost::simd::store(value, dst_);
        dst_ += static_size<T>();
    }

private:
    double const *src_;
    double *dst_;
    std::size_t length_;
};

struct weighted_mean_tag;
struct sd_tag;

template<typename tag>
struct source_2d;

template<>
struct source_2d<weighted_mean_tag>
{
    source_2d<weighted_mean_tag>(double const* array,
                                            double const* weights,
                                            std::size_t length)
        : array_(array), weights_(weights), length_(length) {}

    template<typename T>
    void procure(T& vw, T& w)
    {
        T const value = boost::simd::aligned_load<T>(array_);
        T const weight = boost::simd::load<T>(weights_);
        vw += value * weight;
        w += weight;
        array_ += static_size<T>();
        weights_ += static_size<T>();
        length_ -= static_size<T>();
    }

    template<typename T>
    bool can_procure(int num, T const& item) const
    {
        return length_ >= num * static_size<T>();
    }

    bool is_aligned() const
    {
        return boost::alignment::is_aligned(array_, pack_double::alignment);
    }

    double result(double vw, double w) const
    {
        return vw / w;
    }

private:
    double const* array_;
    double const *weights_;
    std::size_t length_;
};

template<>
struct source_2d<sd_tag>
{
    source_2d<sd_tag>(double const* array,
                                                 std::size_t length)
        : array_(array), length_(length), initial_length_(length) {}

    template<typename T>
    void procure(T& v2, T& v)
    {
        T const value = boost::simd::aligned_load<T>(array_);
        v2 += value * value;
        v += value;
        array_ += static_size<T>();
        length_ -= static_size<T>();
    }

    template<typename T>
    bool can_procure(int num, T const& item) const
    {
        return length_ >= num * static_size<T>();
    }

    bool is_aligned() const
    {
        return boost::alignment::is_aligned(array_, pack_double::alignment);
    }

    double result(double v2, double v) const
    {
        double const res = (v2 - v * v / initial_length_)
            / (initial_length_ - 1);
        return (res < 0.) ? 0. : sqrt(res);
    }

private:
    double const* array_;
    std::size_t length_;
    std::size_t const initial_length_;
};


/* boost random/detail/uniform_int_float.hpp header file
 *
 * Copyright Jens Maurer 2000-2001
 * Copyright Steven Watanabe 2011
 * Distributed under the Boost Software License, Version 1.0. (See
 * accompanying file LICENSE_1_0.txt or copy at
 * http://www.boost.org/LICENSE_1_0.txt)
 *
 * See http://www.boost.org for most recent version including documentation.
 *
 * $Id: uniform_int_float.hpp 72951 2011-07-07 04:57:37Z steven_watanabe $
 *
 */

#ifndef BOOST_RANDOM_DETAIL_UNIFORM_INT_FLOAT_HPP
#define BOOST_RANDOM_DETAIL_UNIFORM_INT_FLOAT_HPP

#include <boost/limits.hpp>
#include <boost/config.hpp>
#include <boost/integer.hpp>
#include <boost/random/detail/config.hpp>
#include <boost/random/detail/generator_bits.hpp>

#include <boost/random/detail/disable_warnings.hpp>

namespace boost {
namespace random {
namespace detail {

template<class URNG>
class uniform_int_float
{
public:
    typedef URNG base_type;
    typedef typename base_type::result_type base_result;

    typedef typename boost::uint_t<
        (std::numeric_limits<boost::uintmax_t>::digits <
            std::numeric_limits<base_result>::digits)?
        std::numeric_limits<boost::uintmax_t>::digits :
        std::numeric_limits<base_result>::digits
    >::fast result_type;

    uniform_int_float(base_type& rng)
      : _rng(rng) {}

    static result_type min BOOST_PREVENT_MACRO_SUBSTITUTION ()
    { return 0; }
    static result_type max BOOST_PREVENT_MACRO_SUBSTITUTION ()
    {
        std::size_t digits = std::numeric_limits<result_type>::digits;
        if(detail::generator_bits<URNG>::value() < digits) {
            digits = detail::generator_bits<URNG>::value();
        }
        return (result_type(2) << (digits - 1)) - 1;
    }
    base_type& base() { return _rng; }
    const base_type& base() const { return _rng; }

    result_type operator()()
    {
        base_result range = static_cast<base_result>((max)())+1;
        return static_cast<result_type>(_rng() * range);
    }

private:
    base_type& _rng;
};

} // namespace detail
} // namespace random
} // namespace boost

#include <boost/random/detail/enable_warnings.hpp>

#endif // BOOST_RANDOM_DETAIL_UNIFORM_INT_FLOAT_HPP


#include "cloudproc/convexdecomp.hpp"
#include "cloudproc/cloudproc.hpp"
#include "cloudproc/sphere_sampling.hpp"
#include <boost/foreach.hpp>
#include "LinearMath/btConvexHull.h"
#include <openrave/openrave.h>
#include <openrave-core.h>
#include "osgviewer/osgviewer.hpp"
using namespace pcl;
using namespace OpenRAVE;
using namespace std;
using namespace cloudproc;

#ifdef __CDT_PARSER__
#define BOOST_FOREACH(...) for(;;)
#endif

btVector3 toBt(const Vector& v) {
  return btVector3(v.x, v.y, v.z);
}
Vector toOR(const btVector3& v) {
  return Vector(v.x(), v.y(), v.z());
}
btVector3 toBt(const PointXYZ& v) {
  return btVector3(v.x, v.y, v.z);
}
ostream &operator<<(ostream &stream, const btVector3& v) {
  stream << v.x() << " " << v.y() << " " << v.z();
  return stream;
}

int main() {

  RaveInitialize(true, OpenRAVE::Level_Debug);
  EnvironmentBasePtr env = RaveCreateEnvironment();
  bool success = env->Load("robots/pr2-beta-static.zae");
  OSGViewerPtr viewer(new OSGViewer(env));

  vector<RobotBasePtr> robots;
  env->GetRobots(robots);
  RobotBasePtr robot = robots[0];
  Vector cam = robot->GetLink("laser_tilt_link")->GetTransform().trans;

  PointCloud<PointXYZ>::Ptr cloud = readPCDXYZ(
      "/home/joschu/Downloads/laser_cloud.pcd");
  cloud = downsampleCloud(cloud, .02);

  vector<IntVec> hull_inds;
  ConvexDecomp(cloud, getSpherePoints(1), .02, NULL, &hull_inds);

  float extrusion_dist = .5;
  vector<GraphHandlePtr> handles;

  BOOST_FOREACH(const IntVec& inds, hull_inds) {
    vector<btVector3> origpts;
    BOOST_FOREACH(int ind, inds) {
      origpts.push_back(toBt(cloud->points[ind]));
      origpts.push_back(origpts.back() + extrusion_dist * (origpts.back() - toBt(cam)).normalized());
    }
    cout << "num pts: " << origpts.size() << endl;
    if (origpts.size() < 5) continue;
    HullDesc hd;
    hd.mFlags = QF_TRIANGLES;
    hd.mVcount = origpts.size();
    hd.mVertices = &origpts[0];
    hd.mVertexStride = sizeof(btVector3);

    HullLibrary hl;
    HullResult hr;

    if (hl.CreateConvexHull(hd, hr) == QE_FAIL) {
      continue;
    }

#define randf() ((float)rand())/RAND_MAX

    handles.push_back(viewer->drawtrimesh((float*) &hr.m_OutputVertices[0], 16,
        (int*) &hr.m_Indices[0], hr.mNumFaces, RaveVectorf(randf(), randf(), randf(), 1)));
    SetTransparency(handles.back(), 1);

  }

  Eigen::MatrixXf colors(cloud->size(), 4);
  colors.setOnes();
  handles.push_back(viewer->plot3((float*)cloud->points.data(), cloud->size(), 16, 8, colors.data()));

  viewer->Idle();

}


#include <smtbx/refinement/weighting_schemes.h>

#include <boost/python/class.hpp>

namespace smtbx { namespace refinement { namespace least_squares {
  namespace boost_python {

  template<template<typename FloatType> class WeightingScheme>
  struct weighting_scheme_class
    : boost::python::class_< WeightingScheme<double> >
  {
    typedef WeightingScheme<double> wt;
    typedef boost::python::class_<wt> base_t;

    static af::shared<double> weights(wt const &weighting_scheme,
                                      af::const_ref<double> const &fo_sq,
                                      af::const_ref<double> const &sigmas,
                                      af::const_ref<double> const &fc_sq,
                                      double scale_factor)
    {
      return least_squares::weights(
        weighting_scheme, fo_sq, sigmas, fc_sq, scale_factor);
    }

    weighting_scheme_class(char const *name)
      : base_t(name, boost::python::no_init)
    {
      using namespace boost::python;
      this->def("__call__", &wt::operator(),
          (arg("fo_sq"), arg("sigma"), arg("fc_sq"), arg("scale_factor")));
      this->def("__call__", weights,
          (arg("fo_sq"), arg("sigmas"), arg("fc_sq"), arg("scale_factor")));
    }
  };

  struct mainstream_shelx_weighting_wrapper
  {
    static void wrap() {
      using namespace boost::python;
      typedef weighting_scheme_class<mainstream_shelx_weighting> wt;
      wt("mainstream_shelx_weighting")
        .def(init<optional<double, double> >((arg("a"), arg("b"))))
        .def_readwrite("a", &wt::wt::a)
        .def_readwrite("b", &wt::wt::b)
        ;
    }
  };

  struct unit_weighting_wrapper
  {
    static void wrap() {
      using namespace boost::python;
      weighting_scheme_class<unit_weighting>("unit_weighting")
        .def(init<>())
        ;
    }
  };

  struct sigma_weighting_wrapper
  {
    static void wrap() {
      using namespace boost::python;
      weighting_scheme_class<sigma_weighting>("sigma_weighting")
        .def(init<>())
        ;
    }
  };

  void wrap_weighting_schemes() {
    mainstream_shelx_weighting_wrapper::wrap();
    unit_weighting_wrapper::wrap();
    sigma_weighting_wrapper::wrap();
  }

}}}}


///////////////////////////////////////////////////////////////////////////////
// marginal_cells.hpp                                                        //
//                                                                           //
//  Copyright 2010 Erwann Rogard. Distributed under the Boost                //
//  Software License, Version 1.0. (See accompanying file                    //
//  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)         //
///////////////////////////////////////////////////////////////////////////////
#ifndef BOOST_STATISTICS_DETAIL_NON_PARAMETRIC_CONTINGENCY_TABLE_CELLS_MARGINAL_CELLS_HPP_ER_2010
#define BOOST_STATISTICS_DETAIL_NON_PARAMETRIC_CONTINGENCY_TABLE_CELLS_MARGINAL_CELLS_HPP_ER_2010
#include <boost/mpl/fold.hpp>
#include <boost/mpl/vector/vector0.hpp>
#include <boost/mpl/vector/vector10.hpp>
#include <boost/mpl/push_back.hpp>
#include <boost/mpl/placeholders.hpp>
#include <boost/statistics/detail/non_parametric/contingency_table/cells/cells.hpp>

namespace boost {
namespace statistics{
namespace detail{
namespace contingency_table{

    template<typename Keys>
    struct marginal_cells : boost::mpl::fold<
        Keys,
        boost::mpl::vector0<>,
        boost::mpl::push_back<
            boost::mpl::_1,
            contingency_table::tag::cells<
                boost::mpl::vector1<boost::mpl::_2>
            >
        >
    >{};

}// contingency_table
}// detail
}// statistics
}// boost

#endif


/*
 * 	surgeme_server.hpp
 *
 *	Author(s): Tamas D. Nagy
 *	Created on: 2017-11-06
 *
 *	Separated ROS node to support preempted irob_msgs/Surgeme actions.
 *  Contains the implementation of surgemes, like
 *	grasp, release, cut, nav_to_pos, etc.
 *  Handles instrument info to decide is the surgeme is executable with the
 *  installed instrument, and calculate using the length of jaws.
 *
 */

#ifndef SURGAME_SERVER_HPP_
#define SURGAME_SERVER_HPP_

#include <iostream>
#include <sstream>
#include <vector>
#include <cmath>
#include <ros/ros.h>
#include <ros/package.h>
#include <std_msgs/String.h>
#include <geometry_msgs/PoseStamped.h>
#include <std_msgs/Float32.h>
#include <Eigen/Dense>
#include <Eigen/Geometry>
#include <irob_utils/tool_pose.hpp>
#include <irob_utils/trajectory.hpp>
#include <irob_utils/abstract_directions.hpp>
#include <irob_utils/utils.hpp>
#include <irob_general_robot/robot_client.hpp>
#include <actionlib/server/simple_action_server.h>
#include <actionlib/client/simple_action_client.h>
#include <actionlib/client/terminal_state.h>
#include <irob_msgs/ToolPoseStamped.h>
#include <sensor_msgs/JointState.h>
#include <cmath>
#include <tf2/LinearMath/Quaternion.h>
#include <tf2_ros/transform_broadcaster.h>
#include <geometry_msgs/TransformStamped.h>

#include <irob_msgs/SurgemeAction.h>

namespace saf {

class SurgemeServer {

public:

  struct SurgemeSetting {
    double jaw_open_angle;
    double jaw_closed_angle;
    Eigen::Vector3d t;	// translation

    friend std::ostream& operator<<(std::ostream&, const SurgemeSetting&);
  };

protected:
  RobotClient arm;
  ros::NodeHandle nh;
  tf2_ros::TransformBroadcaster br_ori;
  tf2_ros::TransformBroadcaster br_new;

  // Action servers
  actionlib::SimpleActionServer<irob_msgs::SurgemeAction> as;

  static const double DEFAULT_LOOP_RATE;					// Hz

public:
  SurgemeServer(ros::NodeHandle, std::string, double);		// dt
  ~SurgemeServer();

  // Callbacks

  void surgemeActionCB(
      const irob_msgs::SurgemeGoalConstPtr &);

  ToolPose getPoseCurrent();

  sensor_msgs::JointState getJointStateCurrent();

  std::string getArmName();

protected:

  // Methods for surgeme execution
  void stop();

  void nav_to_pos(ToolPose ,std::vector<ToolPose>, InterpolationMethod, double);

  void grasp(ToolPose, ToolPose, double, double, std::vector<ToolPose>,
             InterpolationMethod,
             double, double);

  void cut(ToolPose, ToolPose, double, std::vector<ToolPose>,
           InterpolationMethod,
           double, double);

  void push(ToolPose, ToolPose, Eigen::Vector3d, std::vector<ToolPose>,
            InterpolationMethod,
            double, double);

  void dissect(ToolPose, ToolPose, Eigen::Vector3d,double, std::vector<ToolPose>,
               InterpolationMethod,
               double, double);

  void release(ToolPose, double,
               double, double);

  void place(ToolPose, ToolPose, double, std::vector<ToolPose>, InterpolationMethod,
             double);

  void manipulate(Eigen::Vector3d,
                  double);

  void move_cam(Eigen::Vector3d, Eigen::Vector3d,
                  double);

  bool waitForActionDone(std::string);
  void handleActionState(std::string, bool = false);
  bool isAbleToDoSurgeme(int);
  irob_msgs::InstrumentJawPart findInstrumentJawPartForSurgeme(int);
  SurgemeSetting calcSurgemeSetting(int, irob_msgs::InstrumentJawPart,
                                    Eigen::Quaternion<double>, double, double = 1.0);

};

}
#endif /* SURGAME_SERVER_HPP_ */


#pragma once

#include <chratos/lib/interface.h>
#include <chratos/secure/common.hpp>

#include <boost/asio.hpp>

#include <bitset>

#include <xxhash/xxhash.h>

namespace chratos
{
using endpoint = boost::asio::ip::udp::endpoint;
bool parse_port (std::string const &, uint16_t &);
bool parse_address_port (std::string const &, boost::asio::ip::address &, uint16_t &);
using tcp_endpoint = boost::asio::ip::tcp::endpoint;
bool parse_endpoint (std::string const &, chratos::endpoint &);
bool parse_tcp_endpoint (std::string const &, chratos::tcp_endpoint &);
bool reserved_address (chratos::endpoint const &, bool);
}
static uint64_t endpoint_hash_raw (chratos::endpoint const & endpoint_a)
{
	assert (endpoint_a.address ().is_v6 ());
	chratos::uint128_union address;
	address.bytes = endpoint_a.address ().to_v6 ().to_bytes ();
	XXH64_state_t hash;
	XXH64_reset (&hash, 0);
	XXH64_update (&hash, address.bytes.data (), address.bytes.size ());
	auto port (endpoint_a.port ());
	XXH64_update (&hash, &port, sizeof (port));
	auto result (XXH64_digest (&hash));
	return result;
}
static uint64_t ip_address_hash_raw (boost::asio::ip::address const & ip_a)
{
	assert (ip_a.is_v6 ());
	chratos::uint128_union bytes;
	bytes.bytes = ip_a.to_v6 ().to_bytes ();
	XXH64_state_t hash;
	XXH64_reset (&hash, 0);
	XXH64_update (&hash, bytes.bytes.data (), bytes.bytes.size ());
	auto result (XXH64_digest (&hash));
	return result;
}

namespace std
{
template <size_t size>
struct endpoint_hash
{
};
template <>
struct endpoint_hash<8>
{
	size_t operator() (chratos::endpoint const & endpoint_a) const
	{
		return endpoint_hash_raw (endpoint_a);
	}
};
template <>
struct endpoint_hash<4>
{
	size_t operator() (chratos::endpoint const & endpoint_a) const
	{
		uint64_t big (endpoint_hash_raw (endpoint_a));
		uint32_t result (static_cast<uint32_t> (big) ^ static_cast<uint32_t> (big >> 32));
		return result;
	}
};
template <>
struct hash<chratos::endpoint>
{
	size_t operator() (chratos::endpoint const & endpoint_a) const
	{
		endpoint_hash<sizeof (size_t)> ehash;
		return ehash (endpoint_a);
	}
};
template <size_t size>
struct ip_address_hash
{
};
template <>
struct ip_address_hash<8>
{
	size_t operator() (boost::asio::ip::address const & ip_address_a) const
	{
		return ip_address_hash_raw (ip_address_a);
	}
};
template <>
struct ip_address_hash<4>
{
	size_t operator() (boost::asio::ip::address const & ip_address_a) const
	{
		uint64_t big (ip_address_hash_raw (ip_address_a));
		uint32_t result (static_cast<uint32_t> (big) ^ static_cast<uint32_t> (big >> 32));
		return result;
	}
};
template <>
struct hash<boost::asio::ip::address>
{
	size_t operator() (boost::asio::ip::address const & ip_a) const
	{
		ip_address_hash<sizeof (size_t)> ihash;
		return ihash (ip_a);
	}
};
}
namespace boost
{
template <>
struct hash<chratos::endpoint>
{
	size_t operator() (chratos::endpoint const & endpoint_a) const
	{
		std::hash<chratos::endpoint> hash;
		return hash (endpoint_a);
	}
};
}

namespace chratos
{
/**
 * Message types are serialized to the network and existing values must thus never change as
 * types are added, removed and reordered in the enum.
 */
enum class message_type : uint8_t
{
	invalid = 0x0,
	not_a_type = 0x1,
	keepalive = 0x2,
	publish = 0x3,
	confirm_req = 0x4,
	confirm_ack = 0x5,
	bulk_pull = 0x6,
	bulk_push = 0x7,
	frontier_req = 0x8,
	bulk_pull_blocks = 0x9,
	node_id_handshake = 0x0a,
	bulk_pull_account = 0x0b
};
enum class bulk_pull_blocks_mode : uint8_t
{
	list_blocks,
	checksum_blocks
};
enum class bulk_pull_account_flags : uint8_t
{
	pending_hash_and_amount = 0x0,
	pending_address_only = 0x1,
	pending_hash_amount_and_address = 0x2
};
class message_visitor;
class message_header
{
public:
	message_header (chratos::message_type);
	message_header (bool &, chratos::stream &);
	void serialize (chratos::stream &);
	bool deserialize (chratos::stream &);
	chratos::block_type block_type () const;
	void block_type_set (chratos::block_type);
	bool ipv4_only ();
	void ipv4_only_set (bool);
	static std::array<uint8_t, 2> constexpr magic_number = chratos::chratos_network == chratos::chratos_networks::chratos_test_network ? std::array<uint8_t, 2> { { 'C', 'A' } } : chratos::chratos_network == chratos::chratos_networks::chratos_beta_network ? std::array<uint8_t, 2> { { 'C', 'B' } } : std::array<uint8_t, 2> { { 'C', 'C' } };
	uint8_t version_max;
	uint8_t version_using;
	uint8_t version_min;
	chratos::message_type type;
	std::bitset<16> extensions;
	static size_t constexpr ipv4_only_position = 1;
	static size_t constexpr bootstrap_server_position = 2;
	static std::bitset<16> constexpr block_type_mask = std::bitset<16> (0x0f00);
	inline bool valid_magic () const
	{
		return magic_number[0] == 'C' && magic_number[1] >= 'A' && magic_number[1] <= 'C';
	}
	inline bool valid_network () const
	{
		return (magic_number[1] - 'A') == static_cast<int> (chratos::chratos_network);
	}
};
class message
{
public:
	message (chratos::message_type);
	message (chratos::message_header const &);
	virtual ~message () = default;
	virtual void serialize (chratos::stream &) = 0;
	virtual bool deserialize (chratos::stream &) = 0;
	virtual void visit (chratos::message_visitor &) const = 0;
	chratos::message_header header;
};
class work_pool;
class message_parser
{
public:
	enum class parse_status
	{
		success,
		insufficient_work,
		invalid_header,
		invalid_message_type,
		invalid_keepalive_message,
		invalid_publish_message,
		invalid_confirm_req_message,
		invalid_confirm_ack_message,
		invalid_node_id_handshake_message,
		outdated_version,
		invalid_magic,
		invalid_network
	};
	message_parser (chratos::message_visitor &, chratos::work_pool &);
	void deserialize_buffer (uint8_t const *, size_t);
	void deserialize_keepalive (chratos::stream &, chratos::message_header const &);
	void deserialize_publish (chratos::stream &, chratos::message_header const &);
	void deserialize_confirm_req (chratos::stream &, chratos::message_header const &);
	void deserialize_confirm_ack (chratos::stream &, chratos::message_header const &);
	void deserialize_node_id_handshake (chratos::stream &, chratos::message_header const &);
	bool at_end (chratos::stream &);
	chratos::message_visitor & visitor;
	chratos::work_pool & pool;
	parse_status status;
	std::string status_string ();
	static const size_t max_safe_udp_message_size;
};
class keepalive : public message
{
public:
	keepalive (bool &, chratos::stream &, chratos::message_header const &);
	keepalive ();
	void visit (chratos::message_visitor &) const override;
	bool deserialize (chratos::stream &) override;
	void serialize (chratos::stream &) override;
	bool operator== (chratos::keepalive const &) const;
	std::array<chratos::endpoint, 8> peers;
};
class publish : public message
{
public:
	publish (bool &, chratos::stream &, chratos::message_header const &);
	publish (std::shared_ptr<chratos::block>);
	void visit (chratos::message_visitor &) const override;
	bool deserialize (chratos::stream &) override;
	void serialize (chratos::stream &) override;
	bool operator== (chratos::publish const &) const;
	std::shared_ptr<chratos::block> block;
};
class confirm_req : public message
{
public:
	confirm_req (bool &, chratos::stream &, chratos::message_header const &);
	confirm_req (std::shared_ptr<chratos::block>);
	bool deserialize (chratos::stream &) override;
	void serialize (chratos::stream &) override;
	void visit (chratos::message_visitor &) const override;
	bool operator== (chratos::confirm_req const &) const;
	std::shared_ptr<chratos::block> block;
};
class confirm_ack : public message
{
public:
	confirm_ack (bool &, chratos::stream &, chratos::message_header const &);
	confirm_ack (std::shared_ptr<chratos::vote>);
	bool deserialize (chratos::stream &) override;
	void serialize (chratos::stream &) override;
	void visit (chratos::message_visitor &) const override;
	bool operator== (chratos::confirm_ack const &) const;
	std::shared_ptr<chratos::vote> vote;
};
class frontier_req : public message
{
public:
	frontier_req ();
	frontier_req (bool &, chratos::stream &, chratos::message_header const &);
	bool deserialize (chratos::stream &) override;
	void serialize (chratos::stream &) override;
	void visit (chratos::message_visitor &) const override;
	bool operator== (chratos::frontier_req const &) const;
	chratos::account start;
	uint32_t age;
	uint32_t count;
};
class bulk_pull : public message
{
public:
	bulk_pull ();
	bulk_pull (bool &, chratos::stream &, chratos::message_header const &);
	bool deserialize (chratos::stream &) override;
	void serialize (chratos::stream &) override;
	void visit (chratos::message_visitor &) const override;
	chratos::uint256_union start;
	chratos::block_hash end;
};
class bulk_pull_account : public message
{
public:
	bulk_pull_account ();
	bulk_pull_account (bool &, chratos::stream &, chratos::message_header const &);
	bool deserialize (chratos::stream &) override;
	void serialize (chratos::stream &) override;
	void visit (chratos::message_visitor &) const override;
	chratos::uint256_union account;
	chratos::uint128_union minimum_amount;
	bulk_pull_account_flags flags;
};
class bulk_pull_blocks : public message
{
public:
	bulk_pull_blocks ();
	bulk_pull_blocks (bool &, chratos::stream &, chratos::message_header const &);
	bool deserialize (chratos::stream &) override;
	void serialize (chratos::stream &) override;
	void visit (chratos::message_visitor &) const override;
	chratos::block_hash min_hash;
	chratos::block_hash max_hash;
	bulk_pull_blocks_mode mode;
	uint32_t max_count;
};
class bulk_push : public message
{
public:
	bulk_push ();
	bulk_push (chratos::message_header const &);
	bool deserialize (chratos::stream &) override;
	void serialize (chratos::stream &) override;
	void visit (chratos::message_visitor &) const override;
};
class node_id_handshake : public message
{
public:
	node_id_handshake (bool &, chratos::stream &, chratos::message_header const &);
	node_id_handshake (boost::optional<chratos::block_hash>, boost::optional<std::pair<chratos::account, chratos::signature>>);
	bool deserialize (chratos::stream &) override;
	void serialize (chratos::stream &) override;
	void visit (chratos::message_visitor &) const override;
	bool operator== (chratos::node_id_handshake const &) const;
	boost::optional<chratos::uint256_union> query;
	boost::optional<std::pair<chratos::account, chratos::signature>> response;
	static size_t constexpr query_flag = 0;
	static size_t constexpr response_flag = 1;
};
class message_visitor
{
public:
	virtual void keepalive (chratos::keepalive const &) = 0;
	virtual void publish (chratos::publish const &) = 0;
	virtual void confirm_req (chratos::confirm_req const &) = 0;
	virtual void confirm_ack (chratos::confirm_ack const &) = 0;
	virtual void bulk_pull (chratos::bulk_pull const &) = 0;
	virtual void bulk_pull_account (chratos::bulk_pull_account const &) = 0;
	virtual void bulk_pull_blocks (chratos::bulk_pull_blocks const &) = 0;
	virtual void bulk_push (chratos::bulk_push const &) = 0;
	virtual void frontier_req (chratos::frontier_req const &) = 0;
	virtual void node_id_handshake (chratos::node_id_handshake const &) = 0;
	virtual ~message_visitor ();
};

/**
 * Returns seconds passed since unix epoch (posix time)
 */
inline uint64_t seconds_since_epoch ()
{
	return std::chrono::duration_cast<std::chrono::seconds> (std::chrono::system_clock::now ().time_since_epoch ()).count ();
}
}


// Boost.Geometry (aka GGL, Generic Geometry Library)

// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
// Copyright (c) 2008-2012 Bruno Lalande, Paris, France.
// Copyright (c) 2009-2012 Mateusz Loskot, London, UK.

// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands.

// Use, modification and distribution is subject to the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_GEOMETRY_ALGORITHMS_UNIQUE_HPP
#define BOOST_GEOMETRY_ALGORITHMS_UNIQUE_HPP

#include <algorithm>

#include <boost/range.hpp>
#include <boost/typeof/typeof.hpp>

#include <boost/geometry/core/interior_rings.hpp>
#include <boost/geometry/core/mutable_range.hpp>
#include <boost/geometry/geometries/concepts/check.hpp>
#include <boost/geometry/policies/compare.hpp>

namespace boost { namespace geometry
{

#ifndef DOXYGEN_NO_DETAIL
namespace detail { namespace unique
{

template <typename Range, typename ComparePolicy>
struct range_unique
{
    static inline void apply(Range& range, ComparePolicy const& policy)
    {
        typename boost::range_iterator<Range>::type it
            = std::unique
                (
                    boost::begin(range),
                    boost::end(range),
                    policy
                );

        traits::resize<Range>::apply(range, it - boost::begin(range));
    }
};

template <typename Polygon, typename ComparePolicy>
struct polygon_unique
{
    static inline void apply(Polygon& polygon, ComparePolicy const& policy)
    {
        typedef typename geometry::ring_type<Polygon>::type ring_type;

        typedef range_unique<ring_type, ComparePolicy> per_range;
        per_range::apply(exterior_ring(polygon), policy);

        typename interior_return_type<Polygon>::type rings
                    = interior_rings(polygon);
        for (BOOST_AUTO_TPL(it, boost::begin(rings)); it != boost::end(rings); ++it)
        {
            per_range::apply(*it, policy);
        }
    }
};

}} // namespace detail::unique
#endif // DOXYGEN_NO_DETAIL

#ifndef DOXYGEN_NO_DISPATCH
namespace dispatch
{

template
<
    typename Tag,
    typename Geometry,
    typename ComparePolicy
>
struct unique
{
    static inline void apply(Geometry&, ComparePolicy const& )
    {}
};

template <typename Ring, typename ComparePolicy>
struct unique<ring_tag, Ring, ComparePolicy>
    : detail::unique::range_unique<Ring, ComparePolicy>
{};

template <typename LineString, typename ComparePolicy>
struct unique<linestring_tag, LineString, ComparePolicy>
    : detail::unique::range_unique<LineString, ComparePolicy>
{};

template <typename Polygon, typename ComparePolicy>
struct unique<polygon_tag, Polygon, ComparePolicy>
    : detail::unique::polygon_unique<Polygon, ComparePolicy>
{};

} // namespace dispatch
#endif

/*!
\brief \brief_calc{minimal set}
\ingroup unique
\details \details_calc{unique,minimal set (where duplicate consecutive points are removed)}.
\tparam Geometry \tparam_geometry
\param geometry \param_geometry which will be made unique

\qbk{[include reference/algorithms/unique.qbk]}
*/
template <typename Geometry>
inline void unique(Geometry& geometry)
{
    concept::check<Geometry>();

    // Default strategy is the default point-comparison policy
    typedef geometry::equal_to
        <
            typename geometry::point_type<Geometry>::type
        > policy;

    dispatch::unique
        <
            typename tag<Geometry>::type,
            Geometry,
            policy
        >::apply(geometry, policy());
}

}} // namespace boost::geometry

#endif // BOOST_GEOMETRY_ALGORITHMS_UNIQUE_HPP


//==============================================================================
//         Copyright 2003 - 2011   LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2011   LRI    UMR 8623 CNRS/Univ Paris Sud XI
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
#ifndef BOOST_SIMD_CONSTANT_CONSTANTS_THOUSAND_HPP_INCLUDED
#define BOOST_SIMD_CONSTANT_CONSTANTS_THOUSAND_HPP_INCLUDED

#include <boost/simd/include/functor.hpp>
#include <boost/simd/constant/register.hpp>
#include <boost/simd/constant/hierarchy.hpp>
#include <boost/config.hpp>

#ifdef BOOST_MSVC
#pragma warning(push)
#pragma warning(disable: 4310) // truncation of constant
#endif

namespace boost { namespace simd
{
  namespace tag
  {
   /*!
     @brief Thousand generic tag

     Represents the Thousand constant in generic contexts.

     @par Models:
        Hierarchy
   **/
    BOOST_SIMD_CONSTANT_REGISTER(Thousand,double,1000, 0x447a0000, 0x408f400000000000ll)
  }
  namespace ext
  {
   template<class Site>
   BOOST_FORCEINLINE generic_dispatcher<tag::Thousand, Site> dispatching_Thousand(adl_helper, boost::dispatch::meta::unknown_<Site>, ...)
   {
     return generic_dispatcher<tag::Thousand, Site>();
   }
   template<class... Args>
   struct impl_Thousand;
  }
  /*!
    Generates value 1000

    @par Semantic:

    @code
    T r = Thousand<T>();
    @endcode

    is similar to:

    @code
    T r = T(1000);
    @endcode
  **/
  BOOST_SIMD_CONSTANT_IMPLEMENTATION(boost::simd::tag::Thousand, Thousand)
} }

#ifdef BOOST_MSVC
#pragma warning(pop)
#endif

#include <boost/simd/constant/common.hpp>

#endif


/*
@copyright Louis Dionne 2014
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
 */

#include <boost/hana/lazy.hpp>

#include <array>
#include <cassert>
#include <iostream>
using namespace boost::hana;

int main() {
    // Make sure the monadic chain is evaluated in the right order.
    std::array<bool, 3> executed = {{false, false, false}};
    int dummy = 0;

    std::cerr << "creating the monadic chain...\n";
    auto chain = lazy(dummy)
        | [&](int dummy) {
            std::cerr << "executing the first computation...\n";
            executed[0] = true;
            assert((executed == std::array<bool, 3>{{true, false, false}}));
            return lazy(dummy);
        }
        | [&](int dummy) {
            std::cerr << "executing the second computation...\n";
            executed[1] = true;
            assert((executed == std::array<bool, 3>{{true, true, false}}));
            return lazy(dummy);
        }
        | [&](int dummy) {
            std::cerr << "executing the third computation...\n";
            executed[2] = true;
            assert((executed == std::array<bool, 3>{{true, true, true}}));
            return lazy(dummy);
        };

    assert((executed == std::array<bool, 3>{{false, false, false}}));

    std::cerr << "evaluating the chain...\n";
    eval(chain);
}


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

#ifndef BOOST_MATH_BN_MPFI_HPP
#define BOOST_MATH_BN_MPFI_HPP

#include <boost/multiprecision/number.hpp>
#include <boost/math/special_functions/fpclassify.hpp>
#include <boost/cstdint.hpp>
#include <boost/multiprecision/detail/big_lanczos.hpp>
#include <boost/multiprecision/detail/digits.hpp>
#include <boost/multiprecision/mpfr.hpp>
#include <boost/math/constants/constants.hpp>
#include <mpfi.h>
#include <cmath>
#include <algorithm>

namespace boost{
namespace multiprecision{
namespace backends{

template <unsigned digits10>
struct mpfi_float_backend;

} // namespace backends

template <unsigned digits10>
struct number_category<backends::mpfi_float_backend<digits10> > : public mpl::int_<number_kind_floating_point>{};

struct interval_error : public std::runtime_error
{
   interval_error(const std::string& s) : std::runtime_error(s) {}
};

namespace backends{

namespace detail{

inline int mpfi_sgn(mpfi_srcptr p)
{
   if(mpfi_is_zero(p))
      return 0;
   if(mpfi_is_strictly_pos(p))
      return 1;
   if(mpfi_is_strictly_neg(p))
      return -1;
   BOOST_THROW_EXCEPTION(interval_error("Sign of interval is ambiguous."));
}

template <unsigned digits10>
struct mpfi_float_imp;

template <unsigned digits10>
struct mpfi_float_imp
{
   typedef mpl::list<long, long long>                     signed_types;
   typedef mpl::list<unsigned long, unsigned long long>   unsigned_types;
   typedef mpl::list<double, long double>                 float_types;
   typedef long                                           exponent_type;

   mpfi_float_imp()
   {
      mpfi_init2(m_data, multiprecision::detail::digits10_2_2(digits10 ? digits10 : get_default_precision()));
   }
   mpfi_float_imp(unsigned prec)
   {
      mpfi_init2(m_data, prec);
   }

   mpfi_float_imp(const mpfi_float_imp& o)
   {
      mpfi_init2(m_data, multiprecision::detail::digits10_2_2(digits10 ? digits10 : get_default_precision()));
      if(o.m_data[0].left._mpfr_d)
         mpfi_set(m_data, o.m_data);
   }
#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
   mpfi_float_imp(mpfi_float_imp&& o) BOOST_NOEXCEPT
   {
      m_data[0] = o.m_data[0];
      o.m_data[0].left._mpfr_d = 0;
   }
#endif
   mpfi_float_imp& operator = (const mpfi_float_imp& o)
   {
      if(m_data[0].left._mpfr_d == 0)
         mpfi_init2(m_data, multiprecision::detail::digits10_2_2(digits10 ? digits10 : get_default_precision()));
      if(o.m_data[0].left._mpfr_d)
         mpfi_set(m_data, o.m_data);
      return *this;
   }
#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
   mpfi_float_imp& operator = (mpfi_float_imp&& o) BOOST_NOEXCEPT
   {
      mpfi_swap(m_data, o.m_data);
      return *this;
   }
#endif
#ifdef _MPFR_H_HAVE_INTMAX_T
   mpfi_float_imp& operator = (unsigned long long i)
   {
      if(m_data[0].left._mpfr_d == 0)
         mpfi_init2(m_data, multiprecision::detail::digits10_2_2(digits10 ? digits10 : get_default_precision()));
      mpfr_set_uj(left_data(), i, GMP_RNDD);
      mpfr_set_uj(right_data(), i, GMP_RNDU);
      return *this;
   }
   mpfi_float_imp& operator = (long long i)
   {
      if(m_data[0].left._mpfr_d == 0)
         mpfi_init2(m_data, multiprecision::detail::digits10_2_2(digits10 ? digits10 : get_default_precision()));
      mpfr_set_sj(left_data(), i, GMP_RNDD);
      mpfr_set_sj(right_data(), i, GMP_RNDU);
      return *this;
   }
#else
   mpfi_float_imp& operator = (unsigned long long i)
   {
      if(m_data[0].left._mpfr_d == 0)
         mpfi_init2(m_data, multiprecision::detail::digits10_2_2(digits10 ? digits10 : get_default_precision()));
      unsigned long long mask = ((1uLL << std::numeric_limits<unsigned>::digits) - 1);
      unsigned shift = 0;
      mpfi_t t;
      mpfi_init2(t, (std::max)(static_cast<unsigned>(std::numeric_limits<unsigned long long>::digits), static_cast<unsigned>(multiprecision::detail::digits10_2_2(digits10))));
      mpfi_set_ui(m_data, 0);
      while(i)
      {
         mpfi_set_ui(t, static_cast<unsigned>(i & mask));
         if(shift)
            mpfi_mul_2exp(t, t, shift);
         mpfi_add(m_data, m_data, t);
         shift += std::numeric_limits<unsigned>::digits;
         i >>= std::numeric_limits<unsigned>::digits;
      }
      mpfi_clear(t);
      return *this;
   }
   mpfi_float_imp& operator = (long long i)
   {
      if(m_data[0].left._mpfr_d == 0)
         mpfi_init2(m_data, multiprecision::detail::digits10_2_2(digits10 ? digits10 : get_default_precision()));
      bool neg = i < 0;
      *this = boost::multiprecision::detail::unsigned_abs(i);
      if(neg)
         mpfi_neg(m_data, m_data);
      return *this;
   }
#endif
   mpfi_float_imp& operator = (unsigned long i)
   {
      if(m_data[0].left._mpfr_d == 0)
         mpfi_init2(m_data, multiprecision::detail::digits10_2_2(digits10 ? digits10 : get_default_precision()));
      mpfi_set_ui(m_data, i);
      return *this;
   }
   mpfi_float_imp& operator = (long i)
   {
      if(m_data[0].left._mpfr_d == 0)
         mpfi_init2(m_data, multiprecision::detail::digits10_2_2(digits10 ? digits10 : get_default_precision()));
      mpfi_set_si(m_data, i);
      return *this;
   }
   mpfi_float_imp& operator = (double d)
   {
      if(m_data[0].left._mpfr_d == 0)
         mpfi_init2(m_data, multiprecision::detail::digits10_2_2(digits10 ? digits10 : get_default_precision()));
      mpfi_set_d(m_data, d);
      return *this;
   }
   mpfi_float_imp& operator = (long double a)
   {
      if(m_data[0].left._mpfr_d == 0)
         mpfi_init2(m_data, multiprecision::detail::digits10_2_2(digits10 ? digits10 : get_default_precision()));
      mpfr_set_ld(left_data(), a, GMP_RNDD);
      mpfr_set_ld(right_data(), a, GMP_RNDU);
      return *this;
   }
   mpfi_float_imp& operator = (const char* s)
   {
      using default_ops::eval_fpclassify;

      if(m_data[0].left._mpfr_d == 0)
         mpfi_init2(m_data, multiprecision::detail::digits10_2_2(digits10 ? digits10 : get_default_precision()));

      if(s && (*s == '{'))
      {
         mpfr_float_backend<digits10> a, b;
         std::string part;
         const char* p = ++s;
         while(*p && (*p != ',') && (*p != '}'))
            ++p;
         part.assign(s + 1, p);
         a = part.c_str();
         s = p;
         if(*p && (*p != '}'))
         {
            ++p;
            while(*p && (*p != ',') && (*p != '}'))
               ++p;
            part.assign(s + 1, p);
         }
         else
            part.erase();
         b = part.c_str();

         if(eval_fpclassify(a) == (int)FP_NAN)
         {
            mpfi_set_fr(this->data(), a.data());
         }
         else if(eval_fpclassify(b) == (int)FP_NAN)
         {
            mpfi_set_fr(this->data(), b.data());
         }
         else
         {
            if(a.compare(b) > 0)
            {
               BOOST_THROW_EXCEPTION(std::runtime_error("Attempt to create interval with invalid range (start is greater than end)."));
            }
            mpfi_interv_fr(m_data, a.data(), b.data());
         }
      }
      else if(mpfi_set_str(m_data, s, 10) != 0)
      {
         BOOST_THROW_EXCEPTION(std::runtime_error(std::string("Unable to parse string \"") + s + std::string("\"as a valid floating point number.")));
      }
      return *this;
   }
   void swap(mpfi_float_imp& o) BOOST_NOEXCEPT
   {
      mpfi_swap(m_data, o.m_data);
   }
   std::string str(std::streamsize digits, std::ios_base::fmtflags f)const
   {
      BOOST_ASSERT(m_data[0].left._mpfr_d);

      mpfr_float_backend<digits10> a, b;

      mpfi_get_left(a.data(), m_data);
      mpfi_get_right(b.data(), m_data);

      if(a.compare(b) == 0)
         return a.str(digits, f);

      return "{" + a.str(digits, f) + "," + b.str(digits, f) + "}";
   }
   ~mpfi_float_imp() BOOST_NOEXCEPT
   {
      if(m_data[0].left._mpfr_d)
         mpfi_clear(m_data);
   }
   void negate() BOOST_NOEXCEPT
   {
      BOOST_ASSERT(m_data[0].left._mpfr_d);
      mpfi_neg(m_data, m_data);
   }
   int compare(const mpfi_float_imp& o)const BOOST_NOEXCEPT
   {
      BOOST_ASSERT(m_data[0].left._mpfr_d && o.m_data[0].left._mpfr_d);
      if(mpfr_cmp(right_data(), o.left_data()) < 0)
         return -1;
      if(mpfr_cmp(left_data(), o.right_data()) > 0)
         return 1;
      if((mpfr_cmp(left_data(), o.left_data()) == 0) && (mpfr_cmp(right_data(), o.right_data()) == 0))
         return 0;
      BOOST_THROW_EXCEPTION(interval_error("Ambiguous comparison between two values."));
      return 0;
   }
   template <class V>
   int compare(V v)const BOOST_NOEXCEPT
   {
      mpfi_float_imp d;
      d = v;
      return compare(d);
   }
   mpfi_t& data() BOOST_NOEXCEPT
   {
      BOOST_ASSERT(m_data[0].left._mpfr_d);
      return m_data;
   }
   const mpfi_t& data()const BOOST_NOEXCEPT
   {
      BOOST_ASSERT(m_data[0].left._mpfr_d);
      return m_data;
   }
   mpfr_ptr left_data() BOOST_NOEXCEPT
   {
      BOOST_ASSERT(m_data[0].left._mpfr_d);
      return &(m_data[0].left);
   }
   mpfr_srcptr left_data()const BOOST_NOEXCEPT
   {
      BOOST_ASSERT(m_data[0].left._mpfr_d);
      return &(m_data[0].left);
   }
   mpfr_ptr right_data() BOOST_NOEXCEPT
   {
      BOOST_ASSERT(m_data[0].left._mpfr_d);
      return &(m_data[0].right);
   }
   mpfr_srcptr right_data()const BOOST_NOEXCEPT
   {
      BOOST_ASSERT(m_data[0].left._mpfr_d);
      return &(m_data[0].right);
   }
protected:
   mpfi_t m_data;
   static unsigned& get_default_precision() BOOST_NOEXCEPT
   {
      static unsigned val = 50;
      return val;
   }
};

} // namespace detail

template <unsigned digits10>
struct mpfi_float_backend : public detail::mpfi_float_imp<digits10>
{
   mpfi_float_backend() : detail::mpfi_float_imp<digits10>() {}
   mpfi_float_backend(const mpfi_float_backend& o) : detail::mpfi_float_imp<digits10>(o) {}
#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
   mpfi_float_backend(mpfi_float_backend&& o) : detail::mpfi_float_imp<digits10>(static_cast<detail::mpfi_float_imp<digits10>&&>(o)) {}
#endif
   template <unsigned D>
   mpfi_float_backend(const mpfi_float_backend<D>& val, typename enable_if_c<D <= digits10>::type* = 0)
       : detail::mpfi_float_imp<digits10>()
   {
      mpfi_set(this->m_data, val.data());
   }
   template <unsigned D>
   explicit mpfi_float_backend(const mpfi_float_backend<D>& val, typename disable_if_c<D <= digits10>::type* = 0)
       : detail::mpfi_float_imp<digits10>()
   {
      mpfi_set(this->m_data, val.data());
   }
   mpfi_float_backend(const mpfi_t val)
       : detail::mpfi_float_imp<digits10>()
   {
      mpfi_set(this->m_data, val);
   }
   mpfi_float_backend& operator=(const mpfi_float_backend& o)
   {
      *static_cast<detail::mpfi_float_imp<digits10>*>(this) = static_cast<detail::mpfi_float_imp<digits10> const&>(o);
      return *this;
   }
   template <unsigned D>
   mpfi_float_backend(const mpfr_float_backend<D>& val, typename enable_if_c<D <= digits10>::type* = 0)
       : detail::mpfi_float_imp<digits10>()
   {
      mpfi_set_fr(this->m_data, val.data());
   }
   template <unsigned D>
   mpfi_float_backend& operator=(const mpfr_float_backend<D>& val)
   {
      mpfi_set_fr(this->m_data, val.data());
      return *this;
   }
   template <unsigned D>
   explicit mpfi_float_backend(const mpfr_float_backend<D>& val, typename disable_if_c<D <= digits10>::type* = 0)
       : detail::mpfi_float_imp<digits10>()
   {
      mpfi_set_fr(this->m_data, val.data());
   }
#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
   mpfi_float_backend& operator=(mpfi_float_backend&& o) BOOST_NOEXCEPT
   {
      *static_cast<detail::mpfi_float_imp<digits10>*>(this) = static_cast<detail::mpfi_float_imp<digits10>&&>(o);
      return *this;
   }
#endif
   template <class V>
   mpfi_float_backend& operator=(const V& v)
   {
      *static_cast<detail::mpfi_float_imp<digits10>*>(this) = v;
      return *this;
   }
   mpfi_float_backend& operator=(const mpfi_t val)
   {
      mpfi_set(this->m_data, val);
      return *this;
   }
   // We don't change our precision here, this is a fixed precision type:
   template <unsigned D>
   mpfi_float_backend& operator=(const mpfi_float_backend<D>& val)
   {
      mpfi_set(this->m_data, val.data());
      return *this;
   }
};

template <>
struct mpfi_float_backend<0> : public detail::mpfi_float_imp<0>
{
   mpfi_float_backend() : detail::mpfi_float_imp<0>() {}
   mpfi_float_backend(const mpfi_t val)
      : detail::mpfi_float_imp<0>(mpfi_get_prec(val))
   {
      mpfi_set(this->m_data, val);
   }
   mpfi_float_backend(const mpfi_float_backend& o) : detail::mpfi_float_imp<0>(o) {}
#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
   mpfi_float_backend(mpfi_float_backend&& o) BOOST_NOEXCEPT : detail::mpfi_float_imp<0>(static_cast<detail::mpfi_float_imp<0>&&>(o)) {}
#endif
   mpfi_float_backend(const mpfi_float_backend& o, unsigned digits10)
      : detail::mpfi_float_imp<0>(digits10)
   {
      *this = o;
   }
   template <unsigned D>
   mpfi_float_backend(const mpfi_float_backend<D>& val)
      : detail::mpfi_float_imp<0>(mpfi_get_prec(val.data()))
   {
      mpfi_set(this->m_data, val.data());
   }
   mpfi_float_backend& operator=(const mpfi_float_backend& o)
   {
      mpfi_set_prec(this->m_data, mpfi_get_prec(o.data()));
      mpfi_set(this->m_data, o.data());
      return *this;
   }
#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
   mpfi_float_backend& operator=(mpfi_float_backend&& o) BOOST_NOEXCEPT
   {
      *static_cast<detail::mpfi_float_imp<0>*>(this) = static_cast<detail::mpfi_float_imp<0> &&>(o);
      return *this;
   }
#endif
   template <class V>
   mpfi_float_backend& operator=(const V& v)
   {
      *static_cast<detail::mpfi_float_imp<0>*>(this) = v;
      return *this;
   }
   mpfi_float_backend& operator=(const mpfi_t val)
   {
      mpfi_set_prec(this->m_data, mpfi_get_prec(val));
      mpfi_set(this->m_data, val);
      return *this;
   }
   template <unsigned D>
   mpfi_float_backend& operator=(const mpfi_float_backend<D>& val)
   {
      mpfi_set_prec(this->m_data, mpfi_get_prec(val.data()));
      mpfi_set(this->m_data, val.data());
      return *this;
   }
   static unsigned default_precision() BOOST_NOEXCEPT
   {
      return get_default_precision();
   }
   static void default_precision(unsigned v) BOOST_NOEXCEPT
   {
      get_default_precision() = v;
   }
   unsigned precision()const BOOST_NOEXCEPT
   {
      return multiprecision::detail::digits2_2_10(mpfi_get_prec(this->m_data));
   }
   void precision(unsigned digits10) BOOST_NOEXCEPT
   {
      mpfi_set_prec(this->m_data, multiprecision::detail::digits2_2_10((digits10)));
   }
};

template <unsigned digits10, class T>
inline typename enable_if<is_arithmetic<T>, bool>::type eval_eq(const mpfi_float_backend<digits10>& a, const T& b) BOOST_NOEXCEPT
{
   return a.compare(b) == 0;
}
template <unsigned digits10, class T>
inline typename enable_if<is_arithmetic<T>, bool>::type eval_lt(const mpfi_float_backend<digits10>& a, const T& b) BOOST_NOEXCEPT
{
   return a.compare(b) < 0;
}
template <unsigned digits10, class T>
inline typename enable_if<is_arithmetic<T>, bool>::type eval_gt(const mpfi_float_backend<digits10>& a, const T& b) BOOST_NOEXCEPT
{
   return a.compare(b) > 0;
}

template <unsigned D1, unsigned D2>
inline void eval_add(mpfi_float_backend<D1>& result, const mpfi_float_backend<D2>& o)
{
   mpfi_add(result.data(), result.data(), o.data());
}
template <unsigned D1, unsigned D2>
inline void eval_subtract(mpfi_float_backend<D1>& result, const mpfi_float_backend<D2>& o)
{
   mpfi_sub(result.data(), result.data(), o.data());
}
template <unsigned D1, unsigned D2>
inline void eval_multiply(mpfi_float_backend<D1>& result, const mpfi_float_backend<D2>& o)
{
   if((void*)&result == (void*)&o)
      mpfi_sqr(result.data(), o.data());
   else
      mpfi_mul(result.data(), result.data(), o.data());
}
template <unsigned D1, unsigned D2>
inline void eval_divide(mpfi_float_backend<D1>& result, const mpfi_float_backend<D2>& o)
{
   mpfi_div(result.data(), result.data(), o.data());
}
template <unsigned digits10>
inline void eval_add(mpfi_float_backend<digits10>& result, unsigned long i)
{
   mpfi_add_ui(result.data(), result.data(), i);
}
template <unsigned digits10>
inline void eval_subtract(mpfi_float_backend<digits10>& result, unsigned long i)
{
   mpfi_sub_ui(result.data(), result.data(), i);
}
template <unsigned digits10>
inline void eval_multiply(mpfi_float_backend<digits10>& result, unsigned long i)
{
   mpfi_mul_ui(result.data(), result.data(), i);
}
template <unsigned digits10>
inline void eval_divide(mpfi_float_backend<digits10>& result, unsigned long i)
{
   mpfi_div_ui(result.data(), result.data(), i);
}
template <unsigned digits10>
inline void eval_add(mpfi_float_backend<digits10>& result, long i)
{
   if(i > 0)
      mpfi_add_ui(result.data(), result.data(), i);
   else
      mpfi_sub_ui(result.data(), result.data(), boost::multiprecision::detail::unsigned_abs(i));
}
template <unsigned digits10>
inline void eval_subtract(mpfi_float_backend<digits10>& result, long i)
{
   if(i > 0)
      mpfi_sub_ui(result.data(), result.data(), i);
   else
      mpfi_add_ui(result.data(), result.data(), boost::multiprecision::detail::unsigned_abs(i));
}
template <unsigned digits10>
inline void eval_multiply(mpfi_float_backend<digits10>& result, long i)
{
   mpfi_mul_ui(result.data(), result.data(), boost::multiprecision::detail::unsigned_abs(i));
   if(i < 0)
      mpfi_neg(result.data(), result.data());
}
template <unsigned digits10>
inline void eval_divide(mpfi_float_backend<digits10>& result, long i)
{
   mpfi_div_ui(result.data(), result.data(), boost::multiprecision::detail::unsigned_abs(i));
   if(i < 0)
      mpfi_neg(result.data(), result.data());
}
//
// Specialised 3 arg versions of the basic operators:
//
template <unsigned D1, unsigned D2, unsigned D3>
inline void eval_add(mpfi_float_backend<D1>& a, const mpfi_float_backend<D2>& x, const mpfi_float_backend<D3>& y)
{
   mpfi_add(a.data(), x.data(), y.data());
}
template <unsigned D1, unsigned D2>
inline void eval_add(mpfi_float_backend<D1>& a, const mpfi_float_backend<D2>& x, unsigned long y)
{
   mpfi_add_ui(a.data(), x.data(), y);
}
template <unsigned D1, unsigned D2>
inline void eval_add(mpfi_float_backend<D1>& a, const mpfi_float_backend<D2>& x, long y)
{
   if(y < 0)
      mpfi_sub_ui(a.data(), x.data(), boost::multiprecision::detail::unsigned_abs(y));
   else
      mpfi_add_ui(a.data(), x.data(), y);
}
template <unsigned D1, unsigned D2>
inline void eval_add(mpfi_float_backend<D1>& a, unsigned long x, const mpfi_float_backend<D2>& y)
{
   mpfi_add_ui(a.data(), y.data(), x);
}
template <unsigned D1, unsigned D2>
inline void eval_add(mpfi_float_backend<D1>& a, long x, const mpfi_float_backend<D2>& y)
{
   if(x < 0)
   {
      mpfi_ui_sub(a.data(), boost::multiprecision::detail::unsigned_abs(x), y.data());
      mpfi_neg(a.data(), a.data());
   }
   else
      mpfi_add_ui(a.data(), y.data(), x);
}
template <unsigned D1, unsigned D2, unsigned D3>
inline void eval_subtract(mpfi_float_backend<D1>& a, const mpfi_float_backend<D2>& x, const mpfi_float_backend<D3>& y)
{
   mpfi_sub(a.data(), x.data(), y.data());
}
template <unsigned D1, unsigned D2>
inline void eval_subtract(mpfi_float_backend<D1>& a, const mpfi_float_backend<D2>& x, unsigned long y)
{
   mpfi_sub_ui(a.data(), x.data(), y);
}
template <unsigned D1, unsigned D2>
inline void eval_subtract(mpfi_float_backend<D1>& a, const mpfi_float_backend<D2>& x, long y)
{
   if(y < 0)
      mpfi_add_ui(a.data(), x.data(), boost::multiprecision::detail::unsigned_abs(y));
   else
      mpfi_sub_ui(a.data(), x.data(), y);
}
template <unsigned D1, unsigned D2>
inline void eval_subtract(mpfi_float_backend<D1>& a, unsigned long x, const mpfi_float_backend<D2>& y)
{
   mpfi_ui_sub(a.data(), x, y.data());
}
template <unsigned D1, unsigned D2>
inline void eval_subtract(mpfi_float_backend<D1>& a, long x, const mpfi_float_backend<D2>& y)
{
   if(x < 0)
   {
      mpfi_add_ui(a.data(), y.data(), boost::multiprecision::detail::unsigned_abs(x));
      mpfi_neg(a.data(), a.data());
   }
   else
      mpfi_ui_sub(a.data(), x, y.data());
}

template <unsigned D1, unsigned D2, unsigned D3>
inline void eval_multiply(mpfi_float_backend<D1>& a, const mpfi_float_backend<D2>& x, const mpfi_float_backend<D3>& y)
{
   if((void*)&x == (void*)&y)
      mpfi_sqr(a.data(), x.data());
   else
      mpfi_mul(a.data(), x.data(), y.data());
}
template <unsigned D1, unsigned D2>
inline void eval_multiply(mpfi_float_backend<D1>& a, const mpfi_float_backend<D2>& x, unsigned long y)
{
   mpfi_mul_ui(a.data(), x.data(), y);
}
template <unsigned D1, unsigned D2>
inline void eval_multiply(mpfi_float_backend<D1>& a, const mpfi_float_backend<D2>& x, long y)
{
   if(y < 0)
   {
      mpfi_mul_ui(a.data(), x.data(), boost::multiprecision::detail::unsigned_abs(y));
      a.negate();
   }
   else
      mpfi_mul_ui(a.data(), x.data(), y);
}
template <unsigned D1, unsigned D2>
inline void eval_multiply(mpfi_float_backend<D1>& a, unsigned long x, const mpfi_float_backend<D2>& y)
{
   mpfi_mul_ui(a.data(), y.data(), x);
}
template <unsigned D1, unsigned D2>
inline void eval_multiply(mpfi_float_backend<D1>& a, long x, const mpfi_float_backend<D2>& y)
{
   if(x < 0)
   {
      mpfi_mul_ui(a.data(), y.data(), boost::multiprecision::detail::unsigned_abs(x));
      mpfi_neg(a.data(), a.data());
   }
   else
      mpfi_mul_ui(a.data(), y.data(), x);
}

template <unsigned D1, unsigned D2, unsigned D3>
inline void eval_divide(mpfi_float_backend<D1>& a, const mpfi_float_backend<D2>& x, const mpfi_float_backend<D3>& y)
{
   mpfi_div(a.data(), x.data(), y.data());
}
template <unsigned D1, unsigned D2>
inline void eval_divide(mpfi_float_backend<D1>& a, const mpfi_float_backend<D2>& x, unsigned long y)
{
   mpfi_div_ui(a.data(), x.data(), y);
}
template <unsigned D1, unsigned D2>
inline void eval_divide(mpfi_float_backend<D1>& a, const mpfi_float_backend<D2>& x, long y)
{
   if(y < 0)
   {
      mpfi_div_ui(a.data(), x.data(), boost::multiprecision::detail::unsigned_abs(y));
      a.negate();
   }
   else
      mpfi_div_ui(a.data(), x.data(), y);
}
template <unsigned D1, unsigned D2>
inline void eval_divide(mpfi_float_backend<D1>& a, unsigned long x, const mpfi_float_backend<D2>& y)
{
   mpfi_ui_div(a.data(), x, y.data());
}
template <unsigned D1, unsigned D2>
inline void eval_divide(mpfi_float_backend<D1>& a, long x, const mpfi_float_backend<D2>& y)
{
   if(x < 0)
   {
      mpfi_ui_div(a.data(), boost::multiprecision::detail::unsigned_abs(x), y.data());
      mpfi_neg(a.data(), a.data());
   }
   else
      mpfi_ui_div(a.data(), x, y.data());
}

template <unsigned digits10>
inline bool eval_is_zero(const mpfi_float_backend<digits10>& val) BOOST_NOEXCEPT
{
   return 0 != mpfi_is_zero(val.data());
}
template <unsigned digits10>
inline int eval_get_sign(const mpfi_float_backend<digits10>& val)
{
   return detail::mpfi_sgn(val.data());
}

template <unsigned digits10>
inline void eval_convert_to(unsigned long* result, const mpfi_float_backend<digits10>& val)
{
   mpfr_float_backend<digits10> t;
   mpfi_mid(t.data(), val.data());
   eval_convert_to(result, t);
}
template <unsigned digits10>
inline void eval_convert_to(long* result, const mpfi_float_backend<digits10>& val)
{
   mpfr_float_backend<digits10> t;
   mpfi_mid(t.data(), val.data());
   eval_convert_to(result, t);
}
#ifdef _MPFR_H_HAVE_INTMAX_T
template <unsigned digits10>
inline void eval_convert_to(unsigned long long* result, const mpfi_float_backend<digits10>& val)
{
   mpfr_float_backend<digits10> t;
   mpfi_mid(t.data(), val.data());
   eval_convert_to(result, t);
}
template <unsigned digits10>
inline void eval_convert_to(long long* result, const mpfi_float_backend<digits10>& val)
{
   mpfr_float_backend<digits10> t;
   mpfi_mid(t.data(), val.data());
   eval_convert_to(result, t);
}
#endif
template <unsigned digits10>
inline void eval_convert_to(double* result, const mpfi_float_backend<digits10>& val) BOOST_NOEXCEPT
{
   *result = mpfi_get_d(val.data());
}
template <unsigned digits10>
inline void eval_convert_to(long double* result, const mpfi_float_backend<digits10>& val) BOOST_NOEXCEPT
{
   mpfr_float_backend<digits10> t;
   mpfi_mid(t.data(), val.data());
   eval_convert_to(result, t);
}

template <unsigned D1, unsigned D2, mpfr_allocation_type AllocationType>
inline void assign_components(mpfi_float_backend<D1>& result, const mpfr_float_backend<D2, AllocationType>& a, const mpfr_float_backend<D2, AllocationType>& b)
{
   using default_ops::eval_fpclassify;
   if(eval_fpclassify(a) == (int)FP_NAN)
   {
      mpfi_set_fr(result.data(), a.data());
   }
   else if(eval_fpclassify(b) == (int)FP_NAN)
   {
      mpfi_set_fr(result.data(), b.data());
   }
   else
   {
      if(a.compare(b) > 0)
      {
         BOOST_THROW_EXCEPTION(std::runtime_error("Attempt to create interval with invalid range (start is greater than end)."));
      }
      mpfi_interv_fr(result.data(), a.data(), b.data());
   }
}

template <unsigned Digits10, class V>
inline typename enable_if_c<is_convertible<V, number<mpfr_float_backend<Digits10, allocate_dynamic>, et_on> >::value >::type
   assign_components(mpfi_float_backend<Digits10>& result, const V& a, const V& b)
{
   number<mpfr_float_backend<Digits10, allocate_dynamic>, et_on> x(a), y(b);
   assign_components(result, x.backend(), y.backend());
}

//
// Native non-member operations:
//
template <unsigned Digits10>
inline void eval_sqrt(mpfi_float_backend<Digits10>& result, const mpfi_float_backend<Digits10>& val)
{
   mpfi_sqrt(result.data(), val.data());
}

template <unsigned Digits10>
inline void eval_abs(mpfi_float_backend<Digits10>& result, const mpfi_float_backend<Digits10>& val)
{
   mpfi_abs(result.data(), val.data());
}

template <unsigned Digits10>
inline void eval_fabs(mpfi_float_backend<Digits10>& result, const mpfi_float_backend<Digits10>& val)
{
   mpfi_abs(result.data(), val.data());
}
template <unsigned Digits10>
inline void eval_ceil(mpfi_float_backend<Digits10>& result, const mpfi_float_backend<Digits10>& val)
{
   mpfr_float_backend<Digits10> a, b;
   mpfr_set(a.data(), val.left_data(), GMP_RNDN);
   mpfr_set(b.data(), val.right_data(), GMP_RNDN);
   eval_ceil(a, a);
   eval_ceil(b, b);
   if(a.compare(b) != 0)
   {
      BOOST_THROW_EXCEPTION(interval_error("Attempt to take the ceil of a value that straddles an integer boundary."));
   }
   mpfi_set_fr(result.data(), a.data());
}
template <unsigned Digits10>
inline void eval_floor(mpfi_float_backend<Digits10>& result, const mpfi_float_backend<Digits10>& val)
{
   mpfr_float_backend<Digits10> a, b;
   mpfr_set(a.data(), val.left_data(), GMP_RNDN);
   mpfr_set(b.data(), val.right_data(), GMP_RNDN);
   eval_floor(a, a);
   eval_floor(b, b);
   if(a.compare(b) != 0)
   {
      BOOST_THROW_EXCEPTION(interval_error("Attempt to take the floor of a value that straddles an integer boundary."));
   }
   mpfi_set_fr(result.data(), a.data());
}
template <unsigned Digits10>
inline void eval_ldexp(mpfi_float_backend<Digits10>& result, const mpfi_float_backend<Digits10>& val, long e)
{
   if(e > 0)
      mpfi_mul_2exp(result.data(), val.data(), e);
   else if(e < 0)
      mpfi_div_2exp(result.data(), val.data(), -e);
   else
      result = val;
}
template <unsigned Digits10>
inline void eval_frexp(mpfi_float_backend<Digits10>& result, const mpfi_float_backend<Digits10>& val, int* e)
{
   mpfr_float_backend<Digits10> t, rt;
   mpfi_mid(t.data(), val.data());
   eval_frexp(rt, t, e);
   eval_ldexp(result, val, -*e);
}
template <unsigned Digits10>
inline void eval_frexp(mpfi_float_backend<Digits10>& result, const mpfi_float_backend<Digits10>& val, long* e)
{
   mpfr_float_backend<Digits10> t, rt;
   mpfi_mid(t.data(), val.data());
   eval_frexp(rt, t, e);
   eval_ldexp(result, val, -*e);
}

template <unsigned Digits10>
inline int eval_fpclassify(const mpfi_float_backend<Digits10>& val) BOOST_NOEXCEPT
{
   return mpfi_inf_p(val.data()) ? FP_INFINITE : mpfi_nan_p(val.data()) ? FP_NAN : mpfi_is_zero(val.data()) ? FP_ZERO : FP_NORMAL;
}

template <unsigned Digits10>
inline void eval_pow(mpfi_float_backend<Digits10>& result, const mpfi_float_backend<Digits10>& b, const mpfi_float_backend<Digits10>& e)
{
   typedef typename boost::multiprecision::detail::canonical<unsigned, mpfi_float_backend<Digits10> >::type ui_type;
   using default_ops::eval_get_sign;
   int s = eval_get_sign(b);
   if(s == 0)
   {
      if(eval_get_sign(e) == 0)
      {
         result = ui_type(1);
      }
      else
      {
         result = ui_type(0);
      }
      return;
   }
   if(s < 0)
   {
      if(eval_get_sign(e) < 0)
      {
         mpfi_float_backend<Digits10> t1, t2;
         t1 = e;
         t1.negate();
         eval_pow(t2, b, t1);
         t1 = ui_type(1);
         eval_divide(result, t1, t2);
         return;
      }
      typename boost::multiprecision::detail::canonical<boost::uintmax_t, mpfi_float_backend<Digits10> >::type an;
      try
      {
         using default_ops::eval_convert_to;
         eval_convert_to(&an, e);
         if(e.compare(an) == 0)
         {
            mpfi_float_backend<Digits10> pb(b);
            pb.negate();
            eval_pow(result, pb, e);
            if(an & 1u)
               result.negate();
            return;
         }
      }
      catch(const std::exception&)
      {
         // conversion failed, just fall through, value is not an integer.
      }
      result = std::numeric_limits<number<mpfi_float_backend<Digits10>, et_on> >::quiet_NaN().backend();
      return;
   }
   mpfi_log(result.data(), b.data());
   mpfi_mul(result.data(), result.data(), e.data());
   mpfi_exp(result.data(), result.data());
}

template <unsigned Digits10>
inline void eval_exp(mpfi_float_backend<Digits10>& result, const mpfi_float_backend<Digits10>& arg)
{
   mpfi_exp(result.data(), arg.data());
}

template <unsigned Digits10>
inline void eval_log(mpfi_float_backend<Digits10>& result, const mpfi_float_backend<Digits10>& arg)
{
   mpfi_log(result.data(), arg.data());
}

template <unsigned Digits10>
inline void eval_log10(mpfi_float_backend<Digits10>& result, const mpfi_float_backend<Digits10>& arg)
{
   mpfi_log10(result.data(), arg.data());
}

template <unsigned Digits10>
inline void eval_sin(mpfi_float_backend<Digits10>& result, const mpfi_float_backend<Digits10>& arg)
{
   mpfi_sin(result.data(), arg.data());
}

template <unsigned Digits10>
inline void eval_cos(mpfi_float_backend<Digits10>& result, const mpfi_float_backend<Digits10>& arg)
{
   mpfi_cos(result.data(), arg.data());
}

template <unsigned Digits10>
inline void eval_tan(mpfi_float_backend<Digits10>& result, const mpfi_float_backend<Digits10>& arg)
{
   mpfi_tan(result.data(), arg.data());
}

template <unsigned Digits10>
inline void eval_asin(mpfi_float_backend<Digits10>& result, const mpfi_float_backend<Digits10>& arg)
{
   mpfi_asin(result.data(), arg.data());
}

template <unsigned Digits10>
inline void eval_acos(mpfi_float_backend<Digits10>& result, const mpfi_float_backend<Digits10>& arg)
{
   mpfi_acos(result.data(), arg.data());
}

template <unsigned Digits10>
inline void eval_atan(mpfi_float_backend<Digits10>& result, const mpfi_float_backend<Digits10>& arg)
{
   mpfi_atan(result.data(), arg.data());
}

template <unsigned Digits10>
inline void eval_atan2(mpfi_float_backend<Digits10>& result, const mpfi_float_backend<Digits10>& arg1, const mpfi_float_backend<Digits10>& arg2)
{
   mpfi_atan2(result.data(), arg1.data(), arg2.data());
}

template <unsigned Digits10>
inline void eval_sinh(mpfi_float_backend<Digits10>& result, const mpfi_float_backend<Digits10>& arg)
{
   mpfi_sinh(result.data(), arg.data());
}

template <unsigned Digits10>
inline void eval_cosh(mpfi_float_backend<Digits10>& result, const mpfi_float_backend<Digits10>& arg)
{
   mpfi_cosh(result.data(), arg.data());
}

template <unsigned Digits10>
inline void eval_tanh(mpfi_float_backend<Digits10>& result, const mpfi_float_backend<Digits10>& arg)
{
   mpfi_tanh(result.data(), arg.data());
}

} // namespace backends

#ifdef BOOST_NO_SFINAE_EXPR

namespace detail{

template<unsigned D1, unsigned D2>
struct is_explicitly_convertible<backends::mpfi_float_backend<D1>, backends::mpfi_float_backend<D2> > : public mpl::true_ {};

}

#endif

template<>
struct number_category<detail::canonical<mpfi_t, backends::mpfi_float_backend<0> >::type> : public mpl::int_<number_kind_floating_point>{};
template <unsigned Digits10>
struct is_interval_number<backends::mpfi_float_backend<Digits10> > : public mpl::true_ {};

using boost::multiprecision::backends::mpfi_float_backend;

typedef number<mpfi_float_backend<50> >    mpfi_float_50;
typedef number<mpfi_float_backend<100> >   mpfi_float_100;
typedef number<mpfi_float_backend<500> >   mpfi_float_500;
typedef number<mpfi_float_backend<1000> >  mpfi_float_1000;
typedef number<mpfi_float_backend<0> >     mpfi_float;

//
// Special interval specific functions:
//
template <unsigned Digits10, expression_template_option ExpressionTemplates>
inline number<mpfr_float_backend<Digits10>, ExpressionTemplates> lower(const number<mpfi_float_backend<Digits10>, ExpressionTemplates>& val)
{
   number<mpfr_float_backend<Digits10> > result;
   mpfr_set(result.backend().data(), val.backend().left_data(), GMP_RNDN);
   return result;
}

template <unsigned Digits10, expression_template_option ExpressionTemplates>
inline number<mpfr_float_backend<Digits10>, ExpressionTemplates> upper(const number<mpfi_float_backend<Digits10>, ExpressionTemplates>& val)
{
   number<mpfr_float_backend<Digits10> > result;
   mpfr_set(result.backend().data(), val.backend().right_data(), GMP_RNDN);
   return result;
}

template <unsigned Digits10, expression_template_option ExpressionTemplates>
inline number<mpfr_float_backend<Digits10>, ExpressionTemplates> median(const number<mpfi_float_backend<Digits10>, ExpressionTemplates>& val)
{
   number<mpfr_float_backend<Digits10> > result;
   mpfi_mid(result.backend().data(), val.backend().data());
   return result;
}

template <unsigned Digits10, expression_template_option ExpressionTemplates>
inline number<mpfr_float_backend<Digits10>, ExpressionTemplates> width(const number<mpfi_float_backend<Digits10>, ExpressionTemplates>& val)
{
   number<mpfr_float_backend<Digits10> > result;
   mpfi_diam_abs(result.backend().data(), val.backend().data());
   return result;
}

template <unsigned Digits10, expression_template_option ExpressionTemplates>
inline number<mpfi_float_backend<Digits10>, ExpressionTemplates> intersect(const number<mpfi_float_backend<Digits10>, ExpressionTemplates>& a, const number<mpfi_float_backend<Digits10>, ExpressionTemplates>&  b)
{
   number<mpfi_float_backend<Digits10>, ExpressionTemplates> result;
   mpfi_intersect(result.backend().data(), a.backend().data(), b.backend().data());
   return result;
}

template <unsigned Digits10, expression_template_option ExpressionTemplates>
inline number<mpfi_float_backend<Digits10>, ExpressionTemplates> hull(const number<mpfi_float_backend<Digits10>, ExpressionTemplates>& a, const number<mpfi_float_backend<Digits10>, ExpressionTemplates>&  b)
{
   number<mpfi_float_backend<Digits10>, ExpressionTemplates> result;
   mpfi_union(result.backend().data(), a.backend().data(), b.backend().data());
   return result;
}

template <unsigned Digits10, expression_template_option ExpressionTemplates>
inline bool overlap(const number<mpfi_float_backend<Digits10>, ExpressionTemplates>& a, const number<mpfi_float_backend<Digits10>, ExpressionTemplates>&  b)
{
  return (lower(a) <= lower(b) && lower(b) <= upper(a)) ||
         (lower(b) <= lower(a) && lower(a) <= upper(b));
}

template <unsigned Digits10, expression_template_option ExpressionTemplates1, expression_template_option ExpressionTemplates2>
inline bool in(const number<mpfr_float_backend<Digits10>, ExpressionTemplates1>& a, const number<mpfi_float_backend<Digits10>, ExpressionTemplates2>&  b)
{
  return mpfi_is_inside_fr(a.backend().data(), b.backend().data()) != 0;
}

template <unsigned Digits10, expression_template_option ExpressionTemplates>
inline bool zero_in(const number<mpfi_float_backend<Digits10>, ExpressionTemplates>&  a)
{
  return mpfi_has_zero(a.backend().data()) != 0;
}

template <unsigned Digits10, expression_template_option ExpressionTemplates>
inline bool subset(const number<mpfi_float_backend<Digits10>, ExpressionTemplates>& a, const number<mpfi_float_backend<Digits10>, ExpressionTemplates>&  b)
{
  return mpfi_is_inside(a.backend().data(), b.backend().data()) != 0;
}

template <unsigned Digits10, expression_template_option ExpressionTemplates>
inline bool proper_subset(const number<mpfi_float_backend<Digits10>, ExpressionTemplates>& a, const number<mpfi_float_backend<Digits10>, ExpressionTemplates>&  b)
{
  return mpfi_is_strictly_inside(a.backend().data(), b.backend().data()) != 0;
}

template <unsigned Digits10, expression_template_option ExpressionTemplates>
inline bool empty(const number<mpfi_float_backend<Digits10>, ExpressionTemplates>&  a)
{
  return mpfi_is_empty(a.backend().data()) != 0;
}

template <unsigned Digits10, expression_template_option ExpressionTemplates>
inline bool singleton(const number<mpfi_float_backend<Digits10>, ExpressionTemplates>&  a)
{
  return mpfr_cmp(a.backend().left_data(), a.backend().right_data()) == 0;
}

template <unsigned Digits10, expression_template_option ExpressionTemplates>
struct component_type<number<mpfi_float_backend<Digits10>, ExpressionTemplates> >
{
   typedef number<mpfr_float_backend<Digits10>, ExpressionTemplates> type;
};

} // namespace multiprecision

namespace math{

namespace tools{

template <>
inline int digits<boost::multiprecision::mpfi_float>()
{
   return boost::multiprecision::backends::detail::get_default_precision();
}
template <>
inline int digits<boost::multiprecision::number<boost::multiprecision::mpfi_float_backend<0>, boost::multiprecision::et_off> >()
{
   return boost::multiprecision::backends::detail::get_default_precision();
}

} // namespace tools

namespace constants{ namespace detail{

template <class T> struct constant_pi;
template <class T> struct constant_ln_two;
template <class T> struct constant_euler;
template <class T> struct constant_catalan;

//
// Initializer: ensure all our constants are initialized prior to the first call of main:
//
template <class T>
struct mpfi_initializer
{
   struct init
   {
      init()
      {
         boost::math::constants::pi<T>();
         boost::math::constants::ln_two<T>();
         boost::math::constants::euler<T>();
         boost::math::constants::catalan<T>();
      }
      void force_instantiate()const{}
   };
   static const init initializer;
   static void force_instantiate()
   {
      initializer.force_instantiate();
   }
};

template <class T>
const typename mpfi_initializer<T>::init mpfi_initializer<T>::initializer;

template<unsigned Digits10, boost::multiprecision::expression_template_option ExpressionTemplates>
struct constant_pi<boost::multiprecision::number<boost::multiprecision::mpfi_float_backend<Digits10>, ExpressionTemplates> >
{
   typedef boost::multiprecision::number<boost::multiprecision::mpfi_float_backend<Digits10>, ExpressionTemplates> result_type;
   template<int N>
   static inline result_type const& get(const mpl::int_<N>&)
   {
      mpfi_initializer<result_type>::force_instantiate();
      static result_type result;
      static bool has_init = false;
      if(!has_init)
      {
         has_init = true;
         mpfi_const_pi(result.backend().data());
      }
      return result;
   }
};
template<unsigned Digits10, boost::multiprecision::expression_template_option ExpressionTemplates>
struct constant_ln_two<boost::multiprecision::number<boost::multiprecision::mpfi_float_backend<Digits10>, ExpressionTemplates> >
{
   typedef boost::multiprecision::number<boost::multiprecision::mpfi_float_backend<Digits10>, ExpressionTemplates> result_type;
   template<int N>
   static inline result_type const& get(const mpl::int_<N>&)
   {
      mpfi_initializer<result_type>::force_instantiate();
      static result_type result;
      static bool has_init = false;
      if(!has_init)
      {
         has_init = true;
         mpfi_const_log2(result.backend().data());
      }
      return result;
   }
};
template<unsigned Digits10, boost::multiprecision::expression_template_option ExpressionTemplates>
struct constant_euler<boost::multiprecision::number<boost::multiprecision::mpfi_float_backend<Digits10>, ExpressionTemplates> >
{
   typedef boost::multiprecision::number<boost::multiprecision::mpfi_float_backend<Digits10>, ExpressionTemplates> result_type;
   template<int N>
   static inline result_type const& get(const mpl::int_<N>&)
   {
      mpfi_initializer<result_type>::force_instantiate();
      static result_type result;
      static bool has_init = false;
      if(!has_init)
      {
         has_init = true;
         mpfi_const_euler(result.backend().data());
      }
      return result;
   }
};
template<unsigned Digits10, boost::multiprecision::expression_template_option ExpressionTemplates>
struct constant_catalan<boost::multiprecision::number<boost::multiprecision::mpfi_float_backend<Digits10>, ExpressionTemplates> >
{
   typedef boost::multiprecision::number<boost::multiprecision::mpfi_float_backend<Digits10>, ExpressionTemplates> result_type;
   template<int N>
   static inline result_type const& get(const mpl::int_<N>&)
   {
      mpfi_initializer<result_type>::force_instantiate();
      static result_type result;
      static bool has_init = false;
      if(!has_init)
      {
         has_init = true;
         mpfi_const_catalan(result.backend().data());
      }
      return result;
   }
};

}} // namespaces

}}  // namespaces

namespace std{

//
// numeric_limits [partial] specializations for the types declared in this header:
//
template<unsigned Digits10, boost::multiprecision::expression_template_option ExpressionTemplates>
class numeric_limits<boost::multiprecision::number<boost::multiprecision::mpfi_float_backend<Digits10>, ExpressionTemplates> >
{
   typedef boost::multiprecision::number<boost::multiprecision::mpfi_float_backend<Digits10>, ExpressionTemplates> number_type;
public:
   BOOST_STATIC_CONSTEXPR bool is_specialized = true;
   static number_type (min)()
   {
      initializer.do_nothing();
      static std::pair<bool, number_type> value;
      if(!value.first)
      {
         value.first = true;
         value.second = 0.5;
         mpfi_div_2exp(value.second.backend().data(), value.second.backend().data(), -mpfr_get_emin());
      }
      return value.second;
   }
   static number_type (max)()
   {
      initializer.do_nothing();
      static std::pair<bool, number_type> value;
      if(!value.first)
      {
         value.first = true;
         value.second = 0.5;
         mpfi_mul_2exp(value.second.backend().data(), value.second.backend().data(), mpfr_get_emax());
      }
      return value.second;
   }
   BOOST_STATIC_CONSTEXPR number_type lowest()
   {
      return -(max)();
   }
   BOOST_STATIC_CONSTEXPR int digits = static_cast<int>((Digits10 * 1000L) / 301L + ((Digits10 * 1000L) % 301 ? 2 : 1));
   BOOST_STATIC_CONSTEXPR int digits10 = Digits10;
   // Is this really correct???
   BOOST_STATIC_CONSTEXPR int max_digits10 = Digits10 + 2;
   BOOST_STATIC_CONSTEXPR bool is_signed = true;
   BOOST_STATIC_CONSTEXPR bool is_integer = false;
   BOOST_STATIC_CONSTEXPR bool is_exact = false;
   BOOST_STATIC_CONSTEXPR int radix = 2;
   static number_type epsilon()
   {
      initializer.do_nothing();
      static std::pair<bool, number_type> value;
      if(!value.first)
      {
         value.first = true;
         value.second = 1;
         mpfi_div_2exp(value.second.backend().data(), value.second.backend().data(), std::numeric_limits<number_type>::digits - 1);
      }
      return value.second;
   }
   // What value should this be????
   static number_type round_error()
   {
      // returns epsilon/2
      initializer.do_nothing();
      static std::pair<bool, number_type> value;
      if(!value.first)
      {
         value.first = true;
         value.second = 1;
         mpfi_div_2exp(value.second.backend().data(), value.second.backend().data(), 1);
      }
      return value.second;
   }
   BOOST_STATIC_CONSTEXPR long min_exponent = MPFR_EMIN_DEFAULT;
   BOOST_STATIC_CONSTEXPR long min_exponent10 = (MPFR_EMIN_DEFAULT / 1000) * 301L;
   BOOST_STATIC_CONSTEXPR long max_exponent = MPFR_EMAX_DEFAULT;
   BOOST_STATIC_CONSTEXPR long max_exponent10 = (MPFR_EMAX_DEFAULT / 1000) * 301L;
   BOOST_STATIC_CONSTEXPR bool has_infinity = true;
   BOOST_STATIC_CONSTEXPR bool has_quiet_NaN = true;
   BOOST_STATIC_CONSTEXPR bool has_signaling_NaN = false;
   BOOST_STATIC_CONSTEXPR float_denorm_style has_denorm = denorm_absent;
   BOOST_STATIC_CONSTEXPR bool has_denorm_loss = false;
   static number_type infinity()
   {
      initializer.do_nothing();
      static std::pair<bool, number_type> value;
      if(!value.first)
      {
         boost::multiprecision::mpfr_float_backend<Digits10> t;
         mpfr_set_inf(t.data(), 1);
         value.first = true;
         mpfi_set_fr(value.second.backend().data(), t.data());
      }
      return value.second;
   }
   static number_type quiet_NaN()
   {
      initializer.do_nothing();
      static std::pair<bool, number_type> value;
      if(!value.first)
      {
         boost::multiprecision::mpfr_float_backend<Digits10> t;
         mpfr_set_nan(t.data());
         value.first = true;
         mpfi_set_fr(value.second.backend().data(), t.data());
      }
      return value.second;
   }
   BOOST_STATIC_CONSTEXPR number_type signaling_NaN()
   {
      return number_type(0);
   }
   BOOST_STATIC_CONSTEXPR number_type denorm_min() { return number_type(0); }
   BOOST_STATIC_CONSTEXPR bool is_iec559 = false;
   BOOST_STATIC_CONSTEXPR bool is_bounded = true;
   BOOST_STATIC_CONSTEXPR bool is_modulo = false;
   BOOST_STATIC_CONSTEXPR bool traps = true;
   BOOST_STATIC_CONSTEXPR bool tinyness_before = false;
   BOOST_STATIC_CONSTEXPR float_round_style round_style = round_to_nearest;

private:
   struct data_initializer
   {
      data_initializer()
      {
         std::numeric_limits<boost::multiprecision::number<boost::multiprecision::mpfi_float_backend<digits10> > >::epsilon();
         std::numeric_limits<boost::multiprecision::number<boost::multiprecision::mpfi_float_backend<digits10> > >::round_error();
         (std::numeric_limits<boost::multiprecision::number<boost::multiprecision::mpfi_float_backend<digits10> > >::min)();
         (std::numeric_limits<boost::multiprecision::number<boost::multiprecision::mpfi_float_backend<digits10> > >::max)();
         std::numeric_limits<boost::multiprecision::number<boost::multiprecision::mpfi_float_backend<digits10> > >::infinity();
         std::numeric_limits<boost::multiprecision::number<boost::multiprecision::mpfi_float_backend<digits10> > >::quiet_NaN();
      }
      void do_nothing()const{}
   };
   static const data_initializer initializer;
};

template<unsigned Digits10, boost::multiprecision::expression_template_option ExpressionTemplates>
const typename numeric_limits<boost::multiprecision::number<boost::multiprecision::mpfi_float_backend<Digits10>, ExpressionTemplates> >::data_initializer numeric_limits<boost::multiprecision::number<boost::multiprecision::mpfi_float_backend<Digits10>, ExpressionTemplates> >::initializer;

#ifndef BOOST_NO_INCLASS_MEMBER_INITIALIZATION

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

#endif

template<boost::multiprecision::expression_template_option ExpressionTemplates>
class numeric_limits<boost::multiprecision::number<boost::multiprecision::mpfi_float_backend<0>, ExpressionTemplates> >
{
   typedef boost::multiprecision::number<boost::multiprecision::mpfi_float_backend<0>, ExpressionTemplates> number_type;
public:
   BOOST_STATIC_CONSTEXPR bool is_specialized = false;
   static number_type (min)() { return number_type(0); }
   static number_type (max)() { return number_type(0); }
   static number_type lowest() { return number_type(0); }
   BOOST_STATIC_CONSTEXPR int digits = 0;
   BOOST_STATIC_CONSTEXPR int digits10 = 0;
   BOOST_STATIC_CONSTEXPR int max_digits10 = 0;
   BOOST_STATIC_CONSTEXPR bool is_signed = false;
   BOOST_STATIC_CONSTEXPR bool is_integer = false;
   BOOST_STATIC_CONSTEXPR bool is_exact = false;
   BOOST_STATIC_CONSTEXPR int radix = 0;
   static number_type epsilon() { return number_type(0); }
   static number_type round_error() { return number_type(0); }
   BOOST_STATIC_CONSTEXPR int min_exponent = 0;
   BOOST_STATIC_CONSTEXPR int min_exponent10 = 0;
   BOOST_STATIC_CONSTEXPR int max_exponent = 0;
   BOOST_STATIC_CONSTEXPR int max_exponent10 = 0;
   BOOST_STATIC_CONSTEXPR bool has_infinity = false;
   BOOST_STATIC_CONSTEXPR bool has_quiet_NaN = false;
   BOOST_STATIC_CONSTEXPR bool has_signaling_NaN = false;
   BOOST_STATIC_CONSTEXPR float_denorm_style has_denorm = denorm_absent;
   BOOST_STATIC_CONSTEXPR bool has_denorm_loss = false;
   static number_type infinity() { return number_type(0); }
   static number_type quiet_NaN() { return number_type(0); }
   static number_type signaling_NaN() { return number_type(0); }
   static number_type denorm_min() { return number_type(0); }
   BOOST_STATIC_CONSTEXPR bool is_iec559 = false;
   BOOST_STATIC_CONSTEXPR bool is_bounded = false;
   BOOST_STATIC_CONSTEXPR bool is_modulo = false;
   BOOST_STATIC_CONSTEXPR bool traps = false;
   BOOST_STATIC_CONSTEXPR bool tinyness_before = false;
   BOOST_STATIC_CONSTEXPR float_round_style round_style = round_toward_zero;
};

#ifndef BOOST_NO_INCLASS_MEMBER_INITIALIZATION

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

#endif
} // namespace std
#endif