case 221:
if(ch == '\r') goto s260;
if(ch == ' ' || ch == '\t') break;
- case 230: s230: LOOPCHECK m_state0 = 230;
+ case 230: LOOPCHECK m_state0 = 230;
if(ch == '"') goto s240;
if(ch == '\r') goto s260;
if(isCTL(ch)) goto sError;
case 240: s240: LOOPCHECK m_state0 = 241;
break;
case 241:
- if(ch == '"') goto s230;
+ if(ch == '"') m_state0 = 230; break;
if(ch == '\\') goto s250;
if(isCTL(ch)) goto sError;
m_request->m_attributes.last().second.append(ch);
#include "debug.h"
-#define TM_VERSION 1
+#define TM_VERSION 2
+#define TM_DATABASE_VERSION 1
int main(int argc, char *argv[])
{
Settings settings(path, QSettings::IniFormat);
QString app_path = QCoreApplication::applicationDirPath();
+ bool debug = false;
+#ifdef QT_DEBUG
+ debug = true;
+#endif
+
+ bool remove = false;
+
// 設定ファイルのバージョンと違う場合、必要に応じてファイルや設定を消去する。
if(settings.value("Main/version", 0) != TM_VERSION)
{
- //qDebug() << settings.;
+ settings.setValue("Main/version", TM_VERSION);
+ remove = true;
}
+ // DEBUG/RELEASEの違いで、必要に応じてファイルや設定を消去する。
+ if(debug != settings.value("Main/debug", false).toBool()) remove = true;
+ settings.setValue("Main/debug", debug);
// Language
settings.setValue(
"Japanese/mecabdic", "C:\\Users\\Kouichi\\Documents\\OSDN\\wordring-tm\\third-party\\lib\\ipadic");
#else
-// if(!settings.contains("Japanese/mecabdic"))
+ if(remove) settings.remove("Japanese/mecabdic");
+ if(!settings.contains("Japanese/mecabdic"))
{
QString mecabdic = app_path + "/ipadic";
QDir dir(mecabdic);
#endif
// HttpServer
- if(!settings.contains("HttpServer/port")) settings.setValue("HttpServer/port", 8080);
+ if(remove) settings.remove("HttpServer/port");
+ if(!settings.contains("HttpServer/port"))
+ settings.setValue("HttpServer/port", 82);
+
// SocketServer
- if(!settings.contains("SocketServer/port")) settings.setValue("SocketServer/port", 8081);
+ if(remove) settings.remove("SocketServer/port");
+ if(!settings.contains("SocketServer/port"))
+ settings.setValue("SocketServer/port", 83);
+
// ProxyModule
- //if(!settings.contains("ProxyModule/prefix"))
+ if(remove) settings.remove("ProxyModule/prefix");
+ if(!settings.contains("ProxyModule/prefix"))
settings.setValue("ProxyModule/prefix", "/?u=");
+
// ProxyHandler
#ifdef QT_DEBUG
settings.setValue("ProxyHandler/js_file", QString(PROXYSRCDIR) + "/tm-default.js");
settings.setValue("ProxyHandler/css_file", QString(PROXYSRCDIR) + "/tm-default.css");
#else
-// if(!settings.contains("ProxyHandler/js_file"))
+ if(remove) settings.remove("ProxyHandler/js_file");
+ if(!settings.contains("ProxyHandler/js_file"))
settings.setValue("ProxyHandler/js_file", app_path + "/tm-default.js");
-// if(!settings.contains("ProxyHandler/css_file"))
+ if(remove) settings.remove("ProxyHandler/css_file");
+ if(!settings.contains("ProxyHandler/css_file"))
settings.setValue("ProxyHandler/css_file", app_path + "/tm-default.css");
#endif
// DefaultHtmlModule
#ifdef QT_DEBUG
settings.setValue("DefaultHtmlModule/file", QString(PROXYSRCDIR) + "/index.html");
#else
-// if(!settings.contains("DefaultHtmlModule/file"))
+ if(remove) settings.remove("DefaultHtmlModule/file");
+ if(!settings.contains("DefaultHtmlModule/file"))
settings.setValue("DefaultHtmlModule/file", app_path + "/index.html");
#endif
// Database
+ if(remove) settings.remove("Database/root");
if(!settings.contains("Database/root"))
{
QString path = QStandardPaths::writableLocation(QStandardPaths::CacheLocation);
+ path += "/";
+ path += QString::number(TM_DATABASE_VERSION);
settings.setValue("Database/root", path);
}
#ifdef QT_DEBUG
settings.setValue("WordringConnection/url", "ws://wordring.net/ws");
#else
+ if(remove) settings.remove("WordringConnection/url");
if(!settings.contains("WordringConnection/url"))
settings.setValue("WordringConnection/url", "ws://wordring.net/ws");
#endif
HttpServer *server = new HttpServer(&settings, &w);
w.set_http_port(server->port());
- TM::SocketServer *socket = new TM::SocketServer(&settings, service, w.editor_widget(), &w);
+ TM::SocketServer *socket = new TM::SocketServer(&settings, service, &w);
server->install(new TM::DefaultHtmlModule(&settings, server->port(), server));
server->install(new TM::ProxyModule(
#include "mainwindow.h"
#include "tmeditorwidget.h"
#include "tmcandidatewidget.h"
+#include <tmservice.h>
#include "settings.h"
// 設定の復帰
restoreGeometry(m_settings->value("MainWindow/geometry").toByteArray());
restoreState(m_settings->value("MainWindow/state").toByteArray());
+
+ service->set_editor_widget(m_editor_widget);
+ service->set_candidate_widget(m_candidate_widget);
}
MainWindow::~MainWindow()
TM::EditorWidget* MainWindow::editor_widget() { return m_editor_widget; }
+TM::CandidateWidget* MainWindow::candidate_widget() { return m_candidate_widget; }
+
void MainWindow::set_http_port(quint16 http_port)
{
editor_widget()->set_http_port(http_port);
~MainWindow();
TM::EditorWidget* editor_widget();
+ TM::CandidateWidget* candidate_widget();
+
void set_http_port(quint16 http_port);
signals:
<file alias="link.png">image/insert-link.png</file>
<file alias="setting.png">image/document-properties.png</file>
<file alias="browser.png">image/web-browser.png</file>
+ <file alias="search.png">image/edit-find.png</file>
</qresource>
</RCC>
-#ifndef TMCANDIDATE_H
+#ifndef TMCANDIDATE_H
#define TMCANDIDATE_H
-class tmcandidate
+namespace TM
{
-public:
- tmcandidate();
-signals:
+/*!
+ * \brief 候補文のためのデータ構造です。
+ */
+class candidate_data_type_
+{
-public slots:
};
+
+} // namespace TM
+
#endif // TMCANDIDATE_H
-
+#include "tmcandidatewidget.h"
#include "settings.h"
#include "tmservice.h"
-#include "tmcandidatewidget.h"
-
+#include "tmsocket.h"
+#include <QToolBar>
+#include <QAction>
+#include <QListWidget>
#include <QBoxLayout>
#include <QLabel>
TM::CandidateWidget::CandidateWidget(Settings *settings, Service *service, QWidget *parent)
- : QListWidget(parent)
+ : QWidget(parent)
+ , m_service(service)
+ , m_settings(settings)
+ , m_connection(nullptr)
{
- addItem("<b>test\r\n</b>\r\na\r\n\r\na\r\n\r\na\r\n\r\na\r\n\r\na\r");
- addItem("test\r\n\r\na\r\n\r\na\r\n\r\na\r\n\r\na\r\n\r\na\r");
- addItem("test\r\n\r\na\r\n\r\na\r\n\r\na\r\n\r\na\r\n\r\na\r");
- addItem("test\r\n\r\na\r\n\r\na\r\n\r\na\r\n\r\na\r\n\r\na\r");
- addItem("test\r\n\r\na\r\n\r\na\r\n\r\na\r\n\r\na\r\n\r\na\r");
- /*
- setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
- setWidgetResizable(false);
- //new CandidateArea(this);
QVBoxLayout *vlayout = new QVBoxLayout(this);
vlayout->setSpacing(4);
vlayout->setContentsMargins(0, 0, 0, 0);
- vlayout->setSizeConstraint(QLayout::SetMinAndMaxSize);
-
- QLabel *l = new QLabel("test\r\n\r\na\r\n\r\na\r\n\r\na\r\n\r\na\r\n\r\na\r\n\r\na\r\n\r\na", this);
- l->setAlignment(Qt::AlignLeft | Qt::AlignTop);
- l->setFrameStyle(QFrame::Panel);
- //l->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding));
- vlayout->addWidget(l);
- QLabel *l2 = new QLabel("<b><a href=\"#\">test</a></b>", this);
- l2->setAlignment(Qt::AlignLeft | Qt::AlignTop);
- vlayout->addWidget(l2);
- */
+
+ m_toolbar = new QToolBar("candidate", this);
+
+ m_search = new QAction(QIcon(":/search.png"), "search", this);
+ m_toolbar->addAction(m_search);
+
+ m_list_widget = new QListWidget(this);
+ m_list_widget->setWordWrap(true);
+
+ vlayout->addWidget(m_toolbar);
+ vlayout->addWidget(m_list_widget);
+
+ connect(m_search, SIGNAL(triggered(bool)), this, SLOT(onSearchTriggered(bool)));
}
+void TM::CandidateWidget::attach(SocketConnection *connection)
+{
+ if(m_connection == connection) return;
+ if(m_connection) detach(connection);
-TM::CandidateArea::CandidateArea(QWidget *parent)
- : QWidget(parent)
+
+ m_connection = connection;
+}
+
+void TM::CandidateWidget::detach(SocketConnection *connection)
{
+ if(m_connection != connection) return;
+
+ clear();
+ m_connection = nullptr;
}
+
+void TM::CandidateWidget::clear() { m_list_widget->clear(); }
+
+void TM::CandidateWidget::append(QString text)
+{
+ qDebug() << text;
+ QListWidgetItem *wi = new QListWidgetItem(m_list_widget);
+ wi->setText(text);
+ //QLabel *l = new QLabel(m_list_widget);
+ //l->setTextFormat(Qt::RichText);
+ //l->setWordWrap(true);
+ //l->setText(text);
+ m_list_widget->addItem(wi);
+ //m_list_widget->setItemWidget(wi, l);
+}
+
+void TM::CandidateWidget::onSearchTriggered(bool)
+{
+ clear();
+ if(!m_connection) return;
+ TextSentence::pointer sentence =
+ m_connection->segment_list()->current_sentence();
+ if(!sentence) return;
+
+ m_service->find_candidates(sentence);
+}
+
+
+
+
+
+
#define TMCANDIDATEWIDGET_H
#include <QWidget>
-#include <QScrollArea>
-#include <QListWidget>
+#include <QString>
+
+QT_BEGIN_NAMESPACE
+class QToolBar;
+class QAction;
+class QListWidget;
+QT_END_NAMESPACE
class Settings;
{
class Service;
+class SocketConnection;
-class CandidateWidget : public QListWidget
+class CandidateWidget : public QWidget
{
Q_OBJECT
public:
CandidateWidget(Settings *settings, Service *service, QWidget *parent = 0);
+ void attach(SocketConnection *connection);
+ void detach(SocketConnection *connection);
+
+ void clear();
+ void append(QString text);
signals:
public slots:
+ void onSearchTriggered(bool);
private:
- ;
-};
+ Settings *m_settings;
+ Service *m_service;
-class CandidateArea : public QWidget
-{
- Q_OBJECT
-public:
- CandidateArea(QWidget *parent);
+ SocketConnection *m_connection;
+
+ QToolBar *m_toolbar;
+ QAction *m_search;
+
+ QListWidget *m_list_widget;
};
} // namespace TM
#include "tmdatabase.h"
#include "tmservice.h"
+#include "dtl/dtl.hpp"
+
#include <QSqlQuery>
#include <QSqlError>
#include <QDir>
#include <QDateTime>
#include <QDataStream>
+#include <QtAlgorithms>
+
+#include <string>
+
#include "debug.h"
// DatabaseBase ---------------------------------------------------------------
m_find_sentence_by_crc = prepare("SELECT * FROM sentences WHERE crc=?;", Q_FUNC_INFO);
m_find_sentence_by_source_id = prepare("SELECT * FROM sentences WHERE source_id=?;", Q_FUNC_INFO);
+ m_find_sentence = prepare("SELECT * FROM sentences WHERE sentence_id=?;", Q_FUNC_INFO);
m_insert_sentence = prepare(
"INSERT OR REPLACE INTO sentences("
"source_id, sentence, json, crc, previous_crc, next_crc, user_id, time) "
return m_find_sentence_id_with_context.value(0).toUInt();
}
+TM::sentence_data_type TM::SentenceDatabase::find_sentence(quint32 sentence_id)
+{
+ assert(sentence_id);
+ m_find_sentence.bindValue(0, sentence_id);
+ exec(m_find_sentence, Q_FUNC_INFO);
+ return stuff_value(&m_find_sentence);
+}
+
TM::sentence_data_type
TM::SentenceDatabase::find_sentence_by_source_id(int source_id)
{
assert(source_id);
- sentence_data_type result;
-
m_find_sentence_by_source_id.bindValue(0, source_id);
exec(m_find_sentence_by_source_id, Q_FUNC_INFO);
- if(!m_find_sentence_by_source_id.next()) return result;
-
return stuff_value(&m_find_sentence_by_source_id);
}
TM::sentence_data_type
TM::SentenceDatabase::stuff_value(QSqlQuery *query)
{
- assert(query->isValid());
sentence_data_type result;
+ if(!query->next()) return result;
+
result.sentence_id = query->value(0).toUInt();
result.source_id = query->value(1).toUInt();
result.sentence = query->value(2).toString();
{
}
-TM::index_data_type::pointer TM::index_data_type::create()
-{
- return pointer(new index_data_type());
-}
-
TM::IndexDatabase::IndexDatabase(Settings *settings, QString dbname)
{
m_index_limit = 20 * 1024;
// テーブル作成。
exec("CREATE TABLE IF NOT EXISTS indexes("
"index_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,"
- "word_id INTEGER UNIQUE NOT NULL,"
- "sentence_ids BLOB,"
- "is_stop_word BOOLEAN);", Q_FUNC_INFO);
+ "word_id INTEGER NOT NULL,"
+ "source_id INTEGER NOT NULL,"
+ "UNIQUE(word_id, source_id));", Q_FUNC_INFO);
exec("CREATE INDEX IF NOT EXISTS word_id_index ON indexes(word_id);", Q_FUNC_INFO);
+ exec("CREATE INDEX IF NOT EXISTS source_id_index ON indexes(source_id);", Q_FUNC_INFO);
// クエリ作成。
m_find_index = prepare(
- "SELECT * FROM indexes WHERE word_id=?;", Q_FUNC_INFO);
+ "SELECT source_id FROM indexes WHERE word_id=?;", Q_FUNC_INFO);
m_insert_index = prepare(
- "INSERT INTO indexes(word_id, sentence_ids, is_stop_word) "
- "VALUES(?, ?, ?);", Q_FUNC_INFO);
+ "INSERT OR REPLACE INTO indexes(word_id, source_id) VALUES(?, ?);", Q_FUNC_INFO);
m_update_index = prepare(
- "UPDATE indexes SET "
- "word_id=?,"
- "sentence_ids=?,"
- "is_stop_word=? "
- "WHERE index_id=?;", Q_FUNC_INFO);
+ "UPDATE indexes SET word_id=?, source_id=? "
+ "WHERE word_id=? AND source_id=?;", Q_FUNC_INFO);
+ m_remove_index = prepare(
+ "DELETE FROM indexes WHERE word_id=? AND source_id=?;", Q_FUNC_INFO);
}
-TM::index_data_type::pointer TM::IndexDatabase::find_index(quint32 word_id)
+QSet<quint32> TM::IndexDatabase::find_index(quint32 word_id)
{
assert(word_id);
m_find_index.bindValue(0, word_id);
exec(m_find_index, Q_FUNC_INFO);
- index_data_type::pointer result;
-
- if(m_find_index.next())
+ QSet<quint32> result;
+ while(m_find_index.next())
{
- result = index_data_type::create();
- result->index_id = m_find_index.value(0).toUInt();
- result->word_id = m_find_index.value(1).toUInt();
- QByteArray sentence_ids = m_find_index.value(2).toByteArray();
- QDataStream ds(sentence_ids);
- while(!ds.atEnd())
- {
- quint32 sentence_id;
- ds >> sentence_id;
- result->sentence_ids.insert(sentence_id);
- }
- result->is_stop_word = m_find_index.value(3).toBool();
+ quint32 source_id = m_find_index.value(0).toUInt();
+ result.insert(source_id);
}
-
return result;
}
-void TM::IndexDatabase::insert_index(WordDatabase::pointer word_database,
- sentence_data_type const &sdata, quint32 target_id)
+void TM::IndexDatabase::insert_index(quint32 word_id, quint32 source_id)
{
- for(QString word : sdata.words)
- {
- if(word.size() <= 2) continue;
-
- int word_id = 0;
- word_id = word_database->word_id(word);
- assert(word_id);
-
- index_data_type::pointer index_data = find_index(word_id);
- if(index_data)
- {
- index_data->sentence_ids.insert(target_id);
- // レコードが大きくなりすぎた場合、ストップワードとする。
- if(m_index_limit < index_data->sentence_ids.size())
- {
- index_data->sentence_ids.clear();
- index_data->is_stop_word = true;
- }
- update_index(index_data);
- }
- else
- {
- index_data = index_data_type::create();
- index_data->word_id = word_id;
- index_data->sentence_ids.insert(target_id);
- index_data->is_stop_word = false;
- insert_index(index_data);
- }
- }
-}
-
-void TM::IndexDatabase::insert_index(index_data_type::pointer index_data)
-{
- assert(index_data);
-
- QByteArray sentence_ids;
- QDataStream ds(&sentence_ids, QIODevice::WriteOnly);
- for(quint32 sentence_id : index_data->sentence_ids) ds << sentence_id;
+ assert(word_id);
+ assert(source_id);
- m_insert_index.bindValue(0, index_data->word_id);
- m_insert_index.bindValue(1, sentence_ids);
- m_insert_index.bindValue(2, index_data->is_stop_word);
+ m_insert_index.bindValue(0, word_id);
+ m_insert_index.bindValue(1, source_id);
exec(m_insert_index, Q_FUNC_INFO);
}
-void TM::IndexDatabase::update_index(index_data_type::pointer index_data)
+void TM::IndexDatabase::remove_index(quint32 word_id, quint32 source_id)
{
- assert(index_data);
+ assert(word_id);
+ assert(source_id);
+
+ qDebug() << "word" << word_id << "source" << source_id;
+ qDebug() << "before remove" << find_index(word_id);
- QByteArray sentence_ids;
- QDataStream ds(&sentence_ids, QIODevice::WriteOnly);
- for(quint32 sentence_id : index_data->sentence_ids) ds << sentence_id;
+ m_remove_index.bindValue(0, word_id);
+ m_remove_index.bindValue(1, source_id);
+ exec(m_remove_index, Q_FUNC_INFO);
- m_update_index.bindValue(0, index_data->word_id);
- m_update_index.bindValue(1, sentence_ids);
- m_update_index.bindValue(2, index_data->is_stop_word);
- m_update_index.bindValue(3, index_data->index_id);
+ qDebug() << "after remove" << find_index(word_id);
- exec(m_update_index, Q_FUNC_INFO);
}
TM::IndexDatabase::pointer TM::IndexDatabase::create(Settings *settings, QString dbname)
// 単語とセンテンスの索引を付ける。
// 単語の登録。
- IndexDatabase::pointer index_database =
+ IndexDatabase::pointer idb =
find_index_database(site_id, target.scode, target.tcode);
- assert(index_database);
- WordDatabase::pointer word_database = find_word_database(target.scode);
- assert(word_database);
- index_database->insert_index(word_database, source, target_id);
+ assert(idb);
+ WordDatabase::pointer wdb = find_word_database(target.scode);
+ assert(wdb);
+ for(QString word : source.words)
+ {
+ quint32 word_id = wdb->word_id(word);
+ idb->insert_index(word_id, source_id);
+ }
}
/*!
SentenceDatabase::pointer tdb =
find_sentence_database(site_id, sdata.tcode);
assert(tdb);
-
tdb->remove(source_id);
+
+ // 索引の消去。
+ IndexDatabase::pointer idb = find_index_database(site_id, sdata.scode, sdata.tcode);
+ assert(idb);
+ WordDatabase::pointer wdb = find_word_database(sdata.scode);
+ assert(wdb);
+ for(QString word : sdata.words)
+ {
+ quint32 word_id = wdb->word_id(word);
+ idb->remove_index(word_id, source_id);
+ }
+}
+
+void TM::Database::find_candidates(sentence_data_type source,
+ TextSentence::weak_pointer token)
+{
+ quint32 site_id = find_site_id(source.site);
+ WordDatabase::pointer wdb = find_word_database(source.scode);
+ IndexDatabase::pointer idb =
+ find_index_database(site_id, source.scode, source.tcode);
+
+ QMap<quint32, int> source_ids_map; // 原文ID、回数。
+ for(QString word : source.words)
+ {
+ if(word.size() < 2) continue;
+
+ quint32 word_id = wdb->word_id(word);
+ QSet<quint32> ids = idb->find_index(word_id);
+ if(ids.isEmpty()) continue;
+
+ for(quint32 source_id : ids) source_ids_map[source_id]++;
+
+ if(10000 < source_ids_map.size()) break; //件数制限。
+ }
+
+ QList<QPair<quint32, int>> source_ids; // 原文ID、回数。
+ // コピー。
+ QMap<quint32, int>::const_iterator it1 = source_ids_map.begin();
+ QMap<quint32, int>::const_iterator it2 = source_ids_map.end();
+ while(it1 != it2)
+ {
+ source_ids.append(qMakePair(it1.key(), it1.value()));
+ ++it1;
+ }
+ source_ids_map.clear();
+ // matchする単語数が多い順にソート。
+ qSort(source_ids.begin(), source_ids.end(),
+ [](QPair<quint32,int> const &lhs, QPair<quint32,int> const &rhs)->bool{
+ return lhs.second > rhs.second;
+ });
+ // 件数削減。
+ if(100 < source_ids.size())
+ source_ids.erase(source_ids.begin() + 100, source_ids.end());
+
+ // 編集距離。
+ QList<candidate_data_type> candidates;
+ SentenceDatabase::pointer sdb = find_sentence_database(site_id, source.scode);
+ for(QPair<quint32,int> ¤t : source_ids)
+ {
+ candidate_data_type candidate;
+
+ quint32 source_id = current.first;
+ candidate.source = sdb->find_sentence(source_id);
+
+ std::wstring A = source.sentence.toStdWString();
+ std::wstring B = candidate.source.sentence.toStdWString();
+
+ dtl::Diff<wchar_t,std::wstring> d(A, B);
+ d.compose();
+ candidate.edit_distance = d.getEditDistance();
+
+ candidates.append(candidate);
+ }
+ // 編集距離の小さい順にソート。
+ qSort(candidates.begin(), candidates.end(),
+ [](candidate_data_type const &lhs, candidate_data_type const &rhs)->bool{
+ return lhs.edit_distance < rhs.edit_distance;
+ });
+ // 件数削減。
+ if(10 < candidates.size())
+ candidates.erase(candidates.begin() + 10, candidates.end());
+
+ // 訳文付与。
+ SentenceDatabase::pointer tdb = find_sentence_database(site_id, source.tcode);
+ assert(tdb);
+ for(candidate_data_type &c : candidates)
+ {
+ quint32 source_id = c.source.sentence_id;
+ assert(source_id);
+ c.target = tdb->find_sentence_by_source_id(source_id);
+ }
+
+ // サービスに返答。
+ for(candidate_data_type &c : candidates)
+ {
+ QMetaObject::invokeMethod(
+ m_service, "candidate_found",
+ Qt::QueuedConnection,
+ Q_ARG(candidate_data_type, c),
+ Q_ARG(TextSentence::weak_pointer, token));
+ }
+
}
QString TM::Database::find_language_name(int code) const
// データベース名を作成する。
QString dbname, sname, tname, site_name;
- sname = QLocale::languageToString(static_cast<QLocale::Language>(scode));
+ sname = QLocale::languageToString(static_cast<QLocale::Language>(scode)).toLower();
assert(!sname.isEmpty());
if(!sname.isEmpty())
{
- tname = find_language_name(tcode).toLower();
+ tname = QLocale::languageToString(static_cast<QLocale::Language>(tcode)).toLower();
assert(!tname.isEmpty());
}
if(!tname.isEmpty())
bool find_sentence_by_crc(
quint32 crc, QString *tsentence, QJsonArray *json = nullptr);
+ sentence_data_type find_sentence(quint32 sentence_id);
sentence_data_type find_sentence_by_source_id(int source_id);
void insert(sentence_data_type const &sentence_data);
struct index_data_type
{
- typedef std::shared_ptr<index_data_type> pointer;
-
index_data_type();
- static pointer create();
-
quint32 index_id;
- quint32 word_id;
- QSet<quint32> sentence_ids;
+ quint32 word_id; /*!< 単語ID */
+ QSet<quint32> source_ids; /*!< 原文IDの集合 */
bool is_stop_word;
};
IndexDatabase(Settings *settings, QString dbname);
public:
- index_data_type::pointer find_index(quint32 word_id);
- void insert_index(WordDatabase::pointer word_database,
- const sentence_data_type &sdata, quint32 target_id);
-private:
- void insert_index(index_data_type::pointer index_data);
- void update_index(index_data_type::pointer index_data);
+ QSet<quint32> find_index(quint32 word_id);
+ void insert_index(quint32 word_id, quint32 source_id);
+ void remove_index(quint32 word_id, quint32 source_id);
+
public:
static pointer create(Settings *settings, QString dbname);
QSqlQuery m_find_index;
QSqlQuery m_insert_index;
QSqlQuery m_update_index;
+ QSqlQuery m_remove_index;
int m_index_limit;
};
void remove_sentence(sentence_data_type sdata,
sentence_data_type tdata);
- //void insert_index();
+ void find_candidates(sentence_data_type source,
+ TextSentence::weak_pointer token);
private:
QString find_language_name(int code) const;
TM::EditorWidget::EditorWidget(Settings *settings, Service *service, QWidget *parent)
: QWidget(parent)
- , m_mutex(QMutex::Recursive)
, m_service(service)
, m_settings(settings)
, m_http_port(80)
if(m_socket == socket) return;
if(m_socket) detach(m_socket);
- QMutexLocker lock(&m_mutex);
m_socket = socket;
- connect(this, SIGNAL(editModeChanged(bool)), m_socket, SLOT(changeEditMode(bool)));
+// connect(this, SIGNAL(editModeChanged(bool)), m_socket, SLOT(changeEditMode(bool)));
set_link_mode_disabled(true);
connect(this, SIGNAL(sourceLanguageChanged()), m_socket, SLOT(changeLanguage()));
connect(this, SIGNAL(targetLanguageChanged()), m_socket, SLOT(changeLanguage()));
* \brief ブラウザ上でドキュメントがフォーカスを取得することで、
* 現在のSocketConnectionがフォーカスを失った時に呼び出されます。
*/
-void TM::EditorWidget::detach(SocketConnection *)
+void TM::EditorWidget::detach(SocketConnection *connection)
{
- QMutexLocker lock(&m_mutex);
- disconnect(this, SIGNAL(editModeChanged(bool)), m_socket, SLOT(changeEditMode(bool)));
+ if(m_socket != connection) return;
+
+// disconnect(this, SIGNAL(editModeChanged(bool)), m_socket, SLOT(changeEditMode(bool)));
disconnect(this, SIGNAL(sourceLanguageChanged()), m_socket, SLOT(changeLanguage()));
disconnect(this, SIGNAL(targetLanguageChanged()), m_socket, SLOT(changeLanguage()));
void TM::EditorWidget::set_http_port(quint16 http_port) { m_http_port = http_port; }
/*!
- * \brief 編集モードを変更します。
- */
-void TM::EditorWidget::set_edit_mode(bool mode)
-{
- QMutexLocker lock(&m_mutex);
- bool old_mode = m_edit_mode->isChecked();
- if(old_mode == mode) return;
-
- m_edit_mode->setChecked(mode);
-}
-
-/*!
* \brief 編集モードの場合trueを返します。
*/
bool TM::EditorWidget::edit_mode()
{
- QMutexLocker lock(&m_mutex);
return m_edit_mode->isChecked();
}
*/
void TM::EditorWidget::set_link_mode(bool mode)
{
- QMutexLocker lock(&m_mutex);
bool old_mode = m_link->isChecked();
if(old_mode == mode) return;
*/
bool TM::EditorWidget::link_mode()
{
- QMutexLocker lock(&m_mutex);
return m_link->isChecked();
}
*/
void TM::EditorWidget::set_link_mode_disabled(bool disable)
{
- QMutexLocker lock(&m_mutex);
m_link->setDisabled(disable);
}
*/
int TM::EditorWidget::source_language()
{
- QMutexLocker lock(&m_mutex);
return m_slang->data().toInt();
}
*/
int TM::EditorWidget::target_language()
{
- QMutexLocker lock(&m_mutex);
return m_tlang->data().toInt();
}
void TM::EditorWidget::set_segment(TextSegment::pointer segment)
{
- QMutexLocker lock(&m_mutex);
m_edit->set_segment(segment);
}
m_socket->remove_sentence(text_sentence);
}
+void TM::EditorWidget::do_panel_entered(SourcePanel *panel)
+{
+ //assert(m_socket);
+ if(!m_socket) return;
+
+ m_socket->segment_list()->set_current_sentence(panel->text_sentence());
+}
+
+void TM::EditorWidget::do_panel_leaved(SourcePanel *)
+{
+ assert(m_socket);
+ if(!m_socket) return;
+
+ m_socket->segment_list()->set_current_sentence(TextSentence::pointer());
+}
+
/*!
* \brief 言語プラグインが読み込まれるたびに呼び出されます。
* \param code 言語コード。
m_link->setDisabled(true);
//m_slang->setDisabled(!checked);
//m_tlang->setDisabled(!checked);
- emit editModeChanged(checked);
+ //emit editModeChanged(checked);
+
+ m_service->change_edit_mode(checked);
+ if(!checked) m_edit->clear();
m_edit->set_edit_mode(checked);
}
assert(tp);
tp->show();
if(can_link_mode()) parent_editor_widget()->set_link_mode_disabled(false);
+
+ // 編集ウィジェットに通知。
+ parent_editor_widget()->do_panel_entered(panel);
}
/*!
parent_editor_widget()->save_sentence(text_sentence);
tp->set_text_saved(true);
}
+ // 編集ウィジェットに通知。
+ parent_editor_widget()->do_panel_leaved(panel);
}
/*!
#include <QMutex>
QT_BEGIN_NAMESPACE
-class QSettings;
class QToolBar;
class QAction;
class Service;
//class SocketConnection;
class Editor;
+class SourcePanel;
class TargetPanel;
class EditorWidget : public QWidget
EditorWidget(Settings *settings, Service *service, QWidget *parent = 0);
void attach(SocketConnection *socket);
- void detach(SocketConnection *socket);
+ void detach(SocketConnection *connection);
void set_http_port(quint16 http_port);
- void set_edit_mode(bool mode);
bool edit_mode();
void set_link_mode(bool mode);
bool link_mode();
void save_sentence(TextSentence::pointer text_sentence);
void remove_sentence(TextSentence::pointer text_sentence);
+ void do_panel_entered(SourcePanel *panel);
+ void do_panel_leaved(SourcePanel *panel);
+
signals:
- void editModeChanged(bool mode_);
+ //void editModeChanged(bool mode_);
void linkModeChanged(bool mode_);
void sourceLanguageChanged();
void targetLanguageChanged();
void onBrowserTriggered(bool);
private:
- QMutex m_mutex;
Service *m_service;
Settings *m_settings;
quint16 m_http_port;
QColor color(int index) const;
+signals:
+ void panelEntered();
+ void panelLeaved();
+
private:
Editor *m_parent_editor;
};
#include "tmsocket.h"
+#include "tmeditorwidget.h"
+#include "tmcandidatewidget.h"
+
#include <QMessageBox>
#include <QDir>
TM::Service::Service(Settings *settings, QObject *parent)
: QObject(parent)
, m_settings(settings)
+ , m_editor_widget(nullptr)
+ , m_candidate_widget(nullptr)
, m_database_thread(new QThread(this))
, m_database(new Database(settings, this))
, m_wordring(new WordringConnection(settings, this))
+ , m_current_connection(nullptr)
{
qRegisterMetaType<sentence_data_type>();
+ qRegisterMetaType<candidate_data_type>();
qRegisterMetaType<TextSentence::weak_pointer>();
setup_crc_table();
m_database_thread->wait();
}
+TM::EditorWidget* TM::Service::editor_widget() { return m_editor_widget; }
+
+void TM::Service::set_editor_widget(EditorWidget *editor) { m_editor_widget = editor; }
+
+void TM::Service::change_edit_mode(bool mode)
+{
+ for(SocketConnection *connection : m_connections)
+ {
+ connection->set_edit_mode(mode);
+ }
+}
+
+TM::CandidateWidget* TM::Service::candidate_widget() { return m_candidate_widget; }
+
+void TM::Service::set_candidate_widget(CandidateWidget *candidate) { m_candidate_widget = candidate; }
+
void TM::Service::attach(SocketConnection *connection)
{
m_connections.insert(connection);
m_connections.remove(connection);
}
+void TM::Service::set_current_connection(SocketConnection *connection)
+{
+ m_current_connection = connection;
+}
+
void TM::Service::setup_crc_table()
{
for (quint32 i = 0; i < 256; i++)
}
}
+Language* TM::Service::find_language(int code)
+{
+ assert(m_languages.contains(code));
+ return m_languages[code];
+}
+
/*!
* \brief 引数として与えられたstringを文に分割します。
* \param code 分割に使用する言語コード。
/*!
* \brief 原文から訳文を検索します。
- * \param site_id 翻訳対象サイトを表すID。
- * \param scode 原文の言語コード。
- * \param tcode 訳文の言語コード。
* \param sentence 原文。
*/
void TM::Service::find_sentence(TextSentence::pointer sentence)
}
/*!
+ * \brief 原文から訳文候補を検索します。
+ * \param sentence 原文。
+ */
+void TM::Service::find_candidates(TextSentence::pointer sentence)
+{
+ // 現在の候補をクリア。
+ sentence->clear_candidates();
+
+ sentence_data_type sdata = sentence->ssentence_data();
+ // 原文を正規化。
+ sdata.sentence = normalize(sdata.scode, sdata.sentence);
+
+ // データベース呼び出し。
+ QMetaObject::invokeMethod(
+ m_database, "find_candidates",
+ Qt::QueuedConnection,
+ Q_ARG(sentence_data_type, sdata),
+ Q_ARG(TextSentence::weak_pointer, sentence));
+}
+
+/*!
* \brief 原文と訳文の対をデータベースとサーバへ登録します。
* \param sentence 挿入する原文、訳文の対。
*/
sentence->segment_list()->connection()->sentence_inserted(sentence);
}
+void TM::Service::candidate_found(candidate_data_type candidate,
+ TextSentence::weak_pointer token)
+{
+ if(token.expired()) return;
+
+ TextSentence::pointer sentence = token.lock();
+ bool ret = sentence->append_candidate(candidate);
+
+ if(m_current_connection != sentence->segment_list()->connection())
+ return;
+
+ if(ret)
+ {
+ QString s = candidate.source.sentence;
+ s += "\r\n";
+ s += candidate.target.sentence;
+ s += "\r\n";
+ m_candidate_widget->append(s);
+ }
+}
+
/*!
* \brief サーバからの検索結果通知。
*/
// 原文の単語情報を埋める。
assert(m_languages.contains(result.scode));
Language *language = m_languages[result.scode];
+
Text::pointer sentences = language->divide_into_sentences(sstring);
assert(sentences->size() == 1);
Text::pointer words = language->divide_into_words(sentences->begin());
+ // 単語を正規化。
for(Text::pointer word = words->begin(); word; word = word->next())
- source.words.append(word->to_string());
+ {
+ QString w = word->to_string();
+ w = language->stem(w);
+ source.words.insert(w);
+ }
// データベースへ保存。
QMetaObject::invokeMethod(
class Service;
class SocketConnection;
+class EditorWidget;
+class CandidateWidget;
class WordringConnection : public QObject
{
Service(Settings *settings, QObject *parent = 0);
~Service();
+ // エディタ。
+ EditorWidget* editor_widget();
+ void set_editor_widget(EditorWidget *editor);
+ CandidateWidget* candidate_widget();
+ void set_candidate_widget(CandidateWidget *candidate);
+
+ void change_edit_mode(bool mode);
+
// SocketConnection
void attach(SocketConnection *connection);
void detach(SocketConnection *connection);
+ void set_current_connection(SocketConnection *connection);
// CRC32
void setup_crc_table();
// 言語プラグイン
void load_languages(QString const &path);
+ Language* find_language(int code);
+
Text::pointer divide_into_sentences(int code, QString string);
Text::pointer divide_into_words(int code, Text::pointer sentence);
// データベース
quint32 find_site_id(QString host);
-
void insert_sentence(TextSentence::pointer sentence);
void remove_sentence(TextSentence::pointer sentence);
-
void find_sentence(TextSentence::pointer sentence);
+
+ void find_candidates(TextSentence::pointer sentence);
+
private:
public:
signals:
void sentence_not_found(TextSentence::weak_pointer token);
void sentence_inserted(quint32 source_id, quint32 target_id, TextSentence::weak_pointer token);
+ void candidate_found(candidate_data_type candidate,
+ TextSentence::weak_pointer token);
public:
// サーバから
void sentence_found(QString sstring, sentence_data_type const &result);
QMap<int, Language*> m_languages;
+ EditorWidget *m_editor_widget;
+ CandidateWidget *m_candidate_widget;
+
QThread *m_database_thread;
Database *m_database;
WordringConnection *m_wordring;
QSet<SocketConnection*> m_connections;
+ SocketConnection *m_current_connection;
+
quint32 m_crc_table[256];
};
Q_DECLARE_METATYPE(sentence_data_type)
+Q_DECLARE_METATYPE(candidate_data_type)
Q_DECLARE_METATYPE(TextSentence::weak_pointer)
} // namespace TM
#include "tmsocket.h"
-#include "tmeditorwidget.h"
#include "tmservice.h"
+#include "tmeditorwidget.h"
+#include "tmcandidatewidget.h"
#include "settings.h"
#include "html.h"
// SocketConnection -----------------------------------------------------------
TM::SocketConnection::SocketConnection(Settings *settings, Service *service,
- EditorWidget *editor_widget, QWebSocket *socket)
+ QWebSocket *socket)
: QObject(socket)
, m_settings(settings)
, m_service(service)
- , m_mutex(QMutex::Recursive)
- , m_editor_widget(editor_widget)
- , m_edit_mode(false)
, m_segments(TextSegmentList::create(service, this))
{
qRegisterMetaType<pointer>();
TM::SocketConnection::~SocketConnection()
{
- m_editor_widget->detach(this);
+ m_service->editor_widget()->detach(this);
+ m_service->candidate_widget()->detach(this);
m_service->detach(this);
}
for(TextSentence::pointer s : sentences)
{
- TextSegment::pointer current = m_segments->current();
+ TextSegment::pointer current = m_segments->current_segment();
if(s->parent() == current) continue;
s->set_tsentence(previous_crc, next_crc, tstring, json);
- m_editor_widget->set_segment(s->parent());
+ m_service->editor_widget()->set_segment(s->parent());
}
}
*/
void TM::SocketConnection::set_edit_mode(bool edit_mode)
{
- if(edit_mode == m_edit_mode) return;
-
- m_edit_mode = edit_mode;
QJsonObject json;
json["cmd"] = "set_edit_mode";
json["edit_mode"] = edit_mode;
if(!segment) return;
// 編集中のセグメントに変更があった場合のみ、エディタにセット。
- if(m_segments->set_current(segment)) m_editor_widget->set_segment(segment);
+ if(m_segments->set_current_segment(segment))
+ m_service->editor_widget()->set_segment(segment);
}
/*!
*/
void TM::SocketConnection::do_focus(QJsonObject const &)
{
- m_editor_widget->attach(this);
+ set_edit_mode(m_service->editor_widget()->edit_mode());
- m_editor_widget->set_edit_mode(m_edit_mode);
- if(m_segments->current()) m_editor_widget->set_segment(m_segments->current());
+ m_service->editor_widget()->attach(this);
+ m_service->candidate_widget()->attach(this);
+
+ m_service->set_current_connection(this);
+
+ if(m_segments->current_segment())
+ m_service->editor_widget()->set_segment(m_segments->current_segment());
}
void TM::SocketConnection::do_blur(QJsonObject const &)
void TM::SocketConnection::do_load(QJsonObject const &json)
{
- m_editor_widget->attach(this);
-
- set_edit_mode(m_editor_widget->edit_mode());
+ set_edit_mode(m_service->editor_widget()->edit_mode());
- m_segments->set_scode(m_editor_widget->source_language());
- m_segments->set_tcode(m_editor_widget->target_language());
- qDebug() << QUrl(json["url"].toString());
+ m_segments->set_scode(m_service->editor_widget()->source_language());
+ m_segments->set_tcode(m_service->editor_widget()->target_language());
m_segments->set_url(QUrl(json["url"].toString()));
}
// SocketServer ---------------------------------------------------------------
-TM::SocketServer::SocketServer(Settings *settings, Service* service,
- EditorWidget *editor_widget, QObject *parent)
+TM::SocketServer::SocketServer(Settings *settings, Service* service, QObject *parent)
: QObject(parent)
, m_settings(settings)
, m_service(service)
, m_server(new QWebSocketServer(QStringLiteral("wordring websocket"),
QWebSocketServer::NonSecureMode, this))
- , m_editor_widget(editor_widget)
{
connect(m_server, SIGNAL(newConnection()), this, SLOT(onNewConnection()));
{
QWebSocket *socket = m_server->nextPendingConnection();
connect(socket, SIGNAL(disconnected()), this, SLOT(onDisconnected()));
- new SocketConnection(m_settings, m_service, m_editor_widget, socket);
+ new SocketConnection(m_settings, m_service, socket);
m_sockets.push_back(socket);
}
{
class Service;
-class EditorWidget;
class SocketConnection : public QObject
{
typedef segment_map_type::iterator segment_map_iterator;
public:
- SocketConnection(Settings *settings, Service *service,
- EditorWidget *editor_widget, QWebSocket *socket);
+ SocketConnection(Settings *settings, Service *service, QWebSocket *socket);
virtual ~SocketConnection();
//
private:
Settings *m_settings;
Service *m_service;
- QMutex m_mutex;
- bool m_edit_mode;
+ TextSegmentList::pointer m_segments; // スレッドを渡るため、共有ポインタの必要がある。
- EditorWidget *m_editor_widget;
-
- TextSegmentList::pointer m_segments;
};
class SocketServer : public QObject
{
Q_OBJECT
public:
- SocketServer(Settings *settings, Service* service,
- EditorWidget *editor_widget, QObject *parent);
+ SocketServer(Settings *settings, Service* service, QObject *parent);
~SocketServer();
quint16 port() const;
QWebSocketServer *m_server;
QList<QWebSocket*> m_sockets;
-
- EditorWidget *m_editor_widget;
};
Q_DECLARE_METATYPE(SocketConnection::pointer)
Service *service = segment_list()->service();
// 原文の単語情報を埋める。
- Text::pointer sentences = service->divide_into_sentences(scode(), sstring);
+ Language *language = service->find_language(result.scode);
+ Text::pointer sentences = language->divide_into_sentences(sstring);
assert(sentences->size() == 1);
- Text::pointer words = service->divide_into_words(scode(), sentences->begin());
+ Text::pointer words = language->divide_into_words(sentences->begin());
+ // 単語を正規化。
for(Text::pointer word = words->begin(); word; word = word->next())
{
- QString w = service->normalize(scode(), word->to_string());
- result.words.append(w);
+ QString w = language->normalize(word->to_string());
+ w = language->stem(w);
+ result.words.insert(w);
}
return result;
/*!
* \brief 訳語候補を追加します。
*/
-void TM::TextSentence::append(sentence_data_type const &sentence)
+bool TM::TextSentence::append_candidate(candidate_data_type const &candidate)
{
- m_candidates.append(sentence);
+ bool result = true;
+ for(candidate_data_type const &c : m_candidates)
+ if(c.source.sentence == candidate.source.sentence) result = false;
+ if(result) m_candidates.append(candidate);
+ return result;
}
/*!
* \brief 訳語候補のリストを返します。
*/
-const QList<TM::sentence_data_type> &TM::TextSentence::candidates() const
+QList<TM::candidate_data_type> const &TM::TextSentence::candidates() const
{
return m_candidates;
}
+void TM::TextSentence::clear_candidates()
+{
+ m_candidates.clear();
+}
+
QString TM::TextSentence::debug_dump() const
{
QString result;
return m_source_map.values(ssentence);
}
-TM::TextSegment::pointer TM::TextSegmentList::current()
+TM::TextSegment::pointer TM::TextSegmentList::current_segment()
{
return m_current_segment;
}
* \brief 引数として与えられたセグメントを編集中としてマークします。
* \return 既に編集中とマークされていた場合、falseを返す。
*/
-bool TM::TextSegmentList::set_current(TextSegment::pointer segment)
+bool TM::TextSegmentList::set_current_segment(TextSegment::pointer segment)
{
- if(m_current_segment == segment) return false;
+ if(m_current_segment == segment && m_current_segment) return false;
m_current_segment = segment;
return true;
}
+TM::TextSentence::pointer TM::TextSegmentList::current_sentence()
+{
+ return m_current_sentence;
+}
+
+void TM::TextSegmentList::set_current_sentence(TextSentence::pointer sentence)
+{
+ m_current_sentence = sentence;
+}
+
TM::SocketConnection* TM::TextSegmentList::connection() { return m_connection; }
int TM::TextSegmentList::scode() const { assert(m_scode); return m_scode; }
#include <QUrl>
#include <QList>
#include <QMap>
+#include <QSet>
#include <QJsonDocument>
#include <QJsonObject>
* データベース登録時に、sentenceの単語を示すリスト。
* 原文にのみ必要。
*/
- QList<QString> words;
+ QSet<QString> words;
};
/*!
*/
class candidate_data_type
{
-
+public:
+ int edit_distance;
+ sentence_data_type source;
+ sentence_data_type target;
};
/*!
quint32 user_id() { return 0; }
- void append(sentence_data_type const &sentence);
- QList<sentence_data_type> const & candidates() const;
+ bool append_candidate(candidate_data_type const &candidate);
+ QList<candidate_data_type> const & candidates() const;
+ void clear_candidates();
bool is_loaded() const;
//void set_loaded(bool loaded);
int m_quality; /*!< 訳文の品質 */
- QList<sentence_data_type> m_candidates; /*!< 訳文候補 */
+ QList<candidate_data_type> m_candidates; /*!< 訳文候補 */
parent_weak_pointer m_parent;
int m_index; /*!< セグメント内における位置 */
Text::pointer m_text; /*!< セグメントのHtmlData付テキスト。 */
// 編集用
- storage_type m_sentences; /*!< 編集用センテンスのリスト。 */
+ QList<TextSentence::pointer> m_sentences; /*!< 編集用センテンスのリスト。 */
// 親子。
parent_weak_pointer m_parent;
// 検索。
QList<TextSentence::pointer> find_sentences(QString ssentence);
- // エディタ。
- TextSegment::pointer current();
- bool set_current(TextSegment::pointer segment);
+ // 編集。
+ TextSegment::pointer current_segment();
+ bool set_current_segment(TextSegment::pointer segment);
+ TextSentence::pointer current_sentence();
+ void set_current_sentence(TextSentence::pointer sentence);
SocketConnection* connection();
private:
Service *m_service;
- SocketConnection *m_connection;
+ SocketConnection *m_connection; /*!< このリストのオーナーとなるコネクション */
int m_scode; /*!< 原文言語コード。*/
int m_tcode; /*!< 訳文言語コード。 */
QUrl m_url;
QMap<int, TextSegment::pointer> m_segments; /*!< HTML内のdata-wordring-segmentの値とセグメントのマップ */
- TextSegment::pointer m_current_segment; /*!< 編集中のセグメントを表す。 */
+ TextSegment::pointer m_current_segment; /*!< 編集中のセグメントを表す。(ブラウザから変更)*/
+ TextSentence::pointer m_current_sentence; /*!< 編集中のセンテンスを表す。(エディタから変更) */
/*! 正規化原文と編集文のマルチ・マップ。 */
QMap<QString, TextSentence::pointer> m_source_map;
--- /dev/null
+/**
+ dtl -- Diff Template Library
+
+ In short, Diff Template Library is distributed under so called "BSD license",
+
+ Copyright (c) 2013 Tatsuhiko Kubo <cubicdaiya@gmail.com>
+ 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 authors 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.
+*/
+
+/* If you use this library, you must include dtl.hpp only. */
+
+#ifndef DTL_DIFF_H
+#define DTL_DIFF_H
+
+namespace dtl {
+
+ /**
+ * diff class template
+ * sequence must support random_access_iterator.
+ */
+ template <typename elem, typename sequence = vector< elem >, typename comparator = Compare< elem > >
+ class Diff
+ {
+ private :
+ dtl_typedefs(elem, sequence)
+ sequence A;
+ sequence B;
+ size_t M;
+ size_t N;
+ size_t delta;
+ size_t offset;
+ long long *fp;
+ long long editDistance;
+ Lcs< elem > lcs;
+ Ses< elem > ses;
+ editPath path;
+ editPathCordinates pathCordinates;
+ bool swapped;
+ bool huge;
+ bool trivial;
+ bool editDistanceOnly;
+ uniHunkVec uniHunks;
+ comparator cmp;
+ public :
+ Diff () {}
+
+ Diff (const sequence& a,
+ const sequence& b) : A(a), B(b), ses(false) {
+ init();
+ }
+
+ Diff (const sequence& a,
+ const sequence& b,
+ bool deletesFirst) : A(a), B(b), ses(deletesFirst) {
+ init();
+ }
+
+ Diff (const sequence& a,
+ const sequence& b,
+ const comparator& comp) : A(a), B(b), ses(false), cmp(comp) {
+ init();
+ }
+
+ Diff (const sequence& a,
+ const sequence& b,
+ bool deleteFirst,
+ const comparator& comp) : A(a), B(b), ses(deleteFirst), cmp(comp) {
+ init();
+ }
+
+ ~Diff() {}
+
+ long long getEditDistance () const {
+ return editDistance;
+ }
+
+ Lcs< elem > getLcs () const {
+ return lcs;
+ }
+
+ elemVec getLcsVec () const {
+ return lcs.getSequence();
+ }
+
+ Ses< elem > getSes () const {
+ return ses;
+ }
+
+ uniHunkVec getUniHunks () const {
+ return uniHunks;
+ }
+
+ /* These should be deprecated */
+ bool isHuge () const {
+ return huge;
+ }
+
+ void onHuge () {
+ this->huge = true;
+ }
+
+ void offHuge () {
+ this->huge = false;
+ }
+
+ bool isUnserious () const {
+ return trivial;
+ }
+
+ void onUnserious () {
+ this->trivial = true;
+ }
+
+ void offUnserious () {
+ this->trivial = false;
+ }
+
+ void onOnlyEditDistance () {
+ this->editDistanceOnly = true;
+ }
+
+ /* These are the replacements for the above */
+ bool hugeEnabled () const {
+ return huge;
+ }
+
+ void enableHuge () {
+ this->huge = true;
+ }
+
+ void disableHuge () {
+ this->huge = false;
+ }
+
+ bool trivialEnabled () const {
+ return trivial;
+ }
+
+ void enableTrivial () const {
+ this->trivial = true;
+ }
+
+ void disableTrivial () {
+ this->trivial = false;
+ }
+
+ void editDistanceOnlyEnabled () {
+ this->editDistanceOnly = true;
+ }
+
+ /**
+ * patching with Unified Format Hunks
+ */
+ sequence uniPatch (const sequence& seq) {
+ elemList seqLst(seq.begin(), seq.end());
+ sesElemVec shunk;
+ sesElemVec_iter vsesIt;
+ elemList_iter lstIt = seqLst.begin();
+ long long inc_dec_total = 0;
+ long long gap = 1;
+ for (uniHunkVec_iter it=uniHunks.begin();it!=uniHunks.end();++it) {
+ joinSesVec(shunk, it->common[0]);
+ joinSesVec(shunk, it->change);
+ joinSesVec(shunk, it->common[1]);
+ it->a += inc_dec_total;
+ inc_dec_total += it->inc_dec_count;
+ for (long long i=0;i<it->a - gap;++i) {
+ ++lstIt;
+ }
+ gap = it->a + it->b + it->inc_dec_count;
+ vsesIt = shunk.begin();
+ while (vsesIt!=shunk.end()) {
+ switch (vsesIt->second.type) {
+ case SES_ADD :
+ seqLst.insert(lstIt, vsesIt->first);
+ break;
+ case SES_DELETE :
+ if (lstIt != seqLst.end()) {
+ lstIt = seqLst.erase(lstIt);
+ }
+ break;
+ case SES_COMMON :
+ if (lstIt != seqLst.end()) {
+ ++lstIt;
+ }
+ break;
+ default :
+ // no fall-through
+ break;
+ }
+ ++vsesIt;
+ }
+ shunk.clear();
+ }
+
+ sequence patchedSeq(seqLst.begin(), seqLst.end());
+ return patchedSeq;
+ }
+
+ /**
+ * patching with Shortest Edit Script (SES)
+ */
+ sequence patch (const sequence& seq) const {
+ sesElemVec sesSeq = ses.getSequence();
+ elemList seqLst(seq.begin(), seq.end());
+ elemList_iter lstIt = seqLst.begin();
+ for (sesElemVec_iter sesIt=sesSeq.begin();sesIt!=sesSeq.end();++sesIt) {
+ switch (sesIt->second.type) {
+ case SES_ADD :
+ seqLst.insert(lstIt, sesIt->first);
+ break;
+ case SES_DELETE :
+ lstIt = seqLst.erase(lstIt);
+ break;
+ case SES_COMMON :
+ ++lstIt;
+ break;
+ default :
+ // no through
+ break;
+ }
+ }
+ sequence patchedSeq(seqLst.begin(), seqLst.end());
+ return patchedSeq;
+ }
+
+ /**
+ * compose Longest Common Subsequence and Shortest Edit Script.
+ * The algorithm implemented here is based on "An O(NP) Sequence Comparison Algorithm"
+ * described by Sun Wu, Udi Manber and Gene Myers
+ */
+ void compose() {
+
+ if (isHuge()) {
+ pathCordinates.reserve(MAX_CORDINATES_SIZE);
+ }
+
+ long long p = -1;
+ fp = new long long[M + N + 3];
+ fill(&fp[0], &fp[M + N + 3], -1);
+ path = editPath(M + N + 3);
+ fill(path.begin(), path.end(), -1);
+ ONP:
+ do {
+ ++p;
+ for (long long k=-p;k<=static_cast<long long>(delta)-1;++k) {
+ fp[k+offset] = snake(k, fp[k-1+offset]+1, fp[k+1+offset]);
+ }
+ for (long long k=static_cast<long long>(delta)+p;k>=static_cast<long long>(delta)+1;--k) {
+ fp[k+offset] = snake(k, fp[k-1+offset]+1, fp[k+1+offset]);
+ }
+ fp[delta+offset] = snake(static_cast<long long>(delta), fp[delta-1+offset]+1, fp[delta+1+offset]);
+ } while (fp[delta+offset] != static_cast<long long>(N) && pathCordinates.size() < MAX_CORDINATES_SIZE);
+
+ editDistance += static_cast<long long>(delta) + 2 * p;
+ long long r = path[delta+offset];
+ P cordinate;
+ editPathCordinates epc(0);
+
+ // recording edit distance only
+ if (editDistanceOnly) {
+ delete[] this->fp;
+ return;
+ }
+
+ while(r != -1) {
+ cordinate.x = pathCordinates[(size_t)r].x;
+ cordinate.y = pathCordinates[(size_t)r].y;
+ epc.push_back(cordinate);
+ r = pathCordinates[(size_t)r].k;
+ }
+
+ // record Longest Common Subsequence & Shortest Edit Script
+ if (!recordSequence(epc)) {
+ pathCordinates.resize(0);
+ epc.resize(0);
+ p = -1;
+ goto ONP;
+ }
+ delete[] this->fp;
+ }
+
+ /**
+ * print difference between A and B as an SES
+ */
+ template < typename stream >
+ void printSES (stream& out) const {
+ sesElemVec ses_v = ses.getSequence();
+ for_each(ses_v.begin(), ses_v.end(), ChangePrinter< sesElem, stream >(out));
+ }
+
+ void printSES (ostream& out = cout) const {
+ printSES< ostream >(out);
+ }
+
+ /**
+ * print differences given an SES
+ */
+ template < typename stream >
+ static void printSES (const Ses< elem >& s, stream& out) {
+ sesElemVec ses_v = s.getSequence();
+ for_each(ses_v.begin(), ses_v.end(), ChangePrinter< sesElem, stream >(out));
+ }
+
+ static void printSES (const Ses< elem >& s, ostream& out = cout) {
+ printSES< ostream >(s, out);
+ }
+
+ /**
+ * print difference between A and B as an SES with custom printer
+ */
+ template < typename stream, template < typename SEET, typename STRT > class PT >
+ void printSES (stream& out) const {
+ sesElemVec ses_v = ses.getSequence ();
+ for_each (ses_v.begin (), ses_v.end(), PT < sesElem, stream > (out));
+ }
+
+ /**
+ * print difference between A and B in the Unified Format
+ */
+ template < typename stream >
+ void printUnifiedFormat (stream& out) const {
+ for_each(uniHunks.begin(), uniHunks.end(), UniHunkPrinter< sesElem, stream >(out));
+ }
+
+ void printUnifiedFormat (ostream& out = cout) const {
+ printUnifiedFormat< ostream >(out);
+ }
+
+ /**
+ * print unified format difference with given unified format hunks
+ */
+ template < typename stream >
+ static void printUnifiedFormat (const uniHunkVec& hunks, stream& out) {
+ for_each(hunks.begin(), hunks.end(), UniHunkPrinter< sesElem >(out));
+ }
+
+ static void printUnifiedFormat (const uniHunkVec& hunks, ostream& out = cout) {
+ printUnifiedFormat< ostream >(hunks, out);
+ }
+
+ /**
+ * compose Unified Format Hunks from Shortest Edit Script
+ */
+ void composeUnifiedHunks () {
+ sesElemVec common[2];
+ sesElemVec change;
+ sesElemVec ses_v = ses.getSequence();
+ long long l_cnt = 1;
+ long long length = distance(ses_v.begin(), ses_v.end());
+ long long middle = 0;
+ bool isMiddle, isAfter;
+ elemInfo einfo;
+ long long a, b, c, d; // @@ -a,b +c,d @@
+ long long inc_dec_count = 0;
+ uniHunk< sesElem > hunk;
+ sesElemVec adds;
+ sesElemVec deletes;
+
+ isMiddle = isAfter = false;
+ a = b = c = d = 0;
+
+ for (sesElemVec_iter it=ses_v.begin();it!=ses_v.end();++it, ++l_cnt) {
+ einfo = it->second;
+ switch (einfo.type) {
+ case SES_ADD :
+ middle = 0;
+ ++inc_dec_count;
+ adds.push_back(*it);
+ if (!isMiddle) isMiddle = true;
+ if (isMiddle) ++d;
+ if (l_cnt >= length) {
+ joinSesVec(change, deletes);
+ joinSesVec(change, adds);
+ isAfter = true;
+ }
+ break;
+ case SES_DELETE :
+ middle = 0;
+ --inc_dec_count;
+ deletes.push_back(*it);
+ if (!isMiddle) isMiddle = true;
+ if (isMiddle) ++b;
+ if (l_cnt >= length) {
+ joinSesVec(change, deletes);
+ joinSesVec(change, adds);
+ isAfter = true;
+ }
+ break;
+ case SES_COMMON :
+ ++b;++d;
+ if (common[1].empty() && adds.empty() && deletes.empty() && change.empty()) {
+ if (static_cast<long long>(common[0].size()) < DTL_CONTEXT_SIZE) {
+ if (a == 0 && c == 0) {
+ if (!wasSwapped()) {
+ a = einfo.beforeIdx;
+ c = einfo.afterIdx;
+ } else {
+ a = einfo.afterIdx;
+ c = einfo.beforeIdx;
+ }
+ }
+ common[0].push_back(*it);
+ } else {
+ rotate(common[0].begin(), common[0].begin() + 1, common[0].end());
+ common[0].pop_back();
+ common[0].push_back(*it);
+ ++a;++c;
+ --b;--d;
+ }
+ }
+ if (isMiddle && !isAfter) {
+ ++middle;
+ joinSesVec(change, deletes);
+ joinSesVec(change, adds);
+ change.push_back(*it);
+ if (middle >= DTL_SEPARATE_SIZE || l_cnt >= length) {
+ isAfter = true;
+ }
+ adds.clear();
+ deletes.clear();
+ }
+ break;
+ default :
+ // no through
+ break;
+ }
+ // compose unified format hunk
+ if (isAfter && !change.empty()) {
+ sesElemVec_iter cit = it;
+ long long cnt = 0;
+ for (long long i=0;i<DTL_SEPARATE_SIZE && (cit != ses_v.end());++i, ++cit) {
+ if (cit->second.type == SES_COMMON) {
+ ++cnt;
+ }
+ }
+ if (cnt < DTL_SEPARATE_SIZE && l_cnt < length) {
+ middle = 0;
+ isAfter = false;
+ continue;
+ }
+ if (static_cast<long long>(common[0].size()) >= DTL_SEPARATE_SIZE) {
+ long long c0size = static_cast<long long>(common[0].size());
+ rotate(common[0].begin(),
+ common[0].begin() + (size_t)c0size - DTL_SEPARATE_SIZE,
+ common[0].end());
+ for (long long i=0;i<c0size - DTL_SEPARATE_SIZE;++i) {
+ common[0].pop_back();
+ }
+ a += c0size - DTL_SEPARATE_SIZE;
+ c += c0size - DTL_SEPARATE_SIZE;
+ }
+ if (a == 0) ++a;
+ if (c == 0) ++c;
+ if (wasSwapped()) swap(a, c);
+ hunk.a = a;
+ hunk.b = b;
+ hunk.c = c;
+ hunk.d = d;
+ hunk.common[0] = common[0];
+ hunk.change = change;
+ hunk.common[1] = common[1];
+ hunk.inc_dec_count = inc_dec_count;
+ uniHunks.push_back(hunk);
+ isMiddle = false;
+ isAfter = false;
+ common[0].clear();
+ common[1].clear();
+ adds.clear();
+ deletes.clear();
+ change.clear();
+ a = b = c = d = middle = inc_dec_count = 0;
+ }
+ }
+ }
+
+ /**
+ * compose ses from stream
+ */
+ template <typename stream>
+ static Ses< elem > composeSesFromStream (stream& st)
+ {
+ elem line;
+ Ses< elem > ret;
+ long long x_idx, y_idx;
+ x_idx = y_idx = 1;
+ while (getline(st, line)) {
+ elem mark(line.begin(), line.begin() + 1);
+ elem e(line.begin() + 1, line.end());
+ if (mark == SES_MARK_DELETE) {
+ ret.addSequence(e, x_idx, 0, SES_DELETE);
+ ++x_idx;
+ } else if (mark == SES_MARK_ADD) {
+ ret.addSequence(e, y_idx, 0, SES_ADD);
+ ++y_idx;
+ } else if (mark == SES_MARK_COMMON) {
+ ret.addSequence(e, x_idx, y_idx, SES_COMMON);
+ ++x_idx;
+ ++y_idx;
+ }
+ }
+ return ret;
+ }
+
+ private :
+ /**
+ * initialize
+ */
+ void init () {
+ M = distance(A.begin(), A.end());
+ N = distance(B.begin(), B.end());
+ if (M < N) {
+ swapped = false;
+ } else {
+ swap(A, B);
+ swap(M, N);
+ swapped = true;
+ }
+ editDistance = 0;
+ delta = N - M;
+ offset = M + 1;
+ huge = false;
+ trivial = false;
+ editDistanceOnly = false;
+ fp = NULL;
+ }
+
+ /**
+ * search shortest path and record the path
+ */
+ long long snake(const long long& k, const long long& above, const long long& below) {
+ long long r = above > below ? path[(size_t)k-1+offset] : path[(size_t)k+1+offset];
+ long long y = max(above, below);
+ long long x = y - k;
+ while ((size_t)x < M && (size_t)y < N && (swapped ? cmp.impl(B[(size_t)y], A[(size_t)x]) : cmp.impl(A[(size_t)x], B[(size_t)y]))) {
+ ++x;++y;
+ }
+
+ path[(size_t)k+offset] = static_cast<long long>(pathCordinates.size());
+ if (!editDistanceOnly) {
+ P p;
+ p.x = x;p.y = y;p.k = r;
+ pathCordinates.push_back(p);
+ }
+ return y;
+ }
+
+ /**
+ * record SES and LCS
+ */
+ bool recordSequence (const editPathCordinates& v) {
+ sequence_const_iter x(A.begin());
+ sequence_const_iter y(B.begin());
+ long long x_idx, y_idx; // line number for Unified Format
+ long long px_idx, py_idx; // cordinates
+ bool complete = false;
+ x_idx = y_idx = 1;
+ px_idx = py_idx = 0;
+ for (size_t i=v.size()-1;!complete;--i) {
+ while(px_idx < v[i].x || py_idx < v[i].y) {
+ if (v[i].y - v[i].x > py_idx - px_idx) {
+ if (!wasSwapped()) {
+ ses.addSequence(*y, 0, y_idx, SES_ADD);
+ } else {
+ ses.addSequence(*y, y_idx, 0, SES_DELETE);
+ }
+ ++y;
+ ++y_idx;
+ ++py_idx;
+ } else if (v[i].y - v[i].x < py_idx - px_idx) {
+ if (!wasSwapped()) {
+ ses.addSequence(*x, x_idx, 0, SES_DELETE);
+ } else {
+ ses.addSequence(*x, 0, x_idx, SES_ADD);
+ }
+ ++x;
+ ++x_idx;
+ ++px_idx;
+ } else {
+ if (!wasSwapped()) {
+ lcs.addSequence(*x);
+ ses.addSequence(*x, x_idx, y_idx, SES_COMMON);
+ } else {
+ lcs.addSequence(*y);
+ ses.addSequence(*y, y_idx, x_idx, SES_COMMON);
+ }
+ ++x;
+ ++y;
+ ++x_idx;
+ ++y_idx;
+ ++px_idx;
+ ++py_idx;
+ }
+ }
+ if (i == 0) complete = true;
+ }
+
+ if (x_idx > static_cast<long long>(M) && y_idx > static_cast<long long>(N)) {
+ // all recording succeeded
+ } else {
+ // trivial difference
+ if (trivialEnabled()) {
+ if (!wasSwapped()) {
+ recordOddSequence(x_idx, M, x, SES_DELETE);
+ recordOddSequence(y_idx, N, y, SES_ADD);
+ } else {
+ recordOddSequence(x_idx, M, x, SES_ADD);
+ recordOddSequence(y_idx, N, y, SES_DELETE);
+ }
+ return true;
+ }
+
+ // nontrivial difference
+ sequence A_(A.begin() + (size_t)x_idx - 1, A.end());
+ sequence B_(B.begin() + (size_t)y_idx - 1, B.end());
+ A = A_;
+ B = B_;
+ M = distance(A.begin(), A.end());
+ N = distance(B.begin(), B.end());
+ delta = N - M;
+ offset = M + 1;
+ delete[] fp;
+ fp = new long long[M + N + 3];
+ fill(&fp[0], &fp[M + N + 3], -1);
+ fill(path.begin(), path.end(), -1);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * record odd sequence in SES
+ */
+ void inline recordOddSequence (long long idx, long long length, sequence_const_iter it, const edit_t et) {
+ while(idx < length){
+ ses.addSequence(*it, idx, 0, et);
+ ++it;
+ ++idx;
+ ++editDistance;
+ }
+ ses.addSequence(*it, idx, 0, et);
+ ++editDistance;
+ }
+
+ /**
+ * join SES vectors
+ */
+ void inline joinSesVec (sesElemVec& s1, sesElemVec& s2) const {
+ if (!s2.empty()) {
+ for (sesElemVec_iter vit=s2.begin();vit!=s2.end();++vit) {
+ s1.push_back(*vit);
+ }
+ }
+ }
+
+ /**
+ * check if the sequences have been swapped
+ */
+ bool inline wasSwapped () const {
+ return swapped;
+ }
+
+ };
+}
+
+#endif // DTL_DIFF_H
--- /dev/null
+/**
+ dtl -- Diff Template Library
+
+ In short, Diff Template Library is distributed under so called "BSD license",
+
+ Copyright (c) 2013 Tatsuhiko Kubo <cubicdaiya@gmail.com>
+ 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 authors 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.
+*/
+
+/* If you use this library, you must include dtl.hpp only. */
+
+#ifndef DTL_DIFF3_H
+#define DTL_DIFF3_H
+
+namespace dtl {
+
+ /**
+ * diff3 class template
+ * sequence must support random_access_iterator.
+ */
+ template <typename elem, typename sequence = vector< elem >, typename comparator = Compare< elem > >
+ class Diff3
+ {
+ private:
+ dtl_typedefs(elem, sequence)
+ sequence A;
+ sequence B;
+ sequence C;
+ sequence S;
+ Diff< elem, sequence, comparator > diff_ba;
+ Diff< elem, sequence, comparator > diff_bc;
+ bool conflict;
+ elem csepabegin;
+ elem csepa;
+ elem csepaend;
+ public :
+ Diff3 () {}
+ Diff3 (const sequence& a,
+ const sequence& b,
+ const sequence& c) : A(a), B(b), C(c),
+ diff_ba(b, a), diff_bc(b, c),
+ conflict(false) {}
+
+ ~Diff3 () {}
+
+ bool isConflict () const {
+ return conflict;
+ }
+
+ sequence getMergedSequence () const {
+ return S;
+ }
+
+ /**
+ * merge changes B and C into A
+ */
+ bool merge () {
+ if (diff_ba.getEditDistance() == 0) { // A == B
+ if (diff_bc.getEditDistance() == 0) { // A == B == C
+ S = B;
+ return true;
+ }
+ S = C;
+ return true;
+ } else { // A != B
+ if (diff_bc.getEditDistance() == 0) { // A != B == C
+ S = A;
+ return true;
+ } else { // A != B != C
+ S = merge_();
+ if (isConflict()) { // conflict occured
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * compose differences
+ */
+ void compose () {
+ diff_ba.compose();
+ diff_bc.compose();
+ }
+
+ private :
+ /**
+ * merge implementation
+ */
+ sequence merge_ () {
+ elemVec seq;
+ Ses< elem > ses_ba = diff_ba.getSes();
+ Ses< elem > ses_bc = diff_bc.getSes();
+ sesElemVec ses_ba_v = ses_ba.getSequence();
+ sesElemVec ses_bc_v = ses_bc.getSequence();
+ sesElemVec_iter ba_it = ses_ba_v.begin();
+ sesElemVec_iter bc_it = ses_bc_v.begin();
+ sesElemVec_iter ba_end = ses_ba_v.end();
+ sesElemVec_iter bc_end = ses_bc_v.end();
+
+ while (!isEnd(ba_end, ba_it) || !isEnd(bc_end, bc_it)) {
+ while (true) {
+ if (!isEnd(ba_end, ba_it) &&
+ !isEnd(bc_end, bc_it) &&
+ ba_it->first == bc_it->first &&
+ ba_it->second.type == SES_COMMON &&
+ bc_it->second.type == SES_COMMON) {
+ // do nothing
+ } else {
+ break;
+ }
+ if (!isEnd(ba_end, ba_it)) seq.push_back(ba_it->first);
+ else if (!isEnd(bc_end, bc_it)) seq.push_back(bc_it->first);
+ forwardUntilEnd(ba_end, ba_it);
+ forwardUntilEnd(bc_end, bc_it);
+ }
+ if (isEnd(ba_end, ba_it) || isEnd(bc_end, bc_it)) break;
+ if ( ba_it->second.type == SES_COMMON
+ && bc_it->second.type == SES_DELETE) {
+ forwardUntilEnd(ba_end, ba_it);
+ forwardUntilEnd(bc_end, bc_it);
+ } else if (ba_it->second.type == SES_COMMON &&
+ bc_it->second.type == SES_ADD) {
+ seq.push_back(bc_it->first);
+ forwardUntilEnd(bc_end, bc_it);
+ } else if (ba_it->second.type == SES_DELETE &&
+ bc_it->second.type == SES_COMMON) {
+ forwardUntilEnd(ba_end, ba_it);
+ forwardUntilEnd(bc_end, bc_it);
+ } else if (ba_it->second.type == SES_DELETE &&
+ bc_it->second.type == SES_DELETE) {
+ if (ba_it->first == bc_it->first) {
+ forwardUntilEnd(ba_end, ba_it);
+ forwardUntilEnd(bc_end, bc_it);
+ } else {
+ // conflict
+ conflict = true;
+ return B;
+ }
+ } else if (ba_it->second.type == SES_DELETE &&
+ bc_it->second.type == SES_ADD) {
+ // conflict
+ conflict = true;
+ return B;
+ } else if (ba_it->second.type == SES_ADD &&
+ bc_it->second.type == SES_COMMON) {
+ seq.push_back(ba_it->first);
+ forwardUntilEnd(ba_end, ba_it);
+ } else if (ba_it->second.type == SES_ADD &&
+ bc_it->second.type == SES_DELETE) {
+ // conflict
+ conflict = true;
+ return B;
+ } else if (ba_it->second.type == SES_ADD &&
+ bc_it->second.type == SES_ADD) {
+ if (ba_it->first == bc_it->first) {
+ seq.push_back(ba_it->first);
+ forwardUntilEnd(ba_end, ba_it);
+ forwardUntilEnd(bc_end, bc_it);
+ } else {
+ // conflict
+ conflict = true;
+ return B;
+ }
+ }
+ }
+
+ if (isEnd(ba_end, ba_it)) {
+ addDecentSequence(bc_end, bc_it, seq);
+ } else if (isEnd(bc_end, bc_it)) {
+ addDecentSequence(ba_end, ba_it, seq);
+ }
+
+ sequence mergedSeq(seq.begin(), seq.end());
+ return mergedSeq;
+ }
+
+ /**
+ * join elem vectors
+ */
+ void inline joinElemVec (elemVec& s1, elemVec& s2) const {
+ if (!s2.empty()) {
+ for (elemVec_iter vit=s2.begin();vit!=s2.end();++vit) {
+ s1.push_back(*vit);
+ }
+ }
+ }
+
+ /**
+ * check if sequence is at end
+ */
+ template <typename T_iter>
+ bool inline isEnd (const T_iter& end, const T_iter& it) const {
+ return it == end ? true : false;
+ }
+
+ /**
+ * increment iterator until iterator is at end
+ */
+ template <typename T_iter>
+ void inline forwardUntilEnd (const T_iter& end, T_iter& it) const {
+ if (!isEnd(end, it)) ++it;
+ }
+
+ /**
+ * add elements whose SES's type is ADD
+ */
+ void inline addDecentSequence (const sesElemVec_iter& end, sesElemVec_iter& it, elemVec& seq) const {
+ while (!isEnd(end, it)) {
+ if (it->second.type == SES_ADD) seq.push_back(it->first);
+ ++it;
+ }
+ }
+
+ };
+}
+
+#endif // DTL_DIFF3_H
--- /dev/null
+/**
+ dtl -- Diff Template Library
+
+ In short, Diff Template Library is distributed under so called "BSD license",
+
+ Copyright (c) 2013 Tatsuhiko Kubo <cubicdaiya@gmail.com>
+ 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 authors 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.
+*/
+
+/* If you use this library, you must include dtl.hpp only. */
+
+#ifndef DTL_LCS_H
+#define DTL_LCS_H
+
+namespace dtl {
+
+ /**
+ * Longest Common Subsequence template class
+ */
+ template <typename elem>
+ class Lcs : public Sequence< elem >
+ {
+ public :
+ Lcs () {}
+ ~Lcs () {}
+ };
+}
+
+#endif // DTL_LCS_H
--- /dev/null
+/**
+ dtl -- Diff Template Library
+
+ In short, Diff Template Library is distributed under so called "BSD license",
+
+ Copyright (c) 2013 Tatsuhiko Kubo <cubicdaiya@gmail.com>
+ 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 authors 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.
+*/
+
+/* If you use this library, you must include dtl.hpp only. */
+
+#ifndef DTL_SEQUENCE_H
+#define DTL_SEQUENCE_H
+
+namespace dtl {
+
+ /**
+ * sequence class template
+ */
+ template <typename elem>
+ class Sequence
+ {
+ public :
+ typedef vector< elem > elemVec;
+ Sequence () {}
+ virtual ~Sequence () {}
+
+ elemVec getSequence () const {
+ return sequence;
+ }
+ void addSequence (elem e) {
+ sequence.push_back(e);
+ }
+ protected :
+ elemVec sequence;
+ };
+}
+
+#endif // DTL_SEQUENCE_H
--- /dev/null
+/**
+ dtl -- Diff Template Library
+
+ In short, Diff Template Library is distributed under so called "BSD license",
+
+ Copyright (c) 2013 Tatsuhiko Kubo <cubicdaiya@gmail.com>
+ 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 authors 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.
+*/
+
+/* If you use this library, you must include dtl.hpp only. */
+
+#ifndef DTL_SES_H
+#define DTL_SES_H
+
+namespace dtl {
+
+ /**
+ * Shortest Edit Script template class
+ */
+ template <typename elem>
+ class Ses : public Sequence< elem >
+ {
+ private :
+ typedef pair< elem, elemInfo > sesElem;
+ typedef vector< sesElem > sesElemVec;
+ public :
+
+ Ses () : onlyAdd(true), onlyDelete(true), onlyCopy(true), deletesFirst(false) {
+ nextDeleteIdx = 0;
+ }
+ Ses (bool moveDel) : onlyAdd(true), onlyDelete(true), onlyCopy(true), deletesFirst(moveDel) {
+ nextDeleteIdx = 0;
+ }
+ ~Ses () {}
+
+ bool isOnlyAdd () const {
+ return onlyAdd;
+ }
+
+ bool isOnlyDelete () const {
+ return onlyDelete;
+ }
+
+ bool isOnlyCopy () const {
+ return onlyCopy;
+ }
+
+ bool isOnlyOneOperation () const {
+ return isOnlyAdd() || isOnlyDelete() || isOnlyCopy();
+ }
+
+ bool isChange () const {
+ return !onlyCopy;
+ }
+
+ using Sequence< elem >::addSequence;
+ void addSequence (elem e, long long beforeIdx, long long afterIdx, const edit_t type) {
+ elemInfo info;
+ info.beforeIdx = beforeIdx;
+ info.afterIdx = afterIdx;
+ info.type = type;
+ sesElem pe(e, info);
+ if (!deletesFirst) {
+ sequence.push_back(pe);
+ }
+ switch (type) {
+ case SES_DELETE:
+ onlyCopy = false;
+ onlyAdd = false;
+ if (deletesFirst) {
+ sequence.insert(sequence.begin() + nextDeleteIdx, pe);
+ nextDeleteIdx++;
+ }
+ break;
+ case SES_COMMON:
+ onlyAdd = false;
+ onlyDelete = false;
+ if (deletesFirst) {
+ sequence.push_back(pe);
+ nextDeleteIdx = sequence.size();
+ }
+ break;
+ case SES_ADD:
+ onlyDelete = false;
+ onlyCopy = false;
+ if (deletesFirst) {
+ sequence.push_back(pe);
+ }
+ break;
+ }
+ }
+
+ sesElemVec getSequence () const {
+ return sequence;
+ }
+ private :
+ sesElemVec sequence;
+ bool onlyAdd;
+ bool onlyDelete;
+ bool onlyCopy;
+ bool deletesFirst;
+ size_t nextDeleteIdx;
+ };
+}
+
+#endif // DTL_SES_H
--- /dev/null
+/**
+ dtl -- Diff Template Library
+
+ In short, Diff Template Library is distributed under so called "BSD license",
+
+ Copyright (c) 2013 Tatsuhiko Kubo <cubicdaiya@gmail.com>
+ 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 authors 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.
+*/
+
+#ifndef DTL_H
+#define DTL_H
+
+#include "variables.hpp"
+#include "functors.hpp"
+#include "Sequence.hpp"
+#include "Lcs.hpp"
+#include "Ses.hpp"
+#include "Diff.hpp"
+#include "Diff3.hpp"
+
+#endif // DTL_H
--- /dev/null
+/**
+ dtl -- Diff Template Library
+
+ In short, Diff Template Library is distributed under so called "BSD license",
+
+ Copyright (c) 2013 Tatsuhiko Kubo <cubicdaiya@gmail.com>
+ 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 authors 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.
+*/
+
+/* If you use this library, you must include dtl.hpp only. */
+
+#ifndef DTL_FUNCTORS_H
+#define DTL_FUNCTORS_H
+
+namespace dtl {
+
+ /**
+ * printer class template
+ */
+ template <typename sesElem, typename stream = ostream >
+ class Printer
+ {
+ public :
+ Printer () : out_(cout) {}
+ Printer (stream& out) : out_(out) {}
+ virtual ~Printer () {}
+ virtual void operator() (const sesElem& se) const = 0;
+ protected :
+ stream& out_;
+ };
+
+ /**
+ * common element printer class template
+ */
+ template <typename sesElem, typename stream = ostream >
+ class CommonPrinter : public Printer < sesElem, stream >
+ {
+ public :
+ CommonPrinter () : Printer < sesElem, stream > () {}
+ CommonPrinter (stream& out) : Printer < sesElem, stream > (out) {}
+ ~CommonPrinter () {}
+ void operator() (const sesElem& se) const {
+ this->out_ << SES_MARK_COMMON << se.first << endl;
+ }
+ };
+
+ /**
+ * ses element printer class template
+ */
+ template <typename sesElem, typename stream = ostream >
+ class ChangePrinter : public Printer < sesElem, stream >
+ {
+ public :
+ ChangePrinter () : Printer < sesElem, stream > () {}
+ ChangePrinter (stream& out) : Printer < sesElem, stream > (out) {}
+ ~ChangePrinter () {}
+ void operator() (const sesElem& se) const {
+ switch (se.second.type) {
+ case SES_ADD:
+ this->out_ << SES_MARK_ADD << se.first << endl;
+ break;
+ case SES_DELETE:
+ this->out_ << SES_MARK_DELETE << se.first << endl;
+ break;
+ case SES_COMMON:
+ this->out_ << SES_MARK_COMMON << se.first << endl;
+ break;
+ }
+ }
+ };
+
+ /**
+ * unified format element printer class template
+ */
+ template <typename sesElem, typename stream = ostream >
+ class UniHunkPrinter
+ {
+ public :
+ UniHunkPrinter () : out_(cout) {}
+ UniHunkPrinter (stream& out) : out_(out) {}
+ ~UniHunkPrinter () {}
+ void operator() (const uniHunk< sesElem >& hunk) const {
+ out_ << "@@"
+ << " -" << hunk.a << "," << hunk.b
+ << " +" << hunk.c << "," << hunk.d
+ << " @@" << endl;
+
+ for_each(hunk.common[0].begin(), hunk.common[0].end(), CommonPrinter< sesElem, stream >(out_));
+ for_each(hunk.change.begin(), hunk.change.end(), ChangePrinter< sesElem, stream >(out_));
+ for_each(hunk.common[1].begin(), hunk.common[1].end(), CommonPrinter< sesElem, stream >(out_));
+ }
+ private :
+ stream& out_;
+ };
+
+ /**
+ * compare class template
+ */
+ template <typename elem>
+ class Compare
+ {
+ public :
+ Compare () {}
+ virtual ~Compare () {}
+ virtual inline bool impl (const elem& e1, const elem& e2) const {
+ return e1 == e2;
+ }
+ };
+}
+
+#endif // DTL_FUNCTORS_H
--- /dev/null
+/**
+ dtl -- Diff Template Library
+
+ In short, Diff Template Library is distributed under so called "BSD license",
+
+ Copyright (c) 2013 Tatsuhiko Kubo <cubicdaiya@gmail.com>
+ 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 authors 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.
+*/
+
+/* If you use this library, you must include dtl.hpp only. */
+
+#ifndef DTL_VARIABLES_H
+#define DTL_VARIABLES_H
+
+#include <vector>
+#include <list>
+#include <string>
+#include <algorithm>
+#include <iostream>
+
+namespace dtl {
+
+ using std::vector;
+ using std::string;
+ using std::pair;
+ using std::ostream;
+ using std::list;
+ using std::for_each;
+ using std::distance;
+ using std::fill;
+ using std::cout;
+ using std::endl;
+ using std::rotate;
+ using std::swap;
+ using std::max;
+
+ /**
+ * version string
+ */
+ const string version = "1.18";
+
+ /**
+ * type of edit for SES
+ */
+ typedef int edit_t;
+ const edit_t SES_DELETE = -1;
+ const edit_t SES_COMMON = 0;
+ const edit_t SES_ADD = 1;
+
+ /**
+ * mark of SES
+ */
+#define SES_MARK_DELETE "-"
+#define SES_MARK_COMMON " "
+#define SES_MARK_ADD "+"
+
+ /**
+ * info for Unified Format
+ */
+ typedef struct eleminfo {
+ long long beforeIdx; // index of prev sequence
+ long long afterIdx; // index of after sequence
+ edit_t type; // type of edit(Add, Delete, Common)
+ bool operator==(const eleminfo& other) const{
+ return (this->beforeIdx == other.beforeIdx && this->afterIdx == other.afterIdx && this->type == other.type);
+ }
+ } elemInfo;
+
+ const long long DTL_SEPARATE_SIZE = 3;
+ const long long DTL_CONTEXT_SIZE = 3;
+
+ /**
+ * cordinate for registering route
+ */
+ typedef struct Point {
+ long long x; // x cordinate
+ long long y; // y cordinate
+ long long k; // vertex
+ } P;
+
+ /**
+ * limit of cordinate size
+ */
+ const unsigned long long MAX_CORDINATES_SIZE = 2000000;
+
+ typedef vector< long long > editPath;
+ typedef vector< P > editPathCordinates;
+
+ /**
+ * Structure of Unified Format Hunk
+ */
+ template <typename sesElem>
+ struct uniHunk {
+ long long a, b, c, d; // @@ -a,b +c,d @@
+ vector< sesElem > common[2]; // anteroposterior commons on changes
+ vector< sesElem > change; // changes
+ long long inc_dec_count; // count of increace and decrease
+ };
+
+#define dtl_typedefs(elem, sequence) \
+ typedef pair< elem, elemInfo > sesElem; \
+ typedef vector< sesElem > sesElemVec; \
+ typedef vector< uniHunk< sesElem > > uniHunkVec; \
+ typedef list< elem > elemList; \
+ typedef vector< elem > elemVec; \
+ typedef typename uniHunkVec::iterator uniHunkVec_iter; \
+ typedef typename sesElemVec::iterator sesElemVec_iter; \
+ typedef typename elemList::iterator elemList_iter; \
+ typedef typename sequence::iterator sequence_iter; \
+ typedef typename sequence::const_iterator sequence_const_iter; \
+ typedef typename elemVec::iterator elemVec_iter;
+
+
+}
+
+#endif // DTL_VARIABLES_H