LCOV - code coverage report
Current view: top level - src/utils - MooseUtils.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 99787a Lines: 465 567 82.0 %
Date: 2025-10-14 20:01:24 Functions: 54 62 87.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : //* This file is part of the MOOSE framework
       2             : //* https://mooseframework.inl.gov
       3             : //*
       4             : //* All rights reserved, see COPYRIGHT for full restrictions
       5             : //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
       6             : //*
       7             : //* Licensed under LGPL 2.1, please see LICENSE for details
       8             : //* https://www.gnu.org/licenses/lgpl-2.1.html
       9             : 
      10             : // MOOSE includes
      11             : #include "MooseUtils.h"
      12             : #include "MooseError.h"
      13             : #include "MaterialProperty.h"
      14             : #include "MultiMooseEnum.h"
      15             : #include "InputParameters.h"
      16             : #include "ExecFlagEnum.h"
      17             : #include "InfixIterator.h"
      18             : #include "Registry.h"
      19             : #include "MortarConstraintBase.h"
      20             : #include "MortarNodalAuxKernel.h"
      21             : #include "ExecFlagRegistry.h"
      22             : #include "RestartableDataReader.h"
      23             : 
      24             : #include "libmesh/utility.h"
      25             : #include "libmesh/elem.h"
      26             : 
      27             : // External includes
      28             : #include "pcrecpp.h"
      29             : #include "tinydir.h"
      30             : 
      31             : // C++ includes
      32             : #include <iostream>
      33             : #include <fstream>
      34             : #include <istream>
      35             : #include <iterator>
      36             : #include <filesystem>
      37             : #include <ctime>
      38             : #include <cstdlib>
      39             : #include <regex>
      40             : 
      41             : // System includes
      42             : #include <sys/stat.h>
      43             : #include <numeric>
      44             : #include <unistd.h>
      45             : 
      46             : #include "petscsys.h"
      47             : 
      48             : #ifdef __WIN32__
      49             : #include <windows.h>
      50             : #include <winbase.h>
      51             : #include <fileapi.h>
      52             : #else
      53             : #include <sys/ioctl.h>
      54             : #endif
      55             : 
      56             : namespace MooseUtils
      57             : {
      58             : std::filesystem::path
      59      145059 : pathjoin(const std::filesystem::path & p)
      60             : {
      61      145059 :   return p;
      62             : }
      63             : 
      64             : std::string
      65           7 : runTestsExecutable()
      66             : {
      67           7 :   auto build_loc = pathjoin(Moose::getExecutablePath(), "run_tests");
      68           7 :   if (pathExists(build_loc) && checkFileReadable(build_loc))
      69           7 :     return build_loc;
      70             :   // TODO: maybe no path prefix - just moose_test_runner here?
      71           0 :   return pathjoin(Moose::getExecutablePath(), "moose_test_runner");
      72           7 : }
      73             : 
      74             : std::string
      75           7 : findTestRoot()
      76             : {
      77           7 :   std::string path = ".";
      78           7 :   for (int i = 0; i < 5; i++)
      79             :   {
      80           7 :     auto testroot = pathjoin(path, "testroot");
      81           7 :     if (pathExists(testroot) && checkFileReadable(testroot))
      82           7 :       return testroot;
      83           0 :     path += "/..";
      84           7 :   }
      85           0 :   return "";
      86           7 : }
      87             : 
      88             : bool
      89        2674 : parsesToReal(const std::string & input)
      90             : {
      91        2674 :   std::istringstream ss(input);
      92             :   Real real_value;
      93        2674 :   if (ss >> real_value && ss.eof())
      94        1941 :     return true;
      95         733 :   return false;
      96        2674 : }
      97             : 
      98             : std::string
      99          14 : installedInputsDir(const std::string & app_name,
     100             :                    const std::string & dir_name,
     101             :                    const std::string & extra_error_msg)
     102             : {
     103             :   // See moose.mk for a detailed explanation of the assumed installed application
     104             :   // layout. Installed inputs are expected to be installed in "share/<app_name>/<folder>".
     105             :   // The binary, which has a defined location will be in "bin", a peer directory to "share".
     106             :   std::string installed_path =
     107          14 :       pathjoin(Moose::getExecutablePath(), "..", "share", app_name, dir_name);
     108             : 
     109          14 :   auto test_root = pathjoin(installed_path, "testroot");
     110          14 :   if (!pathExists(installed_path))
     111           0 :     mooseError("Couldn't locate any installed inputs to copy in path: ",
     112             :                installed_path,
     113           0 :                '\n',
     114             :                extra_error_msg);
     115             : 
     116          14 :   checkFileReadable(test_root);
     117          28 :   return installed_path;
     118          14 : }
     119             : 
     120             : std::string
     121           0 : docsDir(const std::string & app_name)
     122             : {
     123             :   // See moose.mk for a detailed explanation of the assumed installed application
     124             :   // layout. Installed docs are expected to be installed in "share/<app_name>/doc".
     125             :   // The binary, which has a defined location will be in "bin", a peer directory to "share".
     126           0 :   std::string installed_path = pathjoin(Moose::getExecutablePath(), "..", "share", app_name, "doc");
     127             : 
     128           0 :   auto docfile = pathjoin(installed_path, "css", "moose.css");
     129           0 :   if (pathExists(docfile) && checkFileReadable(docfile))
     130           0 :     return installed_path;
     131           0 :   return "";
     132           0 : }
     133             : 
     134             : std::string
     135    21428323 : mooseDocsURL(const std::string & path)
     136             : {
     137    21428323 :   return "https://mooseframework.inl.gov/" + path;
     138             : }
     139             : 
     140             : std::string
     141      235292 : replaceAll(std::string str, const std::string & from, const std::string & to)
     142             : {
     143      235292 :   size_t start_pos = 0;
     144      235952 :   while ((start_pos = str.find(from, start_pos)) != std::string::npos)
     145             :   {
     146         660 :     str.replace(start_pos, from.length(), to);
     147         660 :     start_pos += to.length(); // Handles case where 'to' is a substring of 'from'
     148             :   }
     149      235292 :   return str;
     150             : }
     151             : 
     152             : std::string
     153        3640 : convertLatestCheckpoint(std::string orig)
     154             : {
     155        3640 :   auto slash_pos = orig.find_last_of("/");
     156        3640 :   auto path = orig.substr(0, slash_pos);
     157        3640 :   auto file = orig.substr(slash_pos + 1);
     158        3640 :   if (file != "LATEST")
     159        3266 :     return orig;
     160             : 
     161         374 :   auto converted = MooseUtils::getLatestCheckpointFilePrefix(MooseUtils::listDir(path));
     162             : 
     163         374 :   if (converted.empty())
     164           0 :     mooseError("Unable to find suitable recovery file!");
     165             : 
     166         374 :   return converted;
     167        3640 : }
     168             : 
     169             : // this implementation is copied from
     170             : // https://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Levenshtein_distance#C.2B.2B
     171             : int
     172        4158 : levenshteinDist(const std::string & s1, const std::string & s2)
     173             : {
     174             :   // To change the type this function manipulates and returns, change
     175             :   // the return type and the types of the two variables below.
     176        4158 :   auto s1len = s1.size();
     177        4158 :   auto s2len = s2.size();
     178             : 
     179        4158 :   auto column_start = (decltype(s1len))1;
     180             : 
     181        4158 :   auto column = new decltype(s1len)[s1len + 1];
     182        4158 :   std::iota(column + column_start, column + s1len + 1, column_start);
     183             : 
     184       40930 :   for (auto x = column_start; x <= s2len; x++)
     185             :   {
     186       36772 :     column[0] = x;
     187       36772 :     auto last_diagonal = x - column_start;
     188      469920 :     for (auto y = column_start; y <= s1len; y++)
     189             :     {
     190      433148 :       auto old_diagonal = column[y];
     191      433148 :       auto possibilities = {
     192      433148 :           column[y] + 1, column[y - 1] + 1, last_diagonal + (s1[y - 1] == s2[x - 1] ? 0 : 1)};
     193      433148 :       column[y] = std::min(possibilities);
     194      433148 :       last_diagonal = old_diagonal;
     195             :     }
     196             :   }
     197        4158 :   auto result = column[s1len];
     198        4158 :   delete[] column;
     199        4158 :   return result;
     200             : }
     201             : 
     202             : void
     203     8562101 : escape(std::string & str)
     204             : {
     205     8562101 :   std::map<char, std::string> escapes;
     206     8562101 :   escapes['\a'] = "\\a";
     207     8562101 :   escapes['\b'] = "\\b";
     208     8562101 :   escapes['\f'] = "\\f";
     209     8562101 :   escapes['\n'] = "\\n";
     210     8562101 :   escapes['\t'] = "\\t";
     211     8562101 :   escapes['\v'] = "\\v";
     212     8562101 :   escapes['\r'] = "\\r";
     213             : 
     214    68496808 :   for (const auto & it : escapes)
     215    59935035 :     for (size_t pos = 0; (pos = str.find(it.first, pos)) != std::string::npos;
     216         328 :          pos += it.second.size())
     217         328 :       str.replace(pos, 1, it.second);
     218     8562101 : }
     219             : 
     220             : std::string
     221      506660 : removeExtraWhitespace(const std::string & input)
     222             : {
     223      506660 :   return std::regex_replace(input, std::regex("^\\s+|\\s+$|\\s+(?=\\s)"), "");
     224             : }
     225             : 
     226             : bool
     227           0 : pathContains(const std::string & expression,
     228             :              const std::string & string_to_find,
     229             :              const std::string & delims)
     230             : {
     231           0 :   std::vector<std::string> elements;
     232           0 :   tokenize(expression, elements, 0, delims);
     233             : 
     234             :   std::vector<std::string>::iterator found_it =
     235           0 :       std::find(elements.begin(), elements.end(), string_to_find);
     236           0 :   if (found_it != elements.end())
     237           0 :     return true;
     238             :   else
     239           0 :     return false;
     240           0 : }
     241             : 
     242             : bool
     243       25392 : pathExists(const std::string & path)
     244             : {
     245             :   struct stat buffer;
     246       25392 :   return (stat(path.c_str(), &buffer) == 0);
     247             : }
     248             : 
     249             : bool
     250      370135 : checkFileReadable(const std::string & filename,
     251             :                   bool check_line_endings,
     252             :                   bool throw_on_unreadable,
     253             :                   bool check_for_git_lfs_pointer)
     254             : {
     255      370135 :   std::ifstream in(filename.c_str(), std::ifstream::in);
     256      370135 :   if (in.fail())
     257             :   {
     258       74993 :     if (throw_on_unreadable)
     259          18 :       mooseError(
     260          28 :           (std::string("Unable to open file \"") + filename +
     261          22 :            std::string("\". Check to make sure that it exists and that you have read permission."))
     262          20 :               .c_str());
     263             :     else
     264       74975 :       return false;
     265             :   }
     266             : 
     267      295142 :   if (check_line_endings)
     268             :   {
     269       69321 :     std::istream_iterator<char> iter(in);
     270       69321 :     std::istream_iterator<char> eos;
     271       69321 :     in >> std::noskipws;
     272    85267749 :     while (iter != eos)
     273    85198428 :       if (*iter++ == '\r')
     274           0 :         mooseError(filename + " contains Windows(DOS) line endings which are not supported.");
     275             :   }
     276             : 
     277      295142 :   if (check_for_git_lfs_pointer && checkForGitLFSPointer(in))
     278           8 :     mooseError(filename + " appears to be a Git-LFS pointer. Make sure you have \"git-lfs\" "
     279             :                           "installed so that you may pull this file.");
     280      295134 :   in.close();
     281             : 
     282      295134 :   return true;
     283      370111 : }
     284             : 
     285             : bool
     286      225004 : checkForGitLFSPointer(std::ifstream & file)
     287             : {
     288             :   mooseAssert(file.is_open(), "Passed in file handle is not open");
     289             : 
     290      225004 :   std::string line;
     291             : 
     292             :   // git-lfs pointer files contain several name value pairs. The specification states that the
     293             :   // first name/value pair must be "version {url}". We'll do a simplified check for that.
     294      225004 :   file.seekg(0);
     295      225004 :   std::getline(file, line);
     296      225004 :   if (line.find("version https://") != std::string::npos)
     297           8 :     return true;
     298             :   else
     299      224996 :     return false;
     300      225004 : }
     301             : 
     302             : bool
     303         270 : checkFileWriteable(const std::string & filename, bool throw_on_unwritable)
     304             : {
     305         270 :   std::ofstream out(filename.c_str(), std::ios_base::app);
     306         270 :   if (out.fail())
     307             :   {
     308           0 :     if (throw_on_unwritable)
     309           0 :       mooseError(
     310           0 :           (std::string("Unable to open file \"") + filename +
     311           0 :            std::string("\". Check to make sure that it exists and that you have write permission."))
     312           0 :               .c_str());
     313             :     else
     314           0 :       return false;
     315             :   }
     316             : 
     317         270 :   out.close();
     318             : 
     319         270 :   return true;
     320         270 : }
     321             : 
     322             : void
     323      257261 : parallelBarrierNotify(const Parallel::Communicator & comm, bool messaging)
     324             : {
     325             :   processor_id_type secondary_processor_id;
     326             : 
     327      257261 :   if (messaging)
     328           0 :     Moose::out << "Waiting For Other Processors To Finish" << std::endl;
     329      257261 :   if (comm.rank() == 0)
     330             :   {
     331             :     // The primary process is already through, so report it
     332      192200 :     if (messaging)
     333           0 :       Moose::out << "Jobs complete: 1/" << comm.size() << (1 == comm.size() ? "\n" : "\r")
     334           0 :                  << std::flush;
     335      257261 :     for (unsigned int i = 2; i <= comm.size(); ++i)
     336             :     {
     337       65061 :       comm.receive(MPI_ANY_SOURCE, secondary_processor_id);
     338       65061 :       if (messaging)
     339           0 :         Moose::out << "Jobs complete: " << i << "/" << comm.size()
     340           0 :                    << (i == comm.size() ? "\n" : "\r") << std::flush;
     341             :     }
     342             :   }
     343             :   else
     344             :   {
     345       65061 :     secondary_processor_id = comm.rank();
     346       65061 :     comm.send(0, secondary_processor_id);
     347             :   }
     348             : 
     349      257261 :   comm.barrier();
     350      257261 : }
     351             : 
     352             : void
     353          10 : serialBegin(const libMesh::Parallel::Communicator & comm, bool warn)
     354             : {
     355             :   // unless we are the first processor...
     356          10 :   if (comm.rank() > 0)
     357             :   {
     358             :     // ...wait for the previous processor to finish
     359           3 :     int dummy = 0;
     360           3 :     comm.receive(comm.rank() - 1, dummy);
     361             :   }
     362           7 :   else if (warn)
     363           0 :     mooseWarning("Entering serial execution block (use only for debugging)");
     364          10 : }
     365             : 
     366             : void
     367          10 : serialEnd(const libMesh::Parallel::Communicator & comm, bool warn)
     368             : {
     369             :   // unless we are the last processor...
     370          10 :   if (comm.rank() + 1 < comm.size())
     371             :   {
     372             :     // ...notify the next processor of its turn
     373           3 :     int dummy = 0;
     374           3 :     comm.send(comm.rank() + 1, dummy);
     375             :   }
     376             : 
     377          10 :   comm.barrier();
     378          10 :   if (comm.rank() == 0 && warn)
     379           0 :     mooseWarning("Leaving serial execution block (use only for debugging)");
     380          10 : }
     381             : 
     382             : bool
     383       25847 : hasExtension(const std::string & filename, std::string ext, bool strip_exodus_ext)
     384             : {
     385             :   // Extract the extension, w/o the '.'
     386       25847 :   std::string file_ext;
     387       25847 :   if (strip_exodus_ext)
     388             :   {
     389             :     pcrecpp::RE re(
     390         629 :         ".*\\.([^\\.]*?)(?:-s\\d+)?\\s*$"); // capture the complete extension, ignoring -s*
     391         629 :     re.FullMatch(filename, &file_ext);
     392         629 :   }
     393             :   else
     394             :   {
     395       25218 :     pcrecpp::RE re(".*\\.([^\\.]*?)\\s*$"); // capture the complete extension
     396       25218 :     re.FullMatch(filename, &file_ext);
     397       25218 :   }
     398             : 
     399             :   // Perform the comparision
     400       25847 :   if (file_ext == ext)
     401       13808 :     return true;
     402             :   else
     403       12039 :     return false;
     404       25847 : }
     405             : 
     406             : std::string
     407         688 : getExtension(const std::string & filename, const bool rfind)
     408             : {
     409         688 :   std::string file_ext = "";
     410         688 :   if (filename != "")
     411             :   {
     412             :     // The next line splits filename at the last "/" and gives the file name after "/"
     413         674 :     const std::string stripped_filename = splitFileName<std::string>(filename).second;
     414         674 :     auto pos = rfind ? stripped_filename.rfind(".") : stripped_filename.find(".");
     415         674 :     if (pos != std::string::npos)
     416         208 :       file_ext += stripped_filename.substr(pos + 1, std::string::npos);
     417         674 :   }
     418             : 
     419         688 :   return file_ext;
     420           0 : }
     421             : 
     422             : std::string
     423         276 : stripExtension(const std::string & s, const bool rfind)
     424             : {
     425         276 :   const std::string ext = getExtension(s, rfind);
     426         276 :   const bool offset = (ext.size() != 0);
     427             :   // -1 offset accounts for the extension's leading dot ("."), if there is an extension
     428         552 :   return s.substr(0, s.size() - ext.size() - offset);
     429         276 : }
     430             : 
     431             : std::string
     432           7 : getCurrentWorkingDir()
     433             : {
     434             :   // Note: At the time of creating this method, our minimum compiler still
     435             :   // does not support <filesystem>. Additionally, the inclusion of that header
     436             :   // requires an additional library to be linked so for now, we'll just
     437             :   // use the Unix standard library to get us the cwd().
     438           7 :   constexpr unsigned int BUF_SIZE = 1024;
     439             :   char buffer[BUF_SIZE];
     440             : 
     441          14 :   return getcwd(buffer, BUF_SIZE) != nullptr ? buffer : "";
     442             : }
     443             : 
     444             : void
     445          16 : makedirs(const std::string & dir_name, bool throw_on_failure)
     446             : {
     447             :   // split path into directories with delimiter '/'
     448          16 :   std::vector<std::string> split_dir_names;
     449          16 :   MooseUtils::tokenize(dir_name, split_dir_names);
     450             : 
     451          16 :   auto n = split_dir_names.size();
     452             : 
     453             :   // remove '.' and '..' when possible
     454          16 :   auto i = n;
     455          16 :   i = 0;
     456          72 :   while (i != n)
     457             :   {
     458          56 :     if (split_dir_names[i] == ".")
     459             :     {
     460           6 :       for (auto j = i + 1; j < n; ++j)
     461           4 :         split_dir_names[j - 1] = split_dir_names[j];
     462           2 :       --n;
     463             :     }
     464          54 :     else if (i > 0 && split_dir_names[i] == ".." && split_dir_names[i - 1] != "..")
     465             :     {
     466           8 :       for (auto j = i + 1; j < n; ++j)
     467           6 :         split_dir_names[j - 2] = split_dir_names[j];
     468           2 :       n -= 2;
     469           2 :       --i;
     470             :     }
     471             :     else
     472          52 :       ++i;
     473             :   }
     474          16 :   if (n == 0)
     475           0 :     return;
     476             : 
     477          16 :   split_dir_names.resize(n);
     478             : 
     479             :   // start creating directories recursively
     480          16 :   std::string cur_dir = dir_name[0] == '/' ? "" : ".";
     481          64 :   for (auto & dir : split_dir_names)
     482             :   {
     483          50 :     cur_dir += "/" + dir;
     484             : 
     485          50 :     if (!pathExists(cur_dir))
     486             :     {
     487          34 :       auto code = Utility::mkdir(cur_dir.c_str());
     488          34 :       if (code != 0)
     489             :       {
     490           2 :         std::string msg = "Failed creating directory " + dir_name;
     491           2 :         if (throw_on_failure)
     492           2 :           throw std::invalid_argument(msg);
     493             :         else
     494           0 :           mooseError(msg);
     495           2 :       }
     496             :     }
     497             :   }
     498          18 : }
     499             : 
     500             : void
     501          14 : removedirs(const std::string & dir_name, bool throw_on_failure)
     502             : {
     503             :   // split path into directories with delimiter '/'
     504          14 :   std::vector<std::string> split_dir_names;
     505          14 :   MooseUtils::tokenize(dir_name, split_dir_names);
     506             : 
     507          14 :   auto n = split_dir_names.size();
     508             : 
     509             :   // remove '.' and '..' when possible
     510          14 :   auto i = n;
     511          14 :   i = 0;
     512          68 :   while (i != n)
     513             :   {
     514          54 :     if (split_dir_names[i] == ".")
     515             :     {
     516           6 :       for (auto j = i + 1; j < n; ++j)
     517           4 :         split_dir_names[j - 1] = split_dir_names[j];
     518           2 :       --n;
     519             :     }
     520          52 :     else if (i > 0 && split_dir_names[i] == ".." && split_dir_names[i - 1] != "..")
     521             :     {
     522           8 :       for (auto j = i + 1; j < n; ++j)
     523           6 :         split_dir_names[j - 2] = split_dir_names[j];
     524           2 :       n -= 2;
     525           2 :       --i;
     526             :     }
     527             :     else
     528          50 :       ++i;
     529             :   }
     530          14 :   if (n == 0)
     531           0 :     return;
     532             : 
     533          14 :   split_dir_names.resize(n);
     534             : 
     535             :   // start removing directories recursively
     536          14 :   std::string base_dir = dir_name[0] == '/' ? "" : ".";
     537          46 :   for (i = n; i > 0; --i)
     538             :   {
     539          38 :     std::string cur_dir = base_dir;
     540          38 :     auto j = i;
     541         142 :     for (j = 0; j < i; ++j)
     542         104 :       cur_dir += "/" + split_dir_names[j];
     543             : 
     544             :     // listDir should return at least '.' and '..'
     545          38 :     if (pathExists(cur_dir) && listDir(cur_dir).size() == 2)
     546             :     {
     547          32 :       auto code = rmdir(cur_dir.c_str());
     548          32 :       if (code != 0)
     549             :       {
     550           0 :         std::string msg = "Failed removing directory " + dir_name;
     551           0 :         if (throw_on_failure)
     552           0 :           throw std::invalid_argument(msg);
     553             :         else
     554           0 :           mooseError(msg);
     555           0 :       }
     556             :     }
     557             :     else
     558             :       // stop removing
     559           6 :       break;
     560          38 :   }
     561          14 : }
     562             : 
     563             : std::string
     564          28 : camelCaseToUnderscore(const std::string & camel_case_name)
     565             : {
     566          28 :   std::string replaced = camel_case_name;
     567             :   // Put underscores in front of each contiguous set of capital letters
     568          28 :   pcrecpp::RE("(?!^)(?<![A-Z_])([A-Z]+)").GlobalReplace("_\\1", &replaced);
     569             : 
     570             :   // Convert all capital letters to lower case
     571          28 :   std::transform(replaced.begin(), replaced.end(), replaced.begin(), ::tolower);
     572          28 :   return replaced;
     573           0 : }
     574             : 
     575             : std::string
     576         550 : underscoreToCamelCase(const std::string & underscore_name, bool leading_upper_case)
     577             : {
     578         550 :   pcrecpp::StringPiece input(underscore_name);
     579         550 :   pcrecpp::RE re("([^_]*)(_|$)");
     580             : 
     581         550 :   std::string result;
     582         550 :   std::string us, not_us;
     583         550 :   bool make_upper = leading_upper_case;
     584         574 :   while (re.Consume(&input, &not_us, &us))
     585             :   {
     586         574 :     if (not_us.length() > 0)
     587             :     {
     588         562 :       if (make_upper)
     589             :       {
     590         558 :         result += std::toupper(not_us[0]);
     591         558 :         if (not_us.length() > 1)
     592         558 :           result += not_us.substr(1);
     593             :       }
     594             :       else
     595           4 :         result += not_us;
     596             :     }
     597         574 :     if (us == "")
     598         550 :       break;
     599             : 
     600             :     // Toggle flag so next match is upper cased
     601          24 :     make_upper = true;
     602             :   }
     603             : 
     604        1100 :   return result;
     605         550 : }
     606             : 
     607             : std::string
     608     4984554 : shortName(const std::string & name)
     609             : {
     610     4984554 :   return name.substr(name.find_last_of('/') != std::string::npos ? name.find_last_of('/') + 1 : 0);
     611             : }
     612             : 
     613             : std::string
     614     1545837 : baseName(const std::string & name)
     615             : {
     616     1545837 :   return name.substr(0, name.find_last_of('/') != std::string::npos ? name.find_last_of('/') : 0);
     617             : }
     618             : 
     619             : std::string
     620          12 : hostname()
     621             : {
     622             :   char hostname[1024];
     623          12 :   hostname[1023] = '\0';
     624             : #ifndef __WIN32__
     625          12 :   if (gethostname(hostname, 1023))
     626           0 :     mooseError("Failed to retrieve hostname!");
     627             : #else
     628             :   DWORD dwSize = sizeof(hostname);
     629             :   if (!GetComputerNameEx(ComputerNamePhysicalDnsHostname, hostname, &dwSize))
     630             :     mooseError("Failed to retrieve hostname!");
     631             : #endif
     632          24 :   return hostname;
     633             : }
     634             : 
     635             : unsigned short
     636      137210 : getTermWidth(bool use_environment)
     637             : {
     638             : #ifndef __WIN32__
     639             :   struct winsize w;
     640             : #else
     641             :   struct
     642             :   {
     643             :     unsigned short ws_col;
     644             :   } w;
     645             : #endif
     646             :   /**
     647             :    * Initialize the value we intend to populate just in case
     648             :    * the system call fails
     649             :    */
     650      137210 :   w.ws_col = std::numeric_limits<unsigned short>::max();
     651             : 
     652      137210 :   if (use_environment)
     653             :   {
     654      137210 :     char * pps_width = std::getenv("MOOSE_PPS_WIDTH");
     655      137210 :     if (pps_width != NULL)
     656             :     {
     657           0 :       std::stringstream ss(pps_width);
     658           0 :       ss >> w.ws_col;
     659           0 :     }
     660             :   }
     661             :   // Default to AUTO if no environment variable was set
     662      137210 :   if (w.ws_col == std::numeric_limits<unsigned short>::max())
     663             :   {
     664             : #ifndef __WIN32__
     665             :     try
     666             :     {
     667      137210 :       ioctl(0, TIOCGWINSZ, &w);
     668             :     }
     669             :     catch (...)
     670             : #endif
     671             :     {
     672             :     }
     673             :   }
     674             : 
     675             :   // Something bad happened, make sure we have a sane value
     676             :   // 132 seems good for medium sized screens, and is available as a GNOME preset
     677      137210 :   if (w.ws_col == std::numeric_limits<unsigned short>::max())
     678      137210 :     w.ws_col = 132;
     679             : 
     680      137210 :   return w.ws_col;
     681             : }
     682             : 
     683             : void
     684           0 : MaterialPropertyStorageDump(
     685             :     const HashMap<const libMesh::Elem *, HashMap<unsigned int, MaterialProperties>> & props)
     686             : {
     687             :   // Loop through the elements
     688           0 :   for (const auto & elem_it : props)
     689             :   {
     690           0 :     Moose::out << "Element " << elem_it.first->id() << '\n';
     691             : 
     692             :     // Loop through the sides
     693           0 :     for (const auto & side_it : elem_it.second)
     694             :     {
     695           0 :       Moose::out << "  Side " << side_it.first << '\n';
     696             : 
     697             :       // Loop over properties
     698           0 :       unsigned int cnt = 0;
     699           0 :       for (const auto & mat_prop : side_it.second)
     700             :       {
     701           0 :         if (auto mp = dynamic_cast<const MaterialProperty<Real> *>(&mat_prop))
     702             :         {
     703           0 :           Moose::out << "    Property " << cnt << '\n';
     704           0 :           cnt++;
     705             : 
     706             :           // Loop over quadrature points
     707           0 :           for (unsigned int qp = 0; qp < mp->size(); ++qp)
     708           0 :             Moose::out << "      prop[" << qp << "] = " << (*mp)[qp] << '\n';
     709             :         }
     710             :       }
     711             :     }
     712             :   }
     713             : 
     714           0 :   Moose::out << std::flush;
     715           0 : }
     716             : 
     717             : std::string &
     718       22537 : removeColor(std::string & msg)
     719             : {
     720       22537 :   pcrecpp::RE re("(\\33\\[3[0-7]m))", pcrecpp::DOTALL());
     721       22537 :   re.GlobalReplace(std::string(""), &msg);
     722       22537 :   return msg;
     723       22537 : }
     724             : 
     725             : void
     726           0 : addLineBreaks(std::string & message,
     727             :               unsigned int line_width /*= ConsoleUtils::console_line_length*/)
     728             : {
     729           0 :   for (auto i : make_range(int(message.length() / line_width)))
     730           0 :     message.insert((i + 1) * (line_width + 2) - 2, "\n");
     731           0 : }
     732             : 
     733             : void
     734     1368266 : indentMessage(const std::string & prefix,
     735             :               std::string & message,
     736             :               const char * color /*= COLOR_CYAN*/,
     737             :               bool indent_first_line,
     738             :               const std::string & post_prefix)
     739             : {
     740             :   // First we need to see if the message we need to indent (with color) also contains color codes
     741             :   // that span lines.
     742             :   // The code matches all of the XTERM constants (see XTermConstants.h). If it does, then we'll work
     743             :   // on formatting
     744             :   // each colored multiline chunk one at a time with the right codes.
     745     1368266 :   std::string colored_message;
     746     1368266 :   std::string curr_color = COLOR_DEFAULT; // tracks last color code before newline
     747     1368266 :   std::string line, color_code;
     748             : 
     749     1368266 :   bool ends_in_newline = message.empty() ? true : message.back() == '\n';
     750             : 
     751     1368266 :   bool first = true;
     752             : 
     753     1368266 :   std::istringstream iss(message);
     754     3536233 :   for (std::string line; std::getline(iss, line);) // loop over each line
     755             :   {
     756     2167967 :     const static pcrecpp::RE match_color(".*(\\33\\[3\\dm)((?!\\33\\[3\\d)[^\n])*");
     757     2167967 :     pcrecpp::StringPiece line_piece(line);
     758     2167967 :     match_color.FindAndConsume(&line_piece, &color_code);
     759             : 
     760     2167967 :     if (!first || indent_first_line)
     761     2167967 :       colored_message += color + prefix + post_prefix + curr_color;
     762             : 
     763     2167967 :     colored_message += line;
     764             : 
     765             :     // Only add a newline to the last line if it had one to begin with!
     766     2167967 :     if (!iss.eof() || ends_in_newline)
     767     2104557 :       colored_message += "\n";
     768             : 
     769     2167967 :     if (!color_code.empty())
     770      891291 :       curr_color = color_code; // remember last color of this line
     771             : 
     772     2167967 :     first = false;
     773     1368266 :   }
     774     1368266 :   message = colored_message;
     775     1368266 : }
     776             : 
     777             : std::list<std::string>
     778        3505 : listDir(const std::string path, bool files_only)
     779             : {
     780        3505 :   std::list<std::string> files;
     781             : 
     782        3505 :   tinydir_dir dir;
     783        3505 :   dir.has_next = 0; // Avoid a garbage value in has_next (clang StaticAnalysis)
     784        3505 :   tinydir_open(&dir, path.c_str());
     785             : 
     786       22209 :   while (dir.has_next)
     787             :   {
     788             :     tinydir_file file;
     789       18704 :     file.is_dir = 0; // Avoid a garbage value in is_dir (clang StaticAnalysis)
     790       18704 :     tinydir_readfile(&dir, &file);
     791             : 
     792       18704 :     if (!files_only || !file.is_dir)
     793       18704 :       files.push_back(path + "/" + file.name);
     794             : 
     795       18704 :     tinydir_next(&dir);
     796             :   }
     797             : 
     798        3505 :   tinydir_close(&dir);
     799             : 
     800        7010 :   return files;
     801           0 : }
     802             : 
     803             : std::list<std::string>
     804        3059 : getFilesInDirs(const std::list<std::string> & directory_list, const bool files_only /* = true */)
     805             : {
     806        3059 :   std::list<std::string> files;
     807             : 
     808        6154 :   for (const auto & dir_name : directory_list)
     809        3095 :     files.splice(files.end(), listDir(dir_name, files_only));
     810             : 
     811        3059 :   return files;
     812           0 : }
     813             : 
     814             : std::string
     815        3433 : getLatestCheckpointFilePrefix(const std::list<std::string> & checkpoint_files)
     816             : {
     817             :   // Create storage for newest restart files
     818             :   // Note that these might have the same modification time if the simulation was fast.
     819             :   // In that case we're going to save all of the "newest" files and sort it out momentarily
     820        3433 :   std::time_t newest_time = 0;
     821        3433 :   std::list<std::string> newest_restart_files;
     822             : 
     823             :   // Loop through all possible files and store the newest
     824       21950 :   for (const auto & cp_file : checkpoint_files)
     825             :   {
     826       37034 :     if (MooseUtils::hasExtension(cp_file, "rd"))
     827             :     {
     828             :       struct stat stats;
     829        6840 :       stat(cp_file.c_str(), &stats);
     830             : 
     831        6840 :       std::time_t mod_time = stats.st_mtime;
     832        6840 :       if (mod_time > newest_time)
     833             :       {
     834        3617 :         newest_restart_files.clear(); // If the modification time is greater, clear the list
     835        3617 :         newest_time = mod_time;
     836             :       }
     837             : 
     838        6840 :       if (mod_time == newest_time)
     839        6419 :         newest_restart_files.push_back(cp_file);
     840             :     }
     841             :   }
     842             : 
     843             :   // Loop through all of the newest files according the number in the file name
     844        3433 :   int max_file_num = -1;
     845        3433 :   std::string max_file;
     846        3433 :   std::string max_prefix;
     847             : 
     848             :   // Pull out the path including the number and the number itself
     849             :   // This takes something_blah_out_cp/0024-restart-1.rd
     850             :   // and returns "something_blah_out_cp/0024" as the "prefix"
     851             :   // and then "24" as the number itself
     852        3433 :   pcrecpp::RE re_file_num("(.*?(\\d+))-restart-\\d+.rd$");
     853             : 
     854             :   // Now, out of the newest files find the one with the largest number in it
     855        9595 :   for (const auto & res_file : newest_restart_files)
     856             :   {
     857        6162 :     int file_num = 0;
     858             : 
     859             :     // All of the file up to and including the digits
     860        6162 :     std::string file_prefix;
     861             : 
     862        6162 :     re_file_num.FullMatch(res_file, &file_prefix, &file_num);
     863             : 
     864        6162 :     if (file_num > max_file_num)
     865             :     {
     866             :       // Need both the header and the data
     867        3870 :       if (!RestartableDataReader::isAvailable(res_file))
     868           0 :         continue;
     869             : 
     870        3870 :       max_file_num = file_num;
     871        3870 :       max_file = res_file;
     872        3870 :       max_prefix = file_prefix;
     873             :     }
     874        6162 :   }
     875             : 
     876             :   // Error if nothing was located
     877        3433 :   if (max_file_num == -1)
     878           8 :     mooseError("No checkpoint file found!");
     879             : 
     880        6850 :   return max_prefix;
     881        3425 : }
     882             : 
     883             : bool
     884    24101475 : wildCardMatch(std::string name, std::string search_string)
     885             : {
     886             :   // Assume that an empty string matches anything
     887    24101475 :   if (search_string == "")
     888    22282944 :     return true;
     889             : 
     890             :   // transform to lower for case insenstive matching
     891     1818531 :   std::transform(name.begin(), name.end(), name.begin(), (int (*)(int))std::toupper);
     892     1818531 :   std::transform(search_string.begin(),
     893             :                  search_string.end(),
     894             :                  search_string.begin(),
     895             :                  (int (*)(int))std::toupper);
     896             : 
     897             :   // exact match!
     898     1818531 :   if (search_string.find("*") == std::string::npos)
     899     1818531 :     return search_string == name;
     900             : 
     901             :   // wildcard
     902           0 :   std::vector<std::string> tokens;
     903           0 :   MooseUtils::tokenize(search_string, tokens, 1, "*");
     904             : 
     905           0 :   size_t pos = 0;
     906           0 :   for (unsigned int i = 0; i < tokens.size() && pos != std::string::npos; ++i)
     907             :   {
     908           0 :     pos = name.find(tokens[i], pos);
     909             :     // See if we have a leading wildcard
     910           0 :     if (search_string[0] != '*' && i == 0 && pos != 0)
     911           0 :       return false;
     912             :   }
     913             : 
     914           0 :   if (pos != std::string::npos && tokens.size() > 0)
     915             :   {
     916             :     // Now see if we have a trailing wildcard
     917           0 :     size_t last_token_length = tokens.back().length();
     918           0 :     if (*search_string.rbegin() == '*' || pos == name.size() - last_token_length)
     919           0 :       return true;
     920             :     else
     921           0 :       return false;
     922             :   }
     923             :   else
     924           0 :     return false;
     925           0 : }
     926             : 
     927             : bool
     928       61948 : globCompare(const std::string & candidate,
     929             :             const std::string & pattern,
     930             :             std::size_t c,
     931             :             std::size_t p)
     932             : {
     933       61948 :   if (p == pattern.size())
     934        2146 :     return c == candidate.size();
     935             : 
     936       59802 :   if (pattern[p] == '*')
     937             :   {
     938       30722 :     for (; c < candidate.size(); ++c)
     939       29540 :       if (globCompare(candidate, pattern, c, p + 1))
     940         200 :         return true;
     941        1182 :     return globCompare(candidate, pattern, c, p + 1);
     942             :   }
     943             : 
     944       58420 :   if (pattern[p] != '?' && pattern[p] != candidate[c])
     945       38904 :     return false;
     946             : 
     947       19516 :   return globCompare(candidate, pattern, c + 1, p + 1);
     948             : }
     949             : 
     950             : std::string
     951     5373280 : stringJoin(const std::vector<std::string> & values, const std::string & separator)
     952             : {
     953     5373280 :   std::string combined;
     954    13111545 :   for (const auto & value : values)
     955     7738265 :     combined += value + separator;
     956     5373280 :   if (values.size())
     957     5363714 :     combined = combined.substr(0, combined.size() - separator.size());
     958     5373280 :   return combined;
     959           0 : }
     960             : 
     961             : bool
     962     1287511 : beginsWith(const std::string & value, const std::string & begin_value)
     963             : {
     964     1287511 :   return value.rfind(begin_value, 0) == 0;
     965             : }
     966             : 
     967             : ExecFlagEnum
     968    18110211 : getDefaultExecFlagEnum()
     969             : {
     970    18110211 :   return moose::internal::ExecFlagRegistry::getExecFlagRegistry().getDefaultFlags();
     971             : }
     972             : 
     973             : int
     974         428 : stringToInteger(const std::string & input, bool throw_on_failure)
     975             : {
     976         428 :   return convert<int>(input, throw_on_failure);
     977             : }
     978             : 
     979             : void
     980         900 : linearPartitionItems(dof_id_type num_items,
     981             :                      dof_id_type num_chunks,
     982             :                      dof_id_type chunk_id,
     983             :                      dof_id_type & num_local_items,
     984             :                      dof_id_type & local_items_begin,
     985             :                      dof_id_type & local_items_end)
     986             : {
     987         900 :   auto global_num_local_items = num_items / num_chunks;
     988             : 
     989         900 :   num_local_items = global_num_local_items;
     990             : 
     991         900 :   auto leftovers = num_items % num_chunks;
     992             : 
     993         900 :   if (chunk_id < leftovers)
     994             :   {
     995          45 :     num_local_items++;
     996          45 :     local_items_begin = num_local_items * chunk_id;
     997             :   }
     998             :   else
     999         855 :     local_items_begin =
    1000         855 :         (global_num_local_items + 1) * leftovers + global_num_local_items * (chunk_id - leftovers);
    1001             : 
    1002         900 :   local_items_end = local_items_begin + num_local_items;
    1003         900 : }
    1004             : 
    1005             : processor_id_type
    1006      396131 : linearPartitionChunk(dof_id_type num_items, dof_id_type num_chunks, dof_id_type item_id)
    1007             : {
    1008      396131 :   auto global_num_local_items = num_items / num_chunks;
    1009             : 
    1010      396131 :   auto leftovers = num_items % num_chunks;
    1011             : 
    1012      396131 :   auto first_item_past_first_part = leftovers * (global_num_local_items + 1);
    1013             : 
    1014             :   // Is it in the first section (that gets an extra item)
    1015      396131 :   if (item_id < first_item_past_first_part)
    1016         494 :     return item_id / (global_num_local_items + 1);
    1017             :   else
    1018             :   {
    1019      395637 :     auto new_item_id = item_id - first_item_past_first_part;
    1020             : 
    1021             :     // First chunk after the first section + the number of chunks after that
    1022      395637 :     return leftovers + (new_item_id / global_num_local_items);
    1023             :   }
    1024             : }
    1025             : 
    1026             : std::vector<std::string>
    1027      270651 : split(const std::string & str, const std::string & delimiter, std::size_t max_count)
    1028             : {
    1029      270651 :   std::vector<std::string> output;
    1030      270651 :   std::size_t count = 0;
    1031      270651 :   size_t prev = 0, pos = 0;
    1032             :   do
    1033             :   {
    1034      285298 :     pos = str.find(delimiter, prev);
    1035      285298 :     output.push_back(str.substr(prev, pos - prev));
    1036      285298 :     prev = pos + delimiter.length();
    1037      285298 :     count += 1;
    1038      285298 :   } while (pos != std::string::npos && count < max_count);
    1039             : 
    1040      270651 :   if (pos != std::string::npos)
    1041           2 :     output.push_back(str.substr(prev));
    1042             : 
    1043      270651 :   return output;
    1044           0 : }
    1045             : 
    1046             : std::vector<std::string>
    1047       10309 : rsplit(const std::string & str, const std::string & delimiter, std::size_t max_count)
    1048             : {
    1049       10309 :   std::vector<std::string> output;
    1050       10309 :   std::size_t count = 0;
    1051       10309 :   size_t prev = str.length(), pos = str.length();
    1052             :   do
    1053             :   {
    1054       17782 :     pos = str.rfind(delimiter, prev);
    1055       17782 :     output.insert(output.begin(), str.substr(pos + delimiter.length(), prev - pos));
    1056       17782 :     prev = pos - delimiter.length();
    1057       17782 :     count += 1;
    1058       17782 :   } while (pos != std::string::npos && pos > 0 && count < max_count);
    1059             : 
    1060       10309 :   if (pos != std::string::npos)
    1061           4 :     output.insert(output.begin(), str.substr(0, pos));
    1062             : 
    1063       10309 :   return output;
    1064           0 : }
    1065             : 
    1066             : void
    1067         164 : createSymlink(const std::string & target, const std::string & link)
    1068             : {
    1069         164 :   clearSymlink(link);
    1070             : #ifndef __WIN32__
    1071         164 :   auto err = symlink(target.c_str(), link.c_str());
    1072             : #else
    1073             :   auto err = CreateSymbolicLink(target.c_str(), link.c_str(), 0);
    1074             : #endif
    1075         164 :   if (err)
    1076           0 :     mooseError("Failed to create symbolic link (via 'symlink') from ", target, " to ", link);
    1077         164 : }
    1078             : 
    1079             : void
    1080        5316 : clearSymlink(const std::string & link)
    1081             : {
    1082             : #ifndef __WIN32__
    1083             :   struct stat sbuf;
    1084        5316 :   if (lstat(link.c_str(), &sbuf) == 0)
    1085             :   {
    1086         131 :     auto err = unlink(link.c_str());
    1087         131 :     if (err != 0)
    1088           0 :       mooseError("Failed to remove symbolic link (via 'unlink') to ", link);
    1089             :   }
    1090             : #else
    1091             :   auto attr = GetFileAttributesA(link.c_str());
    1092             :   if (attr != INVALID_FILE_ATTRIBUTES)
    1093             :   {
    1094             :     auto err = _unlink(link.c_str());
    1095             :     if (err != 0)
    1096             :       mooseError("Failed to remove link/file (via '_unlink') to ", link);
    1097             :   }
    1098             : #endif
    1099        5316 : }
    1100             : 
    1101             : std::size_t
    1102      133815 : fileSize(const std::string & filename)
    1103             : {
    1104             : #ifndef __WIN32__
    1105             :   struct stat buffer;
    1106      133815 :   if (!stat(filename.c_str(), &buffer))
    1107      133815 :     return buffer.st_size;
    1108             : #else
    1109             :   HANDLE hFile = CreateFile(filename.c_str(),
    1110             :                             GENERIC_READ,
    1111             :                             FILE_SHARE_READ | FILE_SHARE_WRITE,
    1112             :                             NULL,
    1113             :                             OPEN_EXISTING,
    1114             :                             FILE_ATTRIBUTE_NORMAL,
    1115             :                             NULL);
    1116             :   if (hFile == INVALID_HANDLE_VALUE)
    1117             :     return 0;
    1118             : 
    1119             :   LARGE_INTEGER size;
    1120             :   if (GetFileSizeEx(hFile, &size))
    1121             :   {
    1122             :     CloseHandle(hFile);
    1123             :     return size.QuadPart;
    1124             :   }
    1125             : 
    1126             :   CloseHandle(hFile);
    1127             : #endif
    1128           0 :   return 0;
    1129             : }
    1130             : 
    1131             : std::string
    1132       69373 : realpath(const std::string & path)
    1133             : {
    1134       69373 :   return std::filesystem::absolute(path);
    1135             : }
    1136             : 
    1137             : BoundingBox
    1138        8299 : buildBoundingBox(const Point & p1, const Point & p2)
    1139             : {
    1140        8299 :   BoundingBox bb;
    1141        8299 :   bb.union_with(p1);
    1142        8299 :   bb.union_with(p2);
    1143        8299 :   return bb;
    1144             : }
    1145             : 
    1146             : std::string
    1147     8167449 : prettyCppType(const std::string & cpp_type)
    1148             : {
    1149             :   // On mac many of the std:: classes are inline namespaced with __1
    1150             :   // On linux std::string can be inline namespaced with __cxx11
    1151     8167449 :   std::string s = cpp_type;
    1152             :   // Remove all spaces surrounding a >
    1153     8167449 :   pcrecpp::RE("\\s(?=>)").GlobalReplace("", &s);
    1154     8167449 :   pcrecpp::RE("std::__\\w+::").GlobalReplace("std::", &s);
    1155             :   // It would be nice if std::string actually looked normal
    1156     8167449 :   pcrecpp::RE("\\s*std::basic_string<char, std::char_traits<char>, std::allocator<char>>\\s*")
    1157     8167449 :       .GlobalReplace("std::string", &s);
    1158             :   // It would be nice if std::vector looked normal
    1159     8167449 :   pcrecpp::RE r("std::vector<([[:print:]]+),\\s?std::allocator<\\s?\\1\\s?>\\s?>");
    1160     8167449 :   r.GlobalReplace("std::vector<\\1>", &s);
    1161             :   // Do it again for nested vectors
    1162     8167449 :   r.GlobalReplace("std::vector<\\1>", &s);
    1163    16334898 :   return s;
    1164     8167449 : }
    1165             : 
    1166             : std::string
    1167       70282 : canonicalPath(const std::string & path)
    1168             : {
    1169       70282 :   return std::filesystem::weakly_canonical(path).c_str();
    1170             : }
    1171             : 
    1172             : bool
    1173           0 : startsWith(const std::string & string1, const std::string & string2)
    1174             : {
    1175           0 :   if (string2.size() > string1.size())
    1176           0 :     return false;
    1177           0 :   return string1.compare(0, string2.size(), string2) == 0;
    1178             : }
    1179             : 
    1180             : void
    1181           0 : replaceStart(std::string & string1, const std::string & string2, const std::string & string3)
    1182             : {
    1183             :   mooseAssert(startsWith(string1, string2),
    1184             :               "Cannot replace the start because it doesn't match the start string");
    1185           0 :   string1.replace(0, string2.size(), string3);
    1186           0 : }
    1187             : 
    1188             : bool
    1189           0 : isAllLowercase(const std::string & str)
    1190             : {
    1191           0 :   return std::all_of(
    1192           0 :       str.begin(), str.end(), [](unsigned char c) { return !std::isalpha(c) || std::islower(c); });
    1193             : }
    1194             : } // MooseUtils namespace
    1195             : 
    1196             : void
    1197       84744 : removeSubstring(std::string & main, const std::string & sub)
    1198             : {
    1199       84744 :   std::string::size_type n = sub.length();
    1200       84744 :   for (std::string::size_type i = main.find(sub); i != std::string::npos; i = main.find(sub))
    1201           0 :     main.erase(i, n);
    1202       84744 : }
    1203             : 
    1204             : std::string
    1205      316540 : removeSubstring(const std::string & main, const std::string & sub)
    1206             : {
    1207      316540 :   std::string copy_main = main;
    1208      316540 :   std::string::size_type n = sub.length();
    1209      633080 :   for (std::string::size_type i = copy_main.find(sub); i != std::string::npos;
    1210      316540 :        i = copy_main.find(sub))
    1211      316540 :     copy_main.erase(i, n);
    1212      316540 :   return copy_main;
    1213           0 : }

Generated by: LCOV version 1.14