<commit_before>//
//  Copyright (C) 2005 - 2006 Kevin Krammer <kevin.krammer@gmx.at>
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
//

// standard includes
#include <iostream>
#include <string>

// Qt includes
#include <QTextCodec>

// KDE includes
#include <klocale.h>

// KABC includes
#include <kabc/addressee.h>
#include <kabc/addresseedialog.h>
#include <kabc/addresseelist.h>
#include <kabc/vcardconverter.h>

// local includes
#include "csvtemplate.h"
#include "csvtemplatefactory.h"
#include "inputformatimpls.h"

using namespace KABC;

///////////////////////////////////////////////////////////////////////////////

QString UIDInput::description() const
{
    return i18n("Interprets input as a unique KABC contact identifier");
}

///////////////////////////////////////////////////////////////////////////////

bool UIDInput::setOptions(const QByteArray& options)
{
    Q_UNUSED(options)
    return false;
}

///////////////////////////////////////////////////////////////////////////////

bool UIDInput::setCodec(QTextCodec* codec)
{
    if (codec == 0) return false;

    m_codec = codec;

    return true;
}

///////////////////////////////////////////////////////////////////////////////

KABC::Addressee UIDInput::readAddressee(std::istream& stream)
{
    if (stream.bad()) return KABC::Addressee();

    KABC::Addressee addressee;

    std::string line;
    getline(stream, line);
    while (line.empty() && !stream.eof())
    {
        getline(stream, line);
    }

    if (stream.eof()) return addressee;

    QString uid = m_codec->toUnicode(line.c_str(), line.size());

    addressee.setUid(uid);

    return addressee;
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

VCardInput::VCardInput() : m_converter(0)
{
    m_converter = new VCardConverter();
}

///////////////////////////////////////////////////////////////////////////////

QString VCardInput::description() const
{
    return i18n("Interprets input as VCard data");
}

///////////////////////////////////////////////////////////////////////////////

VCardInput::~VCardInput()
{
    delete m_converter;
}

///////////////////////////////////////////////////////////////////////////////

bool VCardInput::setOptions(const QByteArray& options)
{
    Q_UNUSED(options)
    return false;
}

///////////////////////////////////////////////////////////////////////////////

bool VCardInput::setCodec(QTextCodec* codec)
{
    if (codec == 0) return false;

    m_codec = codec;

    QString codecName = QString::fromAscii(m_codec->name());

    if (codecName != QString::fromUtf8("UTF-8"))
    {
        QString warning = i18n("Warning: using codec '%1' with input format vcard, "
                               "but vCards are usually expected to be in UTF-8.",
                               codecName);

        std::cerr << warning.toLocal8Bit().data() << std::endl;
    }

    return true;
}

///////////////////////////////////////////////////////////////////////////////

KABC::Addressee VCardInput::readAddressee(std::istream& stream)
{
    if (stream.bad()) return KABC::Addressee();

    std::string line;
    // read any empty lines
    while (line.empty() && !stream.eof())
    {
        getline(stream, line);
    }
    if (line.empty()) return KABC::Addressee();

    QString input = m_codec->toUnicode(line.c_str(), line.size());
    QString inputLine;
    while (!inputLine.isEmpty() || !stream.eof())
    {
        getline(stream, line);
        inputLine = m_codec->toUnicode(line.c_str(), line.size());

        input += inputLine;
        input += '\n';
        if (inputLine.startsWith("END:VCARD")) break;
    }

    return m_converter->parseVCard(input.toUtf8());
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

QString EmailInput::description() const
{
    return i18n("Interprets input as email and optional name");
}

///////////////////////////////////////////////////////////////////////////////

bool EmailInput::setOptions(const QByteArray& options)
{
    Q_UNUSED(options)
    return false;
}

///////////////////////////////////////////////////////////////////////////////

bool EmailInput::setCodec(QTextCodec* codec)
{
    if (codec == 0) return false;

    m_codec = codec;

    return true;
}

///////////////////////////////////////////////////////////////////////////////

KABC::Addressee EmailInput::readAddressee(std::istream& stream)
{
    if (stream.bad()) return KABC::Addressee();

    KABC::Addressee addressee;

    std::string line;
    getline(stream, line);
    while (line.empty() && !stream.eof())
    {
        getline(stream, line);
    }

    if (stream.eof()) return addressee;

    QString rawEmail = m_codec->toUnicode(line.c_str(), line.size());
    QString name;
    QString email;
    KABC::Addressee::parseEmailAddress(rawEmail, name, email);

    if (!email.isEmpty() && email.indexOf("@") != -1)
    {
        addressee.insertEmail(email, true);

        if (!name.isEmpty())
        {
            addressee.setNameFromString(name);
        }

        addressee.setUid(QString::null);	//krazy:exclude=nullstrassign for old broken gcc
    }

    return addressee;
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

QString SearchInput::description() const
{
    return i18n("Tries to get email and name from input,\n\t\t"
                "otherwise sets input text for both");
}

///////////////////////////////////////////////////////////////////////////////

bool SearchInput::setOptions(const QByteArray& options)
{
    Q_UNUSED(options)
    return false;
}

///////////////////////////////////////////////////////////////////////////////

bool SearchInput::setCodec(QTextCodec* codec)
{
    if (codec == 0) return false;

    m_codec = codec;

    return true;
}

///////////////////////////////////////////////////////////////////////////////

KABC::Addressee SearchInput::readAddressee(std::istream& stream)
{
    if (stream.bad()) return KABC::Addressee();

    KABC::Addressee addressee;

    std::string line;
    getline(stream, line);
    while (line.empty() && !stream.eof())
    {
        getline(stream, line);
    }

    if (stream.eof()) return addressee;

    QString rawEmail = m_codec->toUnicode(line.c_str(), line.size());
    QString name;
    QString email;
    KABC::Addressee::parseEmailAddress(rawEmail, name, email);

    if (email.isEmpty() || email.indexOf("@") == -1)
    {
        addressee.insertEmail(rawEmail, true);
    }
    else
    {
        addressee.insertEmail(email, true);
    }

    if (!name.isEmpty())
    {
        addressee.setNameFromString(name);
    }
    else
    {
        addressee.setNameFromString(rawEmail);
    }

    addressee.setUid(QString::null);	//krazy:exclude=nullstrassign for old broken gcc

    return addressee;
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

QString NameInput::description() const
{
    return i18n("Interprets the input as a name.\n\t\t"
                "Recommended format is 'lastname, firstname middlename'");
}

///////////////////////////////////////////////////////////////////////////////

bool NameInput::setOptions(const QByteArray& options)
{
    Q_UNUSED(options)
    return false;
}

///////////////////////////////////////////////////////////////////////////////

bool NameInput::setCodec(QTextCodec* codec)
{
    if (codec == 0) return false;

    m_codec = codec;

    return true;
}

///////////////////////////////////////////////////////////////////////////////

KABC::Addressee NameInput::readAddressee(std::istream& stream)
{
    if (stream.bad()) return KABC::Addressee();

    KABC::Addressee addressee;

    std::string line;
    getline(stream, line);
    while (line.empty() && !stream.eof())
    {
        getline(stream, line);
    }

    if (stream.eof()) return addressee;

    QString name = m_codec->toUnicode(line.c_str(), line.size());

    if (!name.isEmpty())
    {
        addressee.setNameFromString(name);
    }

    addressee.setUid(QString::null);	//krazy:exclude=nullstrassign for old broken gcc

    return addressee;
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

CSVInput::CSVInput(CSVTemplateFactory* templateFactory)
    : m_codec(0), m_template(0), m_templateFactory(templateFactory)
{
}

///////////////////////////////////////////////////////////////////////////////

QString CSVInput::description() const
{
    return i18n("Interprets the input as a delimiter separated list of fields.");
}

///////////////////////////////////////////////////////////////////////////////

bool CSVInput::setOptions(const QByteArray& options)
{
    QString name = QString::fromLocal8Bit(options);
    if (name.isEmpty()) return false;

    m_template = m_templateFactory->createCachedTemplate(name);

    return m_template != 0;
}

///////////////////////////////////////////////////////////////////////////////

QString CSVInput::optionUsage() const
{
    QString usage =
        i18n("Specify one of the following CSV templates:");

    usage += '\n';

    const QMap<QString, QString> templateNames = m_templateFactory->templateNames();

    QMap<QString, QString>::const_iterator it    = templateNames.begin();
    QMap<QString, QString>::const_iterator endIt = templateNames.end();
    for (; it != endIt; ++it)
    {
        QString name = it.key();
        QString templateName = it.value();

        usage += name;

        usage += name.length() < 8 ? "\t\t" : "\t";

        usage += templateName;

        usage += '\n';
    }

    return usage;
}

///////////////////////////////////////////////////////////////////////////////

bool CSVInput::setCodec(QTextCodec* codec)
{
    if (codec == 0) return false;

    m_codec = codec;

    return true;
}

///////////////////////////////////////////////////////////////////////////////

KABC::Addressee CSVInput::readAddressee(std::istream& stream)
{
    if (stream.bad()) return KABC::Addressee();

    if (m_template == 0) m_template = CSVTemplate::defaultTemplate();

    KABC::Addressee addressee;

    std::string line;
    getline(stream, line);
    while (line.empty() && !stream.eof())
    {
        getline(stream, line);
    }

    if (stream.eof()) return addressee;

    QString values = m_codec->toUnicode(line.c_str(), line.size());

    if (!values.isEmpty())
    {
        QStringList list = split(values);

        QStringList::const_iterator it    = list.begin();
        QStringList::const_iterator endIt = list.end();
        for (int i = 0; it != endIt; ++it, ++i)
        {
            m_template->setFieldText(i, addressee, *it);
        }
    }

    return addressee;
}

///////////////////////////////////////////////////////////////////////////////

QStringList CSVInput::split(const QString& values) const
{
    const QString quote     = m_template->quote();
    const QString delimiter = m_template->delimiter();

    if (quote.isEmpty()) return values.split(delimiter, QString::KeepEmptyParts);

    QString remaining = values;

    QStringList list;
    bool quoted = false;
    while (!remaining.isEmpty())
    {
        if (quoted)
        {
            int quoteIndex = remaining.indexOf(quote);
            if (quoteIndex >= 0)
            {
                list.append(remaining.left(quoteIndex));
                remaining = remaining.mid(quoteIndex + quote.length());

                if (remaining.indexOf(delimiter) == 0)
                {
                    remaining = remaining.mid(delimiter.length());
                }
            }
            else
            {
                list.append(remaining);
                remaining.clear();
            }

            quoted = false;
        }
        else
        {
            int quoteIndex = remaining.indexOf(quote);

            if (quoteIndex == 0)
            {
                quoted = true;

                remaining = remaining.mid(quote.length());
            }
            else
            {
                int delimiterIndex = remaining.indexOf(delimiter);
                if (delimiterIndex >= 0)
                {
                    list.append(remaining.left(delimiterIndex));
                    remaining = remaining.mid(delimiterIndex + delimiter.length());
                }
                else
                {
                    list.append(remaining);
                    remaining.clear();
                }
            }
        }
    }

    return list;
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

class DialogInputPrivate
{
public:
    DialogInputPrivate() : showDialog(true) {}

public:
    bool showDialog;
    KABC::AddresseeList addresseeList;
};

///////////////////////////////////////////////////////////////////////////////

DialogInput::DialogInput() : m_private(new DialogInputPrivate())
{
}

///////////////////////////////////////////////////////////////////////////////

DialogInput::~DialogInput()
{
    delete m_private;
}

///////////////////////////////////////////////////////////////////////////////

QString DialogInput::description() const
{
    return i18n("Select contacts in a dialog instead of reading input text");
}

///////////////////////////////////////////////////////////////////////////////

bool DialogInput::setOptions(const QByteArray& options)
{
    Q_UNUSED(options)
    return false;
}

///////////////////////////////////////////////////////////////////////////////

bool DialogInput::setCodec(QTextCodec* codec)
{
    if (codec == 0) return false;

    return true;
}

///////////////////////////////////////////////////////////////////////////////

KABC::Addressee DialogInput::readAddressee(std::istream& stream)
{
    KABC::Addressee addressee;

    // on first call show dialog for addressee selection
    if (m_private->showDialog)
    {
        m_private->addresseeList = KABC::AddresseeDialog::getAddressees(0);
        m_private->showDialog = false;
    }

    // As long as there are addressees left in the selection list take the first
    // one and use its UID as the output of this InputFormat.
    // When all selected addressees have been processed, mark stream as empty
    if (!m_private->addresseeList.isEmpty())
    {
        KABC::Addressee nextAddressee = m_private->addresseeList.front();

        // using the UID avoid multiple matches by name or email of the addressee
        if (!nextAddressee.isEmpty()) addressee.setUid(nextAddressee.uid());

        m_private->addresseeList.pop_front();
    }
    else
        stream.setstate(std::ios_base::eofbit);

    return addressee;
}

// End of file
<commit_msg>getline() removes the newline but this is expected by the parser later on. Add it to the first line just like we do for any later one<commit_after>//
//  Copyright (C) 2005 - 2006 Kevin Krammer <kevin.krammer@gmx.at>
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
//

// standard includes
#include <iostream>
#include <string>

// Qt includes
#include <QTextCodec>

// KDE includes
#include <klocale.h>

// KABC includes
#include <kabc/addressee.h>
#include <kabc/addresseedialog.h>
#include <kabc/addresseelist.h>
#include <kabc/vcardconverter.h>

// local includes
#include "csvtemplate.h"
#include "csvtemplatefactory.h"
#include "inputformatimpls.h"

using namespace KABC;

///////////////////////////////////////////////////////////////////////////////

QString UIDInput::description() const
{
    return i18n("Interprets input as a unique KABC contact identifier");
}

///////////////////////////////////////////////////////////////////////////////

bool UIDInput::setOptions(const QByteArray& options)
{
    Q_UNUSED(options)
    return false;
}

///////////////////////////////////////////////////////////////////////////////

bool UIDInput::setCodec(QTextCodec* codec)
{
    if (codec == 0) return false;

    m_codec = codec;

    return true;
}

///////////////////////////////////////////////////////////////////////////////

KABC::Addressee UIDInput::readAddressee(std::istream& stream)
{
    if (stream.bad()) return KABC::Addressee();

    KABC::Addressee addressee;

    std::string line;
    getline(stream, line);
    while (line.empty() && !stream.eof())
    {
        getline(stream, line);
    }

    if (stream.eof()) return addressee;

    QString uid = m_codec->toUnicode(line.c_str(), line.size());

    addressee.setUid(uid);

    return addressee;
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

VCardInput::VCardInput() : m_converter(0)
{
    m_converter = new VCardConverter();
}

///////////////////////////////////////////////////////////////////////////////

QString VCardInput::description() const
{
    return i18n("Interprets input as VCard data");
}

///////////////////////////////////////////////////////////////////////////////

VCardInput::~VCardInput()
{
    delete m_converter;
}

///////////////////////////////////////////////////////////////////////////////

bool VCardInput::setOptions(const QByteArray& options)
{
    Q_UNUSED(options)
    return false;
}

///////////////////////////////////////////////////////////////////////////////

bool VCardInput::setCodec(QTextCodec* codec)
{
    if (codec == 0) return false;

    m_codec = codec;

    QString codecName = QString::fromAscii(m_codec->name());

    if (codecName != QString::fromUtf8("UTF-8"))
    {
        QString warning = i18n("Warning: using codec '%1' with input format vcard, "
                               "but vCards are usually expected to be in UTF-8.",
                               codecName);

        std::cerr << warning.toLocal8Bit().data() << std::endl;
    }

    return true;
}

///////////////////////////////////////////////////////////////////////////////

KABC::Addressee VCardInput::readAddressee(std::istream& stream)
{
    if (stream.bad()) return KABC::Addressee();

    std::string line;
    // read any empty lines
    while (line.empty() && !stream.eof())
    {
        getline(stream, line);
    }
    if (line.empty()) return KABC::Addressee();

    QString input = m_codec->toUnicode(line.c_str(), line.size());
    input += '\n';

    QString inputLine;
    while (!inputLine.isEmpty() || !stream.eof())
    {
        getline(stream, line);
        inputLine = m_codec->toUnicode(line.c_str(), line.size());

        input += inputLine;
        input += '\n';
        if (inputLine.startsWith("END:VCARD")) break;
    }

    return m_converter->parseVCard(input.toUtf8());
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

QString EmailInput::description() const
{
    return i18n("Interprets input as email and optional name");
}

///////////////////////////////////////////////////////////////////////////////

bool EmailInput::setOptions(const QByteArray& options)
{
    Q_UNUSED(options)
    return false;
}

///////////////////////////////////////////////////////////////////////////////

bool EmailInput::setCodec(QTextCodec* codec)
{
    if (codec == 0) return false;

    m_codec = codec;

    return true;
}

///////////////////////////////////////////////////////////////////////////////

KABC::Addressee EmailInput::readAddressee(std::istream& stream)
{
    if (stream.bad()) return KABC::Addressee();

    KABC::Addressee addressee;

    std::string line;
    getline(stream, line);
    while (line.empty() && !stream.eof())
    {
        getline(stream, line);
    }

    if (stream.eof()) return addressee;

    QString rawEmail = m_codec->toUnicode(line.c_str(), line.size());
    QString name;
    QString email;
    KABC::Addressee::parseEmailAddress(rawEmail, name, email);

    if (!email.isEmpty() && email.indexOf("@") != -1)
    {
        addressee.insertEmail(email, true);

        if (!name.isEmpty())
        {
            addressee.setNameFromString(name);
        }

        addressee.setUid(QString::null);	//krazy:exclude=nullstrassign for old broken gcc
    }

    return addressee;
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

QString SearchInput::description() const
{
    return i18n("Tries to get email and name from input,\n\t\t"
                "otherwise sets input text for both");
}

///////////////////////////////////////////////////////////////////////////////

bool SearchInput::setOptions(const QByteArray& options)
{
    Q_UNUSED(options)
    return false;
}

///////////////////////////////////////////////////////////////////////////////

bool SearchInput::setCodec(QTextCodec* codec)
{
    if (codec == 0) return false;

    m_codec = codec;

    return true;
}

///////////////////////////////////////////////////////////////////////////////

KABC::Addressee SearchInput::readAddressee(std::istream& stream)
{
    if (stream.bad()) return KABC::Addressee();

    KABC::Addressee addressee;

    std::string line;
    getline(stream, line);
    while (line.empty() && !stream.eof())
    {
        getline(stream, line);
    }

    if (stream.eof()) return addressee;

    QString rawEmail = m_codec->toUnicode(line.c_str(), line.size());
    QString name;
    QString email;
    KABC::Addressee::parseEmailAddress(rawEmail, name, email);

    if (email.isEmpty() || email.indexOf("@") == -1)
    {
        addressee.insertEmail(rawEmail, true);
    }
    else
    {
        addressee.insertEmail(email, true);
    }

    if (!name.isEmpty())
    {
        addressee.setNameFromString(name);
    }
    else
    {
        addressee.setNameFromString(rawEmail);
    }

    addressee.setUid(QString::null);	//krazy:exclude=nullstrassign for old broken gcc

    return addressee;
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

QString NameInput::description() const
{
    return i18n("Interprets the input as a name.\n\t\t"
                "Recommended format is 'lastname, firstname middlename'");
}

///////////////////////////////////////////////////////////////////////////////

bool NameInput::setOptions(const QByteArray& options)
{
    Q_UNUSED(options)
    return false;
}

///////////////////////////////////////////////////////////////////////////////

bool NameInput::setCodec(QTextCodec* codec)
{
    if (codec == 0) return false;

    m_codec = codec;

    return true;
}

///////////////////////////////////////////////////////////////////////////////

KABC::Addressee NameInput::readAddressee(std::istream& stream)
{
    if (stream.bad()) return KABC::Addressee();

    KABC::Addressee addressee;

    std::string line;
    getline(stream, line);
    while (line.empty() && !stream.eof())
    {
        getline(stream, line);
    }

    if (stream.eof()) return addressee;

    QString name = m_codec->toUnicode(line.c_str(), line.size());

    if (!name.isEmpty())
    {
        addressee.setNameFromString(name);
    }

    addressee.setUid(QString::null);	//krazy:exclude=nullstrassign for old broken gcc

    return addressee;
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

CSVInput::CSVInput(CSVTemplateFactory* templateFactory)
    : m_codec(0), m_template(0), m_templateFactory(templateFactory)
{
}

///////////////////////////////////////////////////////////////////////////////

QString CSVInput::description() const
{
    return i18n("Interprets the input as a delimiter separated list of fields.");
}

///////////////////////////////////////////////////////////////////////////////

bool CSVInput::setOptions(const QByteArray& options)
{
    QString name = QString::fromLocal8Bit(options);
    if (name.isEmpty()) return false;

    m_template = m_templateFactory->createCachedTemplate(name);

    return m_template != 0;
}

///////////////////////////////////////////////////////////////////////////////

QString CSVInput::optionUsage() const
{
    QString usage =
        i18n("Specify one of the following CSV templates:");

    usage += '\n';

    const QMap<QString, QString> templateNames = m_templateFactory->templateNames();

    QMap<QString, QString>::const_iterator it    = templateNames.begin();
    QMap<QString, QString>::const_iterator endIt = templateNames.end();
    for (; it != endIt; ++it)
    {
        QString name = it.key();
        QString templateName = it.value();

        usage += name;

        usage += name.length() < 8 ? "\t\t" : "\t";

        usage += templateName;

        usage += '\n';
    }

    return usage;
}

///////////////////////////////////////////////////////////////////////////////

bool CSVInput::setCodec(QTextCodec* codec)
{
    if (codec == 0) return false;

    m_codec = codec;

    return true;
}

///////////////////////////////////////////////////////////////////////////////

KABC::Addressee CSVInput::readAddressee(std::istream& stream)
{
    if (stream.bad()) return KABC::Addressee();

    if (m_template == 0) m_template = CSVTemplate::defaultTemplate();

    KABC::Addressee addressee;

    std::string line;
    getline(stream, line);
    while (line.empty() && !stream.eof())
    {
        getline(stream, line);
    }

    if (stream.eof()) return addressee;

    QString values = m_codec->toUnicode(line.c_str(), line.size());

    if (!values.isEmpty())
    {
        QStringList list = split(values);

        QStringList::const_iterator it    = list.begin();
        QStringList::const_iterator endIt = list.end();
        for (int i = 0; it != endIt; ++it, ++i)
        {
            m_template->setFieldText(i, addressee, *it);
        }
    }

    return addressee;
}

///////////////////////////////////////////////////////////////////////////////

QStringList CSVInput::split(const QString& values) const
{
    const QString quote     = m_template->quote();
    const QString delimiter = m_template->delimiter();

    if (quote.isEmpty()) return values.split(delimiter, QString::KeepEmptyParts);

    QString remaining = values;

    QStringList list;
    bool quoted = false;
    while (!remaining.isEmpty())
    {
        if (quoted)
        {
            int quoteIndex = remaining.indexOf(quote);
            if (quoteIndex >= 0)
            {
                list.append(remaining.left(quoteIndex));
                remaining = remaining.mid(quoteIndex + quote.length());

                if (remaining.indexOf(delimiter) == 0)
                {
                    remaining = remaining.mid(delimiter.length());
                }
            }
            else
            {
                list.append(remaining);
                remaining.clear();
            }

            quoted = false;
        }
        else
        {
            int quoteIndex = remaining.indexOf(quote);

            if (quoteIndex == 0)
            {
                quoted = true;

                remaining = remaining.mid(quote.length());
            }
            else
            {
                int delimiterIndex = remaining.indexOf(delimiter);
                if (delimiterIndex >= 0)
                {
                    list.append(remaining.left(delimiterIndex));
                    remaining = remaining.mid(delimiterIndex + delimiter.length());
                }
                else
                {
                    list.append(remaining);
                    remaining.clear();
                }
            }
        }
    }

    return list;
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

class DialogInputPrivate
{
public:
    DialogInputPrivate() : showDialog(true) {}

public:
    bool showDialog;
    KABC::AddresseeList addresseeList;
};

///////////////////////////////////////////////////////////////////////////////

DialogInput::DialogInput() : m_private(new DialogInputPrivate())
{
}

///////////////////////////////////////////////////////////////////////////////

DialogInput::~DialogInput()
{
    delete m_private;
}

///////////////////////////////////////////////////////////////////////////////

QString DialogInput::description() const
{
    return i18n("Select contacts in a dialog instead of reading input text");
}

///////////////////////////////////////////////////////////////////////////////

bool DialogInput::setOptions(const QByteArray& options)
{
    Q_UNUSED(options)
    return false;
}

///////////////////////////////////////////////////////////////////////////////

bool DialogInput::setCodec(QTextCodec* codec)
{
    if (codec == 0) return false;

    return true;
}

///////////////////////////////////////////////////////////////////////////////

KABC::Addressee DialogInput::readAddressee(std::istream& stream)
{
    KABC::Addressee addressee;

    // on first call show dialog for addressee selection
    if (m_private->showDialog)
    {
        m_private->addresseeList = KABC::AddresseeDialog::getAddressees(0);
        m_private->showDialog = false;
    }

    // As long as there are addressees left in the selection list take the first
    // one and use its UID as the output of this InputFormat.
    // When all selected addressees have been processed, mark stream as empty
    if (!m_private->addresseeList.isEmpty())
    {
        KABC::Addressee nextAddressee = m_private->addresseeList.front();

        // using the UID avoid multiple matches by name or email of the addressee
        if (!nextAddressee.isEmpty()) addressee.setUid(nextAddressee.uid());

        m_private->addresseeList.pop_front();
    }
    else
        stream.setstate(std::ios_base::eofbit);

    return addressee;
}

// End of file
<|endoftext|>


<commit_before>/**
 * @file
 *      exercise_05_17.cpp
 * @author
 *      Henrik Samuelsson, henrik.samuelsson(at)gmail.com
 * @brief
 *      Exercise 5.17 from the book C++ Primer (5th edition).
 */
#include <iostream>
#include <string>
#include <vector>

using std::cout;
using std::endl;
using std::vector;

bool isPrefix(vector<int> v1, vector<int> v2);

int main() {
    // Test vectors used to verify that the program is correct.
    vector<int> test_vec_1 {0, 1, 1, 2};
    vector<int> test_vec_2 {0, 1, 1, 2, 3, 5, 8};
    vector<int> test_vec_3 {0, 1, 1, 2, 4, 8, 7, 9};

    cout << isPrefix(test_vec_1, test_vec_2) << endl;
    cout << isPrefix(test_vec_1, test_vec_3) << endl;
    cout << isPrefix(test_vec_2, test_vec_3) << endl;
    cout << isPrefix(test_vec_2, test_vec_1) << endl;
    cout << isPrefix(test_vec_3, test_vec_1) << endl;
    cout << isPrefix(test_vec_3, test_vec_2) << endl;
}

bool isPrefix(vector<int> v1, vector<int> v2) {
    std::vector<int>::size_type length, i;
    length = v1.size() < v2.size() ? v1.size() : v2.size();

    for (i = 0; i < length; i++) {
        if (v1[i] != v2[i]) {
            return false;
        }
    }

     // All elements in the prefix must have been equal if we end up here.
    return true;
}<commit_msg>Initial commit of exercise 5.17.<commit_after>/**
 * @file
 *      exercise_05_17.cpp
 * @author
 *      Henrik Samuelsson, henrik.samuelsson(at)gmail.com
 * @brief
 *      Exercise 5.17 from the book C++ Primer (5th edition).
 */
#include <iostream>
#include <string>
#include <vector>

using std::cout;
using std::endl;
using std::vector;

bool isPrefix(vector<int> v1, vector<int> v2);

int main() {
    // Test vectors used to verify that the program is correct.
    vector<int> test_vec_1 {0, 1, 1, 2};
    vector<int> test_vec_2 {0, 1, 1, 2, 3, 5, 8};
    vector<int> test_vec_3 {0, 1, 1, 2, 4, 8, 7, 9};

    cout << isPrefix(test_vec_1, test_vec_2) << endl;
    cout << isPrefix(test_vec_1, test_vec_3) << endl;
    cout << isPrefix(test_vec_2, test_vec_3) << endl;
    cout << isPrefix(test_vec_2, test_vec_1) << endl;
    cout << isPrefix(test_vec_3, test_vec_1) << endl;
    cout << isPrefix(test_vec_3, test_vec_2) << endl;
}

bool isPrefix(vector<int> v1, vector<int> v2) {
    std::vector<int>::size_type length, i;
    length = v1.size() < v2.size() ? v1.size() : v2.size();

    for (i = 0; i < length; i++) {
        if (v1[i] != v2[i]) {
            return false;
        }
    }

     // All elements in the prefix must have been equal if we end up here.
    return true;
}
<|endoftext|>


<commit_before>//=============================================================================
// ■ VMStateControl.cpp
//-----------------------------------------------------------------------------
//   VMDE的状态机管理
//=============================================================================

#include "global.hpp"

VMStateControl::StateMachineStruct VMStateControl::StateMachine;

void VMStateControl::ChangeVertexArray (GLuint index) {
	if (index != StateMachine.VERTEX_ARRAY) {
		StateMachine.VERTEX_ARRAY = index;
		glBindVertexArray(index);
	}
}

void VMStateControl::ChangeElementArrayBuffer (GLuint index) {
	if (index != StateMachine.ELEMENT_ARRAY_BUFFER) {
		StateMachine.ELEMENT_ARRAY_BUFFER = index;
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index);
	}
}

void VMStateControl::ChangeArrayBuffer (GLuint index) {
	if (index != StateMachine.ARRAY_BUFFER) {
		StateMachine.ARRAY_BUFFER = index;
		glBindBuffer(GL_ARRAY_BUFFER, index);
	}
}

void VMStateControl::ChangeTexture2D (GLuint index) {
	if (index != StateMachine.TEXTURE_2D) {
		StateMachine.TEXTURE_2D = index;
		glBindTexture(GL_TEXTURE_2D, index);
	}
}

void VMStateControl::ChangeUniformBuffer (GLuint index) {
	if (index != StateMachine.UNIFORM_BUFFER) {
		StateMachine.UNIFORM_BUFFER = index;
		glBindBuffer(GL_UNIFORM_BUFFER, index);
	}
}

void VMStateControl::ChangeShaderProgram (GLuint index) {
	if (index != StateMachine.Shader_Program) {
		StateMachine.Shader_Program = index;
		glUseProgram(index);
	}
}

void VMStateControl::enable_cullface() {
	if (!StateMachine.CULL_FACE) {
		StateMachine.CULL_FACE = true;
		glEnable(GL_CULL_FACE);
	}
}

void VMStateControl::disable_cullface() {
	if (StateMachine.CULL_FACE) {
		StateMachine.CULL_FACE = false;
		glDisable(GL_CULL_FACE);
	}
}

void VMStateControl::enable_depth_test() {
	if (!StateMachine.DEPTH_TEST) {
		StateMachine.DEPTH_TEST = true;
		glEnable(GL_DEPTH_TEST);
	}
}

void VMStateControl::disable_depth_test() {
	if (StateMachine.DEPTH_TEST) {
		StateMachine.DEPTH_TEST = false;
		glDisable(GL_DEPTH_TEST);
	}
}

void VMStateControl::enable_blend() {
	if (!StateMachine.BLEND) {
		StateMachine.BLEND = true;
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	}
}

void VMStateControl::disable_blend() {
	if (StateMachine.BLEND) {
		StateMachine.BLEND = false;
		glDisable(GL_BLEND);
	}
}

void VMStateControl::render_mode_wireframe() {
	if (StateMachine.PolygonMode != GL_LINE) {
		StateMachine.PolygonMode = GL_LINE;
		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
	}
}

void VMStateControl::render_mode_fill() {
	if (StateMachine.PolygonMode != GL_FILL) {
		StateMachine.PolygonMode = GL_FILL;
		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
	}
}

void VMStateControl::init_graphics_state () {
	// 设置当前窗口GL上下文
	glfwMakeContextCurrent(window);
	// 垂直同步，拒绝鬼畜
	glfwSwapInterval(1);

	// 初始化GLEW
	glewExperimental = GL_TRUE;
	if (glewInit() != GLEW_OK) {
		log("glewInit() (GLEW Initialization) failed.");
		return;
	}
	#ifdef __APPLE__
		if (glfwExtensionSupported("GL_APPLE_uniform_buffer_object") != GLFW_TRUE) {
			glfwTerminate();
			log("Your computer need OpenGL 3.3 with Uniform Buffer Object (UBO).");
			log("Uniform buffer object is NOT detected properlly");
			return;
		}
		log("UBO: Apple Uniform Buffer Object （__APPLE__)");
	#else
		if (glfwExtensionSupported("GL_ARB_uniform_buffer_object") != GLFW_TRUE) {
			glfwTerminate();
			error("Your computer need OpenGL 3.3 with Uniform Buffer Object (UBO).");
			return;
		}
		log("UBO: GL33 Uniform Buffer Object");
	#endif

	memset (&StateMachine, 0, sizeof(StateMachine));
}
<commit_msg>General format improvement<commit_after>//=============================================================================
// ■ VMStateControl.cpp
//-----------------------------------------------------------------------------
//   VMDE的状态机管理
//=============================================================================

#include "global.hpp"

VMStateControl::StateMachineStruct VMStateControl::StateMachine;

void VMStateControl::ChangeVertexArray (GLuint index) {
	if (index != StateMachine.VERTEX_ARRAY) {
		StateMachine.VERTEX_ARRAY = index;
		glBindVertexArray(index);
	}
}

void VMStateControl::ChangeElementArrayBuffer (GLuint index) {
	if (index != StateMachine.ELEMENT_ARRAY_BUFFER) {
		StateMachine.ELEMENT_ARRAY_BUFFER = index;
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index);
	}
}

void VMStateControl::ChangeArrayBuffer (GLuint index) {
	if (index != StateMachine.ARRAY_BUFFER) {
		StateMachine.ARRAY_BUFFER = index;
		glBindBuffer(GL_ARRAY_BUFFER, index);
	}
}

void VMStateControl::ChangeTexture2D (GLuint index) {
	if (index != StateMachine.TEXTURE_2D) {
		StateMachine.TEXTURE_2D = index;
		glBindTexture(GL_TEXTURE_2D, index);
	}
}

void VMStateControl::ChangeUniformBuffer (GLuint index) {
	if (index != StateMachine.UNIFORM_BUFFER) {
		StateMachine.UNIFORM_BUFFER = index;
		glBindBuffer(GL_UNIFORM_BUFFER, index);
	}
}

void VMStateControl::ChangeShaderProgram (GLuint index) {
	if (index != StateMachine.Shader_Program) {
		StateMachine.Shader_Program = index;
		glUseProgram(index);
	}
}

void VMStateControl::enable_cullface() {
	if (!StateMachine.CULL_FACE) {
		StateMachine.CULL_FACE = true;
		glEnable(GL_CULL_FACE);
	}
}

void VMStateControl::disable_cullface() {
	if (StateMachine.CULL_FACE) {
		StateMachine.CULL_FACE = false;
		glDisable(GL_CULL_FACE);
	}
}

void VMStateControl::enable_depth_test() {
	if (!StateMachine.DEPTH_TEST) {
		StateMachine.DEPTH_TEST = true;
		glEnable(GL_DEPTH_TEST);
	}
}

void VMStateControl::disable_depth_test() {
	if (StateMachine.DEPTH_TEST) {
		StateMachine.DEPTH_TEST = false;
		glDisable(GL_DEPTH_TEST);
	}
}

void VMStateControl::enable_blend() {
	if (!StateMachine.BLEND) {
		StateMachine.BLEND = true;
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	}
}

void VMStateControl::disable_blend() {
	if (StateMachine.BLEND) {
		StateMachine.BLEND = false;
		glDisable(GL_BLEND);
	}
}

void VMStateControl::render_mode_wireframe() {
	if (StateMachine.PolygonMode != GL_LINE) {
		StateMachine.PolygonMode = GL_LINE;
		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
	}
}

void VMStateControl::render_mode_fill() {
	if (StateMachine.PolygonMode != GL_FILL) {
		StateMachine.PolygonMode = GL_FILL;
		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
	}
}

void VMStateControl::init_graphics_state() {
	// 设置当前窗口GL上下文
	glfwMakeContextCurrent(window);
	// 垂直同步，拒绝鬼畜
	glfwSwapInterval(1);

	// 初始化GLEW
	glewExperimental = GL_TRUE;
	if (glewInit() != GLEW_OK) {
		log("glewInit() (GLEW Initialization) failed.");
		return;
	}
	if (glfwExtensionSupported(
		#ifdef __APPLE__
			"GL_APPLE_uniform_buffer_object"
		#else
			"GL_ARB_uniform_buffer_object"
		#endif
	) != GLFW_TRUE) {
		glfwTerminate();
		log("Your computer need OpenGL 3.3 with Uniform Buffer Object (UBO).");
		error("Uniform buffer object is NOT detected properly.");
	}
	log(
		#ifdef __APPLE__
			"UBO: Apple Uniform Buffer Object (__APPLE__)"
		#else
			"UBO: GL33 Uniform Buffer Object"
		#endif
	);

	memset(&StateMachine, 0, sizeof(StateMachine));
}
<|endoftext|>


<commit_before>#include "FanCalculatorScene.h"
#include "../mahjong-algorithm/stringify.h"
#include "../mahjong-algorithm/fan_calculator.h"
#include "../widget/TilePickWidget.h"
#include "../widget/ExtraInfoWidget.h"
#include "../widget/AlertView.h"
#include "../widget/TilesKeyboard.h"

USING_NS_CC;

Scene *FanCalculatorScene::createScene() {
    auto scene = Scene::create();
    auto layer = FanCalculatorScene::create();
    scene->addChild(layer);
    return scene;
}

bool FanCalculatorScene::init() {
    if (UNLIKELY(!BaseLayer::initWithTitle("国标麻将算番器"))) {
        return false;
    }

    Size visibleSize = Director::getInstance()->getVisibleSize();
    Vec2 origin = Director::getInstance()->getVisibleOrigin();

    // 选牌面板
    TilePickWidget *tilePicker = TilePickWidget::create();
    const Size &widgetSize = tilePicker->getContentSize();
    this->addChild(tilePicker);
    tilePicker->setPosition(Vec2(origin.x + visibleSize.width * 0.5f,
        origin.y + visibleSize.height - 35 - widgetSize.height * 0.5f));
    _tilePicker = tilePicker;

    // 其他信息的相关控件
    ExtraInfoWidget *extraInfo = ExtraInfoWidget::create();
    const Size &extraSize = extraInfo->getContentSize();
    this->addChild(extraInfo);
    extraInfo->setPosition(Vec2(origin.x + visibleSize.width * 0.5f,
        origin.y + visibleSize.height - 35 - widgetSize.height - 5 - extraSize.height * 0.5f));
    _extraInfo = extraInfo;

    // 直接输入
    ui::Button *button = ui::Button::create("source_material/btn_square_highlighted.png", "source_material/btn_square_selected.png");
    button->setScale9Enabled(true);
    button->setContentSize(Size(55.0f, 20.0f));
    button->setTitleFontSize(12);
    button->setTitleText("直接输入");
    extraInfo->addChild(button);
    button->setPosition(Vec2(visibleSize.width - 40, 100.0f));
    button->addClickEventListener([this](Ref *) { showInputAlert(nullptr); });

    // 番算按钮
    button = ui::Button::create("source_material/btn_square_highlighted.png", "source_material/btn_square_selected.png");
    button->setScale9Enabled(true);
    button->setContentSize(Size(45.0f, 20.0f));
    button->setTitleFontSize(12);
    button->setTitleText("算番");
    extraInfo->addChild(button);
    button->setPosition(Vec2(visibleSize.width - 35, 10.0f));
    button->addClickEventListener([this](Ref *) { calculate(); });

    // 番种显示的Node
    Size areaSize(visibleSize.width, visibleSize.height - 35 - widgetSize.height - 5 - extraSize.height - 10);
    _fanAreaNode = Node::create();
    _fanAreaNode->setContentSize(areaSize);
    _fanAreaNode->setAnchorPoint(Vec2::ANCHOR_MIDDLE);
    _fanAreaNode->setIgnoreAnchorPointForPosition(false);
    this->addChild(_fanAreaNode);
    _fanAreaNode->setPosition(Vec2(origin.x + visibleSize.width * 0.5f, origin.y + areaSize.height * 0.5f + 5));

    tilePicker->setFixedPacksChangedCallback([tilePicker, extraInfo]() {
        extraInfo->refreshByKong(tilePicker->isFixedPacksContainsKong());
    });

    tilePicker->setWinTileChangedCallback([tilePicker, extraInfo]() {
        ExtraInfoWidget::RefreshByWinTile rt;
        rt.getWinTile = std::bind(&TilePickWidget::getServingTile, tilePicker);
        rt.isStandingTilesContainsServingTile = std::bind(&TilePickWidget::isStandingTilesContainsServingTile, tilePicker);
        rt.countServingTileInFixedPacks = std::bind(&TilePickWidget::countServingTileInFixedPacks, tilePicker);
        extraInfo->refreshByWinTile(rt);
    });

    return true;
}

cocos2d::Node *createFanResultNode(const long (&fan_table)[mahjong::FAN_TABLE_SIZE], int fontSize, float resultAreaWidth) {
    // 有n个番种，每行排2个
    long n = mahjong::FAN_TABLE_SIZE - std::count(std::begin(fan_table), std::end(fan_table), 0);
    long rows = (n >> 1) + (n & 1);  // 需要这么多行

    // 排列
    Node *node = Node::create();
    const int lineHeight = fontSize + 2;
    long resultAreaHeight = lineHeight * rows;  // 每行间隔2像素
    resultAreaHeight += (3 + lineHeight);  // 总计
    node->setContentSize(Size(resultAreaWidth, resultAreaHeight));

    long fan = 0;
    for (int i = 0, j = 0; i < n; ++i) {
        while (fan_table[++j] == 0) continue;

        int f = mahjong::fan_value_table[j];
        long n = fan_table[j];
        fan += f * n;
        std::string str = (n == 1) ? StringUtils::format("%s %d番\n", mahjong::fan_name[j], f)
            : StringUtils::format("%s %d番x%ld\n", mahjong::fan_name[j], f, fan_table[j]);

        // 创建label，每行排2个
        Label *fanName = Label::createWithSystemFont(str, "Arial", fontSize);
        fanName->setColor(Color3B(0x60, 0x60, 0x60));
        node->addChild(fanName);
        fanName->setAnchorPoint(Vec2::ANCHOR_MIDDLE_LEFT);
        div_t ret = div(i, 2);
        fanName->setPosition(Vec2(ret.rem == 0 ? 10.0f : resultAreaWidth * 0.5f, resultAreaHeight - lineHeight * (ret.quot + 1)));
    }

    Label *fanTotal = Label::createWithSystemFont(StringUtils::format("总计：%ld番", fan), "Arial", fontSize);
    fanTotal->setColor(Color3B::BLACK);
    node->addChild(fanTotal);
    fanTotal->setAnchorPoint(Vec2::ANCHOR_MIDDLE_LEFT);
    fanTotal->setPosition(Vec2(10.0f, lineHeight * 0.5f));

    return node;
}

void FanCalculatorScene::showInputAlert(const char *prevInput) {
    Size visibleSize = Director::getInstance()->getVisibleSize();
    const float width = visibleSize.width * 0.8f - 10;

    ui::Widget *rootWidget = ui::Widget::create();

    Label *label = Label::createWithSystemFont("使用说明：\n"
        "1." INPUT_GUIDE_STRING_1 "\n"
        "2." INPUT_GUIDE_STRING_2 "\n"
        "3." INPUT_GUIDE_STRING_3 "\n"
        "输入范例1：[EEEE][CCCC][FFFF][PPPP]NN\n"
        "输入范例2：1112345678999s9s\n"
        "输入范例3：WWWW 444s 45m678pFF6m\n", "Arial", 10);
    label->setColor(Color3B::BLACK);
    label->setDimensions(width, 0);
    rootWidget->addChild(label);

    // 输入手牌
    ui::EditBox *editBox = ui::EditBox::create(Size(width - 10, 20.0f), ui::Scale9Sprite::create("source_material/btn_square_normal.png"));
    editBox->setInputFlag(ui::EditBox::InputFlag::SENSITIVE);
    editBox->setInputMode(ui::EditBox::InputMode::SINGLE_LINE);
    editBox->setFontColor(Color4B::BLACK);
    editBox->setFontSize(12);
    editBox->setPlaceholderFontColor(Color4B::GRAY);
    editBox->setPlaceHolder("输入手牌");
    if (prevInput != nullptr) {
        editBox->setText(prevInput);
    }
    TilesKeyboard::hookEditBox(editBox);
    rootWidget->addChild(editBox);

    const Size &labelSize = label->getContentSize();
    rootWidget->setContentSize(Size(width, labelSize.height + 30));
    editBox->setPosition(Vec2(width * 0.5f, 10));
    label->setPosition(Vec2(width * 0.5f, labelSize.height * 0.5f + 30));

    AlertView::showWithNode("直接输入", rootWidget, [this, editBox]() {
        const char *input = editBox->getText();
        const char *errorStr = TilesKeyboard::parseInput(input,
            std::bind(&TilePickWidget::setData, _tilePicker, std::placeholders::_1, std::placeholders::_2));
        if (errorStr != nullptr) {
            const std::string str = input;
            AlertView::showWithMessage("直接输入牌", errorStr, 12, [this, str]() {
                showInputAlert(str.c_str());
            }, nullptr);
        }
    }, nullptr);
}

void FanCalculatorScene::calculate() {
    _fanAreaNode->removeAllChildren();

    const Size &fanAreaSize = _fanAreaNode->getContentSize();
    Vec2 pos(fanAreaSize.width * 0.5f, fanAreaSize.height * 0.5f);

    int flowerCnt = _extraInfo->getFlowerCount();
    if (flowerCnt > 8) {
        AlertView::showWithMessage("算番", "花牌数的范围为0~8", 12, nullptr, nullptr);
        return;
    }

    mahjong::hand_tiles_t hand_tiles;
    mahjong::tile_t win_tile;
    _tilePicker->getData(&hand_tiles, &win_tile);
    if (win_tile == 0) {
        AlertView::showWithMessage("算番", "牌张数错误", 12, nullptr, nullptr);
        return;
    }

    std::sort(hand_tiles.standing_tiles, hand_tiles.standing_tiles + hand_tiles.tile_count);

    long fan_table[mahjong::FAN_TABLE_SIZE] = { 0 };

    // 获取绝张、杠开、抢杠、海底信息
    mahjong::win_flag_t win_flag = _extraInfo->getWinFlag();

    // 获取圈风门风
    mahjong::wind_t prevalent_wind = _extraInfo->getPrevalentWind();
    mahjong::wind_t seat_wind = _extraInfo->getSeatWind();

    // 算番
    mahjong::extra_condition_t ext_cond;
    ext_cond.win_flag = win_flag;
    ext_cond.prevalent_wind = prevalent_wind;
    ext_cond.seat_wind = seat_wind;
    int fan = calculate_fan(&hand_tiles, win_tile, &ext_cond, fan_table);

    if (fan == ERROR_NOT_WIN) {
        Label *errorLabel = Label::createWithSystemFont("诈和", "Arial", 14);
        errorLabel->setColor(Color3B::BLACK);
        _fanAreaNode->addChild(errorLabel);
        errorLabel->setPosition(pos);
        return;
    }
    if (fan == ERROR_WRONG_TILES_COUNT) {
        AlertView::showWithMessage("算番", "牌张数错误", 12, nullptr, nullptr);
        return;
    }
    if (fan == ERROR_TILE_COUNT_GREATER_THAN_4) {
        AlertView::showWithMessage("算番", "同一种牌最多只能使用4枚", 12, nullptr, nullptr);
        return;
    }

    // 加花牌
    fan += flowerCnt;
    fan_table[mahjong::FLOWER_TILES] = flowerCnt;

    Node *innerNode = createFanResultNode(fan_table, 14, fanAreaSize.width);

    // 超出高度就使用ScrollView
    if (innerNode->getContentSize().height <= fanAreaSize.height) {
        _fanAreaNode->addChild(innerNode);
        innerNode->setAnchorPoint(Vec2(0.5f, 0.5f));
        innerNode->setPosition(pos);
    }
    else {
        ui::ScrollView *scrollView = ui::ScrollView::create();
        scrollView->setDirection(ui::ScrollView::Direction::VERTICAL);
        scrollView->setScrollBarPositionFromCorner(Vec2(10, 10));
        scrollView->setContentSize(fanAreaSize);
        scrollView->setInnerContainerSize(innerNode->getContentSize());
        scrollView->addChild(innerNode);

        _fanAreaNode->addChild(scrollView);
        scrollView->setAnchorPoint(Vec2(0.5f, 0.5f));
        scrollView->setPosition(pos);
    }
}
<commit_msg>算番结果文本排列调整位置<commit_after>#include "FanCalculatorScene.h"
#include "../mahjong-algorithm/stringify.h"
#include "../mahjong-algorithm/fan_calculator.h"
#include "../widget/TilePickWidget.h"
#include "../widget/ExtraInfoWidget.h"
#include "../widget/AlertView.h"
#include "../widget/TilesKeyboard.h"

USING_NS_CC;

Scene *FanCalculatorScene::createScene() {
    auto scene = Scene::create();
    auto layer = FanCalculatorScene::create();
    scene->addChild(layer);
    return scene;
}

bool FanCalculatorScene::init() {
    if (UNLIKELY(!BaseLayer::initWithTitle("国标麻将算番器"))) {
        return false;
    }

    Size visibleSize = Director::getInstance()->getVisibleSize();
    Vec2 origin = Director::getInstance()->getVisibleOrigin();

    // 选牌面板
    TilePickWidget *tilePicker = TilePickWidget::create();
    const Size &widgetSize = tilePicker->getContentSize();
    this->addChild(tilePicker);
    tilePicker->setPosition(Vec2(origin.x + visibleSize.width * 0.5f,
        origin.y + visibleSize.height - 35 - widgetSize.height * 0.5f));
    _tilePicker = tilePicker;

    // 其他信息的相关控件
    ExtraInfoWidget *extraInfo = ExtraInfoWidget::create();
    const Size &extraSize = extraInfo->getContentSize();
    this->addChild(extraInfo);
    extraInfo->setPosition(Vec2(origin.x + visibleSize.width * 0.5f,
        origin.y + visibleSize.height - 35 - widgetSize.height - 5 - extraSize.height * 0.5f));
    _extraInfo = extraInfo;

    // 直接输入
    ui::Button *button = ui::Button::create("source_material/btn_square_highlighted.png", "source_material/btn_square_selected.png");
    button->setScale9Enabled(true);
    button->setContentSize(Size(55.0f, 20.0f));
    button->setTitleFontSize(12);
    button->setTitleText("直接输入");
    extraInfo->addChild(button);
    button->setPosition(Vec2(visibleSize.width - 40, 100.0f));
    button->addClickEventListener([this](Ref *) { showInputAlert(nullptr); });

    // 番算按钮
    button = ui::Button::create("source_material/btn_square_highlighted.png", "source_material/btn_square_selected.png");
    button->setScale9Enabled(true);
    button->setContentSize(Size(45.0f, 20.0f));
    button->setTitleFontSize(12);
    button->setTitleText("算番");
    extraInfo->addChild(button);
    button->setPosition(Vec2(visibleSize.width - 35, 10.0f));
    button->addClickEventListener([this](Ref *) { calculate(); });

    // 番种显示的Node
    Size areaSize(visibleSize.width, visibleSize.height - 35 - widgetSize.height - 5 - extraSize.height - 10);
    _fanAreaNode = Node::create();
    _fanAreaNode->setContentSize(areaSize);
    _fanAreaNode->setAnchorPoint(Vec2::ANCHOR_MIDDLE);
    _fanAreaNode->setIgnoreAnchorPointForPosition(false);
    this->addChild(_fanAreaNode);
    _fanAreaNode->setPosition(Vec2(origin.x + visibleSize.width * 0.5f, origin.y + areaSize.height * 0.5f + 5));

    tilePicker->setFixedPacksChangedCallback([tilePicker, extraInfo]() {
        extraInfo->refreshByKong(tilePicker->isFixedPacksContainsKong());
    });

    tilePicker->setWinTileChangedCallback([tilePicker, extraInfo]() {
        ExtraInfoWidget::RefreshByWinTile rt;
        rt.getWinTile = std::bind(&TilePickWidget::getServingTile, tilePicker);
        rt.isStandingTilesContainsServingTile = std::bind(&TilePickWidget::isStandingTilesContainsServingTile, tilePicker);
        rt.countServingTileInFixedPacks = std::bind(&TilePickWidget::countServingTileInFixedPacks, tilePicker);
        extraInfo->refreshByWinTile(rt);
    });

    return true;
}

cocos2d::Node *createFanResultNode(const long (&fan_table)[mahjong::FAN_TABLE_SIZE], int fontSize, float resultAreaWidth) {
    // 有n个番种，每行排2个
    long n = mahjong::FAN_TABLE_SIZE - std::count(std::begin(fan_table), std::end(fan_table), 0);
    long rows = (n >> 1) + (n & 1);  // 需要这么多行

    // 排列
    Node *node = Node::create();
    const int lineHeight = fontSize + 2;
    long resultAreaHeight = lineHeight * rows;  // 每行间隔2像素
    resultAreaHeight += (3 + lineHeight);  // 总计
    node->setContentSize(Size(resultAreaWidth, resultAreaHeight));

    long fan = 0;
    for (int i = 0, j = 0; i < n; ++i) {
        while (fan_table[++j] == 0) continue;

        int f = mahjong::fan_value_table[j];
        long n = fan_table[j];
        fan += f * n;
        std::string str = (n == 1) ? StringUtils::format("%s %d番\n", mahjong::fan_name[j], f)
            : StringUtils::format("%s %d番x%ld\n", mahjong::fan_name[j], f, fan_table[j]);

        // 创建label，每行排2个
        Label *fanName = Label::createWithSystemFont(str, "Arial", fontSize);
        fanName->setColor(Color3B(0x60, 0x60, 0x60));
        node->addChild(fanName);
        fanName->setAnchorPoint(Vec2::ANCHOR_MIDDLE_LEFT);
        div_t ret = div(i, 2);
        fanName->setPosition(Vec2(ret.rem == 0 ? 10.0f : resultAreaWidth * 0.5f, resultAreaHeight - lineHeight * (ret.quot + 0.5f)));
    }

    Label *fanTotal = Label::createWithSystemFont(StringUtils::format("总计：%ld番", fan), "Arial", fontSize);
    fanTotal->setColor(Color3B::BLACK);
    node->addChild(fanTotal);
    fanTotal->setAnchorPoint(Vec2::ANCHOR_MIDDLE_LEFT);
    fanTotal->setPosition(Vec2(10.0f, lineHeight * 0.5f));

    return node;
}

void FanCalculatorScene::showInputAlert(const char *prevInput) {
    Size visibleSize = Director::getInstance()->getVisibleSize();
    const float width = visibleSize.width * 0.8f - 10;

    ui::Widget *rootWidget = ui::Widget::create();

    Label *label = Label::createWithSystemFont("使用说明：\n"
        "1." INPUT_GUIDE_STRING_1 "\n"
        "2." INPUT_GUIDE_STRING_2 "\n"
        "3." INPUT_GUIDE_STRING_3 "\n"
        "输入范例1：[EEEE][CCCC][FFFF][PPPP]NN\n"
        "输入范例2：1112345678999s9s\n"
        "输入范例3：WWWW 444s 45m678pFF6m\n", "Arial", 10);
    label->setColor(Color3B::BLACK);
    label->setDimensions(width, 0);
    rootWidget->addChild(label);

    // 输入手牌
    ui::EditBox *editBox = ui::EditBox::create(Size(width - 10, 20.0f), ui::Scale9Sprite::create("source_material/btn_square_normal.png"));
    editBox->setInputFlag(ui::EditBox::InputFlag::SENSITIVE);
    editBox->setInputMode(ui::EditBox::InputMode::SINGLE_LINE);
    editBox->setFontColor(Color4B::BLACK);
    editBox->setFontSize(12);
    editBox->setPlaceholderFontColor(Color4B::GRAY);
    editBox->setPlaceHolder("输入手牌");
    if (prevInput != nullptr) {
        editBox->setText(prevInput);
    }
    TilesKeyboard::hookEditBox(editBox);
    rootWidget->addChild(editBox);

    const Size &labelSize = label->getContentSize();
    rootWidget->setContentSize(Size(width, labelSize.height + 30));
    editBox->setPosition(Vec2(width * 0.5f, 10));
    label->setPosition(Vec2(width * 0.5f, labelSize.height * 0.5f + 30));

    AlertView::showWithNode("直接输入", rootWidget, [this, editBox]() {
        const char *input = editBox->getText();
        const char *errorStr = TilesKeyboard::parseInput(input,
            std::bind(&TilePickWidget::setData, _tilePicker, std::placeholders::_1, std::placeholders::_2));
        if (errorStr != nullptr) {
            const std::string str = input;
            AlertView::showWithMessage("直接输入牌", errorStr, 12, [this, str]() {
                showInputAlert(str.c_str());
            }, nullptr);
        }
    }, nullptr);
}

void FanCalculatorScene::calculate() {
    _fanAreaNode->removeAllChildren();

    const Size &fanAreaSize = _fanAreaNode->getContentSize();
    Vec2 pos(fanAreaSize.width * 0.5f, fanAreaSize.height * 0.5f);

    int flowerCnt = _extraInfo->getFlowerCount();
    if (flowerCnt > 8) {
        AlertView::showWithMessage("算番", "花牌数的范围为0~8", 12, nullptr, nullptr);
        return;
    }

    mahjong::hand_tiles_t hand_tiles;
    mahjong::tile_t win_tile;
    _tilePicker->getData(&hand_tiles, &win_tile);
    if (win_tile == 0) {
        AlertView::showWithMessage("算番", "牌张数错误", 12, nullptr, nullptr);
        return;
    }

    std::sort(hand_tiles.standing_tiles, hand_tiles.standing_tiles + hand_tiles.tile_count);

    long fan_table[mahjong::FAN_TABLE_SIZE] = { 0 };

    // 获取绝张、杠开、抢杠、海底信息
    mahjong::win_flag_t win_flag = _extraInfo->getWinFlag();

    // 获取圈风门风
    mahjong::wind_t prevalent_wind = _extraInfo->getPrevalentWind();
    mahjong::wind_t seat_wind = _extraInfo->getSeatWind();

    // 算番
    mahjong::extra_condition_t ext_cond;
    ext_cond.win_flag = win_flag;
    ext_cond.prevalent_wind = prevalent_wind;
    ext_cond.seat_wind = seat_wind;
    int fan = calculate_fan(&hand_tiles, win_tile, &ext_cond, fan_table);

    if (fan == ERROR_NOT_WIN) {
        Label *errorLabel = Label::createWithSystemFont("诈和", "Arial", 14);
        errorLabel->setColor(Color3B::BLACK);
        _fanAreaNode->addChild(errorLabel);
        errorLabel->setPosition(pos);
        return;
    }
    if (fan == ERROR_WRONG_TILES_COUNT) {
        AlertView::showWithMessage("算番", "牌张数错误", 12, nullptr, nullptr);
        return;
    }
    if (fan == ERROR_TILE_COUNT_GREATER_THAN_4) {
        AlertView::showWithMessage("算番", "同一种牌最多只能使用4枚", 12, nullptr, nullptr);
        return;
    }

    // 加花牌
    fan += flowerCnt;
    fan_table[mahjong::FLOWER_TILES] = flowerCnt;

    Node *innerNode = createFanResultNode(fan_table, 14, fanAreaSize.width);

    // 超出高度就使用ScrollView
    if (innerNode->getContentSize().height <= fanAreaSize.height) {
        _fanAreaNode->addChild(innerNode);
        innerNode->setAnchorPoint(Vec2(0.5f, 0.5f));
        innerNode->setPosition(pos);
    }
    else {
        ui::ScrollView *scrollView = ui::ScrollView::create();
        scrollView->setDirection(ui::ScrollView::Direction::VERTICAL);
        scrollView->setScrollBarPositionFromCorner(Vec2(10, 10));
        scrollView->setContentSize(fanAreaSize);
        scrollView->setInnerContainerSize(innerNode->getContentSize());
        scrollView->addChild(innerNode);

        _fanAreaNode->addChild(scrollView);
        scrollView->setAnchorPoint(Vec2(0.5f, 0.5f));
        scrollView->setPosition(pos);
    }
}
<|endoftext|>


<commit_before>
#pragma once

#include "../Base/String.hpp"
#include "../Base/EnumFlags.hpp"
#include "../Base/RefObject.hpp"

namespace ln
{
class StringWriter;
class CommandLineCommand;
class CommandLineParser;

/** CommandLineOption の追加情報 */
enum class CommandLineOptionFlags
{
	/** 無し */
	None = 0x0000,

	/** オプションをフラグとして扱います。フラグは値を持ちません。 */
	Flag = 0x0001,
};
LN_FLAGS_OPERATORS(CommandLineOptionFlags);

/** CommandLinePositionalArgument の追加情報 */
enum class CommandLinePositionalArgumentFlags
{
	/** 無し */
	None = 0x0000,

	/** 引数を省略可能とします。 */
	Optional = 0x0001,
};
LN_FLAGS_OPERATORS(CommandLinePositionalArgumentFlags);

class CommandLineOption
	: public RefObject
{
public:
	String shortName() const { return m_shortName; }
	String longName() const { return m_longName; }
	String description() const { return m_description; }
	String defaultValue() const { return m_defaultValue; }

	void setShortName(const String& value) { m_shortName = value; }
	void setLongName(const String& value) { m_longName = value; }
	void setDescription(const String& value) { m_description = value; }
	void setNamedValues(const List<String>& values) { m_namedValues = values; }
	void setDefaultValue(const String& value) { m_defaultValue = value; }
	void setFlags(CommandLineOptionFlags value) { m_flags = value; }

	bool isSet() const { return m_isSet; }
	bool hasValue() const { return !m_values.isEmpty(); }
	String value() const { return (m_values.isEmpty()) ? m_defaultValue : m_values[0]; }

	String helpUsageParams() const;
	String helpDescriptionCaption() const;
	String helpDescriptionText() const;

private:
	CommandLineOption();
	virtual ~CommandLineOption();
	void addValue(const String& value) { m_values.add(value); }
	void set(bool value) { m_isSet = value; }
	bool isFlagOption() const { return testFlag(m_flags, CommandLineOptionFlags::Flag); }
	bool isValueOption() const { return !testFlag(m_flags, CommandLineOptionFlags::Flag); }
	bool isRequired() const { return isValueOption() && m_defaultValue.isEmpty(); }

	void makeDislayParamsName(String* shortName, String* longName) const;

	String m_shortName;
	String m_longName;
	String m_description;
	List<String> m_namedValues;
	String m_defaultValue;
	CommandLineOptionFlags m_flags;
	List<String> m_values;
	bool m_isSet;

	friend class CommandLineCommandBase;
	friend class CommandLineCommand;
};

class CommandLinePositionalArgument
	: public RefObject
{
public:
	String name() const { return m_name; }
	String description() const { return m_description; }

	bool hasValue() const { return !m_values.isEmpty(); }
	const String& value() const { return m_values[0]; }
	const List<String>& values() const { return m_values; }

	bool isList() const { return m_isList; }
	bool isOptional() const { return m_flags.hasFlag(CommandLinePositionalArgumentFlags::Optional); }
	String helpDescriptionCaption() const;
	String helpDescriptionText() const;

private:
	CommandLinePositionalArgument();
	virtual ~CommandLinePositionalArgument();
	void setName(const String& value) { m_name = value; }
	void setDescription(const String& value) { m_description = value; }
	void setListType(bool value) { m_isList = value; }
	void setFlags(const Flags<CommandLinePositionalArgumentFlags>& flags) { m_flags = flags; }
	void addValue(const String& value) { m_values.add(value); }

	String m_name;
	String m_description;
	List<String> m_values;
	Flags<CommandLinePositionalArgumentFlags> m_flags;
	bool m_isList;

	friend class CommandLineCommandBase;
	friend class CommandLineCommand;
};

class CommandLineCommandBase
	: public RefObject
{
public:
	const List<Ref<CommandLinePositionalArgument>>& positionalArguments() const { return m_positionalArguments; }
	const List<Ref<CommandLineOption>>& options() const { return m_options; }

	virtual String buildHelpText() const;

protected:
	const String& internalName() const { return m_name; }
	const String& internalDescription() const { return m_description; }
	void setInternalName(const String& value) { m_name = value; }
	void setInternalDescription(const String& value) { m_description = value; }

	CommandLineOption* addFlagOptionInternal(const StringRef& shortName, const StringRef& longName, const StringRef& description)
	{
		auto option = Ref<CommandLineOption>(LN_NEW CommandLineOption(), false);
		option->setShortName(shortName);
		option->setLongName(longName);
		option->setDescription(description);
		option->setFlags(CommandLineOptionFlags::Flag);
		m_options.add(option);
		return option;
	}

	CommandLineOption* addValueOptionInternal(const StringRef& shortName, const StringRef& longName, const StringRef& description, const StringRef& defaultValue = StringRef())
	{
		auto option = Ref<CommandLineOption>(LN_NEW CommandLineOption(), false);
		option->setShortName(shortName);
		option->setLongName(longName);
		option->setDescription(description);
		option->setDefaultValue(defaultValue);
		option->setFlags(CommandLineOptionFlags::None);
		m_options.add(option);
		return option;
	}

	CommandLineOption* addNamedValueOptionInternal(const StringRef& shortName, const StringRef& longName, const StringRef& description, const List<String>& namedValues, const StringRef& defaultValue = StringRef())
	{
		auto option = Ref<CommandLineOption>(LN_NEW CommandLineOption(), false);
		option->setShortName(shortName);
		option->setLongName(longName);
		option->setDescription(description);
		option->setNamedValues(namedValues);
		option->setDefaultValue(defaultValue);
		option->setFlags(CommandLineOptionFlags::None);
		m_options.add(option);
		return option;
	}

	CommandLinePositionalArgument* addPositionalArgumentInternal(const String& name, const String& description, CommandLinePositionalArgumentFlags flags)
	{
		if (!m_positionalArguments.isEmpty()) {
			if (LN_REQUIRE(!m_positionalArguments.back()->isList(), "List positional argument has already been added.")) {
				return nullptr;
			}
		}

		auto pa = Ref<CommandLinePositionalArgument>(LN_NEW CommandLinePositionalArgument(), false);
		pa->setName(name);
		pa->setDescription(description);
		pa->setFlags(flags);
		m_positionalArguments.add(pa);
		return pa;
	}

	CommandLinePositionalArgument* addListPositionalArgumentInternal(const String& name, const String& description, CommandLinePositionalArgumentFlags flags)
	{
		if (!m_positionalArguments.isEmpty()) {
			if (LN_REQUIRE(!m_positionalArguments.back()->isList(), "List positional argument has already been added.")) {
				return nullptr;
			}
		}

		auto pa = Ref<CommandLinePositionalArgument>(LN_NEW CommandLinePositionalArgument(), false);
		pa->setName(name);
		pa->setDescription(description);
		pa->setFlags(flags);
		pa->setListType(true);
		m_positionalArguments.add(pa);
		return pa;
	}

	void addCommandInternal(CommandLineCommand* command)
	{
		m_commands.add(command);
	}

	const List<Ref<CommandLineCommand>>& getCommandsInternal() const { return m_commands; }

	void buildHelpUsageText(StringWriter* writer) const;
	void buildHelpDescriptionText(StringWriter* writer) const;

	bool verify();

private:
	bool isRootCommand() const { return m_name.isEmpty(); }

	String m_name;
	String m_description;
	List<Ref<CommandLineOption>> m_options;
	List<Ref<CommandLinePositionalArgument>> m_positionalArguments;
	List<Ref<CommandLineCommand>> m_commands;
};

class CommandLineCommand
	: public CommandLineCommandBase
{
public:
	const String& name() const { return internalName(); }
	const String& description() const { return internalDescription(); }

	void setName(const String& value) { setInternalName(value); }
	void setDescription(const String& value) { setInternalDescription(value); }

	CommandLineOption* addFlagOption(const StringRef& shortName, const StringRef& longName, const StringRef& description)
	{
		return addFlagOptionInternal(shortName, longName, description);
	}

	CommandLineOption* addValueOption(const StringRef& shortName, const StringRef& longName, const StringRef& description, const StringRef& defaultValue = StringRef())
	{
		return addValueOptionInternal(shortName, longName, description, defaultValue);
	}

	CommandLineOption* addNamedValueOption(const StringRef& shortName, const StringRef& longName, const StringRef& description, const List<String>& namedValues, const StringRef& defaultValue = StringRef())
	{
		return addNamedValueOptionInternal(shortName, longName, description, namedValues, defaultValue);
	}

	CommandLinePositionalArgument* addPositionalArgument(const String& name, const String& description, CommandLinePositionalArgumentFlags flags = CommandLinePositionalArgumentFlags::None)
	{
		return addPositionalArgumentInternal(name, description, flags);
	}

	CommandLinePositionalArgument* addListPositionalArgument(const String& name, const String& description, CommandLinePositionalArgumentFlags flags = CommandLinePositionalArgumentFlags::None)
	{
		return addListPositionalArgumentInternal(name, description, flags);
	}

private:
	CommandLineCommand();
	virtual ~CommandLineCommand();

	static bool parse(
		const List<Ref<CommandLineOption>>& options,
		const List<Ref<CommandLinePositionalArgument>>& positionalArguments,
		const List<Ref<CommandLineCommand>>* commands,
		const List<String>& args, int start, int* outNext, String* outMessage);

	friend class CommandLineParser;
};

class CommandLineParser
	: public CommandLineCommandBase
{
public:

	CommandLineParser();
	~CommandLineParser();

	CommandLineCommand* addCommand(const String& name, const String& description)
	{
		auto command = Ref<CommandLineCommand>(LN_NEW CommandLineCommand(), false);
		command->setName(name);
		command->setDescription(description);
		addCommandInternal(command);
		return command;
	}

	CommandLineOption* addFlagOption(const StringRef& shortName, const StringRef& longName, const StringRef& description)
	{
		return addFlagOptionInternal(shortName, longName, description);
	}

	CommandLineOption* addValueOption(const StringRef& shortName, const StringRef& longName, const StringRef& description, const StringRef& defaultValue = StringRef())
	{
		return addValueOptionInternal(shortName, longName, description, defaultValue);
	}

	CommandLineOption* addNamedValueOption(const StringRef& shortName, const StringRef& longName, const StringRef& description, const List<String>& namedValues, const StringRef& defaultValue = StringRef())
	{
		return addNamedValueOptionInternal(shortName, longName, description, namedValues, defaultValue);
	}

	CommandLinePositionalArgument* addPositionalArgument(const String& name, const String& description, CommandLinePositionalArgumentFlags flags = CommandLinePositionalArgumentFlags::None)
	{
		return addPositionalArgumentInternal(name, description, flags);
	}

	CommandLinePositionalArgument* addListPositionalArgument(const String& name, const String& description, CommandLinePositionalArgumentFlags flags = CommandLinePositionalArgumentFlags::None)
	{
		return addListPositionalArgumentInternal(name, description, flags);
	}

	CommandLineOption* addHelpOption()
	{
		m_helpOption = addFlagOption(_T("h"), _T("help"), _T("Display this help."));
		return m_helpOption;
	}

	CommandLineOption* addVersionOption(const StringRef& versionText)
	{
		m_versionText = versionText;
		m_versionOption = addFlagOption(_T("v"), _T("version"), _T("Display version."));
		return m_versionOption;
	}

	void setApplicationDescription(const StringRef& description) { m_applicationDescription = description; }

	//void setHelpEnabled();

	//void addOption(const CommandLineOption& option)
	//{
	//	auto command = Ref<CommandLineOption>(LN_NEW CommandLineCommand(), false);
	//	command->setName(name);
	//	command->setDescription(description);
	//	m_commands.add(command);
	//	return command;
	//}

	void addOption(const CommandLineOption& option);

	Optional<Ref<CommandLineCommand>> findCommand(const StringRef& commandName) const;

	/** コマンドライン引数を解析します。 */
	bool process(int argc, char** argv);

	bool has(const CommandLineCommand* command) const;
	bool has(const CommandLineOption* command) const;

	//bool hasCommand(const CommandLineCommand* command) const;

	bool hasError() const { return !m_message.isEmpty(); }

	CommandLineCommand* command() const { return m_activeCommand; }

	//bool isSet(const CommandLineOption* option) const;

	/** ヘルプ情報を標準出力します。 */
	void printHelp(const StringRef& commandName = StringRef()) const;

	void printVersion() const;

	virtual String buildHelpText() const override;

private:
	bool parse(const List<String>& args);

	//List<Ref<CommandLinePositionalArgument>> m_rootPositionalArguments;
	//List<Ref<CommandLineOption>> m_rootOptions;
	//List<Ref<CommandLineCommand>> m_commands;
	String m_message;
	CommandLineCommand* m_activeCommand;
	CommandLineOption* m_helpOption;
	CommandLineOption* m_versionOption;
	String m_versionText;
	String m_applicationDescription;
};

} // namespace ln
<commit_msg>add comment<commit_after>
#pragma once

#include "../Base/String.hpp"
#include "../Base/EnumFlags.hpp"
#include "../Base/RefObject.hpp"

namespace ln
{
class StringWriter;
class CommandLineCommand;
class CommandLineParser;

/** CommandLineOption の追加情報 */
enum class CommandLineOptionFlags
{
	/** 無し */
	None = 0x0000,

	/** オプションをフラグとして扱います。フラグは値を持ちません。 */
	Flag = 0x0001,
};
LN_FLAGS_OPERATORS(CommandLineOptionFlags);

/** CommandLinePositionalArgument の追加情報 */
enum class CommandLinePositionalArgumentFlags
{
	/** 無し */
	None = 0x0000,

	/** 引数を省略可能とします。 */
	Optional = 0x0001,
};
LN_FLAGS_OPERATORS(CommandLinePositionalArgumentFlags);

class CommandLineOption
	: public RefObject
{
public:
	String shortName() const { return m_shortName; }
	String longName() const { return m_longName; }
	String description() const { return m_description; }
	String defaultValue() const { return m_defaultValue; }

	void setShortName(const String& value) { m_shortName = value; }
	void setLongName(const String& value) { m_longName = value; }
	void setDescription(const String& value) { m_description = value; }
	void setNamedValues(const List<String>& values) { m_namedValues = values; }
	void setDefaultValue(const String& value) { m_defaultValue = value; }
	void setFlags(CommandLineOptionFlags value) { m_flags = value; }

	bool isSet() const { return m_isSet; }
	bool hasValue() const { return !m_values.isEmpty(); }
	String value() const { return (m_values.isEmpty()) ? m_defaultValue : m_values[0]; }

	String helpUsageParams() const;
	String helpDescriptionCaption() const;
	String helpDescriptionText() const;

private:
	CommandLineOption();
	virtual ~CommandLineOption();
	void addValue(const String& value) { m_values.add(value); }
	void set(bool value) { m_isSet = value; }
	bool isFlagOption() const { return testFlag(m_flags, CommandLineOptionFlags::Flag); }
	bool isValueOption() const { return !testFlag(m_flags, CommandLineOptionFlags::Flag); }
	bool isRequired() const { return isValueOption() && m_defaultValue.isEmpty(); }

	void makeDislayParamsName(String* shortName, String* longName) const;

	String m_shortName;
	String m_longName;
	String m_description;
	List<String> m_namedValues;
	String m_defaultValue;
	CommandLineOptionFlags m_flags;
	List<String> m_values;
	bool m_isSet;

	friend class CommandLineCommandBase;
	friend class CommandLineCommand;
};

class CommandLinePositionalArgument
	: public RefObject
{
public:
	String name() const { return m_name; }
	String description() const { return m_description; }

	bool hasValue() const { return !m_values.isEmpty(); }
	const String& value() const { return m_values[0]; }
	const List<String>& values() const { return m_values; }

	bool isList() const { return m_isList; }
	bool isOptional() const { return m_flags.hasFlag(CommandLinePositionalArgumentFlags::Optional); }
	String helpDescriptionCaption() const;
	String helpDescriptionText() const;

private:
	CommandLinePositionalArgument();
	virtual ~CommandLinePositionalArgument();
	void setName(const String& value) { m_name = value; }
	void setDescription(const String& value) { m_description = value; }
	void setListType(bool value) { m_isList = value; }
	void setFlags(const Flags<CommandLinePositionalArgumentFlags>& flags) { m_flags = flags; }
	void addValue(const String& value) { m_values.add(value); }

	String m_name;
	String m_description;
	List<String> m_values;
	Flags<CommandLinePositionalArgumentFlags> m_flags;
	bool m_isList;

	friend class CommandLineCommandBase;
	friend class CommandLineCommand;
};

class CommandLineCommandBase
	: public RefObject
{
public:
	const List<Ref<CommandLinePositionalArgument>>& positionalArguments() const { return m_positionalArguments; }
	const List<Ref<CommandLineOption>>& options() const { return m_options; }

	virtual String buildHelpText() const;

protected:
	const String& internalName() const { return m_name; }
	const String& internalDescription() const { return m_description; }
	void setInternalName(const String& value) { m_name = value; }
	void setInternalDescription(const String& value) { m_description = value; }

	CommandLineOption* addFlagOptionInternal(const StringRef& shortName, const StringRef& longName, const StringRef& description)
	{
		auto option = Ref<CommandLineOption>(LN_NEW CommandLineOption(), false);
		option->setShortName(shortName);
		option->setLongName(longName);
		option->setDescription(description);
		option->setFlags(CommandLineOptionFlags::Flag);
		m_options.add(option);
		return option;
	}

	CommandLineOption* addValueOptionInternal(const StringRef& shortName, const StringRef& longName, const StringRef& description, const StringRef& defaultValue = StringRef())
	{
		auto option = Ref<CommandLineOption>(LN_NEW CommandLineOption(), false);
		option->setShortName(shortName);
		option->setLongName(longName);
		option->setDescription(description);
		option->setDefaultValue(defaultValue);
		option->setFlags(CommandLineOptionFlags::None);
		m_options.add(option);
		return option;
	}

	CommandLineOption* addNamedValueOptionInternal(const StringRef& shortName, const StringRef& longName, const StringRef& description, const List<String>& namedValues, const StringRef& defaultValue = StringRef())
	{
		auto option = Ref<CommandLineOption>(LN_NEW CommandLineOption(), false);
		option->setShortName(shortName);
		option->setLongName(longName);
		option->setDescription(description);
		option->setNamedValues(namedValues);
		option->setDefaultValue(defaultValue);
		option->setFlags(CommandLineOptionFlags::None);
		m_options.add(option);
		return option;
	}

	CommandLinePositionalArgument* addPositionalArgumentInternal(const String& name, const String& description, CommandLinePositionalArgumentFlags flags)
	{
		if (!m_positionalArguments.isEmpty()) {
			if (LN_REQUIRE(!m_positionalArguments.back()->isList(), "List positional argument has already been added.")) {
				return nullptr;
			}
		}

		auto pa = Ref<CommandLinePositionalArgument>(LN_NEW CommandLinePositionalArgument(), false);
		pa->setName(name);
		pa->setDescription(description);
		pa->setFlags(flags);
		m_positionalArguments.add(pa);
		return pa;
	}

	CommandLinePositionalArgument* addListPositionalArgumentInternal(const String& name, const String& description, CommandLinePositionalArgumentFlags flags)
	{
		if (!m_positionalArguments.isEmpty()) {
			if (LN_REQUIRE(!m_positionalArguments.back()->isList(), "List positional argument has already been added.")) {
				return nullptr;
			}
		}

		auto pa = Ref<CommandLinePositionalArgument>(LN_NEW CommandLinePositionalArgument(), false);
		pa->setName(name);
		pa->setDescription(description);
		pa->setFlags(flags);
		pa->setListType(true);
		m_positionalArguments.add(pa);
		return pa;
	}

	void addCommandInternal(CommandLineCommand* command)
	{
		m_commands.add(command);
	}

	const List<Ref<CommandLineCommand>>& getCommandsInternal() const { return m_commands; }

	void buildHelpUsageText(StringWriter* writer) const;
	void buildHelpDescriptionText(StringWriter* writer) const;

	bool verify();

private:
	bool isRootCommand() const { return m_name.isEmpty(); }

	String m_name;
	String m_description;
	List<Ref<CommandLineOption>> m_options;
	List<Ref<CommandLinePositionalArgument>> m_positionalArguments;
	List<Ref<CommandLineCommand>> m_commands;
};

class CommandLineCommand
	: public CommandLineCommandBase
{
public:
	const String& name() const { return internalName(); }
	const String& description() const { return internalDescription(); }

	void setName(const String& value) { setInternalName(value); }
	void setDescription(const String& value) { setInternalDescription(value); }

	CommandLineOption* addFlagOption(const StringRef& shortName, const StringRef& longName, const StringRef& description)
	{
		return addFlagOptionInternal(shortName, longName, description);
	}

	CommandLineOption* addValueOption(const StringRef& shortName, const StringRef& longName, const StringRef& description, const StringRef& defaultValue = StringRef())
	{
		return addValueOptionInternal(shortName, longName, description, defaultValue);
	}

	CommandLineOption* addNamedValueOption(const StringRef& shortName, const StringRef& longName, const StringRef& description, const List<String>& namedValues, const StringRef& defaultValue = StringRef())
	{
		return addNamedValueOptionInternal(shortName, longName, description, namedValues, defaultValue);
	}

	CommandLinePositionalArgument* addPositionalArgument(const String& name, const String& description, CommandLinePositionalArgumentFlags flags = CommandLinePositionalArgumentFlags::None)
	{
		return addPositionalArgumentInternal(name, description, flags);
	}

	CommandLinePositionalArgument* addListPositionalArgument(const String& name, const String& description, CommandLinePositionalArgumentFlags flags = CommandLinePositionalArgumentFlags::None)
	{
		return addListPositionalArgumentInternal(name, description, flags);
	}

private:
	CommandLineCommand();
	virtual ~CommandLineCommand();

	static bool parse(
		const List<Ref<CommandLineOption>>& options,
		const List<Ref<CommandLinePositionalArgument>>& positionalArguments,
		const List<Ref<CommandLineCommand>>* commands,
		const List<String>& args, int start, int* outNext, String* outMessage);

	friend class CommandLineParser;
};

/** コマンドライン引数を解析するための機能を提供します。 */
class CommandLineParser
	: public CommandLineCommandBase
{
public:
	CommandLineParser();
	~CommandLineParser();

	CommandLineCommand* addCommand(const String& name, const String& description)
	{
		auto command = Ref<CommandLineCommand>(LN_NEW CommandLineCommand(), false);
		command->setName(name);
		command->setDescription(description);
		addCommandInternal(command);
		return command;
	}

	CommandLineOption* addFlagOption(const StringRef& shortName, const StringRef& longName, const StringRef& description)
	{
		return addFlagOptionInternal(shortName, longName, description);
	}

	CommandLineOption* addValueOption(const StringRef& shortName, const StringRef& longName, const StringRef& description, const StringRef& defaultValue = StringRef())
	{
		return addValueOptionInternal(shortName, longName, description, defaultValue);
	}

	CommandLineOption* addNamedValueOption(const StringRef& shortName, const StringRef& longName, const StringRef& description, const List<String>& namedValues, const StringRef& defaultValue = StringRef())
	{
		return addNamedValueOptionInternal(shortName, longName, description, namedValues, defaultValue);
	}

	CommandLinePositionalArgument* addPositionalArgument(const String& name, const String& description, CommandLinePositionalArgumentFlags flags = CommandLinePositionalArgumentFlags::None)
	{
		return addPositionalArgumentInternal(name, description, flags);
	}

	CommandLinePositionalArgument* addListPositionalArgument(const String& name, const String& description, CommandLinePositionalArgumentFlags flags = CommandLinePositionalArgumentFlags::None)
	{
		return addListPositionalArgumentInternal(name, description, flags);
	}

	CommandLineOption* addHelpOption()
	{
		m_helpOption = addFlagOption(_T("h"), _T("help"), _T("Display this help."));
		return m_helpOption;
	}

	CommandLineOption* addVersionOption(const StringRef& versionText)
	{
		m_versionText = versionText;
		m_versionOption = addFlagOption(_T("v"), _T("version"), _T("Display version."));
		return m_versionOption;
	}

	void setApplicationDescription(const StringRef& description) { m_applicationDescription = description; }

	//void setHelpEnabled();

	//void addOption(const CommandLineOption& option)
	//{
	//	auto command = Ref<CommandLineOption>(LN_NEW CommandLineCommand(), false);
	//	command->setName(name);
	//	command->setDescription(description);
	//	m_commands.add(command);
	//	return command;
	//}

	void addOption(const CommandLineOption& option);

	Optional<Ref<CommandLineCommand>> findCommand(const StringRef& commandName) const;

	/** コマンドライン引数を解析します。 */
	bool process(int argc, char** argv);

	bool has(const CommandLineCommand* command) const;
	bool has(const CommandLineOption* command) const;

	//bool hasCommand(const CommandLineCommand* command) const;

	bool hasError() const { return !m_message.isEmpty(); }

	CommandLineCommand* command() const { return m_activeCommand; }

	//bool isSet(const CommandLineOption* option) const;

	/** ヘルプ情報を標準出力します。 */
	void printHelp(const StringRef& commandName = StringRef()) const;

	void printVersion() const;

	virtual String buildHelpText() const override;

private:
	bool parse(const List<String>& args);

	//List<Ref<CommandLinePositionalArgument>> m_rootPositionalArguments;
	//List<Ref<CommandLineOption>> m_rootOptions;
	//List<Ref<CommandLineCommand>> m_commands;
	String m_message;
	CommandLineCommand* m_activeCommand;
	CommandLineOption* m_helpOption;
	CommandLineOption* m_versionOption;
	String m_versionText;
	String m_applicationDescription;
};

} // namespace ln
<|endoftext|>


<commit_before>#include <chrono>
#include <limits>
#include <algorithm>

#include "Tools/Exception/exception.hpp"
#include "Tools/Perf/Reorderer/Reorderer.hpp"
#include "Tools/Math/utils.h"

#include "Decoder_RSC_DB_BCJR.hpp"

using namespace aff3ct;
using namespace aff3ct::module;

template <typename B, typename R>
Decoder_RSC_DB_BCJR<B,R>
::Decoder_RSC_DB_BCJR(const int K,
                      const std::vector<std::vector<int>> &trellis,
                      const bool buffered_encoding,
                      const int n_frames)
: Decoder               (K, 2 * K, n_frames, 1                   ),
  Decoder_SISO_SIHO<B,R>(K, 2 * K, n_frames, 1                   ),
  n_states              ((int)trellis[0].size()/4                ),
  n_ff                  ((int)std::log2(n_states)                ),
  buffered_encoding     (buffered_encoding                       ),
  trellis               (trellis                                 ),
  sys                   (2*K                                     ),
  par                   (  K                                     ),
  ext                   (2*K                                     ),
  s                     (  K                                     ),
  alpha_mp              (n_states                                ),
  beta_mp               (n_states                                ),
  alpha                 (K/2 + 1, std::vector<R>(n_states    , 0)),
  beta                  (K/2 + 1, std::vector<R>(n_states    , 0)),
  gamma                 (K/2    , std::vector<R>(n_states * 4, 0))
{
	const std::string name = "Decoder_RSC_DB_BCJR";
	this->set_name(name);

	if (!tools::is_power_of_2(n_states))
	{
		std::stringstream message;
		message << "'n_states' has to be a power of 2 ('n_states' = " << n_states << ").";
		throw tools::invalid_argument(__FILE__, __LINE__, __func__, message.str());
	}
}

template <typename B, typename R>
void Decoder_RSC_DB_BCJR<B,R>
::_load(const R *Y_N)
{
	notify_new_frame();

	if (buffered_encoding)
	{
		for (auto i = 0; i < this->K / 2; i++)
		{
			R a = tools::div2(Y_N[2*i  ]);
			R b = tools::div2(Y_N[2*i+1]);
			sys[4*i + 0] =  a + b;
			sys[4*i + 1] =  a - b;
			sys[4*i + 2] = -a + b;
			sys[4*i + 3] = -a - b;
		}
		for (auto i = 0; i < this->K; i++)
			par[i] = tools::div2(Y_N[this->K + i]);
	}
	else
	{
		for (auto i = 0; i < this->K / 2; i++)
		{
			R a = tools::div2(Y_N[4*i  ]);
			R b = tools::div2(Y_N[4*i+1]);
			sys[4*i + 0] =  a + b;
			sys[4*i + 1] =  a - b;
			sys[4*i + 2] = -a + b;
			sys[4*i + 3] = -a - b;

			par[2*i  ] = Y_N[4*i + 2];
			par[2*i+2] = Y_N[4*i + 3];
		}
	}
}

template <typename B, typename R>
void Decoder_RSC_DB_BCJR<B,R>
::_decode_siho(const R *Y_N, B *V_K, const int frame_id)
{
//	auto t_load = std::chrono::steady_clock::now(); // ----------------------------------------------------------- LOAD
	_load(Y_N);
//	auto d_load = std::chrono::steady_clock::now() - t_load;

//	auto t_decod = std::chrono::steady_clock::now(); // -------------------------------------------------------- DECODE
	this->_decode_siso(sys.data(), par.data(), ext.data(), frame_id);
//	auto d_decod = std::chrono::steady_clock::now() - t_decod;

//	auto t_store = std::chrono::steady_clock::now(); // --------------------------------------------------------- STORE
	for (auto i = 0; i < this->K; i+=2)
	{
		s[i  ] = (  std::max(ext[2*i+2] + sys[2*i+2], ext[2*i+3] + sys[2*i+3])
		          - std::max(ext[2*i+0] + sys[2*i+0], ext[2*i+1] + sys[2*i+1])  ) > 0;
		s[i+1] = (  std::max(ext[2*i+1] + sys[2*i+1], ext[2*i+3] + sys[2*i+3])
		          - std::max(ext[2*i+0] + sys[2*i+0], ext[2*i+2] + sys[2*i+2])  ) > 0;
	}
	_store(V_K);
//	auto d_store = std::chrono::steady_clock::now() - t_store;

//	(*this)[dec::tsk::decode_siho].update_timer(dec::tm::decode_siho::load,   d_load);
//	(*this)[dec::tsk::decode_siho].update_timer(dec::tm::decode_siho::decode, d_decod);
//	(*this)[dec::tsk::decode_siho].update_timer(dec::tm::decode_siho::store,  d_store);
}

template <typename B, typename R>
void Decoder_RSC_DB_BCJR<B,R>
::_store(B *V_K) const
{
	std::copy(s.begin(), s.begin() + this->K, V_K);
}

template <typename B, typename R>
void Decoder_RSC_DB_BCJR<B,R>
::_decode_siso(const R *sys, const R *par, R *ext, const int frame_id)
{
	__init_alpha_beta();
	__fwd_recursion(sys, par);
	__bwd_recursion(sys, par, ext);
	__save_alpha_beta();
}

template <typename B, typename R>
void Decoder_RSC_DB_BCJR<B,R>
::__init_alpha_beta()
{
	for (auto s = 0; s<n_states; s++)
	{
		alpha[           0][s] = alpha_mp[s];
		beta[beta.size()-1][s] =  beta_mp[s];
	}
}

template <typename B, typename R>
void Decoder_RSC_DB_BCJR<B,R>
::__save_alpha_beta()
{
	for (auto s = 0; s < n_states; s++)
	{
		alpha_mp[s] = alpha[alpha.size()-1][s];
		 beta_mp[s] = beta[0][s];
	}
}

template <typename B, typename R>
void Decoder_RSC_DB_BCJR<B,R>
::notify_new_frame()
{
	for (auto s = 0; s < n_states; s++)
	{
		alpha_mp[s] = (R)0;
		beta_mp [s] = (R)0;
	}
}

// ==================================================================================== explicit template instantiation
#include "Tools/types.h"
#ifdef AFF3CT_MULTI_PREC
template class aff3ct::module::Decoder_RSC_DB_BCJR<B_8,Q_8>;
template class aff3ct::module::Decoder_RSC_DB_BCJR<B_16,Q_16>;
template class aff3ct::module::Decoder_RSC_DB_BCJR<B_32,Q_32>;
template class aff3ct::module::Decoder_RSC_DB_BCJR<B_64,Q_64>;
#else
template class aff3ct::module::Decoder_RSC_DB_BCJR<B,Q>;
#endif
// ==================================================================================== explicit template instantiation
<commit_msg>Fix a bug in the RSC_DB BCJR decoder when loading the LLRs in non-buffered mode.<commit_after>#include <chrono>
#include <limits>
#include <algorithm>

#include "Tools/Exception/exception.hpp"
#include "Tools/Perf/Reorderer/Reorderer.hpp"
#include "Tools/Math/utils.h"

#include "Decoder_RSC_DB_BCJR.hpp"

using namespace aff3ct;
using namespace aff3ct::module;

template <typename B, typename R>
Decoder_RSC_DB_BCJR<B,R>
::Decoder_RSC_DB_BCJR(const int K,
                      const std::vector<std::vector<int>> &trellis,
                      const bool buffered_encoding,
                      const int n_frames)
: Decoder               (K, 2 * K, n_frames, 1                   ),
  Decoder_SISO_SIHO<B,R>(K, 2 * K, n_frames, 1                   ),
  n_states              ((int)trellis[0].size()/4                ),
  n_ff                  ((int)std::log2(n_states)                ),
  buffered_encoding     (buffered_encoding                       ),
  trellis               (trellis                                 ),
  sys                   (2*K                                     ),
  par                   (  K                                     ),
  ext                   (2*K                                     ),
  s                     (  K                                     ),
  alpha_mp              (n_states                                ),
  beta_mp               (n_states                                ),
  alpha                 (K/2 + 1, std::vector<R>(n_states    , 0)),
  beta                  (K/2 + 1, std::vector<R>(n_states    , 0)),
  gamma                 (K/2    , std::vector<R>(n_states * 4, 0))
{
	const std::string name = "Decoder_RSC_DB_BCJR";
	this->set_name(name);

	if (!tools::is_power_of_2(n_states))
	{
		std::stringstream message;
		message << "'n_states' has to be a power of 2 ('n_states' = " << n_states << ").";
		throw tools::invalid_argument(__FILE__, __LINE__, __func__, message.str());
	}
}

template <typename B, typename R>
void Decoder_RSC_DB_BCJR<B,R>
::_load(const R *Y_N)
{
	notify_new_frame();

	if (buffered_encoding)
	{
		for (auto i = 0; i < this->K / 2; i++)
		{
			R a = tools::div2(Y_N[2*i  ]);
			R b = tools::div2(Y_N[2*i+1]);
			sys[4*i + 0] =  a + b;
			sys[4*i + 1] =  a - b;
			sys[4*i + 2] = -a + b;
			sys[4*i + 3] = -a - b;
		}
		for (auto i = 0; i < this->K; i++)
			par[i] = tools::div2(Y_N[this->K + i]);
	}
	else
	{
		for (auto i = 0; i < this->K / 2; i++)
		{
			R a = tools::div2(Y_N[4*i  ]);
			R b = tools::div2(Y_N[4*i+1]);
			sys[4*i + 0] =  a + b;
			sys[4*i + 1] =  a - b;
			sys[4*i + 2] = -a + b;
			sys[4*i + 3] = -a - b;

			par[2*i  ] = tools::div2(Y_N[4*i + 2]);
			par[2*i+1] = tools::div2(Y_N[4*i + 3]);
		}
	}
}

template <typename B, typename R>
void Decoder_RSC_DB_BCJR<B,R>
::_decode_siho(const R *Y_N, B *V_K, const int frame_id)
{
//	auto t_load = std::chrono::steady_clock::now(); // ----------------------------------------------------------- LOAD
	_load(Y_N);
//	auto d_load = std::chrono::steady_clock::now() - t_load;

//	auto t_decod = std::chrono::steady_clock::now(); // -------------------------------------------------------- DECODE
	this->_decode_siso(sys.data(), par.data(), ext.data(), frame_id);
//	auto d_decod = std::chrono::steady_clock::now() - t_decod;

//	auto t_store = std::chrono::steady_clock::now(); // --------------------------------------------------------- STORE
	for (auto i = 0; i < this->K; i+=2)
	{
		s[i  ] = (  std::max(ext[2*i+2] + sys[2*i+2], ext[2*i+3] + sys[2*i+3])
		          - std::max(ext[2*i+0] + sys[2*i+0], ext[2*i+1] + sys[2*i+1])  ) > 0;
		s[i+1] = (  std::max(ext[2*i+1] + sys[2*i+1], ext[2*i+3] + sys[2*i+3])
		          - std::max(ext[2*i+0] + sys[2*i+0], ext[2*i+2] + sys[2*i+2])  ) > 0;
	}
	_store(V_K);
//	auto d_store = std::chrono::steady_clock::now() - t_store;

//	(*this)[dec::tsk::decode_siho].update_timer(dec::tm::decode_siho::load,   d_load);
//	(*this)[dec::tsk::decode_siho].update_timer(dec::tm::decode_siho::decode, d_decod);
//	(*this)[dec::tsk::decode_siho].update_timer(dec::tm::decode_siho::store,  d_store);
}

template <typename B, typename R>
void Decoder_RSC_DB_BCJR<B,R>
::_store(B *V_K) const
{
	std::copy(s.begin(), s.begin() + this->K, V_K);
}

template <typename B, typename R>
void Decoder_RSC_DB_BCJR<B,R>
::_decode_siso(const R *sys, const R *par, R *ext, const int frame_id)
{
	__init_alpha_beta();
	__fwd_recursion(sys, par);
	__bwd_recursion(sys, par, ext);
	__save_alpha_beta();
}

template <typename B, typename R>
void Decoder_RSC_DB_BCJR<B,R>
::__init_alpha_beta()
{
	for (auto s = 0; s<n_states; s++)
	{
		alpha[           0][s] = alpha_mp[s];
		beta[beta.size()-1][s] =  beta_mp[s];
	}
}

template <typename B, typename R>
void Decoder_RSC_DB_BCJR<B,R>
::__save_alpha_beta()
{
	for (auto s = 0; s < n_states; s++)
	{
		alpha_mp[s] = alpha[alpha.size()-1][s];
		 beta_mp[s] = beta[0][s];
	}
}

template <typename B, typename R>
void Decoder_RSC_DB_BCJR<B,R>
::notify_new_frame()
{
	for (auto s = 0; s < n_states; s++)
	{
		alpha_mp[s] = (R)0;
		beta_mp [s] = (R)0;
	}
}

// ==================================================================================== explicit template instantiation
#include "Tools/types.h"
#ifdef AFF3CT_MULTI_PREC
template class aff3ct::module::Decoder_RSC_DB_BCJR<B_8,Q_8>;
template class aff3ct::module::Decoder_RSC_DB_BCJR<B_16,Q_16>;
template class aff3ct::module::Decoder_RSC_DB_BCJR<B_32,Q_32>;
template class aff3ct::module::Decoder_RSC_DB_BCJR<B_64,Q_64>;
#else
template class aff3ct::module::Decoder_RSC_DB_BCJR<B,Q>;
#endif
// ==================================================================================== explicit template instantiation
<|endoftext|>