1 // Copyright (c) 2017-2021, Lawrence Livermore National Security, LLC and
2 // other Axom Project Developers. See the top-level LICENSE file for details.
3 //
4 // SPDX-License-Identifier: (BSD-3-Clause)
5
6 /*!
7 *
8 * \file Utilities.hpp
9 *
10 * \brief Header file containing utility functions.
11 *
12 */
13
14 #ifndef AXOM_UTILITIES_HPP_
15 #define AXOM_UTILITIES_HPP_
16
17 #include "axom/config.hpp" // for compile-time definitions
18 #include "axom/core/Types.hpp"
19 #include "axom/core/Macros.hpp" // for AXOM_STATIC_ASSERT
20
21 #include <cassert> // for assert()
22 #include <cmath> // for log2()
23
24 #include <random> // for random number generator
25 #include <type_traits> // for std::is_floating_point()
26
27 namespace axom
28 {
29 namespace utilities
30 {
31 /*!
32 * \brief Gracefully aborts the application
33 */
34 void processAbort();
35
36 /*!
37 * \brief Returns the absolute value of x.
38 * \accelerated
39 * \param [in] x value whose absolute value is computed.
40 * \return abs(x) the absolute value of x.
41 */
42 template <typename T>
abs(const T & x)43 inline AXOM_HOST_DEVICE T abs(const T& x)
44 {
45 return (x < T(0)) ? -x : x;
46 }
47
48 /*!
49 * \brief Returns the max value of x and y.
50 * \accelerated
51 * \param [in] x the first value to check.
52 * \param [in] y the second value to check.
53 * \return max(x, y) the max value of x and y.
54 */
55 template <typename T>
max(const T & x,const T & y)56 inline AXOM_HOST_DEVICE const T& max(const T& x, const T& y)
57 {
58 return (y < x) ? x : y;
59 }
60
61 /*!
62 * \brief Returns the min value of x and y.
63 * \accelerated
64 * \param [in] x the first value to check.
65 * \param [in] y the second value to check.
66 * \return min(x, y) the min value of x and y.
67 */
68 template <typename T>
min(const T & x,const T & y)69 inline AXOM_HOST_DEVICE const T& min(const T& x, const T& y)
70 {
71 return (y < x) ? y : x;
72 }
73
74 /*!
75 * \brief Swaps the values of a, b.
76 * \accelerated
77 * \param [in,out] a 1st object to swap.
78 * \param [in,out] b 2nd object to swap.
79 */
80 template <typename T>
swap(T & a,T & b)81 inline AXOM_HOST_DEVICE void swap(T& a, T& b)
82 {
83 T tmp = a;
84 a = b;
85 b = tmp;
86 }
87
88 /*!
89 * \brief Returns the base 2 logarithm of the input.
90 * \param [in] val The input value
91 */
92 template <typename T>
log2(T & val)93 inline T log2(T& val)
94 {
95 return static_cast<T>(std::log2(val));
96 }
97
98 /*!
99 * \brief Clamps an input value to a given range.
100 * \accelerated
101 * \param [in] val The value to clamp.
102 * \param [in] lower The lower range.
103 * \param [in] upper The upper range.
104 * \return The clamped value.
105 * \pre lower <= upper
106 * \post lower <= returned value <= upper.
107 */
108 template <typename T>
clampVal(T val,T lower,T upper)109 inline AXOM_HOST_DEVICE T clampVal(T val, T lower, T upper)
110 {
111 return (val < lower) ? lower : (val > upper) ? upper : val;
112 }
113
114 /*!
115 * \brief Clamps the upper range on an input value
116 * \accelerated
117 * \param [in] val The value to clamp
118 * \param [in] upper The upper range
119 * \return upper if val > upper, else val
120 * \post returned value is less than or equal to upper
121 */
122 template <typename T>
clampUpper(T val,T upper)123 inline AXOM_HOST_DEVICE T clampUpper(T val, T upper)
124 {
125 return val > upper ? upper : val;
126 }
127
128 /*!
129 * \brief Clamps the lower range on an input value
130 * \accelerated
131 * \param [in] val The value to clamp
132 * \param [in] lower The lower range
133 * \return lower if val < lower, else val
134 * \post returned value is greater than or equal to lower
135 */
136 template <typename T>
clampLower(T val,T lower)137 inline AXOM_HOST_DEVICE T clampLower(T val, T lower)
138 {
139 return val < lower ? lower : val;
140 }
141
142 /*!
143 * \brief Returns a random real number within the specified interval
144 *
145 * \param [in] a the interval's lower bound
146 * \param [in] b the interval's upper bound
147 * \return r the random real number within the specified interval.
148 *
149 * \tparam T a built-in floating point type, e.g., double, float, long double.
150 *
151 * \note Consecutive calls to this method will generate a non-deterministic
152 * sequence of numbers.
153 *
154 * \pre a < b
155 * \post a <= r < b
156 */
157 template <typename T>
random_real(const T & a,const T & b)158 inline T random_real(const T& a, const T& b)
159 {
160 AXOM_STATIC_ASSERT(std::is_floating_point<T>::value);
161 assert((a < b) && "invalid bounds, a < b");
162
163 static std::random_device rd;
164 static std::mt19937_64 mt(rd());
165 static std::uniform_real_distribution<T> dist(0.0, 1.0);
166
167 T temp = dist(mt);
168 return temp * (b - a) + a;
169 }
170
171 /*!
172 * \brief Returns a random real number within the specified interval given
173 * the bounds of the interval and a seed value for the underlying random
174 * number generator.
175 *
176 * \param [in] a the interval's lower bound
177 * \param [in] b the interval's upper bound
178 * \param [in] seed user-supplied seed for the random number generator
179 * \return r the random real number within the specified interval.
180 *
181 * \tparam T a built-in floating type, e.g., double, float, long double.
182 *
183 * \note Consecutive calls to this method will generate a deterministic
184 * sequence of numbers.
185 *
186 * \pre a < b
187 * \post a <= r < b
188 */
189 template <typename T>
random_real(const T & a,const T & b,unsigned int seed)190 inline T random_real(const T& a, const T& b, unsigned int seed)
191 {
192 AXOM_STATIC_ASSERT(std::is_floating_point<T>::value);
193 assert((a < b) && "invalid bounds, a < b");
194
195 static std::mt19937_64 mt(seed);
196 static std::uniform_real_distribution<T> dist(0.0, 1.0);
197
198 double temp = dist(mt);
199 return temp * (b - a) + a;
200 }
201
202 /*!
203 * \brief Tests the endianness of the system.
204 * \return True, if the system is little endian, false otherwise.
205 */
isLittleEndian()206 inline bool isLittleEndian()
207 {
208 // Note: Endianness test adapted from: https://stackoverflow.com/a/2103095
209
210 enum
211 {
212 O32_LITTLE_ENDIAN = 0x03020100ul,
213 O32_BIG_ENDIAN = 0x00010203ul,
214 O32_PDP_ENDIAN = 0x01000302ul
215 };
216
217 const union
218 {
219 axom::uint8 raw[4];
220 axom::uint32 value;
221 } host_order = {{0, 1, 2, 3}};
222
223 return host_order.value == O32_LITTLE_ENDIAN;
224 }
225
226 /*!
227 * \brief Swaps the endianness of the input value.
228 * \param [in] val The input value.
229 * \return The value with endianness swapped.
230 * \note Assumes endianness is either little or big (not PDP).
231 * \pre T is a native arithmetic type (i.e. integral or floating point).
232 * \pre sizeof(T) must be 2, 4, or 8 bytes.
233 */
234 template <typename T>
swapEndian(T val)235 T swapEndian(T val)
236 {
237 const int NBYTES = sizeof(T);
238
239 AXOM_STATIC_ASSERT_MSG(
240 NBYTES == 2 || NBYTES == 4 || NBYTES == 8,
241 "swapEndian only valid for types of size 2, 4 or 8 bytes.");
242
243 AXOM_STATIC_ASSERT_MSG(std::is_arithmetic<T>::value,
244 "swapEndian only valid for native arithmetic types");
245
246 union
247 {
248 axom::uint8 raw[NBYTES];
249 T val;
250 } swp;
251
252 axom::uint8* src = reinterpret_cast<axom::uint8*>(&val);
253
254 // Reverse the bytes
255 for(int i = 0; i < NBYTES; ++i)
256 {
257 swp.raw[i] = src[(NBYTES - 1) - i];
258 }
259
260 return swp.val;
261 }
262
263 /*!
264 * \brief Fuzzy comparison of two real valued quantities.
265 * \accelerated
266 * \param [in] a The first real valued quantities we are comparing.
267 * \param [in] b The second real valued quantities we are comparing.
268 * \param [in] thresh The threshold of the fuzzy comparison. Default is 1.0e-8.
269 * \return True if the absolute value of the difference is less than thresh and
270 * false otherwise.
271 */
272 template <typename RealType>
isNearlyEqual(RealType a,RealType b,RealType thresh=1.0e-8)273 inline AXOM_HOST_DEVICE bool isNearlyEqual(RealType a,
274 RealType b,
275 RealType thresh = 1.0e-8)
276 {
277 return abs(a - b) <= thresh;
278 }
279
280 /*!
281 * \brief Fuzzy comparison of two real valued quantities.
282 * \accelerated
283 * \param [in] a The first real valued quantities we are comparing.
284 * \param [in] b The second real valued quantities we are comparing.
285 * \param [in] relThresh The relative threshold of the fuzzy comparison.
286 * Default is 1.0e-6.
287 * \param [in] absThresh The absolute threshold of the fuzzy comparison.
288 * Default is 1.0e-8.
289 * \return True if the absolute value of the difference is less than the sum of
290 * absThresh and the relative difference (relThresh times the absolute max of
291 * a and b).
292 */
293 template <typename RealType>
isNearlyEqualRelative(RealType a,RealType b,RealType relThresh=1.0e-6,RealType absThresh=1.0e-8)294 inline AXOM_HOST_DEVICE bool isNearlyEqualRelative(RealType a,
295 RealType b,
296 RealType relThresh = 1.0e-6,
297 RealType absThresh = 1.0e-8)
298 {
299 RealType maxFabs = max(abs(a), abs(b));
300 return abs(a - b) <= (maxFabs * relThresh + absThresh);
301
302 // Equation from Real-Time Collision Detection book --
303 // http://realtimecollisiondetection.net/pubs/Tolerances/
304 // Note: If we use this, we must update the doxygen
305 // return abs(a-b) <= max(absThresh, relThresh * maxFabs );
306 }
307
308 /*!
309 * \brief Compares std::vector< T > in lexicographic order
310 *
311 * \note T must support > and < operators.
312 */
313 template <typename T>
314 class LexiComparator
315 {
316 public:
operator ()(const std::vector<T> & v1,const std::vector<T> & v2) const317 bool operator()(const std::vector<T>& v1, const std::vector<T>& v2) const
318 {
319 size_t s1 = v1.size();
320 size_t s2 = v2.size();
321 size_t size = std::min(s1, s2);
322 size_t i = 0;
323
324 while(i < size)
325 {
326 if(v1[i] < v2[i])
327 {
328 return true;
329 }
330 else if(v1[i] > v2[i])
331 {
332 return false;
333 }
334
335 ++i;
336 }
337
338 if(s1 < s2)
339 {
340 return true;
341 }
342
343 return false;
344 }
345 };
346
347 } // namespace utilities
348 } // namespace axom
349
350 #endif // AXOM_UTILITIES_HPP_
351