1 ///////////////////////////////////////////////////////////////
2 //  Copyright 2011 John Maddock. Distributed under the Boost
3 //  Software License, Version 1.0. (See accompanying file
4 //  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_
5 
6 #ifndef BOOST_MATH_RATIONAL_ADAPTER_HPP
7 #define BOOST_MATH_RATIONAL_ADAPTER_HPP
8 
9 #include <iostream>
10 #include <iomanip>
11 #include <sstream>
12 #include <boost/cstdint.hpp>
13 #include <boost/functional/hash_fwd.hpp>
14 #include <boost/multiprecision/number.hpp>
15 #ifdef BOOST_MSVC
16 #  pragma warning(push)
17 #  pragma warning(disable:4512 4127)
18 #endif
19 #include <boost/rational.hpp>
20 #ifdef BOOST_MSVC
21 #  pragma warning(pop)
22 #endif
23 
24 namespace boost{
25 namespace multiprecision{
26 namespace backends{
27 
28 template <class IntBackend>
29 struct rational_adaptor
30 {
31    typedef number<IntBackend>                integer_type;
32    typedef boost::rational<integer_type>        rational_type;
33 
34    typedef typename IntBackend::signed_types    signed_types;
35    typedef typename IntBackend::unsigned_types  unsigned_types;
36    typedef typename IntBackend::float_types     float_types;
37 
BOOST_MP_NOEXCEPT_IFboost::multiprecision::backends::rational_adaptor38    rational_adaptor() BOOST_MP_NOEXCEPT_IF(noexcept(rational_type())) {}
BOOST_MP_NOEXCEPT_IFboost::multiprecision::backends::rational_adaptor39    rational_adaptor(const rational_adaptor& o) BOOST_MP_NOEXCEPT_IF(noexcept(std::declval<rational_type&>() = std::declval<const rational_type&>()))
40    {
41       m_value = o.m_value;
42    }
BOOST_MP_NOEXCEPT_IFboost::multiprecision::backends::rational_adaptor43    rational_adaptor(const IntBackend& o) BOOST_MP_NOEXCEPT_IF(noexcept(rational_type(std::declval<const IntBackend&>()))) : m_value(o) {}
44 
45    template <class U>
rational_adaptorboost::multiprecision::backends::rational_adaptor46    rational_adaptor(const U& u, typename enable_if_c<is_convertible<U, IntBackend>::value>::type* = 0)
47       : m_value(static_cast<integer_type>(u)){}
48    template <class U>
rational_adaptorboost::multiprecision::backends::rational_adaptor49    explicit rational_adaptor(const U& u,
50       typename enable_if_c<
51          boost::multiprecision::detail::is_explicitly_convertible<U, IntBackend>::value && !is_convertible<U, IntBackend>::value
52       >::type* = 0)
53       : m_value(IntBackend(u)){}
54    template <class U>
operator =boost::multiprecision::backends::rational_adaptor55    typename enable_if_c<(boost::multiprecision::detail::is_explicitly_convertible<U, IntBackend>::value && !is_arithmetic<U>::value), rational_adaptor&>::type operator = (const U& u)
56    {
57       m_value = IntBackend(u);
58       return *this;
59    }
60 
61 #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
BOOST_MP_NOEXCEPT_IFboost::multiprecision::backends::rational_adaptor62    rational_adaptor(rational_adaptor&& o) BOOST_MP_NOEXCEPT_IF(noexcept(rational_type(std::declval<rational_type>()))) : m_value(static_cast<rational_type&&>(o.m_value)) {}
BOOST_MP_NOEXCEPT_IFboost::multiprecision::backends::rational_adaptor63    rational_adaptor(IntBackend&& o) BOOST_MP_NOEXCEPT_IF(noexcept(rational_type(std::declval<IntBackend>()))) : m_value(static_cast<IntBackend&&>(o)) {}
operator =boost::multiprecision::backends::rational_adaptor64    rational_adaptor& operator = (rational_adaptor&& o) BOOST_MP_NOEXCEPT_IF(noexcept(std::declval<rational_type&>() = std::declval<rational_type>()))
65    {
66       m_value = static_cast<rational_type&&>(o.m_value);
67       return *this;
68    }
69 #endif
operator =boost::multiprecision::backends::rational_adaptor70    rational_adaptor& operator = (const rational_adaptor& o)
71    {
72       m_value = o.m_value;
73       return *this;
74    }
operator =boost::multiprecision::backends::rational_adaptor75    rational_adaptor& operator = (const IntBackend& o)
76    {
77       m_value = o;
78       return *this;
79    }
80    template <class Int>
operator =boost::multiprecision::backends::rational_adaptor81    typename enable_if<is_integral<Int>, rational_adaptor&>::type operator = (Int i)
82    {
83       m_value = i;
84       return *this;
85    }
86    template <class Float>
operator =boost::multiprecision::backends::rational_adaptor87    typename enable_if<is_floating_point<Float>, rational_adaptor&>::type operator = (Float i)
88    {
89       int e;
90       Float f = std::frexp(i, &e);
91       f = std::ldexp(f, std::numeric_limits<Float>::digits);
92       e -= std::numeric_limits<Float>::digits;
93       integer_type num(f);
94       integer_type denom(1u);
95       if(e > 0)
96       {
97          num <<= e;
98       }
99       else if(e < 0)
100       {
101          denom <<= -e;
102       }
103       m_value.assign(num, denom);
104       return *this;
105    }
operator =boost::multiprecision::backends::rational_adaptor106    rational_adaptor& operator = (const char* s)
107    {
108       std::string s1;
109       multiprecision::number<IntBackend> v1, v2;
110       char c;
111       bool have_hex = false;
112       const char* p = s; // saved for later
113 
114       while((0 != (c = *s)) && (c == 'x' || c == 'X' || c == '-' || c == '+' || (c >= '0' && c <= '9') || (have_hex && (c >= 'a' && c <= 'f')) || (have_hex && (c >= 'A' && c <= 'F'))))
115       {
116          if(c == 'x' || c == 'X')
117             have_hex = true;
118          s1.append(1, c);
119          ++s;
120       }
121       v1.assign(s1);
122       s1.erase();
123       if(c == '/')
124       {
125          ++s;
126          while((0 != (c = *s)) && (c == 'x' || c == 'X' || c == '-' || c == '+' || (c >= '0' && c <= '9') || (have_hex && (c >= 'a' && c <= 'f')) || (have_hex && (c >= 'A' && c <= 'F'))))
127          {
128             if(c == 'x' || c == 'X')
129                have_hex = true;
130             s1.append(1, c);
131             ++s;
132          }
133          v2.assign(s1);
134       }
135       else
136          v2 = 1;
137       if(*s)
138       {
139          BOOST_THROW_EXCEPTION(std::runtime_error(std::string("Could not parse the string \"") + p + std::string("\" as a valid rational number.")));
140       }
141       data().assign(v1, v2);
142       return *this;
143    }
swapboost::multiprecision::backends::rational_adaptor144    void swap(rational_adaptor& o)
145    {
146       std::swap(m_value, o.m_value);
147    }
strboost::multiprecision::backends::rational_adaptor148    std::string str(std::streamsize digits, std::ios_base::fmtflags f)const
149    {
150       //
151       // We format the string ourselves so we can match what GMP's mpq type does:
152       //
153       std::string result = data().numerator().str(digits, f);
154       if(data().denominator() != 1)
155       {
156          result.append(1, '/');
157          result.append(data().denominator().str(digits, f));
158       }
159       return result;
160    }
negateboost::multiprecision::backends::rational_adaptor161    void negate()
162    {
163       m_value = -m_value;
164    }
compareboost::multiprecision::backends::rational_adaptor165    int compare(const rational_adaptor& o)const
166    {
167       return m_value > o.m_value ? 1 : (m_value < o.m_value ? -1 : 0);
168    }
169    template <class Arithmatic>
compareboost::multiprecision::backends::rational_adaptor170    typename enable_if_c<is_arithmetic<Arithmatic>::value && !is_floating_point<Arithmatic>::value, int>::type compare(Arithmatic i)const
171    {
172       return m_value > i ? 1 : (m_value < i ? -1 : 0);
173    }
174    template <class Arithmatic>
compareboost::multiprecision::backends::rational_adaptor175    typename enable_if_c<is_floating_point<Arithmatic>::value, int>::type compare(Arithmatic i)const
176    {
177       rational_adaptor r;
178       r = i;
179       return this->compare(r);
180    }
databoost::multiprecision::backends::rational_adaptor181    rational_type& data() { return m_value; }
databoost::multiprecision::backends::rational_adaptor182    const rational_type& data()const { return m_value; }
183 
184    template <class Archive>
serializeboost::multiprecision::backends::rational_adaptor185    void serialize(Archive& ar, const mpl::true_&)
186    {
187       // Saving
188       integer_type n(m_value.numerator()), d(m_value.denominator());
189       ar & boost::serialization::make_nvp("numerator", n);
190       ar & boost::serialization::make_nvp("denominator", d);
191    }
192    template <class Archive>
serializeboost::multiprecision::backends::rational_adaptor193    void serialize(Archive& ar, const mpl::false_&)
194    {
195       // Loading
196       integer_type n, d;
197       ar & boost::serialization::make_nvp("numerator", n);
198       ar & boost::serialization::make_nvp("denominator", d);
199       m_value.assign(n, d);
200    }
201    template <class Archive>
serializeboost::multiprecision::backends::rational_adaptor202    void serialize(Archive& ar, const unsigned int /*version*/)
203    {
204       typedef typename Archive::is_saving tag;
205       serialize(ar, tag());
206    }
207 private:
208    rational_type m_value;
209 };
210 
211 template <class IntBackend>
eval_add(rational_adaptor<IntBackend> & result,const rational_adaptor<IntBackend> & o)212 inline void eval_add(rational_adaptor<IntBackend>& result, const rational_adaptor<IntBackend>& o)
213 {
214    result.data() += o.data();
215 }
216 template <class IntBackend>
eval_subtract(rational_adaptor<IntBackend> & result,const rational_adaptor<IntBackend> & o)217 inline void eval_subtract(rational_adaptor<IntBackend>& result, const rational_adaptor<IntBackend>& o)
218 {
219    result.data() -= o.data();
220 }
221 template <class IntBackend>
eval_multiply(rational_adaptor<IntBackend> & result,const rational_adaptor<IntBackend> & o)222 inline void eval_multiply(rational_adaptor<IntBackend>& result, const rational_adaptor<IntBackend>& o)
223 {
224    result.data() *= o.data();
225 }
226 template <class IntBackend>
eval_divide(rational_adaptor<IntBackend> & result,const rational_adaptor<IntBackend> & o)227 inline void eval_divide(rational_adaptor<IntBackend>& result, const rational_adaptor<IntBackend>& o)
228 {
229    using default_ops::eval_is_zero;
230    if(eval_is_zero(o))
231    {
232       BOOST_THROW_EXCEPTION(std::overflow_error("Divide by zero."));
233    }
234    result.data() /= o.data();
235 }
236 
237 template <class R, class IntBackend>
eval_convert_to(R * result,const rational_adaptor<IntBackend> & backend)238 inline typename enable_if_c<number_category<R>::value == number_kind_floating_point>::type eval_convert_to(R* result, const rational_adaptor<IntBackend>& backend)
239 {
240    //
241    // The generic conversion is as good as anything we can write here:
242    //
243    ::boost::multiprecision::detail::generic_convert_rational_to_float(*result, backend);
244 }
245 
246 template <class R, class IntBackend>
eval_convert_to(R * result,const rational_adaptor<IntBackend> & backend)247 inline typename enable_if_c<(number_category<R>::value != number_kind_integer) && (number_category<R>::value != number_kind_floating_point)>::type eval_convert_to(R* result, const rational_adaptor<IntBackend>& backend)
248 {
249    typedef typename component_type<number<rational_adaptor<IntBackend> > >::type comp_t;
250    comp_t num(backend.data().numerator());
251    comp_t denom(backend.data().denominator());
252    *result = num.template convert_to<R>();
253    *result /= denom.template convert_to<R>();
254 }
255 
256 template <class R, class IntBackend>
eval_convert_to(R * result,const rational_adaptor<IntBackend> & backend)257 inline typename enable_if_c<number_category<R>::value == number_kind_integer>::type eval_convert_to(R* result, const rational_adaptor<IntBackend>& backend)
258 {
259    typedef typename component_type<number<rational_adaptor<IntBackend> > >::type comp_t;
260    comp_t t = backend.data().numerator();
261    t /= backend.data().denominator();
262    *result = t.template convert_to<R>();
263 }
264 
265 template <class IntBackend>
eval_is_zero(const rational_adaptor<IntBackend> & val)266 inline bool eval_is_zero(const rational_adaptor<IntBackend>& val)
267 {
268    using default_ops::eval_is_zero;
269    return eval_is_zero(val.data().numerator().backend());
270 }
271 template <class IntBackend>
eval_get_sign(const rational_adaptor<IntBackend> & val)272 inline int eval_get_sign(const rational_adaptor<IntBackend>& val)
273 {
274    using default_ops::eval_get_sign;
275    return eval_get_sign(val.data().numerator().backend());
276 }
277 
278 template<class IntBackend, class V>
assign_components(rational_adaptor<IntBackend> & result,const V & v1,const V & v2)279 inline void assign_components(rational_adaptor<IntBackend>& result, const V& v1, const V& v2)
280 {
281    result.data().assign(v1, v2);
282 }
283 
284 template <class IntBackend>
hash_value(const rational_adaptor<IntBackend> & val)285 inline std::size_t hash_value(const rational_adaptor<IntBackend>& val)
286 {
287    std::size_t result = hash_value(val.data().numerator());
288    boost::hash_combine(result, val.data().denominator());
289    return result;
290 }
291 
292 
293 } // namespace backends
294 
295 template<class IntBackend>
296 struct expression_template_default<backends::rational_adaptor<IntBackend> > : public expression_template_default<IntBackend> {};
297 
298 template<class IntBackend>
299 struct number_category<backends::rational_adaptor<IntBackend> > : public mpl::int_<number_kind_rational>{};
300 
301 using boost::multiprecision::backends::rational_adaptor;
302 
303 template <class Backend, expression_template_option ExpressionTemplates>
304 struct component_type<number<backends::rational_adaptor<Backend>, ExpressionTemplates> >
305 {
306    typedef number<Backend, ExpressionTemplates> type;
307 };
308 
309 template <class IntBackend, expression_template_option ET>
numerator(const number<rational_adaptor<IntBackend>,ET> & val)310 inline number<IntBackend, ET> numerator(const number<rational_adaptor<IntBackend>, ET>& val)
311 {
312    return val.backend().data().numerator();
313 }
314 template <class IntBackend, expression_template_option ET>
denominator(const number<rational_adaptor<IntBackend>,ET> & val)315 inline number<IntBackend, ET> denominator(const number<rational_adaptor<IntBackend>, ET>& val)
316 {
317    return val.backend().data().denominator();
318 }
319 
320 #ifdef BOOST_NO_SFINAE_EXPR
321 
322 namespace detail{
323 
324 template<class U, class IntBackend>
325 struct is_explicitly_convertible<U, rational_adaptor<IntBackend> > : public is_explicitly_convertible<U, IntBackend> {};
326 
327 }
328 
329 #endif
330 
331 }} // namespaces
332 
333 
334 namespace std{
335 
336 template <class IntBackend, boost::multiprecision::expression_template_option ExpressionTemplates>
337 class numeric_limits<boost::multiprecision::number<boost::multiprecision::rational_adaptor<IntBackend>, ExpressionTemplates> > : public std::numeric_limits<boost::multiprecision::number<IntBackend, ExpressionTemplates> >
338 {
339    typedef std::numeric_limits<boost::multiprecision::number<IntBackend> > base_type;
340    typedef boost::multiprecision::number<boost::multiprecision::rational_adaptor<IntBackend> > number_type;
341 public:
342    BOOST_STATIC_CONSTEXPR bool is_integer = false;
343    BOOST_STATIC_CONSTEXPR bool is_exact = true;
number_type(min)344    BOOST_STATIC_CONSTEXPR number_type (min)() { return (base_type::min)(); }
number_type(max)345    BOOST_STATIC_CONSTEXPR number_type (max)() { return (base_type::max)(); }
lowest()346    BOOST_STATIC_CONSTEXPR number_type lowest() { return -(max)(); }
epsilon()347    BOOST_STATIC_CONSTEXPR number_type epsilon() { return base_type::epsilon(); }
round_error()348    BOOST_STATIC_CONSTEXPR number_type round_error() { return epsilon() / 2; }
infinity()349    BOOST_STATIC_CONSTEXPR number_type infinity() { return base_type::infinity(); }
quiet_NaN()350    BOOST_STATIC_CONSTEXPR number_type quiet_NaN() { return base_type::quiet_NaN(); }
signaling_NaN()351    BOOST_STATIC_CONSTEXPR number_type signaling_NaN() { return base_type::signaling_NaN(); }
denorm_min()352    BOOST_STATIC_CONSTEXPR number_type denorm_min() { return base_type::denorm_min(); }
353 };
354 
355 #ifndef BOOST_NO_INCLASS_MEMBER_INITIALIZATION
356 
357 template <class IntBackend, boost::multiprecision::expression_template_option ExpressionTemplates>
358 BOOST_CONSTEXPR_OR_CONST bool numeric_limits<boost::multiprecision::number<boost::multiprecision::rational_adaptor<IntBackend>, ExpressionTemplates> >::is_integer;
359 template <class IntBackend, boost::multiprecision::expression_template_option ExpressionTemplates>
360 BOOST_CONSTEXPR_OR_CONST bool numeric_limits<boost::multiprecision::number<boost::multiprecision::rational_adaptor<IntBackend>, ExpressionTemplates> >::is_exact;
361 
362 #endif
363 
364 
365 }
366 
367 #endif
368