rational_adaptor.hpp 13 KB

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