<commit_before>/*
 * This file is a part of Xpiks - cross platform application for
 * keywording and uploading images for microstocks
 * Copyright (C) 2014 Taras Kushnir <kushnirTV@gmail.com>
 *
 * Xpiks is distributed under the GNU General Public License, version 3.0
 *
 * 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 3 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, see <http://www.gnu.org/licenses/>.
 */

#include <QStringList>
#include <QDirIterator>
#include <QDebug>
#include <QList>
#include "artitemsmodel.h"
#include "artiteminfo.h"
#include "../Helpers/indiceshelper.h"

namespace Models {

    ArtItemsModel::~ArtItemsModel() {
        qDeleteAll(m_ArtworkList);
        m_ArtworkList.clear();

        // being freed in gui
        //delete m_ArtworksDirectories;
    }

    int ArtItemsModel::getModifiedArtworksCount()
    {
        int modifiedCount = 0;
        foreach (ArtworkMetadata *metadata, m_ArtworkList) {
            if (metadata->isModified()) {
                modifiedCount++;
            }
        }

        return modifiedCount;
    }

    void ArtItemsModel::updateAllProperties()
    {
        QList<QPair<int, int> > ranges;
        ranges << qMakePair(0, m_ArtworkList.length() - 1);
        QVector<int> roles;
        roles << ArtworkDescriptionRole << KeywordsRole << IsModifiedRole << ArtworkAuthorRole << ArtworkTitleRole;
        updateItemsAtIndices(ranges, roles);
    }

    void ArtItemsModel::removeArtworksDirectory(int index)
    {
        const QString &directory = m_ArtworksRepository->getDirectory(index);
        QList<int> indicesToRemove;

        QList<ArtworkMetadata*>::const_iterator it = m_ArtworkList.constBegin();
        for (int i = 0; it < m_ArtworkList.constEnd(); ++it, ++i) {
            if ((*it)->isInDirectory(directory)) {
                indicesToRemove.append(i);
            }
        }

        doRemoveItemsAtIndices(indicesToRemove);
        emit selectedArtworksCountChanged();
        emit modifiedArtworksCountChanged();
    }

    void ArtItemsModel::removeKeywordAt(int metadataIndex, int keywordIndex)
    {
        if (metadataIndex >= 0 && metadataIndex < m_ArtworkList.length()) {
            ArtworkMetadata *metadata = m_ArtworkList.at(metadataIndex);

            if (metadata->removeKeywordAt(keywordIndex)) {
                QModelIndex index = this->index(metadataIndex);
                emit dataChanged(index, index, QVector<int>() << KeywordsRole << IsModifiedRole << KeywordsCountRole);
            }
        }
    }

    void ArtItemsModel::removeLastKeyword(int metadataIndex)
    {
        if (metadataIndex >= 0 && metadataIndex < m_ArtworkList.length()) {
            ArtworkMetadata *metadata = m_ArtworkList.at(metadataIndex);

            if (metadata->removeLastKeyword()) {
                QModelIndex index = this->index(metadataIndex);
                emit dataChanged(index, index, QVector<int>() << KeywordsRole << IsModifiedRole << KeywordsCountRole);
            }
        }
    }

    void ArtItemsModel::appendKeyword(int metadataIndex, const QString &keyword)
    {
        if (metadataIndex >= 0 && metadataIndex < m_ArtworkList.length()) {
            ArtworkMetadata *metadata = m_ArtworkList.at(metadataIndex);
            if (metadata->appendKeyword(keyword)) {
                QModelIndex index = this->index(metadataIndex);
                emit dataChanged(index, index, QVector<int>() << KeywordsRole << IsModifiedRole << KeywordsCountRole);
            }
        }
    }

    void ArtItemsModel::setSelectedItemsSaved()
    {
        QList<int> selectedIndices;
        getSelectedItemsIndices(selectedIndices);

        foreach (int index, selectedIndices) {
            m_ArtworkList[index]->unsetModified();
        }

        QList<QPair<int, int> > rangesToUpdate;
        Helpers::indicesToRanges(selectedIndices, rangesToUpdate);
        updateItemsAtIndices(rangesToUpdate, QVector<int>() << IsModifiedRole);

        updateModifiedCount();
    }

    void ArtItemsModel::removeSelectedArtworks()
    {
        QList<int> indicesToRemove;
        getSelectedItemsIndices(indicesToRemove);
        doRemoveItemsAtIndices(indicesToRemove);
    }

    void ArtItemsModel::updateSelectedArtworks()
    {
        QList<int> selectedIndices;
        getSelectedItemsIndices(selectedIndices);
        QList<QPair<int, int> > rangesToUpdate;
        Helpers::indicesToRanges(selectedIndices, rangesToUpdate);
        QVector<int> roles;
        roles << ArtworkDescriptionRole << KeywordsRole << IsModifiedRole << ArtworkAuthorRole << ArtworkTitleRole;
        updateItemsAtIndices(rangesToUpdate, roles);
    }

    void ArtItemsModel::patchSelectedArtworks()
    {
        QList<ArtworkMetadata*> selectedArtworks;
        int count = m_ArtworkList.length();
        for (int i = 0; i < count; ++i) {
            ArtworkMetadata *metadata = m_ArtworkList[i];
            if (metadata->getIsSelected()) {
                selectedArtworks.append(metadata);
            }
        }

        // TODO: assert iptc provider is not null
        // TODO: remove this two times copying
        m_IptcProvider->setArtworks(selectedArtworks);
    }

    void ArtItemsModel::uploadSelectedArtworks()
    {
        QList<ArtworkMetadata*> selectedArtworks;
        int count = m_ArtworkList.length();
        for (int i = 0; i < count; ++i) {
            ArtworkMetadata *metadata = m_ArtworkList[i];
            if (metadata->getIsSelected()) {
                selectedArtworks.append(metadata);
            }
        }

        // TODO: assert uploader is not null
        // TODO: remove this two times copying
        m_ArtworkUploader->setArtworks(selectedArtworks);
    }

    bool ArtItemsModel::areSelectedArtworksSaved()
    {
        bool areModified = false;

        foreach(ArtworkMetadata *metadata, m_ArtworkList) {
            if (metadata->getIsSelected()) {
                if (metadata->isModified()) {
                    areModified = true;
                    break;
                }
            }
        }

        return !areModified;
    }

    void ArtItemsModel::selectDirectory(int directoryIndex)
    {
        QList<int> directoryItems;
        const QString directory = m_ArtworksRepository->getDirectory(directoryIndex);

        int i = 0;
        foreach (ArtworkMetadata *metadata, m_ArtworkList) {
            if (metadata->isInDirectory(directory)) {
                directoryItems.append(i);
                metadata->setIsSelected(!metadata->getIsSelected());
            }

            i++;
        }

        QList<QPair<int, int> > rangesToUpdate;
        Helpers::indicesToRanges(directoryItems, rangesToUpdate);
        updateItemsAtIndices(rangesToUpdate, QVector<int>() << IsSelectedRole);
    }

    int ArtItemsModel::rowCount(const QModelIndex &parent) const {
        Q_UNUSED(parent);
        return m_ArtworkList.count();
    }

    QVariant ArtItemsModel::data(const QModelIndex &index, int role) const {
        if (index.row() < 0 || index.row() >= m_ArtworkList.count())
            return QVariant();

        ArtworkMetadata *metadata = m_ArtworkList.at(index.row());
        switch (role) {
        case ArtworkDescriptionRole:
            return metadata->getDescription();
        case ArtworkFilenameRole:
            return metadata->getFilepath();
        case ArtworkAuthorRole:
            return metadata->getAuthor();
        case ArtworkTitleRole:
            return metadata->getTitle();
        case KeywordsRole:
            return metadata->getKeywords();
        case KeywordsStringRole:
            return metadata->getKeywordsString();
        case IsModifiedRole:
            return metadata->isModified();
        case IsSelectedRole:
            return metadata->getIsSelected();
        case KeywordsCountRole:
            return metadata->getKeywordsCount();
        default:
            return QVariant();
        }
    }

    Qt::ItemFlags ArtItemsModel::flags(const QModelIndex &index) const
    {
        if (!index.isValid()) {
            return Qt::ItemIsEnabled;
        }

        return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
    }

    bool ArtItemsModel::setData(const QModelIndex &index, const QVariant &value, int role)
    {
        if (!index.isValid()) {
            return false;
        }

        ArtworkMetadata *metadata = m_ArtworkList.at(index.row());
        int roleToUpdate = 0;
        switch (role) {
        case EditArtworkDescriptionRole:
            metadata->setDescription(value.toString());
            roleToUpdate = ArtworkDescriptionRole;
            break;
        case EditArtworkTitleRole:
            metadata->setTitle(value.toString());
            roleToUpdate = ArtworkTitleRole;
            break;
        case EditArtworkAuthorRole:
            metadata->setAuthor(value.toString());
            roleToUpdate = ArtworkAuthorRole;
            break;
        case EditIsSelectedRole:
            metadata->setIsSelected(value.toBool());
            roleToUpdate = IsSelectedRole;
            break;
        default:
            return false;
        }

        emit dataChanged(index, index, QVector<int>() << IsModifiedRole << role << roleToUpdate);
        return true;
    }

    void ArtItemsModel::addLocalArtworks(const QList<QUrl> &artworksPaths)
    {
        qDebug() << artworksPaths;
        QStringList fileList;
        foreach (const QUrl &url, artworksPaths) {
            fileList.append(url.toLocalFile());
        }

        addFiles(fileList);
    }

    void ArtItemsModel::addLocalDirectory(const QUrl &directory)
    {
        qDebug() << directory;
        addDirectory(directory.toLocalFile());
    }

    void ArtItemsModel::itemSelectedChanged(bool value)
    {
        int plus = value ? +1 : -1;
        m_SelectedArtworksCount += plus;
        emit selectedArtworksCountChanged();
    }

    void ArtItemsModel::addDirectory(const QString &directory)
    {
        QDir dir(directory);

        dir.setFilter(QDir::NoDotAndDotDot | QDir::Files);

        QStringList items = dir.entryList();
        for (int i = 0; i < items.size(); ++i) {
            items[i] = dir.filePath(items[i]);
        }

        if (items.count() > 0) {
            addFiles(items);
        }
    }

    void ArtItemsModel::addFiles(const QStringList &rawFilenames)
    {
        QStringList filenames = rawFilenames.filter(QRegExp("^.*[.]jpg$", Qt::CaseInsensitive));

        const int count = filenames.count();
        const int newFilesCount = m_ArtworksRepository->getNewFilesCount(filenames);

        bool filesWereAccounted = m_ArtworksRepository->beginAccountingFiles(filenames);

        if (newFilesCount > 0) {
            beginInsertRows(QModelIndex(), rowCount(), rowCount() + newFilesCount - 1);

            for (int i = 0; i < count; ++i) {
                const QString &filename = filenames[i];
                if (m_ArtworksRepository->accountFile(filename))
                {
                    ArtworkMetadata *metadata = new ArtworkMetadata(filename);
                    QObject::connect(metadata, SIGNAL(modifiedChanged(bool)),
                                     this, SLOT(itemModifiedChanged(bool)));
                    QObject::connect(metadata, SIGNAL(selectedChanged(bool)),
                                     this, SLOT(itemSelectedChanged(bool)));
                    QObject::connect(metadata, SIGNAL(fileSelectedChanged(QString,bool)),
                                     m_ArtworksRepository, SLOT(fileSelectedChanged(QString,bool)));

                    m_ArtworkList.append(metadata);
                }
            }

            endInsertRows();
        }

        m_ArtworksRepository->endAccountingFiles(filesWereAccounted);

        if (newFilesCount > 0) {
            m_ArtworksRepository->updateCountsForExistingDirectories();
        }

        // set artworks for initial import
        m_IptcProvider->setArtworks(m_ArtworkList);
    }

    void ArtItemsModel::setAllItemsSelected(bool selected)
    {
        qDebug() << "Setting all items selected (" << selected << ")";
        int length = m_ArtworkList.length();
        for (int i = 0; i < length; ++i) {
            ArtworkMetadata *metadata = m_ArtworkList[i];
            metadata->setIsSelected(selected);
        }

        if (length > 0) {
            QModelIndex startIndex = index(0);
            QModelIndex endIndex = index(length - 1);
            emit dataChanged(startIndex, endIndex, QVector<int>() << IsSelectedRole);
        }
    }

    void ArtItemsModel::doCombineSelectedImages(CombinedArtworksModel *combinedModel) const
    {
        QList<ArtItemInfo*> artworksList;

        foreach (ArtworkMetadata *metadata, m_ArtworkList) {
            if (metadata->getIsSelected()) {
                ArtItemInfo *info = new ArtItemInfo(metadata);
                artworksList.append(info);
            }
        }

        combinedModel->initArtworks(artworksList);
        combinedModel->recombineArtworks();
    }

    void ArtItemsModel::doCombineArtwork(int index, CombinedArtworksModel *combinedModel)
    {
        if (index >= 0 && index < m_ArtworkList.length()) {
            QList<ArtItemInfo*> artworksList;

            ArtworkMetadata *metadata = m_ArtworkList.at(index);
            metadata->setIsSelected(true);
            QModelIndex qmIndex = this->index(index);
            emit dataChanged(qmIndex, qmIndex, QVector<int>() << IsSelectedRole);

            ArtItemInfo *info = new ArtItemInfo(metadata);
            artworksList.append(info);

            combinedModel->initArtworks(artworksList);
            combinedModel->recombineArtworks();
        }
    }

    QHash<int, QByteArray> ArtItemsModel::roleNames() const {
        QHash<int, QByteArray> roles;
        roles[ArtworkDescriptionRole] = "description";
        roles[EditArtworkDescriptionRole] = "editdescription";
        roles[ArtworkAuthorRole] = "author";
        roles[EditArtworkAuthorRole] = "editauthor";
        roles[ArtworkTitleRole] = "title";
        roles[EditArtworkTitleRole] = "edittitle";
        roles[ArtworkFilenameRole] = "filename";
        roles[KeywordsRole] = "keywords";
        roles[KeywordsStringRole] = "keywordsstring";
        roles[IsModifiedRole] = "ismodified";
        roles[IsSelectedRole] = "isselected";
        roles[EditIsSelectedRole] = "editisselected";
        roles[KeywordsCountRole] = "keywordscount";
        return roles;
    }

    void ArtItemsModel::removeInnerItem(int row)
    {
        // TODO: add assert for row
        ArtworkMetadata *metadata = m_ArtworkList[row];
        m_ArtworksRepository->removeFile(metadata->getFilepath());

        if (metadata->getIsSelected()) {
            m_SelectedArtworksCount--;
            emit selectedArtworksCountChanged();
        }

        delete metadata;
        m_ArtworkList.removeAt(row);
    }

    void ArtItemsModel::doRemoveItemsAtIndices(const QList<int> &indicesToRemove)
    {
        QList<QPair<int, int> > rangesToRemove;
        Helpers::indicesToRanges(indicesToRemove, rangesToRemove);
        removeItemsAtIndices(rangesToRemove);

        m_ArtworksRepository->cleanupEmptyDirectories();
        m_ArtworksRepository->updateCountsForExistingDirectories();
    }

    void ArtItemsModel::getSelectedItemsIndices(QList<int> &indices)
    {
        int count = m_ArtworkList.length();
        for (int i = 0; i < count; ++i) {
            ArtworkMetadata *metadata = m_ArtworkList[i];
            if (metadata->getIsSelected()) {
                indices.append(i);
            }
        }
    }
}
<commit_msg>Fixed issue with keywords count after combined edit<commit_after>/*
 * This file is a part of Xpiks - cross platform application for
 * keywording and uploading images for microstocks
 * Copyright (C) 2014 Taras Kushnir <kushnirTV@gmail.com>
 *
 * Xpiks is distributed under the GNU General Public License, version 3.0
 *
 * 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 3 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, see <http://www.gnu.org/licenses/>.
 */

#include <QStringList>
#include <QDirIterator>
#include <QDebug>
#include <QList>
#include "artitemsmodel.h"
#include "artiteminfo.h"
#include "../Helpers/indiceshelper.h"

namespace Models {

    ArtItemsModel::~ArtItemsModel() {
        qDeleteAll(m_ArtworkList);
        m_ArtworkList.clear();

        // being freed in gui
        //delete m_ArtworksDirectories;
    }

    int ArtItemsModel::getModifiedArtworksCount()
    {
        int modifiedCount = 0;
        foreach (ArtworkMetadata *metadata, m_ArtworkList) {
            if (metadata->isModified()) {
                modifiedCount++;
            }
        }

        return modifiedCount;
    }

    void ArtItemsModel::updateAllProperties()
    {
        QList<QPair<int, int> > ranges;
        ranges << qMakePair(0, m_ArtworkList.length() - 1);
        QVector<int> roles;
        roles << ArtworkDescriptionRole << KeywordsRole << IsModifiedRole << ArtworkAuthorRole << ArtworkTitleRole;
        updateItemsAtIndices(ranges, roles);
    }

    void ArtItemsModel::removeArtworksDirectory(int index)
    {
        const QString &directory = m_ArtworksRepository->getDirectory(index);
        QList<int> indicesToRemove;

        QList<ArtworkMetadata*>::const_iterator it = m_ArtworkList.constBegin();
        for (int i = 0; it < m_ArtworkList.constEnd(); ++it, ++i) {
            if ((*it)->isInDirectory(directory)) {
                indicesToRemove.append(i);
            }
        }

        doRemoveItemsAtIndices(indicesToRemove);
        emit selectedArtworksCountChanged();
        emit modifiedArtworksCountChanged();
    }

    void ArtItemsModel::removeKeywordAt(int metadataIndex, int keywordIndex)
    {
        if (metadataIndex >= 0 && metadataIndex < m_ArtworkList.length()) {
            ArtworkMetadata *metadata = m_ArtworkList.at(metadataIndex);

            if (metadata->removeKeywordAt(keywordIndex)) {
                QModelIndex index = this->index(metadataIndex);
                emit dataChanged(index, index, QVector<int>() << KeywordsRole << IsModifiedRole << KeywordsCountRole);
            }
        }
    }

    void ArtItemsModel::removeLastKeyword(int metadataIndex)
    {
        if (metadataIndex >= 0 && metadataIndex < m_ArtworkList.length()) {
            ArtworkMetadata *metadata = m_ArtworkList.at(metadataIndex);

            if (metadata->removeLastKeyword()) {
                QModelIndex index = this->index(metadataIndex);
                emit dataChanged(index, index, QVector<int>() << KeywordsRole << IsModifiedRole << KeywordsCountRole);
            }
        }
    }

    void ArtItemsModel::appendKeyword(int metadataIndex, const QString &keyword)
    {
        if (metadataIndex >= 0 && metadataIndex < m_ArtworkList.length()) {
            ArtworkMetadata *metadata = m_ArtworkList.at(metadataIndex);
            if (metadata->appendKeyword(keyword)) {
                QModelIndex index = this->index(metadataIndex);
                emit dataChanged(index, index, QVector<int>() << KeywordsRole << IsModifiedRole << KeywordsCountRole);
            }
        }
    }

    void ArtItemsModel::setSelectedItemsSaved()
    {
        QList<int> selectedIndices;
        getSelectedItemsIndices(selectedIndices);

        foreach (int index, selectedIndices) {
            m_ArtworkList[index]->unsetModified();
        }

        QList<QPair<int, int> > rangesToUpdate;
        Helpers::indicesToRanges(selectedIndices, rangesToUpdate);
        updateItemsAtIndices(rangesToUpdate, QVector<int>() << IsModifiedRole);

        updateModifiedCount();
    }

    void ArtItemsModel::removeSelectedArtworks()
    {
        QList<int> indicesToRemove;
        getSelectedItemsIndices(indicesToRemove);
        doRemoveItemsAtIndices(indicesToRemove);
    }

    void ArtItemsModel::updateSelectedArtworks()
    {
        QList<int> selectedIndices;
        getSelectedItemsIndices(selectedIndices);
        QList<QPair<int, int> > rangesToUpdate;
        Helpers::indicesToRanges(selectedIndices, rangesToUpdate);
        QVector<int> roles;
        roles << ArtworkDescriptionRole << KeywordsRole << IsModifiedRole <<
                 ArtworkAuthorRole << ArtworkTitleRole << KeywordsCountRole;
        updateItemsAtIndices(rangesToUpdate, roles);
    }

    void ArtItemsModel::patchSelectedArtworks()
    {
        QList<ArtworkMetadata*> selectedArtworks;
        int count = m_ArtworkList.length();
        for (int i = 0; i < count; ++i) {
            ArtworkMetadata *metadata = m_ArtworkList[i];
            if (metadata->getIsSelected()) {
                selectedArtworks.append(metadata);
            }
        }

        // TODO: assert iptc provider is not null
        // TODO: remove this two times copying
        m_IptcProvider->setArtworks(selectedArtworks);
    }

    void ArtItemsModel::uploadSelectedArtworks()
    {
        QList<ArtworkMetadata*> selectedArtworks;
        int count = m_ArtworkList.length();
        for (int i = 0; i < count; ++i) {
            ArtworkMetadata *metadata = m_ArtworkList[i];
            if (metadata->getIsSelected()) {
                selectedArtworks.append(metadata);
            }
        }

        // TODO: assert uploader is not null
        // TODO: remove this two times copying
        m_ArtworkUploader->setArtworks(selectedArtworks);
    }

    bool ArtItemsModel::areSelectedArtworksSaved()
    {
        bool areModified = false;

        foreach(ArtworkMetadata *metadata, m_ArtworkList) {
            if (metadata->getIsSelected()) {
                if (metadata->isModified()) {
                    areModified = true;
                    break;
                }
            }
        }

        return !areModified;
    }

    void ArtItemsModel::selectDirectory(int directoryIndex)
    {
        QList<int> directoryItems;
        const QString directory = m_ArtworksRepository->getDirectory(directoryIndex);

        int i = 0;
        foreach (ArtworkMetadata *metadata, m_ArtworkList) {
            if (metadata->isInDirectory(directory)) {
                directoryItems.append(i);
                metadata->setIsSelected(!metadata->getIsSelected());
            }

            i++;
        }

        QList<QPair<int, int> > rangesToUpdate;
        Helpers::indicesToRanges(directoryItems, rangesToUpdate);
        updateItemsAtIndices(rangesToUpdate, QVector<int>() << IsSelectedRole);
    }

    int ArtItemsModel::rowCount(const QModelIndex &parent) const {
        Q_UNUSED(parent);
        return m_ArtworkList.count();
    }

    QVariant ArtItemsModel::data(const QModelIndex &index, int role) const {
        if (index.row() < 0 || index.row() >= m_ArtworkList.count())
            return QVariant();

        ArtworkMetadata *metadata = m_ArtworkList.at(index.row());
        switch (role) {
        case ArtworkDescriptionRole:
            return metadata->getDescription();
        case ArtworkFilenameRole:
            return metadata->getFilepath();
        case ArtworkAuthorRole:
            return metadata->getAuthor();
        case ArtworkTitleRole:
            return metadata->getTitle();
        case KeywordsRole:
            return metadata->getKeywords();
        case KeywordsStringRole:
            return metadata->getKeywordsString();
        case IsModifiedRole:
            return metadata->isModified();
        case IsSelectedRole:
            return metadata->getIsSelected();
        case KeywordsCountRole:
            return metadata->getKeywordsCount();
        default:
            return QVariant();
        }
    }

    Qt::ItemFlags ArtItemsModel::flags(const QModelIndex &index) const
    {
        if (!index.isValid()) {
            return Qt::ItemIsEnabled;
        }

        return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
    }

    bool ArtItemsModel::setData(const QModelIndex &index, const QVariant &value, int role)
    {
        if (!index.isValid()) {
            return false;
        }

        ArtworkMetadata *metadata = m_ArtworkList.at(index.row());
        int roleToUpdate = 0;
        switch (role) {
        case EditArtworkDescriptionRole:
            metadata->setDescription(value.toString());
            roleToUpdate = ArtworkDescriptionRole;
            break;
        case EditArtworkTitleRole:
            metadata->setTitle(value.toString());
            roleToUpdate = ArtworkTitleRole;
            break;
        case EditArtworkAuthorRole:
            metadata->setAuthor(value.toString());
            roleToUpdate = ArtworkAuthorRole;
            break;
        case EditIsSelectedRole:
            metadata->setIsSelected(value.toBool());
            roleToUpdate = IsSelectedRole;
            break;
        default:
            return false;
        }

        emit dataChanged(index, index, QVector<int>() << IsModifiedRole << role << roleToUpdate);
        return true;
    }

    void ArtItemsModel::addLocalArtworks(const QList<QUrl> &artworksPaths)
    {
        qDebug() << artworksPaths;
        QStringList fileList;
        foreach (const QUrl &url, artworksPaths) {
            fileList.append(url.toLocalFile());
        }

        addFiles(fileList);
    }

    void ArtItemsModel::addLocalDirectory(const QUrl &directory)
    {
        qDebug() << directory;
        addDirectory(directory.toLocalFile());
    }

    void ArtItemsModel::itemSelectedChanged(bool value)
    {
        int plus = value ? +1 : -1;
        m_SelectedArtworksCount += plus;
        emit selectedArtworksCountChanged();
    }

    void ArtItemsModel::addDirectory(const QString &directory)
    {
        QDir dir(directory);

        dir.setFilter(QDir::NoDotAndDotDot | QDir::Files);

        QStringList items = dir.entryList();
        for (int i = 0; i < items.size(); ++i) {
            items[i] = dir.filePath(items[i]);
        }

        if (items.count() > 0) {
            addFiles(items);
        }
    }

    void ArtItemsModel::addFiles(const QStringList &rawFilenames)
    {
        QStringList filenames = rawFilenames.filter(QRegExp("^.*[.]jpg$", Qt::CaseInsensitive));

        const int count = filenames.count();
        const int newFilesCount = m_ArtworksRepository->getNewFilesCount(filenames);

        bool filesWereAccounted = m_ArtworksRepository->beginAccountingFiles(filenames);

        if (newFilesCount > 0) {
            beginInsertRows(QModelIndex(), rowCount(), rowCount() + newFilesCount - 1);

            for (int i = 0; i < count; ++i) {
                const QString &filename = filenames[i];
                if (m_ArtworksRepository->accountFile(filename))
                {
                    ArtworkMetadata *metadata = new ArtworkMetadata(filename);
                    QObject::connect(metadata, SIGNAL(modifiedChanged(bool)),
                                     this, SLOT(itemModifiedChanged(bool)));
                    QObject::connect(metadata, SIGNAL(selectedChanged(bool)),
                                     this, SLOT(itemSelectedChanged(bool)));
                    QObject::connect(metadata, SIGNAL(fileSelectedChanged(QString,bool)),
                                     m_ArtworksRepository, SLOT(fileSelectedChanged(QString,bool)));

                    m_ArtworkList.append(metadata);
                }
            }

            endInsertRows();
        }

        m_ArtworksRepository->endAccountingFiles(filesWereAccounted);

        if (newFilesCount > 0) {
            m_ArtworksRepository->updateCountsForExistingDirectories();
        }

        // set artworks for initial import
        m_IptcProvider->setArtworks(m_ArtworkList);
    }

    void ArtItemsModel::setAllItemsSelected(bool selected)
    {
        qDebug() << "Setting all items selected (" << selected << ")";
        int length = m_ArtworkList.length();
        for (int i = 0; i < length; ++i) {
            ArtworkMetadata *metadata = m_ArtworkList[i];
            metadata->setIsSelected(selected);
        }

        if (length > 0) {
            QModelIndex startIndex = index(0);
            QModelIndex endIndex = index(length - 1);
            emit dataChanged(startIndex, endIndex, QVector<int>() << IsSelectedRole);
        }
    }

    void ArtItemsModel::doCombineSelectedImages(CombinedArtworksModel *combinedModel) const
    {
        QList<ArtItemInfo*> artworksList;

        foreach (ArtworkMetadata *metadata, m_ArtworkList) {
            if (metadata->getIsSelected()) {
                ArtItemInfo *info = new ArtItemInfo(metadata);
                artworksList.append(info);
            }
        }

        combinedModel->initArtworks(artworksList);
        combinedModel->recombineArtworks();
    }

    void ArtItemsModel::doCombineArtwork(int index, CombinedArtworksModel *combinedModel)
    {
        if (index >= 0 && index < m_ArtworkList.length()) {
            QList<ArtItemInfo*> artworksList;

            ArtworkMetadata *metadata = m_ArtworkList.at(index);
            metadata->setIsSelected(true);
            QModelIndex qmIndex = this->index(index);
            emit dataChanged(qmIndex, qmIndex, QVector<int>() << IsSelectedRole);

            ArtItemInfo *info = new ArtItemInfo(metadata);
            artworksList.append(info);

            combinedModel->initArtworks(artworksList);
            combinedModel->recombineArtworks();
        }
    }

    QHash<int, QByteArray> ArtItemsModel::roleNames() const {
        QHash<int, QByteArray> roles;
        roles[ArtworkDescriptionRole] = "description";
        roles[EditArtworkDescriptionRole] = "editdescription";
        roles[ArtworkAuthorRole] = "author";
        roles[EditArtworkAuthorRole] = "editauthor";
        roles[ArtworkTitleRole] = "title";
        roles[EditArtworkTitleRole] = "edittitle";
        roles[ArtworkFilenameRole] = "filename";
        roles[KeywordsRole] = "keywords";
        roles[KeywordsStringRole] = "keywordsstring";
        roles[IsModifiedRole] = "ismodified";
        roles[IsSelectedRole] = "isselected";
        roles[EditIsSelectedRole] = "editisselected";
        roles[KeywordsCountRole] = "keywordscount";
        return roles;
    }

    void ArtItemsModel::removeInnerItem(int row)
    {
        // TODO: add assert for row
        ArtworkMetadata *metadata = m_ArtworkList[row];
        m_ArtworksRepository->removeFile(metadata->getFilepath());

        if (metadata->getIsSelected()) {
            m_SelectedArtworksCount--;
            emit selectedArtworksCountChanged();
        }

        delete metadata;
        m_ArtworkList.removeAt(row);
    }

    void ArtItemsModel::doRemoveItemsAtIndices(const QList<int> &indicesToRemove)
    {
        QList<QPair<int, int> > rangesToRemove;
        Helpers::indicesToRanges(indicesToRemove, rangesToRemove);
        removeItemsAtIndices(rangesToRemove);

        m_ArtworksRepository->cleanupEmptyDirectories();
        m_ArtworksRepository->updateCountsForExistingDirectories();
    }

    void ArtItemsModel::getSelectedItemsIndices(QList<int> &indices)
    {
        int count = m_ArtworkList.length();
        for (int i = 0; i < count; ++i) {
            ArtworkMetadata *metadata = m_ArtworkList[i];
            if (metadata->getIsSelected()) {
                indices.append(i);
            }
        }
    }
}
<|endoftext|>


<commit_before>/*=========================================================================

  Program:   Insight Segmentation & Registration Toolkit
  Module:    itkVotingBinaryImageFilterTest.cxx
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

  Copyright (c) Insight Software Consortium. All rights reserved.
  See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notices for more information.

=========================================================================*/
#if defined(_MSC_VER)
#pragma warning ( disable : 4786 )
#endif
#include "itkImage.h"
#include "itkRandomImageSource.h"
#include "itkBinaryThresholdImageFilter.h"
#include "itkVotingBinaryImageFilter.h"
#include "itkTextOutput.h"

int itkVotingBinaryImageFilterTest(int, char* [] )
{
  // Comment the following if you want to use the itk text output window
  itk::OutputWindow::SetInstance(itk::TextOutput::New());

  typedef itk::Image<unsigned short,2> ImageType;

  itk::RandomImageSource<ImageType>::Pointer random;
  random = itk::RandomImageSource<ImageType>::New();
  random->SetMin(   0 );
  random->SetMax( 100 );

  unsigned long randomSize[2];
  randomSize[0] = randomSize[1] = 8;
  random->SetSize(randomSize);

  float spacing[2] = {0.7, 2.1};
  random->SetSpacing( spacing );
  float origin[2] = {15, 400};
  random->SetOrigin( origin );

  ImageType::PixelType foreground =  97; // prime numbers are good testers
  ImageType::PixelType background =  29;

  itk::BinaryThresholdImageFilter<ImageType,ImageType>::Pointer thresholder;
  thresholder =  itk::BinaryThresholdImageFilter<ImageType,ImageType>::New();
  thresholder->SetInput( random->GetOutput() );
  thresholder->SetLowerThreshold(  30 );
  thresholder->SetUpperThreshold( 100 );
  thresholder->SetInsideValue( foreground );
  thresholder->SetOutsideValue( background );

  // Create a voting image
  itk::VotingBinaryImageFilter<ImageType, ImageType>::Pointer voting;
  voting = itk::VotingBinaryImageFilter<ImageType,ImageType>::New();
  voting->SetInput( thresholder->GetOutput());
  voting->SetForegroundValue( foreground );
  voting->SetBackgroundValue( background );

  // define the neighborhood size used for the voting filter (5x5)
  ImageType::SizeType neighRadius;
  neighRadius[0] = 1;
  neighRadius[1] = 1;
  voting->SetRadius(neighRadius);

  // run the algorithm
  voting->Update();

  itk::ImageRegionIterator<ImageType> it;
  it = itk::ImageRegionIterator<ImageType>(random->GetOutput(),
                               random->GetOutput()->GetBufferedRegion());
  std::cout << "Input image" << std::endl;
  unsigned int i;
  for (i=1; !it.IsAtEnd(); ++i, ++it)
    {
    std::cout << "\t" << it.Get();
    if ((i % 8) == 0)
      {
      std::cout << std::endl;
      }
    }

  it = itk::ImageRegionIterator<ImageType>(thresholder->GetOutput(),
                               thresholder->GetOutput()->GetBufferedRegion());
  std::cout << "Binary image" << std::endl;

  for (i=1; !it.IsAtEnd(); ++i, ++it)
    {
    std::cout << "\t" << it.Get();
    if ((i % 8) == 0)
      {
      std::cout << std::endl;
      }
    }

  std::cout << "Output image" << std::endl;
  it = itk::ImageRegionIterator<ImageType>(voting->GetOutput(),
                               voting->GetOutput()->GetBufferedRegion());
  for (i=1; !it.IsAtEnd(); ++i, ++it)
    {
    std::cout << "\t" << it.Get();
    if ((i % 8) == 0)
      {
      std::cout << std::endl;
      }
    }

  return EXIT_SUCCESS;
}

<commit_msg>ENH: Added invokations for SetSurvivalThreshold and SetBornThreshold.<commit_after>/*=========================================================================

  Program:   Insight Segmentation & Registration Toolkit
  Module:    itkVotingBinaryImageFilterTest.cxx
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

  Copyright (c) Insight Software Consortium. All rights reserved.
  See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notices for more information.

=========================================================================*/
#if defined(_MSC_VER)
#pragma warning ( disable : 4786 )
#endif
#include "itkImage.h"
#include "itkRandomImageSource.h"
#include "itkBinaryThresholdImageFilter.h"
#include "itkVotingBinaryImageFilter.h"
#include "itkTextOutput.h"

int itkVotingBinaryImageFilterTest(int, char* [] )
{
  // Comment the following if you want to use the itk text output window
  itk::OutputWindow::SetInstance(itk::TextOutput::New());

  typedef itk::Image<unsigned short,2> ImageType;

  itk::RandomImageSource<ImageType>::Pointer random;
  random = itk::RandomImageSource<ImageType>::New();
  random->SetMin(   0 );
  random->SetMax( 100 );

  unsigned long randomSize[2];
  randomSize[0] = randomSize[1] = 8;
  random->SetSize(randomSize);

  float spacing[2] = {0.7, 2.1};
  random->SetSpacing( spacing );
  float origin[2] = {15, 400};
  random->SetOrigin( origin );

  ImageType::PixelType foreground =  97; // prime numbers are good testers
  ImageType::PixelType background =  29;

  itk::BinaryThresholdImageFilter<ImageType,ImageType>::Pointer thresholder;
  thresholder =  itk::BinaryThresholdImageFilter<ImageType,ImageType>::New();
  thresholder->SetInput( random->GetOutput() );
  thresholder->SetLowerThreshold(  30 );
  thresholder->SetUpperThreshold( 100 );
  thresholder->SetInsideValue( foreground );
  thresholder->SetOutsideValue( background );

  // Create a voting image
  itk::VotingBinaryImageFilter<ImageType, ImageType>::Pointer voting;
  voting = itk::VotingBinaryImageFilter<ImageType,ImageType>::New();
  voting->SetInput( thresholder->GetOutput());
  voting->SetForegroundValue( foreground );
  voting->SetBackgroundValue( background );

  // define the neighborhood size used for the voting filter (5x5)
  ImageType::SizeType neighRadius;
  neighRadius[0] = 1;
  neighRadius[1] = 1;
  voting->SetRadius(neighRadius);

  voting->SetBornThreshold( 5 );
  voting->SetSurvivalThreshold( 0 );

  // run the algorithm
  voting->Update();

  itk::ImageRegionIterator<ImageType> it;
  it = itk::ImageRegionIterator<ImageType>(random->GetOutput(),
                               random->GetOutput()->GetBufferedRegion());
  std::cout << "Input image" << std::endl;
  unsigned int i;
  for (i=1; !it.IsAtEnd(); ++i, ++it)
    {
    std::cout << "\t" << it.Get();
    if ((i % 8) == 0)
      {
      std::cout << std::endl;
      }
    }

  it = itk::ImageRegionIterator<ImageType>(thresholder->GetOutput(),
                               thresholder->GetOutput()->GetBufferedRegion());
  std::cout << "Binary image" << std::endl;

  for (i=1; !it.IsAtEnd(); ++i, ++it)
    {
    std::cout << "\t" << it.Get();
    if ((i % 8) == 0)
      {
      std::cout << std::endl;
      }
    }

  std::cout << "Output image" << std::endl;
  it = itk::ImageRegionIterator<ImageType>(voting->GetOutput(),
                               voting->GetOutput()->GetBufferedRegion());
  for (i=1; !it.IsAtEnd(); ++i, ++it)
    {
    std::cout << "\t" << it.Get();
    if ((i % 8) == 0)
      {
      std::cout << std::endl;
      }
    }

  return EXIT_SUCCESS;
}

<|endoftext|>


<commit_before>/*
Copyright (c) 2014-2019 AscEmu Team <http://www.ascemu.org>
This file is released under the MIT license. See README-MIT for more information.
*/

#include "StdAfx.h"

#include "ConsoleAuthMgr.h"

ConsoleAuthMgr& ConsoleAuthMgr::getInstance()
{
    static ConsoleAuthMgr mInstance;
    return mInstance;
}

void ConsoleAuthMgr::initialize()
{
    authRequestId = 1;
}

uint32_t ConsoleAuthMgr::getGeneratedId()
{
    consoleAuthMgrLock.Acquire();

    uint32_t requestId = authRequestId++;

    consoleAuthMgrLock.Release();

    return requestId;
}

void ConsoleAuthMgr::addRequestIdSocket(uint32_t id, ConsoleSocket* sock)
{
    consoleAuthMgrLock.Acquire();

    if (sock == nullptr)
    {
        consoleRequestMap.erase(id);
    }
    else
    {
        consoleRequestMap.insert(std::make_pair(id, sock));
    }

    consoleAuthMgrLock.Release();
}

ConsoleSocket* ConsoleAuthMgr::getSocketByRequestId(uint32_t id)
{
    ConsoleSocket* consoleSocket = nullptr;

    consoleAuthMgrLock.Acquire();

    std::map<uint32_t, ConsoleSocket*>::iterator itr = consoleRequestMap.find(id);
    if (itr != consoleRequestMap.end())
    {
        consoleSocket = itr->second;
    }

    consoleAuthMgrLock.Release();

    return consoleSocket;
}
<commit_msg>Resolved CID 351682 (#1 of 1): Data race condition<commit_after>/*
Copyright (c) 2014-2019 AscEmu Team <http://www.ascemu.org>
This file is released under the MIT license. See README-MIT for more information.
*/

#include "StdAfx.h"

#include "ConsoleAuthMgr.h"

ConsoleAuthMgr& ConsoleAuthMgr::getInstance()
{
    static ConsoleAuthMgr mInstance;
    return mInstance;
}

void ConsoleAuthMgr::initialize()
{
    consoleAuthMgrLock.Acquire();

    authRequestId = 1;

    consoleAuthMgrLock.Release();
}

uint32_t ConsoleAuthMgr::getGeneratedId()
{
    consoleAuthMgrLock.Acquire();

    uint32_t requestId = authRequestId++;

    consoleAuthMgrLock.Release();

    return requestId;
}

void ConsoleAuthMgr::addRequestIdSocket(uint32_t id, ConsoleSocket* sock)
{
    consoleAuthMgrLock.Acquire();

    if (sock == nullptr)
    {
        consoleRequestMap.erase(id);
    }
    else
    {
        consoleRequestMap.insert(std::make_pair(id, sock));
    }

    consoleAuthMgrLock.Release();
}

ConsoleSocket* ConsoleAuthMgr::getSocketByRequestId(uint32_t id)
{
    ConsoleSocket* consoleSocket = nullptr;

    consoleAuthMgrLock.Acquire();

    std::map<uint32_t, ConsoleSocket*>::iterator itr = consoleRequestMap.find(id);
    if (itr != consoleRequestMap.end())
    {
        consoleSocket = itr->second;
    }

    consoleAuthMgrLock.Release();

    return consoleSocket;
}
<|endoftext|>


<commit_before>/**
 ******************************************************************************
 * Xenia : Xbox 360 Emulator Research Project                                 *
 ******************************************************************************
 * Copyright 2014 Ben Vanik. All rights reserved.                             *
 * Released under the BSD license - see LICENSE in the root for more details. *
 ******************************************************************************
 */

#include "xenia/hid/winkey/winkey_input_driver.h"

#include "xenia/base/platform_win.h"
#include "xenia/hid/hid_flags.h"
#include "xenia/hid/input_system.h"
#include "xenia/ui/window.h"

namespace xe {
namespace hid {
namespace winkey {

WinKeyInputDriver::WinKeyInputDriver(xe::ui::Window* window)
    : InputDriver(window), packet_number_(1) {
  // Register a key listener.
  window_->on_key_down.AddListener([this](ui::KeyEvent* evt) {
    auto global_lock = global_critical_region_.Acquire();

    KeyEvent key;
    key.vkey = evt->key_code();
    key.transition = true;
    key.prev_state = evt->prev_state();
    key.repeat_count = evt->repeat_count();
    key_events_.push(key);
  });
  window_->on_key_up.AddListener([this](ui::KeyEvent* evt) {
    auto global_lock = global_critical_region_.Acquire();

    KeyEvent key;
    key.vkey = evt->key_code();
    key.transition = false;
    key.prev_state = evt->prev_state();
    key.repeat_count = evt->repeat_count();
    key_events_.push(key);
  });
}

WinKeyInputDriver::~WinKeyInputDriver() = default;

X_STATUS WinKeyInputDriver::Setup() { return X_STATUS_SUCCESS; }

X_RESULT WinKeyInputDriver::GetCapabilities(uint32_t user_index, uint32_t flags,
                                            X_INPUT_CAPABILITIES* out_caps) {
  if (user_index != 0) {
    return X_ERROR_DEVICE_NOT_CONNECTED;
  }

  // TODO(benvanik): confirm with a real XInput controller.
  out_caps->type = 0x01;      // XINPUT_DEVTYPE_GAMEPAD
  out_caps->sub_type = 0x01;  // XINPUT_DEVSUBTYPE_GAMEPAD
  out_caps->flags = 0;
  out_caps->gamepad.buttons = 0xFFFF;
  out_caps->gamepad.left_trigger = 0xFF;
  out_caps->gamepad.right_trigger = 0xFF;
  out_caps->gamepad.thumb_lx = (int16_t)0xFFFFu;
  out_caps->gamepad.thumb_ly = (int16_t)0xFFFFu;
  out_caps->gamepad.thumb_rx = (int16_t)0xFFFFu;
  out_caps->gamepad.thumb_ry = (int16_t)0xFFFFu;
  out_caps->vibration.left_motor_speed = 0;
  out_caps->vibration.right_motor_speed = 0;
  return X_ERROR_SUCCESS;
}

#define IS_KEY_TOGGLED(key) ((GetKeyState(key) & 0x1) == 0x1)
#define IS_KEY_DOWN(key) ((GetAsyncKeyState(key) & 0x8000) == 0x8000)

X_RESULT WinKeyInputDriver::GetState(uint32_t user_index,
                                     X_INPUT_STATE* out_state) {
  if (user_index != 0) {
    return X_ERROR_DEVICE_NOT_CONNECTED;
  }

  packet_number_++;

  uint16_t buttons = 0;
  uint8_t left_trigger = 0;
  uint8_t right_trigger = 0;
  int16_t thumb_lx = 0;
  int16_t thumb_ly = 0;
  int16_t thumb_rx = 0;
  int16_t thumb_ry = 0;

  if (window_->has_focus()) {
    if (IS_KEY_TOGGLED(VK_CAPITAL)) {
      // dpad toggled
      if (IS_KEY_DOWN(0x41)) {
        // A
        buttons |= 0x0004;  // XINPUT_GAMEPAD_DPAD_LEFT
      }
      if (IS_KEY_DOWN(0x44)) {
        // D
        buttons |= 0x0008;  // XINPUT_GAMEPAD_DPAD_RIGHT
      }
      if (IS_KEY_DOWN(0x53)) {
        // S
        buttons |= 0x0002;  // XINPUT_GAMEPAD_DPAD_DOWN
      }
      if (IS_KEY_DOWN(0x57)) {
        // W
        buttons |= 0x0001;  // XINPUT_GAMEPAD_DPAD_UP
      }
    } else {
      // left stick
      if (IS_KEY_DOWN(0x41)) {
        // A
        thumb_lx += SHRT_MIN;
      }
      if (IS_KEY_DOWN(0x44)) {
        // D
        thumb_lx += SHRT_MAX;
      }
      if (IS_KEY_DOWN(0x53)) {
        // S
        thumb_ly += SHRT_MIN;
      }
      if (IS_KEY_DOWN(0x57)) {
        // W
        thumb_ly += SHRT_MAX;
      }
    }

    // Right stick
    if (IS_KEY_DOWN(0x26)) {
      // Up
      thumb_ry += SHRT_MAX;
    }
    if (IS_KEY_DOWN(0x28)) {
      // Down
      thumb_ry += SHRT_MIN;
    }
    if (IS_KEY_DOWN(0x27)) {
      // Right
      thumb_rx += SHRT_MAX;
    }
    if (IS_KEY_DOWN(0x25)) {
      // Left
      thumb_rx += SHRT_MIN;
    }

    if (IS_KEY_DOWN(0x4C)) {
      // L
      buttons |= 0x4000;  // XINPUT_GAMEPAD_X
    }
    if (IS_KEY_DOWN(VK_OEM_7)) {
      // '
      buttons |= 0x2000;  // XINPUT_GAMEPAD_B
    }
    if (IS_KEY_DOWN(VK_OEM_1)) {
      // ;
      buttons |= 0x1000;  // XINPUT_GAMEPAD_A
    }
    if (IS_KEY_DOWN(0x50)) {
      // P
      buttons |= 0x8000;  // XINPUT_GAMEPAD_Y
    }

    if (IS_KEY_DOWN(0x51) || IS_KEY_DOWN(0x49)) {
      // Q / I
      left_trigger = 0xFF;
    }

    if (IS_KEY_DOWN(0x45) || IS_KEY_DOWN(0x4F)) {
      // E / O
      right_trigger = 0xFF;
    }

    if (IS_KEY_DOWN(0x5A)) {
      // Z
      buttons |= 0x0020;  // XINPUT_GAMEPAD_BACK
    }
    if (IS_KEY_DOWN(0x58)) {
      // X
      buttons |= 0x0010;  // XINPUT_GAMEPAD_START
    }
  }

  out_state->packet_number = packet_number_;
  out_state->gamepad.buttons = buttons;
  out_state->gamepad.left_trigger = left_trigger;
  out_state->gamepad.right_trigger = right_trigger;
  out_state->gamepad.thumb_lx = thumb_lx;
  out_state->gamepad.thumb_ly = thumb_ly;
  out_state->gamepad.thumb_rx = thumb_rx;
  out_state->gamepad.thumb_ry = thumb_ry;

  return X_ERROR_SUCCESS;
}

X_RESULT WinKeyInputDriver::SetState(uint32_t user_index,
                                     X_INPUT_VIBRATION* vibration) {
  if (user_index != 0) {
    return X_ERROR_DEVICE_NOT_CONNECTED;
  }

  return X_ERROR_SUCCESS;
}

X_RESULT WinKeyInputDriver::GetKeystroke(uint32_t user_index, uint32_t flags,
                                         X_INPUT_KEYSTROKE* out_keystroke) {
  if (user_index != 0) {
    return X_ERROR_DEVICE_NOT_CONNECTED;
  }

  X_RESULT result = X_ERROR_EMPTY;

  uint16_t virtual_key = 0;
  uint16_t unicode = 0;
  uint16_t keystroke_flags = 0;
  uint8_t hid_code = 0;

  // Pop from the queue.
  KeyEvent evt;
  {
    auto global_lock = global_critical_region_.Acquire();
    if (key_events_.empty()) {
      // No keys!
      return X_ERROR_EMPTY;
    }
    evt = key_events_.front();
    key_events_.pop();
  }

  // TODO(DrChat): Some other way to toggle this...
  if (IS_KEY_TOGGLED(VK_CAPITAL)) {
    // dpad toggled
    if (evt.vkey == (0x41)) {
      // A
      virtual_key = 0x5812;  // VK_PAD_DPAD_LEFT
    } else if (evt.vkey == (0x44)) {
      // D
      virtual_key = 0x5813;  // VK_PAD_DPAD_RIGHT
    } else if (evt.vkey == (0x53)) {
      // S
      virtual_key = 0x5811;  // VK_PAD_DPAD_DOWN
    } else if (evt.vkey == (0x57)) {
      // W
      virtual_key = 0x5810;  // VK_PAD_DPAD_UP
    }
  } else {
    // left stick
    if (evt.vkey == (0x57)) {
      // W
      virtual_key = 0x5820;  // VK_PAD_LTHUMB_UP
    }
    if (evt.vkey == (0x53)) {
      // S
      virtual_key = 0x5821;  // VK_PAD_LTHUMB_DOWN
    }
    if (evt.vkey == (0x44)) {
      // D
      virtual_key = 0x5822;  // VK_PAD_LTHUMB_RIGHT
    }
    if (evt.vkey == (0x41)) {
      // A
      virtual_key = 0x5823;  // VK_PAD_LTHUMB_LEFT
    }
  }

  // Right stick
  if (evt.vkey == (0x26)) {
    // Up
    virtual_key = 0x5830;
  }
  if (evt.vkey == (0x28)) {
    // Down
    virtual_key = 0x5831;
  }
  if (evt.vkey == (0x27)) {
    // Right
    virtual_key = 0x5832;
  }
  if (evt.vkey == (0x25)) {
    // Left
    virtual_key = 0x5833;
  }

  if (evt.vkey == (0x4C)) {
    // L
    virtual_key = 0x5802;  // VK_PAD_X
  } else if (evt.vkey == (VK_OEM_7)) {
    // '
    virtual_key = 0x5801;  // VK_PAD_B
  } else if (evt.vkey == (VK_OEM_1)) {
    // ;
    virtual_key = 0x5800;  // VK_PAD_A
  } else if (evt.vkey == (0x50)) {
    // P
    virtual_key = 0x5803;  // VK_PAD_Y
  }

  if (evt.vkey == (0x58)) {
    // X
    virtual_key = 0x5814;  // VK_PAD_START
  }
  if (evt.vkey == (0x5A)) {
    // Z
    virtual_key = 0x5815;  // VK_PAD_BACK
  }

  if (virtual_key != 0) {
    if (evt.transition == true) {
      keystroke_flags |= 0x0001;  // XINPUT_KEYSTROKE_KEYDOWN
    } else if (evt.transition == false) {
      keystroke_flags |= 0x0002;  // XINPUT_KEYSTROKE_KEYUP
    }

    if (evt.prev_state == evt.transition) {
      keystroke_flags |= 0x0004;  // XINPUT_KEYSTROKE_REPEAT
    }

    result = X_ERROR_SUCCESS;
  }

  out_keystroke->virtual_key = virtual_key;
  out_keystroke->unicode = unicode;
  out_keystroke->flags = keystroke_flags;
  out_keystroke->user_index = 0;
  out_keystroke->hid_code = hid_code;

  // X_ERROR_EMPTY if no new keys
  // X_ERROR_DEVICE_NOT_CONNECTED if no device
  // X_ERROR_SUCCESS if key
  return result;
}

}  // namespace winkey
}  // namespace hid
}  // namespace xe
<commit_msg>Added keyboard support for triggers and shoulder buttons<commit_after>/**
 ******************************************************************************
 * Xenia : Xbox 360 Emulator Research Project                                 *
 ******************************************************************************
 * Copyright 2014 Ben Vanik. All rights reserved.                             *
 * Released under the BSD license - see LICENSE in the root for more details. *
 ******************************************************************************
 */

#include "xenia/hid/winkey/winkey_input_driver.h"

#include "xenia/base/platform_win.h"
#include "xenia/hid/hid_flags.h"
#include "xenia/hid/input_system.h"
#include "xenia/ui/window.h"

namespace xe {
namespace hid {
namespace winkey {

WinKeyInputDriver::WinKeyInputDriver(xe::ui::Window* window)
    : InputDriver(window), packet_number_(1) {
  // Register a key listener.
  window_->on_key_down.AddListener([this](ui::KeyEvent* evt) {
    auto global_lock = global_critical_region_.Acquire();

    KeyEvent key;
    key.vkey = evt->key_code();
    key.transition = true;
    key.prev_state = evt->prev_state();
    key.repeat_count = evt->repeat_count();
    key_events_.push(key);
  });
  window_->on_key_up.AddListener([this](ui::KeyEvent* evt) {
    auto global_lock = global_critical_region_.Acquire();

    KeyEvent key;
    key.vkey = evt->key_code();
    key.transition = false;
    key.prev_state = evt->prev_state();
    key.repeat_count = evt->repeat_count();
    key_events_.push(key);
  });
}

WinKeyInputDriver::~WinKeyInputDriver() = default;

X_STATUS WinKeyInputDriver::Setup() { return X_STATUS_SUCCESS; }

X_RESULT WinKeyInputDriver::GetCapabilities(uint32_t user_index, uint32_t flags,
                                            X_INPUT_CAPABILITIES* out_caps) {
  if (user_index != 0) {
    return X_ERROR_DEVICE_NOT_CONNECTED;
  }

  // TODO(benvanik): confirm with a real XInput controller.
  out_caps->type = 0x01;      // XINPUT_DEVTYPE_GAMEPAD
  out_caps->sub_type = 0x01;  // XINPUT_DEVSUBTYPE_GAMEPAD
  out_caps->flags = 0;
  out_caps->gamepad.buttons = 0xFFFF;
  out_caps->gamepad.left_trigger = 0xFF;
  out_caps->gamepad.right_trigger = 0xFF;
  out_caps->gamepad.thumb_lx = (int16_t)0xFFFFu;
  out_caps->gamepad.thumb_ly = (int16_t)0xFFFFu;
  out_caps->gamepad.thumb_rx = (int16_t)0xFFFFu;
  out_caps->gamepad.thumb_ry = (int16_t)0xFFFFu;
  out_caps->vibration.left_motor_speed = 0;
  out_caps->vibration.right_motor_speed = 0;
  return X_ERROR_SUCCESS;
}

#define IS_KEY_TOGGLED(key) ((GetKeyState(key) & 0x1) == 0x1)
#define IS_KEY_DOWN(key) ((GetAsyncKeyState(key) & 0x8000) == 0x8000)

X_RESULT WinKeyInputDriver::GetState(uint32_t user_index,
                                     X_INPUT_STATE* out_state) {
  if (user_index != 0) {
    return X_ERROR_DEVICE_NOT_CONNECTED;
  }

  packet_number_++;

  uint16_t buttons = 0;
  uint8_t left_trigger = 0;
  uint8_t right_trigger = 0;
  int16_t thumb_lx = 0;
  int16_t thumb_ly = 0;
  int16_t thumb_rx = 0;
  int16_t thumb_ry = 0;

  if (window_->has_focus()) {
    if (IS_KEY_TOGGLED(VK_CAPITAL)) {
      // dpad toggled
      if (IS_KEY_DOWN(0x41)) {
        // A
        buttons |= 0x0004;  // XINPUT_GAMEPAD_DPAD_LEFT
      }
      if (IS_KEY_DOWN(0x44)) {
        // D
        buttons |= 0x0008;  // XINPUT_GAMEPAD_DPAD_RIGHT
      }
      if (IS_KEY_DOWN(0x53)) {
        // S
        buttons |= 0x0002;  // XINPUT_GAMEPAD_DPAD_DOWN
      }
      if (IS_KEY_DOWN(0x57)) {
        // W
        buttons |= 0x0001;  // XINPUT_GAMEPAD_DPAD_UP
      }
    } else {
      // left stick
      if (IS_KEY_DOWN(0x41)) {
        // A
        thumb_lx += SHRT_MIN;
      }
      if (IS_KEY_DOWN(0x44)) {
        // D
        thumb_lx += SHRT_MAX;
      }
      if (IS_KEY_DOWN(0x53)) {
        // S
        thumb_ly += SHRT_MIN;
      }
      if (IS_KEY_DOWN(0x57)) {
        // W
        thumb_ly += SHRT_MAX;
      }
    }

    // Right stick
    if (IS_KEY_DOWN(0x26)) {
      // Up
      thumb_ry += SHRT_MAX;
    }
    if (IS_KEY_DOWN(0x28)) {
      // Down
      thumb_ry += SHRT_MIN;
    }
    if (IS_KEY_DOWN(0x27)) {
      // Right
      thumb_rx += SHRT_MAX;
    }
    if (IS_KEY_DOWN(0x25)) {
      // Left
      thumb_rx += SHRT_MIN;
    }

    if (IS_KEY_DOWN(0x4C)) {
      // L
      buttons |= 0x4000;  // XINPUT_GAMEPAD_X
    }
    if (IS_KEY_DOWN(VK_OEM_7)) {
      // '
      buttons |= 0x2000;  // XINPUT_GAMEPAD_B
    }
    if (IS_KEY_DOWN(VK_OEM_1)) {
      // ;
      buttons |= 0x1000;  // XINPUT_GAMEPAD_A
    }
    if (IS_KEY_DOWN(0x50)) {
      // P
      buttons |= 0x8000;  // XINPUT_GAMEPAD_Y
    }

    if (IS_KEY_DOWN(0x51) || IS_KEY_DOWN(0x49)) {
      // Q / I
      left_trigger = 0xFF;
    }

    if (IS_KEY_DOWN(0x45) || IS_KEY_DOWN(0x4F)) {
      // E / O
      right_trigger = 0xFF;
    }

    if (IS_KEY_DOWN(0x5A)) {
      // Z
      buttons |= 0x0020;  // XINPUT_GAMEPAD_BACK
    }
    if (IS_KEY_DOWN(0x58)) {
      // X
      buttons |= 0x0010;  // XINPUT_GAMEPAD_START
    }
    if (IS_KEY_DOWN(0x31)) {
      // 1
      buttons |= 0x0100;  // XINPUT_GAMEPAD_LEFT_SHOULDER
    }
    if (IS_KEY_DOWN(0x33)) {
      // 3
      buttons |= 0x0200;  // XINPUT_GAMEPAD_RIGHT_SHOULDER
    }
  }

  out_state->packet_number = packet_number_;
  out_state->gamepad.buttons = buttons;
  out_state->gamepad.left_trigger = left_trigger;
  out_state->gamepad.right_trigger = right_trigger;
  out_state->gamepad.thumb_lx = thumb_lx;
  out_state->gamepad.thumb_ly = thumb_ly;
  out_state->gamepad.thumb_rx = thumb_rx;
  out_state->gamepad.thumb_ry = thumb_ry;

  return X_ERROR_SUCCESS;
}

X_RESULT WinKeyInputDriver::SetState(uint32_t user_index,
                                     X_INPUT_VIBRATION* vibration) {
  if (user_index != 0) {
    return X_ERROR_DEVICE_NOT_CONNECTED;
  }

  return X_ERROR_SUCCESS;
}

X_RESULT WinKeyInputDriver::GetKeystroke(uint32_t user_index, uint32_t flags,
                                         X_INPUT_KEYSTROKE* out_keystroke) {
  if (user_index != 0) {
    return X_ERROR_DEVICE_NOT_CONNECTED;
  }

  X_RESULT result = X_ERROR_EMPTY;

  uint16_t virtual_key = 0;
  uint16_t unicode = 0;
  uint16_t keystroke_flags = 0;
  uint8_t hid_code = 0;

  // Pop from the queue.
  KeyEvent evt;
  {
    auto global_lock = global_critical_region_.Acquire();
    if (key_events_.empty()) {
      // No keys!
      return X_ERROR_EMPTY;
    }
    evt = key_events_.front();
    key_events_.pop();
  }

  // TODO(DrChat): Some other way to toggle this...
  if (IS_KEY_TOGGLED(VK_CAPITAL)) {
    // dpad toggled
    if (evt.vkey == (0x41)) {
      // A
      virtual_key = 0x5812;  // VK_PAD_DPAD_LEFT
    } else if (evt.vkey == (0x44)) {
      // D
      virtual_key = 0x5813;  // VK_PAD_DPAD_RIGHT
    } else if (evt.vkey == (0x53)) {
      // S
      virtual_key = 0x5811;  // VK_PAD_DPAD_DOWN
    } else if (evt.vkey == (0x57)) {
      // W
      virtual_key = 0x5810;  // VK_PAD_DPAD_UP
    }
  } else {
    // left stick
    if (evt.vkey == (0x57)) {
      // W
      virtual_key = 0x5820;  // VK_PAD_LTHUMB_UP
    }
    if (evt.vkey == (0x53)) {
      // S
      virtual_key = 0x5821;  // VK_PAD_LTHUMB_DOWN
    }
    if (evt.vkey == (0x44)) {
      // D
      virtual_key = 0x5822;  // VK_PAD_LTHUMB_RIGHT
    }
    if (evt.vkey == (0x41)) {
      // A
      virtual_key = 0x5823;  // VK_PAD_LTHUMB_LEFT
    }
  }

  // Right stick
  if (evt.vkey == (0x26)) {
    // Up
    virtual_key = 0x5830;
  }
  if (evt.vkey == (0x28)) {
    // Down
    virtual_key = 0x5831;
  }
  if (evt.vkey == (0x27)) {
    // Right
    virtual_key = 0x5832;
  }
  if (evt.vkey == (0x25)) {
    // Left
    virtual_key = 0x5833;
  }

  if (evt.vkey == (0x4C)) {
    // L
    virtual_key = 0x5802;  // VK_PAD_X
  } else if (evt.vkey == (VK_OEM_7)) {
    // '
    virtual_key = 0x5801;  // VK_PAD_B
  } else if (evt.vkey == (VK_OEM_1)) {
    // ;
    virtual_key = 0x5800;  // VK_PAD_A
  } else if (evt.vkey == (0x50)) {
    // P
    virtual_key = 0x5803;  // VK_PAD_Y
  }

  if (evt.vkey == (0x58)) {
    // X
    virtual_key = 0x5814;  // VK_PAD_START
  }
  if (evt.vkey == (0x5A)) {
    // Z
    virtual_key = 0x5815;  // VK_PAD_BACK
  }
  if (evt.vkey == (0x51) || evt.vkey == (0x49)) {
    // Q / I
    virtual_key = 0x5806;  // VK_PAD_LTRIGGER
  }
  if (evt.vkey == (0x45) || evt.vkey == (0x4F)) {
    // E / O
    virtual_key = 0x5807;  // VK_PAD_RTRIGGER
  }
  if (evt.vkey == (0x31)) {
    // 1
    virtual_key = 0x5805;  // VK_PAD_LSHOULDER
  }
  if (evt.vkey == (0x33)) {
    // 3
    virtual_key = 0x5804;  // VK_PAD_RSHOULDER
  }

  if (virtual_key != 0) {
    if (evt.transition == true) {
      keystroke_flags |= 0x0001;  // XINPUT_KEYSTROKE_KEYDOWN
    } else if (evt.transition == false) {
      keystroke_flags |= 0x0002;  // XINPUT_KEYSTROKE_KEYUP
    }

    if (evt.prev_state == evt.transition) {
      keystroke_flags |= 0x0004;  // XINPUT_KEYSTROKE_REPEAT
    }

    result = X_ERROR_SUCCESS;
  }

  out_keystroke->virtual_key = virtual_key;
  out_keystroke->unicode = unicode;
  out_keystroke->flags = keystroke_flags;
  out_keystroke->user_index = 0;
  out_keystroke->hid_code = hid_code;

  // X_ERROR_EMPTY if no new keys
  // X_ERROR_DEVICE_NOT_CONNECTED if no device
  // X_ERROR_SUCCESS if key
  return result;
}

}  // namespace winkey
}  // namespace hid
}  // namespace xe
<|endoftext|>


<commit_before>#pragma once

#include <assert.h>
#include <mutex>
#include <iostream>

/** Thread safe output stream.
  *
  * Insert SafeStream::begin to start a session => lock the stream
  * Insert SafeStream::end to stop a session => unlock the stream.
  * Insert anything else to insert in the protected stream (requires a session).
  *
  * @todo It would be better if the open/close session was part of a trait
  * class instead of virtual but this would imply partial specialization for
  * the insertion operator, which is a pain in the ass in c++O3.
  */
class SafeStream
{
public:
    /** @param[in] stream is the stream to protect from concurrent access. */
    SafeStream(std::ostream & stream) : lock_taken(false), _output(stream) {}
    virtual ~SafeStream() {};

    /** Manipulator: type used to begin a session. */
    struct Begin {};
    /** A static instance of Begin to avoid instantiating one for each session start */
    static const Begin begin;

    /** Manipulator: type used to end a session. */
    struct End {};
    /** A static instance of End to avoid instantiating one for each session stop. */
    static const End end;

    /** The insertion operator.
      *
      * @tparam T If Begin or End, start or end a session.
      *           If any other type, append to the stream using operator <<.
      * @param[in] ti The object to be output or a manipulator.
      */
    template<class T>
    SafeStream & operator<< (const T & ti);

private:
    /** Open session */
    virtual void openSession() {}
    /** Close session */
    virtual void closeSession() {}

    /** Start a session. */
    void start() {
        lock();
        openSession();
    }
    /** End the current session. */
    void stop() {
        closeSession();
        unlock();
    }
    /** Lock the stream */
    void lock() {
        _mutex.lock();
        lock_taken = true;
    }
    /** Unlock the stream */
    void unlock() {
        assert(lock_taken);
        lock_taken = false;
        _mutex.unlock();
    }

    /** The mutex state, for debug purpose only. */
    bool lock_taken;

    /** The stream mutex*/
    std::mutex _mutex;
    /** The concurrency access protected stream. */
    std::ostream & _output;
};

const SafeStream::Begin SafeStream::begin;
const SafeStream::End SafeStream::end;

/** Add a token to stream. */
template<class T>
SafeStream & SafeStream::operator<< (const T & ti) {
    assert(lock_taken);
    _output << ti;
    return *this;
}

/** Start a session.
  *
  * Will block if is a session is already started.
  */
template<>
SafeStream & SafeStream::operator<< <SafeStream::Begin> (const SafeStream::Begin &) {
    start();
    return *this;
}

/** Stop the current session. */
template<>
SafeStream & SafeStream::operator<< <SafeStream::End> (const SafeStream::End &) {
    stop();
    return *this;
}
<commit_msg>Use cassert instead of assert.h<commit_after>#pragma once

#include <cassert>
#include <mutex>
#include <iostream>

/** Thread safe output stream.
  *
  * Insert SafeStream::begin to start a session => lock the stream
  * Insert SafeStream::end to stop a session => unlock the stream.
  * Insert anything else to insert in the protected stream (requires a session).
  *
  * @todo It would be better if the open/close session was part of a trait
  * class instead of virtual but this would imply partial specialization for
  * the insertion operator, which is a pain in the ass in c++O3.
  */
class SafeStream
{
public:
    /** @param[in] stream is the stream to protect from concurrent access. */
    SafeStream(std::ostream & stream) : lock_taken(false), _output(stream) {}
    virtual ~SafeStream() {};

    /** Manipulator: type used to begin a session. */
    struct Begin {};
    /** A static instance of Begin to avoid instantiating one for each session start */
    static const Begin begin;

    /** Manipulator: type used to end a session. */
    struct End {};
    /** A static instance of End to avoid instantiating one for each session stop. */
    static const End end;

    /** The insertion operator.
      *
      * @tparam T If Begin or End, start or end a session.
      *           If any other type, append to the stream using operator <<.
      * @param[in] ti The object to be output or a manipulator.
      */
    template<class T>
    SafeStream & operator<< (const T & ti);

private:
    /** Open session */
    virtual void openSession() {}
    /** Close session */
    virtual void closeSession() {}

    /** Start a session. */
    void start() {
        lock();
        openSession();
    }
    /** End the current session. */
    void stop() {
        closeSession();
        unlock();
    }
    /** Lock the stream */
    void lock() {
        _mutex.lock();
        lock_taken = true;
    }
    /** Unlock the stream */
    void unlock() {
        assert(lock_taken);
        lock_taken = false;
        _mutex.unlock();
    }

    /** The mutex state, for debug purpose only. */
    bool lock_taken;

    /** The stream mutex*/
    std::mutex _mutex;
    /** The concurrency access protected stream. */
    std::ostream & _output;
};

const SafeStream::Begin SafeStream::begin;
const SafeStream::End SafeStream::end;

/** Add a token to stream. */
template<class T>
SafeStream & SafeStream::operator<< (const T & ti) {
    assert(lock_taken);
    _output << ti;
    return *this;
}

/** Start a session.
  *
  * Will block if is a session is already started.
  */
template<>
SafeStream & SafeStream::operator<< <SafeStream::Begin> (const SafeStream::Begin &) {
    start();
    return *this;
}

/** Stop the current session. */
template<>
SafeStream & SafeStream::operator<< <SafeStream::End> (const SafeStream::End &) {
    stop();
    return *this;
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2020 ScyllaDB
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla 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 Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "size_tiered_compaction_strategy.hh"
#include <boost/range/adaptor/transformed.hpp>
#include <boost/range/adaptors.hpp>
#include <boost/range/algorithm.hpp>

namespace sstables {

size_tiered_compaction_strategy_options::size_tiered_compaction_strategy_options(const std::map<sstring, sstring>& options) {
    using namespace cql3::statements;

    auto tmp_value = compaction_strategy_impl::get_value(options, MIN_SSTABLE_SIZE_KEY);
    min_sstable_size = property_definitions::to_long(MIN_SSTABLE_SIZE_KEY, tmp_value, DEFAULT_MIN_SSTABLE_SIZE);

    tmp_value = compaction_strategy_impl::get_value(options, BUCKET_LOW_KEY);
    bucket_low = property_definitions::to_double(BUCKET_LOW_KEY, tmp_value, DEFAULT_BUCKET_LOW);

    tmp_value = compaction_strategy_impl::get_value(options, BUCKET_HIGH_KEY);
    bucket_high = property_definitions::to_double(BUCKET_HIGH_KEY, tmp_value, DEFAULT_BUCKET_HIGH);

    tmp_value = compaction_strategy_impl::get_value(options, COLD_READS_TO_OMIT_KEY);
    cold_reads_to_omit = property_definitions::to_double(COLD_READS_TO_OMIT_KEY, tmp_value, DEFAULT_COLD_READS_TO_OMIT);
}

size_tiered_compaction_strategy_options::size_tiered_compaction_strategy_options() {
    min_sstable_size = DEFAULT_MIN_SSTABLE_SIZE;
    bucket_low = DEFAULT_BUCKET_LOW;
    bucket_high = DEFAULT_BUCKET_HIGH;
    cold_reads_to_omit = DEFAULT_COLD_READS_TO_OMIT;
}

std::vector<std::pair<sstables::shared_sstable, uint64_t>>
size_tiered_compaction_strategy::create_sstable_and_length_pairs(const std::vector<sstables::shared_sstable>& sstables) {

    std::vector<std::pair<sstables::shared_sstable, uint64_t>> sstable_length_pairs;
    sstable_length_pairs.reserve(sstables.size());

    for(auto& sstable : sstables) {
        auto sstable_size = sstable->data_size();
        assert(sstable_size != 0);

        sstable_length_pairs.emplace_back(sstable, sstable_size);
    }

    return sstable_length_pairs;
}

std::vector<std::vector<sstables::shared_sstable>>
size_tiered_compaction_strategy::get_buckets(const std::vector<sstables::shared_sstable>& sstables, size_tiered_compaction_strategy_options options) {
    // sstables sorted by size of its data file.
    auto sorted_sstables = create_sstable_and_length_pairs(sstables);

    std::sort(sorted_sstables.begin(), sorted_sstables.end(), [] (auto& i, auto& j) {
        return i.second < j.second;
    });

    using bucket_type = std::vector<sstables::shared_sstable>;
    std::vector<bucket_type> bucket_list;
    std::vector<size_t> bucket_average_size_list;

    for (auto& pair : sorted_sstables) {
        size_t size = pair.second;

        // look for a bucket containing similar-sized files:
        // group in the same bucket if it's w/in 50% of the average for this bucket,
        // or this file and the bucket are all considered "small" (less than `minSSTableSize`)
        if (!bucket_list.empty()) {
            // FIXME: rename old_average_size since it's being assigned below
            auto& old_average_size = bucket_average_size_list.back();

            if ((size > (old_average_size * options.bucket_low) && size < (old_average_size * options.bucket_high)) ||
                    (size < options.min_sstable_size && old_average_size < options.min_sstable_size)) {
                auto& bucket = bucket_list.back();
                size_t total_size = bucket.size() * old_average_size;
                size_t new_average_size = (total_size + size) / (bucket.size() + 1);

                bucket.push_back(pair.first);
                old_average_size = new_average_size;
                continue;
            }
        }

        // no similar bucket found; put it in a new one
        bucket_type new_bucket = {pair.first};
        bucket_list.push_back(std::move(new_bucket));
        bucket_average_size_list.push_back(size);
    }

    return bucket_list;
}

std::vector<std::vector<sstables::shared_sstable>>
size_tiered_compaction_strategy::get_buckets(const std::vector<sstables::shared_sstable>& sstables) const {
    return get_buckets(sstables, _options);
}

std::vector<sstables::shared_sstable>
size_tiered_compaction_strategy::most_interesting_bucket(std::vector<std::vector<sstables::shared_sstable>> buckets,
        unsigned min_threshold, unsigned max_threshold)
{
    std::vector<std::pair<std::vector<sstables::shared_sstable>, uint64_t>> pruned_buckets_and_hotness;
    pruned_buckets_and_hotness.reserve(buckets.size());

    // FIXME: add support to get hotness for each bucket.

    for (auto& bucket : buckets) {
        // FIXME: the coldest sstables will be trimmed to meet the threshold, so we must add support to this feature
        // by converting SizeTieredCompactionStrategy::trimToThresholdWithHotness.
        // By the time being, we will only compact buckets that meet the threshold.
        bucket.resize(std::min(bucket.size(), size_t(max_threshold)));
        if (is_bucket_interesting(bucket, min_threshold)) {
            auto avg = avg_size(bucket);
            pruned_buckets_and_hotness.push_back({ std::move(bucket), avg });
        }
    }

    if (pruned_buckets_and_hotness.empty()) {
        return std::vector<sstables::shared_sstable>();
    }

    // NOTE: Compacting smallest sstables first, located at the beginning of the sorted vector.
    auto& min = *std::min_element(pruned_buckets_and_hotness.begin(), pruned_buckets_and_hotness.end(), [] (auto& i, auto& j) {
        // FIXME: ignoring hotness by the time being.

        return i.second < j.second;
    });
    auto hottest = std::move(min.first);

    return hottest;
}

compaction_descriptor
size_tiered_compaction_strategy::get_sstables_for_compaction(column_family& cfs, std::vector<sstables::shared_sstable> candidates) {
    // make local copies so they can't be changed out from under us mid-method
    int min_threshold = cfs.min_compaction_threshold();
    int max_threshold = cfs.schema()->max_compaction_threshold();
    auto gc_before = gc_clock::now() - cfs.schema()->gc_grace_seconds();

    // TODO: Add support to filter cold sstables (for reference: SizeTieredCompactionStrategy::filterColdSSTables).

    auto buckets = get_buckets(candidates);

    if (is_any_bucket_interesting(buckets, min_threshold)) {
        std::vector<sstables::shared_sstable> most_interesting = most_interesting_bucket(std::move(buckets), min_threshold, max_threshold);
        return sstables::compaction_descriptor(std::move(most_interesting), cfs.get_sstable_set(), service::get_local_compaction_priority());
    }

    // If we are not enforcing min_threshold explicitly, try any pair of SStables in the same tier.
    if (!cfs.compaction_enforce_min_threshold() && is_any_bucket_interesting(buckets, 2)) {
        std::vector<sstables::shared_sstable> most_interesting = most_interesting_bucket(std::move(buckets), 2, max_threshold);
        return sstables::compaction_descriptor(std::move(most_interesting), cfs.get_sstable_set(), service::get_local_compaction_priority());
    }

    // if there is no sstable to compact in standard way, try compacting single sstable whose droppable tombstone
    // ratio is greater than threshold.
    // prefer oldest sstables from biggest size tiers because they will be easier to satisfy conditions for
    // tombstone purge, i.e. less likely to shadow even older data.
    for (auto&& sstables : buckets | boost::adaptors::reversed) {
        // filter out sstables which droppable tombstone ratio isn't greater than the defined threshold.
        auto e = boost::range::remove_if(sstables, [this, &gc_before] (const sstables::shared_sstable& sst) -> bool {
            return !worth_dropping_tombstones(sst, gc_before);
        });
        sstables.erase(e, sstables.end());
        if (sstables.empty()) {
            continue;
        }
        // find oldest sstable from current tier
        auto it = std::min_element(sstables.begin(), sstables.end(), [] (auto& i, auto& j) {
            return i->get_stats_metadata().min_timestamp < j->get_stats_metadata().min_timestamp;
        });
        return sstables::compaction_descriptor({ *it }, cfs.get_sstable_set(), service::get_local_compaction_priority());
    }
    return sstables::compaction_descriptor();
}

int64_t size_tiered_compaction_strategy::estimated_pending_compactions(const std::vector<sstables::shared_sstable>& sstables,
        int min_threshold, int max_threshold, size_tiered_compaction_strategy_options options) {
    int64_t n = 0;
    for (auto& bucket : get_buckets(sstables, options)) {
        if (bucket.size() >= size_t(min_threshold)) {
            n += std::ceil(double(bucket.size()) / max_threshold);
        }
    }
    return n;
}

int64_t size_tiered_compaction_strategy::estimated_pending_compactions(column_family& cf) const {
    int min_threshold = cf.min_compaction_threshold();
    int max_threshold = cf.schema()->max_compaction_threshold();
    std::vector<sstables::shared_sstable> sstables;

    sstables.reserve(cf.sstables_count());
    for (auto all_sstables = cf.get_sstables(); auto& entry : *all_sstables) {
        sstables.push_back(entry);
    }

    return estimated_pending_compactions(sstables, min_threshold, max_threshold, _options);
}

std::vector<sstables::shared_sstable>
size_tiered_compaction_strategy::most_interesting_bucket(const std::vector<sstables::shared_sstable>& candidates,
        int min_threshold, int max_threshold, size_tiered_compaction_strategy_options options) {
    size_tiered_compaction_strategy cs(options);

    auto buckets = cs.get_buckets(candidates);

    std::vector<sstables::shared_sstable> most_interesting = cs.most_interesting_bucket(std::move(buckets),
        min_threshold, max_threshold);

    return most_interesting;
}

compaction_descriptor
size_tiered_compaction_strategy::get_reshaping_job(std::vector<shared_sstable> input, schema_ptr schema, const ::io_priority_class& iop, reshape_mode mode)
{
    size_t offstrategy_threshold = std::max(schema->min_compaction_threshold(), 4);
    size_t max_sstables = std::max(schema->max_compaction_threshold(), int(offstrategy_threshold));

    if (mode == reshape_mode::relaxed) {
        offstrategy_threshold = max_sstables;
    }

    for (auto& bucket : get_buckets(input)) {
        if (bucket.size() >= offstrategy_threshold) {
            // reshape job can work on #max_sstables sstables at once, so by reshaping sstables with the smallest tokens first,
            // token contiguity is preserved iff sstables are disjoint.
            if (bucket.size() > max_sstables) {
                std::partial_sort(bucket.begin(), bucket.begin() + max_sstables, bucket.end(), [&schema](const sstables::shared_sstable& a, const sstables::shared_sstable& b) {
                    return a->get_first_decorated_key().tri_compare(*schema, b->get_first_decorated_key()) <= 0;
                });
                bucket.resize(max_sstables);
            }
            compaction_descriptor desc(std::move(bucket), std::optional<sstables::sstable_set>(), iop);
            desc.options = compaction_options::make_reshape();
            return desc;
        }
    }

    return compaction_descriptor();
}

}
<commit_msg>compaction: size_tiered_compaction_strategy: get_buckets: rename old_average_size to bucket_average_size<commit_after>/*
 * Copyright (C) 2020 ScyllaDB
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla 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 Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "size_tiered_compaction_strategy.hh"
#include <boost/range/adaptor/transformed.hpp>
#include <boost/range/adaptors.hpp>
#include <boost/range/algorithm.hpp>

namespace sstables {

size_tiered_compaction_strategy_options::size_tiered_compaction_strategy_options(const std::map<sstring, sstring>& options) {
    using namespace cql3::statements;

    auto tmp_value = compaction_strategy_impl::get_value(options, MIN_SSTABLE_SIZE_KEY);
    min_sstable_size = property_definitions::to_long(MIN_SSTABLE_SIZE_KEY, tmp_value, DEFAULT_MIN_SSTABLE_SIZE);

    tmp_value = compaction_strategy_impl::get_value(options, BUCKET_LOW_KEY);
    bucket_low = property_definitions::to_double(BUCKET_LOW_KEY, tmp_value, DEFAULT_BUCKET_LOW);

    tmp_value = compaction_strategy_impl::get_value(options, BUCKET_HIGH_KEY);
    bucket_high = property_definitions::to_double(BUCKET_HIGH_KEY, tmp_value, DEFAULT_BUCKET_HIGH);

    tmp_value = compaction_strategy_impl::get_value(options, COLD_READS_TO_OMIT_KEY);
    cold_reads_to_omit = property_definitions::to_double(COLD_READS_TO_OMIT_KEY, tmp_value, DEFAULT_COLD_READS_TO_OMIT);
}

size_tiered_compaction_strategy_options::size_tiered_compaction_strategy_options() {
    min_sstable_size = DEFAULT_MIN_SSTABLE_SIZE;
    bucket_low = DEFAULT_BUCKET_LOW;
    bucket_high = DEFAULT_BUCKET_HIGH;
    cold_reads_to_omit = DEFAULT_COLD_READS_TO_OMIT;
}

std::vector<std::pair<sstables::shared_sstable, uint64_t>>
size_tiered_compaction_strategy::create_sstable_and_length_pairs(const std::vector<sstables::shared_sstable>& sstables) {

    std::vector<std::pair<sstables::shared_sstable, uint64_t>> sstable_length_pairs;
    sstable_length_pairs.reserve(sstables.size());

    for(auto& sstable : sstables) {
        auto sstable_size = sstable->data_size();
        assert(sstable_size != 0);

        sstable_length_pairs.emplace_back(sstable, sstable_size);
    }

    return sstable_length_pairs;
}

std::vector<std::vector<sstables::shared_sstable>>
size_tiered_compaction_strategy::get_buckets(const std::vector<sstables::shared_sstable>& sstables, size_tiered_compaction_strategy_options options) {
    // sstables sorted by size of its data file.
    auto sorted_sstables = create_sstable_and_length_pairs(sstables);

    std::sort(sorted_sstables.begin(), sorted_sstables.end(), [] (auto& i, auto& j) {
        return i.second < j.second;
    });

    using bucket_type = std::vector<sstables::shared_sstable>;
    std::vector<bucket_type> bucket_list;
    std::vector<size_t> bucket_average_size_list;

    for (auto& pair : sorted_sstables) {
        size_t size = pair.second;

        // look for a bucket containing similar-sized files:
        // group in the same bucket if it's w/in 50% of the average for this bucket,
        // or this file and the bucket are all considered "small" (less than `minSSTableSize`)
        if (!bucket_list.empty()) {
            auto& bucket_average_size = bucket_average_size_list.back();

            if ((size > (bucket_average_size * options.bucket_low) && size < (bucket_average_size * options.bucket_high)) ||
                    (size < options.min_sstable_size && bucket_average_size < options.min_sstable_size)) {
                auto& bucket = bucket_list.back();
                size_t total_size = bucket.size() * bucket_average_size;
                size_t new_average_size = (total_size + size) / (bucket.size() + 1);

                bucket.push_back(pair.first);
                bucket_average_size = new_average_size;
                continue;
            }
        }

        // no similar bucket found; put it in a new one
        bucket_type new_bucket = {pair.first};
        bucket_list.push_back(std::move(new_bucket));
        bucket_average_size_list.push_back(size);
    }

    return bucket_list;
}

std::vector<std::vector<sstables::shared_sstable>>
size_tiered_compaction_strategy::get_buckets(const std::vector<sstables::shared_sstable>& sstables) const {
    return get_buckets(sstables, _options);
}

std::vector<sstables::shared_sstable>
size_tiered_compaction_strategy::most_interesting_bucket(std::vector<std::vector<sstables::shared_sstable>> buckets,
        unsigned min_threshold, unsigned max_threshold)
{
    std::vector<std::pair<std::vector<sstables::shared_sstable>, uint64_t>> pruned_buckets_and_hotness;
    pruned_buckets_and_hotness.reserve(buckets.size());

    // FIXME: add support to get hotness for each bucket.

    for (auto& bucket : buckets) {
        // FIXME: the coldest sstables will be trimmed to meet the threshold, so we must add support to this feature
        // by converting SizeTieredCompactionStrategy::trimToThresholdWithHotness.
        // By the time being, we will only compact buckets that meet the threshold.
        bucket.resize(std::min(bucket.size(), size_t(max_threshold)));
        if (is_bucket_interesting(bucket, min_threshold)) {
            auto avg = avg_size(bucket);
            pruned_buckets_and_hotness.push_back({ std::move(bucket), avg });
        }
    }

    if (pruned_buckets_and_hotness.empty()) {
        return std::vector<sstables::shared_sstable>();
    }

    // NOTE: Compacting smallest sstables first, located at the beginning of the sorted vector.
    auto& min = *std::min_element(pruned_buckets_and_hotness.begin(), pruned_buckets_and_hotness.end(), [] (auto& i, auto& j) {
        // FIXME: ignoring hotness by the time being.

        return i.second < j.second;
    });
    auto hottest = std::move(min.first);

    return hottest;
}

compaction_descriptor
size_tiered_compaction_strategy::get_sstables_for_compaction(column_family& cfs, std::vector<sstables::shared_sstable> candidates) {
    // make local copies so they can't be changed out from under us mid-method
    int min_threshold = cfs.min_compaction_threshold();
    int max_threshold = cfs.schema()->max_compaction_threshold();
    auto gc_before = gc_clock::now() - cfs.schema()->gc_grace_seconds();

    // TODO: Add support to filter cold sstables (for reference: SizeTieredCompactionStrategy::filterColdSSTables).

    auto buckets = get_buckets(candidates);

    if (is_any_bucket_interesting(buckets, min_threshold)) {
        std::vector<sstables::shared_sstable> most_interesting = most_interesting_bucket(std::move(buckets), min_threshold, max_threshold);
        return sstables::compaction_descriptor(std::move(most_interesting), cfs.get_sstable_set(), service::get_local_compaction_priority());
    }

    // If we are not enforcing min_threshold explicitly, try any pair of SStables in the same tier.
    if (!cfs.compaction_enforce_min_threshold() && is_any_bucket_interesting(buckets, 2)) {
        std::vector<sstables::shared_sstable> most_interesting = most_interesting_bucket(std::move(buckets), 2, max_threshold);
        return sstables::compaction_descriptor(std::move(most_interesting), cfs.get_sstable_set(), service::get_local_compaction_priority());
    }

    // if there is no sstable to compact in standard way, try compacting single sstable whose droppable tombstone
    // ratio is greater than threshold.
    // prefer oldest sstables from biggest size tiers because they will be easier to satisfy conditions for
    // tombstone purge, i.e. less likely to shadow even older data.
    for (auto&& sstables : buckets | boost::adaptors::reversed) {
        // filter out sstables which droppable tombstone ratio isn't greater than the defined threshold.
        auto e = boost::range::remove_if(sstables, [this, &gc_before] (const sstables::shared_sstable& sst) -> bool {
            return !worth_dropping_tombstones(sst, gc_before);
        });
        sstables.erase(e, sstables.end());
        if (sstables.empty()) {
            continue;
        }
        // find oldest sstable from current tier
        auto it = std::min_element(sstables.begin(), sstables.end(), [] (auto& i, auto& j) {
            return i->get_stats_metadata().min_timestamp < j->get_stats_metadata().min_timestamp;
        });
        return sstables::compaction_descriptor({ *it }, cfs.get_sstable_set(), service::get_local_compaction_priority());
    }
    return sstables::compaction_descriptor();
}

int64_t size_tiered_compaction_strategy::estimated_pending_compactions(const std::vector<sstables::shared_sstable>& sstables,
        int min_threshold, int max_threshold, size_tiered_compaction_strategy_options options) {
    int64_t n = 0;
    for (auto& bucket : get_buckets(sstables, options)) {
        if (bucket.size() >= size_t(min_threshold)) {
            n += std::ceil(double(bucket.size()) / max_threshold);
        }
    }
    return n;
}

int64_t size_tiered_compaction_strategy::estimated_pending_compactions(column_family& cf) const {
    int min_threshold = cf.min_compaction_threshold();
    int max_threshold = cf.schema()->max_compaction_threshold();
    std::vector<sstables::shared_sstable> sstables;

    sstables.reserve(cf.sstables_count());
    for (auto all_sstables = cf.get_sstables(); auto& entry : *all_sstables) {
        sstables.push_back(entry);
    }

    return estimated_pending_compactions(sstables, min_threshold, max_threshold, _options);
}

std::vector<sstables::shared_sstable>
size_tiered_compaction_strategy::most_interesting_bucket(const std::vector<sstables::shared_sstable>& candidates,
        int min_threshold, int max_threshold, size_tiered_compaction_strategy_options options) {
    size_tiered_compaction_strategy cs(options);

    auto buckets = cs.get_buckets(candidates);

    std::vector<sstables::shared_sstable> most_interesting = cs.most_interesting_bucket(std::move(buckets),
        min_threshold, max_threshold);

    return most_interesting;
}

compaction_descriptor
size_tiered_compaction_strategy::get_reshaping_job(std::vector<shared_sstable> input, schema_ptr schema, const ::io_priority_class& iop, reshape_mode mode)
{
    size_t offstrategy_threshold = std::max(schema->min_compaction_threshold(), 4);
    size_t max_sstables = std::max(schema->max_compaction_threshold(), int(offstrategy_threshold));

    if (mode == reshape_mode::relaxed) {
        offstrategy_threshold = max_sstables;
    }

    for (auto& bucket : get_buckets(input)) {
        if (bucket.size() >= offstrategy_threshold) {
            // reshape job can work on #max_sstables sstables at once, so by reshaping sstables with the smallest tokens first,
            // token contiguity is preserved iff sstables are disjoint.
            if (bucket.size() > max_sstables) {
                std::partial_sort(bucket.begin(), bucket.begin() + max_sstables, bucket.end(), [&schema](const sstables::shared_sstable& a, const sstables::shared_sstable& b) {
                    return a->get_first_decorated_key().tri_compare(*schema, b->get_first_decorated_key()) <= 0;
                });
                bucket.resize(max_sstables);
            }
            compaction_descriptor desc(std::move(bucket), std::optional<sstables::sstable_set>(), iop);
            desc.options = compaction_options::make_reshape();
            return desc;
        }
    }

    return compaction_descriptor();
}

}
<|endoftext|>


<commit_before>#ifndef STAN_MATH_PRIM_MAT_FUN_MDIVIDE_LEFT_TRI_HPP
#define STAN_MATH_PRIM_MAT_FUN_MDIVIDE_LEFT_TRI_HPP

#include <boost/math/tools/promotion.hpp>
#include <stan/math/prim/mat/fun/Eigen.hpp>
#include <stan/math/prim/mat/fun/promote_common.hpp>
#include <stan/math/prim/mat/err/check_multiplicable.hpp>
#include <stan/math/prim/mat/err/check_square.hpp>

namespace stan {
namespace math {

/**
 * Returns the solution of the system Ax=b when A is triangular
 * @param A Triangular matrix.  Specify upper or lower with TriView
 * being Eigen::Upper or Eigen::Lower.
 * @param b Right hand side matrix or vector.
 * @return x = A^-1 b, solution of the linear system.
 * @throws std::domain_error if A is not square or the rows of b don't
 * match the size of A.
 */
template <int TriView, typename T1, typename T2, int R1, int C1, int R2, int C2>
inline Eigen::Matrix<typename boost::math::tools::promote_args<T1, T2>::type,
                     R1, C2>
mdivide_left_tri(const Eigen::Matrix<T1, R1, C1> &A,
                 const Eigen::Matrix<T2, R2, C2> &b) {
  check_square("mdivide_left_tri", "A", A);
  check_multiplicable("mdivide_left_tri", "A", A, "b", b);
  return promote_common<Eigen::Matrix<T1, R1, C1>, Eigen::Matrix<T2, R1, C1> >(
             A)
      .template triangularView<TriView>()
      .solve(
          promote_common<Eigen::Matrix<T1, R2, C2>, Eigen::Matrix<T2, R2, C2> >(
              b));
}

/**
 * Returns the solution of the system Ax=b when A is triangular and b=I.
 * @param A Triangular matrix.  Specify upper or lower with TriView
 * being Eigen::Upper or Eigen::Lower.
 * @return x = A^-1 .
 * @throws std::domain_error if A is not square
 */
template <int TriView, typename T, int R1, int C1>
inline Eigen::Matrix<T, R1, C1> mdivide_left_tri(
    const Eigen::Matrix<T, R1, C1> &A) {
  check_square("mdivide_left_tri", "A", A);
  int n = A.rows();
  Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic> b;
  b.setIdentity(n, n);
  A.template triangularView<TriView>().solveInPlace(b);
  return b;
}

}  // namespace math
}  // namespace stan
#endif
<commit_msg>mdivide prim<commit_after>#ifndef STAN_MATH_PRIM_MAT_FUN_MDIVIDE_LEFT_TRI_HPP
#define STAN_MATH_PRIM_MAT_FUN_MDIVIDE_LEFT_TRI_HPP

#include <boost/math/tools/promotion.hpp>
#include <stan/math/prim/mat/fun/Eigen.hpp>
#include <stan/math/prim/mat/fun/promote_common.hpp>
#include <stan/math/prim/mat/err/check_multiplicable.hpp>
#include <stan/math/prim/mat/err/check_square.hpp>
#ifdef STAN_OPENCL
#include <stan/math/opencl/opencl_context.hpp>
#include <stan/math/opencl/multiply.hpp>
#include <stan/math/opencl/lower_tri_inverse.hpp>
#include <stan/math/opencl/transpose.hpp>
#include <stan/math/opencl/copy.hpp>
#endif
namespace stan {
namespace math {

/**
 * Returns the solution of the system Ax=b when A is triangular
 * @param A Triangular matrix.  Specify upper or lower with TriView
 * being Eigen::Upper or Eigen::Lower.
 * @param b Right hand side matrix or vector.
 * @return x = A^-1 b, solution of the linear system.
 * @throws std::domain_error if A is not square or the rows of b don't
 * match the size of A.
 */
template <int TriView, typename T1, typename T2, int R1, int C1, int R2, int C2>
inline Eigen::Matrix<typename boost::math::tools::promote_args<T1, T2>::type,
                     R1, C2>
mdivide_left_tri(const Eigen::Matrix<T1, R1, C1> &A,
                 const Eigen::Matrix<T2, R2, C2> &b) {
  check_square("mdivide_left_tri", "A", A);
  check_multiplicable("mdivide_left_tri", "A", A, "b", b);
  return promote_common<Eigen::Matrix<T1, R1, C1>, Eigen::Matrix<T2, R1, C1> >(
             A)
      .template triangularView<TriView>()
      .solve(
          promote_common<Eigen::Matrix<T1, R2, C2>, Eigen::Matrix<T2, R2, C2> >(
              b));
}

/**
 * Returns the solution of the system Ax=b when A is triangular and b=I.
 * @param A Triangular matrix.  Specify upper or lower with TriView
 * being Eigen::Upper or Eigen::Lower.
 * @return x = A^-1 .
 * @throws std::domain_error if A is not square
 */
template <int TriView, typename T, int R1, int C1>
inline Eigen::Matrix<T, R1, C1> mdivide_left_tri(
    const Eigen::Matrix<T, R1, C1> &A) {
  check_square("mdivide_left_tri", "A", A);
  int n = A.rows();
  Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic> b;
  b.setIdentity(n, n);
  A.template triangularView<TriView>().solveInPlace(b);
  return b;
}

#ifdef STAN_OPENCL
/**
 * Returns the solution of the system Ax=b when A is triangular
 * @param A Triangular matrix.  Specify upper or lower with TriView
 * being Eigen::Upper or Eigen::Lower.
 * @param b Right hand side matrix or vector.
 * @return x = A^-1 b, solution of the linear system.
 * @throws std::domain_error if A is not square or the rows of b don't
 * match the size of A.
 */
template <int TriView, int R1, int C1, int R2, int C2>
inline Eigen::Matrix<double, R1, C2>
mdivide_left_tri(const Eigen::Matrix<double, R1, C1> &A,
                 const Eigen::Matrix<double, R2, C2> &b) {
  check_square("mdivide_left_tri", "A", A);
  check_multiplicable("mdivide_left_tri", "A", A, "b", b);
  if (A.rows() >=
      opencl_context.tuning_opts().lower_tri_inverse_size_worth_transfer) {
    matrix_cl A_cl(A);
    matrix_cl b_cl(b);
    matrix_cl A_inv_cl(A.rows(), A.cols());
    if (TriView == Eigen::Lower) {
      A_inv_cl = lower_triangular_inverse(A_cl);
    } else {
      A_inv_cl = transpose(lower_triangular_inverse(transpose(A_cl)));
    }
    Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> C(A.rows(),
                                                                 A.cols());
    auto C_cl = A_inv_cl * b_cl;
    copy(C, C_cl);  // NOLINT
    return C;
  } else {
    return promote_common<Eigen::Matrix<double, R1, C1>, Eigen::Matrix<double, R1, C1> >(
             A)
      .template triangularView<TriView>()
      .solve(
          promote_common<Eigen::Matrix<double, R2, C2>, Eigen::Matrix<double, R2, C2> >(
              b));
  }
}

/**
 * Returns the solution of the system Ax=b when A is triangular and b=I.
 * @param A Triangular matrix.  Specify upper or lower with TriView
 * being Eigen::Upper or Eigen::Lower.
 * @return x = A^-1 .
 * @note Because OpenCL only works on doubles there are two
 * mdivide_left_tri(A) functions. One that works on doubles
 * (this one) and another that works on all other types.
 * @throws std::domain_error if A is not square
 */
template <int TriView, int R1, int C1>
inline Eigen::Matrix<double, R1, C1> mdivide_left_tri(
    const Eigen::Matrix<double, R1, C1> &A) {
  check_square("mdivide_left_tri", "A", A);
  if (A.rows() >=
      opencl_context.tuning_opts().lower_tri_inverse_size_worth_transfer) {
    matrix_cl A_cl(A);
    matrix_cl A_inv_cl(A.rows(), A.cols());
    if (TriView == Eigen::Lower) {
      A_inv_cl = lower_triangular_inverse(A_cl);
    } else {
      //TODO: Should we create an upper_triangular_inverse instea?
      // in terms of performance this is fine
      A_inv_cl = transpose(lower_triangular_inverse(transpose(A_cl)));
    }
    Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> A_inv(A.rows(),
                                                                 A.cols());
    copy(A_inv, A_inv_cl);  // NOLINT
    return A_inv;
  } else {
    int n = A.rows();
    Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> b;
    b.setIdentity(n, n);
    A.template triangularView<TriView>().solveInPlace(b);
    return b;
  }
}
#endif

}  // namespace math
}  // namespace stan
#endif
<|endoftext|>