1 ///////////////////////////////////////////////////////////////
2 //  Copyright 2012 John Maddock. Distributed under the Boost
3 //  Software License, Version 1.0. (See accompanying file
4 //  LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt
5 //
6 
7 #ifndef BOOST_MULTIPRECISION_TEST_HPP
8 #define BOOST_MULTIPRECISION_TEST_HPP
9 
10 #include <limits>
11 #include <cmath>
12 #include <typeinfo>
13 
14 #include <boost/detail/lightweight_test.hpp>
15 #include <boost/current_function.hpp>
16 #include <boost/static_assert.hpp>
17 #include <boost/utility/enable_if.hpp>
18 #include <boost/type_traits/is_unsigned.hpp>
19 #include <boost/multiprecision/number.hpp>
20 
21 namespace detail {
22 
23 template <class T>
24 inline typename boost::disable_if_c<boost::is_unsigned<T>::value || boost::multiprecision::is_unsigned_number<T>::value, T>::type
abs(const T & a)25 abs(const T& a)
26 {
27    return a < 0 ? -a : a;
28 }
29 
30 template <class T>
31 inline typename boost::enable_if_c<boost::is_unsigned<T>::value || boost::multiprecision::is_unsigned_number<T>::value, T>::type
abs(const T & a)32 abs(const T& a)
33 {
34    return a;
35 }
36 
37 } // namespace detail
38 
39 template <class T>
relative_error(T a,T b)40 typename boost::enable_if_c<boost::multiprecision::number_category<T>::value == boost::multiprecision::number_kind_integer, T>::type relative_error(T a, T b)
41 {
42    return a > b ? a - b : b - a;
43 }
44 
45 template <class T>
relative_error(T a,T b)46 typename boost::disable_if_c<(boost::multiprecision::number_category<T>::value == boost::multiprecision::number_kind_integer) || boost::multiprecision::is_interval_number<T>::value, T>::type relative_error(T a, T b)
47 {
48    using ::detail::abs;
49    using std::abs;
50 
51    T min_val = (std::numeric_limits<T>::min)();
52    T max_val = (std::numeric_limits<T>::max)();
53 
54    if ((a != 0) && (b != 0))
55    {
56       if (a == b)
57          return 0;
58 
59       // TODO: use isfinite:
60       if (abs(b) >= max_val)
61       {
62          if (abs(a) >= max_val)
63             return 0; // one infinity is as good as another!
64       }
65       // If the result is denormalised, treat all denorms as equivalent:
66       if ((a < min_val) && (a > 0))
67          a = min_val;
68       else if ((a > -min_val) && (a < 0))
69          a = -min_val;
70       if ((b < min_val) && (b > 0))
71          b = min_val;
72       else if ((b > -min_val) && (b < 0))
73          b = -min_val;
74 
75       return (std::max)(abs(T((a - b) / a)), abs(T((a - b) / b))) / std::numeric_limits<T>::epsilon();
76    }
77 
78    // Handle special case where one or both are zero:
79    if (min_val == 0)
80       return abs(T(a - b));
81    if (abs(a) < min_val)
82       a = min_val;
83    if (abs(b) < min_val)
84       b = min_val;
85 
86    return (std::max)(abs(T((a - b) / a)), abs(T((a - b) / b))) / std::numeric_limits<T>::epsilon();
87 }
88 
89 template <class T, class U>
90 typename boost::mpl::if_c<boost::is_convertible<T, U>::value, U, T>::type
relative_error(T a,U b)91 relative_error(T a, U b)
92 {
93    typedef typename boost::mpl::if_c<boost::is_convertible<T, U>::value, U, T>::type cast_type;
94    return relative_error<cast_type>(static_cast<cast_type>(a), static_cast<cast_type>(b));
95 }
96 
97 template <class T>
relative_error(T a,T b)98 typename boost::enable_if_c<boost::multiprecision::is_interval_number<T>::value, T>::type relative_error(T a, T b)
99 {
100    typename boost::multiprecision::component_type<T>::type am = median(a);
101    typename boost::multiprecision::component_type<T>::type bm = median(b);
102    return relative_error<typename boost::multiprecision::component_type<T>::type>(am, bm);
103 }
104 
105 enum
106 {
107    warn_on_fail,
108    error_on_fail,
109    abort_on_fail
110 };
111 
112 template <class T>
epsilon_of(const T &)113 inline T epsilon_of(const T&)
114 {
115    BOOST_STATIC_ASSERT(std::numeric_limits<T>::is_specialized);
116    return std::numeric_limits<T>::is_integer ? static_cast<T>(1) : std::numeric_limits<T>::epsilon();
117 }
118 
119 template <class T>
digits_of(const T &)120 inline int digits_of(const T&)
121 {
122    return std::numeric_limits<T>::is_specialized ? std::numeric_limits<T>::digits10 + 3 : std::numeric_limits<long double>::digits10 + 3;
123 }
124 
report_where(const char * file,int line,const char * function)125 inline std::ostream& report_where(const char* file, int line, const char* function)
126 {
127    if (function)
128       BOOST_LIGHTWEIGHT_TEST_OSTREAM << "In function: " << function << std::endl;
129    BOOST_LIGHTWEIGHT_TEST_OSTREAM << file << ":" << line;
130    return BOOST_LIGHTWEIGHT_TEST_OSTREAM;
131 }
132 
133 #define BOOST_MP_REPORT_WHERE report_where(__FILE__, __LINE__, BOOST_CURRENT_FUNCTION)
134 
report_severity(int severity)135 inline void report_severity(int severity)
136 {
137    if (severity == error_on_fail)
138       ++boost::detail::test_errors();
139    else if (severity == abort_on_fail)
140    {
141       ++boost::detail::test_errors();
142       abort();
143    }
144 }
145 
146 #define BOOST_MP_REPORT_SEVERITY(severity) report_severity(severity)
147 
148 template <class E>
report_unexpected_exception(const E & e,int severity,const char * file,int line,const char * function)149 void report_unexpected_exception(const E& e, int severity, const char* file, int line, const char* function)
150 {
151    report_where(file, line, function) << " Unexpected exception of type " << typeid(e).name() << std::endl;
152    BOOST_LIGHTWEIGHT_TEST_OSTREAM << "Errot message was: " << e.what() << std::endl;
153    BOOST_MP_REPORT_SEVERITY(severity);
154 }
155 
156 #ifndef BOOST_NO_EXCEPTIONS
157 #define BOOST_MP_UNEXPECTED_EXCEPTION_CHECK(severity)                                       \
158    catch (const std::exception& e)                                                          \
159    {                                                                                        \
160       report_unexpected_exception(e, severity, __FILE__, __LINE__, BOOST_CURRENT_FUNCTION); \
161    }                                                                                        \
162    catch (...)                                                                              \
163    {                                                                                        \
164       std::cout << "Exception of unknown type was thrown" << std::endl;                     \
165       report_severity(severity);                                                            \
166    }
167 #define BOOST_MP_TRY try
168 #else
169 #define BOOST_MP_UNEXPECTED_EXCEPTION_CHECK(severity)
170 #define BOOST_MP_TRY
171 #endif
172 
173 #define BOOST_CHECK_IMP(x, severity)                                                        \
174    BOOST_MP_TRY                                                                             \
175    {                                                                                        \
176       if (x)                                                                                \
177       {                                                                                     \
178       }                                                                                     \
179       else                                                                                  \
180       {                                                                                     \
181          BOOST_MP_REPORT_WHERE << " Failed predicate: " << BOOST_STRINGIZE(x) << std::endl; \
182          BOOST_MP_REPORT_SEVERITY(severity);                                                \
183       }                                                                                     \
184    }                                                                                        \
185    BOOST_MP_UNEXPECTED_EXCEPTION_CHECK(severity)
186 
187 #define BOOST_CHECK(x) BOOST_CHECK_IMP(x, error_on_fail)
188 #define BOOST_WARN(x) BOOST_CHECK_IMP(x, warn_on_fail)
189 #define BOOST_REQUIRE(x) BOOST_CHECK_IMP(x, abort_on_fail)
190 
191 #define BOOST_CLOSE_IMP(x, y, tol, severity)                                                \
192    BOOST_MP_TRY                                                                             \
193    {                                                                                        \
194       if (relative_error(x, y) > tol)                                                       \
195       {                                                                                     \
196          BOOST_MP_REPORT_WHERE << " Failed check for closeness: \n"                         \
197                                << std::setprecision(digits_of(x)) << std::scientific        \
198                                << "Value of LHS was: " << x << "\n"                         \
199                                << "Value of RHS was: " << y << "\n"                         \
200                                << std::setprecision(5) << std::fixed                        \
201                                << "Relative error was: " << relative_error(x, y) << "eps\n" \
202                                << "Tolerance was: " << tol << "eps" << std::endl;           \
203          BOOST_MP_REPORT_SEVERITY(severity);                                                \
204       }                                                                                     \
205    }                                                                                        \
206    BOOST_MP_UNEXPECTED_EXCEPTION_CHECK(severity)
207 
208 #define BOOST_EQUAL_IMP(x, y, severity)                                              \
209    BOOST_MP_TRY                                                                      \
210    {                                                                                 \
211       if (!((x) == (y)))                                                             \
212       {                                                                              \
213          BOOST_MP_REPORT_WHERE << " Failed check for equality: \n"                   \
214                                << std::setprecision(digits_of(x)) << std::scientific \
215                                << "Value of LHS was: " << (x) << "\n"                \
216                                << "Value of RHS was: " << (y) << "\n"                \
217                                << std::setprecision(3) << std::endl;                 \
218          BOOST_MP_REPORT_SEVERITY(severity);                                         \
219       }                                                                              \
220    }                                                                                 \
221    BOOST_MP_UNEXPECTED_EXCEPTION_CHECK(severity)
222 
223 #define BOOST_NE_IMP(x, y, severity)                                                 \
224    BOOST_MP_TRY                                                                      \
225    {                                                                                 \
226       if (!(x != y))                                                                 \
227       {                                                                              \
228          BOOST_MP_REPORT_WHERE << " Failed check for non-equality: \n"               \
229                                << std::setprecision(digits_of(x)) << std::scientific \
230                                << "Value of LHS was: " << x << "\n"                  \
231                                << "Value of RHS was: " << y << "\n"                  \
232                                << std::setprecision(3) << std::endl;                 \
233          BOOST_MP_REPORT_SEVERITY(severity);                                         \
234       }                                                                              \
235    }                                                                                 \
236    BOOST_MP_UNEXPECTED_EXCEPTION_CHECK(severity)
237 
238 #define BOOST_LT_IMP(x, y, severity)                                                 \
239    BOOST_MP_TRY                                                                      \
240    {                                                                                 \
241       if (!(x < y))                                                                  \
242       {                                                                              \
243          BOOST_MP_REPORT_WHERE << " Failed check for less than: \n"                  \
244                                << std::setprecision(digits_of(x)) << std::scientific \
245                                << "Value of LHS was: " << x << "\n"                  \
246                                << "Value of RHS was: " << y << "\n"                  \
247                                << std::setprecision(3) << std::endl;                 \
248          BOOST_MP_REPORT_SEVERITY(severity);                                         \
249       }                                                                              \
250    }                                                                                 \
251    BOOST_MP_UNEXPECTED_EXCEPTION_CHECK(severity)
252 
253 #define BOOST_GT_IMP(x, y, severity)                                                 \
254    BOOST_MP_TRY                                                                      \
255    {                                                                                 \
256       if (!(x > y))                                                                  \
257       {                                                                              \
258          BOOST_MP_REPORT_WHERE << " Failed check for greater than: \n"               \
259                                << std::setprecision(digits_of(x)) << std::scientific \
260                                << "Value of LHS was: " << x << "\n"                  \
261                                << "Value of RHS was: " << y << "\n"                  \
262                                << std::setprecision(3) << std::endl;                 \
263          BOOST_MP_REPORT_SEVERITY(severity);                                         \
264       }                                                                              \
265    }                                                                                 \
266    BOOST_MP_UNEXPECTED_EXCEPTION_CHECK(severity)
267 
268 #define BOOST_LE_IMP(x, y, severity)                                                 \
269    BOOST_MP_TRY                                                                      \
270    {                                                                                 \
271       if (!(x <= y))                                                                 \
272       {                                                                              \
273          BOOST_MP_REPORT_WHERE << " Failed check for less-than-equal-to: \n"         \
274                                << std::setprecision(digits_of(x)) << std::scientific \
275                                << "Value of LHS was: " << x << "\n"                  \
276                                << "Value of RHS was: " << y << "\n"                  \
277                                << std::setprecision(3) << std::endl;                 \
278          BOOST_MP_REPORT_SEVERITY(severity);                                         \
279       }                                                                              \
280    }                                                                                 \
281    BOOST_MP_UNEXPECTED_EXCEPTION_CHECK(severity)
282 
283 #define BOOST_GE_IMP(x, y, severity)                                                 \
284    BOOST_MP_TRY                                                                      \
285    {                                                                                 \
286       if (!(x >= y))                                                                 \
287       {                                                                              \
288          BOOST_MP_REPORT_WHERE << " Failed check for greater-than-equal-to \n"       \
289                                << std::setprecision(digits_of(x)) << std::scientific \
290                                << "Value of LHS was: " << x << "\n"                  \
291                                << "Value of RHS was: " << y << "\n"                  \
292                                << std::setprecision(3) << std::endl;                 \
293          BOOST_MP_REPORT_SEVERITY(severity);                                         \
294       }                                                                              \
295    }                                                                                 \
296    BOOST_MP_UNEXPECTED_EXCEPTION_CHECK(severity)
297 
298 #ifndef BOOST_NO_EXCEPTIONS
299 #define BOOST_MT_CHECK_THROW_IMP(x, E, severity)                                                                   \
300    BOOST_MP_TRY                                                                                                    \
301    {                                                                                                               \
302       x;                                                                                                           \
303       BOOST_MP_REPORT_WHERE << " Expected exception not thrown in expression " << BOOST_STRINGIZE(x) << std::endl; \
304       BOOST_MP_REPORT_SEVERITY(severity);                                                                          \
305    }                                                                                                               \
306    catch (const E&) {}                                                                                             \
307    BOOST_MP_UNEXPECTED_EXCEPTION_CHECK(severity)
308 #else
309 #define BOOST_MT_CHECK_THROW_IMP(x, E, severity)
310 #endif
311 
312 #define BOOST_CHECK_CLOSE(x, y, tol) BOOST_CLOSE_IMP(x, y, ((tol / (100 * epsilon_of(x)))), error_on_fail)
313 #define BOOST_WARN_CLOSE(x, y, tol) BOOST_CLOSE_IMP(x, y, (tol / (100 * epsilon_of(x))), warn_on_fail)
314 #define BOOST_REQUIRE_CLOSE(x, y, tol) BOOST_CLOSE_IMP(x, y, (tol / (100 * epsilon_of(x))), abort_on_fail)
315 
316 #define BOOST_CHECK_CLOSE_FRACTION(x, y, tol) BOOST_CLOSE_IMP(x, y, ((tol / (epsilon_of(x)))), error_on_fail)
317 #define BOOST_WARN_CLOSE_FRACTION(x, y, tol) BOOST_CLOSE_IMP(x, y, (tol / (epsilon_of(x))), warn_on_fail)
318 #define BOOST_REQUIRE_CLOSE_FRACTION(x, y, tol) BOOST_CLOSE_IMP(x, y, (tol / (epsilon_of(x))), abort_on_fail)
319 
320 #define BOOST_CHECK_EQUAL(x, y) BOOST_EQUAL_IMP(x, y, error_on_fail)
321 #define BOOST_WARN_EQUAL(x, y) BOOST_EQUAL_IMP(x, y, warn_on_fail)
322 #define BOOST_REQUIRE_EQUAL(x, y) BOOST_EQUAL_IMP(x, y, abort_on_fail)
323 
324 #define BOOST_CHECK_NE(x, y) BOOST_NE_IMP(x, y, error_on_fail)
325 #define BOOST_WARN_NE(x, y) BOOST_NE_IMP(x, y, warn_on_fail)
326 #define BOOST_REQUIRE_NE(x, y) BOOST_NE_IMP(x, y, abort_on_fail)
327 
328 #define BOOST_CHECK_LT(x, y) BOOST_LT_IMP(x, y, error_on_fail)
329 #define BOOST_WARN_LT(x, y) BOOST_LT_IMP(x, y, warn_on_fail)
330 #define BOOST_REQUIRE_LT(x, y) BOOST_LT_IMP(x, y, abort_on_fail)
331 
332 #define BOOST_CHECK_GT(x, y) BOOST_GT_IMP(x, y, error_on_fail)
333 #define BOOST_WARN_GT(x, y) BOOST_GT_IMP(x, y, warn_on_fail)
334 #define BOOST_REQUIRE_GT(x, y) BOOST_GT_IMP(x, y, abort_on_fail)
335 
336 #define BOOST_CHECK_LE(x, y) BOOST_LE_IMP(x, y, error_on_fail)
337 #define BOOST_WARN_LE(x, y) BOOST_LE_IMP(x, y, warn_on_fail)
338 #define BOOST_REQUIRE_LE(x, y) BOOST_LE_IMP(x, y, abort_on_fail)
339 
340 #define BOOST_CHECK_GE(x, y) BOOST_GE_IMP(x, y, error_on_fail)
341 #define BOOST_WARN_GE(x, y) BOOST_GE_IMP(x, y, warn_on_fail)
342 #define BOOST_REQUIRE_GE(x, y) BOOST_GE_IMP(x, y, abort_on_fail)
343 
344 #define BOOST_CHECK_THROW(x, E) BOOST_MT_CHECK_THROW_IMP(x, E, error_on_fail)
345 #define BOOST_WARN_THROW(x, E) BOOST_MT_CHECK_THROW_IMP(x, E, warn_on_fail)
346 #define BOOST_REQUIRE_THROW(x, E) BOOST_MT_CHECK_THROW_IMP(x, E, abort_on_fail)
347 
348 #endif
349