]>
Commit | Line | Data |
---|---|---|
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 | ||
38 | rational_adaptor() BOOST_MP_NOEXCEPT_IF(noexcept(rational_type())) {} | |
39 | 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 | } | |
43 | rational_adaptor(const IntBackend& o) BOOST_MP_NOEXCEPT_IF(noexcept(rational_type(std::declval<const IntBackend&>()))) : m_value(o) {} | |
44 | ||
45 | template <class U> | |
46 | 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> | |
49 | 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> | |
55 | 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 | |
62 | 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)) {} | |
63 | rational_adaptor(IntBackend&& o) BOOST_MP_NOEXCEPT_IF(noexcept(rational_type(std::declval<IntBackend>()))) : m_value(static_cast<IntBackend&&>(o)) {} | |
64 | 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 | |
70 | rational_adaptor& operator = (const rational_adaptor& o) | |
71 | { | |
72 | m_value = o.m_value; | |
73 | return *this; | |
74 | } | |
75 | rational_adaptor& operator = (const IntBackend& o) | |
76 | { | |
77 | m_value = o; | |
78 | return *this; | |
79 | } | |
80 | template <class Int> | |
81 | 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> | |
87 | 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 | } | |
106 | 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 | } | |
144 | void swap(rational_adaptor& o) | |
145 | { | |
146 | std::swap(m_value, o.m_value); | |
147 | } | |
148 | 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 | } | |
161 | void negate() | |
162 | { | |
163 | m_value = -m_value; | |
164 | } | |
165 | 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> | |
170 | 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> | |
175 | 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 | } | |
181 | rational_type& data() { return m_value; } | |
182 | const rational_type& data()const { return m_value; } | |
183 | ||
184 | template <class Archive> | |
185 | void serialize(Archive& ar, const mpl::true_&) | |
186 | { | |
187 | // Saving | |
188 | integer_type n(m_value.numerator()), d(m_value.denominator()); | |
189 | ar & n; | |
190 | ar & d; | |
191 | } | |
192 | template <class Archive> | |
193 | void serialize(Archive& ar, const mpl::false_&) | |
194 | { | |
195 | // Loading | |
196 | integer_type n, d; | |
197 | ar & n; | |
198 | ar & d; | |
199 | m_value.assign(n, d); | |
200 | } | |
201 | template <class Archive> | |
202 | 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> | |
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> | |
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> | |
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> | |
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> | |
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> | |
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> | |
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> | |
266 | inline bool eval_is_zero(const rational_adaptor<IntBackend>& val) | |
267 | { | |
268 | return eval_is_zero(val.data().numerator().backend()); | |
269 | } | |
270 | template <class IntBackend> | |
271 | inline int eval_get_sign(const rational_adaptor<IntBackend>& val) | |
272 | { | |
273 | return eval_get_sign(val.data().numerator().backend()); | |
274 | } | |
275 | ||
276 | template<class IntBackend, class V> | |
277 | inline void assign_components(rational_adaptor<IntBackend>& result, const V& v1, const V& v2) | |
278 | { | |
279 | result.data().assign(v1, v2); | |
280 | } | |
281 | ||
282 | template <class IntBackend> | |
283 | inline std::size_t hash_value(const rational_adaptor<IntBackend>& val) | |
284 | { | |
285 | std::size_t result = hash_value(val.data().numerator()); | |
286 | boost::hash_combine(result, val.data().denominator()); | |
287 | return result; | |
288 | } | |
289 | ||
290 | ||
291 | } // namespace backends | |
292 | ||
293 | template<class IntBackend> | |
294 | struct expression_template_default<backends::rational_adaptor<IntBackend> > : public expression_template_default<IntBackend> {}; | |
295 | ||
296 | template<class IntBackend> | |
297 | struct number_category<backends::rational_adaptor<IntBackend> > : public mpl::int_<number_kind_rational>{}; | |
298 | ||
299 | using boost::multiprecision::backends::rational_adaptor; | |
300 | ||
301 | template <class T> | |
302 | struct component_type<rational_adaptor<T> > | |
303 | { | |
304 | typedef number<T> type; | |
305 | }; | |
306 | ||
307 | template <class IntBackend, expression_template_option ET> | |
308 | inline number<IntBackend, ET> numerator(const number<rational_adaptor<IntBackend>, ET>& val) | |
309 | { | |
310 | return val.backend().data().numerator(); | |
311 | } | |
312 | template <class IntBackend, expression_template_option ET> | |
313 | inline number<IntBackend, ET> denominator(const number<rational_adaptor<IntBackend>, ET>& val) | |
314 | { | |
315 | return val.backend().data().denominator(); | |
316 | } | |
317 | ||
318 | #ifdef BOOST_NO_SFINAE_EXPR | |
319 | ||
320 | namespace detail{ | |
321 | ||
322 | template<class U, class IntBackend> | |
323 | struct is_explicitly_convertible<U, rational_adaptor<IntBackend> > : public is_explicitly_convertible<U, IntBackend> {}; | |
324 | ||
325 | } | |
326 | ||
327 | #endif | |
328 | ||
329 | }} // namespaces | |
330 | ||
331 | ||
332 | namespace std{ | |
333 | ||
334 | template <class IntBackend, boost::multiprecision::expression_template_option ExpressionTemplates> | |
335 | class numeric_limits<boost::multiprecision::number<boost::multiprecision::rational_adaptor<IntBackend>, ExpressionTemplates> > : public std::numeric_limits<boost::multiprecision::number<IntBackend, ExpressionTemplates> > | |
336 | { | |
337 | typedef std::numeric_limits<boost::multiprecision::number<IntBackend> > base_type; | |
338 | typedef boost::multiprecision::number<boost::multiprecision::rational_adaptor<IntBackend> > number_type; | |
339 | public: | |
340 | BOOST_STATIC_CONSTEXPR bool is_integer = false; | |
341 | BOOST_STATIC_CONSTEXPR bool is_exact = true; | |
342 | BOOST_STATIC_CONSTEXPR number_type (min)() { return (base_type::min)(); } | |
343 | BOOST_STATIC_CONSTEXPR number_type (max)() { return (base_type::max)(); } | |
344 | BOOST_STATIC_CONSTEXPR number_type lowest() { return -(max)(); } | |
345 | BOOST_STATIC_CONSTEXPR number_type epsilon() { return base_type::epsilon(); } | |
346 | BOOST_STATIC_CONSTEXPR number_type round_error() { return epsilon() / 2; } | |
347 | BOOST_STATIC_CONSTEXPR number_type infinity() { return base_type::infinity(); } | |
348 | BOOST_STATIC_CONSTEXPR number_type quiet_NaN() { return base_type::quiet_NaN(); } | |
349 | BOOST_STATIC_CONSTEXPR number_type signaling_NaN() { return base_type::signaling_NaN(); } | |
350 | BOOST_STATIC_CONSTEXPR number_type denorm_min() { return base_type::denorm_min(); } | |
351 | }; | |
352 | ||
353 | #ifndef BOOST_NO_INCLASS_MEMBER_INITIALIZATION | |
354 | ||
355 | template <class IntBackend, boost::multiprecision::expression_template_option ExpressionTemplates> | |
356 | BOOST_CONSTEXPR_OR_CONST bool numeric_limits<boost::multiprecision::number<boost::multiprecision::rational_adaptor<IntBackend>, ExpressionTemplates> >::is_integer; | |
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_exact; | |
359 | ||
360 | #endif | |
361 | ||
362 | ||
363 | } | |
364 | ||
365 | #endif |