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