LCOV - code coverage report
Current view: top level - include/utils - utility.h (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4229 (6a9aeb) with base 727f46 Lines: 46 66 69.7 %
Date: 2025-08-19 19:27:09 Functions: 96 187 51.3 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // The libMesh Finite Element Library.
       2             : // Copyright (C) 2002-2025 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
       3             : 
       4             : // This library is free software; you can redistribute it and/or
       5             : // modify it under the terms of the GNU Lesser General Public
       6             : // License as published by the Free Software Foundation; either
       7             : // version 2.1 of the License, or (at your option) any later version.
       8             : 
       9             : // This library is distributed in the hope that it will be useful,
      10             : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12             : // Lesser General Public License for more details.
      13             : 
      14             : // You should have received a copy of the GNU Lesser General Public
      15             : // License along with this library; if not, write to the Free Software
      16             : // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
      17             : 
      18             : 
      19             : #ifndef LIBMESH_UTILITY_H
      20             : #define LIBMESH_UTILITY_H
      21             : 
      22             : // LibMesh includes
      23             : #include "libmesh/libmesh_common.h" // for Real
      24             : 
      25             : // C++ includes
      26             : #include <string>
      27             : #include <vector>
      28             : #include <algorithm> // is_sorted, lower_bound
      29             : #include <memory> // unique_ptr
      30             : 
      31             : namespace libMesh
      32             : {
      33             : 
      34             : /**
      35             :  * Encapsulates the common "get value from map, otherwise error"
      36             :  * idiom, which is similar to calling map.at(), but gives a more
      37             :  * useful error message with a line number.
      38             :  */
      39             : #define libmesh_map_find(map, key) libMesh::Utility::map_find((map), (key), __FILE__, __LINE__)
      40             : 
      41             : /**
      42             :  * Encapsulates the common "get value from vector, otherwise error"
      43             :  * idiom, which is similar to calling vec.at(), but gives a more
      44             :  * useful error message with a line number.
      45             :  */
      46             : #define libmesh_vector_at(vec, idx) libMesh::Utility::vector_at((vec), (idx), __FILE__, __LINE__)
      47             : 
      48             : // ------------------------------------------------------------
      49             : // The Utility namespace is for functions
      50             : // which are useful but don't necessarily belong anywhere else.
      51             : 
      52             : namespace Utility
      53             : {
      54             : 
      55             : /**
      56             :  * \returns A string containing information about the system you are
      57             :  * running on.
      58             :  */
      59             : std::string system_info();
      60             : 
      61             : /**
      62             :  * Helper struct for enabling template metaprogramming/SFINAE.
      63             :  */
      64             : template <typename T>
      65             : class is_streamable
      66             : {
      67             :   template <typename U> // must be template to get SFINAE fall-through...
      68             :   static auto test(const U* u) -> decltype(std::cout << *u);
      69             : 
      70             :   static auto test(...) -> std::false_type;
      71             : 
      72             : public:
      73             :   enum { value = !std::is_same<decltype(test((T*)0)), std::false_type>::value };
      74             : };
      75             : 
      76             : /**
      77             :  * -Wdangling-reference was nowhere *near* ready to add to -Wall in
      78             :  *  gcc 13.  It's been moved to -Wextra, but we use that too.  :-)
      79             :  *
      80             :  *  See e.g. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=109642
      81             :  *
      82             :  *  Our map_find functions trigger it.
      83             :  */
      84             : 
      85             : #if defined(__GNUC__) && !defined(__INTEL_COMPILER) && !defined(__clang__)
      86             : #if (__GNUC__ > 12)
      87             : #pragma GCC diagnostic push
      88             : #pragma GCC diagnostic ignored "-Wdangling-reference"
      89             : #endif
      90             : #endif
      91             : 
      92             : /**
      93             :  * This function should not be called directly (although it can be),
      94             :  * instead see the libmesh_map_find() macro.
      95             :  *
      96             :  * Calls find(key), and checks the result against end(). Returns the
      97             :  * corresponding value if found, throws an error otherwise. Templated
      98             :  * on the type of map, so this will work with both std::map and
      99             :  * std::unordered_map.
     100             :  */
     101             : template<typename Map, typename Key,
     102             :          typename std::enable_if<!is_streamable<Key>::value, Key>::type* = nullptr>
     103             : inline
     104             : typename Map::mapped_type &
     105      316664 : map_find(Map & map,
     106             :          const Key & key,
     107             :          const char * filename,
     108             :          int line_number)
     109             : {
     110      205880 :   auto it = map.find(key);
     111      316664 :   libmesh_error_msg_if(it == map.end(),
     112             :                        "map_find() error: key not found in file "
     113             :                        << filename << " on line " << line_number);
     114      316664 :   return it->second;
     115             : }
     116             : 
     117             : /**
     118             :  * A version of the function above that works for const objects.
     119             :  */
     120             : template<typename Map, typename Key,
     121             :          typename std::enable_if<!is_streamable<Key>::value, Key>::type* = nullptr>
     122             : inline
     123             : const typename Map::mapped_type &
     124        8736 : map_find(const Map & map,
     125             :          const Key & key,
     126             :          const char * filename,
     127             :          int line_number)
     128             : {
     129         684 :   auto it = map.find(key);
     130        8736 :   libmesh_error_msg_if(it == map.end(),
     131             :                        "map_find() error: key not found in file "
     132             :                        << filename << " on line " << line_number);
     133        8736 :   return it->second;
     134             : }
     135             : 
     136             : /**
     137             :  * A version of the map_find() utility which can only be used if
     138             :  * the map key is printable via std::stream.
     139             :  */
     140             : template<typename Map, typename Key,
     141             :          typename std::enable_if<is_streamable<Key>::value, Key>::type* = nullptr>
     142             : inline
     143             : typename Map::mapped_type &
     144    29520989 : map_find(Map & map,
     145             :          const Key & key,
     146             :          const char * filename,
     147             :          int line_number)
     148             : {
     149    19673053 :   auto it = map.find(key);
     150    29520989 :   libmesh_error_msg_if(it == map.end(),
     151             :                        "map_find() error: key \"" << key << "\" not found in file "
     152             :                        << filename << " on line " << line_number);
     153    29520989 :   return it->second;
     154             : }
     155             : 
     156             : /**
     157             :  * A version of the function above that works for const objects.
     158             :  */
     159             : template<typename Map, typename Key,
     160             :          typename std::enable_if<is_streamable<Key>::value, Key>::type* = nullptr>
     161             : inline
     162             : const typename Map::mapped_type &
     163    59730078 : map_find(const Map & map,
     164             :          const Key & key,
     165             :          const char * filename,
     166             :          int line_number)
     167             : {
     168     4564172 :   auto it = map.find(key);
     169    59730504 :   libmesh_error_msg_if(it == map.end(),
     170             :                        "map_find() error: key \"" << key << "\" not found in file "
     171             :                        << filename << " on line " << line_number);
     172    59729865 :   return it->second;
     173             : }
     174             : 
     175             : // The map_find functions are our only dangling-reference false positives
     176             : 
     177             : #if defined(__GNUC__) && !defined(__INTEL_COMPILER) && !defined(__clang__)
     178             : #if (__GNUC__ > 12)
     179             : #pragma GCC diagnostic pop
     180             : #endif
     181             : #endif
     182             : 
     183             : 
     184             : /**
     185             :  * A replacement for std::vector::at(i) which is meant to be used with
     186             :  * a macro, and, unlike at(), gives a proper line number and useful
     187             :  * error message when the index is past the end.
     188             :  */
     189             : template<typename Vector>
     190             : inline
     191             : typename Vector::reference &
     192     4623480 : vector_at(Vector & vec,
     193             :           typename Vector::size_type i,
     194             :           const char * filename,
     195             :           int line_number)
     196             : {
     197     5008770 :   libmesh_error_msg_if(i >= vec.size(),
     198             :                        "vec_at() error: Index " << i <<
     199             :                        " past end of vector in file " << filename <<
     200             :                        " on line " << line_number);
     201     4623480 :   return vec[i];
     202             : }
     203             : 
     204             : /**
     205             :  * Same as above, but for const inputs.
     206             :  */
     207             : template<typename Vector>
     208             : inline
     209             : typename Vector::const_reference &
     210       33237 : vector_at(const Vector & vec,
     211             :           typename Vector::size_type i,
     212             :           const char * filename,
     213             :           int line_number)
     214             : {
     215       33237 :   libmesh_error_msg_if(i >= vec.size(),
     216             :                        "vec_at() error: Index " << i <<
     217             :                        " past end of vector in file " << filename <<
     218             :                        " on line " << line_number);
     219       33237 :   return vec[i];
     220             : }
     221             : 
     222             : 
     223             : #ifdef LIBMESH_ENABLE_DEPRECATED
     224             : /**
     225             :  * Utility::iota was created back when std::iota was just an
     226             :  * SGI STL extension.
     227             :  */
     228             : template <typename ForwardIter, typename T>
     229             : void iota (ForwardIter first, ForwardIter last, T value)
     230             : {
     231             :   // Use std::iota instead!
     232             :   libmesh_deprecated();
     233             : 
     234             :   while (first != last)
     235             :     {
     236             :       *first = value++;
     237             :       ++first;
     238             :     }
     239             : }
     240             : 
     241             : 
     242             : /**
     243             :  * Utility::is_sorted was created back when std::is_sorted was just an
     244             :  * SGI STL extension.
     245             :  */
     246             : template<class InputIterator >
     247             : bool is_sorted(InputIterator first, InputIterator last)
     248             : {
     249             :   libmesh_deprecated();
     250             :   return std::is_sorted(first, last);
     251             : }
     252             : #endif // LIBMESH_ENABLE_DEPRECATED
     253             : 
     254             : 
     255             : /**
     256             :  * The STL provides \p std::binary_search() which returns \p true or
     257             :  * \p false depending on whether the searched-for value is found.  In
     258             :  * contrast, Utility::binary_find() uses a std::lower_bound() based
     259             :  * search on a sorted range to find the required value.
     260             :  *
     261             :  * \returns An iterator to the searched-for element, or "last" if the
     262             :  * element is not found.
     263             :  */
     264             : template<class ForwardIterator, class T>
     265         572 : ForwardIterator binary_find(ForwardIterator first, ForwardIterator last, const T & value)
     266             : {
     267         572 :   ForwardIterator it = std::lower_bound(first, last, value);
     268         572 :   return (it == last || value < *it) ? last : it;
     269             : }
     270             : 
     271             : /**
     272             :  * As above, but takes a custom comparison object.
     273             :  */
     274             : template<class ForwardIterator, class T, class Compare>
     275             : ForwardIterator binary_find(ForwardIterator first, ForwardIterator last, const T & value, Compare comp)
     276             : {
     277             :   ForwardIterator it = std::lower_bound(first, last, value, comp);
     278             :   return (it == last || comp(value,*it)) ? last : it;
     279             : }
     280             : 
     281             : 
     282             : /**
     283             :  * An efficient template instantiation for raising
     284             :  * to an arbitrary integer power.
     285             :  */
     286             : template <int N, typename T>
     287             : struct do_pow {
     288   147958473 :   static inline T apply (const T & x)
     289             :   {
     290             :     libmesh_assert(N>1);
     291             : 
     292             :     if (N%2) // odd exponent
     293     3765234 :       return x * do_pow<N-1,T>::apply(x);
     294             : 
     295   147882656 :     const T xNover2 = do_pow<N/2,T>::apply(x);
     296             : 
     297  7240791008 :     return xNover2*xNover2;
     298             :   }
     299             : };
     300             : 
     301             : // An efficient compiler would distill N=6 down to 3
     302             : // multiplications, but an inefficient one (or a complicated
     303             : // T::operator*) might do worse, so we'll specialize here.
     304             : template <typename T>
     305             : struct do_pow<6,T> {
     306         289 :   static inline T apply (const T & x)
     307             :   {
     308        3179 :     const T x2 = x*x,
     309        3179 :       x4 = x2*x2;
     310             : 
     311        3179 :     return x4*x2;
     312             :   }
     313             : };
     314             : 
     315             : template <typename T>
     316             : struct do_pow<1,T> {
     317    59113157 :   static inline T apply (const T & x) { return x; }
     318             : };
     319             : 
     320             : template <typename T>
     321             : struct do_pow<0,T> {
     322             :   static inline T apply (const T &) { return 1; }
     323             : };
     324             : 
     325             : 
     326             : template <int N, typename T>
     327             : inline
     328   598789643 : T pow(const T & x)
     329             : {
     330   598789643 :   return do_pow<N,T>::apply(x);
     331             : }
     332             : 
     333             : /**
     334             :  * A simple implementation of the factorial.
     335             :  */
     336             : inline
     337             : unsigned int factorial(unsigned int n)
     338             : {
     339             : 
     340             :   unsigned int factorial_n = 1;
     341             : 
     342             :   if (n==0)
     343             :     return factorial_n;
     344             : 
     345             :   for (unsigned int i=1; i<n; i++)
     346             :     factorial_n *= i+1;
     347             : 
     348             :   return factorial_n;
     349             : }
     350             : 
     351             : 
     352             : // Simple function to compute "n choose k", aka the binomial coefficient.
     353             : template <typename T>
     354           0 : T binomial(T n, T k)
     355             : {
     356           0 :   T ret = 1;
     357             : 
     358             :   // Binomial function is "symmetric" in k, C(n, k) = C(n, n-k).
     359           0 :   if (k > n - k)
     360           0 :     k = n - k;
     361             : 
     362             :   // Compute n * (n-1) * ... * (n-k+1) / (k * (k-1) * ... * 1)
     363           0 :   for (T i = 0; i < k; ++i)
     364             :     {
     365           0 :       ret *= (n - i);
     366           0 :       ret /= (i + 1);
     367             :     }
     368             : 
     369           0 :   return ret;
     370             : }
     371             : 
     372             : 
     373             : /**
     374             :  * A convenient method to truly empty a vector using the "swap trick"
     375             :  */
     376             : template <typename T>
     377        9912 : void deallocate (std::vector<T> & vec)
     378             : {
     379        9960 :   std::vector<T>().swap(vec);
     380        9912 : }
     381             : 
     382             : 
     383             : // When looking for a complicated suffix to a filename, we only want
     384             : // to consider the base name, without any preceding path name.
     385             : // "/tmp/foo.e25ad0/mesh.msh" is not ExodusII.
     386             : std::string_view basename_of(const std::string & fullname);
     387             : 
     388             : 
     389             : /**
     390             :  * Look for a substring within a string.
     391             :  */
     392             : bool contains(std::string_view superstring,
     393             :               std::string_view substring);
     394             : 
     395             : 
     396             : /**
     397             :  * Look for a substring at the very end of a string.
     398             :  */
     399             : bool ends_with(std::string_view superstring,
     400             :                std::string_view suffix);
     401             : 
     402             : 
     403             : // Utility functions useful when dealing with complex numbers.
     404             : 
     405             : #ifdef LIBMESH_USE_COMPLEX_NUMBERS
     406             : 
     407             : /**
     408             :  * \returns For \p r_o_c = 0 the filename for output of the real part
     409             :  * of complex data, and for  \p r_o_c = 1 the filename for the imaginary
     410             :  * part.
     411             :  */
     412             : std::string complex_filename (std::string basename,
     413             :                               unsigned int r_o_c=0);
     414             : 
     415             : /**
     416             :  * Prepare complex data for writing.
     417             :  */
     418             : void prepare_complex_data (const std::vector<Complex> & source,
     419             :                            std::vector<Real> & real_part,
     420             :                            std::vector<Real> & imag_part);
     421             : 
     422             : #endif // #ifdef LIBMESH_USE_COMPLEX_NUMBERS
     423             : 
     424             : 
     425             : /**
     426             :  * Create a directory.
     427             :  */
     428             : int mkdir(const char* pathname);
     429             : 
     430             : 
     431             : /**
     432             :  * Create an unzipped copy of a bz2 or xz file, returning the name of
     433             :  * the now-unzipped file that can be directly opened.
     434             :  *
     435             :  * This is a hack because we don't have a neat bz2/xz equivalent to
     436             :  * gzstreams.
     437             :  */
     438             : std::string unzip_file (std::string_view name);
     439             : 
     440             : 
     441             : /**
     442             :  * This Functor simply takes an object and reverses its byte
     443             :  * representation.  This is useful for changing endian-ness
     444             :  * for file IO.  This class has been tested on x86 architectures
     445             :  * with 4-byte words.
     446             :  *
     447             :  *
     448             :  */
     449             : class ReverseBytes
     450             : {
     451             : public:
     452             : 
     453             :   /**
     454             :    * Constructor.  Takes a bool, determines if we will actually
     455             :    * do byte reversing.
     456             :    */
     457             :   explicit
     458             :   ReverseBytes (const bool dr);
     459             : 
     460             :   /**
     461             :    * Functor.  Takes the data to reverse and performs the
     462             :    * byte-ordering reversal.
     463             :    */
     464             :   template <typename T>
     465             :   T operator () (T & data) const;
     466             : 
     467             : private:
     468             : 
     469             :   /**
     470             :    * \returns The value of the reverse flag.
     471             :    */
     472       42185 :   bool reverse () const { return _do_reverse; }
     473             : 
     474             :   /**
     475             :    * flag
     476             :    */
     477             :   const bool _do_reverse;
     478             : };
     479             : 
     480             : 
     481             : 
     482             : // ReverseBytes inline members
     483             : inline
     484          11 : ReverseBytes::ReverseBytes (const bool rb) :
     485          11 :   _do_reverse (rb)
     486           1 : {}
     487             : 
     488             : 
     489             : template <typename T>
     490             : inline
     491       42185 : T ReverseBytes::operator() (T & data) const
     492             : {
     493             :   // Possibly reverse the byte ordering
     494       42185 :   if (this->reverse())
     495             :     {
     496           0 :       unsigned char * b = (unsigned char *) &data;
     497             : 
     498           0 :       int i=0;
     499           0 :       int j=(sizeof(T) - 1);
     500             : 
     501           0 :       while (i < j)
     502             :         {
     503           0 :           std::swap (b[i], b[j]);
     504           0 :           i++; j--;
     505             :         }
     506             :     }
     507             : 
     508       43464 :   return data;
     509             : }
     510             : 
     511             : 
     512             : 
     513             : /**
     514             :  * Struct which defines a custom comparison object that
     515             :  * can be used with std::sets of std::unique_ptrs
     516             :  */
     517             : struct CompareUnderlying
     518             : {
     519             :   /**
     520             :    * As of C++14, std::set::find() can be a templated overload.
     521             :    * https://en.cppreference.com/w/cpp/container/set/find
     522             :    * We enable this by defining is_transparent as a type.
     523             :    */
     524             :   using is_transparent = void;
     525             : 
     526             :   /**
     527             :    * This is already what the default operator< comparison for std::unique_ptrs does,
     528             :    * we are not adding anything here.
     529             :    */
     530             :   template <class T>
     531           0 :   bool operator()(const std::unique_ptr<T> & a, const std::unique_ptr<T> & b) const
     532             :   {
     533           0 :     return a.get() < b.get();
     534             :   }
     535             : 
     536             :   /**
     537             :    * operator< comparison when rhs is a dumb pointer
     538             :    */
     539             :   template <class T>
     540           0 :   bool operator()(const std::unique_ptr<T> & a, const T * const & b) const
     541             :   {
     542           0 :     return a.get() < b;
     543             :   }
     544             : 
     545             :   /**
     546             :    * operator< comparison when lhs is a dumb pointer
     547             :    */
     548             :   template <class T>
     549           0 :   bool operator()(const T * const & a, const std::unique_ptr<T> & b) const
     550             :   {
     551           0 :     return a < b.get();
     552             :   }
     553             : };
     554             : 
     555             : } // namespace Utility
     556             : 
     557             : } // namespace libMesh
     558             : 
     559             : #endif // LIBMESH_UTILITY_H

Generated by: LCOV version 1.14