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