text:
/******************************************************************************
 * Targoman: A robust Machine Translation framework               *
 *                                                                            *
 * Copyright 2014-2018 by ITRC <http://itrc.ac.ir>                            *
 *                                                                            *
 * This file is part of Targoman.                                             *
 *                                                                            *
 * Targoman is free software: you can redistribute it and/or modify           *
 * it under the terms of the GNU Lesser General Public License as published   *
 * by the Free Software Foundation, either version 3 of the License, or       *
 * (at your option) any later version.                                        *
 *                                                                            *
 * Targoman is distributed in the hope that it will be useful,                *
 * but WITHOUT ANY WARRANTY; without even the implied warranty of             *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              *
 * GNU Lesser General Public License for more details.                        *
 * You should have received a copy of the GNU Lesser General Public License   *
 * along with Targoman. If not, see <http://www.gnu.org/licenses/>.           *
 *                                                                            *
 ******************************************************************************/
/**
 * @author Behrooz Vedadian <vedadian@targoman.com>
 * @author S.Mehran M.Ziabary <ziabary@targoman.com>
 */

#include <stdexcept>
#include <iostream>
#include <memory>
#include <chrono>
#include <thread>
#include <atomic>
#include <vector>
#include <algorithm>
#include <string>
#include <string.h>
#include <regex>
#include <functional>
#include <evhttp.h>
#include <algorithm>

#include <cstdlib>
#include <boost/timer/timer.hpp>

#include "common/god.h"
#include "common/logging.h"
#include "common/search.h"
#include "common/threadpool.h"
#include "common/printer.h"
#include "common/sentence.h"
#include "common/sentences.h"
#include "common/exception.h"
#include "common/translation_task.h"

#include <sys/socket.h>
#include <arpa/inet.h>

#include "gason.h"

using namespace amunmt;

static std::string SERVER_NAME = "";
// trim from start (in place)
static inline void ltrim(std::string &s) {
    s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) {
        return !std::isspace(ch);
    }));
}

// trim from end (in place)
static inline void rtrim(std::string &s) {
    s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) {
        return !std::isspace(ch);
    }).base(), s.end());
}

// trim from both ends (in place)
static inline std::string& trim(std::string &s) {
    ltrim(s);
    rtrim(s);
    return s;
}

std::string replaceAll(std::string str, const std::string& from, const std::string& to) {
    size_t start_pos = 0;
    while((start_pos = str.find(from, start_pos)) != std::string::npos) {
        str.replace(start_pos, from.length(), to);
        start_pos += to.length(); // Handles case where 'to' is a substring of 'from'
    }
    return str;
}

void GetPrimaryIp(char* buffer, size_t buflen)
{
    assert(buflen >= 16);

    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    assert(sock != -1);

    const char* kGoogleDnsIp = "74.125.115.104";
    uint16_t kDnsPort = 80;
    struct sockaddr_in serv;
    memset(&serv, 0, sizeof(serv));
    serv.sin_family = AF_INET;
    serv.sin_addr.s_addr = inet_addr(kGoogleDnsIp);
    serv.sin_port = htons(kDnsPort);

    int err = connect(sock, (const sockaddr*) &serv, sizeof(serv));
    assert(err != -1);

    sockaddr_in name;
    socklen_t namelen = sizeof(name);
    err = getsockname(sock, (sockaddr*) &name, &namelen);
    assert(err != -1);

    const char* p = inet_ntop(AF_INET, &name.sin_addr, buffer, buflen);
    assert(p);

    close(sock);
}

std::string getPostBody(evhttp_request *_request)
{
    std::string result;
    evbuffer *buf = evhttp_request_get_input_buffer(_request);
    size_t len = evbuffer_get_length(buf);
    result.resize(len);
    evbuffer_copyout(buf, (void*)result.data(), len);
    return result;
}

typedef std::tuple<std::vector<std::string>, std::vector<std::string>, std::vector<int>> InputLines_t;

const std::regex gLineEnderRegEx(" [\\.\\?\\!] ");

InputLines_t getTextLines(std::string _body)
{
    std::vector<std::string> srcs;
    std::vector<std::string> origs;
    std::vector<int> originalLineNumbers;
    Json::JsonValue value;
    Json::JsonAllocator allocator;
    char *s = (char *)_body.data();
    char *endptr;
    int status = Json::jsonParse(s, &endptr, &value, allocator);
    if (status != Json::JSON_OK)
        throw std::runtime_error("Input is not a valid JSON string.");
    if(value.getTag() != Json::JSON_ARRAY)
        throw std::runtime_error("Input string must be a JSON array.");
    int LineIndex = 0;
    for(auto e : value) {
        if(e->value.getTag() != Json::JSON_OBJECT)
            throw std::runtime_error("Each element of the input array must be an object containing `src` and `orig` fields.");
        char *src = nullptr, *orig = nullptr;
        for(auto i : e->value) {
            if(strcmp(i->key, "src") == 0) {
                if(i->value.getTag() != Json::JSON_STRING)
                    throw std::runtime_error("`src` field must contain string.");
                src = i->value.toString();
            }
            // if(strcmp(i->key, "orig") == 0) {
            //     if(i->value.getTag() != Json::JSON_STRING)
            //         throw std::runtime_error("`orig` field must contain string.");
            //     orig = i->value.toString();
            // }
        }

        std::string Src = src;
        int i = 0;
        std::smatch LineEnderMatch;
        std::cout << "GET_TEXT_LINE: " << Src << std::endl;
        while(std::regex_search(Src, LineEnderMatch, gLineEnderRegEx)) {
            int j = LineEnderMatch.position() + 2;
            srcs.push_back(Src.substr(i, j - i));
            originalLineNumbers.push_back(LineIndex);
            Src = LineEnderMatch.suffix();
        }
        if(Src.size()) {
            srcs.push_back(Src);
            originalLineNumbers.push_back(LineIndex);
        }
        if(orig == nullptr)
            origs.push_back(std::string());
        else
            origs.push_back(orig);
        ++LineIndex;
    }

    return InputLines_t(srcs, origs, originalLineNumbers);
}

template<typename T>
void evbuffer_add_printf_array(evbuffer* _buff, char* _format, const T& _array) {
    evbuffer_add_printf(_buff, "[");
    if(_array.size() > 0) {
        evbuffer_add_printf_array(_buff, _format, _array[0]);
        for(size_t Index = 1; Index < _array.size(); ++Index) {
            evbuffer_add_printf(_buff, ",");
            evbuffer_add_printf_array(_buff, _format, _array[Index]);
        }
    }
    evbuffer_add_printf(_buff, "]");
}

template<>
void evbuffer_add_printf_array<std::string>(evbuffer* _buff, char* _format, const std::string& _array) {
    if(_array == "<NULL>")
        evbuffer_add_printf(_buff, _format, " ");
    else {
        std::string normal = replaceAll(_array, "<PIPE>", "|");
        evbuffer_add_printf(_buff, _format, (char *)replaceAll(normal, "\"","\\\"").data());
    }
}

template<>
void evbuffer_add_printf_array<int>(evbuffer* _buff, char* _format, const int& _array) {
    evbuffer_add_printf(_buff, _format, _array);
}

void sendResults(
    evhttp_request *_request,
    std::vector<std::vector<std::string>> _source,
    std::vector<std::vector<std::vector<std::string>>> _phrases,
    std::vector<std::vector<std::vector<int>>> _alignments
) {
    auto *OutBuf = evhttp_request_get_output_buffer(_request);
    if (!OutBuf) {
        evhttp_send_error(_request, HTTP_INTERNAL, "{ \"code\": 500, \"msg\": \"Internal server error.\" }");
        return;
    }

    bool First = true;
    evbuffer_add_printf(OutBuf, "{\"rslt\":[");
    for(size_t Index = 0; Index < _source.size(); ++Index) {
        if(!First)
            evbuffer_add_printf(OutBuf, ",");
        std::vector<std::string>& Tokens = _source[Index];
        std::vector<std::vector<std::string>>& Phrases = _phrases[Index];
        std::vector<std::vector<int>> Alignments = _alignments[Index];
        evbuffer_add_printf(OutBuf, "{");
        evbuffer_add_printf(OutBuf, "\"tokens\":");
        evbuffer_add_printf_array(OutBuf, "\"%s\"", Tokens);
        evbuffer_add_printf(OutBuf, ",\"phrases\":");
        evbuffer_add_printf_array(OutBuf, "\"%s\"", Phrases);
        evbuffer_add_printf(OutBuf, ",\"alignments\":");
        evbuffer_add_printf_array(OutBuf, "%d", Alignments);
        evbuffer_add_printf(OutBuf, "}");
        First = false;
    }
    evbuffer_add_printf(OutBuf, "],");
    evbuffer_add_printf(OutBuf, "\"serverName\":\"%s\"}", SERVER_NAME.c_str());
    evhttp_send_reply(_request, HTTP_OK, "", OutBuf);
}

typedef std::vector<std::vector<std::string>> NBestTranslations_t;
struct Alignment_t {
    size_t SourceSize;
    size_t TargetSize;
    std::vector<float> M;
};

void saveAlignmentsToList(const HypothesisPtr& hypothesis, size_t target_size, size_t source_size, Alignment_t& alignment) {
  std::vector<SoftAlignment> aligns;
  HypothesisPtr last = hypothesis->GetPrevHyp();
  while (last->GetPrevHyp().get() != nullptr) {
    SoftAlignment copy = *(last->GetAlignment(0));
    copy.resize(source_size - 1);
    aligns.insert(aligns.begin(), copy);
    last = last->GetPrevHyp();
  }
  for(int i = (int)aligns.size(); i < (int)target_size - 1; ++i) {
    SoftAlignment dummy;
    dummy.resize(source_size - 1);
    dummy[dummy.size() - 1] = 1.0;
    aligns.insert(aligns.begin(), dummy);
  }
  alignment.SourceSize = source_size - 1;
  alignment.TargetSize = target_size - 1;
  alignment.M.resize((source_size - 1) * (target_size - 1));
  for(int i = 0; i < (int)target_size - 1; ++i) {
    const SoftAlignment& item = aligns[i];
    for(int j = 0; j < (int)item.size(); ++j) {
      const float value = item[j];
      alignment.M[i * (source_size - 1) + j] = value;
    }
  }
}

void saveHistoryToOutput(
  const History& history,
  const Sentence& sentence,
  God& __GOD__,
  std::vector<std::vector<std::string>>& _sentenceWords,
  std::vector<NBestTranslations_t>& _translations,
  std::vector<std::vector<Alignment_t>>& _alignments
) {
  God& god = __GOD__;
//   const Words& SentenceWordIds = sentence.GetWords(0);
//   const Vocab& SourceVocab = god.GetSourceVocab(0);
//   std::vector<std::string> ThisSentenceWords;
//   ThisSentenceWords.resize(SentenceWordIds.size() - 1);
//   for(size_t WordIndex = 0; WordIndex < ThisSentenceWords.size(); ++WordIndex)
//     ThisSentenceWords[WordIndex] = SourceVocab[SentenceWordIds[WordIndex]];
//   _sentenceWords.push_back(ThisSentenceWords);
  if (god.Get<bool>("n-best")) {
    std::vector<std::string> scorerNames = god.GetScorerNames();
    const NBestList &nbl = history.NBest(god.Get<size_t>("beam-size"));

    NBestTranslations_t sentence_outputs;
    std::vector<Alignment_t> sentence_alignment;

    for (size_t i = 0; i < nbl.size(); ++i) {
      const Result& result = nbl[i];
      const Words &words = result.first;
      const HypothesisPtr &hypo = result.second;

      std::vector<std::string> translation = god.Postprocess(god.GetTargetVocab()(words));
      sentence_outputs.push_back(translation);
      Alignment_t alignment;
      saveAlignmentsToList(hypo, translation.size() + 1, sentence.size(), alignment);
      sentence_alignment.push_back(alignment);
    }
    _translations.push_back(sentence_outputs);
    _alignments.push_back(sentence_alignment);
  } else {
    auto bestTranslation = history.Top();
    std::vector<std::string> bestSentenceWords = god.Postprocess(god.GetTargetVocab()(bestTranslation.first));

    NBestTranslations_t sentence_outputs;
    std::vector<Alignment_t> sentence_alignment;

    sentence_outputs.push_back(bestSentenceWords);
    Alignment_t alignment;
    saveAlignmentsToList(bestTranslation.second, bestSentenceWords.size() + 1, sentence.size(), alignment);
    sentence_alignment.push_back(alignment);

    _translations.push_back(sentence_outputs);
    _alignments.push_back(sentence_alignment);
  }
}

void translate(std::vector<std::string>& _lines, God& __GOD__, std::vector<std::vector<std::string>>& _sentenceWords, std::vector<NBestTranslations_t>& _translations, std::vector<std::vector<Alignment_t>>& _alignments)
{
//   LOG(info)->info("Retrieving batching settings.");

  size_t miniSize = __GOD__.Get<size_t>("mini-batch");
  size_t maxiSize = __GOD__.Get<size_t>("maxi-batch");
  int miniWords = __GOD__.Get<int>("mini-batch-words");

  std::vector<std::future< std::shared_ptr<Histories> >> results;
  SentencesPtr maxiBatch(new Sentences());
  std::vector<SentencePtr> sentences_by_lineno;

//   LOG(info)->info("Creating batches.");

  sentences_by_lineno.resize(_lines.size());
  for(int lineNum = 0; lineNum < _lines.size(); ++lineNum) {
    std::string& line = trim(_lines[lineNum]);

    if (line.empty()) line = "<NULL>";
    else line = replaceAll(line, "|", "<PIPE>");
    LOG(info)->info("Creating batch: <" + line + ">");

    std::vector<std::string> __words;
    Split(line, __words, " ");
    for(std::string& w : __words) {
        Word __index = __GOD__.GetSourceVocab(0)[w];
        if(__index == UNK_ID) {
            std::string __lower = w;
            std::transform(__lower.begin(), __lower.end(), __lower.begin(), ::tolower);
            __index = __GOD__.GetSourceVocab(0)[__lower];
            if(__index != UNK_ID)
                w = __lower;
        }
    }
    _sentenceWords.push_back(__words);
    SentencePtr __s(new Sentence(__GOD__, lineNum, line));
    sentences_by_lineno[lineNum] = __s;
    maxiBatch->push_back(__s);

    if (maxiBatch->size() >= maxiSize) {

      maxiBatch->SortByLength();
      while (maxiBatch->size()) {
        SentencesPtr miniBatch = maxiBatch->NextMiniBatch(miniSize, miniWords);

        // LOG(info)->info("Enqueuing batch of size {}", miniBatch->size());

        results.emplace_back(
          __GOD__.GetThreadPool().enqueue(
              [&__GOD__, miniBatch]{ return TranslationTask(__GOD__, miniBatch); }
              )
        );
      }

      maxiBatch.reset(new Sentences());
    }
  }

  // last batch
  if (maxiBatch->size()) {
    maxiBatch->SortByLength();
    while (maxiBatch->size()) {
      SentencesPtr miniBatch = maxiBatch->NextMiniBatch(miniSize, miniWords);
    //   LOG(info)->info("Enqueuing batch of size {}", miniBatch->size());
      results.emplace_back(
        __GOD__.GetThreadPool().enqueue(
            [&__GOD__, miniBatch]{ return TranslationTask(__GOD__, miniBatch); }
            )
      );
    }
  }

//   LOG(info)->info("Gathering histories.");
  // resort batch into line number order
  Histories allHistories;
  int historyIndex = 0;
  for (auto&& result : results) {
    // LOG(info)->info("Getting history {} ...", historyIndex);
    std::shared_ptr<Histories> histories = result.get();
    // LOG(info)->info("Got history {} ...", historyIndex);
    allHistories.Append(*histories);
    // LOG(info)->info("Saved history {}.", historyIndex);
  }
  allHistories.SortByLineNum();

//   LOG(info)->info("Saving to output.");
  // output
  for (size_t i = 0; i < allHistories.size(); ++i) {
    const History& history = *(allHistories.at(i).get());
    const Sentence& sentence = *(sentences_by_lineno[history.GetLineNum()]);
    saveHistoryToOutput(history,  sentence, __GOD__, _sentenceWords, _translations, _alignments);
  }
}

std::vector<int> getHardAlignment(Alignment_t& _alignment) {
    std::vector<int> _hardAlignment;
    for(size_t i = 0; i < _alignment.SourceSize; ++i) {
        float Max = 0.0;
        for(size_t j = 0; j < _alignment.TargetSize; ++j)
            if(_alignment.M[j * _alignment.SourceSize + i] > Max)
                Max = _alignment.M[j * _alignment.SourceSize + i];
        if(Max < 1e-5)
            continue;
        for(size_t j = 0; j < _alignment.TargetSize; ++j)
            _alignment.M[j * _alignment.SourceSize + i] /= Max;
    }
    _hardAlignment.resize(_alignment.TargetSize);
    for(size_t j = 0; j < _alignment.TargetSize; ++j) {
        float Max = _alignment.M[j * _alignment.SourceSize + 0];
        int ArgMax = 0;
        for(size_t i = 1; i < _alignment.SourceSize; ++i)
            if(_alignment.M[j * _alignment.SourceSize + i] > Max) {
                Max = _alignment.M[j * _alignment.SourceSize + i];
                ArgMax = i;
            }
        _hardAlignment[j] = ArgMax;
    }
    return _hardAlignment;
}

inline int minofVec(const std::vector<int>& _values)
{
    int Min = _values.at(0);
    for(size_t i = 1; i < _values.size(); ++i)
        if(Min > _values.at(i))
            Min = _values.at(i);
    return Min;
}

std::vector<std::tuple<int, int>> getCorrespondence(const std::string& _str1, const std::string& _str2)
{
#define DELETION     0
#define INSERTION    1
#define SUBSTITUTION 2

    const size_t L1 = _str1.size();
    const size_t L2 = _str2.size();
    const size_t S = L1 + 1;

    std::vector<int> D((L1 + 1) * (L2 + 1), 0);
    std::vector<int> Dij(3, 0);

    for(size_t i = 1; i <= L1; ++i)
        D[i] = 4 * i;
    for(size_t i = 1; i <= L2; ++i)
        D[i * S] = 4 * i;
    for(size_t i = 1; i <= L1; ++i) {
        for(size_t j = 1; j <= L2; ++j) {
            Dij[DELETION] = D[j * S + (i - 1)] + 1;
            Dij[INSERTION] = D[(j - 1) * S + i] + 1;
            Dij[SUBSTITUTION] = D[(j - 1) * S + (i - 1)];
            if(_str1.at(i - 1) != _str2.at(j - 1))
                Dij[SUBSTITUTION] += 1;
            D[j * S + i] = minofVec(Dij);
        }
    }

    std::vector<std::tuple<int, int>> Correspondence(_str2.size());

    size_t i = L1;
    size_t j = L2;
    size_t End = L1;
    while(i > 0 && j > 0) {
        Dij[DELETION] = D[j * S + (i - 1)] + 1;
        Dij[INSERTION] = D[(j - 1) * S + i] + 1;
        Dij[SUBSTITUTION] = D[(j - 1) * S + (i - 1)];
        if(_str1.at(i - 1) != _str2.at(j - 1))
            Dij[SUBSTITUTION] += 1;

        int Winner;
        if((_str1.at(i - 1) == ' ') != (_str2.at(j - 1) == ' ')) {
            if(_str1.at(i - 1) == ' ')
                Winner = DELETION;
            else
                Winner = INSERTION;
        } else {
            Winner = DELETION;
            if(Dij[Winner] > Dij[INSERTION])
                Winner = INSERTION;
            if(Dij[Winner] > Dij[SUBSTITUTION])
                Winner = SUBSTITUTION;
        }

        switch(Winner) {
        case DELETION:
            --i;
            break;
        case INSERTION:
            Correspondence[j - 1] = std::tuple<int, int>(-1, -1);
            --j;
            break;
        case SUBSTITUTION:
            Correspondence[j - 1] = std::tuple<int, int>(i - 1, End);
            End = i - 1;
            --i;
            --j;
            break;
        }
    }
    while(j > 0) {
        Correspondence[j - 1] = std::tuple<int, int>(-1, -1);
        --j;
    }

    return Correspondence;
}

void handleRequest(evhttp_request *_request, void *context)
{

    if (_request == nullptr)
        return;

    if (evhttp_request_get_command(_request) != EVHTTP_REQ_POST)
    {
        evhttp_send_error(_request, HTTP_BADMETHOD, "{ \"code\": 405, \"msg\": \"Method not allowed.\" }");
        return;
    }

    try {

        LOG(info)->info("Processing incoming request ...");

        boost::timer::cpu_timer timer;

        InputLines_t SrcAndOrigs = getTextLines(getPostBody(_request));
        std::vector<std::string>& Lines = std::get<0>(SrcAndOrigs);

        LOG(info)->info("Found {} lines in {}", Lines.size(), timer.format());

        std::vector<std::string>& Origs = std::get<1>(SrcAndOrigs);
        std::vector<int>& OriginalLineNumbers = std::get<2>(SrcAndOrigs);

        God &__GOD__ = *(God *)context;

        std::vector<NBestTranslations_t> Translations;
        std::vector<std::vector<Alignment_t>> Alignments;
        std::vector<std::vector<std::string>> SourceTokens;

        translate(Lines, __GOD__, SourceTokens, Translations, Alignments);

        LOG(info)->info("Translation took {}", timer.format());

        std::vector<std::vector<std::string>> AllSourceTokens;
        std::vector<std::vector<std::vector<std::string>>> AllPhrases;
        std::vector<std::vector<std::vector<int>>> AllAlignments;
        std::vector<std::vector<std::vector<std::tuple<int, int>>>> AllOrigAlignments;

        int PreviousOriginalLineNumber = -1;
        for(size_t Index = 0; Index < Lines.size(); ++Index) {
            std::vector<std::string>& Line = SourceTokens[Index];
            int CurrentOriginalLineNumber = OriginalLineNumbers[Index];

            NBestTranslations_t& LineTranslations = Translations[Index];
            std::vector<Alignment_t>& LineAlignments = Alignments[Index];

            std::vector<std::string>& BestTranslation = LineTranslations[0];
            std::vector<int> WordMapping = getHardAlignment(LineAlignments[0]);
            // for(size_t iiii=0; iiii < LineAlignments[0].TargetSize; ++iiii) {
            //     for(size_t jjjj=0; jjjj < LineAlignments[0].SourceSize; ++jjjj)
            //         std::cerr << LineAlignments[0].M[iiii * LineAlignments[0].SourceSize + jjjj] << ", ";
            //     std::cerr << std::endl;
            // }
            // for(size_t iiii=0; iiii < BestTranslation.size(); ++iiii)
            //     std::cerr << BestTranslation[iiii] << ": " << iiii << std::endl;
            // for(size_t iiii=0; iiii < WordMapping.size(); ++iiii) {
            //     int jjjj = WordMapping[iiii];
            //     std::cerr << BestTranslation[iiii] << ": " << iiii << " (" << Line[jjjj] << ")" << std::endl;
            // }

            std::vector<std::vector<std::string>> Phrases;
            std::vector<std::vector<int>> Alignments;
            size_t PhraseCount = 0;

            size_t LastSourceIndex = (size_t)-1;
            for(size_t TargetIndex = 0; TargetIndex < WordMapping.size(); ++TargetIndex) {
                std::string TargetWord = BestTranslation[TargetIndex];
                size_t SourceIndex = WordMapping[TargetIndex];
                if(TargetWord == UNK_STR) {
                    TargetWord = Line[SourceIndex];
                }
                if(SourceIndex == LastSourceIndex) {
                    Phrases[PhraseCount - 1][0] += std::string(" ") + TargetWord;
                } else {
                    Phrases.push_back({ TargetWord });
                    Alignments.push_back({ SourceIndex });
                    ++PhraseCount;
                }
                LastSourceIndex = SourceIndex;
            }

            /*
            for(int NGram=1; NGram <= 5; ++NGram) {
                std::cout << "NGram=" << NGram << std::endl;
                std::vector<std::pair<std::string, int>> ToCheck;
                for(int i=0; i < NGram; ++i)
                    ToCheck.push_back(std::make_pair(std::string("<NEVER>"), (int)-1));

                std::vector<std::vector<int>> ProcessedAlignments;
                std::vector<std::vector<std::string>> ProcessedPhrases;

                int i = 0;
                while(i < Alignments.size()) {
                    bool Similar = true;
                    if(NGram + i - 1 < Alignments.size()) {
                        std::cout << "NGram + i - 1=" << (NGram + i - 1) << ", Alignments.size()=" << Alignments.size() << std::endl;
                        for(int j=0; j < NGram; ++j) {
                            std::cout << "i=" << i << ", j=" << j << ", Phrases.size()=" << Phrases.size() << ", Alignments.size()=" << Alignments.size() <<  ToCheck[j].first << " vs. " << Phrases[j + i][0] << ", " << ToCheck[j].second << " vs. " << Alignments[j + i][0] << std::endl;
                            if(ToCheck[j].first != Phrases[j + i][0] || ToCheck[j].second != Alignments[j + i][0]) {
                                Similar = false;
                                break;
                            }
                        }
                    }else {
                        Similar = false;
                    }
                    std::cout << "i=" << i << ", Similar=" << Similar << std::endl;
                    if(Similar == false) {
                        ProcessedAlignments.push_back(Alignments[i]);
                        ProcessedPhrases.push_back(Phrases[i]);
                        for(int j=0; j < NGram - 1; ++j)
                            ToCheck[j] = ToCheck[j+1];
                        ToCheck[NGram-1] = std::make_pair(Phrases[i][0], Alignments[i][0]);
                        std::cout << "Did not skip" << std::endl;
                    }
                    else {
                        i = i + NGram - 1;
                        std::cout << "Skipped" << std::endl;
                    }
                    i++;
                }
                Alignments = ProcessedAlignments;
                Phrases = ProcessedPhrases;
            }
            */

            for(size_t NBestIndex = 1; NBestIndex < LineTranslations.size(); ++NBestIndex) {
                std::vector<std::string>& TranslationCandidate = LineTranslations[NBestIndex];
                std::vector<int> WordMapping = getHardAlignment(LineAlignments[NBestIndex]);

                std::vector<std::string> CandidatesBySource;
                CandidatesBySource.resize(Line.size());
                for(size_t TargetIndex = 0; TargetIndex < WordMapping.size(); ++TargetIndex) {
                    auto TargetWord = TranslationCandidate[TargetIndex];
                    size_t SourceIndex = WordMapping[TargetIndex];
                    if(TargetWord == UNK_STR) {
                        TargetWord = Line[SourceIndex];
                    }
                    if(CandidatesBySource[SourceIndex].size() > 0)
                        CandidatesBySource[SourceIndex] += " ";
                    CandidatesBySource[SourceIndex] += TargetWord;
                }

                for(size_t PhraseIndex = 0; PhraseIndex < Phrases.size(); ++PhraseIndex) {
                    size_t SourceIndex = Alignments[PhraseIndex][0];
                    std::vector<std::string>& Candidates = Phrases[PhraseIndex];
                    bool Found = false;
                    for(size_t CandidateIndex = 0; CandidateIndex < Candidates.size(); ++CandidateIndex)
                        if(Candidates[CandidateIndex] == CandidatesBySource[SourceIndex]) {
                            Found = true;
                            break;
                        }
                    if(Found == false) {
                        Candidates.push_back(CandidatesBySource[SourceIndex]);
                    }
                }
            }

            if(PreviousOriginalLineNumber == CurrentOriginalLineNumber) {
                auto& LastLine = AllSourceTokens[AllSourceTokens.size() - 1];
                auto& LastPhrases = AllPhrases[AllPhrases.size() - 1];
                auto& LastAlignments = AllAlignments[AllAlignments.size() - 1];
                int SourceOffset = LastLine.size();
                int TargetOffset = LastPhrases.size();
                for(const auto& Word : Line)
                    LastLine.push_back(Word);
                for(const auto& Phrase : Phrases)
                    LastPhrases.push_back(Phrase);
                for(const auto& A : Alignments) {
                    std::vector<int> UpdatedAlignments;
                    for(auto a : A)
                        UpdatedAlignments.push_back(a + SourceOffset);
                    LastAlignments.push_back(UpdatedAlignments);
                }
            } else {
                AllSourceTokens.push_back(Line);
                AllPhrases.push_back(Phrases);
                AllAlignments.push_back(Alignments);
            }
            PreviousOriginalLineNumber = CurrentOriginalLineNumber;
        }

        sendResults(_request, AllSourceTokens, AllPhrases, AllAlignments);

    } catch(std::exception& e) {
        std::cerr << "Exception during handling request: " << e.what() << std::endl;
        evhttp_send_error(_request, HTTP_INTERNAL, "{ \"code\": 500, \"msg\": \"Internal server error.\" }");
    }
}

typedef std::function<void(evhttp *)> SocketBinder_t;
void runEventLoop(std::atomic_bool &_done, SocketBinder_t _bind, God &__GOD__)
{
    std::unique_ptr<event_base, decltype(&event_base_free)> EventBase(
        event_base_new(), &event_base_free);
    if (!EventBase)
        throw std::runtime_error("Failed to create a new event base.");
    std::unique_ptr<evhttp, decltype(&evhttp_free)> EvHttp(
        evhttp_new(EventBase.get()),
        &evhttp_free);
    if (!EvHttp)
        throw std::runtime_error("Failed to create a new http event handler.");
    evhttp_set_gencb(EvHttp.get(), handleRequest, &__GOD__);
    _bind(EvHttp.get());
    while (_done == false)
    {
        event_base_loop(EventBase.get(), EVLOOP_NONBLOCK);
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
}

int main(int argc, char *argv[])
{
    if (argc < 5)
    {
        std::cerr << "Usage: http_server <name> <address> <port> <path> [amun options, run with --help]" << std::endl;
        return 1;
    }

    SERVER_NAME = argv[1];
    const char *address = argv[2];
    uint16_t port = std::stoi(argv[3]);
    const char *path = argv[4];

    God __GOD__;
    __GOD__.Init(argc - 4, argv + 4);

    std::setvbuf(stdout, NULL, _IONBF, 0);
    std::setvbuf(stdin, NULL, _IONBF, 0);

    std::atomic_bool Done;

    auto ThreadDeleter = [&](std::thread *_thread) {
        Done = true;
        _thread->join();
        delete _thread;
    };

    typedef std::unique_ptr<std::thread, decltype(ThreadDeleter)> thread_ptr;
    typedef std::vector<thread_ptr> ThreadPool_t;

    std::exception_ptr InitException;
    evutil_socket_t Socket = -1;

    auto Binder = [&](evhttp *_evHttp) {
        if (Socket == -1)
        {
            evhttp_bound_socket *BoundSock = evhttp_bind_socket_with_handle(
                _evHttp,
                address,
                port);
            if (!BoundSock)
                throw std::runtime_error("Failed to bind server socket.");
            Socket = evhttp_bound_socket_get_fd(BoundSock);
            if (Socket == -1)
                throw std::runtime_error("Failed to get server socket descriptor for next instance.");
        }
        else
        {
            if (evhttp_accept_socket(_evHttp, Socket) == -1)
                throw std::runtime_error("Failed to bind server socket for new instance.");
        }
    };

    Done = false;

    ThreadPool_t Pool;
    unsigned int ThreadCount = std::thread::hardware_concurrency() - 1;
    for (int Index = 0; Index < ThreadCount; ++Index)
    {
        thread_ptr Thread(
            new std::thread([&]() {
                try
                {
                    runEventLoop(Done, Binder, __GOD__);
                }
                catch (...)
                {
                    InitException = std::current_exception();
                }
            }),
            ThreadDeleter);
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
        if (InitException != std::exception_ptr())
        {
            Done = true;
            std::rethrow_exception(InitException);
        }
        Pool.push_back(std::move(Thread));
    }

    while(true) sleep(1);
    Done = true;
}

max_stars_repo_name: Targoman/TargomanNMT

max_issues_repo_name: Targoman/TargomanNMT

max_forks_repo_name: Targoman/TargomanNMT


text:
// Erwann Rogard, wrote in July 2009:
//
// This iterator is by nbecker and was found in the boost's vault.
// Changes that I made are shown by ER_2007_07
//
// arch-tag: ed320324-7f40-4e99-8364-8a6b7cf4d19e
#ifndef cycle_iterator_ext_HPP_2009
#define cycle_iterator_ext_HPP_2009
#include <boost/type_traits/is_convertible.hpp>
#include <boost/utility/enable_if.hpp>
#include <boost/iterator/iterator_adaptor.hpp>
#include <iterator>

// See http://www.nabble.com/cycle-iterators-td25215321.html

namespace boost {

  //! This is a cycle iterator that does keep track of wraparound.
  template<typename BaseIterator, typename offset_t>
  class cycle_iterator_ext : public boost::iterator_adaptor<cycle_iterator_ext<BaseIterator, offset_t>,
							BaseIterator
						       >
  {
  public:
    typedef typename boost::iterator_adaptor<cycle_iterator_ext<BaseIterator, offset_t>,
					     BaseIterator
					    > super_t;

    typedef typename super_t::difference_type difference_type;
    typedef typename super_t::reference reference;

    explicit cycle_iterator_ext()
    :super_t(),size(0),position(0),wrap(0) // ER_2009_07
    {}

    explicit cycle_iterator_ext (BaseIterator const& _b, BaseIterator const& _e, offset_t offset=0) :
      //base(_b), //ER_2009_07
      super_t(_b), //ER_2009_07
      size (std::distance (_b, _e))
      , wrap(0) //ER_2009_07
    {
      SetPos (offset);
    }

    template <typename OtherBase, typename OtherOffset>
    cycle_iterator_ext (cycle_iterator_ext<OtherBase,OtherOffset> const& other,
		    typename enable_if_convertible<OtherBase, BaseIterator>::type* = 0) :
      super_t(other),       // ER_2009_07
      //base (other.base),  // ER_2009_07
      size (other.size),
      position (other.position),
      wrap (other.wrap)
    {}

    // ER_2009_07
    //template <typename OtherBase, typename OtherOffset>
    //typename enable_if<
    //    is_convertible<OtherBase,BaseIterator>,
    //    cycle_iterator_ext&
    //>::type
    //operator= (cycle_iterator_ext<OtherBase,OtherOffset> const& other)
    //{
    //    if(&other!=this){
    //        super_t& super = static_cast<super_t&>(this);
    //        super = other;
    //        size = (other.size),
    //        position = (other.position),
    //        wrap = (other.wrap);
    //    }
    //    return *this;
    //}

  private:
    friend class boost::iterator_core_access;

    void increment () {
      ++position;
      if (position >= size) {
	++wrap;
	position -= size;
      }
    }

    void decrement () {
      --position;
      if (position < 0) {
	--wrap;
	position += size;
      }
    }

    void SetPos (offset_t newpos) {
      position = newpos % size;
      wrap = newpos / size;
      if (position < 0) {
	--wrap;
	position += size;
      }
    }

    void advance (difference_type n) {
      offset_t newpos = realposition() + n;
      SetPos (newpos);
    }

    template<typename OtherBase, typename OtherOffset>
    difference_type
    distance_to (cycle_iterator_ext<OtherBase, OtherOffset> const& y) const {
      if (size == 0)
	return 0;

      else {
	offset_t pos1 = realposition();
	offset_t pos2 = y.realposition();
	return -(pos1 - pos2);
      }
    }

    template<typename OtherBase, typename OtherOffset>
    bool equal (cycle_iterator_ext<OtherBase, OtherOffset> const& y) const {
      return distance_to (y) == 0;
    }

    reference dereference() const {
       // return *(base + position); // ER_2009_07
        return *(this->base_reference() + position); // ER_2009_07
    }

   offset_t PositiveMod (offset_t x) const {
      offset_t y = x % size;
      if (y < 0)
	y += size;
      return y;
    }

  public:

    reference operator[] (difference_type n) const {
        // return *(base + PositiveMod (position + n)); // ER_2009_07
        return *(this->base_reference() + PositiveMod (position + n)); // ER_2009_07
    }

    offset_t offset() const { return position; }

    offset_t realposition () const {
      return position + wrap * size;
    }

    //  private:

    // BaseIterator base; // ER_2009
    offset_t size;
    offset_t position;
    offset_t wrap;
  };

  template<typename offset_t, typename BaseIterator>
  cycle_iterator_ext<BaseIterator, offset_t> make_cycle_iterator_ext(BaseIterator b, BaseIterator e, offset_t offset=0) {
    return cycle_iterator_ext<BaseIterator, offset_t> (b, e, offset);
  }

  template<typename BaseIterator>
  cycle_iterator_ext<BaseIterator, int> make_cycle_iterator_ext(BaseIterator b, BaseIterator e, int offset=0) {
    return cycle_iterator_ext<BaseIterator, int> (b, e, offset);
  }

} //namespace boost

#endif

ext: hpp


text:
//          Copyright Carl Philipp Reh 2009 - 2016.
// Distributed under the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE_1_0.txt or copy at
//          http://www.boost.org/LICENSE_1_0.txt)

#ifndef FCPPT_PREPROCESSOR_DETAIL_STRINGIZE_HPP_INCLUDED
#define FCPPT_PREPROCESSOR_DETAIL_STRINGIZE_HPP_INCLUDED

#include <fcppt/public_config.hpp>

#include <fcppt/config/external_begin.hpp>
#if defined(FCPPT_NARROW_STRING)
#include <boost/preprocessor/stringize.hpp>
#else
#include <boost/preprocessor/wstringize.hpp>
#endif
#include <fcppt/config/external_end.hpp>

#if defined(FCPPT_NARROW_STRING)
#define FCPPT_PP_DETAIL_STRINGIZE(s) BOOST_PP_STRINGIZE(s)
#else
#define FCPPT_PP_DETAIL_STRINGIZE(s) BOOST_PP_WSTRINGIZE(s)
#endif

#endif

max_stars_repo_name: vinzenz/fcppt

max_issues_repo_name: vinzenz/fcppt

max_forks_repo_name: vinzenz/fcppt


text:
/*!@file
 * @copyright This code is licensed under the 3-clause BSD license.
 *   Copyright ETH Zurich, Laboratory of Physical Chemistry, Reiher Group.
 *   See LICENSE.txt for details.
 */

#include "Molassembler/DistanceGeometry/ConformerGeneration.h"

#include <Eigen/Dense>
#include "Utils/Constants.h"
#include "Utils/Typenames.h"

#include "Molassembler/BondStereopermutator.h"
#include "Molassembler/DistanceGeometry/EigenRefinement.h"
#include "Molassembler/DistanceGeometry/Error.h"
#include "Molassembler/DistanceGeometry/ExplicitBoundsGraph.h"
#include "Molassembler/DistanceGeometry/MetricMatrix.h"
#include "Molassembler/DistanceGeometry/RefinementMeta.h"
#include "Molassembler/Graph/GraphAlgorithms.h"
#include "Utils/Math/QuaternionFit.h"

#include "Molassembler/Detail/Cartesian.h"
#include "Molassembler/Temple/Optimization/Lbfgs.h"
#include "Molassembler/Temple/Optionals.h"
#include "Molassembler/Temple/Functional.h"
#include "Molassembler/Temple/Random.h"

#include <iostream>

namespace Scine {
namespace Molassembler {
namespace DistanceGeometry {
namespace Detail {

Eigen::MatrixXd gather(const Eigen::VectorXd& vectorizedPositions) {
  constexpr unsigned dimensionality = 4;
  const unsigned N = vectorizedPositions.size() / dimensionality;
  Eigen::MatrixXd positionMatrix(N, 3);
  for(unsigned i = 0; i < N; ++i) {
    positionMatrix.row(i) = vectorizedPositions.template segment<3>(dimensionality * i);
  }
  return positionMatrix;
}

AngstromPositions convertToAngstromPositions(const Eigen::MatrixXd& positions) {
  assert(positions.cols() == 3);
  const unsigned N = positions.rows();
  AngstromPositions angstromWrapper {N};
  for(unsigned i = 0; i < N; ++i) {
    angstromWrapper.positions.row(i) = Utils::Position {
      positions.row(i).transpose()
    };
  }
  return angstromWrapper;
}

Eigen::MatrixXd fitAndSetFixedPositions(
  const Eigen::MatrixXd& positions,
  const Configuration& configuration
) {
  /* Fixed positions postprocessing:
   * - Rotate and translate the generated coordinates towards the fixed
   *   positions indicated for each.
   *
   * Maybe?
   * - Assuming the fit isn't absolutely exact, overwrite the existing
   *   positions with the fixed ones
   */
  /* Construct a reference matrix from the fixed positions (these are still in
   * bohr!) and a weights vector
   */
  assert(positions.cols() == 3);
  const unsigned N = positions.rows();
  Eigen::MatrixXd referenceMatrix = Eigen::MatrixXd::Zero(N, 3);
  Eigen::VectorXd weights = Eigen::VectorXd::Zero(N);
  for(const auto& indexPositionPair : configuration.fixedPositions) {
    referenceMatrix.row(indexPositionPair.first) = indexPositionPair.second.transpose();
    weights(indexPositionPair.first) = 1;
  }

  referenceMatrix *= Utils::Constants::angstrom_per_bohr;

  // Perform the QuaternionFit
  Utils::QuaternionFit fit(referenceMatrix, positions, weights);

  return fit.getFittedData();
}

Molecule narrow(Molecule molecule, Random::Engine& engine) {
  const auto& stereopermutatorList = molecule.stereopermutators();

  do {
    /* If we change any stereopermutator, we must re-check if there are still
     * unassigned stereopermutators since assigning a stereopermutator can
     * invalidate the entire stereopermutator list (because stereopermutators
     * may appear or disappear on assignment due to ranking)
     */
    std::vector<AtomIndex> unassignedAtomStereopermutators;

    for(const auto& atomStereopermutator : stereopermutatorList.atomStereopermutators()) {
      if(!atomStereopermutator.assigned()) {
        unassignedAtomStereopermutators.push_back(
          atomStereopermutator.placement()
        );
      }
    }

    if(!unassignedAtomStereopermutators.empty()) {
      molecule.assignStereopermutatorRandomly(
        Temple::Random::pick(unassignedAtomStereopermutators, engine),
        engine
      );

      // Re-check the loop condition
      continue;
    }

    std::vector<BondIndex> unassignedBondStereopermutators;

    for(const auto& bondStereopermutator : stereopermutatorList.bondStereopermutators()) {
      if(!bondStereopermutator.assigned()) {
        unassignedBondStereopermutators.push_back(
          bondStereopermutator.placement()
        );
      }
    }

    if(!unassignedBondStereopermutators.empty()) {
      molecule.assignStereopermutatorRandomly(
        Temple::Random::pick(unassignedBondStereopermutators, engine),
        engine
      );
    }

  } while(stereopermutatorList.hasUnassignedStereopermutators());

  return molecule;
}

template<typename EigenRefinementType>
struct InversionOrIterLimitStop {
  using VectorType = typename EigenRefinementType::VectorType;
  using FloatType = typename EigenRefinementType::FloatingPointType;

  InversionOrIterLimitStop(
    const unsigned passIter,
    const EigenRefinementType& functor
  ) : iterLimit(passIter),
      refinementFunctorReference(functor)
  {}

  template<typename StepValues>
  bool shouldContinue(unsigned iteration, const StepValues& step) {
    return (
      iteration < iterLimit
      && refinementFunctorReference.proportionChiralConstraintsCorrectSign < 1.0
      && (step.parameters.proposed - step.parameters.current).norm() > minParameterDiffNorm
    );
  }

  const unsigned iterLimit;
  const EigenRefinementType& refinementFunctorReference;
  double minParameterDiffNorm = 1e-3;
};

template<typename FloatType>
struct GradientOrIterLimitStop {
  using VectorType = Eigen::Matrix<FloatType, Eigen::Dynamic, 1>;

  template<typename StepValues>
  bool shouldContinue(unsigned iteration, const StepValues& step) {
    return (
      iteration < iterLimit
      && step.gradients.current.template cast<double>().norm() > gradNorm
      && (step.parameters.proposed - step.parameters.current).norm() > minParameterDiffNorm
    );
  }

  unsigned iterLimit = 10000;
  double gradNorm = 1e-5;
  double minParameterDiffNorm = 1e-3;
};

template<unsigned dimensionality>
Eigen::Vector3d averagePosition(
  const Eigen::VectorXd& linearPositions,
  const std::vector<AtomIndex>& indices
) {
  if(indices.size() == 1) {
    return linearPositions.template segment<3>(dimensionality * indices.front());
  }

  Eigen::Vector3d average = Eigen::Vector3d::Zero();
  for(AtomIndex i : indices) {
    average += linearPositions.template segment<3>(dimensionality * i);
  }
  average /= indices.size();
  return average;
}

template<unsigned dimensionality>
void twistRotatableDihedrals(
  Eigen::Ref<Eigen::VectorXd> positions,
  const std::vector<DihedralConstraint>& constraints,
  const MoleculeDGInformation::GroupMapType& rotatableGroupMap
) {
  for(const DihedralConstraint& constraint : constraints) {
    const AtomIndex j = constraint.sites.at(1).front();
    const AtomIndex k = constraint.sites.at(2).front();
    BondIndex bond {j, k};

    const auto findIter = rotatableGroupMap.find(bond);
    if(findIter == std::end(rotatableGroupMap)) {
      continue;
    }

    const MoleculeDGInformation::RotatableGroup& group = findIter->second;

    const Eigen::Vector3d jVector = positions.template segment<3>(dimensionality * j);
    const Eigen::Vector3d kVector = positions.template segment<3>(dimensionality * k);

    const double measuredDihedral = Cartesian::dihedral(
      averagePosition<dimensionality>(positions, constraint.sites.at(0)),
      jVector,
      kVector,
      averagePosition<dimensionality>(positions, constraint.sites.at(3))
    );

    const double targetDihedral = Cartesian::dihedralAverage(
      constraint.lower,
      constraint.upper
    );

    Eigen::Vector3d axisVector = (kVector - jVector).normalized();
    if(group.side == j) {
      axisVector *= -1;
    }

    const Eigen::Matrix3d rotation = Eigen::AngleAxisd {
      targetDihedral - measuredDihedral,
      axisVector
    }.toRotationMatrix();

    const Eigen::Vector3d sideVector = positions.template segment<3>(dimensionality * group.side);
    for(AtomIndex i : group.vertices) {
      positions.template segment<3>(dimensionality * i) = sideVector + rotation * (
        positions.template segment<3>(dimensionality * i) - sideVector
      );
    }
  }
}

} // namespace Detail

MoleculeDGInformation::GroupMapType MoleculeDGInformation::make(
  const std::vector<DihedralConstraint>& constraints,
  const Molecule& molecule
) {
  MoleculeDGInformation::GroupMapType groups;

  Temple::forEach(constraints,
    [&](const DihedralConstraint& constraint) {
      // It is guaranteed that the sites in the middle have one index only
      BondIndex bond {
        constraint.sites.at(1).front(),
        constraint.sites.at(2).front(),
      };

      // Populate with group to rotate information on the smaller side
      if(molecule.graph().cycles().numCycleFamilies(bond) == 0) {
        auto sides = molecule.graph().splitAlongBridge(bond);
        if(sides.first.size() < sides.second.size()) {
          groups.emplace(
            bond,
            MoleculeDGInformation::RotatableGroup {bond.first, sides.first}
          );
        } else {
          groups.emplace(
            bond,
            MoleculeDGInformation::RotatableGroup {bond.second, sides.second}
          );
        }
      }
    }
  );

  return groups;
}

MoleculeDGInformation gatherDGInformation(
  const Molecule& molecule,
  const Configuration& configuration
) {
  // Generate a spatial model from the molecular graph and stereopermutators
  SpatialModel spatialModel {molecule, configuration};

  // Extract gathered data
  MoleculeDGInformation data;
  data.bounds = spatialModel.makePairwiseBounds();
  data.chiralConstraints = spatialModel.getChiralConstraints();
  data.dihedralConstraints = spatialModel.getDihedralConstraints();
  data.rotatableGroups = MoleculeDGInformation::make(data.dihedralConstraints, molecule);

  return data;
}

outcome::result<AngstromPositions> refine(
  Eigen::MatrixXd embeddedPositions,
  const DistanceBoundsMatrix& distanceBounds,
  const Configuration& configuration,
  const std::shared_ptr<MoleculeDGInformation>& DgDataPtr
) {
  /* Refinement problem compile-time settings
   * - Dimensionality four is needed to ensure chiral constraints invert
   *   nicely
   * - FloatType double is helpful for refinement stability, and float
   *   doesn't affect speed
   * - Using the alternative SIMD implementations of the refinement problems
   *   hardly affects speed at all, in fact, it commonly worsens it.
   */
  constexpr unsigned dimensionality = 4;
  using FloatType = double;
  constexpr bool SIMD = false;

  using FullRefinementType = EigenRefinementProblem<dimensionality, FloatType, SIMD>;
  using VectorType = typename FullRefinementType::VectorType;

  // Vectorize positions
  VectorType transformedPositions = Eigen::Map<Eigen::VectorXd>(
    embeddedPositions.data(),
    embeddedPositions.cols() * embeddedPositions.rows()
  ).template cast<FloatType>().eval();

  const unsigned N = transformedPositions.size() / dimensionality;

  const auto squaredBounds = static_cast<Eigen::MatrixXd>(
    distanceBounds.access().cwiseProduct(distanceBounds.access())
  );

  FullRefinementType refinementFunctor {
    squaredBounds,
    DgDataPtr->chiralConstraints,
    DgDataPtr->dihedralConstraints
  };

  /* If a count of chiral constraints reveals that more than half are
   * incorrect, we can invert the structure (by multiplying e.g. all y
   * coordinates with -1) and then have more than half of chirality
   * constraints correct! In the count, chiral constraints with a target
   * value of zero are not considered (this would skew the count as those
   * chiral constraints should not have to pass an energetic maximum to
   * converge properly as opposed to tetrahedra with volume).
   */
  double initiallyCorrectChiralConstraints = refinementFunctor.calculateProportionChiralConstraintsCorrectSign(transformedPositions);
  if(initiallyCorrectChiralConstraints < 0.5) {
    // Invert y coordinates
    for(unsigned i = 0; i < N; ++i) {
      transformedPositions(dimensionality * i + 1) *= -1;
    }

    initiallyCorrectChiralConstraints = 1 - initiallyCorrectChiralConstraints;
  }

  /* Refinement without penalty on fourth dimension only necessary if not all
   * chiral centers are correct. Of course, for molecules without chiral
   * centers at all, this stage is unnecessary
   */
  unsigned firstStageIterations = 0;
  if(initiallyCorrectChiralConstraints < 1) {
    Detail::InversionOrIterLimitStop<FullRefinementType> inversionChecker {
      configuration.refinementStepLimit,
      refinementFunctor
    };

    Temple::Lbfgs<FloatType, 32> optimizer;

    try {
      auto result = optimizer.minimize(
        transformedPositions,
        refinementFunctor,
        inversionChecker
      );
      firstStageIterations = result.iterations;
    } catch(std::runtime_error& e) {
      return DgError::RefinementException;
    }

    if(firstStageIterations >= configuration.refinementStepLimit) {
      return DgError::RefinementMaxIterationsReached;
    }

    if(refinementFunctor.proportionChiralConstraintsCorrectSign < 1.0) {
      return DgError::RefinedChiralsWrong;
    }
  }

  /* Set up the second stage of refinement where we compress out the fourth
   * dimension that we allowed expansion into to invert the chiralities.
   */
  refinementFunctor.compressFourthDimension = true;

  unsigned secondStageIterations = 0;
  Detail::GradientOrIterLimitStop<FloatType> gradientChecker;
  gradientChecker.gradNorm = 1e-3;
  gradientChecker.iterLimit = configuration.refinementStepLimit - firstStageIterations;

  try {
    Temple::Lbfgs<FloatType, 32> optimizer;

    auto result = optimizer.minimize(
      transformedPositions,
      refinementFunctor,
      gradientChecker
    );
    secondStageIterations = result.iterations;
  } catch(std::out_of_range& e) {
    return DgError::RefinementException;
  }

  // Max iterations reached
  if(secondStageIterations >= gradientChecker.iterLimit) {
    return DgError::RefinementMaxIterationsReached;
  }

  // Not all chiral constraints have the right sign
  if(refinementFunctor.proportionChiralConstraintsCorrectSign < 1) {
    return DgError::RefinedChiralsWrong;
  }

  /* Twist all freely rotatable dihedrals to their target values to avoid
   * conflicts between distance and dihedral errors to prevent rotations to
   * target values.
   */
  Detail::twistRotatableDihedrals<dimensionality>(
    transformedPositions,
    DgDataPtr->dihedralConstraints,
    DgDataPtr->rotatableGroups
  );

  /* Add dihedral terms and refine again */
  unsigned thirdStageIterations = 0;
  gradientChecker = Detail::GradientOrIterLimitStop<FloatType> {};
  gradientChecker.gradNorm = 1e-3;
  gradientChecker.iterLimit = (
    configuration.refinementStepLimit
    - firstStageIterations
    - secondStageIterations
  );

  refinementFunctor.dihedralTerms = true;

  try {
    Temple::Lbfgs<FloatType, 32> optimizer;

    auto result = optimizer.minimize(
      transformedPositions,
      refinementFunctor,
      gradientChecker
    );
    thirdStageIterations = result.iterations;
  } catch(std::out_of_range& e) {
    return DgError::RefinementException;
  }

  if(thirdStageIterations >= gradientChecker.iterLimit) {
    return DgError::RefinementMaxIterationsReached;
  }

  // Structure inacceptable
  if(
    !finalStructureAcceptable(
      refinementFunctor,
      distanceBounds,
      transformedPositions
    )
  ) {
    return DgError::RefinedStructureInacceptable;
  }

  auto gatheredPositions = Detail::gather(transformedPositions);

  if(!configuration.fixedPositions.empty()) {
    return Detail::convertToAngstromPositions(
      Detail::fitAndSetFixedPositions(gatheredPositions, configuration)
    );
  }

  return Detail::convertToAngstromPositions(gatheredPositions);
}

outcome::result<AngstromPositions> generateConformer(
  const Molecule& molecule,
  const Configuration& configuration,
  std::shared_ptr<MoleculeDGInformation>& DgDataPtr,
  bool regenerateDGDataEachStep,
  Random::Engine& engine
) {
  if(regenerateDGDataEachStep) {
    auto moleculeCopy = Detail::narrow(molecule, engine);

    if(moleculeCopy.stereopermutators().hasZeroAssignmentStereopermutators()) {
      return DgError::ZeroAssignmentStereopermutators;
    }

    DgDataPtr = std::make_shared<MoleculeDGInformation>(
      gatherDGInformation(moleculeCopy, configuration)
    );
  }

  ExplicitBoundsGraph explicitGraph {
    molecule.graph().inner(),
    DgDataPtr->bounds
  };

  // Get distance bounds matrix from the graph
  auto distanceBoundsResult = explicitGraph.makeDistanceBounds();
  if(!distanceBoundsResult) {
    return distanceBoundsResult.as_failure();
  }

  DistanceBoundsMatrix distanceBounds {std::move(distanceBoundsResult.value())};

  /* There should be no need to smooth the distance bounds, because the graph
   * type ought to create them within the triangle inequality bounds:
   */
  assert(distanceBounds.boundInconsistencies() == 0);

  // Generate a distances matrix from the graph
  auto distanceMatrixResult = explicitGraph.makeDistanceMatrix(
    engine,
    configuration.partiality
  );
  if(!distanceMatrixResult) {
    return distanceMatrixResult.as_failure();
  }

  // Make a metric matrix from the distances matrix
  MetricMatrix metric(
    std::move(distanceMatrixResult.value())
  );

  // Get a position matrix by embedding the metric matrix
  auto embeddedPositions = metric.embed();

  /* Refinement */
  return refine(
    std::move(embeddedPositions),
    distanceBounds,
    configuration,
    DgDataPtr
  );
}

std::vector<
  outcome::result<AngstromPositions>
> run(
  const Molecule& molecule,
  const unsigned numConformers,
  const Configuration& configuration,
  const boost::optional<unsigned> seedOption
) {
  using ReturnType = std::vector<
    outcome::result<AngstromPositions>
  >;

  // In case there are zero assignment stereopermutators, we give up immediately
  if(molecule.stereopermutators().hasZeroAssignmentStereopermutators()) {
    return ReturnType(numConformers, DgError::ZeroAssignmentStereopermutators);
  }

#ifdef _OPENMP
  /* Ensure the molecule's mutable properties are already generated so none are
   * generated on threaded const-access.
   */
  molecule.graph().inner().populateProperties();
#endif

  /* In case the molecule has unassigned stereopermutators, we need to randomly
   * assign them for each conformer generated prior to generating the distance
   * bounds matrix. If not, then modelling data can be kept across all
   * conformer generation runs since no randomness has entered the equation.
   */
  auto DgDataPtr = std::make_shared<MoleculeDGInformation>();
  bool regenerateEachStep = molecule.stereopermutators().hasUnassignedStereopermutators();
  if(!regenerateEachStep) {
    *DgDataPtr = gatherDGInformation(molecule, configuration);
  }

  ReturnType results(numConformers, static_cast<DgError>(0));

  /* If a seed is supplied, the global prng state is not to be advanced.
   * We create a random engine from the seed here if a seed is supplied.
   */
  auto engineOption = Temple::Optionals::map(
    seedOption,
    [](unsigned seed) { return Random::Engine(seed); }
  );

  /* Now we need something referencing either our new engine or the global prng
   * engine.
   */
  std::reference_wrapper<Random::Engine> backgroundEngineWrapper = randomnessEngine();
  if(engineOption) {
    backgroundEngineWrapper = engineOption.value();
  }
  Random::Engine& backgroundEngine = backgroundEngineWrapper.get();

  /* We have to distribute pseudo-randomness into each thread reproducibly
   * and want to avoid having to guard the global PRNG against access from
   * multiple threads, so we provide each thread its own Engine and pre-generate
   * each conformer's individual seed in the sequential section.
   */
#ifdef _OPENMP
  const unsigned nThreads = omp_get_max_threads();
#else
  const unsigned nThreads = 1;
#endif

  std::vector<Random::Engine> randomnessEngines(nThreads);
  const auto seeds = Temple::Random::getN<int>(
    0,
    std::numeric_limits<int>::max(),
    numConformers,
    backgroundEngine
  );

  /* Each thread has its own DgDataPtr, for the following reason: If we do
   * not need to regenerate the SpatialModel data, then having all threads
   * share access the underlying data to generate conformers is fine. If,
   * otherwise, we do need to regenerate the SpatialModel data for each
   * conformer, then each thread will reset its pointer to its self-generated
   * SpatialModel data, creating thread-private state.
   */
#pragma omp parallel for firstprivate(DgDataPtr) schedule(dynamic)
  for(unsigned i = 0; i < numConformers; ++i) {
    // Get thread-specific randomness engine reference
#ifdef _OPENMP
    Random::Engine& engine = randomnessEngines.at(
      omp_get_thread_num()
    );
#else
    Random::Engine& engine = randomnessEngines.front();
#endif

    // Re-seed the thread-local PRNG engine for each conformer
    engine.seed(seeds.at(i));

    /* We have to handle any and all exceptions here bceause this is a parallel
     * environment and exceptions are not propagated anywhere
     */
    try {
      // Generate the conformer
      auto conformerResult = generateConformer(
        molecule,
        configuration,
        DgDataPtr,
        regenerateEachStep,
        engine
      );

      results.at(i) = std::move(conformerResult);
    } catch(std::exception& e) {
#pragma omp critical(outputWarning)
      {
        std::cerr << "WARNING: Uncaught exception in conformer generation: " << e.what() << "\n";
      }
      results.at(i) = DgError::UnknownException;
    } // end catch
  } // end pragma omp for private(DgDataPtr)

  return results;
}

} // namespace DistanceGeometry
} // namespace Molassembler
} // namespace Scine

ext: cpp


text:
// Copyright 2020 Tier IV, Inc.
//
// 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.

#ifndef SCENE_MODULE__TRAFFIC_LIGHT__SCENE_HPP_
#define SCENE_MODULE__TRAFFIC_LIGHT__SCENE_HPP_

#include <memory>
#include <string>
#include <tuple>
#include <vector>

#define EIGEN_MPL2_ONLY
#include <Eigen/Core>
#include <Eigen/Geometry>
#include <lanelet2_extension/utility/query.hpp>
#include <rclcpp/rclcpp.hpp>
#include <scene_module/scene_module_interface.hpp>
#include <utilization/boost_geometry_helper.hpp>

#include <autoware_auto_perception_msgs/msg/looking_traffic_signal.hpp>

#include <lanelet2_core/LaneletMap.h>
#include <lanelet2_routing/RoutingGraph.h>

namespace behavior_velocity_planner
{
class TrafficLightModule : public SceneModuleInterface
{
public:
  enum class State { APPROACH, GO_OUT };
  enum class Input { PERCEPTION, EXTERNAL, NONE };  // EXTERNAL: FOA, V2X, etc.

  struct DebugData
  {
    double base_link2front;
    std::vector<std::tuple<
      std::shared_ptr<const lanelet::TrafficLight>,
      autoware_auto_perception_msgs::msg::TrafficSignal>>
      tl_state;
    std::vector<geometry_msgs::msg::Pose> stop_poses;
    geometry_msgs::msg::Pose first_stop_pose;
    std::vector<geometry_msgs::msg::Pose> dead_line_poses;
    std::vector<geometry_msgs::msg::Point> traffic_light_points;
    geometry_msgs::msg::Point highest_confidence_traffic_light_point;
  };

  struct PlannerParam
  {
    double stop_margin;
    double tl_state_timeout;
    double external_tl_state_timeout;
    double yellow_lamp_period;
    bool enable_pass_judge;
  };

public:
  TrafficLightModule(
    const int64_t module_id, const lanelet::TrafficLight & traffic_light_reg_elem,
    lanelet::ConstLanelet lane, const PlannerParam & planner_param, const rclcpp::Logger logger,
    const rclcpp::Clock::SharedPtr clock);

  bool modifyPathVelocity(
    autoware_auto_planning_msgs::msg::PathWithLaneId * path,
    tier4_planning_msgs::msg::StopReason * stop_reason) override;

  visualization_msgs::msg::MarkerArray createDebugMarkerArray() override;

  inline autoware_auto_perception_msgs::msg::LookingTrafficSignal getTrafficSignal() const
  {
    return looking_tl_state_;
  }

  inline State getTrafficLightModuleState() const { return state_; }

  inline boost::optional<int> getFirstRefStopPathPointIndex() const
  {
    return first_ref_stop_path_point_index_;
  }

private:
  bool isStopSignal(const lanelet::ConstLineStringsOrPolygons3d & traffic_lights);

  bool isTrafficSignalStop(
    const autoware_auto_perception_msgs::msg::TrafficSignal & tl_state) const;

  autoware_auto_planning_msgs::msg::PathWithLaneId insertStopPose(
    const autoware_auto_planning_msgs::msg::PathWithLaneId & input,
    const size_t & insert_target_point_idx, const Eigen::Vector2d & target_point,
    tier4_planning_msgs::msg::StopReason * stop_reason);

  bool isPassthrough(const double & signed_arc_length) const;

  bool hasTrafficLightCircleColor(
    const autoware_auto_perception_msgs::msg::TrafficSignal & tl_state,
    const uint8_t & lamp_color) const;

  bool hasTrafficLightShape(
    const autoware_auto_perception_msgs::msg::TrafficSignal & tl_state,
    const uint8_t & lamp_shape) const;

  bool getHighestConfidenceTrafficSignal(
    const lanelet::ConstLineStringsOrPolygons3d & traffic_lights,
    autoware_auto_perception_msgs::msg::TrafficSignalStamped & highest_confidence_tl_state);

  bool getExternalTrafficSignal(
    const lanelet::ConstLineStringsOrPolygons3d & traffic_lights,
    autoware_auto_perception_msgs::msg::TrafficSignalStamped & external_tl_state);

  bool updateTrafficSignal(const lanelet::ConstLineStringsOrPolygons3d & traffic_lights);

  autoware_auto_perception_msgs::msg::TrafficSignalWithJudge generateTlStateWithJudgeFromTlState(
    const autoware_auto_perception_msgs::msg::TrafficSignal tl_state) const;

  // Key Feature
  const lanelet::TrafficLight & traffic_light_reg_elem_;
  lanelet::ConstLanelet lane_;

  // State
  State state_;

  // Input
  Input input_;

  // Parameter
  PlannerParam planner_param_;

  // Debug
  DebugData debug_data_;

  // prevent paththrough chattering
  bool is_prev_state_stop_;

  boost::optional<int> first_ref_stop_path_point_index_;

  // Traffic Light State
  autoware_auto_perception_msgs::msg::LookingTrafficSignal looking_tl_state_;
};
}  // namespace behavior_velocity_planner

#endif  // SCENE_MODULE__TRAFFIC_LIGHT__SCENE_HPP_


text:
// Copyright (c) 2005, Rodrigo Braz Monteiro, Niels Martin Hansen
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//   * Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//   * Neither the name of the Aegisub Group nor the names of its contributors
//     may be used to endorse or promote products derived from this software
//     without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// Aegisub Project http://www.aegisub.org/

/// @file frame_main.cpp
/// @brief Main window creation and control management
/// @ingroup main_ui

#include "config.h"

#include "frame_main.h"

#include <libaegisub/fs.h>
#include <libaegisub/log.h>
#include <libaegisub/path.h>
#include <libaegisub/util.h>

#include "include/aegisub/context.h"
#include "include/aegisub/menu.h"
#include "include/aegisub/toolbar.h"
#include "include/aegisub/hotkey.h"

#include "ass_file.h"
#include "audio_controller.h"
#include "audio_box.h"
#include "auto4_base.h"
#include "base_grid.h"
#include "compat.h"
#include "command/command.h"
#include "dialog_detached_video.h"
#include "dialog_manager.h"
#include "dialog_version_check.h"
#include "help_button.h"
#include "initial_line_state.h"
#include "libresrc/libresrc.h"
#include "main.h"
#include "options.h"
#include "search_replace_engine.h"
#include "subs_controller.h"
#include "subs_edit_box.h"
#include "subs_edit_ctrl.h"
#include "utils.h"
#include "version.h"
#include "video_box.h"
#include "video_context.h"
#include "video_display.h"
#include "video_provider_manager.h"
#include "video_slider.h"

#include <boost/algorithm/string/predicate.hpp>

#include <wx/dnd.h>
#include <wx/filename.h>
#include <wx/image.h>
#include <wx/msgdlg.h>
#include <wx/statline.h>
#include <wx/sysopt.h>

enum {
	ID_APP_TIMER_STATUSCLEAR				= 12002
};

#ifdef WITH_STARTUPLOG
#define StartupLog(a) MessageBox(0, a, "Aegisub startup log", 0)
#else
#define StartupLog(a) LOG_I("frame_main/init") << a
#endif

wxDEFINE_EVENT(FILE_LIST_DROPPED, wxThreadEvent);

static void get_files_to_load(wxArrayString const& list, std::string &subs, std::string &audio, std::string &video) {
	// Keep these lists sorted

	// Video formats
	const wxString videoList[] = {
		"asf",
		"avi",
		"avs",
		"d2v",
		"m2ts",
		"m4v",
		"mkv",
		"mov",
		"mp4",
		"mpeg",
		"mpg",
		"ogm",
		"rm",
		"rmvb",
		"ts",
		"webm"
		"wmv",
		"y4m",
		"yuv"
	};

	// Subtitle formats
	const wxString subsList[] = {
		"ass",
		"srt",
		"ssa",
		"sub",
		"ttxt",
		"txt"
	};

	// Audio formats
	const wxString audioList[] = {
		"aac",
		"ac3",
		"ape",
		"dts",
		"flac",
		"m4a",
		"mka",
		"mp3",
		"ogg",
		"w64",
		"wav",
		"wma"
	};

	// Scan list
	for (wxFileName file : list) {
		if (file.IsRelative()) file.MakeAbsolute();
		if (!file.FileExists()) continue;

		wxString ext = file.GetExt().Lower();

		if (subs.empty() && std::binary_search(std::begin(subsList), std::end(subsList), ext))
			subs = from_wx(file.GetFullPath());
		if (video.empty() && std::binary_search(std::begin(videoList), std::end(videoList), ext))
			video = from_wx(file.GetFullPath());
		if (audio.empty() && std::binary_search(std::begin(audioList), std::end(audioList), ext))
			audio = from_wx(file.GetFullPath());
	}
}

/// Handle files drag and dropped onto Aegisub
class AegisubFileDropTarget : public wxFileDropTarget {
	FrameMain *parent;
public:
	AegisubFileDropTarget(FrameMain *parent) : parent(parent) { }
	bool OnDropFiles(wxCoord, wxCoord, const wxArrayString& filenames) override {
		std::string subs, audio, video;
		get_files_to_load(filenames, subs, audio, video);

		if (subs.empty() && audio.empty() && video.empty())
			return false;

		auto evt = new wxThreadEvent(FILE_LIST_DROPPED);
		evt->SetPayload(filenames);
		parent->QueueEvent(evt);
		return true;
	}
};

FrameMain::FrameMain()
: wxFrame(nullptr, -1, "", wxDefaultPosition, wxSize(920,700), wxDEFAULT_FRAME_STYLE | wxCLIP_CHILDREN)
, context(agi::util::make_unique<agi::Context>())
{
	StartupLog("Entering FrameMain constructor");

#ifdef __WXGTK__
	// XXX HACK XXX
	// We need to set LC_ALL to "" here for input methods to work reliably.
	setlocale(LC_ALL, "");

	// However LC_NUMERIC must be "C", otherwise some parsing fails.
	setlocale(LC_NUMERIC, "C");
#endif
#ifdef __APPLE__
	// When run from an app bundle, LC_CTYPE defaults to "C", which breaks on
	// anything involving unicode and in some cases number formatting.
	// The right thing to do here would be to query CoreFoundation for the user's
	// locale and add .UTF-8 to that, but :effort:
	LOG_D("locale") << setlocale(LC_ALL, nullptr);
	setlocale(LC_CTYPE, "en_US.UTF-8");
	LOG_D("locale") << setlocale(LC_ALL, nullptr);
#endif

	StartupLog("Initializing context models");
	memset(context.get(), 0, sizeof(*context));
	context->ass = new AssFile;

	StartupLog("Initializing context controls");
	context->subsController = new SubsController(context.get());
	context->ass->AddCommitListener(&FrameMain::UpdateTitle, this);
	context->subsController->AddFileOpenListener(&FrameMain::OnSubtitlesOpen, this);
	context->subsController->AddFileSaveListener(&FrameMain::UpdateTitle, this);

	context->audioController = new AudioController(context.get());
	context->audioController->AddAudioOpenListener(&FrameMain::OnAudioOpen, this);
	context->audioController->AddAudioCloseListener(&FrameMain::OnAudioClose, this);

	context->local_scripts = new Automation4::LocalScriptManager(context.get());

	// Initialized later due to that the selection controller is currently the subtitles grid
	context->selectionController = nullptr;

	context->videoController = VideoContext::Get(); // derp
	context->videoController->AddVideoOpenListener(&FrameMain::OnVideoOpen, this);

	StartupLog("Initializing context frames");
	context->parent = this;
	context->previousFocus = nullptr;

	StartupLog("Install PNG handler");
	wxImage::AddHandler(new wxPNGHandler);
#ifndef __APPLE__
	wxSafeYield();
#endif

	StartupLog("Apply saved Maximized state");
	if (OPT_GET("App/Maximized")->GetBool()) Maximize(true);

	StartupLog("Initialize toolbar");
	InitToolbar();

	StartupLog("Initialize menu bar");
	menu::GetMenuBar("main", this, context.get());

	StartupLog("Create status bar");
	CreateStatusBar(2);

	StartupLog("Set icon");
#ifdef _WIN32
	SetIcon(wxICON(wxicon));
#else
	wxIcon icon;
	icon.CopyFromBitmap(GETIMAGE(wxicon));
	SetIcon(icon);
#endif

	StartupLog("Create views and inner main window controls");
	context->dialog = new DialogManager;
	InitContents();
	OPT_SUB("Video/Detached/Enabled", &FrameMain::OnVideoDetach, this, agi::signal::_1);

	StartupLog("Complete context initialization");
	context->videoController->SetContext(context.get());

	StartupLog("Set up drag/drop target");
	SetDropTarget(new AegisubFileDropTarget(this));

	StartupLog("Load default file");
	context->subsController->Close();

	StartupLog("Display main window");
	AddFullScreenButton(this);
	Show();
	SetDisplayMode(1, 1);

	// Version checker
	StartupLog("Possibly perform automatic updates check");
	if (OPT_GET("App/First Start")->GetBool()) {
		OPT_SET("App/First Start")->SetBool(false);
#ifdef WITH_UPDATE_CHECKER
		int result = wxMessageBox(_("Do you want Aegisub to check for updates whenever it starts? You can still do it manually via the Help menu."),_("Check for updates?"), wxYES_NO | wxCENTER);
		OPT_SET("App/Auto/Check For Updates")->SetBool(result == wxYES);
		config::opt->Flush();
#endif
	}

#ifdef WITH_UPDATE_CHECKER
	PerformVersionCheck(false);
#endif

	Bind(FILE_LIST_DROPPED, &FrameMain::OnFilesDropped, this);

	StartupLog("Leaving FrameMain constructor");
}

/// @brief Delete everything but @a keep and its parents
/// @param window Root window to delete the children of
/// @param keep Window to keep alive
/// @return Was @a keep found?
static bool delete_children(wxWindow *window, wxWindow *keep) {
	bool found = false;
	while (window->GetChildren().size() > (size_t)found) {
		auto it = window->GetChildren().begin();

		if (*it == keep)
			found = true;

		if (found) {
			if (++it != window->GetChildren().end())
				(*it)->wxWindowBase::Destroy();
		}
		else if (!delete_children(*it, keep))
			(*it)->wxWindowBase::Destroy();
		else
			found = true;
	}
	return found;
}

FrameMain::~FrameMain () {
	wxGetApp().frame = nullptr;

	context->videoController->SetVideo("");
	context->audioController->CloseAudio();

	// SubsGrid needs to be deleted last due to being the selection
	// controller, but everything else needs to be deleted before the context
	// is cleaned up
	delete_children(this, SubsGrid);

	delete context->ass;
	delete context->audioController;
	delete context->local_scripts;
}

void FrameMain::InitToolbar() {
	wxSystemOptions::SetOption("msw.remap", 0);
	OPT_SUB("App/Show Toolbar", &FrameMain::EnableToolBar, this);
	EnableToolBar(*OPT_GET("App/Show Toolbar"));
}

void FrameMain::EnableToolBar(agi::OptionValue const& opt) {
	if (opt.GetBool()) {
		if (!GetToolBar()) {
			toolbar::AttachToolbar(this, "main", context.get(), "Default");
			GetToolBar()->Realize();
		}
	}
	else if (wxToolBar *old_tb = GetToolBar()) {
		SetToolBar(nullptr);
		delete old_tb;
		Layout();
	}
}

void FrameMain::InitContents() {
	StartupLog("Create background panel");
	wxPanel *Panel = new wxPanel(this,-1,wxDefaultPosition,wxDefaultSize,wxTAB_TRAVERSAL | wxCLIP_CHILDREN);

	StartupLog("Create subtitles grid");
	context->subsGrid = SubsGrid = new BaseGrid(Panel, context.get(), wxDefaultSize, wxWANTS_CHARS | wxSUNKEN_BORDER);
	context->selectionController = context->subsGrid;
	context->search = new SearchReplaceEngine(context.get());
	context->initialLineState = new InitialLineState(context.get());

	StartupLog("Create video box");
	videoBox = new VideoBox(Panel, false, context.get());

	StartupLog("Create audio box");
	context->audioBox = audioBox = new AudioBox(Panel, context.get());

	StartupLog("Create subtitle editing box");
	auto EditBox = new SubsEditBox(Panel, context.get());

	StartupLog("Arrange main sizers");
	ToolsSizer = new wxBoxSizer(wxVERTICAL);
	ToolsSizer->Add(audioBox, 0, wxEXPAND);
	ToolsSizer->Add(EditBox, 1, wxEXPAND);
	TopSizer = new wxBoxSizer(wxHORIZONTAL);
	TopSizer->Add(videoBox, 0, wxEXPAND, 0);
	TopSizer->Add(ToolsSizer, 1, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5);
	MainSizer = new wxBoxSizer(wxVERTICAL);
	MainSizer->Add(new wxStaticLine(Panel),0,wxEXPAND | wxALL,0);
	MainSizer->Add(TopSizer,0,wxEXPAND | wxALL,0);
	MainSizer->Add(SubsGrid,1,wxEXPAND | wxALL,0);
	Panel->SetSizer(MainSizer);

	StartupLog("Perform layout");
	Layout();
	StartupLog("Leaving InitContents");
}

void FrameMain::SetDisplayMode(int video, int audio) {
	if (!IsShownOnScreen()) return;

	bool sv = false, sa = false;

	if (video == -1) sv = showVideo;
	else if (video)  sv = context->videoController->IsLoaded() && !context->dialog->Get<DialogDetachedVideo>();

	if (audio == -1) sa = showAudio;
	else if (audio)  sa = context->audioController->IsAudioOpen();

	// See if anything changed
	if (sv == showVideo && sa == showAudio) return;

	showVideo = sv;
	showAudio = sa;

	bool didFreeze = !IsFrozen();
	if (didFreeze) Freeze();

	context->videoController->Stop();

	TopSizer->Show(videoBox, showVideo, true);
	ToolsSizer->Show(audioBox, showAudio, true);

	MainSizer->CalcMin();
	MainSizer->RecalcSizes();
	MainSizer->Layout();
	Layout();

	if (didFreeze) Thaw();
}

void FrameMain::UpdateTitle() {
	wxString newTitle;
	if (context->subsController->IsModified()) newTitle << "* ";
	newTitle << context->subsController->Filename().filename().wstring();

#ifndef __WXMAC__
	newTitle << " - Aegisub " << GetAegisubLongVersionString();
#endif

#if defined(__WXMAC__) && !defined(__LP64__)
	// On Mac, set the mark in the close button
	OSXSetModified(context->subsController->IsModified());
#endif

	if (GetTitle() != newTitle) SetTitle(newTitle);
}

void FrameMain::OnVideoOpen() {
	if (!context->videoController->IsLoaded()) {
		SetDisplayMode(0, -1);
		return;
	}

	Freeze();
	int vidx = context->videoController->GetWidth(),
		vidy = context->videoController->GetHeight();

	// Set zoom level based on video resolution and window size
	double zoom = context->videoDisplay->GetZoom();
	wxSize windowSize = GetSize();
	if (vidx*3*zoom > windowSize.GetX()*4 || vidy*4*zoom > windowSize.GetY()*6)
		context->videoDisplay->SetZoom(zoom * .25);
	else if (vidx*3*zoom > windowSize.GetX()*2 || vidy*4*zoom > windowSize.GetY()*3)
		context->videoDisplay->SetZoom(zoom * .5);

	SetDisplayMode(1,-1);

	if (OPT_GET("Video/Detached/Enabled")->GetBool() && !context->dialog->Get<DialogDetachedVideo>())
		cmd::call("video/detach", context.get());
	Thaw();

	if (!blockAudioLoad && OPT_GET("Video/Open Audio")->GetBool() && context->audioController->GetAudioURL() != context->videoController->GetVideoName()) {
		try {
			context->audioController->OpenAudio(context->videoController->GetVideoName());
		}
		catch (agi::UserCancelException const&) { }
		// Opening a video with no audio data isn't an error, so just log
		// and move on
		catch (agi::fs::FileSystemError const&) {
			LOG_D("video/open/audio") << "File " << context->videoController->GetVideoName() << " found by video provider but not audio provider";
		}
		catch (agi::AudioDataNotFoundError const& e) {
			LOG_D("video/open/audio") << "File " << context->videoController->GetVideoName() << " has no audio data: " << e.GetChainedMessage();
		}
		catch (agi::AudioOpenError const& err) {
			wxMessageBox(to_wx(err.GetMessage()), "Error loading audio", wxOK | wxICON_ERROR | wxCENTER);
		}
	}
}

void FrameMain::OnVideoDetach(agi::OptionValue const& opt) {
	if (opt.GetBool())
		SetDisplayMode(0, -1);
	else if (context->videoController->IsLoaded())
		SetDisplayMode(1, -1);
}

void FrameMain::StatusTimeout(wxString text,int ms) {
	SetStatusText(text,1);
	StatusClear.SetOwner(this, ID_APP_TIMER_STATUSCLEAR);
	StatusClear.Start(ms,true);
}

void FrameMain::OnFilesDropped(wxThreadEvent &evt) {
	LoadList(evt.GetPayload<wxArrayString>());
}

bool FrameMain::LoadList(wxArrayString list) {
	std::string audio, video, subs;
	get_files_to_load(list, subs, audio, video);

	blockVideoLoad = !video.empty();
	blockAudioLoad = !audio.empty();

	// Load files
	if (subs.size())
		context->subsController->Load(subs);

	if (blockVideoLoad) {
		blockVideoLoad = false;
		context->videoController->SetVideo(video);
	}

	if (blockAudioLoad) {
		blockAudioLoad = false;
		try {
			context->audioController->OpenAudio(audio);
		} catch (agi::UserCancelException const&) { }
	}

	bool loaded_any = subs.size() || audio.size() || video.size();
	if (loaded_any)
		Refresh(false);

	return loaded_any;
}

BEGIN_EVENT_TABLE(FrameMain, wxFrame)
	EVT_TIMER(ID_APP_TIMER_STATUSCLEAR, FrameMain::OnStatusClear)

	EVT_CLOSE(FrameMain::OnCloseWindow)

	EVT_CHAR_HOOK(FrameMain::OnKeyDown)
	EVT_MOUSEWHEEL(FrameMain::OnMouseWheel)
END_EVENT_TABLE()

void FrameMain::OnCloseWindow(wxCloseEvent &event) {
	wxEventBlocker blocker(this, wxEVT_CLOSE_WINDOW);

	context->videoController->Stop();
	context->audioController->Stop();

	// Ask user if he wants to save first
	if (context->subsController->TryToClose(event.CanVeto()) == wxCANCEL) {
		event.Veto();
		return;
	}

	delete context->dialog;
	context->dialog = nullptr;

	// Store maximization state
	OPT_SET("App/Maximized")->SetBool(IsMaximized());

	Destroy();
}

void FrameMain::OnStatusClear(wxTimerEvent &) {
	SetStatusText("",1);
}

void FrameMain::OnAudioOpen(AudioProvider *) {
	SetDisplayMode(-1, 1);
}

void FrameMain::OnAudioClose() {
	SetDisplayMode(-1, 0);
}

void FrameMain::OnSubtitlesOpen() {
	UpdateTitle();
	auto vc = context->videoController;

	/// @todo figure out how to move this to the relevant controllers without
	///       prompting for each file loaded/unloaded

	// Load stuff from the new script
	auto video     = config::path->MakeAbsolute(context->ass->GetScriptInfo("Video File"), "?script");
	auto vfr       = config::path->MakeAbsolute(context->ass->GetScriptInfo("VFR File"), "?script");
	auto keyframes = config::path->MakeAbsolute(context->ass->GetScriptInfo("Keyframes File"), "?script");
	auto audio     = config::path->MakeAbsolute(context->ass->GetScriptInfo("Audio URI"), "?script");

	bool videoChanged     = !blockVideoLoad && video != vc->GetVideoName();
	bool timecodesChanged = vfr != vc->GetTimecodesName();
	bool keyframesChanged = keyframes != vc->GetKeyFramesName();
	bool audioChanged     = !blockAudioLoad && audio != context->audioController->GetAudioURL();

	// Check if there is anything to change
	int autoLoadMode = OPT_GET("App/Auto/Load Linked Files")->GetInt();
	if (autoLoadMode == 0 || (!videoChanged && !timecodesChanged && !keyframesChanged && !audioChanged)) {
		SetDisplayMode(1, 1);
		return;
	}

	if (autoLoadMode == 2) {
		if (wxMessageBox(_("Do you want to load/unload the associated files?"), _("(Un)Load files?"), wxYES_NO | wxCENTRE, this) != wxYES) {
			SetDisplayMode(1, 1);
			if (vc->IsLoaded() && vc->GetProvider()->GetColorSpace() != context->ass->GetScriptInfo("YCbCr Matrix"))
				vc->Reload();
			return;
		}
	}

	if (audioChanged)
		blockAudioLoad = true;

	// Video
	if (videoChanged) {
		vc->SetVideo(video);
		if (vc->IsLoaded()) {
			vc->JumpToFrame(context->ass->GetUIStateAsInt("Video Position"));

			std::string arString = context->ass->GetUIState("Video Aspect Ratio");
			if (boost::starts_with(arString, "c")) {
				double ar = 0.;
				agi::util::try_parse(arString.substr(1), &ar);
				vc->SetAspectRatio(ar);
			}
			else {
				int ar = 0;
				if (agi::util::try_parse(arString, &ar) && ar >= 0 && ar < 4)
					vc->SetAspectRatio((AspectRatio)ar);
			}

			double videoZoom = 0.;
			if (agi::util::try_parse(context->ass->GetUIState("Video Zoom Percent"), &videoZoom))
				context->videoDisplay->SetZoom(videoZoom);
		}
	}
	else if (vc->IsLoaded() && vc->GetProvider()->GetColorSpace() != context->ass->GetScriptInfo("YCbCr Matrix"))
		vc->Reload();

	vc->LoadTimecodes(vfr);
	vc->LoadKeyframes(keyframes);

	// Audio
	if (audioChanged) {
		blockAudioLoad = false;
		try {
			if (audio.empty())
				context->audioController->CloseAudio();
			else
				context->audioController->OpenAudio(audio);
		}
		catch (agi::UserCancelException const&) { }
		catch (agi::fs::FileSystemError const& err) {
			wxMessageBox(to_wx(err.GetMessage()), "Error opening audio", wxOK | wxICON_ERROR | wxCENTER, this);
		}
	}

	SetDisplayMode(1, 1);
}

void FrameMain::OnKeyDown(wxKeyEvent &event) {
	hotkey::check("Main Frame", context.get(), event);
}

void FrameMain::OnMouseWheel(wxMouseEvent &evt) {
	ForwardMouseWheelEvent(this, evt);
}

max_stars_repo_name: rcombs/Aegisub

max_issues_repo_name: rcombs/Aegisub

max_forks_repo_name: rcombs/Aegisub