file src/util_functions.cpp

[No description available] More…

Namespaces

Name
Gambit
TODO: see if we can use this one:
Gambit::Utils

Detailed Description

Author:

Date:

  • 2013 Apr, July, Aug, Sep
  • 2014 Mar
  • 2013 May, June, July

General small utility functions.


Authors (add name and date if you modify):


Source code

//   GAMBIT: Global and Modular BSM Inference Tool
//   *********************************************
///  \file
///
///  General small utility functions.
///
///  *********************************************
///
///  Authors (add name and date if you modify):
///
///  \author Pat Scott
///          (patscott@physics.mcgill.ca)
///  \date 2013 Apr, July, Aug, Sep
///  \date 2014 Mar
///
///  \author Ben Farmer
///          (benjamin.farmer@monash.edu.au)
///  \date 2013 May, June, July
///
///  *********************************************

/// Standard libraries
#include <cstring>
#include <chrono>  // chrono::system_clock
#include <ctime>   // localtime
#include <cctype>  // ::tolower function
#include <sstream> // stringstream
#include <string>  // string
#include <regex>   // regular expressions

/// POSIX filesystem libraries
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <libgen.h>
#include <unistd.h>

/// Gambit
#include "gambit/Utils/util_functions.hpp"
#include "gambit/cmake/cmake_variables.hpp"
#include "gambit/Utils/mpiwrapper.hpp"

/// Boost
#include <boost/algorithm/string/iter_find.hpp>
#include <boost/algorithm/string/finder.hpp>
#include <boost/algorithm/string/replace.hpp>

namespace Gambit
{

  namespace Utils
  {

    const char* whitespaces[] = {" ", "\t", "\n", "\f", "\r"};

    /// Return the path to the run-specific scratch directory
    /// Don't call this from a destructor, as the internal static str may have already been destroyed.
    const str& runtime_scratch()
    {
      static const str path = construct_runtime_scratch();
      return path;
    }

    /// Construct the path to the run-specific scratch directory
    /// This version is safe to call from a destructor.
    str construct_runtime_scratch(bool
    #ifdef WITH_MPI
      fail_on_mpi_uninitialised
    #endif
    )
    {
      str master_procID;
      #ifdef WITH_MPI
        if (GMPI::Is_initialized() and not GMPI::Is_finalized())
        {
          master_procID = "/master_process_" + std::to_string(GMPI::Comm().MasterPID());
        }
        else
        {
          if (fail_on_mpi_uninitialised)
            utils_error().raise(LOCAL_INFO, "Tried to call construct_runtime_scratch without MPI initialised!");
          master_procID = "/unattached_MPI_process_" + std::to_string(getpid());
        }
      #else
        master_procID = "/master_process_" + std::to_string(getpid());
      #endif
      return ensure_path_exists(GAMBIT_DIR "/scratch/run_time/machine_" + std::to_string(gethostid()) + master_procID + "/");
    }

    /// Convert all instances of "p" in a string to "."
    str p2dot(str s)
    {
      boost::replace_all(s, "p", ".");
      return s;
    }

    /// Split a string into a vector of strings using a delimiter,
    /// and remove any whitespace around the delimiters.
    std::vector<str> delimiterSplit(str s, str delim)
    {
      std::vector<str> vec;
      // Get rid of any whitespace around the delimiters
      std::regex rgx1("\\s+"+delim), rgx2(delim+"\\s+");
      s = std::regex_replace(s, rgx1, delim);
      s = std::regex_replace(s, rgx2, delim);
      if (s == "") return vec;
      // Split up the list of versions by the delimiters
      boost::split(vec, s, boost::is_any_of(delim), boost::token_compress_on);
      return vec;
    }

    /// Strips namespace from the start of a string, or after "const".
    str strip_leading_namespace(str s, str ns)
    {
      std::regex expression("(^|[\\s\\*\\&\\(\\,\\[])"+ns+"::");
      s = std::regex_replace(s, expression, str("$1"));
      return s;
    }

    /// Replaces a namespace at the start of a string, or after "const".
    str replace_leading_namespace(str s, str ns, str ns_new)
    {
      std::regex expression("(^|[\\s\\*\\&\\(\\,\\[])"+ns+"::");
      s = std::regex_replace(s, expression, str("$1")+ns_new+"::");
      return s;
    }

    /// Strips all whitespaces from a string, but re-inserts a single regular space after "const".
    void strip_whitespace_except_after_const(str &s)
    {
      str tempstr("__TEMP__"), empty(""), constdec2("const ");
      std::regex constdec1("const\\s+"), temp(tempstr), whitespace("\\s+");
      s = std::regex_replace(s, constdec1, tempstr);
      s = std::regex_replace(s, whitespace, empty);
      s = std::regex_replace(s, temp, constdec2);
    }

    /// Strips leading and/or trailing parentheses from a string.
    void strip_parentheses(str &s)
    {
      if (s.at(0) == '(')       s = s.substr(1, s.size());
      if (*s.rbegin() == ')')   s = s.substr(0, s.size()-1);
    }

    /// Test if a set of str,str pairs contains any entry with first element matching a given string
    bool sspairset_contains(const str& el, const std::set<std::pair<str,str>>& set)
    {
      for (std::pair<str,str> x : set) { if (x.first == el) return true; }
      return false;
    }

    /// Tests if a set of str,str pairs contains an entry matching two given strings
    bool sspairset_contains(const str& el1, const str& el2, const std::set<std::pair<str,str>>& set)
    {
      return sspairset_contains(std::pair<str,str>(el1, el2), set);
    }

    /// Tests if a set of str,str pairs contains an entry matching a given pair
    bool sspairset_contains(const sspair& quantity, const std::set<sspair>& set)
    {
      return std::find(set.begin(), set.end(), quantity) != set.end();
    }

    /// Created a std::string of a specified length.
    str str_fixed_len(str s, int len)
    {
      int oldlen = s.length();
      if (oldlen > len)
      {
        return s.substr(0,len-1);
      }
      else if (oldlen < len)
      {
        s.append(len-oldlen,' ');
      }
      return s;
    }

    /// Check if a string represents an integer
    /// From: http://stackoverflow.com/a/2845275/1447953
    bool isInteger(const std::string & s)
    {
       if(s.empty() || ((!isdigit(s[0])) && (s[0] != '-') && (s[0] != '+'))) return false ;

       char * p ;
       strtol(s.c_str(), &p, 10) ;

       return (*p == 0) ;
    }

    /// Copy a std::string to a character array, stripping the null termination character.  Good for sending to Fortran.
    void strcpy2f(char* arr, int len, str s)
    {
      s = str_fixed_len(s, len-1);
      strcpy(arr, s.c_str());
      arr[len-1] = ' ';
    }

    /// Perform a simple case-insensitive string comparison
    /// From: https://stackoverflow.com/a/4119881/1447953
    bool iequals(const std::string& a, const std::string& b, bool case_sensitive)
    {
        if(case_sensitive)
            return a==b;
        unsigned int sz = a.size();
        if (b.size() != sz)
            return false;
        for (unsigned int i = 0; i < sz; ++i)
            if (tolower(a[i]) != tolower(b[i]))
                return false;
        return true;
    }

    /// Split string into vector of strings, using a delimiter string
    std::vector<std::string> split(const std::string& input, const std::string& delimiter)
    {
        std::vector<std::string> result;
        boost::iter_split(result, input, boost::algorithm::first_finder(delimiter));
        return result;
    }

    /// Convert a whole string to lowercase
    std::string strtolower(const std::string& a)
    {
        unsigned int sz = a.size();
        std::string b = a;
        for (unsigned int i = 0; i < sz; ++i)
        { b[i] = tolower(a[i]); }
        return b;
    }

    /// Ensure that a path exists (and then return the path, for chaining purposes)
    const std::string& ensure_path_exists(const std::string& path)
    {
       // Split off potential filename
       // If only path is provided, it must end in a slash!!!
       size_t found = path.find_last_of("/\\");
       if (found != std::string::npos)
       {
         std::string prefix = path.substr(0,found);
         recursive_mkdir( prefix.c_str() );
       }
       return path;
    }

    /// Check if a file exists
    bool file_exists(const std::string& filename)
    {
        //std::ifstream file(filename);
        //return not file.fail();
        struct stat buffer;
        return (stat(filename.c_str(), &buffer) == 0);
    }

    /// Return a vector of strings listing the contents of a directory (POSIX)
    /// Based on http://www.gnu.org/software/libtool/manual/libc/Simple-Directory-Lister.html
    std::vector<std::string> ls_dir(const std::string& dir)
    {
      std::vector<std::string> dir_contents;
      DIR *dp;
      struct dirent *ep;
      dp = opendir(dir.c_str());

      if( dp != NULL )
      {
        while( (ep = readdir(dp)) )
        {
          dir_contents.push_back(ep->d_name);
        }
        (void) closedir(dp);
      }
      else
      {
        std::string msg = "Utils::ls_dir function failed to open the directory '"+dir+"'!";
        std::cerr << msg << std::endl;
        abort();
      }
      return dir_contents;
    }

    /// Get directory name from full path+filename (POSIX)
    std::string dir_name(const std::string& path)
    {
       char buffer[1000]; // temporary buffer for dirname to work with (it is a C function)
       path.copy(buffer, path.size()); //TODO: error if path.size()>1000
       buffer[path.size()] = '\0';
       std::string result = dirname(&buffer[0]); // should use the C function...
       return result;
    }

    /// Get file name from full path+filename (POSIX)
    std::string base_name(const std::string& path)
    {
       char buffer[1000]; // temporary buffer for basename to work with (it is a C function)
       path.copy(buffer, path.size()); //TODO: error if path.size()>1000
       buffer[path.size()] = '\0';
       std::string result = basename(&buffer[0]); // should use the C function...
       return result;
    }

    /// Delete all files in a directory (does not act recursively)
    int remove_all_files_in(const str& dirname, bool error_if_absent)
    {
      struct dirent *pDirent;
      DIR *pDir;
      pDir = opendir(dirname.c_str());
      if (pDir == NULL)
      {
        if (error_if_absent)
        {
          utils_error().raise(LOCAL_INFO, "Directory "+dirname+" not found.");
        }
        else
        {
          return 1;
        }
      }
      while ( (pDirent = readdir(pDir)) != NULL )
      {
        // Delete the contents
        if ( strcmp(pDirent->d_name, ".") and strcmp(pDirent->d_name, "..") )
        {
          std::ostringstream ss;
          ss << dirname << pDirent->d_name;
          cout << "Deleting " << ss.str() << endl;
          remove(ss.str().c_str());
        }
      }
      closedir (pDir);
      return 0;
    }

    /// Get current system clock time
    time_point get_clock_now()
    {
        return std::chrono::system_clock::now();
    }

    /// Return (locally defined) date and time corresponding to time_point
    std::string return_time_and_date(const time_point& in)
    {
        std::time_t t = std::chrono::system_clock::to_time_t(in);

        std::string ts = std::ctime(&t); // for example : Tue Sep 27 14:21:13 2011\n
        ts.resize(ts.size()-1); // Remove the annoying trailing newline
        return ts;
    }

    /// Check if two strings are a "close" match
    /// Used for "did you mean?" type checking during command line argument processing
    bool are_similar(const std::string& s1, const std::string& s2)
    {
      if(check1(s1,s2) or check1(s2,s1)){ return true; }
      else if(check2(s1,s2)){ return true; } // symmetric
      else{ return false; }
      //TODO: Add more checks? These ones are pretty minimal. Maybe something that computes percentage match between strings...
    }

    /// true if s1 can be obtained by deleting one character from s2
    bool check1(const std::string& s1, const std::string& s2)
    {
      if(s2.length() - s1.length() != 1){ return false; }
      unsigned int i,j;
      for(i=0,j=0; i<s2.length(); i++,j++)
      {
          if(s2[i] == s1[j])
          {/*do  nothing*/}
          else if(i == j)
          { j++;}
          else
          {return false;}
      }
      return true;
    }

    /// true if s1 can be obtained from s2 by changing no more than X characters (X=2 for now)
    bool check2(const std::string& s1, const std::string& s2)
    {
      unsigned int error_limit = 2;
      unsigned int number_of_errors = 0;

      if(s2.length() != s1.length()){ return false; }
      unsigned int i,j;
      for(i=0,j=0; i<s2.length(); i++,j++)
      {
          if(s2[i] == s1[j])
          {/*do  nothing*/}
          else if(number_of_errors <= error_limit)
          { number_of_errors++;}
          else
          {return false;}
      }
      return true;
    }

    /// returns square of double - saves tedious repetition
    double sqr(double a)
    {
      return a * a;
    }

    /// Checks whether `str' ends with `suffix'
    // credit: http://stackoverflow.com/a/41041484/1447953
    bool endsWith(const std::string& str, const std::string& suffix)
    {
      if (&suffix == &str) return true; // str and suffix are the same string
      if (suffix.length() > str.length()) return false;
      size_t delta = str.length() - suffix.length();
      for (size_t i = 0; i < suffix.length(); ++i) {
          if (suffix[i] != str[delta + i]) return false;
      }
      return true;
    }

    // Inspired by the above. Checks whether 'str' begins with 'prefix'
    bool startsWith(const std::string& str, const std::string& prefix, bool case_sensitive)
    {
      if (&prefix == &str) return true; // str and prefix are the same string
      if (prefix.length() > str.length()) return false;
      for (size_t i = 0; i < prefix.length(); ++i) {
          if(case_sensitive)
          {
             if (prefix[i] != str[i]) return false;
          }
          else
          {
             if (tolower(prefix[i]) != tolower(str[i])) return false;
          }
      }
      return true;
    }

    /// Enclose a string in quotation marks if it contains commas
    std::string quote_if_contains_commas(str in)
    {
      if (in.find(',') == std::string::npos)
      {
        return in;
      }
      else
      {
        return "\""+in+"\"";
      }
    }

    // case-independent (ci) compare_less binary function
    bool ci_less::operator() (const std::string & s1, const std::string & s2) const
    {
      return std::lexicographical_compare
        (s1.begin (), s1.end (),   // source range
        s2.begin (), s2.end (),    // dest range
        nocase_compare ());        // comparison
    }

    // case-independent (ci) compare_less binary function
    bool ci_less::nocase_compare::operator() (const unsigned char& c1, const unsigned char& c2) const
    {
      return tolower (c1) < tolower (c2);
    }
        
  }

}

Updated on 2024-07-18 at 13:53:32 +0000