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/multiprecision/number.hpp>
14 #ifdef BOOST_MSVC
15 #  pragma warning(push)
16 #  pragma warning(disable:4512 4127)
17 #endif
18 #include <boost/rational.hpp>
19 #ifdef BOOST_MSVC
20 #  pragma warning(pop)
21 #endif
22 
23 namespace boost{
24 namespace multiprecision{
25 namespace backends{
26 
27 template <class IntBackend>
28 struct rational_adaptor
29 {
30    typedef number<IntBackend>                integer_type;
31    typedef boost::rational<integer_type>        rational_type;
32 
33    typedef typename IntBackend::signed_types    signed_types;
34    typedef typename IntBackend::unsigned_types  unsigned_types;
35    typedef typename IntBackend::float_types     float_types;
36 
BOOST_MP_NOEXCEPT_IFboost::multiprecision::backends::rational_adaptor37    rational_adaptor() BOOST_MP_NOEXCEPT_IF(noexcept(rational_type())) {}
BOOST_MP_NOEXCEPT_IFboost::multiprecision::backends::rational_adaptor38    rational_adaptor(const rational_adaptor& o) BOOST_MP_NOEXCEPT_IF(noexcept(std::declval<rational_type&>() = std::declval<const rational_type&>()))
39    {
40       m_value = o.m_value;
41    }
BOOST_MP_NOEXCEPT_IFboost::multiprecision::backends::rational_adaptor42    rational_adaptor(const IntBackend& o) BOOST_MP_NOEXCEPT_IF(noexcept(rational_type(std::declval<const IntBackend&>()))) : m_value(o) {}
43 
44    template <class U>
rational_adaptorboost::multiprecision::backends::rational_adaptor45    rational_adaptor(const U& u, typename enable_if_c<is_convertible<U, IntBackend>::value>::type* = 0)
46       : m_value(static_cast<integer_type>(u)){}
47    template <class U>
rational_adaptorboost::multiprecision::backends::rational_adaptor48    explicit rational_adaptor(const U& u,
49       typename enable_if_c<
50          boost::multiprecision::detail::is_explicitly_convertible<U, IntBackend>::value && !is_convertible<U, IntBackend>::value
51       >::type* = 0)
52       : m_value(IntBackend(u)){}
53    template <class U>
operator =boost::multiprecision::backends::rational_adaptor54    typename enable_if_c<(boost::multiprecision::detail::is_explicitly_convertible<U, IntBackend>::value && !is_arithmetic<U>::value), rational_adaptor&>::type operator = (const U& u)
55    {
56       m_value = IntBackend(u);
57    }
58 
59 #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
BOOST_MP_NOEXCEPT_IFboost::multiprecision::backends::rational_adaptor60    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_adaptor61    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_adaptor62    rational_adaptor& operator = (rational_adaptor&& o) BOOST_MP_NOEXCEPT_IF(noexcept(std::declval<rational_type&>() = std::declval<rational_type>()))
63    {
64       m_value = static_cast<rational_type&&>(o.m_value);
65       return *this;
66    }
67 #endif
operator =boost::multiprecision::backends::rational_adaptor68    rational_adaptor& operator = (const rational_adaptor& o)
69    {
70       m_value = o.m_value;
71       return *this;
72    }
operator =boost::multiprecision::backends::rational_adaptor73    rational_adaptor& operator = (const IntBackend& o)
74    {
75       m_value = o;
76       return *this;
77    }
78    template <class Int>
operator =boost::multiprecision::backends::rational_adaptor79    typename enable_if<is_integral<Int>, rational_adaptor&>::type operator = (Int i)
80    {
81       m_value = i;
82       return *this;
83    }
84    template <class Float>
operator =boost::multiprecision::backends::rational_adaptor85    typename enable_if<is_floating_point<Float>, rational_adaptor&>::type operator = (Float i)
86    {
87       int e;
88       Float f = std::frexp(i, &e);
89       f = std::ldexp(f, std::numeric_limits<Float>::digits);
90       e -= std::numeric_limits<Float>::digits;
91       integer_type num(f);
92       integer_type denom(1u);
93       if(e > 0)
94       {
95          num <<= e;
96       }
97       else if(e < 0)
98       {
99          denom <<= -e;
100       }
101       m_value.assign(num, denom);
102       return *this;
103    }
operator =boost::multiprecision::backends::rational_adaptor104    rational_adaptor& operator = (const char* s)
105    {
106       std::string s1;
107       multiprecision::number<IntBackend> v1, v2;
108       char c;
109       bool have_hex = false;
110       const char* p = s; // saved for later
111 
112       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'))))
113       {
114          if(c == 'x' || c == 'X')
115             have_hex = true;
116          s1.append(1, c);
117          ++s;
118       }
119       v1.assign(s1);
120       s1.erase();
121       if(c == '/')
122       {
123          ++s;
124          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'))))
125          {
126             if(c == 'x' || c == 'X')
127                have_hex = true;
128             s1.append(1, c);
129             ++s;
130          }
131          v2.assign(s1);
132       }
133       else
134          v2 = 1;
135       if(*s)
136       {
137          BOOST_THROW_EXCEPTION(std::runtime_error(std::string("Could not parse the string \"") + p + std::string("\" as a valid rational number.")));
138       }
139       data().assign(v1, v2);
140       return *this;
141    }
swapboost::multiprecision::backends::rational_adaptor142    void swap(rational_adaptor& o)
143    {
144       std::swap(m_value, o.m_value);
145    }
strboost::multiprecision::backends::rational_adaptor146    std::string str(std::streamsize digits, std::ios_base::fmtflags f)const
147    {
148       //
149       // We format the string ourselves so we can match what GMP's mpq type does:
150       //
151       std::string result = data().numerator().str(digits, f);
152       if(data().denominator() != 1)
153       {
154          result.append(1, '/');
155          result.append(data().denominator().str(digits, f));
156       }
157       return result;
158    }
negateboost::multiprecision::backends::rational_adaptor159    void negate()
160    {
161       m_value = -m_value;
162    }
compareboost::multiprecision::backends::rational_adaptor163    int compare(const rational_adaptor& o)const
164    {
165       return m_value > o.m_value ? 1 : (m_value < o.m_value ? -1 : 0);
166    }
167    template <class Arithmatic>
compareboost::multiprecision::backends::rational_adaptor168    typename enable_if_c<is_arithmetic<Arithmatic>::value && !is_floating_point<Arithmatic>::value, int>::type compare(Arithmatic i)const
169    {
170       return m_value > i ? 1 : (m_value < i ? -1 : 0);
171    }
172    template <class Arithmatic>
compareboost::multiprecision::backends::rational_adaptor173    typename enable_if_c<is_floating_point<Arithmatic>::value, int>::type compare(Arithmatic i)const
174    {
175       rational_adaptor r;
176       r = i;
177       return this->compare(r);
178    }
databoost::multiprecision::backends::rational_adaptor179    rational_type& data() { return m_value; }
databoost::multiprecision::backends::rational_adaptor180    const rational_type& data()const { return m_value; }
181 
182    template <class Archive>
serializeboost::multiprecision::backends::rational_adaptor183    void serialize(Archive& ar, const mpl::true_&)
184    {
185       // Saving
186       integer_type n(m_value.numerator()), d(m_value.denominator());
187       ar & n;
188       ar & d;
189    }
190    template <class Archive>
serializeboost::multiprecision::backends::rational_adaptor191    void serialize(Archive& ar, const mpl::false_&)
192    {
193       // Loading
194       integer_type n, d;
195       ar & n;
196       ar & d;
197       m_value.assign(n, d);
198    }
199    template <class Archive>
serializeboost::multiprecision::backends::rational_adaptor200    void serialize(Archive& ar, const unsigned int /*version*/)
201    {
202       typedef typename Archive::is_saving tag;
203       serialize(ar, tag());
204    }
205 private:
206    rational_type m_value;
207 };
208 
209 template <class IntBackend>
eval_add(rational_adaptor<IntBackend> & result,const rational_adaptor<IntBackend> & o)210 inline void eval_add(rational_adaptor<IntBackend>& result, const rational_adaptor<IntBackend>& o)
211 {
212    result.data() += o.data();
213 }
214 template <class IntBackend>
eval_subtract(rational_adaptor<IntBackend> & result,const rational_adaptor<IntBackend> & o)215 inline void eval_subtract(rational_adaptor<IntBackend>& result, const rational_adaptor<IntBackend>& o)
216 {
217    result.data() -= o.data();
218 }
219 template <class IntBackend>
eval_multiply(rational_adaptor<IntBackend> & result,const rational_adaptor<IntBackend> & o)220 inline void eval_multiply(rational_adaptor<IntBackend>& result, const rational_adaptor<IntBackend>& o)
221 {
222    result.data() *= o.data();
223 }
224 template <class IntBackend>
eval_divide(rational_adaptor<IntBackend> & result,const rational_adaptor<IntBackend> & o)225 inline void eval_divide(rational_adaptor<IntBackend>& result, const rational_adaptor<IntBackend>& o)
226 {
227    using default_ops::eval_is_zero;
228    if(eval_is_zero(o))
229    {
230       BOOST_THROW_EXCEPTION(std::overflow_error("Divide by zero."));
231    }
232    result.data() /= o.data();
233 }
234 
235 template <class R, class IntBackend>
eval_convert_to(R * result,const rational_adaptor<IntBackend> & backend)236 inline typename enable_if_c<number_category<R>::value == number_kind_floating_point>::type eval_convert_to(R* result, const rational_adaptor<IntBackend>& backend)
237 {
238    //
239    // The generic conversion is as good as anything we can write here:
240    //
241    ::boost::multiprecision::detail::generic_convert_rational_to_float(*result, backend);
242 }
243 
244 template <class R, class IntBackend>
eval_convert_to(R * result,const rational_adaptor<IntBackend> & backend)245 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)
246 {
247    typedef typename component_type<number<rational_adaptor<IntBackend> > >::type comp_t;
248    comp_t num(backend.data().numerator());
249    comp_t denom(backend.data().denominator());
250    *result = num.template convert_to<R>();
251    *result /= denom.template convert_to<R>();
252 }
253 
254 template <class R, class IntBackend>
eval_convert_to(R * result,const rational_adaptor<IntBackend> & backend)255 inline typename enable_if_c<number_category<R>::value == number_kind_integer>::type eval_convert_to(R* result, const rational_adaptor<IntBackend>& backend)
256 {
257    typedef typename component_type<number<rational_adaptor<IntBackend> > >::type comp_t;
258    comp_t t = backend.data().numerator();
259    t /= backend.data().denominator();
260    *result = t.template convert_to<R>();
261 }
262 
263 template <class IntBackend>
eval_is_zero(const rational_adaptor<IntBackend> & val)264 inline bool eval_is_zero(const rational_adaptor<IntBackend>& val)
265 {
266    return eval_is_zero(val.data().numerator().backend());
267 }
268 template <class IntBackend>
eval_get_sign(const rational_adaptor<IntBackend> & val)269 inline int eval_get_sign(const rational_adaptor<IntBackend>& val)
270 {
271    return eval_get_sign(val.data().numerator().backend());
272 }
273 
274 template<class IntBackend, class V>
assign_components(rational_adaptor<IntBackend> & result,const V & v1,const V & v2)275 inline void assign_components(rational_adaptor<IntBackend>& result, const V& v1, const V& v2)
276 {
277    result.data().assign(v1, v2);
278 }
279 
280 } // namespace backends
281 
282 template<class IntBackend>
283 struct expression_template_default<backends::rational_adaptor<IntBackend> > : public expression_template_default<IntBackend> {};
284 
285 template<class IntBackend>
286 struct number_category<backends::rational_adaptor<IntBackend> > : public mpl::int_<number_kind_rational>{};
287 
288 using boost::multiprecision::backends::rational_adaptor;
289 
290 template <class T>
291 struct component_type<rational_adaptor<T> >
292 {
293    typedef number<T> type;
294 };
295 
296 template <class IntBackend, expression_template_option ET>
numerator(const number<rational_adaptor<IntBackend>,ET> & val)297 inline number<IntBackend, ET> numerator(const number<rational_adaptor<IntBackend>, ET>& val)
298 {
299    return val.backend().data().numerator();
300 }
301 template <class IntBackend, expression_template_option ET>
denominator(const number<rational_adaptor<IntBackend>,ET> & val)302 inline number<IntBackend, ET> denominator(const number<rational_adaptor<IntBackend>, ET>& val)
303 {
304    return val.backend().data().denominator();
305 }
306 
307 #ifdef BOOST_NO_SFINAE_EXPR
308 
309 namespace detail{
310 
311 template<class U, class IntBackend>
312 struct is_explicitly_convertible<U, rational_adaptor<IntBackend> > : public is_explicitly_convertible<U, IntBackend> {};
313 
314 }
315 
316 #endif
317 
318 }} // namespaces
319 
320 
321 namespace std{
322 
323 template <class IntBackend, boost::multiprecision::expression_template_option ExpressionTemplates>
324 class numeric_limits<boost::multiprecision::number<boost::multiprecision::rational_adaptor<IntBackend>, ExpressionTemplates> > : public std::numeric_limits<boost::multiprecision::number<IntBackend, ExpressionTemplates> >
325 {
326    typedef std::numeric_limits<boost::multiprecision::number<IntBackend> > base_type;
327    typedef boost::multiprecision::number<boost::multiprecision::rational_adaptor<IntBackend> > number_type;
328 public:
329    BOOST_STATIC_CONSTEXPR bool is_integer = false;
330    BOOST_STATIC_CONSTEXPR bool is_exact = true;
number_type(min)331    BOOST_STATIC_CONSTEXPR number_type (min)() { return (base_type::min)(); }
number_type(max)332    BOOST_STATIC_CONSTEXPR number_type (max)() { return (base_type::max)(); }
lowest()333    BOOST_STATIC_CONSTEXPR number_type lowest() { return -(max)(); }
epsilon()334    BOOST_STATIC_CONSTEXPR number_type epsilon() { return base_type::epsilon(); }
round_error()335    BOOST_STATIC_CONSTEXPR number_type round_error() { return epsilon() / 2; }
infinity()336    BOOST_STATIC_CONSTEXPR number_type infinity() { return base_type::infinity(); }
quiet_NaN()337    BOOST_STATIC_CONSTEXPR number_type quiet_NaN() { return base_type::quiet_NaN(); }
signaling_NaN()338    BOOST_STATIC_CONSTEXPR number_type signaling_NaN() { return base_type::signaling_NaN(); }
denorm_min()339    BOOST_STATIC_CONSTEXPR number_type denorm_min() { return base_type::denorm_min(); }
340 };
341 
342 #ifndef BOOST_NO_INCLASS_MEMBER_INITIALIZATION
343 
344 template <class IntBackend, boost::multiprecision::expression_template_option ExpressionTemplates>
345 BOOST_CONSTEXPR_OR_CONST bool numeric_limits<boost::multiprecision::number<boost::multiprecision::rational_adaptor<IntBackend>, ExpressionTemplates> >::is_integer;
346 template <class IntBackend, boost::multiprecision::expression_template_option ExpressionTemplates>
347 BOOST_CONSTEXPR_OR_CONST bool numeric_limits<boost::multiprecision::number<boost::multiprecision::rational_adaptor<IntBackend>, ExpressionTemplates> >::is_exact;
348 
349 #endif
350 
351 
352 }
353 
354 #endif
355