1 // Boost.Geometry (aka GGL, Generic Geometry Library)
2 // Unit Test
3 
4 // Copyright (c) 2015, Oracle and/or its affiliates.
5 
6 // Contributed and/or modified by Menelaos Karavelas, 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_TEST_MODULE
12 #define BOOST_TEST_MODULE test_promote_integral
13 #endif
14 
15 #include <climits>
16 #include <cstddef>
17 #include <algorithm>
18 #include <limits>
19 #include <iostream>
20 #include <string>
21 #include <sstream>
22 
23 #include <boost/test/included/unit_test.hpp>
24 
25 #include <boost/config.hpp>
26 #include <boost/type_traits/is_same.hpp>
27 #include <boost/type_traits/is_unsigned.hpp>
28 
29 #include <geometry_test_common.hpp>
30 
31 #include <boost/geometry/util/condition.hpp>
32 #include <boost/geometry/util/promote_integral.hpp>
33 
34 #if !defined(BOOST_GEOMETRY_NO_MULTIPRECISION_INTEGER)
35 #include <boost/multiprecision/cpp_int.hpp>
36 #endif
37 
38 #if defined(BOOST_GEOMETRY_TEST_DEBUG)
39 #if defined(BOOST_HAS_INT128) && defined(BOOST_GEOMETRY_ENABLE_INT128)
print_uint128_t(std::ostream & os,boost::uint128_type i)40 void print_uint128_t(std::ostream& os, boost::uint128_type i)
41 {
42     if (i == 0)
43     {
44         os << "0";
45         return;
46     }
47 
48     std::stringstream stream;
49     while (i > 0)
50     {
51         stream << static_cast<int>(i % 10);
52         i /= 10;
53     }
54     std::string str = stream.str();
55     std::reverse(str.begin(), str.end());
56     os << str;
57 }
58 
operator <<(std::ostream & os,boost::int128_type i)59 std::ostream& operator<<(std::ostream& os, boost::int128_type i)
60 {
61     if (i < 0)
62     {
63         os << "-";
64         print_uint128_t(os, static_cast<boost::uint128_type>(-i));
65     }
66     else
67     {
68         print_uint128_t(os, static_cast<boost::uint128_type>(i));
69     }
70     return os;
71 }
72 
operator <<(std::ostream & os,boost::uint128_type i)73 std::ostream& operator<<(std::ostream& os, boost::uint128_type i)
74 {
75     print_uint128_t(os, i);
76     return os;
77 }
78 #endif // BOOST_HAS_INT128 && BOOST_GEOMETRY_ENABLE_INT128
79 #endif // BOOST_GEOMETRY_TEST_DEBUG
80 
81 namespace bg = boost::geometry;
82 
83 template
84 <
85     typename T,
86     bool Signed = boost::is_fundamental<T>::type::value
87     && ! boost::is_unsigned<T>::type::value
88 >
89 struct absolute_value
90 {
applyabsolute_value91     static inline T apply(T const& t)
92     {
93         return t < 0 ? -t : t;
94     }
95 };
96 
97 template <typename T>
98 struct absolute_value<T, false>
99 {
applyabsolute_value100     static inline T apply(T const& t)
101     {
102         return t;
103     }
104 };
105 
106 
107 
108 template
109 <
110     typename Integral,
111     typename Promoted,
112     bool Signed = ! boost::is_unsigned<Promoted>::type::value
113 >
114 struct test_max_values
115 {
applytest_max_values116     static inline void apply()
117     {
118         Promoted min_value = (std::numeric_limits<Integral>::min)();
119         min_value *= min_value;
120         BOOST_CHECK(absolute_value<Promoted>::apply(min_value) == min_value);
121         Promoted max_value = (std::numeric_limits<Integral>::max)();
122         max_value *= max_value;
123         BOOST_CHECK(absolute_value<Promoted>::apply(max_value) == max_value);
124 
125 #ifdef BOOST_GEOMETRY_TEST_DEBUG
126         std::cout << "integral min_value^2: " << min_value << std::endl;
127         std::cout << "promoted max_value:   "
128                   << (std::numeric_limits<Promoted>::max)() << std::endl;
129 #endif
130     }
131 };
132 
133 template <typename Integral, typename Promoted>
134 struct test_max_values<Integral, Promoted, false>
135 {
applytest_max_values136     static inline void apply()
137     {
138         Promoted max_value = (std::numeric_limits<Integral>::max)();
139         Promoted max_value_sqr = max_value * max_value;
140         BOOST_CHECK(max_value_sqr < (std::numeric_limits<Promoted>::max)()
141                     &&
142                     max_value_sqr > max_value);
143 
144 #ifdef BOOST_GEOMETRY_TEST_DEBUG
145         std::cout << "integral max_value^2: " << max_value_sqr << std::endl;
146         std::cout << "promoted max_value:   "
147                   << (std::numeric_limits<Promoted>::max)() << std::endl;
148 #endif
149     }
150 };
151 
152 
153 // helper function that returns the bit size of a type
154 template
155 <
156     typename T,
157     bool IsFundamental = boost::is_fundamental<T>::type::value
158 >
159 struct bit_size_impl : boost::mpl::size_t<0>
160 {};
161 
162 template <typename T>
163 struct bit_size_impl<T, true> : bg::detail::promote_integral::bit_size<T>::type
164 {};
165 
166 #if !defined(BOOST_GEOMETRY_NO_MULTIPRECISION_INTEGER)
167 template
168 <
169     typename Backend,
170     boost::multiprecision::expression_template_option ExpressionTemplates
171 >
172 struct bit_size_impl
173     <
174         boost::multiprecision::number<Backend, ExpressionTemplates>,
175         false
176     > : bg::detail::promote_integral::bit_size
177         <
178             boost::multiprecision::number<Backend, ExpressionTemplates>
179         >
180 {};
181 #endif
182 
183 
184 template <typename T>
bit_size()185 std::size_t bit_size()
186 {
187     return bit_size_impl<T>::type::value;
188 }
189 
190 template <bool PromoteUnsignedToUnsigned>
191 struct test_promote_integral
192 {
193     template <typename Type, typename ExpectedPromotedType>
applytest_promote_integral194     static inline void apply(std::string const& case_id)
195     {
196         typedef typename bg::promote_integral
197             <
198                 Type, PromoteUnsignedToUnsigned
199             >::type promoted_integral_type;
200 
201         bool const same_types = boost::is_same
202             <
203                 promoted_integral_type, ExpectedPromotedType
204             >::type::value;
205 
206         BOOST_CHECK_MESSAGE(same_types,
207                             "case ID: " << case_id
208                             << "input type: " << typeid(Type).name()
209                             << "; detected: "
210                             << typeid(promoted_integral_type).name()
211                             << "; expected: "
212                             << typeid(ExpectedPromotedType).name());
213 
214         if (BOOST_GEOMETRY_CONDITION((! boost::is_same
215                 <
216                     Type, promoted_integral_type
217                 >::type::value)))
218         {
219             test_max_values<Type, promoted_integral_type>::apply();
220         }
221 
222 #ifdef BOOST_GEOMETRY_TEST_DEBUG
223         std::cout << "case ID: " << case_id << std::endl
224                   << "type : " << typeid(Type).name()
225                   << ", sizeof (bits): " << bit_size<Type>()
226                   << ", min value: "
227                   << (std::numeric_limits<Type>::min)()
228                   << ", max value: "
229                   << (std::numeric_limits<Type>::max)()
230                   << std::endl;
231         std::cout << "detected promoted type : "
232                   << typeid(promoted_integral_type).name()
233                   << ", sizeof (bits): " << bit_size<promoted_integral_type>()
234                   << ", min value: "
235                   << (std::numeric_limits<promoted_integral_type>::min)()
236                   << ", max value: "
237                   << (std::numeric_limits<promoted_integral_type>::max)()
238                   << std::endl;
239         std::cout << "expected promoted type : "
240                   << typeid(ExpectedPromotedType).name()
241                   << ", sizeof (bits): " << bit_size<ExpectedPromotedType>()
242                   << ", min value: "
243                   << (std::numeric_limits<ExpectedPromotedType>::min)()
244                   << ", max value: "
245                   << (std::numeric_limits<ExpectedPromotedType>::max)()
246                   << std::endl;
247         std::cout << std::endl;
248 #endif
249     }
250 };
251 
252 template
253 <
254     typename T,
255     bool PromoteUnsignedToUnsigned = false,
256     bool IsSigned = ! boost::is_unsigned<T>::type::value
257 >
258 struct test_promotion
259 {
applytest_promotion260     static inline void apply(std::string case_id)
261     {
262 #ifdef BOOST_GEOMETRY_TEST_DEBUG
263         std::cout << "*** "
264                   << (IsSigned ? "signed" : "unsigned")
265                   << " -> signed ***" << std::endl;
266 #endif
267 
268         typedef test_promote_integral<PromoteUnsignedToUnsigned> tester;
269 
270         case_id += (PromoteUnsignedToUnsigned ? "-t" : "-f");
271 
272         std::size_t min_size = 2 * bit_size<T>() - 1;
273         if (BOOST_GEOMETRY_CONDITION(! IsSigned))
274         {
275             min_size += 2;
276         }
277 
278 #ifdef BOOST_GEOMETRY_TEST_DEBUG
279         std::cout << "min size: " << min_size << std::endl;
280 #endif
281 
282         if (bit_size<short>() >= min_size)
283         {
284             tester::template apply<T, short>(case_id);
285         }
286         else if (bit_size<int>() >= min_size)
287         {
288             tester::template apply<T, int>(case_id);
289         }
290         else if (bit_size<long>() >= min_size)
291         {
292             tester::template apply<T, long>(case_id);
293         }
294 #if defined(BOOST_HAS_LONG_LONG)
295         else if (bit_size<boost::long_long_type>() >= min_size)
296         {
297             tester::template apply<T, boost::long_long_type>(case_id);
298         }
299 #endif
300 #if defined(BOOST_HAS_INT128) && defined(BOOST_GEOMETRY_ENABLE_INT128)
301         else if (bit_size<boost::int128_type>() >= min_size)
302         {
303             tester::template apply<T, boost::int128_type>(case_id);
304         }
305 #endif
306         else
307         {
308 #if !defined(BOOST_GEOMETRY_NO_MULTIPRECISION_INTEGER)
309             namespace bm = boost::multiprecision;
310             typedef bm::number
311                 <
312                     bm::cpp_int_backend
313                     <
314                         2 * CHAR_BIT * sizeof(T) + (IsSigned ? -1 : 1),
315                         2 * CHAR_BIT * sizeof(T) + (IsSigned ? -1 : 1),
316                         bm::signed_magnitude,
317                         bm::unchecked,
318                         void
319                     >
320                 > multiprecision_integer_type;
321 
322             tester::template apply<T, multiprecision_integer_type>(case_id);
323 #else
324             tester::template apply<T, T>(case_id);
325 #endif
326         }
327     }
328 };
329 
330 template <typename T>
331 struct test_promotion<T, true, false>
332 {
applytest_promotion333     static inline void apply(std::string case_id)
334     {
335 #ifdef BOOST_GEOMETRY_TEST_DEBUG
336         std::cout << "*** unsigned -> unsigned ***" << std::endl;
337 #endif
338         case_id += "-t";
339 
340         typedef test_promote_integral<true> tester;
341 
342         std::size_t min_size = 2 * bit_size<T>();
343 
344 #ifdef BOOST_GEOMETRY_TEST_DEBUG
345         std::cout << "min size: " << min_size << std::endl;
346 #endif
347 
348         if (bit_size<unsigned short>() >= min_size)
349         {
350             tester::apply<T, unsigned short>(case_id);
351         }
352         else if (bit_size<unsigned int>() >= min_size)
353         {
354             tester::apply<T, unsigned int>(case_id);
355         }
356         else if (bit_size<unsigned long>() >= min_size)
357         {
358             tester::apply<T, unsigned long>(case_id);
359         }
360         else if (bit_size<std::size_t>() >= min_size)
361         {
362             tester::apply<T, std::size_t>(case_id);
363         }
364 #if defined(BOOST_HAS_LONG_LONG)
365         else if (bit_size<boost::ulong_long_type>() >= min_size)
366         {
367             tester::template apply<T, boost::ulong_long_type>(case_id);
368         }
369 #endif
370 #if defined(BOOST_HAS_INT128) && defined(BOOST_GEOMETRY_ENABLE_INT128)
371         else if (bit_size<boost::uint128_type>() >= min_size)
372         {
373             tester::template apply<T, boost::uint128_type>(case_id);
374         }
375 #endif
376         else
377         {
378 #if !defined(BOOST_GEOMETRY_NO_MULTIPRECISION_INTEGER)
379             namespace bm = boost::multiprecision;
380             typedef bm::number
381                 <
382                     bm::cpp_int_backend
383                     <
384                         2 * CHAR_BIT * sizeof(T),
385                         2 * CHAR_BIT * sizeof(T),
386                         bm::unsigned_magnitude,
387                         bm::unchecked,
388                         void
389                     >
390                 > multiprecision_integer_type;
391 
392             tester::apply<T, multiprecision_integer_type>(case_id);
393 #else
394             tester::apply<T, T>(case_id);
395 #endif
396         }
397     }
398 };
399 
400 
401 
BOOST_AUTO_TEST_CASE(test_char)402 BOOST_AUTO_TEST_CASE( test_char )
403 {
404     test_promotion<char>::apply("char");
405     test_promotion<char, true>::apply("char");
406     test_promotion<signed char>::apply("schar");
407     test_promotion<signed char, true>::apply("schar");
408     test_promotion<unsigned char>::apply("uchar");
409     test_promotion<unsigned char, true>::apply("uchar");
410 }
411 
BOOST_AUTO_TEST_CASE(test_short)412 BOOST_AUTO_TEST_CASE( test_short )
413 {
414     test_promotion<short>::apply("short");
415     test_promotion<short, true>::apply("short");
416     test_promotion<unsigned short>::apply("ushort");
417     test_promotion<unsigned short, true>::apply("ushort");
418 }
419 
BOOST_AUTO_TEST_CASE(test_int)420 BOOST_AUTO_TEST_CASE( test_int )
421 {
422     test_promotion<int>::apply("int");
423     test_promotion<int, true>::apply("int");
424     test_promotion<unsigned int>::apply("uint");
425     test_promotion<unsigned int, true>::apply("uint");
426 }
427 
BOOST_AUTO_TEST_CASE(test_long)428 BOOST_AUTO_TEST_CASE( test_long )
429 {
430     test_promotion<long>::apply("long");
431     test_promotion<long, true>::apply("long");
432     test_promotion<unsigned long>::apply("ulong");
433     test_promotion<unsigned long, true>::apply("ulong");
434 }
435 
BOOST_AUTO_TEST_CASE(test_std_size_t)436 BOOST_AUTO_TEST_CASE( test_std_size_t )
437 {
438     test_promotion<std::size_t>::apply("size_t");
439     test_promotion<std::size_t, true>::apply("size_t");
440 }
441 
442 #ifdef BOOST_HAS_LONG_LONG
BOOST_AUTO_TEST_CASE(test_long_long)443 BOOST_AUTO_TEST_CASE( test_long_long )
444 {
445     test_promotion<boost::long_long_type>::apply("long long");
446     test_promotion<boost::long_long_type, true>::apply("long long");
447     test_promotion<boost::ulong_long_type>::apply("ulong long");
448     test_promotion<boost::ulong_long_type, true>::apply("ulong long");
449 }
450 #endif
451 
452 #if defined(BOOST_HAS_INT128) && defined(BOOST_GEOMETRY_ENABLE_INT128)
BOOST_AUTO_TEST_CASE(test_int128)453 BOOST_AUTO_TEST_CASE( test_int128 )
454 {
455     test_promotion<boost::int128_type>::apply("int128_t");
456     test_promotion<boost::int128_type, true>::apply("int128_t");
457     test_promotion<boost::uint128_type>::apply("uint128_t");
458     test_promotion<boost::uint128_type, true>::apply("uint128_t");
459 }
460 #endif
461 
462 #if !defined(BOOST_GEOMETRY_NO_MULTIPRECISION_INTEGER)
BOOST_AUTO_TEST_CASE(test_user_types)463 BOOST_AUTO_TEST_CASE( test_user_types )
464 {
465     namespace bm = boost::multiprecision;
466     typedef bm::number
467         <
468             bm::cpp_int_backend
469                 <
470                     17,
471                     17,
472                     bm::signed_magnitude,
473                     bm::unchecked,
474                     void
475                 >
476         > user_signed_type1;
477 
478     typedef bm::number
479         <
480             bm::cpp_int_backend
481                 <
482                     17,
483                     17,
484                     bm::unsigned_magnitude,
485                     bm::unchecked,
486                     void
487                 >
488         > user_unsigned_type1;
489 
490     typedef bm::number
491         <
492             bm::cpp_int_backend
493                 <
494                     500,
495                     500,
496                     bm::signed_magnitude,
497                     bm::unchecked,
498                     void
499                 >
500         > user_signed_type2;
501 
502     typedef bm::number
503         <
504             bm::cpp_int_backend
505                 <
506                     500,
507                     500,
508                     bm::unsigned_magnitude,
509                     bm::unchecked,
510                     void
511                 >
512         > user_unsigned_type2;
513 
514     // for user defined number types we do not do any promotion
515     typedef test_promote_integral<true> tester1;
516     typedef test_promote_integral<false> tester2;
517     tester1::apply<user_signed_type1, user_signed_type1>("u1s");
518     tester1::apply<user_signed_type2, user_signed_type2>("u2s");
519     tester1::apply<user_unsigned_type1, user_unsigned_type1>("u1u");
520     tester1::apply<user_unsigned_type2, user_unsigned_type2>("u2u");
521 
522     tester2::apply<user_signed_type1, user_signed_type1>("u1s");
523     tester2::apply<user_signed_type2, user_signed_type2>("u2s");
524     tester2::apply<user_unsigned_type1, user_unsigned_type1>("u1u");
525     tester2::apply<user_unsigned_type2, user_unsigned_type2>("u1u");
526 }
527 #endif
528 
BOOST_AUTO_TEST_CASE(test_floating_point)529 BOOST_AUTO_TEST_CASE( test_floating_point )
530 {
531     typedef test_promote_integral<true> tester1;
532     typedef test_promote_integral<false> tester2;
533 
534     // for floating-point types we do not do any promotion
535     tester1::apply<float, float>("fp-f");
536     tester1::apply<double, double>("fp-d");
537     tester1::apply<long double, long double>("fp-ld");
538 
539     tester2::apply<float, float>("fp-f");
540     tester2::apply<double, double>("fp-d");
541     tester2::apply<long double, long double>("fp-ld");
542 
543 #ifdef HAVE_TTMATH
544     tester1::apply<ttmath_big, ttmath_big>("fp-tt");
545     tester2::apply<ttmath_big, ttmath_big>("fp-tt");
546 #endif
547 }
548