<commit_before>/*
 *
 * MIT License
 *
 * Copyright (c) 2016-2018 The Cats Project
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */

#ifndef CATS_CORECAT_SYSTEM_PROCESS_HPP
#define CATS_CORECAT_SYSTEM_PROCESS_HPP

#include "OS.hpp"
#include "../Data/Array.hpp"
#include "../Text/String.hpp"
#include "../Util/Exception.hpp"

#if defined(CORECAT_OS_WINDOWS)
#   include "../Win32/Handle.hpp"
#elif defined(CORECAT_OS_LINUX) || defined(CORECAT_OS_MACOS)
#   include <unistd.h>
#   include <sys/ioctl.h>
#   include <sys/wait.h>
#   if defined(CORECAT_OS_MACOS)
#       include <crt_externs.h>
#       define environ (*_NSGetEnviron())
#   endif
#else
#   error Unknown OS
#endif

namespace Cats {
namespace Corecat {
inline namespace System {

struct ProcessOption {

    const char* file = nullptr;
    const char* const* argument = nullptr;
    const char* const* environment = nullptr;
    const char* directory = nullptr;

    ProcessOption(const char* file_, const char* const* argument_ = nullptr, const char* const* environment_ = nullptr, const char* directory_ = nullptr) :
        file(file_), argument(argument_), environment(environment_), directory(directory_) {}

};

class Process {

private:

    template <typename T>
    static Array<T> toNullTerminated(ArrayView<const T> arr) {

        Array<T> ret(arr.getSize() + 1);
        std::copy(arr.begin(), arr.end(), ret.begin());
        ret[arr.getSize()] = T();
        return ret;

    }

#if defined(CORECAT_OS_WINDOWS)
private:

    static WString getProcessDirectory() {

        WString path;
        path.setLength(MAX_PATH);
        while(true) {

            DWORD length = ::GetModuleFileNameW(nullptr, path.getData(), DWORD(path.getLength() + 1));
            if(::GetLastError() == ERROR_INSUFFICIENT_BUFFER) {

                path.setLength(path.getLength() * 2);
                continue;

            }
            if(!length)
                throw SystemException("::GetModuleFileNameW failed");
            while(length && path[length - 1] != '/' && path[length - 1] != '\\') --length;
            if(length) --length;
            path.setLength(length);
            return path;

        }

    }
    static WString getCurrentDirectory() {

        WString path;
        DWORD length = ::GetCurrentDirectoryW(0, nullptr);
        if(!length) return {};
        path.setLength(length - 1);
        ::GetCurrentDirectoryW(length, path.getData());
        return path;

    }
    static WString getSystemDirectory() {

        WString path;
        UINT length = ::GetSystemDirectoryW(nullptr, 0);
        if(!length) return {};
        path.setLength(length - 1);
        ::GetSystemDirectoryW(path.getData(), length);
        return path;

    }
    static WString getWindowsDirectory() {

        WString path;
        UINT length = ::GetWindowsDirectoryW(nullptr, 0);
        if(!length) return {};
        path.setLength(length - 1);
        ::GetWindowsDirectoryW(path.getData(), length);
        return path;

    }
    static WString getEnvironmentPath() {

        WString path;
        DWORD length = ::GetEnvironmentVariableW(L"PATH", nullptr, 0);
        if(!length) return {};
        path.setLength(length - 1);
        ::GetEnvironmentVariableW(L"PATH", path.getData(), length);
        return path;

    }

    static bool isExist(const WString& path) {

        DWORD attribute = ::GetFileAttributesW(path.getData());
        return attribute != INVALID_FILE_ATTRIBUTES && !(attribute & FILE_ATTRIBUTE_DIRECTORY);

    }

    static const char* findPath(const char* const* environment) {

        if(!environment) return nullptr;
        for(auto p = environment; *p; ++p) {

            auto str = *p;
            if((str[0] == 'P' || str[0] == 'p') && (str[1] == 'A' || str[1] == 'a')
                && (str[2] == 'T' || str[2] == 't') && (str[3] == 'H' || str[3] == 'h') && str[4] == '=')
                return str + 5;

        }
        return nullptr;

    }

    static void addPath(const ProcessOption& option, Array<WString>& pathList) {

        auto optionPath = findPath(option.environment);
        auto path = optionPath ? WString(optionPath) : getEnvironmentPath();
        for(auto i = path.begin(); i != path.end(); ) {

            auto j = i;
            while(*j && *j != L';') ++j;
            pathList.push(i, j);
            i = j;
            if(*i == L';') ++i;

        }

    }

    static WString enumPath(const WString& file, const Array<WString>& pathList) {

        for(auto&& x : pathList) {

            auto path = x + L'\\' + file;
            if(isExist(path)) return path;

        }
        return {};

    }

    static WString enumExtension(const WString& file, const Array<WString>& pathList) {

        WString path;
        path = enumPath(file, pathList);
        if(!path.isEmpty()) return path;
        path = enumPath(file + L".com", pathList);
        if(!path.isEmpty()) return path;
        path = enumPath(file + L".exe", pathList);
        if(!path.isEmpty()) return path;
        throw SystemException("File not found");

    }
#endif

private:
#if defined(CORECAT_OS_WINDOWS)
    Handle handle;
#else
    pid_t pid;
#endif

public:

    Process(const ProcessOption& option) {
#if defined(CORECAT_OS_WINDOWS)
        Array<WString> pathList;
        pathList.push(getProcessDirectory());
        pathList.push(getCurrentDirectory());
        pathList.push(getSystemDirectory());
        pathList.push(getWindowsDirectory());
        addPath(option, pathList);
        auto path = enumExtension(WString(option.file), pathList);

        WString argument;
        if(option.argument) {

            for(auto p = option.argument; *p; ++p) {

                if(!argument.isEmpty()) argument += L' ';
                WString src(*p);
                WString dst;
                bool quote = false;
                for(std::size_t i = src.getLength() - 1; i != std::size_t(-1); --i) {

                    auto c = src[i];
                    dst += c;
                    if(quote && c == L'\\') dst += L'\\';
                    else if(c == L'\"') quote = true, dst += L'\\';
                    else quote = false;

                }
                argument += L'"' + dst.reverse() + L'"';

            }

        }

        WString environment;
        if(option.environment) {

            for(auto p = option.environment; *p; ++p) {

                environment += WString(*p);
                environment += wchar_t();

            }

        }

        STARTUPINFOW si = {};
        si.cb = sizeof(si);
        si.wShowWindow = SW_SHOWDEFAULT;
        PROCESS_INFORMATION pi = {};
        if(!::CreateProcessW(
            path.getData(),
            option.argument ? argument.getData() : nullptr,
            nullptr,
            nullptr,
            true,
            CREATE_UNICODE_ENVIRONMENT,
            option.environment ? environment.getData() : nullptr,
            option.directory ? WString(option.directory).getData() : nullptr,
            &si,
            &pi))
            throw SystemException("::CreateProcessW failed");
        ::CloseHandle(pi.hThread);
        handle = pi.hProcess;
#else
        int messagePipe[2];
        if(::pipe(messagePipe))
            throw SystemException("::pipe failed");

        int ret;
        do
            ret = ::ioctl(messagePipe[1], FIOCLEX);
        while(ret < 0 && errno == EINTR);
        if(ret) {

            ::close(messagePipe[0]);
            ::close(messagePipe[1]);
            throw SystemException("::ioctl failed");

        }

        pid = ::fork();
        if(pid < 0) {

            ::close(messagePipe[0]);
            ::close(messagePipe[1]);
            throw SystemException("::fork failed");

        }

        if(pid > 0) {

            // Parent process
            ::close(messagePipe[1]);

            ssize_t n;
            char data;
            do
                n = ::read(messagePipe[0], &data, 1);
            while(n < 0 && errno == EINTR);

            ::close(messagePipe[0]);
            if(n) {

                do
                    ret = ::waitpid(pid, nullptr, 0);
                while(ret < 0 && errno == EINTR);
                throw SystemException("Failed to create process");

            }

        } else {

            // Child process
            ::close(messagePipe[0]);
            do {

                if(option.environment) environ = const_cast<char**>(option.environment);
                if(option.directory && ::chdir(option.directory))
                    break;
                ::execvp(option.file, const_cast<char* const*>(option.argument));

            } while(false);

            // Failed
            ssize_t n;
            char data = 0;
            do
                n = ::write(messagePipe[1], &data, 1);
            while(n < 0 && errno == EINTR);
            ::_exit(127);

        }
#endif
    }
    Process(const char* file, const char* const* argument = nullptr, const char* const* environment = nullptr, const char* directory = nullptr) :
        Process(ProcessOption(file, argument, environment, directory)) {}
    Process(const char* file, const char* directory) :
        Process(file, nullptr, nullptr, directory) {}
    Process(const char* file, ArrayView<const char* const> argument, const char* directory = nullptr) :
        Process(file, toNullTerminated(argument).getData(), nullptr, directory) {}
    Process(const char* file, ArrayView<const char* const> argument, ArrayView<const char* const> environment, const char* directory = nullptr) :
        Process(file, toNullTerminated(argument).getData(), toNullTerminated(environment).getData(), directory) {}
    Process(const Process& src) = delete;
    ~Process() = default;

    Process& operator =(const Process& src) = delete;

    std::int64_t wait() {
#if defined(CORECAT_OS_WINDOWS)
        if(::WaitForSingleObject(handle, INFINITE) != WAIT_OBJECT_0)
            throw SystemException("::WaitForSingleObject failed");
        DWORD ret;
        if(!::GetExitCodeProcess(handle, &ret))
            throw SystemException("::GetExitCodeProcess failed");
        handle.close();
        return ret;
#else
        int ret, code;
        do
            ret = ::waitpid(pid, &code, 0);
        while(ret < 0 && errno == EINTR);
        if(ret < 0)
            throw SystemException("::waitpid failed");
        pid = 0;
        return code;
#endif
    }

};

}
}
}

#endif
<commit_msg>Update Process<commit_after>/*
 *
 * MIT License
 *
 * Copyright (c) 2016-2018 The Cats Project
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */

#ifndef CATS_CORECAT_SYSTEM_PROCESS_HPP
#define CATS_CORECAT_SYSTEM_PROCESS_HPP

#include "OS.hpp"
#include "../Data/Array.hpp"
#include "../Text/String.hpp"
#include "../Util/Exception.hpp"

#if defined(CORECAT_OS_WINDOWS)
#   include "../Win32/Handle.hpp"
#elif defined(CORECAT_OS_LINUX) || defined(CORECAT_OS_MACOS)
#   include <unistd.h>
#   include <sys/ioctl.h>
#   include <sys/wait.h>
#   if defined(CORECAT_OS_MACOS)
#       include <crt_externs.h>
#       define environ (*_NSGetEnviron())
#   endif
#else
#   error Unknown OS
#endif

namespace Cats {
namespace Corecat {
inline namespace System {

struct ProcessOption {

    const char* file = nullptr;
    const char* const* argument = nullptr;
    const char* const* environment = nullptr;
    const char* directory = nullptr;

    ProcessOption(const char* file_, const char* const* argument_ = nullptr, const char* const* environment_ = nullptr, const char* directory_ = nullptr) :
        file(file_), argument(argument_), environment(environment_), directory(directory_) {}

};

class Process {

private:

    template <typename T>
    static Array<T> toNullTerminated(ArrayView<const T> arr) {

        Array<T> ret(arr.getSize() + 1);
        std::copy(arr.begin(), arr.end(), ret.begin());
        ret[arr.getSize()] = T();
        return ret;

    }

#if defined(CORECAT_OS_WINDOWS)
private:

    static WString getProcessDirectory() {

        WString path;
        path.setLength(MAX_PATH);
        while(true) {

            DWORD length = ::GetModuleFileNameW(nullptr, path.getData(), DWORD(path.getLength() + 1));
            if(::GetLastError() == ERROR_INSUFFICIENT_BUFFER) {

                path.setLength(path.getLength() * 2);
                continue;

            }
            if(!length)
                throw SystemException("::GetModuleFileNameW failed");
            while(length && path[length - 1] != '/' && path[length - 1] != '\\') --length;
            if(length) --length;
            path.setLength(length);
            return path;

        }

    }
    static WString getCurrentDirectory() {

        WString path;
        DWORD length = ::GetCurrentDirectoryW(0, nullptr);
        if(!length) return {};
        path.setLength(length - 1);
        ::GetCurrentDirectoryW(length, path.getData());
        return path;

    }
    static WString getSystemDirectory() {

        WString path;
        UINT length = ::GetSystemDirectoryW(nullptr, 0);
        if(!length) return {};
        path.setLength(length - 1);
        ::GetSystemDirectoryW(path.getData(), length);
        return path;

    }
    static WString getWindowsDirectory() {

        WString path;
        UINT length = ::GetWindowsDirectoryW(nullptr, 0);
        if(!length) return {};
        path.setLength(length - 1);
        ::GetWindowsDirectoryW(path.getData(), length);
        return path;

    }

    static bool isExist(const WString& path) {

        DWORD attribute = ::GetFileAttributesW(path.getData());
        return attribute != INVALID_FILE_ATTRIBUTES && !(attribute & FILE_ATTRIBUTE_DIRECTORY);

    }

    static const char* getOptionPathVariable(const ProcessOption& option) {

        if(!option.environment) return nullptr;
        for(auto p = option.environment; *p; ++p) {

            auto str = *p;
            if((str[0] == 'P' || str[0] == 'p') && (str[1] == 'A' || str[1] == 'a')
                && (str[2] == 'T' || str[2] == 't') && (str[3] == 'H' || str[3] == 'h') && str[4] == '=')
                return str + 5;

        }
        return nullptr;

    }
    static WString getEnvironmentPathVariable() {

        WString path;
        DWORD length = ::GetEnvironmentVariableW(L"PATH", nullptr, 0);
        if(!length) return {};
        path.setLength(length - 1);
        ::GetEnvironmentVariableW(L"PATH", path.getData(), length);
        return path;

    }

    static Array<WString> getPathList(const ProcessOption& option) {

        Array<WString> pathList;

        pathList.push(getProcessDirectory());
        pathList.push(getCurrentDirectory());
        pathList.push(getSystemDirectory());
        pathList.push(getWindowsDirectory());

        auto optionPathVariable = getOptionPathVariable(option);
        auto pathVariable = optionPathVariable ? WString(optionPathVariable) : getEnvironmentPathVariable();
        for(auto i = pathVariable.begin(); i != pathVariable.end(); ) {

            auto j = i;
            while(*j && *j != L';') ++j;
            pathList.push(i, j);
            i = j;
            if(*i == L';') ++i;

        }

        return pathList;

    }

    static WString enumPath(const WString& file, const Array<WString>& pathList) {

        for(auto&& x : pathList) {

            auto path = x + L'\\' + file;
            if(isExist(path)) return path;

        }
        return {};

    }
    static WString enumExtension(const WString& file, const Array<WString>& pathList) {

        WString path;
        path = enumPath(file, pathList);
        if(!path.isEmpty()) return path;
        path = enumPath(file + L".com", pathList);
        if(!path.isEmpty()) return path;
        path = enumPath(file + L".exe", pathList);
        if(!path.isEmpty()) return path;
        throw SystemException("File not found");

    }
#endif

private:
#if defined(CORECAT_OS_WINDOWS)
    Handle handle;
#else
    pid_t pid;
#endif

public:

    Process(const ProcessOption& option) {
#if defined(CORECAT_OS_WINDOWS)
        auto pathList = getPathList(option);
        auto path = enumExtension(WString(option.file), pathList);

        WString argument;
        if(option.argument) {

            for(auto p = option.argument; *p; ++p) {

                if(!argument.isEmpty()) argument += L' ';
                WString src(*p);
                WString dst;
                bool quote = false;
                for(std::size_t i = src.getLength() - 1; i != std::size_t(-1); --i) {

                    auto c = src[i];
                    dst += c;
                    if(quote && c == L'\\') dst += L'\\';
                    else if(c == L'\"') quote = true, dst += L'\\';
                    else quote = false;

                }
                argument += L'"' + dst.reverse() + L'"';

            }

        }

        WString environment;
        if(option.environment) {

            for(auto p = option.environment; *p; ++p) {

                environment += WString(*p);
                environment += wchar_t();

            }

        }

        STARTUPINFOW si = {};
        si.cb = sizeof(si);
        si.wShowWindow = SW_SHOWDEFAULT;
        PROCESS_INFORMATION pi = {};
        if(!::CreateProcessW(
            path.getData(),
            option.argument ? argument.getData() : nullptr,
            nullptr,
            nullptr,
            true,
            CREATE_UNICODE_ENVIRONMENT,
            option.environment ? environment.getData() : nullptr,
            option.directory ? WString(option.directory).getData() : nullptr,
            &si,
            &pi))
            throw SystemException("::CreateProcessW failed");
        ::CloseHandle(pi.hThread);
        handle = pi.hProcess;
#else
        int messagePipe[2];
        if(::pipe(messagePipe))
            throw SystemException("::pipe failed");

        int ret;
        do
            ret = ::ioctl(messagePipe[1], FIOCLEX);
        while(ret < 0 && errno == EINTR);
        if(ret) {

            ::close(messagePipe[0]);
            ::close(messagePipe[1]);
            throw SystemException("::ioctl failed");

        }

        pid = ::fork();
        if(pid < 0) {

            ::close(messagePipe[0]);
            ::close(messagePipe[1]);
            throw SystemException("::fork failed");

        }

        if(pid > 0) {

            // Parent process
            ::close(messagePipe[1]);

            ssize_t n;
            char data;
            do
                n = ::read(messagePipe[0], &data, 1);
            while(n < 0 && errno == EINTR);

            ::close(messagePipe[0]);
            if(n) {

                do
                    ret = ::waitpid(pid, nullptr, 0);
                while(ret < 0 && errno == EINTR);
                throw SystemException("Failed to create process");

            }

        } else {

            // Child process
            ::close(messagePipe[0]);
            do {

                if(option.environment) environ = const_cast<char**>(option.environment);
                if(option.directory && ::chdir(option.directory))
                    break;
                ::execvp(option.file, const_cast<char* const*>(option.argument));

            } while(false);

            // Failed
            ssize_t n;
            char data = 0;
            do
                n = ::write(messagePipe[1], &data, 1);
            while(n < 0 && errno == EINTR);
            ::_exit(127);

        }
#endif
    }
    Process(const char* file, const char* const* argument = nullptr, const char* const* environment = nullptr, const char* directory = nullptr) :
        Process(ProcessOption(file, argument, environment, directory)) {}
    Process(const char* file, const char* directory) :
        Process(file, nullptr, nullptr, directory) {}
    Process(const char* file, ArrayView<const char* const> argument, const char* directory = nullptr) :
        Process(file, toNullTerminated(argument).getData(), nullptr, directory) {}
    Process(const char* file, ArrayView<const char* const> argument, ArrayView<const char* const> environment, const char* directory = nullptr) :
        Process(file, toNullTerminated(argument).getData(), toNullTerminated(environment).getData(), directory) {}
    Process(const Process& src) = delete;
    ~Process() = default;

    Process& operator =(const Process& src) = delete;

    std::int64_t wait() {
#if defined(CORECAT_OS_WINDOWS)
        if(::WaitForSingleObject(handle, INFINITE) != WAIT_OBJECT_0)
            throw SystemException("::WaitForSingleObject failed");
        DWORD ret;
        if(!::GetExitCodeProcess(handle, &ret))
            throw SystemException("::GetExitCodeProcess failed");
        handle.close();
        return ret;
#else
        int ret, code;
        do
            ret = ::waitpid(pid, &code, 0);
        while(ret < 0 && errno == EINTR);
        if(ret < 0)
            throw SystemException("::waitpid failed");
        pid = 0;
        return code;
#endif
    }

};

}
}
}

#endif
<|endoftext|>


<commit_before>/*
 *
 * MIT License
 *
 * Copyright (c) 2016-2018 The Cats Project
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */

#ifndef CATS_CORECAT_SYSTEM_PROCESS_HPP
#define CATS_CORECAT_SYSTEM_PROCESS_HPP

#include "OS.hpp"
#include "../Data/Array.hpp"
#include "../Text/String.hpp"
#include "../Util/Exception.hpp"

#if defined(CORECAT_OS_WINDOWS)
#   include "../Win32/Handle.hpp"
#elif defined(CORECAT_OS_LINUX) || defined(CORECAT_OS_MACOS)
#   include <spawn.h>
#   include <sys/wait.h>
#   if defined(CORECAT_OS_MACOS)
#       include <crt_externs.h>
#       define environ (*_NSGetEnviron())
#   else
#       include <unistd.h>
#   endif
#else
#   error Unknown OS
#endif

namespace Cats {
namespace Corecat {
inline namespace System {

class Process {

private:

    template <typename T>
    static Array<T> toNullTerminated(ArrayView<const T> arr) {

        Array<T> ret(arr.getSize() + 1);
        std::copy(arr.begin(), arr.end(), ret.begin());
        ret[arr.getSize()] = T();
        return ret;

    }

#if defined(CORECAT_OS_WINDOWS)
private:

    static WString getFullPath(const WString& file) {

        HMODULE module = ::LoadLibraryW(file.getData());
        if(!module) return {};
        WString path;
        path.setLength(MAX_PATH);
        while(true) {

            DWORD pathLength = ::GetModuleFileNameW(module, path.getData(), DWORD(path.getLength() + 1));
            if(::GetLastError() == ERROR_INSUFFICIENT_BUFFER) {

                path.setLength(path.getLength() * 2);
                continue;

            }
            if(!pathLength) {

                ::FreeLibrary(module);
                return {};

            }
            path.setLength(pathLength);
            break;

        }
        ::FreeLibrary(module);
        return path;

    }

    static WString findFullPath(const WString& file) {

        WString path;
        path = getFullPath(file + L'.');
        if(!path.isEmpty()) return path;
        path = getFullPath(file + L".exe.");
        if(!path.isEmpty()) return path;
        throw SystemException("File not found");

    }
#endif

private:
#if defined(CORECAT_OS_WINDOWS)
    Handle handle;
#else
    pid_t pid;
#endif

public:

    Process(const char* file, const char* const* arg = nullptr, const char* const* env = nullptr) {
#if defined(CORECAT_OS_WINDOWS)
        WString argument;
        if(arg) {

            for(auto p = arg; *p; ++p) {

                if(!argument.isEmpty()) argument += L' ';
                argument += L'"';
                for(auto&& c : WString(*p)) {

                    if(c == L'"') argument += L"\\\"";
                    else if(c == L'\\') argument += L"\\\\";
                    else argument += c;

                }
                argument += L'"';

            }

        }
        WString environment;
        if(env) {

            for(auto p = env; *p; ++p) {

                environment += WString(*p);
                environment += wchar_t();

            }

        }
        STARTUPINFOW si = {};
        si.cb = sizeof(si);
        PROCESS_INFORMATION pi = {};
        auto path = findFullPath(WString(file));
        if(!::CreateProcessW(path.getData(), arg ? argument.getData() : nullptr, nullptr, nullptr, false, CREATE_UNICODE_ENVIRONMENT, env ? environment.getData() : nullptr, nullptr, &si, &pi))
            throw SystemException("::CreateProcessW failed");
        ::CloseHandle(pi.hThread);
        handle = pi.hProcess;
#else
        if(::posix_spawnp(&pid, file, nullptr, nullptr, const_cast<char* const*>(arg), env ? const_cast<char* const*>(env) : environ))
            throw SystemException("::posix_spawn failed");
#endif
    }
    Process(const char* file, ArrayView<const char* const> arg) : Process(file, toNullTerminated(arg).getData()) {}
    Process(const char* file, ArrayView<const char* const> arg, ArrayView<const char* const> env) : Process(file, toNullTerminated(arg).getData(), toNullTerminated(env).getData()) {}
    Process(const Process& src) = delete;
    ~Process() = default;

    Process& operator =(const Process& src) = delete;

    std::int64_t wait() {
#if defined(CORECAT_OS_WINDOWS)
        if(::WaitForSingleObject(handle, INFINITE) != WAIT_OBJECT_0)
            throw SystemException("::WaitForSingleObject failed");
        DWORD ret;
        if(!::GetExitCodeProcess(handle, &ret))
            throw SystemException("::GetExitCodeProcess failed");
        handle.close();
        return ret;
#else
        int ret;
        if(::waitpid(pid, &ret, 0) < 0)
            throw SystemException("::waitpid failed");
        pid = 0;
        return ret;
#endif
    }

};

}
}
}

#endif
<commit_msg>Update Process<commit_after>/*
 *
 * MIT License
 *
 * Copyright (c) 2016-2018 The Cats Project
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */

#ifndef CATS_CORECAT_SYSTEM_PROCESS_HPP
#define CATS_CORECAT_SYSTEM_PROCESS_HPP

#include "OS.hpp"
#include "../Data/Array.hpp"
#include "../Text/String.hpp"
#include "../Util/Exception.hpp"

#if defined(CORECAT_OS_WINDOWS)
#   include "../Win32/Handle.hpp"
#elif defined(CORECAT_OS_LINUX) || defined(CORECAT_OS_MACOS)
#   include <spawn.h>
#   include <sys/wait.h>
#   if defined(CORECAT_OS_MACOS)
#       include <crt_externs.h>
#       define environ (*_NSGetEnviron())
#   else
#       include <unistd.h>
#   endif
#else
#   error Unknown OS
#endif

namespace Cats {
namespace Corecat {
inline namespace System {

class Process {

private:

    template <typename T>
    static Array<T> toNullTerminated(ArrayView<const T> arr) {

        Array<T> ret(arr.getSize() + 1);
        std::copy(arr.begin(), arr.end(), ret.begin());
        ret[arr.getSize()] = T();
        return ret;

    }

#if defined(CORECAT_OS_WINDOWS)
private:

    static WString getFullPath(const WString& file) {

        HMODULE module = ::LoadLibraryW(file.getData());
        if(!module) return {};
        WString path;
        path.setLength(MAX_PATH);
        while(true) {

            DWORD pathLength = ::GetModuleFileNameW(module, path.getData(), DWORD(path.getLength() + 1));
            if(::GetLastError() == ERROR_INSUFFICIENT_BUFFER) {

                path.setLength(path.getLength() * 2);
                continue;

            }
            if(!pathLength) {

                ::FreeLibrary(module);
                return {};

            }
            path.setLength(pathLength);
            break;

        }
        ::FreeLibrary(module);
        return path;

    }

    static WString findFullPath(const WString& file) {

        WString path;
        path = getFullPath(file + L'.');
        if(!path.isEmpty()) return path;
        path = getFullPath(file + L".exe.");
        if(!path.isEmpty()) return path;
        throw SystemException("File not found");

    }
#endif

private:
#if defined(CORECAT_OS_WINDOWS)
    Handle handle;
#else
    pid_t pid;
#endif

public:

    Process(const char* file, const char* const* arg = nullptr, const char* const* env = nullptr) {
#if defined(CORECAT_OS_WINDOWS)
        WString argument;
        if(arg) {

            for(auto p = arg; *p; ++p) {

                if(!argument.isEmpty()) argument += L' ';
                argument += L'"';
                for(auto&& c : WString(*p)) {

                    if(c == L'"') argument += L"\\\"";
                    else if(c == L'\\') argument += L"\\\\";
                    else argument += c;

                }
                argument += L'"';

            }

        }
        WString environment;
        if(env) {

            for(auto p = env; *p; ++p) {

                environment += WString(*p);
                environment += wchar_t();

            }

        }
        STARTUPINFOW si = {};
        si.cb = sizeof(si);
        si.wShowWindow = SW_SHOWDEFAULT;
        PROCESS_INFORMATION pi = {};
        auto path = findFullPath(WString(file));
        if(!::CreateProcessW(path.getData(), arg ? argument.getData() : nullptr, nullptr, nullptr, false, CREATE_UNICODE_ENVIRONMENT, env ? environment.getData() : nullptr, nullptr, &si, &pi))
            throw SystemException("::CreateProcessW failed");
        ::CloseHandle(pi.hThread);
        handle = pi.hProcess;
#else
        if(::posix_spawnp(&pid, file, nullptr, nullptr, const_cast<char* const*>(arg), env ? const_cast<char* const*>(env) : environ))
            throw SystemException("::posix_spawn failed");
#endif
    }
    Process(const char* file, ArrayView<const char* const> arg) : Process(file, toNullTerminated(arg).getData()) {}
    Process(const char* file, ArrayView<const char* const> arg, ArrayView<const char* const> env) : Process(file, toNullTerminated(arg).getData(), toNullTerminated(env).getData()) {}
    Process(const Process& src) = delete;
    ~Process() = default;

    Process& operator =(const Process& src) = delete;

    std::int64_t wait() {
#if defined(CORECAT_OS_WINDOWS)
        if(::WaitForSingleObject(handle, INFINITE) != WAIT_OBJECT_0)
            throw SystemException("::WaitForSingleObject failed");
        DWORD ret;
        if(!::GetExitCodeProcess(handle, &ret))
            throw SystemException("::GetExitCodeProcess failed");
        handle.close();
        return ret;
#else
        int ret;
        if(::waitpid(pid, &ret, 0) < 0)
            throw SystemException("::waitpid failed");
        pid = 0;
        return ret;
#endif
    }

};

}
}
}

#endif
<|endoftext|>


<commit_before>/*
Copyright 2011-2014 Rogier van Dalen.

This file is part of Rogier van Dalen's Range library for C++.

This library is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/** \file
This file defines a skeleton and helper classes to define range operations.
The other core_*.hpp in this directory give examples of how to define the
innards of such operations.
The operation "default_direction" is needed by this file, so it is defined here
too.
*/

#ifndef RANGE_DETAIL_CORE_BASE_HPP_INCLUDED
#define RANGE_DETAIL_CORE_BASE_HPP_INCLUDED

#include <type_traits>

#include <boost/mpl/if.hpp>
#include <boost/mpl/eval_if.hpp>
#include <boost/mpl/identity.hpp>
#include <boost/mpl/not.hpp>
#include <boost/mpl/and.hpp>

#include <boost/utility/enable_if.hpp>

#include "meta/vector.hpp"
#include "meta/transform.hpp"

#include "rime/core.hpp"

#include "utility/returns.hpp"
#include "utility/nested_callable.hpp"

#include "callable_traits.hpp"
#include "../direction.hpp"

#include "meta_split.hpp"

#include "core_tag.hpp"

namespace range {

using ::callable_traits::has;
using ::callable_traits::result_of;
using ::callable_traits::result_of_or;

template <class Expression> struct decayed_result_of
: std::decay <typename result_of <Expression>::type> {};

/**
Implementation of operations on ranges.
Types in this namespace must be specialised to implement range operations.
This is (hopefully) as simple as possible.
*/
namespace operation {

    // Forward declarations for operations so they can refer to each other.
    template <class RangeTag, class Enable = void> struct default_direction;
    template <class RangeTag, class Direction, class Enable = void>
        struct empty;
    template <class RangeTag, class Direction, class Enable = void>
        struct size;
    template <class RangeTag, class Direction, class Enable = void>
        struct first;
    template <class RangeTag, class Direction, class Increment,
            class Enable = void>
        struct drop;
    template <class RangeTag, class Direction, class Enable = void>
        struct chop;
    template <class RangeTag, class Direction, class Enable = void>
        struct chop_in_place;
    template <class RangeTag, class Direction, class Index, class Enable = void>
        struct at;

    /**
    Base class for marking an operation as not implemented.
    Operations should be marked as unimplemented when their return type cannot
    be computed, and in other cases where it can be decided at compile-time
    that an operation is not be implemented.
    */
    struct unimplemented : ::callable_traits::unimplemented {
        typedef unimplemented type;

        struct no_return_type {};

        // Make false_type depend on Arguments so the static assertion is
        // triggered only if operator() is actually called.
        template <class ... Arguments> struct false_type : rime::false_type {};

        template <class ... Arguments>
            no_return_type operator() (Arguments const & ...) const
        {
            typedef typename
                meta::first <meta::back, meta::vector <Arguments...>>::type
                last_argument;
            static_assert (is_range <last_argument>::value,
                "Last argument must be a range. "
                "Did you forget to include 'std_adaptor.hpp'?");
            static_assert (false_type <Arguments ...>::value,
                "Range operation not implemented.");
            return no_return_type();
        }
    };

    using ::callable_traits::is_implemented;

    namespace helper {
        /**
        Operation that returns a default-constructed object.
        */
        template <class ResultType> struct return_default_constructed {
            template <class... Arguments>
                ResultType operator() (Arguments &&...) const
            { return ResultType(); }
        };

        struct return_rvalue_reference {
            template <class Argument>
                Argument && operator() (Argument && argument) const
            { return static_cast <Argument &&> (argument); }
        };

        /**
        Operation that skips the arguments that are given and returns only the
        result of the function applied to the last "Number" arguments.
        */
        template <unsigned Number, class SkipArguments, class Function>
            struct call_with_last;

#if !(BOOST_CLANG && __clang_major__ == 3 && __clang_minor__ == 0)
        template <unsigned Number, class ... SkipArguments, class Function>
            struct call_with_last <
                Number, meta::vector <SkipArguments ...>, Function>
        {
            template <class ... LastArguments> typename
                boost::lazy_enable_if_c <sizeof ... (LastArguments) == Number,
                std::result_of <Function (LastArguments ...)>>::type
                operator() (SkipArguments const & ... skip_arguments,
                    LastArguments && ... last_arguments) const
            {
                return Function() (
                    std::forward <LastArguments> (last_arguments) ...);
            }
        };
#else
        // We would like to return the last argument.
        // CLang 3.0 confuses types when unpacking SkipArguments ... with
        // LastArgument && last_argument.
        // Workaround:
        template <unsigned Number, class Function>
            struct call_with_last <Number, meta::vector<>, Function>
        {
            template <class ... LastArguments> typename
                boost::lazy_enable_if_c <sizeof ... (LastArguments) == Number,
                std::result_of <Function (LastArguments ...)>>::type
                operator() (LastArguments && ... last_arguments) const
            {
                return Function() (
                    std::forward <LastArguments> (last_arguments) ...);
            }
        };

        template <unsigned Number, class SkipArguments, class Function>
            struct call_with_last
        {
            typedef call_with_last <Number,
                typename meta::drop <SkipArguments>::type, Function> recursive;

            template <class FirstArgument, class ... OtherArguments>
                auto operator() (FirstArgument const & first_argument,
                    OtherArguments && ... other_arguments) const
            RETURNS (recursive() (
                std::forward <OtherArguments> (other_arguments)...));
        };
#endif

    } // namespace helper

    namespace range_detail {

        /**
        Return whether the operation is implemented for the forward direction
        of Direction.
        */
        template <template <class, class, class> class Apply,
            class RangeTag, class Direction>
        struct is_implemented_forward
        : is_implemented <Apply <RangeTag, typename result_of <
            direction::callable::make_forward (Direction)>::type, void>> {};

        /**
        Perform operation Apply that takes a direction and a range with the
        reverse direction.
        \pre make_forward <Direction> is defined.
        */
        template <template <class, class, class> class Apply,
            class RangeTag, class Direction>
        struct forward_operation {
            Apply <RangeTag, typename
                result_of <direction::callable::make_forward (Direction)>::type,
                void> implementation;

            // Workaround for GCC 4.6.
            template <class Range> struct result {
                typedef decltype (implementation (
                    ::direction::make_forward (std::declval <
                        Direction const &>()), std::declval <Range>())) type;
            };

            template <class Range> typename result <Range>::type
                operator() (Direction const & direction, Range && range) const
            {
                return implementation (::direction::make_forward (direction),
                    std::forward <Range> (range));
            }
        };

    } // namespace range_detail

    /**
    Return the default direction for a range.

    If its argument is a range, this returns "front()" by default.
    However, it can be overridden.
    */
    template <class RangeTag, class Enable> struct default_direction {
        template <class Range> direction::front operator() (Range &&) const
        { return direction::front(); }
    };

    template<> struct default_direction <not_a_range_tag> : unimplemented {};

} // namespace operation

/**
Namespace with structures that normalise the parameters for the structures
in the namespace "operation".
They take as class template parameters the arguments that the functors (in
callable) take, and remove cv-qualifications from directions and increments.

The classes are defined for all template parameters, and are implemented
(i.e. do not derive from operation::unimplemented, and have an operator()) only
for the relevant ones.
*/
namespace apply {
    /*
    To allow the classes in callable:: to be implemented easily, with
    template template parameters, the number of template parameters should be
    variadic.
    */

    template <class ... Arguments> struct default_direction;

    template <class Range> struct default_direction <Range>
    : operation::default_direction <typename tag_of <Range>::type> {};

} // namespace apply

/**** Functions that can actually be called. *****/

namespace callable {

    using ::callable_traits::generic;

    struct default_direction : generic <apply::default_direction> {};

} // namespace callable

/*
Function objects.
*/

static const auto default_direction = callable::default_direction();

static const direction::front front = {};
static const direction::back back = {};

namespace apply {
    /*
    For most operations, there is some automatic filling in of arguments
    to be done.
    They take zero or one (or sometimes more) directions, some variable
    parameters, and one (or sometimes more) ranges.
    If no direction is given, default_direction (range) is usually used.
    The variable parameters can sometimes be filled in automatically too.
    Below code deals with that.
    */

    namespace automatic_arguments {

        /**
        Categorise arguments into directions, other, and ranges.
        "type" is set to Apply <meta::vector <Directions ...>,
        meta::vector <Other...>, meta::vector <Ranges ...>, void>.
        The types in Directions are decayed.
        */
        template <class Arguments> struct categorise_arguments {
            typedef typename detail::split <
                    meta::front, is_direction <boost::mpl::_>, Arguments>::type
                directions_rest;

            typedef typename meta::as_vector <meta::transform <
                std::decay <boost::mpl::_>,
                typename directions_rest::first>>::type directions;

            typedef typename detail::split <
                    meta::back, is_range <boost::mpl::_>,
                    typename directions_rest::second
                >::type rest_ranges;

            // Here first and second are counted from the back!
            typedef typename rest_ranges::second other;
            typedef typename rest_ranges::first ranges;

            typedef meta::vector <directions, other, ranges> type;
        };

        /**
        Categorise Arguments into Directions, Other, and Ranges.
        Return Apply <Directions, Other, Ranges>.
        Except when Directions is empty.
        Then it takes the default direction of the first range and passes
        that as the first argument to
        Apply <meta::vector <direction>, Other, Ranges>.
        */
        template <template <class, class, class, class> class Apply,
            class Arguments, class Categorised =
                typename categorise_arguments <Arguments>::type>
        struct categorise_arguments_default_direction;

        // At least one direction.
        template <template <class, class, class, class> class Apply,
            class Arguments, class ... Directions, class Other, class Ranges>
        struct categorise_arguments_default_direction <Apply, Arguments,
            meta::vector <meta::vector <Directions ...>, Other, Ranges>>
        {
            typedef Apply <meta::vector <Directions ...>, Other, Ranges, void>
                type;
        };

        /**
        Actually call the implementation with the default direction.
        */
        template <class Implementation, class Other, class Ranges>
            struct prepend_default_direction;

        // No direction.
        template <template <class, class, class, class> class Apply,
            class Arguments, class Other, class Ranges>
        struct categorise_arguments_default_direction <Apply, Arguments,
            meta::vector <meta::vector<>, Other, Ranges>>
        {
            typedef typename meta::first <Ranges>::type range_type;

            typedef typename result_of_or <
                callable::default_direction (range_type), void>::type direction;
            typedef Apply <meta::vector <direction>, Other, Ranges, void>
                implementation;
            typedef typename boost::mpl::if_ <boost::mpl::and_ <
                        has <callable::default_direction (range_type)>,
                        operation::is_implemented <implementation>>,
                    prepend_default_direction <implementation, Other, Ranges>,
                    operation::unimplemented
                >::type type;
        };

        // No direction but no range either: a bit silly.
        // Needs disabling explicitly.
        template <template <class, class, class, class> class Apply,
            class Arguments, class Other>
        struct categorise_arguments_default_direction <Apply, Arguments,
            meta::vector <meta::vector<>, Other, meta::vector <>>>
        : operation::unimplemented {};

        /**
        \return The default direction of the first range.
        */
        template <class Range, class ... MoreRanges> inline auto
            first_default_direction (
                Range const & range, MoreRanges const & ...)
        RETURNS (::range::default_direction (range));

        // Explicit specialisations for different numbers of "Other" to keep
        // CLang 3.0 happy.
        template <class Implementation, class ... Ranges>
            struct prepend_default_direction <Implementation,
                meta::vector<>, meta::vector <Ranges ...>>
        {
            Implementation implementation;

            auto operator() (Ranges && ... ranges) const
            RETURNS (implementation (first_default_direction (ranges ...),
                std::forward <Ranges> (ranges) ...));
        };

        template <class Implementation, class Other, class ... Ranges>
            struct prepend_default_direction <Implementation,
                meta::vector <Other>, meta::vector <Ranges ...>>
        {
            Implementation implementation;

            auto operator() (Other && other, Ranges && ... ranges) const
            RETURNS (implementation (first_default_direction (ranges ...),
                std::forward <Other> (other),
                std::forward <Ranges> (ranges) ...));
        };

        template <class Implementation, class Other1, class Other2,
                class ... Ranges>
            struct prepend_default_direction <Implementation,
                meta::vector <Other1, Other2>, meta::vector <Ranges ...>>
        {
            Implementation implementation;

            auto operator() (Other1 && other_1, Other2 && other_2,
                Ranges && ... ranges) const
            RETURNS (implementation (first_default_direction (ranges ...),
                std::forward <Other1> (other_1),
                std::forward <Other2> (other_2),
                std::forward <Ranges> (ranges) ...));
        };

        template <class Implementation, class Other1, class Other2,
            class Other3, class ... Ranges>
        struct prepend_default_direction <Implementation,
            meta::vector <Other1, Other2, Other3>, meta::vector <Ranges ...>>
        {
            Implementation implementation;

            auto operator() (Other1 && other_1, Other2 && other_2,
                Other3 && other_3, Ranges && ... ranges) const
            RETURNS (implementation (first_default_direction (ranges ...),
                std::forward <Other1> (other_1),
                std::forward <Other2> (other_2),
                std::forward <Other3> (other_3),
                std::forward <Ranges> (ranges) ...));
        };

    } // namespace automatic_arguments

} // namespace apply

} // namespace range

#endif  // RANGE_DETAIL_CORE_BASE_HPP_INCLUDED

<commit_msg>Fix bug in detail/core_base.hpp<commit_after>/*
Copyright 2011-2014 Rogier van Dalen.

This file is part of Rogier van Dalen's Range library for C++.

This library is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/** \file
This file defines a skeleton and helper classes to define range operations.
The other core_*.hpp in this directory give examples of how to define the
innards of such operations.
The operation "default_direction" is needed by this file, so it is defined here
too.
*/

#ifndef RANGE_DETAIL_CORE_BASE_HPP_INCLUDED
#define RANGE_DETAIL_CORE_BASE_HPP_INCLUDED

#include <type_traits>

#include <boost/mpl/if.hpp>
#include <boost/mpl/eval_if.hpp>
#include <boost/mpl/identity.hpp>
#include <boost/mpl/not.hpp>
#include <boost/mpl/and.hpp>

#include <boost/utility/enable_if.hpp>

#include "meta/vector.hpp"
#include "meta/transform.hpp"

#include "rime/core.hpp"

#include "utility/returns.hpp"
#include "utility/nested_callable.hpp"

#include "callable_traits.hpp"
#include "../direction.hpp"

#include "meta_split.hpp"

#include "core_tag.hpp"

namespace range {

using ::callable_traits::has;
using ::callable_traits::result_of;
using ::callable_traits::result_of_or;

template <class Expression> struct decayed_result_of
: std::decay <typename result_of <Expression>::type> {};

/**
Implementation of operations on ranges.
Types in this namespace must be specialised to implement range operations.
This is (hopefully) as simple as possible.
*/
namespace operation {

    // Forward declarations for operations so they can refer to each other.
    template <class RangeTag, class Enable = void> struct default_direction;
    template <class RangeTag, class Direction, class Enable = void>
        struct empty;
    template <class RangeTag, class Direction, class Enable = void>
        struct size;
    template <class RangeTag, class Direction, class Enable = void>
        struct first;
    template <class RangeTag, class Direction, class Increment,
            class Enable = void>
        struct drop;
    template <class RangeTag, class Direction, class Enable = void>
        struct chop;
    template <class RangeTag, class Direction, class Enable = void>
        struct chop_in_place;
    template <class RangeTag, class Direction, class Index, class Enable = void>
        struct at;

    /**
    Base class for marking an operation as not implemented.
    Operations should be marked as unimplemented when their return type cannot
    be computed, and in other cases where it can be decided at compile-time
    that an operation is not be implemented.
    */
    struct unimplemented : ::callable_traits::unimplemented {
        typedef unimplemented type;

        struct no_return_type {};

        // Make false_type depend on Arguments so the static assertion is
        // triggered only if operator() is actually called.
        template <class ... Arguments> struct false_type : rime::false_type {};

        template <class ... Arguments>
            no_return_type operator() (Arguments const & ...) const
        {
            typedef typename
                meta::first <meta::back, meta::vector <Arguments...>>::type
                last_argument;
            static_assert (is_range <last_argument>::value,
                "Last argument must be a range. "
                "Did you forget to include 'std_adaptor.hpp'?");
            static_assert (false_type <Arguments ...>::value,
                "Range operation not implemented.");
            return no_return_type();
        }
    };

    using ::callable_traits::is_implemented;

    namespace helper {
        /**
        Operation that returns a default-constructed object.
        */
        template <class ResultType> struct return_default_constructed {
            template <class... Arguments>
                ResultType operator() (Arguments &&...) const
            { return ResultType(); }
        };

        struct return_rvalue_reference {
            template <class Argument>
                Argument && operator() (Argument && argument) const
            { return static_cast <Argument &&> (argument); }
        };

        /**
        Operation that skips the arguments that are given and returns only the
        result of the function applied to the last "Number" arguments.
        */
        template <unsigned Number, class SkipArguments, class Function>
            struct call_with_last;

#if !(BOOST_CLANG && __clang_major__ == 3 && __clang_minor__ == 0)
        template <unsigned Number, class ... SkipArguments, class Function>
            struct call_with_last <
                Number, meta::vector <SkipArguments ...>, Function>
        {
            template <class ... LastArguments> typename
                boost::lazy_enable_if_c <sizeof ... (LastArguments) == Number,
                std::result_of <Function (LastArguments ...)>>::type
                operator() (SkipArguments const & ... skip_arguments,
                    LastArguments && ... last_arguments) const
            {
                return Function() (
                    std::forward <LastArguments> (last_arguments) ...);
            }
        };
#else
        // We would like to return the last argument.
        // CLang 3.0 confuses types when unpacking SkipArguments ... with
        // LastArgument && last_argument.
        // Workaround:
        template <unsigned Number, class Function>
            struct call_with_last <Number, meta::vector<>, Function>
        {
            template <class ... LastArguments> typename
                boost::lazy_enable_if_c <sizeof ... (LastArguments) == Number,
                std::result_of <Function (LastArguments ...)>>::type
                operator() (LastArguments && ... last_arguments) const
            {
                return Function() (
                    std::forward <LastArguments> (last_arguments) ...);
            }
        };

        template <unsigned Number, class SkipArguments, class Function>
            struct call_with_last
        {
            typedef call_with_last <Number,
                typename meta::drop <SkipArguments>::type, Function> recursive;

            template <class FirstArgument, class ... OtherArguments>
                auto operator() (FirstArgument const & first_argument,
                    OtherArguments && ... other_arguments) const
            RETURNS (recursive() (
                std::forward <OtherArguments> (other_arguments)...));
        };
#endif

    } // namespace helper

    namespace range_detail {

        /**
        Return whether the operation is implemented for the forward direction
        of Direction.
        */
        template <template <class, class, class> class Apply,
            class RangeTag, class Direction>
        struct is_implemented_forward
        : is_implemented <Apply <RangeTag, typename result_of <
            direction::callable::make_forward (Direction)>::type, void>> {};

        /**
        Perform operation Apply that takes a direction and a range with the
        reverse direction.
        \pre make_forward <Direction> is defined.
        */
        template <template <class, class, class> class Apply,
            class RangeTag, class Direction>
        struct forward_operation {
            Apply <RangeTag, typename
                result_of <direction::callable::make_forward (Direction)>::type,
                void> implementation;

            // Workaround for GCC 4.6.
            template <class Range> struct result {
                typedef decltype (implementation (
                    ::direction::make_forward (std::declval <
                        Direction const &>()), std::declval <Range>())) type;
            };

            template <class Range> typename result <Range>::type
                operator() (Direction const & direction, Range && range) const
            {
                return implementation (::direction::make_forward (direction),
                    std::forward <Range> (range));
            }
        };

    } // namespace range_detail

    /**
    Return the default direction for a range.

    If its argument is a range, this returns "front()" by default.
    However, it can be overridden.
    */
    template <class RangeTag, class Enable> struct default_direction {
        template <class Range> direction::front operator() (Range &&) const
        { return direction::front(); }
    };

    template<> struct default_direction <not_a_range_tag> : unimplemented {};

} // namespace operation

/**
Namespace with structures that normalise the parameters for the structures
in the namespace "operation".
They take as class template parameters the arguments that the functors (in
callable) take, and remove cv-qualifications from directions and increments.

The classes are defined for all template parameters, and are implemented
(i.e. do not derive from operation::unimplemented, and have an operator()) only
for the relevant ones.
*/
namespace apply {
    /*
    To allow the classes in callable:: to be implemented easily, with
    template template parameters, the number of template parameters should be
    variadic.
    */

    template <class ... Arguments> struct default_direction;

    template <class Range> struct default_direction <Range>
    : operation::default_direction <typename tag_of <Range>::type> {};

} // namespace apply

/**** Functions that can actually be called. *****/

namespace callable {

    using ::callable_traits::generic;

    struct default_direction : generic <apply::default_direction> {};

} // namespace callable

/*
Function objects.
*/

static const auto default_direction = callable::default_direction();

static const direction::front front = {};
static const direction::back back = {};

namespace apply {
    /*
    For most operations, there is some automatic filling in of arguments
    to be done.
    They take zero or one (or sometimes more) directions, some variable
    parameters, and one (or sometimes more) ranges.
    If no direction is given, default_direction (range) is usually used.
    The variable parameters can sometimes be filled in automatically too.
    Below code deals with that.
    */

    namespace automatic_arguments {

        /**
        Categorise arguments into directions, other, and ranges.
        "type" is set to Apply <meta::vector <Directions ...>,
        meta::vector <Other...>, meta::vector <Ranges ...>, void>.
        The types in Directions are decayed.
        */
        template <class Arguments> struct categorise_arguments {
            typedef typename detail::split <
                    meta::front, is_direction <boost::mpl::_>, Arguments>::type
                directions_rest;

            typedef typename meta::as_vector <meta::transform <
                std::decay <boost::mpl::_>,
                typename directions_rest::first>>::type directions;

            typedef typename detail::split <
                    meta::back, is_range <boost::mpl::_>,
                    typename directions_rest::second
                >::type rest_ranges;

            // Here first and second are counted from the back!
            typedef typename rest_ranges::second other;
            typedef typename rest_ranges::first ranges;

            typedef meta::vector <directions, other, ranges> type;
        };

        /**
        Categorise Arguments into Directions, Other, and Ranges.
        Return Apply <Directions, Other, Ranges>.
        Except when Directions is empty.
        Then it takes the default direction of the first range and passes
        that as the first argument to
        Apply <meta::vector <direction>, Other, Ranges>.
        */
        template <template <class, class, class, class> class Apply,
            class Arguments, class Categorised =
                typename categorise_arguments <Arguments>::type>
        struct categorise_arguments_default_direction;

        // At least one direction.
        template <template <class, class, class, class> class Apply,
            class Arguments, class ... Directions, class Other, class Ranges>
        struct categorise_arguments_default_direction <Apply, Arguments,
            meta::vector <meta::vector <Directions ...>, Other, Ranges>>
        {
            typedef Apply <meta::vector <Directions ...>, Other, Ranges, void>
                type;
        };

        /**
        Actually call the implementation with the default direction.
        */
        template <class Implementation, class Other, class Ranges>
            struct prepend_default_direction;

        // No direction.
        template <template <class, class, class, class> class Apply,
            class Arguments, class Other, class Ranges>
        struct categorise_arguments_default_direction <Apply, Arguments,
            meta::vector <meta::vector<>, Other, Ranges>>
        {
            typedef typename meta::first <Ranges>::type range_type;

            typedef typename std::decay <typename result_of_or <
                callable::default_direction (range_type), void>::type>::type
                direction;
            typedef Apply <meta::vector <direction>, Other, Ranges, void>
                implementation;
            typedef typename boost::mpl::if_ <boost::mpl::and_ <
                        has <callable::default_direction (range_type)>,
                        operation::is_implemented <implementation>>,
                    prepend_default_direction <implementation, Other, Ranges>,
                    operation::unimplemented
                >::type type;
        };

        // No direction but no range either: a bit silly.
        // Needs disabling explicitly.
        template <template <class, class, class, class> class Apply,
            class Arguments, class Other>
        struct categorise_arguments_default_direction <Apply, Arguments,
            meta::vector <meta::vector<>, Other, meta::vector <>>>
        : operation::unimplemented {};

        /**
        \return The default direction of the first range.
        */
        template <class Range, class ... MoreRanges> inline auto
            first_default_direction (
                Range const & range, MoreRanges const & ...)
        RETURNS (::range::default_direction (range));

        // Explicit specialisations for different numbers of "Other" to keep
        // CLang 3.0 happy.
        template <class Implementation, class ... Ranges>
            struct prepend_default_direction <Implementation,
                meta::vector<>, meta::vector <Ranges ...>>
        {
            Implementation implementation;

            auto operator() (Ranges && ... ranges) const
            RETURNS (implementation (first_default_direction (ranges ...),
                std::forward <Ranges> (ranges) ...));
        };

        template <class Implementation, class Other, class ... Ranges>
            struct prepend_default_direction <Implementation,
                meta::vector <Other>, meta::vector <Ranges ...>>
        {
            Implementation implementation;

            auto operator() (Other && other, Ranges && ... ranges) const
            RETURNS (implementation (first_default_direction (ranges ...),
                std::forward <Other> (other),
                std::forward <Ranges> (ranges) ...));
        };

        template <class Implementation, class Other1, class Other2,
                class ... Ranges>
            struct prepend_default_direction <Implementation,
                meta::vector <Other1, Other2>, meta::vector <Ranges ...>>
        {
            Implementation implementation;

            auto operator() (Other1 && other_1, Other2 && other_2,
                Ranges && ... ranges) const
            RETURNS (implementation (first_default_direction (ranges ...),
                std::forward <Other1> (other_1),
                std::forward <Other2> (other_2),
                std::forward <Ranges> (ranges) ...));
        };

        template <class Implementation, class Other1, class Other2,
            class Other3, class ... Ranges>
        struct prepend_default_direction <Implementation,
            meta::vector <Other1, Other2, Other3>, meta::vector <Ranges ...>>
        {
            Implementation implementation;

            auto operator() (Other1 && other_1, Other2 && other_2,
                Other3 && other_3, Ranges && ... ranges) const
            RETURNS (implementation (first_default_direction (ranges ...),
                std::forward <Other1> (other_1),
                std::forward <Other2> (other_2),
                std::forward <Other3> (other_3),
                std::forward <Ranges> (ranges) ...));
        };

    } // namespace automatic_arguments

} // namespace apply

} // namespace range

#endif  // RANGE_DETAIL_CORE_BASE_HPP_INCLUDED

<|endoftext|>


<commit_before>#include <cctype>
#include <cfloat>
#include <climits>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <string>
#include <sstream>
#include <algorithm>
#include <complex>
#include <deque>
#include <list>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <vector>
#include <utility>

using namespace std;

typedef long l;
typedef long long ll;
typedef unsigned long long ull;
typedef unsigned long ul;
typedef long double ld;
typedef pair<int, int > Pii;

const int INF = INT_MAX;
const int MAX_N = 50005;
const int MAX_V = MAX_N;
const int MAX_E = 100010;

struct edge {
    int u, v;
    int dis;
};
vector<edge> g[MAX_V];
vector<edge> es;
bool vis[MAX_V];
int d[MAX_N], V, E, pre[MAX_V];
int cost[MAX_V][MAX_V];

void dijkstra(int s) {
    //
    // fill(d, d + V, INF);
    // fill(vis, vis + V, false);
    // d[s] = 0;
    // while (true) {
    //     int v = -1;
    //     for (int u = 0; u < V; ++u)
    //         if (!vis[u] && (v == -1 || d[u] < d[v])) v = u;
    //     if (v == -1) break;
    //     vis[v] = true;
    //     for (int u = 0; u < V; ++u) {
    //         d[u] = min(d[v] + cost[v][u], d[u]);
    //     }
    // }
    //
    priority_queue<Pii, vector<Pii>, greater<Pii> > que;// fisrt 是最短距离，second 是顶点编号
    fill(d, d + V, INF);
    d[s] = 0;
    que.push(Pii(0, s));
    while (!que.empty()) {
        Pii p = que.top();que.pop();
        int u = p.second;
        if (d[u] < p.first) continue;
        for (int i = 0; i < g[u].size(); i++) {
            edge e = g[u][i];
            if (d[e.v] > d[u] + e.dis) {
                d[e.v] = d[u] + e.dis;
                que.push(Pii(d[e.v], e.v));
            }
        }
    }
}
void bellman_ford(int s) {
    fill(d, d + V, INF);
    d[s] = 0;
    //
    // for (int i = 0; i < V; ++i)
    //     for (int j = 0; j < E; ++j) {
    //         int u, v;
    //         u = es[j].u;
    //         v = es[j].v;
    //         if (d[u] != INF)
    //             d[v] = min(d[u] + es[j].dis, d[v]);
    //         if (d[v] != INF)
    //             d[u] = min(d[v] + es[j].dis, d[u]);
    //     }
    //
    while (true) {
        bool update = false;
        for (int i = 0; i < E; ++i) {
            edge e = es[i];
            if (d[e.u] != INF && d[e.u] + e.dis < d[e.v]) {
                update = true;
                d[e.v] = d[e.u] + e.dis;
            }
        }
        if (!update) break;
    }
}
void spfa(int s) {
    queue<int> que;
    fill(d, d + V, INF);
    fill(vis, vis + V, false);
    d[s] = 0;
    que.push(s);
    vis[s] = true;
    while (!que.empty()) {
        int u = que.front();
        que.pop();
        vis[u] = false;
        for (int i = 0; i < g[u].size(); ++i) {
            int v = g[u][i].v;
            if (d[v] > d[u] + g[u][i].dis) {
                d[v] = d[u] + g[u][i].dis;
                if (!vis[v]) {
                    que.push(v);
                    vis[v] = true;
                }
            }
        }
    }
}
int prime() {
    fill(d, d + V, INF);
    fill(vis, vis + V, false);
    d[0] = 0;
    int res = 0;
    while (true) {
        int v = -1;
        for (int u = 0; u < V; ++u) {
            if(!vis[u] && (v == -1 || d[u] < d[v])) v = u;
        }
        if (v == -1) break;
        vis[v] = true;
        res += d[v];
        for (int u = 0; u < V; u++)
            d[u] = min(d[u], cost[v][u]);
    }
    return res;
}
bool cmp(edge &e1, edge &e2) {
    return e1.dis < e2.dis;
}
// int kruskal() {
//     sort(es, es + E, cmp);
//     init(V);
//     int res = 0;
//     for (int i = 0; i < E; ++i) {
//         edge e = es[i];
//         if (!same(e.u, e.v)) {
//             unite(e.u, e.v);
//             res += e.dis;
//         }
//     }
//     return res;
// }
int main(void) {

	return 0;
}
<commit_msg>Modified graph.cpp<commit_after>//#include <bits/stdc++.h>
#include <cctype>
#include <cfloat>
#include <climits>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <string>
#include <sstream>
#include <algorithm>
#include <complex>
#include <deque>
#include <list>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <vector>
#include <utility>

#define IOS std::ios::sync_with_stdio(false); std::cin.tie(nullptr); std::cout.tie(nullptr);
// #define __DEBUG__
#ifdef __DEBUG__
#define DEBUG(...) printf(__VA_ARGS__)
#else
#define DEBUG(...)
#endif
#define filename ""
#define setfile() freopen(filename".in", "r", stdin); freopen(filename".out", "w", stdout);

using namespace std;

typedef long l;
typedef long long ll;
typedef unsigned long long ull;
typedef unsigned long ul;
typedef long double ld;
typedef pair<int, int > Pii;

const double pi = acos(-1.0);
const int INF = INT_MAX;
const int MAX_V = 50005;
const int MAX_E = 1e6;

struct edge {
    int from;
    int to, dis;
};
vector<edge> G[MAX_V];
vector<edge> es;
bool vis[MAX_V];
int dist[MAX_V], V, E, pre[MAX_V];
int cost[MAX_V][MAX_V];

void dijkstra(int s) {
    priority_queue<Pii, vector<Pii>, greater<Pii> > que;// fisrt 是最短距离，second 是顶点编号
    fill(dist, dist + V, INF);
    dist[s] = 0; que.push(Pii(0, s));
    while (!que.empty()) {
        Pii p = que.top(); que.pop();
        int v = p.second;
        if (dist[v] < p.first) continue;
        for (int i = 0; i < G[v].size(); i++) {
            edge e = G[v][i];
            if (dist[e.to] > dist[v] + e.dis) {
                dist[e.to] = dist[v] + e.dis;
                que.push(Pii(dist[e.to], e.to));
            }
        }
    }
}
void bellman_ford(int s) {
    fill(dist, dist + V, INF);
    dist[s] = 0;
    while (true) {
        bool update = false;
        for (int i = 0; i < E; ++i) {
            edge e = es[i];
            if (dist[e.from] != INF && dist[e.from] + e.dis < dist[e.to]) {
                update = true;
                dist[e.to] = dist[e.from] + e.dis;
            }
        }
        if (!update) break;
    }
}
void spfa(int s) {
    queue<int> que;
    fill(dist, dist + V, INF);
    fill(vis, vis + V, false);
    dist[s] = 0; que.push(s); vis[s] = true;
    while (!que.empty()) {
        int v = que.front(); que.pop();
        vis[v] = false;
        for (int i = 0; i < G[v].size(); ++i) {
            int u = G[v][i].to;
            if (dist[u] > dist[v] + G[v][i].dis) {
                dist[u] = dist[v] + G[v][i].dis;
                if (!vis[u]) {
                    que.push(u);
                    vis[u] = true;
                }
            }
        }
    }
}

int prime() {
    fill(dist, dist + V, INF);
    fill(vis, vis + V, false);
    dist[0] = 0;
    int res = 0;
    while (true) {
        int v = -1;
        for (int u = 0; u < V; ++u) {
            if(!vis[u] && (v == -1 || dist[u] < dist[v])) v = u;
        }
        if (v == -1) break;
        vis[v] = true;
        res += dist[v];
        for (int u = 0; u < V; u++)
            dist[u] = min(dist[u], cost[v][u]);
    }
    /*
    priority_queue<Pii, vector<Pii>, greater<Pii> > que;
    int res;
    fill(dist, dist + V, INF);
    fill(vis, vis + V, false);
    res = dist[0] = 0;
    que.push(Pii(0, s));
    while (!que.empty()) {
        Pii p = que.top(); que.pop();
        int v = p.second;
        if (used[v] || dist[v] < p.first) continue;
        res += dist[v];
        for (int i = 0; i < G[v].size(); ++i) {
            edge e = g[v][i];
            if (dist[e.to] > e.dis) {
                dist[e.to] = e.dis;
                que.push(Pii(dist[e.to], e.to));
            }
        }
    }
    */
    return res;
}
int par[MAX_V];
int rnk[MAX_V];

void init(int n) {
    for (int i = 0; i < n; ++i) {
        par[i] = i;
        rnk[i] = 0;
    }
}
int find(int x) {
    return par[x] == x? x : par[x] = find(par[x]);
}
bool same(int a, int b) {
    return find(a) == find(b);
}
void unite(int x, int y) {
    x = find(x);
    y = find(y);
    if (x == y) return;
    if (rnk[x] < rnk[y]) {
        par[x] = y;
    } else {
        par[y] = x;
        if (rnk[x] == rnk[y]) rnk[x]++;
    }
}
bool cmp(edge &e1, edge &e2) {
    return e1.dis < e2.dis;
}
int kruskal() {
    sort(es.begin(), es.end(), cmp);
    init(V);
    int res = 0;
    for (int i = 0; i < E; ++i) {
        edge e = es[i];
        if (!same(e.from, e.to)) {
            unite(e.from, e.to);
            res += e.dis;
        }
    }
    return res;
}
int main(int argc, char const *argv[])
{

    return 0;
}<|endoftext|>


<commit_before>#pragma once

#include <Eigen/Sparse>
#include <cassert>
#include <algorithm>
#include <ostream>

template <typename Scalar>
using VectorX = Eigen::Matrix<Scalar, Eigen::Dynamic, 1>;

template <typename Scalar>
using MatrixX = Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic>;

template <typename Scalar, typename Index>
Index max_nnz_in_cols(const Eigen::SparseMatrix<Scalar, 0, Index> & matrix)
{
    Index max_nnz = 0;
    for (Index i = 0; i < matrix.cols(); ++i)
    {
        const auto nnz = matrix.col(i).nonZeros();
        if (nnz > max_nnz) max_nnz = nnz;
    }
    return max_nnz;
}

template <typename Scalar, typename Index>
Eigen::SparseMatrix<Scalar, 0, Index> sparse_submatrix(const Eigen::SparseMatrix<Scalar, 0, Index> & matrix,
                                                       const std::vector<Index> & rows,
                                                       const std::vector<Index> & cols)
{
    assert(std::is_sorted(rows.cbegin(), rows.cend()) && "Row indices must be sorted.");
    assert(std::is_sorted(cols.cbegin(), cols.cend()) && "Column indices must be sorted.");

    Eigen::SparseMatrix<Scalar, 0, Index> submat(static_cast<Index>(rows.size()), static_cast<Index>(cols.size()));

    // TODO: Determine vector of exact number of nnz in each column, so we can more accurately reserve
    // the appropriate amount of nnzs in submat
    //const auto largest_column_nnz_number = max_nnz_in_cols(matrix);

    // TODO: Reserve is crashing on an assertion. Determine why.
    //submat.reserve(Eigen::VectorXi::Constant(static_cast<Index>(cols.size()), largest_column_nnz_number));

    for (Index j = 0; j < submat.cols(); ++j)
    {
        for (Index i = 0; i < submat.rows(); ++i)
        {
            const auto row = rows[i];
            const auto col = cols[j];
            // TODO: Rewrite this function so that we loop over NNZs in matrix instead, as now
            // we have to pay for the binary search of .coeff(), and on top of that we
            // loop over every (i, j) pair in the submatrix, which is very expensive for a very sparse matrix.
            const auto coeff = matrix.coeff(row, col);
            if (coeff != static_cast<Index>(0))
            {
                submat.insert(i, j) = matrix.coeff(row, col);
            }
        }
    }

    submat.makeCompressed();
    return submat;
};

template <typename Scalar, int Rows, int Cols>
Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic> submatrix(const Eigen::Matrix<Scalar, Rows, Cols> &matrix,
                                                                const std::vector<int> rows,
                                                                const std::vector<int> cols)
{
    assert(std::is_sorted(rows.cbegin(), rows.cend()) && "Row indices must be sorted");
    assert(std::is_sorted(cols.cbegin(), cols.cend()) && "Col indices must be sorted");

    Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic> result(rows.size(), cols.size());

    for (size_t j = 0; j < cols.size(); ++j)
    {
        for (size_t i = 0; i < rows.size(); ++i)
        {
            const int row = static_cast<int>(rows[i]);
            const int col = static_cast<int>(cols[j]);
            result(i, j) = matrix(row, col);
        }
    }

    return result;
};
<commit_msg>Make sparse_submatrix more efficient<commit_after>#pragma once

#include <Eigen/Sparse>
#include <cassert>
#include <algorithm>
#include <ostream>

template <typename Scalar>
using VectorX = Eigen::Matrix<Scalar, Eigen::Dynamic, 1>;

template <typename Scalar>
using MatrixX = Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic>;

template <typename Scalar, typename Index>
Index max_nnz_in_cols(const Eigen::SparseMatrix<Scalar, 0, Index> & matrix)
{
    Index max_nnz = 0;
    for (Index i = 0; i < matrix.cols(); ++i)
    {
        const auto nnz = matrix.col(i).nonZeros();
        if (nnz > max_nnz) max_nnz = nnz;
    }
    return max_nnz;
}

template <typename Scalar, typename Index>
Eigen::SparseMatrix<Scalar, 0, Index> sparse_submatrix(const Eigen::SparseMatrix<Scalar, 0, Index> & matrix,
                                                       const std::vector<Index> & rows,
                                                       const std::vector<Index> & cols)
{
    typedef typename Eigen::SparseMatrix<Scalar, 0, Index>::InnerIterator InnerIterator;
    assert(std::is_sorted(rows.cbegin(), rows.cend()) && "Row indices must be sorted.");
    assert(std::is_sorted(cols.cbegin(), cols.cend()) && "Column indices must be sorted.");

    const auto submat_rows = static_cast<Index>(rows.size());
    const auto submat_cols = static_cast<Index>(cols.size());

    Eigen::SparseMatrix<Scalar, 0, Index> submat(submat_rows, submat_cols);

    // TODO: Determine vector of exact number of nnz in each column, so we can more accurately reserve
    // the appropriate amount of nnzs in submat
    //const auto largest_column_nnz_number = max_nnz_in_cols(matrix);

    // TODO: Reserve is crashing on an assertion. Determine why.
    //submat.reserve(Eigen::VectorXi::Constant(static_cast<Index>(cols.size()), largest_column_nnz_number));

    for (Index col = 0; col < submat.cols(); ++col)
    {
        Index current_submat_row = 0;
        const auto original_col = cols[col];

        for (InnerIterator it(matrix, original_col); it && current_submat_row < submat_rows; ++it)
        {
            while (current_submat_row < submat_rows && it.row() > rows[current_submat_row])
            {
                current_submat_row += 1;
            }

            if (current_submat_row < submat_rows && it.row() == rows[current_submat_row])
            {
                const auto val = it.value();
                // Take the opportunity to prune exact zeros
                if (val != Scalar(0))
                {
                    submat.insert(current_submat_row, col) = it.value();
                }

                ++current_submat_row;
            }
        }
    }

    submat.makeCompressed();
    return submat;
};

template <typename Scalar, int Rows, int Cols>
Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic> submatrix(const Eigen::Matrix<Scalar, Rows, Cols> &matrix,
                                                                const std::vector<int> rows,
                                                                const std::vector<int> cols)
{
    assert(std::is_sorted(rows.cbegin(), rows.cend()) && "Row indices must be sorted");
    assert(std::is_sorted(cols.cbegin(), cols.cend()) && "Col indices must be sorted");

    Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic> result(rows.size(), cols.size());

    for (size_t j = 0; j < cols.size(); ++j)
    {
        for (size_t i = 0; i < rows.size(); ++i)
        {
            const int row = static_cast<int>(rows[i]);
            const int col = static_cast<int>(cols[j]);
            result(i, j) = matrix(row, col);
        }
    }

    return result;
};
<|endoftext|>