<commit_before>#include "filetransfer.hh"
#include "cache.hh"
#include "fetchers.hh"
#include "globals.hh"
#include "store-api.hh"
#include "types.hh"
#include "url-parts.hh"

#include <nlohmann/json.hpp>

namespace nix::fetchers {

struct DownloadUrl
{
    std::string url;
    std::optional<std::pair<std::string, std::string>> access_token_header;

    DownloadUrl(const std::string & url)
        : url(url) { }

    DownloadUrl(const std::string & url, const std::pair<std::string, std::string> & access_token_header)
        : url(url), access_token_header(access_token_header) { }
};

// A github or gitlab host
const static std::string hostRegexS = "[a-zA-Z0-9.]*"; // FIXME: check
std::regex hostRegex(hostRegexS, std::regex::ECMAScript);

struct GitArchiveInputScheme : InputScheme
{
    virtual std::string type() = 0;

    virtual std::pair<std::string, std::string> accessHeaderFromToken(const std::string & token) const = 0;

    std::optional<Input> inputFromURL(const ParsedURL & url) override
    {
        if (url.scheme != type()) return {};

        auto path = tokenizeString<std::vector<std::string>>(url.path, "/");

        std::optional<Hash> rev;
        std::optional<std::string> ref;
        std::optional<std::string> host_url;

        if (path.size() == 2) {
        } else if (path.size() == 3) {
            if (std::regex_match(path[2], revRegex))
                rev = Hash::parseAny(path[2], htSHA1);
            else if (std::regex_match(path[2], refRegex))
                ref = path[2];
            else
                throw BadURL("in URL '%s', '%s' is not a commit hash or branch/tag name", url.url, path[2]);
        } else
            throw BadURL("URL '%s' is invalid", url.url);

        for (auto &[name, value] : url.query) {
            if (name == "rev") {
                if (rev)
                    throw BadURL("URL '%s' contains multiple commit hashes", url.url);
                rev = Hash::parseAny(value, htSHA1);
            }
            else if (name == "ref") {
                if (!std::regex_match(value, refRegex))
                    throw BadURL("URL '%s' contains an invalid branch/tag name", url.url);
                if (ref)
                    throw BadURL("URL '%s' contains multiple branch/tag names", url.url);
                ref = value;
            }
            else if (name == "host") {
                if (!std::regex_match(value, hostRegex))
                    throw BadURL("URL '%s' contains an invalid instance host", url.url);
                host_url = value;
            }
            // FIXME: barf on unsupported attributes
        }

        if (ref && rev)
            throw BadURL("URL '%s' contains both a commit hash and a branch/tag name %s %s", url.url, *ref, rev->gitRev());

        Input input;
        input.attrs.insert_or_assign("type", type());
        input.attrs.insert_or_assign("owner", path[0]);
        input.attrs.insert_or_assign("repo", path[1]);
        if (rev) input.attrs.insert_or_assign("rev", rev->gitRev());
        if (ref) input.attrs.insert_or_assign("ref", *ref);
        if (host_url) input.attrs.insert_or_assign("host", *host_url);

        return input;
    }

    std::optional<Input> inputFromAttrs(const Attrs & attrs) override
    {
        if (maybeGetStrAttr(attrs, "type") != type()) return {};

        for (auto & [name, value] : attrs)
            if (name != "type" && name != "owner" && name != "repo" && name != "ref" && name != "rev" && name != "narHash" && name != "lastModified" && name != "host")
                throw Error("unsupported input attribute '%s'", name);

        getStrAttr(attrs, "owner");
        getStrAttr(attrs, "repo");

        Input input;
        input.attrs = attrs;
        return input;
    }

    ParsedURL toURL(const Input & input) override
    {
        auto owner = getStrAttr(input.attrs, "owner");
        auto repo = getStrAttr(input.attrs, "repo");
        auto ref = input.getRef();
        auto rev = input.getRev();
        auto path = owner + "/" + repo;
        assert(!(ref && rev));
        if (ref) path += "/" + *ref;
        if (rev) path += "/" + rev->to_string(Base16, false);
        return ParsedURL {
            .scheme = type(),
            .path = path,
        };
    }

    bool hasAllInfo(const Input & input) override
    {
        return input.getRev() && maybeGetIntAttr(input.attrs, "lastModified");
    }

    Input applyOverrides(
        const Input & _input,
        std::optional<std::string> ref,
        std::optional<Hash> rev) override
    {
        auto input(_input);
        if (rev && ref)
            throw BadURL("cannot apply both a commit hash (%s) and a branch/tag name ('%s') to input '%s'",
                rev->gitRev(), *ref, input.to_string());
        if (rev) {
            input.attrs.insert_or_assign("rev", rev->gitRev());
            input.attrs.erase("ref");
        }
        if (ref) {
            input.attrs.insert_or_assign("ref", *ref);
            input.attrs.erase("rev");
        }
        return input;
    }

    virtual Hash getRevFromRef(nix::ref<Store> store, const Input & input) const = 0;

    virtual DownloadUrl getDownloadUrl(const Input & input) const = 0;

    std::pair<Tree, Input> fetch(ref<Store> store, const Input & _input) override
    {
        Input input(_input);

        if (!maybeGetStrAttr(input.attrs, "ref")) input.attrs.insert_or_assign("ref", "HEAD");

        auto rev = input.getRev();
        if (!rev) rev = getRevFromRef(store, input);

        input.attrs.erase("ref");
        input.attrs.insert_or_assign("rev", rev->gitRev());

        Attrs immutableAttrs({
            {"type", "git-tarball"},
            {"rev", rev->gitRev()},
        });

        if (auto res = getCache()->lookup(store, immutableAttrs)) {
            input.attrs.insert_or_assign("lastModified", getIntAttr(res->first, "lastModified"));
            return {
                Tree(store->toRealPath(res->second), std::move(res->second)),
                input
            };
        }

        auto url = getDownloadUrl(input);

        Headers headers;
        if (url.access_token_header) {
            headers.push_back(*url.access_token_header);
        }

        auto [tree, lastModified] = downloadTarball(store, url.url, headers, "source", true);

        input.attrs.insert_or_assign("lastModified", lastModified);

        getCache()->add(
            store,
            immutableAttrs,
            {
                {"rev", rev->gitRev()},
                {"lastModified", lastModified}
            },
            tree.storePath,
            true);

        return {std::move(tree), input};
    }
};

struct GitHubInputScheme : GitArchiveInputScheme
{
    std::string type() override { return "github"; }

    std::pair<std::string, std::string> accessHeaderFromToken(const std::string & token) const {
        return std::pair<std::string, std::string>("Authorization", fmt("token %s", token));
    }

    Hash getRevFromRef(nix::ref<Store> store, const Input & input) const override
    {
        auto host_url = maybeGetStrAttr(input.attrs, "url").value_or("github.com");
        auto url = fmt("https://api.%s/repos/%s/%s/commits/%s", // FIXME: check
            host_url, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo"), *input.getRef());

        Headers headers;
        std::string accessToken = settings.githubAccessToken.get();
        if (accessToken != "")
            headers.push_back(accessHeaderFromToken(accessToken));

        auto json = nlohmann::json::parse(
            readFile(
                store->toRealPath(
                    downloadFile(store, url, headers, "source", false).storePath)));
        auto rev = Hash::parseAny(std::string { json["sha"] }, htSHA1);
        debug("HEAD revision for '%s' is %s", url, rev.gitRev());
        return rev;
    }

    DownloadUrl getDownloadUrl(const Input & input) const override
    {
        // FIXME: use regular /archive URLs instead? api.github.com
        // might have stricter rate limits.
        auto host_url = maybeGetStrAttr(input.attrs, "host").value_or("github.com");
        auto url = fmt("https://api.%s/repos/%s/%s/tarball/%s", // FIXME: check if this is correct for self hosted instances
            host_url, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo"),
            input.getRev()->to_string(Base16, false));

        std::string accessToken = settings.githubAccessToken.get();
        if (accessToken != "") {
            auto auth_header = accessHeaderFromToken(accessToken);
            return DownloadUrl(url, auth_header);
        } else {
            return DownloadUrl(url);
        }
    }

    void clone(const Input & input, const Path & destDir) override
    {
        auto host_url = maybeGetStrAttr(input.attrs, "url").value_or("github.com");
        Input::fromURL(fmt("git+ssh://git@%s/%s/%s.git",
                host_url, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo")))
            .applyOverrides(input.getRef().value_or("HEAD"), input.getRev())
            .clone(destDir);
    }
};

struct GitLabInputScheme : GitArchiveInputScheme
{
    std::string type() override { return "gitlab"; }

    std::pair<std::string, std::string> accessHeaderFromToken(const std::string & token) const {
        return std::pair<std::string, std::string>("Authorization", fmt("Bearer %s", token));
    }

    Hash getRevFromRef(nix::ref<Store> store, const Input & input) const override
    {
        auto host_url = maybeGetStrAttr(input.attrs, "host").value_or("gitlab.com");
        auto url = fmt("https://%s/api/v4/projects/%s%%2F%s/repository/commits?ref_name=%s",
            host_url, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo"), *input.getRef());

        Headers headers;
        std::string accessToken = settings.gitlabAccessToken.get();
        if (accessToken != "")
            headers.push_back(accessHeaderFromToken(accessToken));

        auto json = nlohmann::json::parse(
            readFile(
                store->toRealPath(
                    downloadFile(store, url, headers, "source", false).storePath)));
        auto rev = Hash::parseAny(std::string(json[0]["id"]), htSHA1);
        debug("HEAD revision for '%s' is %s", url, rev.gitRev());
        return rev;
    }

    DownloadUrl getDownloadUrl(const Input & input) const override
    {
        // FIXME: This endpoint has a rate limit threshold of 5 requests per minute
        auto host_url = maybeGetStrAttr(input.attrs, "url").value_or("gitlab.com");
        auto url = fmt("https://%s/api/v4/projects/%s%%2F%s/repository/archive.tar.gz?sha=%s",
            host_url, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo"),
            input.getRev()->to_string(Base16, false));

        std::string accessToken = settings.gitlabAccessToken.get();
        if (accessToken != "") {
            auto auth_header = accessHeaderFromToken(accessToken);
            return DownloadUrl(url, auth_header);
        } else {
            return DownloadUrl(url);
        }

    }

    void clone(const Input & input, const Path & destDir) override
    {
        auto host_url = maybeGetStrAttr(input.attrs, "url").value_or("gitlab.com");
        // FIXME: get username somewhere
        Input::fromURL(fmt("git+ssh://git@%s/%s/%s.git",
                host_url, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo")))
            .applyOverrides(input.getRef().value_or("HEAD"), input.getRev())
            .clone(destDir);
    }
};

static auto r1 = OnStartup([] { registerInputScheme(std::make_unique<GitHubInputScheme>()); });
static auto r2 = OnStartup([] { registerInputScheme(std::make_unique<GitLabInputScheme>()); });

}
<commit_msg>Cleanup<commit_after>#include "filetransfer.hh"
#include "cache.hh"
#include "fetchers.hh"
#include "globals.hh"
#include "store-api.hh"
#include "types.hh"
#include "url-parts.hh"

#include <nlohmann/json.hpp>

namespace nix::fetchers {

struct DownloadUrl
{
    std::string url;
    std::optional<std::pair<std::string, std::string>> access_token_header;
};

// A github or gitlab host
const static std::string hostRegexS = "[a-zA-Z0-9.]*"; // FIXME: check
std::regex hostRegex(hostRegexS, std::regex::ECMAScript);

struct GitArchiveInputScheme : InputScheme
{
    virtual std::string type() = 0;

    virtual std::pair<std::string, std::string> accessHeaderFromToken(const std::string & token) const = 0;

    std::optional<Input> inputFromURL(const ParsedURL & url) override
    {
        if (url.scheme != type()) return {};

        auto path = tokenizeString<std::vector<std::string>>(url.path, "/");

        std::optional<Hash> rev;
        std::optional<std::string> ref;
        std::optional<std::string> host_url;

        if (path.size() == 2) {
        } else if (path.size() == 3) {
            if (std::regex_match(path[2], revRegex))
                rev = Hash::parseAny(path[2], htSHA1);
            else if (std::regex_match(path[2], refRegex))
                ref = path[2];
            else
                throw BadURL("in URL '%s', '%s' is not a commit hash or branch/tag name", url.url, path[2]);
        } else
            throw BadURL("URL '%s' is invalid", url.url);

        for (auto &[name, value] : url.query) {
            if (name == "rev") {
                if (rev)
                    throw BadURL("URL '%s' contains multiple commit hashes", url.url);
                rev = Hash::parseAny(value, htSHA1);
            }
            else if (name == "ref") {
                if (!std::regex_match(value, refRegex))
                    throw BadURL("URL '%s' contains an invalid branch/tag name", url.url);
                if (ref)
                    throw BadURL("URL '%s' contains multiple branch/tag names", url.url);
                ref = value;
            }
            else if (name == "host") {
                if (!std::regex_match(value, hostRegex))
                    throw BadURL("URL '%s' contains an invalid instance host", url.url);
                host_url = value;
            }
            // FIXME: barf on unsupported attributes
        }

        if (ref && rev)
            throw BadURL("URL '%s' contains both a commit hash and a branch/tag name %s %s", url.url, *ref, rev->gitRev());

        Input input;
        input.attrs.insert_or_assign("type", type());
        input.attrs.insert_or_assign("owner", path[0]);
        input.attrs.insert_or_assign("repo", path[1]);
        if (rev) input.attrs.insert_or_assign("rev", rev->gitRev());
        if (ref) input.attrs.insert_or_assign("ref", *ref);
        if (host_url) input.attrs.insert_or_assign("host", *host_url);

        return input;
    }

    std::optional<Input> inputFromAttrs(const Attrs & attrs) override
    {
        if (maybeGetStrAttr(attrs, "type") != type()) return {};

        for (auto & [name, value] : attrs)
            if (name != "type" && name != "owner" && name != "repo" && name != "ref" && name != "rev" && name != "narHash" && name != "lastModified" && name != "host")
                throw Error("unsupported input attribute '%s'", name);

        getStrAttr(attrs, "owner");
        getStrAttr(attrs, "repo");

        Input input;
        input.attrs = attrs;
        return input;
    }

    ParsedURL toURL(const Input & input) override
    {
        auto owner = getStrAttr(input.attrs, "owner");
        auto repo = getStrAttr(input.attrs, "repo");
        auto ref = input.getRef();
        auto rev = input.getRev();
        auto path = owner + "/" + repo;
        assert(!(ref && rev));
        if (ref) path += "/" + *ref;
        if (rev) path += "/" + rev->to_string(Base16, false);
        return ParsedURL {
            .scheme = type(),
            .path = path,
        };
    }

    bool hasAllInfo(const Input & input) override
    {
        return input.getRev() && maybeGetIntAttr(input.attrs, "lastModified");
    }

    Input applyOverrides(
        const Input & _input,
        std::optional<std::string> ref,
        std::optional<Hash> rev) override
    {
        auto input(_input);
        if (rev && ref)
            throw BadURL("cannot apply both a commit hash (%s) and a branch/tag name ('%s') to input '%s'",
                rev->gitRev(), *ref, input.to_string());
        if (rev) {
            input.attrs.insert_or_assign("rev", rev->gitRev());
            input.attrs.erase("ref");
        }
        if (ref) {
            input.attrs.insert_or_assign("ref", *ref);
            input.attrs.erase("rev");
        }
        return input;
    }

    virtual Hash getRevFromRef(nix::ref<Store> store, const Input & input) const = 0;

    virtual DownloadUrl getDownloadUrl(const Input & input) const = 0;

    std::pair<Tree, Input> fetch(ref<Store> store, const Input & _input) override
    {
        Input input(_input);

        if (!maybeGetStrAttr(input.attrs, "ref")) input.attrs.insert_or_assign("ref", "HEAD");

        auto rev = input.getRev();
        if (!rev) rev = getRevFromRef(store, input);

        input.attrs.erase("ref");
        input.attrs.insert_or_assign("rev", rev->gitRev());

        Attrs immutableAttrs({
            {"type", "git-tarball"},
            {"rev", rev->gitRev()},
        });

        if (auto res = getCache()->lookup(store, immutableAttrs)) {
            input.attrs.insert_or_assign("lastModified", getIntAttr(res->first, "lastModified"));
            return {
                Tree(store->toRealPath(res->second), std::move(res->second)),
                input
            };
        }

        auto url = getDownloadUrl(input);

        Headers headers;
        if (url.access_token_header) {
            headers.push_back(*url.access_token_header);
        }

        auto [tree, lastModified] = downloadTarball(store, url.url, headers, "source", true);

        input.attrs.insert_or_assign("lastModified", lastModified);

        getCache()->add(
            store,
            immutableAttrs,
            {
                {"rev", rev->gitRev()},
                {"lastModified", lastModified}
            },
            tree.storePath,
            true);

        return {std::move(tree), input};
    }
};

struct GitHubInputScheme : GitArchiveInputScheme
{
    std::string type() override { return "github"; }

    std::pair<std::string, std::string> accessHeaderFromToken(const std::string & token) const {
        return std::pair<std::string, std::string>("Authorization", fmt("token %s", token));
    }

    Hash getRevFromRef(nix::ref<Store> store, const Input & input) const override
    {
        auto host_url = maybeGetStrAttr(input.attrs, "url").value_or("github.com");
        auto url = fmt("https://api.%s/repos/%s/%s/commits/%s", // FIXME: check
            host_url, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo"), *input.getRef());

        Headers headers;
        std::string accessToken = settings.githubAccessToken.get();
        if (accessToken != "")
            headers.push_back(accessHeaderFromToken(accessToken));

        auto json = nlohmann::json::parse(
            readFile(
                store->toRealPath(
                    downloadFile(store, url, headers, "source", false).storePath)));
        auto rev = Hash::parseAny(std::string { json["sha"] }, htSHA1);
        debug("HEAD revision for '%s' is %s", url, rev.gitRev());
        return rev;
    }

    DownloadUrl getDownloadUrl(const Input & input) const override
    {
        // FIXME: use regular /archive URLs instead? api.github.com
        // might have stricter rate limits.
        auto host_url = maybeGetStrAttr(input.attrs, "host").value_or("github.com");
        auto url = fmt("https://api.%s/repos/%s/%s/tarball/%s", // FIXME: check if this is correct for self hosted instances
            host_url, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo"),
            input.getRev()->to_string(Base16, false));

        std::string accessToken = settings.githubAccessToken.get();
        if (accessToken != "") {
            auto auth_header = accessHeaderFromToken(accessToken);
            return DownloadUrl { url, auth_header };
        } else {
            return DownloadUrl { url };
        }
    }

    void clone(const Input & input, const Path & destDir) override
    {
        auto host_url = maybeGetStrAttr(input.attrs, "url").value_or("github.com");
        Input::fromURL(fmt("git+ssh://git@%s/%s/%s.git",
                host_url, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo")))
            .applyOverrides(input.getRef().value_or("HEAD"), input.getRev())
            .clone(destDir);
    }
};

struct GitLabInputScheme : GitArchiveInputScheme
{
    std::string type() override { return "gitlab"; }

    std::pair<std::string, std::string> accessHeaderFromToken(const std::string & token) const {
        return std::pair<std::string, std::string>("Authorization", fmt("Bearer %s", token));
    }

    Hash getRevFromRef(nix::ref<Store> store, const Input & input) const override
    {
        auto host_url = maybeGetStrAttr(input.attrs, "host").value_or("gitlab.com");
        auto url = fmt("https://%s/api/v4/projects/%s%%2F%s/repository/commits?ref_name=%s",
            host_url, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo"), *input.getRef());

        Headers headers;
        std::string accessToken = settings.gitlabAccessToken.get();
        if (accessToken != "")
            headers.push_back(accessHeaderFromToken(accessToken));

        auto json = nlohmann::json::parse(
            readFile(
                store->toRealPath(
                    downloadFile(store, url, headers, "source", false).storePath)));
        auto rev = Hash::parseAny(std::string(json[0]["id"]), htSHA1);
        debug("HEAD revision for '%s' is %s", url, rev.gitRev());
        return rev;
    }

    DownloadUrl getDownloadUrl(const Input & input) const override
    {
        // FIXME: This endpoint has a rate limit threshold of 5 requests per minute
        auto host_url = maybeGetStrAttr(input.attrs, "url").value_or("gitlab.com");
        auto url = fmt("https://%s/api/v4/projects/%s%%2F%s/repository/archive.tar.gz?sha=%s",
            host_url, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo"),
            input.getRev()->to_string(Base16, false));

        std::string accessToken = settings.gitlabAccessToken.get();
        if (accessToken != "") {
            auto auth_header = accessHeaderFromToken(accessToken);
            return DownloadUrl { url, auth_header };
        } else {
            return DownloadUrl { url };
        }

    }

    void clone(const Input & input, const Path & destDir) override
    {
        auto host_url = maybeGetStrAttr(input.attrs, "url").value_or("gitlab.com");
        // FIXME: get username somewhere
        Input::fromURL(fmt("git+ssh://git@%s/%s/%s.git",
                host_url, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo")))
            .applyOverrides(input.getRef().value_or("HEAD"), input.getRev())
            .clone(destDir);
    }
};

static auto r1 = OnStartup([] { registerInputScheme(std::make_unique<GitHubInputScheme>()); });
static auto r2 = OnStartup([] { registerInputScheme(std::make_unique<GitLabInputScheme>()); });

}
<|endoftext|>


<commit_before>/*
Copyright libCellML Contributors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#include "xmldoc.h"

#include <vector>

#include "libcellml/component.h"
#include "libcellml/generator.h"
#include "libcellml/model.h"
#include "libcellml/validator.h"

namespace libcellml{

struct GeneratorVariable
{
};

typedef std::shared_ptr<GeneratorVariable> GeneratorVariablePtr;

struct Generator::GeneratorImpl
{
    bool mWithNames;

    std::vector<GeneratorVariablePtr> mStates;
    std::vector<GeneratorVariablePtr> mVariables;

    size_t mathmlChildCount(const XmlNodePtr &node) const;
    XmlNodePtr mathmlChildNode(const XmlNodePtr &node, int index) const;

    void processNode(const XmlNodePtr &node);
};

size_t Generator::GeneratorImpl::mathmlChildCount(const XmlNodePtr &node) const
{
    // Return the number of child elements, in the MathML namespace, for the
    // given node

    size_t res = 0;
    XmlNodePtr childNode = node->getFirstChild();

    while (childNode != nullptr) {
        if (childNode->isMathmlElement()) {
            ++res;
        }

        childNode = childNode->getNext();
    }

    return res;
}

XmlNodePtr Generator::GeneratorImpl::mathmlChildNode(const XmlNodePtr &node, int index) const
{
    // Return the nth child element of the given node, skipping anything that is
    // not int he MathML namespace

    // Retrieve the first MathML node, if it exists

    XmlNodePtr res = node->getFirstChild();

    while (res && !res->isMathmlElement()) {
        res = res->getNext();
    }

    // Retrieve the nth MathML element, if it exists

    int nodeIndex = 0;

    while (res && (nodeIndex != index)) {
        while (res && !res->isMathmlElement()) {
            res = res->getNext();
        }

        ++nodeIndex;
    }

    return res;
}

void Generator::GeneratorImpl::processNode(const XmlNodePtr &node)
{
(void) node;

//TODO
}

Generator::Generator()
    : mPimpl(new GeneratorImpl())
{
}

Generator::~Generator()
{
    delete mPimpl;
}

Generator::Generator(const Generator &rhs)
    : Logger(rhs)
    , mPimpl(new GeneratorImpl())
{
    mPimpl->mStates = rhs.mPimpl->mStates;
    mPimpl->mVariables = rhs.mPimpl->mVariables;
}

Generator::Generator(Generator &&rhs)
    : Logger(std::move(rhs))
    , mPimpl(rhs.mPimpl)
{
    rhs.mPimpl = nullptr;
}

Generator& Generator::operator=(Generator rhs)
{
    Logger::operator =(rhs);
    rhs.swap(*this);
    return *this;
}

void Generator::swap(Generator &rhs)
{
    std::swap(this->mPimpl, rhs.mPimpl);
}

void Generator::processModel(const ModelPtr &model)
{
    // Make sure that the model is valid before processing it
/*TODO: reenable the validation once it is known to work fine...
    libcellml::Validator validator;

    validator.validateModel(model);

    if (validator.errorCount() > 0) {
        // The model is not valid, so retrieve the validation errors and keep a
        // copy of them

        for (size_t i = 0; i < validator.errorCount(); ++i)
            addError(validator.getError(i));

        return;
    }
*/
    // Determine the order in which equations should be executed by processing
    // each of the components in the given model

    for (size_t i = 0; i < model->componentCount(); ++i) {
        // Retrieve the math string associated with the given component and
        // process it, one equation at a time
        // Note: at this stage, we know the model is valid, so no point in
        //       revalidating the math string...

        ComponentPtr component = model->getComponent(i);
        XmlDocPtr xmlDoc = std::make_shared<XmlDoc>();
        std::string math = component->getMath();

        xmlDoc->parse(math);

        XmlNodePtr mathNode = xmlDoc->getRootNode();

        for (XmlNodePtr node = mathNode->getFirstChild();
             node != nullptr; node = node->getNext()) {
            mPimpl->processNode(node);
        }
    }
}

void Generator::setWithNames(bool withNames)
{
    mPimpl->mWithNames = withNames;
}

size_t Generator::stateCount() const
{
    return mPimpl->mStates.size();
}

size_t Generator::rateCount() const
{
    return stateCount();
}

size_t Generator::variableCount() const
{
    return mPimpl->mVariables.size();
}

std::string Generator::initializeVariables() const
{
    return "";
}

std::string Generator::computeConstantEquations() const
{
    return "";
}

std::string Generator::computeRateEquations() const
{
    return "";
}

std::string Generator::computeAlgebraicEquations() const
{
    return "";
}

}
<commit_msg>Generator: skeleton of the GeneratorEquation and GeneratorEquationBinTree classes.<commit_after>/*
Copyright libCellML Contributors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#include "xmldoc.h"

#include <vector>

#include "libcellml/component.h"
#include "libcellml/generator.h"
#include "libcellml/model.h"
#include "libcellml/validator.h"

namespace libcellml{

class GeneratorEquationBinTree;
typedef std::shared_ptr<GeneratorEquationBinTree> GeneratorEquationBinTreePtr;

class GeneratorEquationBinTree
{
public:
    explicit GeneratorEquationBinTree();

    GeneratorEquationBinTreePtr &left();
    GeneratorEquationBinTreePtr &right();

private:
    GeneratorEquationBinTreePtr mLeft;
    GeneratorEquationBinTreePtr mRight;
};

GeneratorEquationBinTree::GeneratorEquationBinTree()
    : mLeft(nullptr)
    , mRight(nullptr)
{
}

GeneratorEquationBinTreePtr &GeneratorEquationBinTree::left()
{
    return mLeft;
}

GeneratorEquationBinTreePtr &GeneratorEquationBinTree::right()
{
    return mRight;
}

class GeneratorEquation;
typedef std::shared_ptr<GeneratorEquation> GeneratorEquationPtr;

class GeneratorEquation
{
public:
    explicit GeneratorEquation();

    const GeneratorEquationBinTreePtr &binTree() const;

private:
    GeneratorEquationBinTreePtr mBinTree;
};

GeneratorEquation::GeneratorEquation()
    : mBinTree(std::make_shared<GeneratorEquationBinTree>())
{
}

const GeneratorEquationBinTreePtr &GeneratorEquation::binTree() const
{
    return mBinTree;
}

struct GeneratorVariable
{
};

typedef std::shared_ptr<GeneratorVariable> GeneratorVariablePtr;

struct Generator::GeneratorImpl
{
    bool mWithNames;

    std::vector<GeneratorVariablePtr> mStates;
    std::vector<GeneratorVariablePtr> mVariables;

    size_t mathmlChildCount(const XmlNodePtr &node) const;
    XmlNodePtr mathmlChildNode(const XmlNodePtr &node, int index) const;

    void processNode(const XmlNodePtr &node);
};

size_t Generator::GeneratorImpl::mathmlChildCount(const XmlNodePtr &node) const
{
    // Return the number of child elements, in the MathML namespace, for the
    // given node

    size_t res = 0;
    XmlNodePtr childNode = node->getFirstChild();

    while (childNode != nullptr) {
        if (childNode->isMathmlElement()) {
            ++res;
        }

        childNode = childNode->getNext();
    }

    return res;
}

XmlNodePtr Generator::GeneratorImpl::mathmlChildNode(const XmlNodePtr &node, int index) const
{
    // Return the nth child element of the given node, skipping anything that is
    // not int he MathML namespace

    // Retrieve the first MathML node, if it exists

    XmlNodePtr res = node->getFirstChild();

    while (res && !res->isMathmlElement()) {
        res = res->getNext();
    }

    // Retrieve the nth MathML element, if it exists

    int nodeIndex = 0;

    while (res && (nodeIndex != index)) {
        while (res && !res->isMathmlElement()) {
            res = res->getNext();
        }

        ++nodeIndex;
    }

    return res;
}

void Generator::GeneratorImpl::processNode(const XmlNodePtr &node)
{
(void) node;

//TODO
}

Generator::Generator()
    : mPimpl(new GeneratorImpl())
{
}

Generator::~Generator()
{
    delete mPimpl;
}

Generator::Generator(const Generator &rhs)
    : Logger(rhs)
    , mPimpl(new GeneratorImpl())
{
    mPimpl->mStates = rhs.mPimpl->mStates;
    mPimpl->mVariables = rhs.mPimpl->mVariables;
}

Generator::Generator(Generator &&rhs)
    : Logger(std::move(rhs))
    , mPimpl(rhs.mPimpl)
{
    rhs.mPimpl = nullptr;
}

Generator& Generator::operator=(Generator rhs)
{
    Logger::operator =(rhs);
    rhs.swap(*this);
    return *this;
}

void Generator::swap(Generator &rhs)
{
    std::swap(this->mPimpl, rhs.mPimpl);
}

void Generator::processModel(const ModelPtr &model)
{
    // Make sure that the model is valid before processing it
/*TODO: reenable the validation once it is known to work fine...
    libcellml::Validator validator;

    validator.validateModel(model);

    if (validator.errorCount() > 0) {
        // The model is not valid, so retrieve the validation errors and keep a
        // copy of them

        for (size_t i = 0; i < validator.errorCount(); ++i)
            addError(validator.getError(i));

        return;
    }
*/
    // Determine the order in which equations should be executed by processing
    // each of the components in the given model

    for (size_t i = 0; i < model->componentCount(); ++i) {
        // Retrieve the math string associated with the given component and
        // process it, one equation at a time
        // Note: at this stage, we know the model is valid, so no point in
        //       revalidating the math string...

        ComponentPtr component = model->getComponent(i);
        XmlDocPtr xmlDoc = std::make_shared<XmlDoc>();
        std::string math = component->getMath();

        xmlDoc->parse(math);

        XmlNodePtr mathNode = xmlDoc->getRootNode();

        for (XmlNodePtr node = mathNode->getFirstChild();
             node != nullptr; node = node->getNext()) {
            mPimpl->processNode(node);
        }
    }
}

void Generator::setWithNames(bool withNames)
{
    mPimpl->mWithNames = withNames;
}

size_t Generator::stateCount() const
{
    return mPimpl->mStates.size();
}

size_t Generator::rateCount() const
{
    return stateCount();
}

size_t Generator::variableCount() const
{
    return mPimpl->mVariables.size();
}

std::string Generator::initializeVariables() const
{
    return "";
}

std::string Generator::computeConstantEquations() const
{
    return "";
}

std::string Generator::computeRateEquations() const
{
    return "";
}

std::string Generator::computeAlgebraicEquations() const
{
    return "";
}

}
<|endoftext|>


<commit_before>#include <memory>
#include <iostream>
#include <string>
#include <sstream>
#include <functional>
#include <utility>
#include <unordered_map>
#include <vector>
#include <nan.h>
#include <v8.h>
#include <uv.h>

#include "log.h"
#include "queue.h"
#include "status.h"
#include "result.h"
#include "worker/worker_thread.h"

using v8::Local;
using v8::Value;
using v8::Object;
using v8::String;
using v8::Number;
using v8::Uint32;
using v8::Function;
using v8::FunctionTemplate;
using v8::Array;
using std::shared_ptr;
using std::unique_ptr;
using std::string;
using std::ostringstream;
using std::endl;
using std::unordered_map;
using std::vector;
using std::move;
using std::make_pair;

static void handle_events_helper(uv_async_t *handle);

class Main {
public:
  Main() : worker_thread{&event_handler}
  {
    int err;

    next_command_id = 0;
    next_channel_id = NULL_CHANNEL_ID + 1;

    err = uv_async_init(uv_default_loop(), &event_handler, handle_events_helper);
    if (err) return;

    worker_thread.run();
  }

  Result<> send_worker_command(
    const CommandAction action,
    const std::string &&root,
    unique_ptr<Nan::Callback> callback,
    ChannelID channel_id = NULL_CHANNEL_ID
  )
  {
    CommandID command_id = next_command_id;

    CommandPayload command_payload(next_command_id, action, move(root), channel_id);
    Message command_message(move(command_payload));

    pending_callbacks.emplace(command_id, move(callback));

    next_command_id++;

    LOGGER << "Sending command " << command_message << " to worker thread." << endl;
    return worker_thread.send(move(command_message));
  }

  void use_main_log_file(string &&main_log_file)
  {
    Logger::to_file(main_log_file.c_str());
  }

  void disable_main_log()
  {
    Logger::disable();
  }

  Result<> use_worker_log_file(string &&worker_log_file, unique_ptr<Nan::Callback> callback)
  {
    return send_worker_command(COMMAND_LOG_FILE, move(worker_log_file), move(callback));
  }

  Result<> disable_worker_log(unique_ptr<Nan::Callback> callback)
  {
    return send_worker_command(COMMAND_LOG_DISABLE, "", move(callback));
  }

  Result<> watch(string &&root, unique_ptr<Nan::Callback> ack_callback, unique_ptr<Nan::Callback> event_callback)
  {
    ChannelID channel_id = next_channel_id;
    next_channel_id++;

    channel_callbacks.emplace(channel_id, move(event_callback));

    return send_worker_command(COMMAND_ADD, move(root), move(ack_callback), channel_id);
  }

  Result<> unwatch(ChannelID channel_id, unique_ptr<Nan::Callback> ack_callback)
  {
    string root;
    Result<> r = send_worker_command(COMMAND_REMOVE, move(root), move(ack_callback), channel_id);

    auto maybe_event_callback = channel_callbacks.find(channel_id);
    if (maybe_event_callback == channel_callbacks.end()) {
      LOGGER << "Channel " << channel_id << " already has no event callback." << endl;
      return ok_result();
    }
    channel_callbacks.erase(maybe_event_callback);
    return r;
  }

  void handle_events()
  {
    Nan::HandleScope scope;

    Result< unique_ptr<vector<Message>> > rr = worker_thread.receive_all();
    if (rr.is_error()) {
      LOGGER << "Unable to receive messages from the worker thread: " << rr << "." << endl;
      return;
    }

    unique_ptr<vector<Message>> &accepted = rr.get_value();
    if (!accepted) {
      // No events to process.
      return;
    }

    unordered_map<ChannelID, vector<Local<Object>>> to_deliver;

    for (auto it = accepted->begin(); it != accepted->end(); ++it) {
      const AckPayload *ack_message = it->as_ack();
      if (ack_message) {
        LOGGER << "Received ack message " << *it << "." << endl;

        auto maybe_callback = pending_callbacks.find(ack_message->get_key());
        if (maybe_callback == pending_callbacks.end()) {
          LOGGER << "Ignoring unexpected ack " << *it << "." << endl;
          continue;
        }

        unique_ptr<Nan::Callback> callback = move(maybe_callback->second);
        pending_callbacks.erase(maybe_callback);

        ChannelID channel_id = ack_message->get_channel_id();
        if (channel_id != NULL_CHANNEL_ID) {
          if (ack_message->was_successful()) {
            Local<Value> argv[] = {Nan::Null(), Nan::New<Number>(channel_id)};
            callback->Call(2, argv);
          } else {
            Local<Value> err = Nan::Error(ack_message->get_message().c_str());
            Local<Value> argv[] = {err, Nan::Null()};
            callback->Call(2, argv);
          }
        } else {
          callback->Call(0, nullptr);
        }

        continue;
      }

      const FileSystemPayload *filesystem_message = it->as_filesystem();
      if (filesystem_message) {
        LOGGER << "Received filesystem event message " << *it << "." << endl;

        ChannelID channel_id = filesystem_message->get_channel_id();

        Local<Object> js_event = Nan::New<Object>();
        js_event->Set(
          Nan::New<String>("actionType").ToLocalChecked(),
          Nan::New<Number>(static_cast<int>(filesystem_message->get_filesystem_action()))
        );
        js_event->Set(
          Nan::New<String>("entryKind").ToLocalChecked(),
          Nan::New<Number>(static_cast<int>(filesystem_message->get_entry_kind()))
        );
        js_event->Set(
          Nan::New<String>("oldPath").ToLocalChecked(),
          Nan::New<String>(filesystem_message->get_old_path()).ToLocalChecked()
        );
        js_event->Set(
          Nan::New<String>("newPath").ToLocalChecked(),
          Nan::New<String>(filesystem_message->get_new_path()).ToLocalChecked()
        );

        to_deliver[channel_id].push_back(js_event);
        continue;
      }

      LOGGER << "Received unexpected message " << *it << "." << endl;
    }

    for (auto it = to_deliver.begin(); it != to_deliver.end(); ++it) {
      ChannelID channel_id = it->first;
      vector<Local<Object>> js_events = it->second;

      auto maybe_callback = channel_callbacks.find(channel_id);
      if (maybe_callback == channel_callbacks.end()) {
        LOGGER << "Ignoring unexpected filesystem event channel " << channel_id << "." << endl;
        continue;
      }
      shared_ptr<Nan::Callback> callback = maybe_callback->second;

      LOGGER << "Dispatching " << js_events.size()
        << " event(s) on channel " << channel_id << " to node callbacks." << endl;

      Local<Array> js_array = Nan::New<Array>(js_events.size());

      int index = 0;
      for (auto et = js_events.begin(); et != js_events.end(); ++et) {
        js_array->Set(index, *et);
        index++;
      }

      Local<Value> argv[] = {
        Nan::Null(),
        js_array
      };
      callback->Call(2, argv);
    }
  }

  void collect_status(Status &status)
  {
    status.pending_callback_count = pending_callbacks.size();
    status.channel_callback_count = channel_callbacks.size();

    worker_thread.collect_status(status);
  }

private:
  uv_async_t event_handler;

  WorkerThread worker_thread;

  CommandID next_command_id;
  ChannelID next_channel_id;

  unordered_map<CommandID, unique_ptr<Nan::Callback>> pending_callbacks;
  unordered_map<ChannelID, shared_ptr<Nan::Callback>> channel_callbacks;
};

static Main instance;

static void handle_events_helper(uv_async_t *handle)
{
  instance.handle_events();
}

static bool get_string_option(Local<Object>& options, const char *key_name, string &out)
{
  Nan::HandleScope scope;
  const Local<String> key = Nan::New<String>(key_name).ToLocalChecked();

  Nan::MaybeLocal<Value> as_maybe_value = Nan::Get(options, key);
  if (as_maybe_value.IsEmpty()) {
    return true;
  }
  Local<Value> as_value = as_maybe_value.ToLocalChecked();
  if (as_value->IsUndefined()) {
    return true;
  }

  if (!as_value->IsString()) {
    ostringstream message;
    message << "configure() option " << key_name << " must be a String";
    Nan::ThrowError(message.str().c_str());
    return false;
  }

  Nan::Utf8String as_string(as_value);

  if (*as_string == nullptr) {
    ostringstream message;
    message << "configure() option " << key_name << " must be a valid UTF-8 String";
    Nan::ThrowError(message.str().c_str());
    return false;
  }

  out.assign(*as_string, as_string.length());
  return true;
}

void configure(const Nan::FunctionCallbackInfo<Value> &info)
{
  string main_log_file;
  string worker_log_file;
  bool async = false;

  Nan::MaybeLocal<Object> maybe_options = Nan::To<Object>(info[0]);
  if (maybe_options.IsEmpty()) {
    Nan::ThrowError("configure() requires an option object");
    return;
  }

  Local<Object> options = maybe_options.ToLocalChecked();
  if (!get_string_option(options, "mainLogFile", main_log_file)) return;
  if (!get_string_option(options, "workerLogFile", worker_log_file)) return;

  unique_ptr<Nan::Callback> callback(new Nan::Callback(info[1].As<Function>()));

  if (!main_log_file.empty()) {
    instance.use_main_log_file(move(main_log_file));
  }

  if (!worker_log_file.empty()) {
    Result<> r = instance.use_worker_log_file(move(worker_log_file), move(callback));
    if (r.is_error()) {
      Nan::ThrowError(r.get_error().c_str());
      return;
    }
    async = true;
  }

  if (!async) {
    callback->Call(0, 0);
  }
}

void watch(const Nan::FunctionCallbackInfo<Value> &info)
{
  if (info.Length() != 3) {
    return Nan::ThrowError("watch() requires three arguments");
  }

  Nan::MaybeLocal<String> maybe_root = Nan::To<String>(info[0]);
  if (maybe_root.IsEmpty()) {
    Nan::ThrowError("watch() requires a string as argument one");
    return;
  }
  Local<String> root_v8_string = maybe_root.ToLocalChecked();
  Nan::Utf8String root_utf8(root_v8_string);
  if (*root_utf8 == nullptr) {
    Nan::ThrowError("watch() argument one must be a valid UTF-8 string");
    return;
  }
  string root_str(*root_utf8, root_utf8.length());

  unique_ptr<Nan::Callback> ack_callback(new Nan::Callback(info[1].As<Function>()));
  unique_ptr<Nan::Callback> event_callback(new Nan::Callback(info[2].As<Function>()));

  Result<> r = instance.watch(move(root_str), move(ack_callback), move(event_callback));
  if (r.is_error()) {
    Nan::ThrowError(r.get_error().c_str());
  }
}

void unwatch(const Nan::FunctionCallbackInfo<Value> &info)
{
  if (info.Length() != 2) {
    Nan::ThrowError("watch() requires two arguments");
    return;
  }

  Nan::Maybe<uint32_t> maybe_channel_id = Nan::To<uint32_t>(info[0]);
  if (maybe_channel_id.IsNothing()) {
    Nan::ThrowError("unwatch() requires a channel ID as its first argument");
    return;
  }
  ChannelID channel_id = static_cast<ChannelID>(maybe_channel_id.FromJust());

  unique_ptr<Nan::Callback> ack_callback(new Nan::Callback(info[1].As<Function>()));

  Result<> r = instance.unwatch(channel_id, move(ack_callback));
  if (r.is_error()) {
    Nan::ThrowError(r.get_error().c_str());
  }
}

void status(const Nan::FunctionCallbackInfo<Value> &info)
{
  Status status;
  instance.collect_status(status);

  Local<Object> status_object = Nan::New<Object>();
  Nan::Set(
    status_object,
    Nan::New<String>("pendingCallbackCount").ToLocalChecked(),
    Nan::New<Uint32>(static_cast<uint32_t>(status.pending_callback_count))
  );
  Nan::Set(
    status_object,
    Nan::New<String>("channelCallbackCount").ToLocalChecked(),
    Nan::New<Uint32>(static_cast<uint32_t>(status.channel_callback_count))
  );
  Nan::Set(
    status_object,
    Nan::New<String>("workerThreadOk").ToLocalChecked(),
    Nan::New<String>(status.worker_thread_ok).ToLocalChecked()
  );
  Nan::Set(
    status_object,
    Nan::New<String>("workerInSize").ToLocalChecked(),
    Nan::New<Uint32>(static_cast<uint32_t>(status.worker_in_size))
  );
  Nan::Set(
    status_object,
    Nan::New<String>("workerInOk").ToLocalChecked(),
    Nan::New<String>(status.worker_in_ok).ToLocalChecked()
  );
  Nan::Set(
    status_object,
    Nan::New<String>("workerOutSize").ToLocalChecked(),
    Nan::New<Uint32>(static_cast<uint32_t>(status.worker_out_size))
  );
  Nan::Set(
    status_object,
    Nan::New<String>("workerOutOk").ToLocalChecked(),
    Nan::New<String>(status.worker_out_ok).ToLocalChecked()
  );
  info.GetReturnValue().Set(status_object);
}

void initialize(Local<Object> exports)
{
  Nan::Set(
    exports,
    Nan::New<String>("configure").ToLocalChecked(),
    Nan::GetFunction(Nan::New<FunctionTemplate>(configure)).ToLocalChecked()
  );
  Nan::Set(
    exports,
    Nan::New<String>("watch").ToLocalChecked(),
    Nan::GetFunction(Nan::New<FunctionTemplate>(watch)).ToLocalChecked()
  );
  Nan::Set(
    exports,
    Nan::New<String>("unwatch").ToLocalChecked(),
    Nan::GetFunction(Nan::New<FunctionTemplate>(unwatch)).ToLocalChecked()
  );
  Nan::Set(
    exports,
    Nan::New<String>("status").ToLocalChecked(),
    Nan::GetFunction(Nan::New<FunctionTemplate>(status)).ToLocalChecked()
  );
}

NODE_MODULE(watcher, initialize);
<commit_msg>Permit null in string options<commit_after>#include <memory>
#include <iostream>
#include <string>
#include <sstream>
#include <functional>
#include <utility>
#include <unordered_map>
#include <vector>
#include <nan.h>
#include <v8.h>
#include <uv.h>

#include "log.h"
#include "queue.h"
#include "status.h"
#include "result.h"
#include "worker/worker_thread.h"

using v8::Local;
using v8::Value;
using v8::Object;
using v8::String;
using v8::Number;
using v8::Uint32;
using v8::Function;
using v8::FunctionTemplate;
using v8::Array;
using std::shared_ptr;
using std::unique_ptr;
using std::string;
using std::ostringstream;
using std::endl;
using std::unordered_map;
using std::vector;
using std::move;
using std::make_pair;

static void handle_events_helper(uv_async_t *handle);

class Main {
public:
  Main() : worker_thread{&event_handler}
  {
    int err;

    next_command_id = 0;
    next_channel_id = NULL_CHANNEL_ID + 1;

    err = uv_async_init(uv_default_loop(), &event_handler, handle_events_helper);
    if (err) return;

    worker_thread.run();
  }

  Result<> send_worker_command(
    const CommandAction action,
    const std::string &&root,
    unique_ptr<Nan::Callback> callback,
    ChannelID channel_id = NULL_CHANNEL_ID
  )
  {
    CommandID command_id = next_command_id;

    CommandPayload command_payload(next_command_id, action, move(root), channel_id);
    Message command_message(move(command_payload));

    pending_callbacks.emplace(command_id, move(callback));

    next_command_id++;

    LOGGER << "Sending command " << command_message << " to worker thread." << endl;
    return worker_thread.send(move(command_message));
  }

  void use_main_log_file(string &&main_log_file)
  {
    Logger::to_file(main_log_file.c_str());
  }

  void disable_main_log()
  {
    Logger::disable();
  }

  Result<> use_worker_log_file(string &&worker_log_file, unique_ptr<Nan::Callback> callback)
  {
    return send_worker_command(COMMAND_LOG_FILE, move(worker_log_file), move(callback));
  }

  Result<> disable_worker_log(unique_ptr<Nan::Callback> callback)
  {
    return send_worker_command(COMMAND_LOG_DISABLE, "", move(callback));
  }

  Result<> watch(string &&root, unique_ptr<Nan::Callback> ack_callback, unique_ptr<Nan::Callback> event_callback)
  {
    ChannelID channel_id = next_channel_id;
    next_channel_id++;

    channel_callbacks.emplace(channel_id, move(event_callback));

    return send_worker_command(COMMAND_ADD, move(root), move(ack_callback), channel_id);
  }

  Result<> unwatch(ChannelID channel_id, unique_ptr<Nan::Callback> ack_callback)
  {
    string root;
    Result<> r = send_worker_command(COMMAND_REMOVE, move(root), move(ack_callback), channel_id);

    auto maybe_event_callback = channel_callbacks.find(channel_id);
    if (maybe_event_callback == channel_callbacks.end()) {
      LOGGER << "Channel " << channel_id << " already has no event callback." << endl;
      return ok_result();
    }
    channel_callbacks.erase(maybe_event_callback);
    return r;
  }

  void handle_events()
  {
    Nan::HandleScope scope;

    Result< unique_ptr<vector<Message>> > rr = worker_thread.receive_all();
    if (rr.is_error()) {
      LOGGER << "Unable to receive messages from the worker thread: " << rr << "." << endl;
      return;
    }

    unique_ptr<vector<Message>> &accepted = rr.get_value();
    if (!accepted) {
      // No events to process.
      return;
    }

    unordered_map<ChannelID, vector<Local<Object>>> to_deliver;

    for (auto it = accepted->begin(); it != accepted->end(); ++it) {
      const AckPayload *ack_message = it->as_ack();
      if (ack_message) {
        LOGGER << "Received ack message " << *it << "." << endl;

        auto maybe_callback = pending_callbacks.find(ack_message->get_key());
        if (maybe_callback == pending_callbacks.end()) {
          LOGGER << "Ignoring unexpected ack " << *it << "." << endl;
          continue;
        }

        unique_ptr<Nan::Callback> callback = move(maybe_callback->second);
        pending_callbacks.erase(maybe_callback);

        ChannelID channel_id = ack_message->get_channel_id();
        if (channel_id != NULL_CHANNEL_ID) {
          if (ack_message->was_successful()) {
            Local<Value> argv[] = {Nan::Null(), Nan::New<Number>(channel_id)};
            callback->Call(2, argv);
          } else {
            Local<Value> err = Nan::Error(ack_message->get_message().c_str());
            Local<Value> argv[] = {err, Nan::Null()};
            callback->Call(2, argv);
          }
        } else {
          callback->Call(0, nullptr);
        }

        continue;
      }

      const FileSystemPayload *filesystem_message = it->as_filesystem();
      if (filesystem_message) {
        LOGGER << "Received filesystem event message " << *it << "." << endl;

        ChannelID channel_id = filesystem_message->get_channel_id();

        Local<Object> js_event = Nan::New<Object>();
        js_event->Set(
          Nan::New<String>("actionType").ToLocalChecked(),
          Nan::New<Number>(static_cast<int>(filesystem_message->get_filesystem_action()))
        );
        js_event->Set(
          Nan::New<String>("entryKind").ToLocalChecked(),
          Nan::New<Number>(static_cast<int>(filesystem_message->get_entry_kind()))
        );
        js_event->Set(
          Nan::New<String>("oldPath").ToLocalChecked(),
          Nan::New<String>(filesystem_message->get_old_path()).ToLocalChecked()
        );
        js_event->Set(
          Nan::New<String>("newPath").ToLocalChecked(),
          Nan::New<String>(filesystem_message->get_new_path()).ToLocalChecked()
        );

        to_deliver[channel_id].push_back(js_event);
        continue;
      }

      LOGGER << "Received unexpected message " << *it << "." << endl;
    }

    for (auto it = to_deliver.begin(); it != to_deliver.end(); ++it) {
      ChannelID channel_id = it->first;
      vector<Local<Object>> js_events = it->second;

      auto maybe_callback = channel_callbacks.find(channel_id);
      if (maybe_callback == channel_callbacks.end()) {
        LOGGER << "Ignoring unexpected filesystem event channel " << channel_id << "." << endl;
        continue;
      }
      shared_ptr<Nan::Callback> callback = maybe_callback->second;

      LOGGER << "Dispatching " << js_events.size()
        << " event(s) on channel " << channel_id << " to node callbacks." << endl;

      Local<Array> js_array = Nan::New<Array>(js_events.size());

      int index = 0;
      for (auto et = js_events.begin(); et != js_events.end(); ++et) {
        js_array->Set(index, *et);
        index++;
      }

      Local<Value> argv[] = {
        Nan::Null(),
        js_array
      };
      callback->Call(2, argv);
    }
  }

  void collect_status(Status &status)
  {
    status.pending_callback_count = pending_callbacks.size();
    status.channel_callback_count = channel_callbacks.size();

    worker_thread.collect_status(status);
  }

private:
  uv_async_t event_handler;

  WorkerThread worker_thread;

  CommandID next_command_id;
  ChannelID next_channel_id;

  unordered_map<CommandID, unique_ptr<Nan::Callback>> pending_callbacks;
  unordered_map<ChannelID, shared_ptr<Nan::Callback>> channel_callbacks;
};

static Main instance;

static void handle_events_helper(uv_async_t *handle)
{
  instance.handle_events();
}

static bool get_string_option(Local<Object>& options, const char *key_name, bool &null, string &out)
{
  Nan::HandleScope scope;
  null = false;
  const Local<String> key = Nan::New<String>(key_name).ToLocalChecked();

  Nan::MaybeLocal<Value> as_maybe_value = Nan::Get(options, key);
  if (as_maybe_value.IsEmpty()) {
    return true;
  }
  Local<Value> as_value = as_maybe_value.ToLocalChecked();
  if (as_value->IsUndefined()) {
    return true;
  }

  if (as_value->IsNull()) {
    null = true;
    return true;
  }

  if (!as_value->IsString()) {
    ostringstream message;
    message << "configure() option " << key_name << " must be a String";
    Nan::ThrowError(message.str().c_str());
    return false;
  }

  Nan::Utf8String as_string(as_value);

  if (*as_string == nullptr) {
    ostringstream message;
    message << "configure() option " << key_name << " must be a valid UTF-8 String";
    Nan::ThrowError(message.str().c_str());
    return false;
  }

  out.assign(*as_string, as_string.length());
  return true;
}

void configure(const Nan::FunctionCallbackInfo<Value> &info)
{
  bool main_log_file_null = false;
  string main_log_file;

  bool worker_log_file_null = false;
  string worker_log_file;

  bool async = false;

  Nan::MaybeLocal<Object> maybe_options = Nan::To<Object>(info[0]);
  if (maybe_options.IsEmpty()) {
    Nan::ThrowError("configure() requires an option object");
    return;
  }

  Local<Object> options = maybe_options.ToLocalChecked();
  if (!get_string_option(options, "mainLogFile", main_log_file_null, main_log_file)) return;
  if (!get_string_option(options, "workerLogFile", worker_log_file_null, worker_log_file)) return;

  unique_ptr<Nan::Callback> callback(new Nan::Callback(info[1].As<Function>()));

  if (!main_log_file.empty()) {
    instance.use_main_log_file(move(main_log_file));
  }

  if (!worker_log_file.empty()) {
    Result<> r = instance.use_worker_log_file(move(worker_log_file), move(callback));
    if (r.is_error()) {
      Nan::ThrowError(r.get_error().c_str());
      return;
    }
    async = true;
  }

  if (!async) {
    callback->Call(0, 0);
  }
}

void watch(const Nan::FunctionCallbackInfo<Value> &info)
{
  if (info.Length() != 3) {
    return Nan::ThrowError("watch() requires three arguments");
  }

  Nan::MaybeLocal<String> maybe_root = Nan::To<String>(info[0]);
  if (maybe_root.IsEmpty()) {
    Nan::ThrowError("watch() requires a string as argument one");
    return;
  }
  Local<String> root_v8_string = maybe_root.ToLocalChecked();
  Nan::Utf8String root_utf8(root_v8_string);
  if (*root_utf8 == nullptr) {
    Nan::ThrowError("watch() argument one must be a valid UTF-8 string");
    return;
  }
  string root_str(*root_utf8, root_utf8.length());

  unique_ptr<Nan::Callback> ack_callback(new Nan::Callback(info[1].As<Function>()));
  unique_ptr<Nan::Callback> event_callback(new Nan::Callback(info[2].As<Function>()));

  Result<> r = instance.watch(move(root_str), move(ack_callback), move(event_callback));
  if (r.is_error()) {
    Nan::ThrowError(r.get_error().c_str());
  }
}

void unwatch(const Nan::FunctionCallbackInfo<Value> &info)
{
  if (info.Length() != 2) {
    Nan::ThrowError("watch() requires two arguments");
    return;
  }

  Nan::Maybe<uint32_t> maybe_channel_id = Nan::To<uint32_t>(info[0]);
  if (maybe_channel_id.IsNothing()) {
    Nan::ThrowError("unwatch() requires a channel ID as its first argument");
    return;
  }
  ChannelID channel_id = static_cast<ChannelID>(maybe_channel_id.FromJust());

  unique_ptr<Nan::Callback> ack_callback(new Nan::Callback(info[1].As<Function>()));

  Result<> r = instance.unwatch(channel_id, move(ack_callback));
  if (r.is_error()) {
    Nan::ThrowError(r.get_error().c_str());
  }
}

void status(const Nan::FunctionCallbackInfo<Value> &info)
{
  Status status;
  instance.collect_status(status);

  Local<Object> status_object = Nan::New<Object>();
  Nan::Set(
    status_object,
    Nan::New<String>("pendingCallbackCount").ToLocalChecked(),
    Nan::New<Uint32>(static_cast<uint32_t>(status.pending_callback_count))
  );
  Nan::Set(
    status_object,
    Nan::New<String>("channelCallbackCount").ToLocalChecked(),
    Nan::New<Uint32>(static_cast<uint32_t>(status.channel_callback_count))
  );
  Nan::Set(
    status_object,
    Nan::New<String>("workerThreadOk").ToLocalChecked(),
    Nan::New<String>(status.worker_thread_ok).ToLocalChecked()
  );
  Nan::Set(
    status_object,
    Nan::New<String>("workerInSize").ToLocalChecked(),
    Nan::New<Uint32>(static_cast<uint32_t>(status.worker_in_size))
  );
  Nan::Set(
    status_object,
    Nan::New<String>("workerInOk").ToLocalChecked(),
    Nan::New<String>(status.worker_in_ok).ToLocalChecked()
  );
  Nan::Set(
    status_object,
    Nan::New<String>("workerOutSize").ToLocalChecked(),
    Nan::New<Uint32>(static_cast<uint32_t>(status.worker_out_size))
  );
  Nan::Set(
    status_object,
    Nan::New<String>("workerOutOk").ToLocalChecked(),
    Nan::New<String>(status.worker_out_ok).ToLocalChecked()
  );
  info.GetReturnValue().Set(status_object);
}

void initialize(Local<Object> exports)
{
  Nan::Set(
    exports,
    Nan::New<String>("configure").ToLocalChecked(),
    Nan::GetFunction(Nan::New<FunctionTemplate>(configure)).ToLocalChecked()
  );
  Nan::Set(
    exports,
    Nan::New<String>("watch").ToLocalChecked(),
    Nan::GetFunction(Nan::New<FunctionTemplate>(watch)).ToLocalChecked()
  );
  Nan::Set(
    exports,
    Nan::New<String>("unwatch").ToLocalChecked(),
    Nan::GetFunction(Nan::New<FunctionTemplate>(unwatch)).ToLocalChecked()
  );
  Nan::Set(
    exports,
    Nan::New<String>("status").ToLocalChecked(),
    Nan::GetFunction(Nan::New<FunctionTemplate>(status)).ToLocalChecked()
  );
}

NODE_MODULE(watcher, initialize);
<|endoftext|>


<commit_before>/*
 *            Copyright 2009-2017 The VOTCA Development Team
 *                       (http://www.votca.org)
 *
 *      Licensed under the Apache License, Version 2.0 (the "License")
 *
 * You may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *              http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

// Overload of uBLAS prod function with MKL/GSL implementations
#include <votca/tools/linalg.h>
#include <votca/xtp/aomatrix.h>
#include <votca/xtp/dftcoupling.h>

#include <boost/numeric/ublas/operation.hpp>
#include <boost/numeric/ublas/banded.hpp>
#include <boost/numeric/ublas/matrix_proxy.hpp>
#include <boost/numeric/ublas/vector_proxy.hpp>
#include <boost/numeric/ublas/symmetric.hpp>
#include <votca/tools/constants.h>
#include <boost/format.hpp>
#include <boost/progress.hpp>

namespace votca {
    namespace xtp {

        namespace ub = boost::numeric::ublas;

        using boost::format;

        double DFTcoupling::getCouplingElement(int levelA, int levelB, Orbitals* _orbitalsA,
                Orbitals* _orbitalsB, ub::matrix<double>* _JAB, double _energy_difference) {

            int _levelsA = _orbitalsA->getNumberOfLevels();

            if (_energy_difference != 0) {
                std::vector<int> list_levelsA = *_orbitalsA->getDegeneracy(levelA, _energy_difference);
                std::vector<int> list_levelsB = *_orbitalsA->getDegeneracy(levelB, _energy_difference);

                double _JAB_sq = 0;
                double _JAB_one_level;

                for (std::vector<int>::iterator iA = list_levelsA.begin()++; iA != list_levelsA.end(); iA++) {
                    for (std::vector<int>::iterator iB = list_levelsB.begin()++; iB != list_levelsB.end(); iB++) {
                        //cout << *iA << ':' << *iB << endl;
                        _JAB_one_level = _JAB->at_element(*iA - 1, *iB - 1 + _levelsA);
                        _JAB_sq += _JAB_one_level*_JAB_one_level;
                    }
                }

                return sqrt(_JAB_sq / (list_levelsA.size() * list_levelsB.size())) * tools::conv::hrt2ev;

            } else {

                return _JAB->at_element(levelA - 1, levelB - 1 + _levelsA) * tools::conv::hrt2ev;

            }
            // the  matrix should be symmetric, could also return this element
            // _JAB.at_element( _levelsA + levelB - 1  , levelA - 1 );
        }

        /**
         * \brief evaluates electronic couplings
         *
         * This is a fast version with a rather large block matrix AxB
         *
         * @param _orbitalsA molecular orbitals of molecule A
         * @param _orbitalsB molecular orbitals of molecule B
         * @param _orbitalsAB molecular orbitals of the dimer AB
         * @param _JAB matrix with electronic couplings
         * @return false if failed
         */
        bool DFTcoupling::CalculateIntegrals(Orbitals* _orbitalsA, Orbitals* _orbitalsB,
                Orbitals* _orbitalsAB, ub::matrix<double>* _JAB) {

            CTP_LOG(ctp::logDEBUG, *_pLog) << "Calculating electronic couplings" << flush;

            const std::vector<QMAtom*> atomsA = _orbitalsA->QMAtoms();
            const std::vector<QMAtom*> atomsB = _orbitalsB->QMAtoms();
            const std::vector<QMAtom*> atomsAll = _orbitalsAB->QMAtoms();

            for (unsigned i = 0; i < atomsAll.size(); i++) {
                QMAtom* dimer = atomsAll[i];
                QMAtom* monomer = NULL;

                if (i < atomsA.size()) {
                    monomer = atomsA[i];
                    if(!monomer->getPos().isClose(dimer->getPos(), 0.001) ) {
                        CTP_LOG(ctp::logERROR, *_pLog) << "======WARNING=======\n Coordinates of monomers and dimer atoms do not agree, do you know what you are doing?\n " << flush;
                        continue;
                    }
                } else if (i < atomsB.size() + atomsA.size()) {
                    monomer = atomsB[i - atomsA.size()];
                    if(monomer->getPos().isClose(dimer->getPos(), 0.001)){
                        CTP_LOG(ctp::logERROR, *_pLog) << "======WARNING=======\n Coordinates of monomers and dimer atoms do not agree, do you know what you are doing?\n " << flush;
                        continue;
                    }
                } else {
                    // Linker
                    CTP_LOG(ctp::logDEBUG, *_pLog) << (format("Neither Monomer A nor Monomer B contains atom %s on line %u. Hence, this atom is part of a linker. \n") %dimer->getType() %(i+1) ).str()<<flush;
                    continue;
                }

                if (monomer->getType() != dimer->getType()) {
                    throw runtime_error("\nERROR: Atom types do not agree in dimer and monomers\n");
                }
                if (tools::abs(monomer->getPos() - dimer->getPos()) > 0.001) {
                    CTP_LOG(ctp::logERROR, *_pLog) << "======WARNING=======\n Coordinates of monomers and dimer atoms do not agree, do you know what you are doing?\n " << flush;
                    break;
                }
            }

            // constructing the direct product orbA x orbB
            int _basisA = _orbitalsA->getBasisSetSize();
            int _basisB = _orbitalsB->getBasisSetSize();

            if ((_basisA == 0) || (_basisB == 0)) {
                CTP_LOG(ctp::logERROR, *_pLog) << "Basis set size is not stored in monomers" << flush;
                return false;
            }

            int _levelsA = _orbitalsA->getNumberOfLevels();
            int _levelsB = _orbitalsB->getNumberOfLevels();

            //boost::timer t; // start timing
            //double _st = t.elapsed();

            CTP_LOG(ctp::logDEBUG, *_pLog) << "Levels:Basis A[" << _levelsA << ":" << _basisA << "]"
                    << " B[" << _levelsB << ":" << _basisB << "]" << flush;

            if ((_levelsA == 0) || (_levelsB == 0)) {
                CTP_LOG(ctp::logERROR, *_pLog) << "No information about number of occupied/unoccupied levels is stored" << flush;
                return false;
            }

            //       | Orbitals_A          0 |      | Overlap_A |
            //       | 0          Orbitals_B |  X   | Overlap_B |  X  Transpose( Orbitals_AB )

            ub::matrix<double>_psi_AxB= ub::zero_matrix<double>(_levelsA + _levelsB ,_orbitalsAB->getBasisSetSize());

            CTP_LOG(ctp::logDEBUG, *_pLog) << "Constructing direct product AxB ["
                    << _psi_AxB.size1() << "x"
                    << _psi_AxB.size2() << "]" << flush;

            ub::project(_psi_AxB, ub::range(0, _levelsA), ub::range(0, _basisA)) = _orbitalsA->MOCoefficients();
            ub::project(_psi_AxB, ub::range(_levelsA, _levelsA + _levelsB), ub::range(_basisA, _basisA + _basisB)) = _orbitalsB->MOCoefficients();

            // psi_AxB * S_AB * psi_AB
            CTP_LOG(ctp::logDEBUG, *_pLog) << "Projecting dimer onto monomer orbitals" << flush;
            ub::matrix<double> overlap;
            if (_orbitalsAB->hasAOOverlap()) {
                CTP_LOG(ctp::logDEBUG, *_pLog) << "Reading overlap matrix from orbitals" << flush;
                overlap = _orbitalsAB->AOOverlap();
            } else {
                CTP_LOG(ctp::logDEBUG, *_pLog) << "Calculating overlap matrix for basisset: " << _orbitalsAB->getDFTbasis() << flush;
                BasisSet _dftbasisset;
                AOBasis _dftbasis;
                _dftbasisset.LoadBasisSet(_orbitalsAB->getDFTbasis());

                _dftbasis.AOBasisFill(&_dftbasisset, _orbitalsAB->QMAtoms());
                AOOverlap _dftAOoverlap;
                _dftAOoverlap.Fill(_dftbasis);
                overlap = _dftAOoverlap.Matrix();
            }

            ub::matrix<double> _psi_AB = ub::prod(overlap, ub::trans(_orbitalsAB->MOCoefficients()));
            ub::matrix<double> _psi_AxB_dimer_basis = ub::prod(_psi_AxB, _psi_AB);
            _psi_AB.clear();

            //check to see if projection quality is sufficient
            for (unsigned i = 0; i < _psi_AxB_dimer_basis.size1(); i++) {
                double mag = 0.0;
                for (unsigned j = 0; j < _psi_AxB_dimer_basis.size2(); j++) {
                    mag += _psi_AxB_dimer_basis(i, j) * _psi_AxB_dimer_basis(i, j);

                }
                if (mag < 0.95) {
                    throw runtime_error("\nERROR: Projection of monomer orbitals on dimer is insufficient, maybe the orbital order is screwed up, otherwise increase dimer basis.\n");
                }
            }

            // J = psi_AxB_dimer_basis * HAB * psi_AxB_dimer_basis^T
            CTP_LOG(ctp::logDEBUG, *_pLog) << "Projecting the Fock matrix onto the dimer basis" << flush;
            ub::diagonal_matrix<double> _hamiltonian_AB(_orbitalsAB->getNumberOfLevels(), _orbitalsAB->MOEnergies().data());
            ub::matrix<double> _temp = ub::prod(_hamiltonian_AB, ub::trans(_psi_AxB_dimer_basis));
            ub::matrix<double> JAB_dimer = ub::prod(_psi_AxB_dimer_basis, _temp);

            // S = psi_AxB_dimer_basis * psi_AxB_dimer_basis^T
            CTP_LOG(ctp::logDEBUG, *_pLog) << "Constructing Overlap matrix" << flush;
            ub::matrix<double> _S_AxB = ub::prod(_psi_AxB_dimer_basis, ub::trans(_psi_AxB_dimer_basis));

            CTP_LOG(ctp::logDEBUG, *_pLog) << "Calculating the effective overlap JAB ["
                    << JAB_dimer.size1() << "x"
                    << JAB_dimer.size2() << "]" << flush;
            double smalleig = linalg_loewdin(JAB_dimer, _S_AxB);
            CTP_LOG(ctp::logDEBUG, *_pLog) << "Smallest eigenvalue of overlap matrix is " << smalleig << flush;
            (*_JAB) = JAB_dimer;

            CTP_LOG(ctp::logDEBUG, *_pLog) << "Done with electronic couplings" << flush;

            return true;

        }

    }
}
<commit_msg>Removed blank lines<commit_after>/*
 *            Copyright 2009-2017 The VOTCA Development Team
 *                       (http://www.votca.org)
 *
 *      Licensed under the Apache License, Version 2.0 (the "License")
 *
 * You may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *              http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

// Overload of uBLAS prod function with MKL/GSL implementations
#include <votca/tools/linalg.h>
#include <votca/xtp/aomatrix.h>
#include <votca/xtp/dftcoupling.h>

#include <boost/numeric/ublas/operation.hpp>
#include <boost/numeric/ublas/banded.hpp>
#include <boost/numeric/ublas/matrix_proxy.hpp>
#include <boost/numeric/ublas/vector_proxy.hpp>
#include <boost/numeric/ublas/symmetric.hpp>
#include <votca/tools/constants.h>
#include <boost/format.hpp>
#include <boost/progress.hpp>

namespace votca {
    namespace xtp {

        namespace ub = boost::numeric::ublas;

        using boost::format;

        double DFTcoupling::getCouplingElement(int levelA, int levelB, Orbitals* _orbitalsA,
                Orbitals* _orbitalsB, ub::matrix<double>* _JAB, double _energy_difference) {

            int _levelsA = _orbitalsA->getNumberOfLevels();

            if (_energy_difference != 0) {
                std::vector<int> list_levelsA = *_orbitalsA->getDegeneracy(levelA, _energy_difference);
                std::vector<int> list_levelsB = *_orbitalsA->getDegeneracy(levelB, _energy_difference);

                double _JAB_sq = 0;
                double _JAB_one_level;

                for (std::vector<int>::iterator iA = list_levelsA.begin()++; iA != list_levelsA.end(); iA++) {
                    for (std::vector<int>::iterator iB = list_levelsB.begin()++; iB != list_levelsB.end(); iB++) {
                        //cout << *iA << ':' << *iB << endl;
                        _JAB_one_level = _JAB->at_element(*iA - 1, *iB - 1 + _levelsA);
                        _JAB_sq += _JAB_one_level*_JAB_one_level;
                    }
                }

                return sqrt(_JAB_sq / (list_levelsA.size() * list_levelsB.size())) * tools::conv::hrt2ev;

            } else {

                return _JAB->at_element(levelA - 1, levelB - 1 + _levelsA) * tools::conv::hrt2ev;

            }
            // the  matrix should be symmetric, could also return this element
            // _JAB.at_element( _levelsA + levelB - 1  , levelA - 1 );
        }

        /**
         * \brief evaluates electronic couplings
         *
         * This is a fast version with a rather large block matrix AxB
         *
         * @param _orbitalsA molecular orbitals of molecule A
         * @param _orbitalsB molecular orbitals of molecule B
         * @param _orbitalsAB molecular orbitals of the dimer AB
         * @param _JAB matrix with electronic couplings
         * @return false if failed
         */
        bool DFTcoupling::CalculateIntegrals(Orbitals* _orbitalsA, Orbitals* _orbitalsB,
                Orbitals* _orbitalsAB, ub::matrix<double>* _JAB) {

            CTP_LOG(ctp::logDEBUG, *_pLog) << "Calculating electronic couplings" << flush;

            const std::vector<QMAtom*> atomsA = _orbitalsA->QMAtoms();
            const std::vector<QMAtom*> atomsB = _orbitalsB->QMAtoms();
            const std::vector<QMAtom*> atomsAll = _orbitalsAB->QMAtoms();

            for (unsigned i = 0; i < atomsAll.size(); i++) {
                QMAtom* dimer = atomsAll[i];
                QMAtom* monomer = NULL;

                if (i < atomsA.size()) {
                    monomer = atomsA[i];
                    if(!monomer->getPos().isClose(dimer->getPos(), 0.001) ) {
                        CTP_LOG(ctp::logERROR, *_pLog) << "======WARNING=======\n Coordinates of monomers and dimer atoms do not agree, do you know what you are doing?\n " << flush;
                        continue;
                    }
                } else if (i < atomsB.size() + atomsA.size()) {
                    monomer = atomsB[i - atomsA.size()];
                    if(monomer->getPos().isClose(dimer->getPos(), 0.001)){
                        CTP_LOG(ctp::logERROR, *_pLog) << "======WARNING=======\n Coordinates of monomers and dimer atoms do not agree, do you know what you are doing?\n " << flush;
                        continue;
                    }
                } else {
                    // Linker
                    CTP_LOG(ctp::logDEBUG, *_pLog) << (format("Neither Monomer A nor Monomer B contains atom %s on line %u. Hence, this atom is part of a linker. \n") %dimer->getType() %(i+1) ).str()<<flush;
                    continue;
                }

                if (monomer->getType() != dimer->getType()) {
                    throw runtime_error("\nERROR: Atom types do not agree in dimer and monomers\n");
                }
                if (tools::abs(monomer->getPos() - dimer->getPos()) > 0.001) {
                    CTP_LOG(ctp::logERROR, *_pLog) << "======WARNING=======\n Coordinates of monomers and dimer atoms do not agree, do you know what you are doing?\n " << flush;
                    break;
                }
            }

            // constructing the direct product orbA x orbB
            int _basisA = _orbitalsA->getBasisSetSize();
            int _basisB = _orbitalsB->getBasisSetSize();

            if ((_basisA == 0) || (_basisB == 0)) {
                CTP_LOG(ctp::logERROR, *_pLog) << "Basis set size is not stored in monomers" << flush;
                return false;
            }

            int _levelsA = _orbitalsA->getNumberOfLevels();
            int _levelsB = _orbitalsB->getNumberOfLevels();

            //boost::timer t; // start timing
            //double _st = t.elapsed();

            CTP_LOG(ctp::logDEBUG, *_pLog) << "Levels:Basis A[" << _levelsA << ":" << _basisA << "]"
                    << " B[" << _levelsB << ":" << _basisB << "]" << flush;

            if ((_levelsA == 0) || (_levelsB == 0)) {
                CTP_LOG(ctp::logERROR, *_pLog) << "No information about number of occupied/unoccupied levels is stored" << flush;
                return false;
            }

            //       | Orbitals_A          0 |      | Overlap_A |
            //       | 0          Orbitals_B |  X   | Overlap_B |  X  Transpose( Orbitals_AB )

            ub::matrix<double>_psi_AxB= ub::zero_matrix<double>(_levelsA + _levelsB ,_orbitalsAB->getBasisSetSize());

            CTP_LOG(ctp::logDEBUG, *_pLog) << "Constructing direct product AxB ["
                    << _psi_AxB.size1() << "x"
                    << _psi_AxB.size2() << "]" << flush;

            ub::project(_psi_AxB, ub::range(0, _levelsA), ub::range(0, _basisA)) = _orbitalsA->MOCoefficients();
            ub::project(_psi_AxB, ub::range(_levelsA, _levelsA + _levelsB), ub::range(_basisA, _basisA + _basisB)) = _orbitalsB->MOCoefficients();

            // psi_AxB * S_AB * psi_AB
            CTP_LOG(ctp::logDEBUG, *_pLog) << "Projecting dimer onto monomer orbitals" << flush;
            ub::matrix<double> overlap;
            if (_orbitalsAB->hasAOOverlap()) {
                CTP_LOG(ctp::logDEBUG, *_pLog) << "Reading overlap matrix from orbitals" << flush;
                overlap = _orbitalsAB->AOOverlap();
            } else {
                CTP_LOG(ctp::logDEBUG, *_pLog) << "Calculating overlap matrix for basisset: " << _orbitalsAB->getDFTbasis() << flush;
                BasisSet _dftbasisset;
                AOBasis _dftbasis;
                _dftbasisset.LoadBasisSet(_orbitalsAB->getDFTbasis());

                _dftbasis.AOBasisFill(&_dftbasisset, _orbitalsAB->QMAtoms());
                AOOverlap _dftAOoverlap;
                _dftAOoverlap.Fill(_dftbasis);
                overlap = _dftAOoverlap.Matrix();
            }

            ub::matrix<double> _psi_AB = ub::prod(overlap, ub::trans(_orbitalsAB->MOCoefficients()));
            ub::matrix<double> _psi_AxB_dimer_basis = ub::prod(_psi_AxB, _psi_AB);
            _psi_AB.clear();

            //check to see if projection quality is sufficient
            for (unsigned i = 0; i < _psi_AxB_dimer_basis.size1(); i++) {
                double mag = 0.0;
                for (unsigned j = 0; j < _psi_AxB_dimer_basis.size2(); j++) {
                    mag += _psi_AxB_dimer_basis(i, j) * _psi_AxB_dimer_basis(i, j);

                }
                if (mag < 0.95) {
                    throw runtime_error("\nERROR: Projection of monomer orbitals on dimer is insufficient, maybe the orbital order is screwed up, otherwise increase dimer basis.\n");
                }
            }

            // J = psi_AxB_dimer_basis * HAB * psi_AxB_dimer_basis^T
            CTP_LOG(ctp::logDEBUG, *_pLog) << "Projecting the Fock matrix onto the dimer basis" << flush;
            ub::diagonal_matrix<double> _hamiltonian_AB(_orbitalsAB->getNumberOfLevels(), _orbitalsAB->MOEnergies().data());
            ub::matrix<double> _temp = ub::prod(_hamiltonian_AB, ub::trans(_psi_AxB_dimer_basis));
            ub::matrix<double> JAB_dimer = ub::prod(_psi_AxB_dimer_basis, _temp);

            // S = psi_AxB_dimer_basis * psi_AxB_dimer_basis^T
            CTP_LOG(ctp::logDEBUG, *_pLog) << "Constructing Overlap matrix" << flush;
            ub::matrix<double> _S_AxB = ub::prod(_psi_AxB_dimer_basis, ub::trans(_psi_AxB_dimer_basis));

            CTP_LOG(ctp::logDEBUG, *_pLog) << "Calculating the effective overlap JAB ["
                    << JAB_dimer.size1() << "x"
                    << JAB_dimer.size2() << "]" << flush;
            double smalleig = linalg_loewdin(JAB_dimer, _S_AxB);
            CTP_LOG(ctp::logDEBUG, *_pLog) << "Smallest eigenvalue of overlap matrix is " << smalleig << flush;
            (*_JAB) = JAB_dimer;

            CTP_LOG(ctp::logDEBUG, *_pLog) << "Done with electronic couplings" << flush;

            return true;

        }

    }
}
<|endoftext|>


<commit_before>#include <vector>
#include <list>
#include <stdlib.h>
#include <queue>
#include <votca/xtp/huffmantree.h>
#include <votca/xtp/glink.h>
using namespace std;

namespace votca {namespace xtp {

void huffmanTree::setEvents(vector<GLink> * v){
    this->events=v;
}

//https://en.wikipedia.org/wiki/Huffman_coding
void huffmanTree::makeTree(){
    if (!events) throw runtime_error("Error in Huffmantree::makeTree : Pointer to Events not set!");

    //queue of the nodes, sorted by probability
    auto compare = [](huffmanNode * n1, huffmanNode * n2)
    { return n1->probability>n2->probability;};

    //priority queues, because the algorithm always needs the element with the smallest probability. Also, it keep adding nodes to it, so it would we very inefficient to sort it in every iteration.
    priority_queue<huffmanNode *,vector<huffmanNode *>, decltype(compare)> queue(compare);

    htree=vector<huffmanNode>(events->size()%2?events->size():events->size()-1);

    auto comp2 = [](GLink * e1, GLink * e2){
        return e1->rate>e2->rate;
    };
    priority_queue<GLink *, vector<GLink *>, decltype(comp2)> eventQueue(comp2);
    escape_rate=0.0;

    int firstEmptyFieldIndex=0;
    for (GLink &e:*events){
        eventQueue.push(&e);
        escape_rate+=e.rate;
     }
    while (eventQueue.size()>1){
        htree[firstEmptyFieldIndex].isOnLastLevel=true;
        htree[firstEmptyFieldIndex].leftLeaf=eventQueue.top();
        eventQueue.pop();
        htree[firstEmptyFieldIndex].rightLeaf=eventQueue.top();
        eventQueue.pop();
        htree[firstEmptyFieldIndex].probability=(htree[firstEmptyFieldIndex].leftLeaf->rate+htree[firstEmptyFieldIndex].rightLeaf->rate)/escape_rate;
        queue.push(&(htree[firstEmptyFieldIndex]));
        firstEmptyFieldIndex++;
    }
    if (!eventQueue.empty()){
        htree[firstEmptyFieldIndex].isOnLastLevel=true;
        htree[firstEmptyFieldIndex].rightLeaf=eventQueue.top();
        htree[firstEmptyFieldIndex].leftLeaf=eventQueue.top();
        htree[firstEmptyFieldIndex].probability=2*htree[firstEmptyFieldIndex].leftLeaf->rate/escape_rate;
        queue.push(&(htree[firstEmptyFieldIndex]));
        firstEmptyFieldIndex++;
    }

    //now connect the hnodes, making a new one for every connection:
    //always take the two nodes with the smallest probability and "combine" them, repeat, until just one node (the root) is left.
    huffmanNode * h1;
    huffmanNode * h2;
    while (queue.size()>1){
        h1=queue.top();
        queue.pop();
        h2=queue.top();
        queue.pop();
        htree[firstEmptyFieldIndex].probability=h1->probability+h2->probability;
        htree[firstEmptyFieldIndex].leftChild=h1;
        htree[firstEmptyFieldIndex].rightChild=h2;
        queue.push(&(htree[firstEmptyFieldIndex]));
        firstEmptyFieldIndex++;
    }
    //reorganize the probabilities: in every node, add the probability of one subtree ("small")
    //to all nodes of the other subtree.
    addProbabilityFromRightSubtreeToLeftSubtree(&htree[htree.size()-1],0);
    moveProbabilitiesFromRightSubtreesOneLevelUp(&htree[htree.size()-1]);
    treeIsMade=true;
}

void huffmanTree::addProbabilityFromRightSubtreeToLeftSubtree(huffmanNode * n,double add){
    //for each node, adds the probability of the right childnode to the left childnode and every node under it.
    //if the Tree would look like this (with the Numbers representing the probability of every node) before calling this function

    //           1.0
    //           /\
    //          /  \
    //         /    \
    //        /      \
    //       /        \
    //     0.4       0.6
    //     / \       / \
    //    /   \     /   \
    //  0.25 0.15 0.35 0.25
    //        /\
    //       /  \
    //     0.1 0.05
    //then it would look like this after calling it
    //           1.0
    //           /\
    //          /  \
    //         /    \
    //        /      \
    //       /        \
    //     1.0       0.6
    //     / \       / \
    //    /   \     /   \
    //   1.0 0.75 0.6  0.25
    //        /\
    //       /  \
    //     0.75 0.65
    //now the tree could be traversed with "while (!n.isLeaf()) n=p>n.right.p?n.left:n.right"
    //so in the function moveProbabilitiesFromRightSubtreesOneLevelUp the numbers are moved one level up to call n.p instead of n.right.p

    //adds "add" to the probability, then calls itself recursively.
    //this calculates the probabilities needed to traverse the tree quickly
    n->probability+=add;
    //if leftId=-1 (=> node is leaf), returns
    if (n->isOnLastLevel){
        return;
    }

    addProbabilityFromRightSubtreeToLeftSubtree(n->leftChild,add+n->rightChild->probability);
    addProbabilityFromRightSubtreeToLeftSubtree(n->rightChild,add);
}

void huffmanTree::moveProbabilitiesFromRightSubtreesOneLevelUp(huffmanNode * n){
    //moves the Probabilities on the right subtrees one level up.
    //if the Tree would look like this (with the Numbers representing the probability of every node) before calling this function
    //           1.0
    //           /\
    //          /  \
    //         /    \
    //        /      \
    //       /        \
    //     1.0       0.6
    //     / \       / \
    //    /   \     /   \
    //   1.0 0.75 0.6  0.25
    //        /\
    //       /  \
    //     0.75 0.65
    //then it would look like this after calling it
    //           0.6
    //           /\
    //          /  \
    //         /    \
    //        /      \
    //       /        \
    //     0.75      0.25
    //     / \       / \
    //    /   \     /   \
    //   1.0 0.65 0.6  0.25
    //        /\
    //       /  \
    //     0.75 0.65
    //note, that now the probabilities on the leaf level are not needed anymore to traverse the tree; the algorithm now is "while (!n.isLeaf()) n=p>n.p?n.left:n.right"
    if (n->isOnLastLevel){
        n->probability-=n->leftLeaf->rate/escape_rate;
    }
    else{
        n->probability=n->rightChild->probability;
        moveProbabilitiesFromRightSubtreesOneLevelUp(n->rightChild);
        moveProbabilitiesFromRightSubtreesOneLevelUp(n->leftChild);
    }
}

GLink * huffmanTree::findHoppingDestination(double p){
    if (!treeIsMade) throw runtime_error("Tried to find Hopping Destination without initializing the Huffmantree first!");
    huffmanNode * node=&htree.back();
    while (!node->isOnLastLevel){
        if (p>node->probability) node=node->leftChild;
        else node=node->rightChild;
    }
    return (p>node->probability?node->leftLeaf:node->rightLeaf);
}

}}
<commit_msg>Fixed issue with commentaries with backslashes<commit_after>#include <vector>
#include <list>
#include <stdlib.h>
#include <queue>
#include <votca/xtp/huffmantree.h>
#include <votca/xtp/glink.h>
using namespace std;

namespace votca {namespace xtp {

void huffmanTree::setEvents(vector<GLink> * v){
    this->events=v;
}

//https://en.wikipedia.org/wiki/Huffman_coding
void huffmanTree::makeTree(){
    if (!events) throw runtime_error("Error in Huffmantree::makeTree : Pointer to Events not set!");

    //queue of the nodes, sorted by probability
    auto compare = [](huffmanNode * n1, huffmanNode * n2)
    { return n1->probability>n2->probability;};

    //priority queues, because the algorithm always needs the element with the smallest probability. Also, it keep adding nodes to it, so it would we very inefficient to sort it in every iteration.
    priority_queue<huffmanNode *,vector<huffmanNode *>, decltype(compare)> queue(compare);

    htree=vector<huffmanNode>(events->size()%2?events->size():events->size()-1);

    auto comp2 = [](GLink * e1, GLink * e2){
        return e1->rate>e2->rate;
    };
    priority_queue<GLink *, vector<GLink *>, decltype(comp2)> eventQueue(comp2);
    escape_rate=0.0;

    int firstEmptyFieldIndex=0;
    for (GLink &e:*events){
        eventQueue.push(&e);
        escape_rate+=e.rate;
     }
    while (eventQueue.size()>1){
        htree[firstEmptyFieldIndex].isOnLastLevel=true;
        htree[firstEmptyFieldIndex].leftLeaf=eventQueue.top();
        eventQueue.pop();
        htree[firstEmptyFieldIndex].rightLeaf=eventQueue.top();
        eventQueue.pop();
        htree[firstEmptyFieldIndex].probability=(htree[firstEmptyFieldIndex].leftLeaf->rate+htree[firstEmptyFieldIndex].rightLeaf->rate)/escape_rate;
        queue.push(&(htree[firstEmptyFieldIndex]));
        firstEmptyFieldIndex++;
    }
    if (!eventQueue.empty()){
        htree[firstEmptyFieldIndex].isOnLastLevel=true;
        htree[firstEmptyFieldIndex].rightLeaf=eventQueue.top();
        htree[firstEmptyFieldIndex].leftLeaf=eventQueue.top();
        htree[firstEmptyFieldIndex].probability=htree[firstEmptyFieldIndex].leftLeaf->rate/escape_rate;
        queue.push(&(htree[firstEmptyFieldIndex]));
        firstEmptyFieldIndex++;
    }

    //now connect the hnodes, making a new one for every connection:
    //always take the two nodes with the smallest probability and "combine" them, repeat, until just one node (the root) is left.
    huffmanNode * h1;
    huffmanNode * h2;
    while (queue.size()>1){
        h1=queue.top();
        queue.pop();
        h2=queue.top();
        queue.pop();
        htree[firstEmptyFieldIndex].probability=h1->probability+h2->probability;
        htree[firstEmptyFieldIndex].leftChild=h1;
        htree[firstEmptyFieldIndex].rightChild=h2;
        queue.push(&(htree[firstEmptyFieldIndex]));
        firstEmptyFieldIndex++;
    }
    //reorganize the probabilities: in every node, add the probability of one subtree ("small")
    //to all nodes of the other subtree.
    addProbabilityFromRightSubtreeToLeftSubtree(&htree[htree.size()-1],0);
    moveProbabilitiesFromRightSubtreesOneLevelUp(&htree[htree.size()-1]);
    treeIsMade=true;

}

void huffmanTree::addProbabilityFromRightSubtreeToLeftSubtree(huffmanNode * n,double add){
    //for each node, adds the probability of the right childnode to the left childnode and every node under it.
    //if the Tree would look like this (with the Numbers representing the probability of every node) before calling this function

    //           1.0
    //       ____||____
    //      |          |
    //     0.4        0.6
    //     _||_      _||_
    //    |    |    |    |
    //  0.25 0.15  0.35 0.25
    //        _||_
    //       |    |
    //      0.1  0.05
    //then it would look like this after calling it
    //           1.0
    //       ____||____
    //      |          |
    //     1.0        0.6
    //     _||_      _||_
    //    |    |    |    |
    //   1.0 0.75  0.6  0.25
    //        _||_
    //       |    |
    //      0.75 0.65
    //now the tree could be traversed with "while (!n.isLeaf()) n=p>n.right.p?n.left:n.right"
    //so in the function moveProbabilitiesFromRightSubtreesOneLevelUp the numbers are moved one level up to call n.p instead of n.right.p

    //adds "add" to the probability, then calls itself recursively.
    //this calculates the probabilities needed to traverse the tree quickly
    n->probability+=add;
    //if leftId=-1 (=> node is leaf), returns
    if (n->isOnLastLevel){
        return;
    }

    addProbabilityFromRightSubtreeToLeftSubtree(n->leftChild,add+n->rightChild->probability);
    addProbabilityFromRightSubtreeToLeftSubtree(n->rightChild,add);
}

void huffmanTree::moveProbabilitiesFromRightSubtreesOneLevelUp(huffmanNode * n){
    //moves the Probabilities on the right subtrees one level up.
    //if the Tree would look like this (with the Numbers representing the probability of every node) before calling this function
    //           1.0
    //       ____||____
    //      |          |
    //     1.0        0.6
    //     _||_      _||_
    //    |    |    |    |
    //   1.0 0.75  0.6  0.25
    //        _||_
    //       |    |
    //      0.75 0.65
    //then it would look like this after calling it
    //           0.
    //       ____||____
    //      |          |
    //     0.75      0.25
    //     _||_      _||_
    //    |    |    |    |
    //   1.0 0.65  0.6  0.25
    //        _||_
    //       |    |
    //     0.75  0.65
    //note, that now the probabilities on the leaf level are not needed anymore to traverse the tree; the algorithm now is "while (!n.isLeaf()) n=p>n.p?n.left:n.right"
    if (n->isOnLastLevel){
        n->probability-=n->leftLeaf->rate/escape_rate;
    }
    else{
        n->probability=n->rightChild->probability;
        moveProbabilitiesFromRightSubtreesOneLevelUp(n->rightChild);
        moveProbabilitiesFromRightSubtreesOneLevelUp(n->leftChild);
    }
}

GLink * huffmanTree::findHoppingDestination(double p){
    if (!treeIsMade) throw runtime_error("Tried to find Hopping Destination without initializing the Huffmantree first!");
    huffmanNode * node=&htree.back();
    while (!node->isOnLastLevel){
        if (p>node->probability) node=node->leftChild;
        else node=node->rightChild;
    }
    return (p>node->probability?node->leftLeaf:node->rightLeaf);
}

}}
<|endoftext|>


<commit_before>#include "CImg.h"
#include <time.h>
using namespace cimg_library;
typedef unsigned char uchar;

int main(){
	CImg<uchar> image("./donuts.bmp");
	CImg<uchar> rot(image);
	uchar * ptr;

	CImgDisplay main_disp(image, "Original"), draw_disp(rot, "Negatif");

	while (!main_disp.is_closed() && !draw_disp.is_closed()) {
		cimg_for(rot, ptr, uchar){ *ptr = (*ptr + 1) % 256; }
		draw_disp.display(rot);
		sleep(20);
	}
	return 0;
}<commit_msg>Macros<commit_after>#include "CImg.h"
#include <time.h>
#include <iostream>

using namespace std;

using namespace cimg_library;
typedef unsigned char uchar;

#define ANTOINE

#ifdef ANTOINE
#define FOLDER "/Users/antoineprouvost/GitHub/sparse_coding/images/"
#endif

#ifdef PAUL
#define FOLDER "../images/"
#endif

#define FILE "donuts.bmp"

int main(){

	CImg<uchar> image(FOLDER FILE);
	CImg<uchar> rot(image);
	uchar * ptr;

	CImgDisplay main_disp(image, "Original"), draw_disp(rot, "Negatif");

	while (!main_disp.is_closed() && !draw_disp.is_closed()) {
		cimg_for(rot, ptr, uchar){ *ptr = (*ptr + 1) % 256; }
		draw_disp.display(rot);
//		sleep(20);
	}

	return 0;
}<|endoftext|>