/*
Copyright © 2015-2021 Amebis
This file is part of ZRCola.
ZRCola is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ZRCola is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with ZRCola. If not, see .
*/
#include "pch.h"
using namespace std;
using namespace stdex;
using namespace winstd;
///
/// (destination character rank, (source character rank, source character)) data holder
///
class com_translation
{
public:
int rank_src; ///< Source sequence rank
int rank_dst; ///< Destination character rank
string norm; ///< Normalization footprint
inline com_translation() :
rank_src(0),
rank_dst(0)
{
}
inline com_translation(int _rank_src, int _rank_dst) :
rank_src(_rank_src),
rank_dst(_rank_dst)
{
}
inline com_translation(int _rank_src, int _rank_dst, const char *_norm) :
rank_src(_rank_src),
rank_dst(_rank_dst),
norm (_norm )
{
}
inline com_translation(int _rank_src, int _rank_dst, string &&_norm) :
rank_src( _rank_src ),
rank_dst( _rank_dst ),
norm (std::move(_norm ))
{
}
inline com_translation(const com_translation &other) :
rank_src(other.rank_src),
rank_dst(other.rank_dst),
norm (other.norm )
{
}
inline com_translation(com_translation &&other) noexcept :
rank_src( other.rank_src ),
rank_dst( other.rank_dst ),
norm (std::move(other.norm ))
{
}
inline com_translation& operator=(const com_translation &other)
{
if (this != std::addressof(other)) {
rank_src = other.rank_src;
rank_dst = other.rank_dst;
norm = other.norm ;
}
return *this;
}
inline com_translation& operator=(com_translation &&other) noexcept
{
if (this != std::addressof(other)) {
rank_src = other.rank_src ;
rank_dst = other.rank_dst ;
norm = std::move(other.norm );
}
return *this;
}
inline bool operator==(_In_ const com_translation& other) const
{
return
rank_src == other.rank_src &&
rank_dst == other.rank_dst &&
norm == other.norm;
}
inline bool operator!=(_In_ const com_translation &other) const
{
return !operator==(other);
}
inline bool operator<(_In_ const com_translation& other) const
{
if (rank_src < other.rank_src) return true;
else if (rank_src > other.rank_src) return false;
else if (rank_dst < other.rank_dst) return true;
else if (rank_dst > other.rank_dst) return false;
else if (norm < other.norm ) return true;
else return false;
}
inline bool operator<=(_In_ const com_translation &other) const
{
return !operator>(other);
}
inline bool operator>(_In_ const com_translation &other) const
{
return other.operator<(*this);
}
inline bool operator>=(_In_ const com_translation &other) const
{
return !operator<(other);
}
};
typedef map > translation_db;
typedef map normperm_db;
static set translate_inv(_In_ const translation_db &db_trans, _In_ const normperm_db &db_np, _In_z_ const wchar_t *str, _Inout_ set &path);
static inline set permutate_and_translate_inv(_In_ const translation_db &db_trans, _In_ const normperm_db &db_np, _In_z_ const wchar_t *str, _In_z_ const char *norm, _Inout_ set &path);
static set translate_inv(_In_ const translation_db &db_trans, _In_ const normperm_db &db_np, _In_z_ const wchar_t *str, _Inout_ set &path)
{
set res;
if (!*str) {
// Empty string results in empty inverse translation.
res.insert(ZRCola::DBSource::charseq(0, L""));
return res;
}
// Prepare inverse translate of the remainder string (without the first character).
auto res_rem = translate_inv(db_trans, db_np, str + 1, path);
if (res_rem.empty())
return res;
// See if first character is inverse translatable.
translation_db::key_type chr(1, *str);
auto const hit_trans = db_trans.find(chr);
if (hit_trans != db_trans.end()) {
// Current character is inverse translatable.
// Add the current character to the path before recursing.
auto hit_path = path.insert(chr);
if (!hit_path.second) {
// Path already contains this character: Cycle detected!
return res;
}
// Iterate all possible character inverse translations and combine them with the remainder string inverse translations.
for (auto d = hit_trans->second.cbegin(), d_end = hit_trans->second.cend(); d != d_end; ++d) {
auto res_chr = d->second.norm.empty() ?
translate_inv(db_trans, db_np, d->first.c_str(), path) :
permutate_and_translate_inv(db_trans, db_np, d->first.c_str(), d->second.norm.c_str(), path);
if (!res_chr.empty()) {
for (auto r_chr = res_chr.cbegin(), r_chr_end = res_chr.cend(); r_chr != r_chr_end; ++r_chr) {
for (auto r_rem = res_rem.cbegin(), r_rem_end = res_rem.cend(); r_rem != r_rem_end; ++r_rem)
res.insert(ZRCola::DBSource::charseq(d->second.rank_src + r_chr->rank + r_rem->rank, r_chr->str + r_rem->str));
}
} else {
// Cycle detected. Do not continue inverse translation.
for (auto r_rem = res_rem.cbegin(), r_end = res_rem.cend(); r_rem != r_end; ++r_rem)
res.insert(ZRCola::DBSource::charseq(r_rem->rank, chr + r_rem->str));
}
}
// Remove the current character from the path.
path.erase(hit_path.first);
} else {
// First character is non-inverse translatable. Combine it with the remainder(s).
for (auto r_rem = res_rem.cbegin(), r_end = res_rem.cend(); r_rem != r_end; ++r_rem)
res.insert(ZRCola::DBSource::charseq(r_rem->rank, chr + r_rem->str));
}
return res;
}
static inline set permutate_and_translate_inv(_In_ const translation_db &db_trans, _In_ const normperm_db &db_np, _In_z_ const wchar_t *str, _In_z_ const char *norm, _Inout_ set &path)
{
// Primary permutation inverse translate.
auto res = translate_inv(db_trans, db_np, str, path);
// Secondary permutation(s).
auto const hit_np = db_np.find(norm);
if (hit_np != db_np.end()) {
for (auto perm = hit_np->second.cbegin(), perm_end = hit_np->second.cend(); perm != perm_end; ++perm) {
// Prepare permutated string.
translation_db::mapped_type::key_type str_perm;
for (auto idx = perm->cbegin(), idx_end = perm->cend(); idx != idx_end; ++idx)
str_perm += str[*idx];
// Secondary permutation inverse translate.
auto res_perm = translate_inv(db_trans, db_np, str_perm.c_str(), path);
for (auto r = res_perm.cbegin(), r_end = res_perm.cend(); r != r_end; ++r)
res.insert(ZRCola::DBSource::charseq(r->rank + 1, r->str));
}
}
return res;
}
///
/// Main function
///
int _tmain(int argc, _TCHAR *argv[])
{
wxApp::CheckBuildOptions(WX_BUILD_OPTIONS_SIGNATURE, "program");
// Initialize wxWidgets.
wxInitializer initializer;
if (!initializer) {
_ftprintf(stderr, wxT("Failed to initialize the wxWidgets library, aborting.\n"));
return -1;
}
// Initialize configuration.
wxConfigBase *cfgPrev = wxConfigBase::Set(new wxConfig(wxT(PRODUCT_CFG_APPLICATION), wxT(PRODUCT_CFG_VENDOR)));
if (cfgPrev) wxDELETE(cfgPrev);
// Initialize locale.
wxLocale locale;
if (wxInitializeLocale(locale))
wxVERIFY(locale.AddCatalog(wxT("ZRColaCompile")));
// Parse command line.
static const wxCmdLineEntryDesc cmdLineDesc[] =
{
{ wxCMD_LINE_SWITCH, "h" , "help", _("Show this help message"), wxCMD_LINE_VAL_NONE , wxCMD_LINE_OPTION_HELP },
{ wxCMD_LINE_PARAM , NULL, NULL , _("" ), wxCMD_LINE_VAL_STRING, wxCMD_LINE_OPTION_MANDATORY },
{ wxCMD_LINE_PARAM , NULL, NULL , _("