promote_integral.hpp 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. // Boost.Geometry (aka GGL, Generic Geometry Library)
  2. // Copyright (c) 2015-2020, Oracle and/or its affiliates.
  3. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
  4. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
  5. // Licensed under the Boost Software License version 1.0.
  6. // http://www.boost.org/users/license.html
  7. #ifndef BOOST_GEOMETRY_UTIL_PROMOTE_INTEGRAL_HPP
  8. #define BOOST_GEOMETRY_UTIL_PROMOTE_INTEGRAL_HPP
  9. // Uncommenting this macro will use Boost.Multiprecision's cpp_int<> as a last resort
  10. // TODO (#1380): change this to BOOST_GEOMETRY_PROMOTE_INTEGER_TO_BOOST_MULTI_PRECISION
  11. // to be able to let users actively choose to use Boost.Multiprecision, but not enable it by default
  12. #define BOOST_GEOMETRY_NO_MULTIPRECISION_INTEGER
  13. #include <climits>
  14. #include <cstddef>
  15. #include <type_traits>
  16. #if !defined(BOOST_GEOMETRY_NO_MULTIPRECISION_INTEGER)
  17. #include <boost/multiprecision/cpp_int.hpp>
  18. #endif
  19. namespace boost { namespace geometry
  20. {
  21. #ifndef DOXYGEN_NO_DETAIL
  22. namespace detail { namespace promote_integral
  23. {
  24. // meta-function that returns the bit size of a type
  25. template
  26. <
  27. typename T,
  28. bool IsFundamental = std::is_fundamental<T>::value
  29. >
  30. struct bit_size
  31. {};
  32. // for fundamental types, just return CHAR_BIT * sizeof(T)
  33. template <typename T>
  34. struct bit_size<T, true>
  35. : std::integral_constant<std::size_t, (CHAR_BIT * sizeof(T))>
  36. {};
  37. #if !defined(BOOST_GEOMETRY_NO_MULTIPRECISION_INTEGER)
  38. // partial specialization for cpp_int
  39. template
  40. <
  41. unsigned MinSize,
  42. unsigned MaxSize,
  43. boost::multiprecision::cpp_integer_type SignType,
  44. boost::multiprecision::cpp_int_check_type Checked,
  45. typename Allocator,
  46. boost::multiprecision::expression_template_option ExpressionTemplates
  47. >
  48. struct bit_size
  49. <
  50. boost::multiprecision::number
  51. <
  52. boost::multiprecision::cpp_int_backend
  53. <
  54. MinSize, MaxSize, SignType, Checked, Allocator
  55. >,
  56. ExpressionTemplates
  57. >,
  58. false
  59. >
  60. : std::integral_constant<std::size_t, MaxSize>
  61. {};
  62. #endif // BOOST_GEOMETRY_NO_MULTIPRECISION_INTEGER
  63. template <typename T, std::size_t MinSize, typename ...Ts>
  64. struct promote_to_larger
  65. {
  66. // if promotion fails, keep the number T
  67. // (and cross fingers that overflow will not occur)
  68. typedef T type;
  69. };
  70. template <typename T, std::size_t MinSize, typename CurrentT, typename ...Ts>
  71. struct promote_to_larger<T, MinSize, CurrentT, Ts...>
  72. {
  73. typedef std::conditional_t
  74. <
  75. (bit_size<CurrentT>::value >= MinSize),
  76. CurrentT,
  77. typename promote_to_larger<T, MinSize, Ts...>::type
  78. > type;
  79. };
  80. template <typename ...Ts>
  81. struct integral_types {};
  82. template <typename T, std::size_t MinSize, typename ...Ts>
  83. struct promote_to_larger<T, MinSize, integral_types<Ts...>>
  84. : promote_to_larger<T, MinSize, Ts...>
  85. {};
  86. }} // namespace detail::promote_integral
  87. #endif // DOXYGEN_NO_DETAIL
  88. /*!
  89. \brief Meta-function to define an integral type with size
  90. than is (roughly) twice the bit size of T
  91. \ingroup utility
  92. \details
  93. This meta-function tries to promote the fundamental integral type T
  94. to a another integral type with size (roughly) twice the bit size of T.
  95. To do this, two times the bit size of T is tested against the bit sizes of:
  96. short, int, long, long long, boost::int128_t
  97. and the one that first matches is chosen.
  98. For unsigned types the bit size of T is tested against the bit
  99. sizes of the types above, if T is promoted to a signed type, or
  100. the bit sizes of
  101. unsigned short, unsigned int, unsigned long, std::size_t,
  102. unsigned long long, boost::uint128_t
  103. if T is promoted to an unsigned type.
  104. By default an unsigned type is promoted to a signed type.
  105. This behavior is controlled by the PromoteUnsignedToUnsigned
  106. boolean template parameter, whose default value is "false".
  107. To promote an unsigned type to an unsigned type set the value of
  108. this template parameter to "true".
  109. If the macro BOOST_GEOMETRY_NO_MULTIPRECISION_INTEGER is not
  110. defined, boost's multiprecision integer cpp_int<> is used as a
  111. last resort.
  112. If BOOST_GEOMETRY_NO_MULTIPRECISION_INTEGER is defined and an
  113. appropriate type cannot be detected, the input type is returned as is.
  114. Finally, if the passed type is either a floating-point type or a
  115. user-defined type it is returned as is.
  116. \note boost::int128_type and boost::uint128_type are considered
  117. only if the macros BOOST_HAS_INT128 and BOOST_GEOMETRY_ENABLE_INT128
  118. are defined
  119. */
  120. template
  121. <
  122. typename T,
  123. bool PromoteUnsignedToUnsigned = false,
  124. bool UseCheckedInteger = false,
  125. bool IsIntegral = std::is_integral<T>::value
  126. >
  127. class promote_integral
  128. {
  129. private:
  130. static bool const is_unsigned = std::is_unsigned<T>::value;
  131. typedef detail::promote_integral::bit_size<T> bit_size_type;
  132. #if !defined(BOOST_GEOMETRY_NO_MULTIPRECISION_INTEGER)
  133. // Define the proper check policy for the multiprecision integer
  134. typedef std::conditional_t
  135. <
  136. UseCheckedInteger,
  137. std::integral_constant
  138. <
  139. boost::multiprecision::cpp_int_check_type,
  140. boost::multiprecision::checked
  141. >,
  142. std::integral_constant
  143. <
  144. boost::multiprecision::cpp_int_check_type,
  145. boost::multiprecision::unchecked
  146. >
  147. > check_policy_type;
  148. // Meta-function to get the multiprecision integer type for the
  149. // given size and sign type (signed/unsigned)
  150. template
  151. <
  152. unsigned int Size,
  153. boost::multiprecision::cpp_integer_type SignType
  154. >
  155. struct multiprecision_integer_type
  156. {
  157. typedef boost::multiprecision::number
  158. <
  159. boost::multiprecision::cpp_int_backend
  160. <
  161. Size,
  162. Size,
  163. SignType,
  164. check_policy_type::value,
  165. void
  166. >
  167. > type;
  168. };
  169. #endif
  170. // Define the minimum size (in bits) needed for the promoted type
  171. // If T is the input type and P the promoted type, then the
  172. // minimum number of bits for P are (below b stands for the number
  173. // of bits of T):
  174. // * if T is unsigned and P is unsigned: 2 * b
  175. // * if T is signed and P is signed: 2 * b - 1
  176. // * if T is unsigned and P is signed: 2 * b + 1
  177. typedef std::conditional_t
  178. <
  179. (PromoteUnsignedToUnsigned && is_unsigned),
  180. std::integral_constant<std::size_t, (2 * bit_size_type::value)>,
  181. std::conditional_t
  182. <
  183. is_unsigned,
  184. std::integral_constant<std::size_t, (2 * bit_size_type::value + 1)>,
  185. std::integral_constant<std::size_t, (2 * bit_size_type::value - 1)>
  186. >
  187. > min_bit_size_type;
  188. // Define the list of signed integral types we are going to use
  189. // for promotion
  190. typedef detail::promote_integral::integral_types
  191. <
  192. short,
  193. int,
  194. long,
  195. long long
  196. #if defined(BOOST_HAS_INT128) && defined(BOOST_GEOMETRY_ENABLE_INT128)
  197. , boost::int128_type
  198. #endif
  199. #if !defined(BOOST_GEOMETRY_NO_MULTIPRECISION_INTEGER)
  200. , typename multiprecision_integer_type
  201. <
  202. min_bit_size_type::value,
  203. boost::multiprecision::signed_magnitude
  204. >::type
  205. #endif
  206. > signed_integral_types;
  207. // Define the list of unsigned integral types we are going to use
  208. // for promotion
  209. typedef detail::promote_integral::integral_types
  210. <
  211. unsigned short,
  212. unsigned int,
  213. unsigned long,
  214. std::size_t,
  215. unsigned long long
  216. #if defined(BOOST_HAS_INT128) && defined(BOOST_GEOMETRY_ENABLE_INT128)
  217. , boost::uint128_type
  218. #endif
  219. #if !defined(BOOST_GEOMETRY_NO_MULTIPRECISION_INTEGER)
  220. , typename multiprecision_integer_type
  221. <
  222. min_bit_size_type::value,
  223. boost::multiprecision::unsigned_magnitude
  224. >::type
  225. #endif
  226. > unsigned_integral_types;
  227. // Define the list of integral types that will be used for
  228. // promotion (depending in whether we was to promote unsigned to
  229. // unsigned or not)
  230. typedef std::conditional_t
  231. <
  232. (is_unsigned && PromoteUnsignedToUnsigned),
  233. unsigned_integral_types,
  234. signed_integral_types
  235. > integral_types;
  236. public:
  237. typedef typename detail::promote_integral::promote_to_larger
  238. <
  239. T,
  240. min_bit_size_type::value,
  241. integral_types
  242. >::type type;
  243. };
  244. template <typename T, bool PromoteUnsignedToUnsigned, bool UseCheckedInteger>
  245. class promote_integral
  246. <
  247. T, PromoteUnsignedToUnsigned, UseCheckedInteger, false
  248. >
  249. {
  250. public:
  251. typedef T type;
  252. };
  253. }} // namespace boost::geometry
  254. #endif // BOOST_GEOMETRY_UTIL_PROMOTE_INTEGRAL_HPP