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