rational_adaptor.hpp 12 KB

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