Program Listing for File MiscUtils.hpp

↰ Return to documentation for file (src/utils/MiscUtils.hpp)

#ifndef MISCUTILS_HPP
#define MISCUTILS_HPP

#include <algorithm>
#include <cmath>
#include <cstddef>
#include <cstdlib>
#include <filesystem>
#include <limits>
#include <string>
#include <vector>
namespace utils
{
    inline auto approxEqual(double a, double b, double tol = 1e-6) -> bool {
        return std::fabs(a - b) < tol;
    }

    inline auto splitSearchPath(const std::string& searchPath) -> std::vector<std::string>
    {
#ifdef _WIN32
        constexpr char sep = ';';
#else
        constexpr char sep = ':';
#endif
        std::vector<std::string> dirs;
        std::size_t start = 0;
        while (start <= searchPath.size())
        {
            const auto end = std::min(searchPath.find(sep, start), searchPath.size());
            if (end > start) { dirs.push_back(searchPath.substr(start, end - start)); }
            start = end + 1;
        }
        return dirs;
    }

    inline auto getFilePath(const std::string& fileName,
        const std::string& prefix = "")
    {
        std::filesystem::path filePath(fileName);
        if (std::filesystem::exists(filePath)) { return filePath; }
        if (filePath.is_absolute()) { return std::filesystem::path(); }

        auto *slugDir = std::getenv("SLUG_DIR"); // NOLINT(concurrency-mt-unsafe) -- no thread-safe standard alternative; only ever called during single-threaded setup
        if (slugDir != nullptr)
        {
            auto slugDirPath = std::filesystem::path(slugDir) /
                std::filesystem::path(prefix) / filePath;
            if (std::filesystem::exists(slugDirPath)) { return slugDirPath; }
        }

        auto *slugDataPath = std::getenv("SLUG_DATA_PATH"); // NOLINT(concurrency-mt-unsafe) -- see SLUG_DIR above
        if (slugDataPath != nullptr)
        {
            for (const auto& dir : splitSearchPath(slugDataPath))
            {
                auto dataDirPath = std::filesystem::path(dir) /
                    std::filesystem::path(prefix) / filePath;
                if (std::filesystem::exists(dataDirPath)) { return dataDirPath; }
            }
        }

        const std::string repoDir = REPO_DIR;
        if (!repoDir.empty())
        {
            auto repoDirPath = std::filesystem::path(repoDir) /
                std::filesystem::path(prefix) / filePath;
            if (std::filesystem::exists(repoDirPath)) { return repoDirPath; }
        }

        return std::filesystem::path();
    }

    inline auto romanToInt(const std::string& s) -> int
    {
        if (s.empty()) { return 0; }
        const auto romanVal = [](char c) -> int {
            switch (c) {
                case 'I': return 1;
                case 'V': return 5;
                case 'X': return 10;
                case 'L': return 50;
                case 'C': return 100;
                case 'D': return 500;
                case 'M': return 1000;
                default:  return 0;
            }
        };
        int result = 0;
        int prev = 0;
        for (auto it = s.rbegin(); it != s.rend(); ++it) { // NOLINT(modernize-loop-convert) -- range-based reverse iteration requires C++20 views::reverse
            const int val = romanVal(*it);
            if (val == 0) { return 0; }
            result += (val < prev) ? -val : val;
            prev = val;
        }
        return result;
    }

    inline auto logspace(const double xMin, const double xMax, const std::size_t n) -> std::vector<double>
    {
        std::vector<double> result(n);
        result[0] = xMin; // NOLINT(cppcoreguidelines-pro-bounds-avoid-unchecked-container-access) -- n >= 2 by this function's own (unchecked) precondition, so result is non-empty
        result[n - 1] = xMax; // NOLINT(cppcoreguidelines-pro-bounds-avoid-unchecked-container-access) -- n >= 2 by this function's own (unchecked) precondition, so n - 1 < result.size()
        const double logXMin = std::log(xMin);
        const double logXMax = std::log(xMax);
        const double dLogX = (logXMax - logXMin) / static_cast<double>(n - 1);
        for (std::size_t i = 1; i < n - 1; ++i)
        {
            result[i] = std::exp(logXMin + (static_cast<double>(i) * dLogX)); // NOLINT(cppcoreguidelines-pro-bounds-avoid-unchecked-container-access) -- i < n - 1 < n == result.size() by the loop bound
        }
        return result;
    }

    inline auto minNonZeroMagnitude(const std::vector<double>& v, const double fallback = 1.0) -> double
    {
        double m = std::numeric_limits<double>::infinity();
        for (const double x : v) { if (x != 0.0) { m = std::min(m, std::abs(x)); } }
        return std::isfinite(m) ? m : fallback;
    }

    template <class F>
    auto integrandScale(const F& f, const std::vector<double>& points) -> double
    {
        constexpr double maxScaled = 1e300;
        double scale = std::numeric_limits<double>::infinity();
        double maxAbs = 0.0;
        for (const double x : points)
        {
            const auto v = f(x);
            scale = std::min(scale, minNonZeroMagnitude(v, scale));
            for (const double e : v) { maxAbs = std::max(maxAbs, std::abs(e)); }
        }
        if (!std::isfinite(scale)) { return 1.0; }
        return std::max(scale, maxAbs / maxScaled);
    }

} // namespace utils

#endif // MISCUTILS_HPP