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