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