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
23namespace boost{
24namespace multiprecision{
25namespace backends{
26
27template <class IntBackend>
28struct 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
37 rational_adaptor() BOOST_MP_NOEXCEPT_IF(noexcept(rational_type())) {}
38 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 }
42 rational_adaptor(const IntBackend& o) BOOST_MP_NOEXCEPT_IF(noexcept(rational_type(std::declval<const IntBackend&>()))) : m_value(o) {}
43
44 template <class U>
45 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>
48 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>
54 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
60 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)) {}
61 rational_adaptor(IntBackend&& o) BOOST_MP_NOEXCEPT_IF(noexcept(rational_type(std::declval<IntBackend>()))) : m_value(static_cast<IntBackend&&>(o)) {}
62 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
68 rational_adaptor& operator = (const rational_adaptor& o)
69 {
70 m_value = o.m_value;
71 return *this;
72 }
73 rational_adaptor& operator = (const IntBackend& o)
74 {
75 m_value = o;
76 return *this;
77 }
78 template <class Int>
79 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>
85 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 }
104 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(n: 1, c: 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(n: 1, c: 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 }
142 void swap(rational_adaptor& o)
143 {
144 std::swap(m_value, o.m_value);
145 }
146 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(n: 1, c: '/');
155 result.append(data().denominator().str(digits, f));
156 }
157 return result;
158 }
159 void negate()
160 {
161 m_value = -m_value;
162 }
163 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>
168 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>
173 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 }
179 rational_type& data() { return m_value; }
180 const rational_type& data()const { return m_value; }
181
182 template <class Archive>
183 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>
191 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>
200 void serialize(Archive& ar, const unsigned int /*version*/)
201 {
202 typedef typename Archive::is_saving tag;
203 serialize(ar, tag());
204 }
205private:
206 rational_type m_value;
207};
208
209template <class IntBackend>
210inline void eval_add(rational_adaptor<IntBackend>& result, const rational_adaptor<IntBackend>& o)
211{
212 result.data() += o.data();
213}
214template <class IntBackend>
215inline void eval_subtract(rational_adaptor<IntBackend>& result, const rational_adaptor<IntBackend>& o)
216{
217 result.data() -= o.data();
218}
219template <class IntBackend>
220inline void eval_multiply(rational_adaptor<IntBackend>& result, const rational_adaptor<IntBackend>& o)
221{
222 result.data() *= o.data();
223}
224template <class IntBackend>
225inline 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
235template <class R, class IntBackend>
236inline 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
244template <class R, class IntBackend>
245inline 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
254template <class R, class IntBackend>
255inline 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
263template <class IntBackend>
264inline bool eval_is_zero(const rational_adaptor<IntBackend>& val)
265{
266 return eval_is_zero(val.data().numerator().backend());
267}
268template <class IntBackend>
269inline int eval_get_sign(const rational_adaptor<IntBackend>& val)
270{
271 return eval_get_sign(val.data().numerator().backend());
272}
273
274template<class IntBackend, class V>
275inline 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
282template<class IntBackend>
283struct expression_template_default<backends::rational_adaptor<IntBackend> > : public expression_template_default<IntBackend> {};
284
285template<class IntBackend>
286struct number_category<backends::rational_adaptor<IntBackend> > : public mpl::int_<number_kind_rational>{};
287
288using boost::multiprecision::backends::rational_adaptor;
289
290template <class T>
291struct component_type<rational_adaptor<T> >
292{
293 typedef number<T> type;
294};
295
296template <class IntBackend, expression_template_option ET>
297inline number<IntBackend, ET> numerator(const number<rational_adaptor<IntBackend>, ET>& val)
298{
299 return val.backend().data().numerator();
300}
301template <class IntBackend, expression_template_option ET>
302inline 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
309namespace detail{
310
311template<class U, class IntBackend>
312struct is_explicitly_convertible<U, rational_adaptor<IntBackend> > : public is_explicitly_convertible<U, IntBackend> {};
313
314}
315
316#endif
317
318}} // namespaces
319
320
321namespace std{
322
323template <class IntBackend, boost::multiprecision::expression_template_option ExpressionTemplates>
324class 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;
328public:
329 BOOST_STATIC_CONSTEXPR bool is_integer = false;
330 BOOST_STATIC_CONSTEXPR bool is_exact = true;
331 BOOST_STATIC_CONSTEXPR number_type (min)() { return (base_type::min)(); }
332 BOOST_STATIC_CONSTEXPR number_type (max)() { return (base_type::max)(); }
333 BOOST_STATIC_CONSTEXPR number_type lowest() { return -(max)(); }
334 BOOST_STATIC_CONSTEXPR number_type epsilon() { return base_type::epsilon(); }
335 BOOST_STATIC_CONSTEXPR number_type round_error() { return epsilon() / 2; }
336 BOOST_STATIC_CONSTEXPR number_type infinity() { return base_type::infinity(); }
337 BOOST_STATIC_CONSTEXPR number_type quiet_NaN() { return base_type::quiet_NaN(); }
338 BOOST_STATIC_CONSTEXPR number_type signaling_NaN() { return base_type::signaling_NaN(); }
339 BOOST_STATIC_CONSTEXPR number_type denorm_min() { return base_type::denorm_min(); }
340};
341
342#ifndef BOOST_NO_INCLASS_MEMBER_INITIALIZATION
343
344template <class IntBackend, boost::multiprecision::expression_template_option ExpressionTemplates>
345BOOST_CONSTEXPR_OR_CONST bool numeric_limits<boost::multiprecision::number<boost::multiprecision::rational_adaptor<IntBackend>, ExpressionTemplates> >::is_integer;
346template <class IntBackend, boost::multiprecision::expression_template_option ExpressionTemplates>
347BOOST_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

source code of boost/boost/multiprecision/rational_adaptor.hpp