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 | |
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 | } |
205 | private: |
206 | rational_type m_value; |
207 | }; |
208 | |
209 | template <class IntBackend> |
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> |
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> |
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> |
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> |
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> |
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> |
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> |
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> |
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> |
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> |
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> |
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; |
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 | |
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 | |