<commit_before>/* nobleNote, a note taking application
 * Copyright (C) 2012 Christian Metscher <hakaishi@web.de>,
                      Fabian Deuchler <Taiko000@gmail.com>

 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:

 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.

 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.

 * nobleNote is licensed under the MIT, see `http://copyfree.org/licenses/mit/license.txt'.
 */

#include "backup.h"
#include "htmlnotereader.h"
#include "abstractnotereader.h"
#include <QDirIterator>
#include <QMessageBox>
#include <QSettings>
#include <QtConcurrentMap>
#include <QAbstractItemModel>

Backup::Backup(QWidget *parent): QDialog(parent){
     setupUi(this);

     splitter = new QSplitter(groupBox);
     gridLayout_2->addWidget(splitter);
     treeWidget = new QTreeWidget(splitter);
     treeWidget->setAlternatingRowColors(true);
     treeWidget->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
     treeWidget->setSortingEnabled(true);
     treeWidget->setHeaderLabel(tr("Backups of deleted notes"));
     treeWidget->setSelectionMode(QAbstractItemView::MultiSelection);
     treeWidget->setSelectionBehavior(QAbstractItemView::SelectRows);

     frame = new QFrame(splitter);
     gridLayout3 = new QGridLayout(frame);
     label = new QLabel(frame);
     label->setText(tr("Preview of the selected backup"));
     gridLayout3->addWidget(label, 0, 0, 1, 1);
     textEdit = new QTextEdit(this);
     textEdit->setDisabled(frame);
     gridLayout3->addWidget(textEdit, 1, 0, 1, 1);

     document = new QTextDocument(this);
     textEdit->setDocument(document);

     setupTreeData();

     deleteOldButton = new QPushButton(tr("&Delete all old backups and file entries"),this);
     buttonBox->addButton(deleteOldButton ,QDialogButtonBox::ActionRole);

     connect(treeWidget, SIGNAL(itemSelectionChanged()), this, SLOT(showPreview()));
     connect(this, SIGNAL(accepted()), this, SLOT(restoreBackup()));
     connect(deleteOldButton, SIGNAL(clicked(bool)), this, SLOT(deleteOldBackupsAndFileEntries()));
}

void Backup::setupTreeData()
{
     treeWidget->clear(); //if there already is any data

     //get backup uuids
     QDir backupDir(QSettings().value("backupDirPath").toString());
     QList<QFileInfo> backupList = backupDir.entryInfoList(QDir::Files, QDir::Name);
     QStringList backupUuids;
     foreach(QFileInfo backup, backupList)
          backupUuids << "{" + backup.fileName() + "}";

     //get note files
     QDirIterator itFiles(QSettings().value("rootPath").toString(),
                                              QDirIterator::Subdirectories);
     QStringList noteFiles;
     while(itFiles.hasNext()){
       QString filePath = itFiles.next();
       if(itFiles.fileInfo().isFile())
         noteFiles << filePath;
     }
     QStringList noteUuids; //get note uuids
     foreach(QString note, noteFiles)
          noteUuids << getFileData(note).first();

     foreach(QString uuid, noteUuids)
          if(backupUuids.contains(uuid))
             backupUuids.removeOne(uuid);

     if(backupUuids.isEmpty())
          return;

     QHash<QString,QStringList> backupHash;
     foreach(QString str, backupUuids)
     {
          str.remove("{");
          str.remove("}");
          QStringList data = getFileData(QSettings().value("backupDirPath").toString()
                                   + "/" + str);
          QString uuid = data.takeFirst();
          backupHash.insert(uuid, data);
     }

     foreach(QString key, backupHash.keys())
     {
          QTreeWidgetItem *item = new QTreeWidgetItem(treeWidget);
          item->setText(0,backupHash[key].first());
          item->setData(0,Qt::UserRole,backupHash[key]);
     }
}

QStringList Backup::getFileData(const QString &file)
{
     AbstractNoteReader *reader = new HtmlNoteReader(file,document);
     QStringList list;
     QString uuid = reader->uuid();
     list << uuid << reader->title() << QFileInfo(file).absoluteFilePath() << document->toHtml();
     return list;
}

void Backup::showPreview()
{
//TODO:Even with no backup a preview is shown...
     if(treeWidget->selectedItems().isEmpty())
       return;
     QStringList data = treeWidget->selectedItems().first()->data(0,Qt::UserRole).toStringList();
     if(data.isEmpty())
       return;
     textEdit->setText(data.last());
}

void Backup::restoreBackup()
{
//TODO: enable restore and deletion for multiple backups
/*     if(!treeWidget->selectionModel()->currentIndex().isValid())
       return;
     if(treeWidget->selectionModel()->currentIndex().data(Qt::UserRole).toStringList().isEmpty())
       return;
     QStringList dataList = treeWidget->selectionModel()->currentIndex().data(Qt::UserRole).toStringList();
     QString title = dataList.takeFirst();
     if(!QFile(dataList.first()).exists())
       return;
     else
     {
        if(!QDir(QSettings().value("rootPath").toString()+"/restored notes").exists())
          QDir().mkpath(QSettings().value("rootPath").toString()+"/restored notes");
        QFile(dataList.first()).copy(QSettings().value("rootPath").toString()+"/restored notes/"+title);
     }*/
}

void Backup::getNoteUuidList(){
     QFutureIterator<QUuid> it(future1->future());
     while(it.hasNext())
       notesUuids << it.next();
}

void Backup::deleteOldBackupsAndFileEntries(){
     notesUuids.clear(); //make sure that it's empty

     //searching for existing Notes
     QDirIterator itFiles(QSettings().value("rootPath").toString(),
                                              QDirIterator::Subdirectories);
     QStringList noteFiles;
     while(itFiles.hasNext()){
       QString filePath = itFiles.next();
       if(itFiles.fileInfo().isFile())
         noteFiles << filePath;
     }

     future1 = new QFutureWatcher<QUuid>(this);

     QUuid (*uuidPtr)(QString) = & HtmlNoteReader::uuid; // function pointer, because uuid method is overloaded
     future1->setFuture(QtConcurrent::mapped(noteFiles, uuidPtr));

     indexDialog = new QProgressDialog(this);
     indexDialog->setLabelText(QString(tr("Indexing notes...")));

     QObject::connect(future1, SIGNAL(finished()), this, SLOT(getNoteUuidList()));
     QObject::connect(future1, SIGNAL(finished()), this, SLOT(progressChanges()));
     QObject::connect(future1, SIGNAL(finished()), indexDialog, SLOT(reset()));
     QObject::connect(indexDialog, SIGNAL(canceled()), future1, SLOT(cancel()));
     QObject::connect(future1, SIGNAL(progressRangeChanged(int,int)),
                indexDialog, SLOT(setRange(int,int)));
     QObject::connect(future1, SIGNAL(progressValueChanged(int)), indexDialog,
                SLOT(setValue(int)));

     indexDialog->exec();
}

void actualRemoval(const QString& backupAndUuid){
     if(!backupAndUuid.contains("Notes/"))
       QFile::remove(backupAndUuid);
     else
       QSettings().remove(backupAndUuid);
}

void Backup::progressChanges(){
     //get backup files
     QStringList backups;
     QDir backupDir(QSettings().value("backupDirPath").toString());
     QList<QFileInfo> backupList = backupDir.entryInfoList(QDir::Files, QDir::Name);
     foreach(QFileInfo backup, backupList)
          backups << backup.absoluteFilePath();

     //add QSettings Uuids to the backups
     QStringList backupsAndUuids = backups + QSettings().allKeys().filter("Notes/");

     //We only need the redundant backups and Uuids
     foreach(QString str, backupsAndUuids){
       if(!str.contains("Notes/") && notesUuids.contains(QFileInfo(str).fileName()))
         backupsAndUuids.removeOne(str);
       if(str.contains("Notes/")){
         QString settings = str;
         settings.remove("Notes/");
         settings.remove("_size");
         settings.remove("_cursor_position");
         if(notesUuids.contains(settings))
           backupsAndUuids.removeOne(str);
       }
     }

     QString redundantBackupList;
     foreach(QString str, backupsAndUuids)
       if(!str.contains("Notes/"))
         redundantBackupList += (str+"\n");

     if(backupsAndUuids.isEmpty()){
          QMessageBox::information(this, tr("No redundant data!"), tr("no redundant"
                                   " Everything is clean! No redundant data!"));
          return;
     }
     else{
       if(QMessageBox::warning(this,tr("Deleting backups and file entries"),
           tr("Do you really want to delete the backups and entries for the "
              "following files?\n\n%1\nYou won't be able to restore them!").arg(
           redundantBackupList),
           QMessageBox::Yes | QMessageBox::Abort) != QMessageBox::Yes)
         return;
     }

     progressDialog = new QProgressDialog(this);
     progressDialog->setLabelText(QString(tr("Progressing files...")));

     future2 = new QFutureWatcher<void>(this);
     future2->setFuture(QtConcurrent::map(backupsAndUuids, actualRemoval));

     QObject::connect(future2, SIGNAL(finished()), progressDialog, SLOT(reset()));
     QObject::connect(future2, SIGNAL(finished()), this, SLOT(setupTreeData()));
     QObject::connect(progressDialog, SIGNAL(canceled()), future2, SLOT(cancel()));
     QObject::connect(future2, SIGNAL(progressRangeChanged(int,int)), progressDialog,
                                                                SLOT(setRange(int,int)));
     QObject::connect(future2, SIGNAL(progressValueChanged(int)), progressDialog,
                                                                SLOT(setValue(int)));

     progressDialog->exec();
}
<commit_msg>minor fix<commit_after>/* nobleNote, a note taking application
 * Copyright (C) 2012 Christian Metscher <hakaishi@web.de>,
                      Fabian Deuchler <Taiko000@gmail.com>

 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:

 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.

 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.

 * nobleNote is licensed under the MIT, see `http://copyfree.org/licenses/mit/license.txt'.
 */

#include "backup.h"
#include "htmlnotereader.h"
#include "abstractnotereader.h"
#include <QDirIterator>
#include <QMessageBox>
#include <QSettings>
#include <QtConcurrentMap>
#include <QAbstractItemModel>

Backup::Backup(QWidget *parent): QDialog(parent){
     setupUi(this);

     splitter = new QSplitter(groupBox);
     gridLayout_2->addWidget(splitter);
     treeWidget = new QTreeWidget(splitter);
     treeWidget->setAlternatingRowColors(true);
     treeWidget->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
     treeWidget->setSortingEnabled(true);
     treeWidget->setHeaderLabel(tr("Backups of deleted notes"));
     treeWidget->setSelectionMode(QAbstractItemView::MultiSelection);
     treeWidget->setSelectionBehavior(QAbstractItemView::SelectRows);

     frame = new QFrame(splitter);
     gridLayout3 = new QGridLayout(frame);
     label = new QLabel(frame);
     label->setText(tr("Preview of the selected backup"));
     gridLayout3->addWidget(label, 0, 0, 1, 1);
     textEdit = new QTextEdit(this);
     textEdit->setDisabled(frame);
     gridLayout3->addWidget(textEdit, 1, 0, 1, 1);

     document = new QTextDocument(this);
     textEdit->setDocument(document);

     setupTreeData();

     deleteOldButton = new QPushButton(tr("&Delete all old backups and file entries"),this);
     buttonBox->addButton(deleteOldButton ,QDialogButtonBox::ActionRole);

     connect(treeWidget, SIGNAL(itemSelectionChanged()), this, SLOT(showPreview()));
     connect(this, SIGNAL(accepted()), this, SLOT(restoreBackup()));
     connect(deleteOldButton, SIGNAL(clicked(bool)), this, SLOT(deleteOldBackupsAndFileEntries()));
}

void Backup::setupTreeData()
{
     treeWidget->clear(); //if there already is any data

     //get backup uuids
     QDir backupDir(QSettings().value("backupDirPath").toString());
     QList<QFileInfo> backupList = backupDir.entryInfoList(QDir::Files, QDir::Name);
     QStringList backupUuids;
     foreach(QFileInfo backup, backupList)
          backupUuids << "{" + backup.fileName() + "}";

     //get note files
     QDirIterator itFiles(QSettings().value("rootPath").toString(),
                                              QDirIterator::Subdirectories);
     QStringList noteFiles;
     while(itFiles.hasNext()){
       QString filePath = itFiles.next();
       if(itFiles.fileInfo().isFile())
         noteFiles << filePath;
     }
     QStringList noteUuids; //get note uuids
     foreach(QString note, noteFiles)
          noteUuids << getFileData(note).first();

     foreach(QString uuid, noteUuids)
          if(backupUuids.contains(uuid))
             backupUuids.removeOne(uuid);

     if(backupUuids.isEmpty())
          return;

     QHash<QString,QStringList> backupHash;
     foreach(QString str, backupUuids)
     {
          str.remove("{");
          str.remove("}");
          QStringList data = getFileData(QSettings().value("backupDirPath").toString()
                                   + "/" + str);
          QString uuid = data.takeFirst();
          backupHash.insert(uuid, data);
     }

     foreach(QString key, backupHash.keys())
     {
          QTreeWidgetItem *item = new QTreeWidgetItem(treeWidget);
          item->setText(0,backupHash[key].first());
          item->setData(0,Qt::UserRole,backupHash[key]);
     }
}

QStringList Backup::getFileData(const QString &file)
{
     AbstractNoteReader *reader = new HtmlNoteReader(file,document);
     QStringList list;
     QString uuid = reader->uuid();
     list << uuid << reader->title() << QFileInfo(file).absoluteFilePath() << document->toHtml();
     return list;
}

void Backup::showPreview()
{
//TODO:Even with no backup a preview is shown...
     if(treeWidget->selectedItems().isEmpty())
       return;
     QStringList data = treeWidget->selectedItems().last()->data(0,Qt::UserRole).toStringList();
     if(data.isEmpty())
       return;
     textEdit->setText(data.last());
}

void Backup::restoreBackup()
{
//TODO: enable restore and deletion for multiple backups
/*     if(!treeWidget->selectionModel()->currentIndex().isValid())
       return;
     if(treeWidget->selectionModel()->currentIndex().data(Qt::UserRole).toStringList().isEmpty())
       return;
     QStringList dataList = treeWidget->selectionModel()->currentIndex().data(Qt::UserRole).toStringList();
     QString title = dataList.takeFirst();
     if(!QFile(dataList.first()).exists())
       return;
     else
     {
        if(!QDir(QSettings().value("rootPath").toString()+"/restored notes").exists())
          QDir().mkpath(QSettings().value("rootPath").toString()+"/restored notes");
        QFile(dataList.first()).copy(QSettings().value("rootPath").toString()+"/restored notes/"+title);
     }*/
}

void Backup::getNoteUuidList(){
     QFutureIterator<QUuid> it(future1->future());
     while(it.hasNext())
       notesUuids << it.next();
}

void Backup::deleteOldBackupsAndFileEntries(){
     notesUuids.clear(); //make sure that it's empty

     //searching for existing Notes
     QDirIterator itFiles(QSettings().value("rootPath").toString(),
                                              QDirIterator::Subdirectories);
     QStringList noteFiles;
     while(itFiles.hasNext()){
       QString filePath = itFiles.next();
       if(itFiles.fileInfo().isFile())
         noteFiles << filePath;
     }

     future1 = new QFutureWatcher<QUuid>(this);

     QUuid (*uuidPtr)(QString) = & HtmlNoteReader::uuid; // function pointer, because uuid method is overloaded
     future1->setFuture(QtConcurrent::mapped(noteFiles, uuidPtr));

     indexDialog = new QProgressDialog(this);
     indexDialog->setLabelText(QString(tr("Indexing notes...")));

     QObject::connect(future1, SIGNAL(finished()), this, SLOT(getNoteUuidList()));
     QObject::connect(future1, SIGNAL(finished()), this, SLOT(progressChanges()));
     QObject::connect(future1, SIGNAL(finished()), indexDialog, SLOT(reset()));
     QObject::connect(indexDialog, SIGNAL(canceled()), future1, SLOT(cancel()));
     QObject::connect(future1, SIGNAL(progressRangeChanged(int,int)),
                indexDialog, SLOT(setRange(int,int)));
     QObject::connect(future1, SIGNAL(progressValueChanged(int)), indexDialog,
                SLOT(setValue(int)));

     indexDialog->exec();
}

void actualRemoval(const QString& backupAndUuid){
     if(!backupAndUuid.contains("Notes/"))
       QFile::remove(backupAndUuid);
     else
       QSettings().remove(backupAndUuid);
}

void Backup::progressChanges(){
     //get backup files
     QStringList backups;
     QDir backupDir(QSettings().value("backupDirPath").toString());
     QList<QFileInfo> backupList = backupDir.entryInfoList(QDir::Files, QDir::Name);
     foreach(QFileInfo backup, backupList)
          backups << backup.absoluteFilePath();

     //add QSettings Uuids to the backups
     QStringList backupsAndUuids = backups + QSettings().allKeys().filter("Notes/");

     //We only need the redundant backups and Uuids
     foreach(QString str, backupsAndUuids){
       if(!str.contains("Notes/") && notesUuids.contains(QFileInfo(str).fileName()))
         backupsAndUuids.removeOne(str);
       if(str.contains("Notes/")){
         QString settings = str;
         settings.remove("Notes/");
         settings.remove("_size");
         settings.remove("_cursor_position");
         if(notesUuids.contains(settings))
           backupsAndUuids.removeOne(str);
       }
     }

     QString redundantBackupList;
     foreach(QString str, backupsAndUuids)
       if(!str.contains("Notes/"))
         redundantBackupList += (str+"\n");

     if(backupsAndUuids.isEmpty()){
          QMessageBox::information(this, tr("No redundant data!"), tr("no redundant"
                                   " Everything is clean! No redundant data!"));
          return;
     }
     else{
       if(QMessageBox::warning(this,tr("Deleting backups and file entries"),
           tr("Do you really want to delete the backups and entries for the "
              "following files?\n\n%1\nYou won't be able to restore them!").arg(
           redundantBackupList),
           QMessageBox::Yes | QMessageBox::Abort) != QMessageBox::Yes)
         return;
     }

     progressDialog = new QProgressDialog(this);
     progressDialog->setLabelText(QString(tr("Progressing files...")));

     future2 = new QFutureWatcher<void>(this);
     future2->setFuture(QtConcurrent::map(backupsAndUuids, actualRemoval));

     QObject::connect(future2, SIGNAL(finished()), progressDialog, SLOT(reset()));
     QObject::connect(future2, SIGNAL(finished()), this, SLOT(setupTreeData()));
     QObject::connect(progressDialog, SIGNAL(canceled()), future2, SLOT(cancel()));
     QObject::connect(future2, SIGNAL(progressRangeChanged(int,int)), progressDialog,
                                                                SLOT(setRange(int,int)));
     QObject::connect(future2, SIGNAL(progressValueChanged(int)), progressDialog,
                                                                SLOT(setValue(int)));

     progressDialog->exec();
}
<|endoftext|>


<commit_before>// Copyright (c) 2014 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "base58.h"

#include "hash.h"
#include "uint256.h"

#include <assert.h>
#include <stdint.h>
#include <string.h>
#include <vector>
#include <string>
#include <boost/variant/apply_visitor.hpp>
#include <boost/variant/static_visitor.hpp>

/* All alphanumeric characters except for "0", "I", "O", and "l" */
static const char* pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";

bool DecodeBase58(const char* psz, std::vector<unsigned char>& vch)
{
    // Skip leading spaces.
    while (*psz && isspace(*psz))
        psz++;
    // Skip and count leading '1's.
    int zeroes = 0;
    while (*psz == '1') {
        zeroes++;
        psz++;
    }
    // Allocate enough space in big-endian base256 representation.
    std::vector<unsigned char> b256(strlen(psz) * 733 / 1000 + 1); // log(58) / log(256), rounded up.
    // Process the characters.
    while (*psz && !isspace(*psz)) {
        // Decode base58 character
        const char* ch = strchr(pszBase58, *psz);
        if (ch == NULL)
            return false;
        // Apply "b256 = b256 * 58 + ch".
        int carry = ch - pszBase58;
        for (std::vector<unsigned char>::reverse_iterator it = b256.rbegin(); it != b256.rend(); it++) {
            carry += 58 * (*it);
            *it = carry % 256;
            carry /= 256;
        }
        assert(carry == 0);
        psz++;
    }
    // Skip trailing spaces.
    while (isspace(*psz))
        psz++;
    if (*psz != 0)
        return false;
    // Skip leading zeroes in b256.
    std::vector<unsigned char>::iterator it = b256.begin();
    while (it != b256.end() && *it == 0)
        it++;
    // Copy result into output vector.
    vch.reserve(zeroes + (b256.end() - it));
    vch.assign(zeroes, 0x00);
    while (it != b256.end())
        vch.push_back(*(it++));
    return true;
}

std::string EncodeBase58(const unsigned char* pbegin, const unsigned char* pend)
{
    // Skip & count leading zeroes.
    int zeroes = 0;
    while (pbegin != pend && *pbegin == 0) {
        pbegin++;
        zeroes++;
    }
    // Allocate enough space in big-endian base58 representation.
    std::vector<unsigned char> b58((pend - pbegin) * 138 / 100 + 1); // log(256) / log(58), rounded up.
    // Process the bytes.
    while (pbegin != pend) {
        int carry = *pbegin;
        // Apply "b58 = b58 * 256 + ch".
        for (std::vector<unsigned char>::reverse_iterator it = b58.rbegin(); it != b58.rend(); it++) {
            carry += 256 * (*it);
            *it = carry % 58;
            carry /= 58;
        }
        assert(carry == 0);
        pbegin++;
    }
    // Skip leading zeroes in base58 result.
    std::vector<unsigned char>::iterator it = b58.begin();
    while (it != b58.end() && *it == 0)
        it++;
    // Translate the result into a string.
    std::string str;
    str.reserve(zeroes + (b58.end() - it));
    str.assign(zeroes, '1');
    while (it != b58.end())
        str += pszBase58[*(it++)];
    return str;
}

std::string EncodeBase58(const std::vector<unsigned char>& vch)
{
    return EncodeBase58(&vch[0], &vch[0] + vch.size());
}

bool DecodeBase58(const std::string& str, std::vector<unsigned char>& vchRet)
{
    return DecodeBase58(str.c_str(), vchRet);
}

std::string EncodeBase58Check(const std::vector<unsigned char>& vchIn)
{
    // add 4-byte hash check to the end
    std::vector<unsigned char> vch(vchIn);
    uint256 hash = Hash(vch.begin(), vch.end());
    vch.insert(vch.end(), (unsigned char*)&hash, (unsigned char*)&hash + 4);
    return EncodeBase58(vch);
}

bool DecodeBase58Check(const char* psz, std::vector<unsigned char>& vchRet)
{
    if (!DecodeBase58(psz, vchRet) ||
        (vchRet.size() < 4)) {
        vchRet.clear();
        return false;
    }
    // re-calculate the checksum, insure it matches the included 4-byte checksum
    uint256 hash = Hash(vchRet.begin(), vchRet.end() - 4);
    if (memcmp(&hash, &vchRet.end()[-4], 4) != 0) {
        vchRet.clear();
        return false;
    }
    vchRet.resize(vchRet.size() - 4);
    return true;
}

bool DecodeBase58Check(const std::string& str, std::vector<unsigned char>& vchRet)
{
    return DecodeBase58Check(str.c_str(), vchRet);
}

CBase58Data::CBase58Data()
{
    vchVersion.clear();
    vchData.clear();
}

void CBase58Data::SetData(const std::vector<unsigned char>& vchVersionIn, const void* pdata, size_t nSize)
{
    vchVersion = vchVersionIn;
    vchData.resize(nSize);
    if (!vchData.empty())
        memcpy(&vchData[0], pdata, nSize);
}

void CBase58Data::SetData(const std::vector<unsigned char>& vchVersionIn, const unsigned char* pbegin, const unsigned char* pend)
{
    SetData(vchVersionIn, (void*)pbegin, pend - pbegin);
}

bool CBase58Data::SetString(const char* psz, unsigned int nVersionBytes)
{
    std::vector<unsigned char> vchTemp;
    bool rc58 = DecodeBase58Check(psz, vchTemp);
    if ((!rc58) || (vchTemp.size() < nVersionBytes)) {
        vchData.clear();
        vchVersion.clear();
        return false;
    }
    vchVersion.assign(vchTemp.begin(), vchTemp.begin() + nVersionBytes);
    vchData.resize(vchTemp.size() - nVersionBytes);
    if (!vchData.empty())
        memcpy(&vchData[0], &vchTemp[nVersionBytes], vchData.size());
    OPENSSL_cleanse(&vchTemp[0], vchData.size());
    return true;
}

bool CBase58Data::SetString(const std::string& str)
{
    return SetString(str.c_str());
}

std::string CBase58Data::ToString() const
{
    std::vector<unsigned char> vch = vchVersion;
    vch.insert(vch.end(), vchData.begin(), vchData.end());
    return EncodeBase58Check(vch);
}

int CBase58Data::CompareTo(const CBase58Data& b58) const
{
    if (vchVersion < b58.vchVersion)
        return -1;
    if (vchVersion > b58.vchVersion)
        return 1;
    if (vchData < b58.vchData)
        return -1;
    if (vchData > b58.vchData)
        return 1;
    return 0;
}

namespace
{
class CBitcoinAddressVisitor : public boost::static_visitor<bool>
{
private:
    CBitcoinAddress* addr;

public:
    CBitcoinAddressVisitor(CBitcoinAddress* addrIn) : addr(addrIn) {}

    bool operator()(const CKeyID& id) const { return addr->Set(id); }
    bool operator()(const CScriptID& id) const { return addr->Set(id); }
    bool operator()(const CNoDestination& no) const { return false; }
};

} // anon namespace

bool CBitcoinAddress::Set(const CKeyID& id)
{
    SetData(Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS), &id, 20);
    return true;
}

bool CBitcoinAddress::Set(const CScriptID& id)
{
    SetData(Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS), &id, 20);
    return true;
}

bool CBitcoinAddress::Set(const CTxDestination& dest)
{
    return boost::apply_visitor(CBitcoinAddressVisitor(this), dest);
}

bool CBitcoinAddress::IsValid() const
{
    return IsValid(Params());
}

bool CBitcoinAddress::IsValid(const CChainParams& params) const
{
    bool fCorrectSize = vchData.size() == 20;
    bool fKnownVersion = vchVersion == params.Base58Prefix(CChainParams::PUBKEY_ADDRESS) ||
                         vchVersion == params.Base58Prefix(CChainParams::SCRIPT_ADDRESS);
    return fCorrectSize && fKnownVersion;
}

CTxDestination CBitcoinAddress::Get() const
{
    if (!IsValid())
        return CNoDestination();
    uint160 id;
    memcpy(&id, &vchData[0], 20);
    if (vchVersion == Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS))
        return CKeyID(id);
    else if (vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS))
        return CScriptID(id);
    else
        return CNoDestination();
}

bool CBitcoinAddress::GetKeyID(CKeyID& keyID) const
{
    if (!IsValid() || vchVersion != Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS))
        return false;
    uint160 id;
    memcpy(&id, &vchData[0], 20);
    keyID = CKeyID(id);
    return true;
}

bool CBitcoinAddress::IsScript() const
{
    return IsValid() && vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS);
}

void CBitcoinSecret::SetKey(const CKey& vchSecret)
{
    assert(vchSecret.IsValid());
    SetData(Params().Base58Prefix(CChainParams::SECRET_KEY), vchSecret.begin(), vchSecret.size());
    if (vchSecret.IsCompressed())
        vchData.push_back(1);
}

CKey CBitcoinSecret::GetKey()
{
    CKey ret;
    ret.Set(&vchData[0], &vchData[32], vchData.size() > 32 && vchData[32] == 1);
    return ret;
}

bool CBitcoinSecret::IsValid() const
{
    bool fExpectedFormat = vchData.size() == 32 || (vchData.size() == 33 && vchData[32] == 1);
    bool fCorrectVersion = vchVersion == Params().Base58Prefix(CChainParams::SECRET_KEY);
    return fExpectedFormat && fCorrectVersion;
}

bool CBitcoinSecret::SetString(const char* pszSecret)
{
    return CBase58Data::SetString(pszSecret) && IsValid();
}

bool CBitcoinSecret::SetString(const std::string& strSecret)
{
    return SetString(strSecret.c_str());
}
<commit_msg>Fixing out of bounds error in GetKey()<commit_after>// Copyright (c) 2014 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "base58.h"

#include "hash.h"
#include "uint256.h"

#include <assert.h>
#include <stdint.h>
#include <string.h>
#include <vector>
#include <string>
#include <boost/variant/apply_visitor.hpp>
#include <boost/variant/static_visitor.hpp>

/* All alphanumeric characters except for "0", "I", "O", and "l" */
static const char* pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";

bool DecodeBase58(const char* psz, std::vector<unsigned char>& vch)
{
    // Skip leading spaces.
    while (*psz && isspace(*psz))
        psz++;
    // Skip and count leading '1's.
    int zeroes = 0;
    while (*psz == '1') {
        zeroes++;
        psz++;
    }
    // Allocate enough space in big-endian base256 representation.
    std::vector<unsigned char> b256(strlen(psz) * 733 / 1000 + 1); // log(58) / log(256), rounded up.
    // Process the characters.
    while (*psz && !isspace(*psz)) {
        // Decode base58 character
        const char* ch = strchr(pszBase58, *psz);
        if (ch == NULL)
            return false;
        // Apply "b256 = b256 * 58 + ch".
        int carry = ch - pszBase58;
        for (std::vector<unsigned char>::reverse_iterator it = b256.rbegin(); it != b256.rend(); it++) {
            carry += 58 * (*it);
            *it = carry % 256;
            carry /= 256;
        }
        assert(carry == 0);
        psz++;
    }
    // Skip trailing spaces.
    while (isspace(*psz))
        psz++;
    if (*psz != 0)
        return false;
    // Skip leading zeroes in b256.
    std::vector<unsigned char>::iterator it = b256.begin();
    while (it != b256.end() && *it == 0)
        it++;
    // Copy result into output vector.
    vch.reserve(zeroes + (b256.end() - it));
    vch.assign(zeroes, 0x00);
    while (it != b256.end())
        vch.push_back(*(it++));
    return true;
}

std::string EncodeBase58(const unsigned char* pbegin, const unsigned char* pend)
{
    // Skip & count leading zeroes.
    int zeroes = 0;
    while (pbegin != pend && *pbegin == 0) {
        pbegin++;
        zeroes++;
    }
    // Allocate enough space in big-endian base58 representation.
    std::vector<unsigned char> b58((pend - pbegin) * 138 / 100 + 1); // log(256) / log(58), rounded up.
    // Process the bytes.
    while (pbegin != pend) {
        int carry = *pbegin;
        // Apply "b58 = b58 * 256 + ch".
        for (std::vector<unsigned char>::reverse_iterator it = b58.rbegin(); it != b58.rend(); it++) {
            carry += 256 * (*it);
            *it = carry % 58;
            carry /= 58;
        }
        assert(carry == 0);
        pbegin++;
    }
    // Skip leading zeroes in base58 result.
    std::vector<unsigned char>::iterator it = b58.begin();
    while (it != b58.end() && *it == 0)
        it++;
    // Translate the result into a string.
    std::string str;
    str.reserve(zeroes + (b58.end() - it));
    str.assign(zeroes, '1');
    while (it != b58.end())
        str += pszBase58[*(it++)];
    return str;
}

std::string EncodeBase58(const std::vector<unsigned char>& vch)
{
    return EncodeBase58(&vch[0], &vch[0] + vch.size());
}

bool DecodeBase58(const std::string& str, std::vector<unsigned char>& vchRet)
{
    return DecodeBase58(str.c_str(), vchRet);
}

std::string EncodeBase58Check(const std::vector<unsigned char>& vchIn)
{
    // add 4-byte hash check to the end
    std::vector<unsigned char> vch(vchIn);
    uint256 hash = Hash(vch.begin(), vch.end());
    vch.insert(vch.end(), (unsigned char*)&hash, (unsigned char*)&hash + 4);
    return EncodeBase58(vch);
}

bool DecodeBase58Check(const char* psz, std::vector<unsigned char>& vchRet)
{
    if (!DecodeBase58(psz, vchRet) ||
        (vchRet.size() < 4)) {
        vchRet.clear();
        return false;
    }
    // re-calculate the checksum, insure it matches the included 4-byte checksum
    uint256 hash = Hash(vchRet.begin(), vchRet.end() - 4);
    if (memcmp(&hash, &vchRet.end()[-4], 4) != 0) {
        vchRet.clear();
        return false;
    }
    vchRet.resize(vchRet.size() - 4);
    return true;
}

bool DecodeBase58Check(const std::string& str, std::vector<unsigned char>& vchRet)
{
    return DecodeBase58Check(str.c_str(), vchRet);
}

CBase58Data::CBase58Data()
{
    vchVersion.clear();
    vchData.clear();
}

void CBase58Data::SetData(const std::vector<unsigned char>& vchVersionIn, const void* pdata, size_t nSize)
{
    vchVersion = vchVersionIn;
    vchData.resize(nSize);
    if (!vchData.empty())
        memcpy(&vchData[0], pdata, nSize);
}

void CBase58Data::SetData(const std::vector<unsigned char>& vchVersionIn, const unsigned char* pbegin, const unsigned char* pend)
{
    SetData(vchVersionIn, (void*)pbegin, pend - pbegin);
}

bool CBase58Data::SetString(const char* psz, unsigned int nVersionBytes)
{
    std::vector<unsigned char> vchTemp;
    bool rc58 = DecodeBase58Check(psz, vchTemp);
    if ((!rc58) || (vchTemp.size() < nVersionBytes)) {
        vchData.clear();
        vchVersion.clear();
        return false;
    }
    vchVersion.assign(vchTemp.begin(), vchTemp.begin() + nVersionBytes);
    vchData.resize(vchTemp.size() - nVersionBytes);
    if (!vchData.empty())
        memcpy(&vchData[0], &vchTemp[nVersionBytes], vchData.size());
    OPENSSL_cleanse(&vchTemp[0], vchData.size());
    return true;
}

bool CBase58Data::SetString(const std::string& str)
{
    return SetString(str.c_str());
}

std::string CBase58Data::ToString() const
{
    std::vector<unsigned char> vch = vchVersion;
    vch.insert(vch.end(), vchData.begin(), vchData.end());
    return EncodeBase58Check(vch);
}

int CBase58Data::CompareTo(const CBase58Data& b58) const
{
    if (vchVersion < b58.vchVersion)
        return -1;
    if (vchVersion > b58.vchVersion)
        return 1;
    if (vchData < b58.vchData)
        return -1;
    if (vchData > b58.vchData)
        return 1;
    return 0;
}

namespace
{
class CBitcoinAddressVisitor : public boost::static_visitor<bool>
{
private:
    CBitcoinAddress* addr;

public:
    CBitcoinAddressVisitor(CBitcoinAddress* addrIn) : addr(addrIn) {}

    bool operator()(const CKeyID& id) const { return addr->Set(id); }
    bool operator()(const CScriptID& id) const { return addr->Set(id); }
    bool operator()(const CNoDestination& no) const { return false; }
};

} // anon namespace

bool CBitcoinAddress::Set(const CKeyID& id)
{
    SetData(Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS), &id, 20);
    return true;
}

bool CBitcoinAddress::Set(const CScriptID& id)
{
    SetData(Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS), &id, 20);
    return true;
}

bool CBitcoinAddress::Set(const CTxDestination& dest)
{
    return boost::apply_visitor(CBitcoinAddressVisitor(this), dest);
}

bool CBitcoinAddress::IsValid() const
{
    return IsValid(Params());
}

bool CBitcoinAddress::IsValid(const CChainParams& params) const
{
    bool fCorrectSize = vchData.size() == 20;
    bool fKnownVersion = vchVersion == params.Base58Prefix(CChainParams::PUBKEY_ADDRESS) ||
                         vchVersion == params.Base58Prefix(CChainParams::SCRIPT_ADDRESS);
    return fCorrectSize && fKnownVersion;
}

CTxDestination CBitcoinAddress::Get() const
{
    if (!IsValid())
        return CNoDestination();
    uint160 id;
    memcpy(&id, &vchData[0], 20);
    if (vchVersion == Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS))
        return CKeyID(id);
    else if (vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS))
        return CScriptID(id);
    else
        return CNoDestination();
}

bool CBitcoinAddress::GetKeyID(CKeyID& keyID) const
{
    if (!IsValid() || vchVersion != Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS))
        return false;
    uint160 id;
    memcpy(&id, &vchData[0], 20);
    keyID = CKeyID(id);
    return true;
}

bool CBitcoinAddress::IsScript() const
{
    return IsValid() && vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS);
}

void CBitcoinSecret::SetKey(const CKey& vchSecret)
{
    assert(vchSecret.IsValid());
    SetData(Params().Base58Prefix(CChainParams::SECRET_KEY), vchSecret.begin(), vchSecret.size());
    if (vchSecret.IsCompressed())
        vchData.push_back(1);
}

CKey CBitcoinSecret::GetKey()
{
    CKey ret;
    assert(vchData.size() >= 32);
    ret.Set(vchData.begin(), vchData.begin() + 32, vchData.size() > 32 && vchData[32] == 1);
    return ret;
}

bool CBitcoinSecret::IsValid() const
{
    bool fExpectedFormat = vchData.size() == 32 || (vchData.size() == 33 && vchData[32] == 1);
    bool fCorrectVersion = vchVersion == Params().Base58Prefix(CChainParams::SECRET_KEY);
    return fExpectedFormat && fCorrectVersion;
}

bool CBitcoinSecret::SetString(const char* pszSecret)
{
    return CBase58Data::SetString(pszSecret) && IsValid();
}

bool CBitcoinSecret::SetString(const std::string& strSecret)
{
    return SetString(strSecret.c_str());
}
<|endoftext|>


<commit_before>// This file is part of the dune-gdt project:
//   https://github.com/dune-community/dune-gdt
// Copyright 2010-2018 dune-gdt developers and contributors. All rights reserved.
// License: Dual licensed as BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)
//      or  GPL-2.0+ (http://opensource.org/licenses/gpl-license)
//          with "runtime exception" (http://www.dune-project.org/license.html)
// Authors:
//   Felix Schindler (2013 - 2017)
//   Rene Milk       (2014, 2016 - 2018)
//   Sven Kaulmann   (2014)
//   Tobias Leibner  (2014, 2016 - 2017)

#ifndef DUNE_GDT_SPACES_INTERFACE_HH
#define DUNE_GDT_SPACES_INTERFACE_HH

#include <dune/geometry/type.hh>

#include <dune/xt/grid/type_traits.hh>

#include <dune/gdt/exceptions.hh>
#include <dune/gdt/local/finite-elements/interfaces.hh>
#include <dune/gdt/spaces/basis/interface.hh>
#include <dune/gdt/spaces/mapper/interfaces.hh>
#include <dune/gdt/spaces/parallel/communication.hh>
#include <dune/gdt/type_traits.hh>

namespace Dune {
namespace GDT {

template <class GridView, size_t range_dim = 1, size_t range_dim_columns = 1, class RangeField = double>
class SpaceInterface
{
  static_assert(XT::Grid::is_view<GridView>::value, "");

public:
  using GridViewType = GridView;
  using GV = GridViewType;
  using D = typename GridViewType::ctype;
  static const constexpr size_t d = GridViewType::dimension;
  using R = RangeField;
  static const constexpr size_t r = range_dim;
  static const constexpr size_t rC = range_dim_columns;

  using GlobalBasisType = GlobalBasisInterface<GridViewType, r, rC, R>;
  using MapperType = MapperInterface<GridViewType>;
  using FiniteElementType = LocalFiniteElementInterface<D, d, R, r, rC>;

  using DofCommunicatorType = typename DofCommunicationChooser<GridViewType>::Type;

  SpaceInterface()
    : dof_communicator_(nullptr)
  {
  }

  virtual ~SpaceInterface() = default;

  virtual const GridViewType& grid_view() const = 0;

  /**
   * \name These methods provide the actual functionality, they have to be implemented.
   * \{
   **/

  virtual const MapperType& mapper() const = 0;

  virtual const GlobalBasisType& basis() const = 0;

  virtual const FiniteElementType& finite_element(const GeometryType& geometry_type) const = 0;

  /// \}

  /**
   * \name These methods help to identify the space, they have to be implemented.
   * \{
   **/

  virtual SpaceType type() const = 0;

  virtual int min_polorder() const = 0;

  virtual int max_polorder() const = 0;

  /**
   * To query if elements of this space (functions) are continuous (i.e., in C^0), use `continuous(0)`.
   */
  virtual bool continuous(const int diff_order) const = 0;

  virtual bool continuous_normal_components() const = 0;

  /**
   * If this returns true, every finite_element() is expected to provide lagrange_points().
   */
  virtual bool is_lagrangian() const = 0;

  /// \}

  /**
   * \name These methods are required for MPI communication, they are provided.
   * \{
   */

  virtual const DofCommunicatorType& dof_communicator() const
  {
    if (!dof_communicator_)
      DUNE_THROW(Exceptions::space_error,
                 "The actual space has to either implement its own dof_communicator() or call "
                 "create_communicator() in the ctor!");
    return *dof_communicator_;
  }

  /// \}

protected:
  void create_communicator()
  {
    if (!dof_communicator_) {
      dof_communicator_ =
          std::shared_ptr<DofCommunicatorType>(DofCommunicationChooser<GridViewType>::create(grid_view()));
      DofCommunicationChooser<GridViewType>::prepare(*this, *dof_communicator_);
    }
  }

private:
  std::shared_ptr<DofCommunicatorType> dof_communicator_;
}; // class SpaceInterface

} // namespace GDT
} // namespace Dune

#endif // DUNE_GDT_SPACES_INTERFACE_HH
<commit_msg>[spaces] add contains(), operator<<<commit_after>// This file is part of the dune-gdt project:
//   https://github.com/dune-community/dune-gdt
// Copyright 2010-2018 dune-gdt developers and contributors. All rights reserved.
// License: Dual licensed as BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)
//      or  GPL-2.0+ (http://opensource.org/licenses/gpl-license)
//          with "runtime exception" (http://www.dune-project.org/license.html)
// Authors:
//   Felix Schindler (2013 - 2017)
//   Rene Milk       (2014, 2016 - 2018)
//   Sven Kaulmann   (2014)
//   Tobias Leibner  (2014, 2016 - 2017)

#ifndef DUNE_GDT_SPACES_INTERFACE_HH
#define DUNE_GDT_SPACES_INTERFACE_HH

#include <dune/geometry/type.hh>

#include <dune/xt/la/container/vector-interface.hh>
#include <dune/xt/grid/type_traits.hh>

#include <dune/gdt/exceptions.hh>
#include <dune/gdt/local/finite-elements/interfaces.hh>
#include <dune/gdt/spaces/basis/interface.hh>
#include <dune/gdt/spaces/mapper/interfaces.hh>
#include <dune/gdt/spaces/parallel/communication.hh>
#include <dune/gdt/type_traits.hh>

namespace Dune {
namespace GDT {

// forward
template <class V, class GV, size_t r, size_t rC, class R>
class ConstDiscreteFunction;

template <class GridView, size_t range_dim = 1, size_t range_dim_columns = 1, class RangeField = double>
class SpaceInterface
{
  static_assert(XT::Grid::is_view<GridView>::value, "");

public:
  using GridViewType = GridView;
  using GV = GridViewType;
  using D = typename GridViewType::ctype;
  static const constexpr size_t d = GridViewType::dimension;
  using R = RangeField;
  static const constexpr size_t r = range_dim;
  static const constexpr size_t rC = range_dim_columns;

  using GlobalBasisType = GlobalBasisInterface<GridViewType, r, rC, R>;
  using MapperType = MapperInterface<GridViewType>;
  using FiniteElementType = LocalFiniteElementInterface<D, d, R, r, rC>;

  using DofCommunicatorType = typename DofCommunicationChooser<GridViewType>::Type;

  SpaceInterface()
    : dof_communicator_(nullptr)
  {
  }

  virtual ~SpaceInterface() = default;

  /// \name These methods provide the actual functionality, they have to be implemented.
  /// \{

  virtual const GridViewType& grid_view() const = 0;

  virtual const MapperType& mapper() const = 0;

  virtual const GlobalBasisType& basis() const = 0;

  virtual const FiniteElementType& finite_element(const GeometryType& geometry_type) const = 0;

  /// \}
  /// \name These methods help to identify the space, they have to be implemented.
  /// \{

  virtual SpaceType type() const = 0;

  virtual int min_polorder() const = 0;

  virtual int max_polorder() const = 0;

  /**
   * To query if elements of this space (functions) are continuous (i.e., in C^0), use `continuous(0)`.
   */
  virtual bool continuous(const int diff_order) const = 0;

  virtual bool continuous_normal_components() const = 0;

  /**
   * If this returns true, every finite_element() is expected to provide lagrange_points().
   */
  virtual bool is_lagrangian() const = 0;

  /// \}
  /// \name These methods are required for MPI communication, they are provided.
  /// \{

  virtual const DofCommunicatorType& dof_communicator() const
  {
    if (!dof_communicator_)
      DUNE_THROW(Exceptions::space_error,
                 "The actual space has to either implement its own dof_communicator() or call "
                 "create_communicator() in the ctor!");
    return *dof_communicator_;
  }

  /// \}
  /// \name These methods are provided for convenience.
  /// \{

  template <class V>
  bool contains(const XT::LA::VectorInterface<V>& vector) const
  {
    return vector.size() == this->mapper().size();
  }

  /**
   * \note A return value of true cannot be ultimately trusted yet.
   *
   * \sa https://github.com/dune-community/dune-gdt/issues/123
   */
  template <class V>
  bool contains(const ConstDiscreteFunction<V, GV, r, rC, R>& function) const
  {
    // this is the only case where we are sure^^
    if (&function.space() == this)
      return true;
    if (function.space().type() != this->type())
      return false;
    if (function.space().mapper().size() != this->mapper().size())
      return false;
    // the spaces might still differ (different grid views of same size), but we have no means to check this
    return true;
  }

  /// \}

protected:
  void create_communicator()
  {
    if (!dof_communicator_) {
      dof_communicator_ =
          std::shared_ptr<DofCommunicatorType>(DofCommunicationChooser<GridViewType>::create(grid_view()));
      DofCommunicationChooser<GridViewType>::prepare(*this, *dof_communicator_);
    }
  }

private:
  std::shared_ptr<DofCommunicatorType> dof_communicator_;
}; // class SpaceInterface

template <class GV, size_t r, size_t rC, class R>
std::ostream& operator<<(std::ostream& out, const SpaceInterface<GV, r, rC, R>& space)
{
  out << "Space(" << space.type() << ", " << space.mapper().size() << " DoFs)";
  return out;
}

} // namespace GDT
} // namespace Dune

#include <dune/gdt/discretefunction/default.hh>

#endif // DUNE_GDT_SPACES_INTERFACE_HH
<|endoftext|>


<commit_before>// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2009 Gael Guennebaud <g.gael@free.fr>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include "main.h"
#include <unsupported/Eigen/AlignedVector3>

template<typename Scalar>
void alignedvector3()
{
  Scalar s1 = internal::random<Scalar>();
  Scalar s2 = internal::random<Scalar>();
  typedef Matrix<Scalar,3,1> RefType;
  typedef Matrix<Scalar,3,3> Mat33;
  typedef AlignedVector3<Scalar> FastType;
  RefType  r1(RefType::Random()), r2(RefType::Random()), r3(RefType::Random()),
           r4(RefType::Random()), r5(RefType::Random()), r6(RefType::Random());
  FastType f1(r1), f2(r2), f3(r3), f4(r4), f5(r5), f6(r6);
  Mat33 m1(Mat33::Random());

  VERIFY_IS_APPROX(f1,r1);
  VERIFY_IS_APPROX(f4,r4);

  VERIFY_IS_APPROX(f4+f1,r4+r1);
  VERIFY_IS_APPROX(f4-f1,r4-r1);
  VERIFY_IS_APPROX(f4+f1-f2,r4+r1-r2);
  VERIFY_IS_APPROX(f4+=f3,r4+=r3);
  VERIFY_IS_APPROX(f4-=f5,r4-=r5);
  VERIFY_IS_APPROX(f4-=f5+f1,r4-=r5+r1);
  VERIFY_IS_APPROX(f5+f1-s1*f2,r5+r1-s1*r2);
  VERIFY_IS_APPROX(f5+f1/s2-s1*f2,r5+r1/s2-s1*r2);

  VERIFY_IS_APPROX(m1*f4,m1*r4);
  VERIFY_IS_APPROX(f4.transpose()*m1,r4.transpose()*m1);

  VERIFY_IS_APPROX(f2.dot(f3),r2.dot(r3));
  VERIFY_IS_APPROX(f2.cross(f3),r2.cross(r3));
  VERIFY_IS_APPROX(f2.norm(),r2.norm());

  VERIFY_IS_APPROX(f2.normalized(),r2.normalized());

  VERIFY_IS_APPROX((f2+f1).normalized(),(r2+r1).normalized());

  f2.normalize();
  r2.normalize();
  VERIFY_IS_APPROX(f2,r2);
}

void test_alignedvector3()
{
  for(int i = 0; i < g_repeat; i++) {
    CALL_SUBTEST( alignedvector3<float>() );
  }
}
<commit_msg>Fix compilation of alignedvector3 unit test<commit_after>// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2009 Gael Guennebaud <g.gael@free.fr>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include "main.h"
#include <unsupported/Eigen/AlignedVector3>

namespace Eigen {

template<typename T,typename Derived>
T test_relative_error(const AlignedVector3<T> &a, const MatrixBase<Derived> &b)
{
  return test_relative_error(a.coeffs().template head<3>(), b);
}

}

template<typename Scalar>
void alignedvector3()
{
  Scalar s1 = internal::random<Scalar>();
  Scalar s2 = internal::random<Scalar>();
  typedef Matrix<Scalar,3,1> RefType;
  typedef Matrix<Scalar,3,3> Mat33;
  typedef AlignedVector3<Scalar> FastType;
  RefType  r1(RefType::Random()), r2(RefType::Random()), r3(RefType::Random()),
           r4(RefType::Random()), r5(RefType::Random()), r6(RefType::Random());
  FastType f1(r1), f2(r2), f3(r3), f4(r4), f5(r5), f6(r6);
  Mat33 m1(Mat33::Random());

  VERIFY_IS_APPROX(f1,r1);
  VERIFY_IS_APPROX(f4,r4);

  VERIFY_IS_APPROX(f4+f1,r4+r1);
  VERIFY_IS_APPROX(f4-f1,r4-r1);
  VERIFY_IS_APPROX(f4+f1-f2,r4+r1-r2);
  VERIFY_IS_APPROX(f4+=f3,r4+=r3);
  VERIFY_IS_APPROX(f4-=f5,r4-=r5);
  VERIFY_IS_APPROX(f4-=f5+f1,r4-=r5+r1);
  VERIFY_IS_APPROX(f5+f1-s1*f2,r5+r1-s1*r2);
  VERIFY_IS_APPROX(f5+f1/s2-s1*f2,r5+r1/s2-s1*r2);

  VERIFY_IS_APPROX(m1*f4,m1*r4);
  VERIFY_IS_APPROX(f4.transpose()*m1,r4.transpose()*m1);

  VERIFY_IS_APPROX(f2.dot(f3),r2.dot(r3));
  VERIFY_IS_APPROX(f2.cross(f3),r2.cross(r3));
  VERIFY_IS_APPROX(f2.norm(),r2.norm());

  VERIFY_IS_APPROX(f2.normalized(),r2.normalized());

  VERIFY_IS_APPROX((f2+f1).normalized(),(r2+r1).normalized());

  f2.normalize();
  r2.normalize();
  VERIFY_IS_APPROX(f2,r2);
}

void test_alignedvector3()
{
  for(int i = 0; i < g_repeat; i++) {
    CALL_SUBTEST( alignedvector3<float>() );
  }
}
<|endoftext|>


<commit_before>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2011 Artem Pavlenko
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

#ifndef MAPNIK_RASTER_HPP
#define MAPNIK_RASTER_HPP

// mapnik
#include <mapnik/box2d.hpp>
#include <mapnik/image_data.hpp>
#include <mapnik/noncopyable.hpp>

 // boost
#include <boost/optional.hpp>

namespace mapnik {
class raster : private mapnik::noncopyable
{
public:
    box2d<double> ext_;
    image_data_32 data_;
    double filter_factor_;
    bool premultiplied_alpha_;
    boost::optional<double> nodata_;
    raster(box2d<double> const& ext,
           unsigned width,
           unsigned height,
           double filter_factor,
           bool premultiplied_alpha = false)
        : ext_(ext),
          data_(width,height),
          filter_factor_(filter_factor),
          premultiplied_alpha_(premultiplied_alpha) {}

    void set_nodata(double nodata)
    {
        nodata_ = nodata;
    }

    boost::optional<double> const& nodata() const
    {
        return nodata_;
    }

    double get_filter_factor() const
    {
        return filter_factor_;
    }

    void set_filter_factor(double factor)
    {
        filter_factor_ = factor;
    }

};
}

#endif // MAPNIK_RASTER_HPP
<commit_msg>mapnik::raster : allow passing image_data into ctor<commit_after>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2011 Artem Pavlenko
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

#ifndef MAPNIK_RASTER_HPP
#define MAPNIK_RASTER_HPP

// mapnik
#include <mapnik/box2d.hpp>
#include <mapnik/image_data.hpp>
#include <mapnik/noncopyable.hpp>

 // boost
#include <boost/optional.hpp>

namespace mapnik {
class raster : private mapnik::noncopyable
{
public:
    box2d<double> ext_;
    image_data_32 data_;
    double filter_factor_;
    bool premultiplied_alpha_;
    boost::optional<double> nodata_;
    raster(box2d<double> const& ext,
           unsigned width,
           unsigned height,
           double filter_factor,
           bool premultiplied_alpha = false)
        : ext_(ext),
          data_(width,height),
          filter_factor_(filter_factor),
          premultiplied_alpha_(premultiplied_alpha) {}

    raster(box2d<double> const& ext, image_data_32 && data,
           double filter_factor, bool premultiplied_alpha = false)
        : ext_(ext),
          data_(std::move(data)),
          filter_factor_(filter_factor),
          premultiplied_alpha_(premultiplied_alpha) {}

    void set_nodata(double nodata)
    {
        nodata_ = nodata;
    }

    boost::optional<double> const& nodata() const
    {
        return nodata_;
    }

    double get_filter_factor() const
    {
        return filter_factor_;
    }

    void set_filter_factor(double factor)
    {
        filter_factor_ = factor;
    }

};
}

#endif // MAPNIK_RASTER_HPP
<|endoftext|>


<commit_before>#pragma once

#ifndef SIPL_MATRIX_MATRIX_H
#define SIPL_MATRIX_MATRIX_H

#include "matrix/MatrixBase.hpp"
#include "matrix/Vector.hpp"
#include "matrix/Wrappers.hpp"
#include <cmath>
#include <iostream>

namespace sipl
{

// Need to use 'this' pointer below because templated base class members are
// not visible in a certain phase of compilation. See here:
// http://stackoverflow.com/a/6592617

// Initial class for statically-allocated matrices
template <typename Dtype, int32_t Rows, int32_t Cols>
class Matrix : public MatrixBase<Dtype,
                                 Rows,
                                 Cols,
                                 StaticArrayWrapper<Dtype, Rows * Cols>>
{
public:
    using ContainerType = StaticArrayWrapper<Dtype, Rows * Cols>;
    using BaseClass = MatrixBase<Dtype, Rows, Cols, ContainerType>;
    using BaseClass::BaseClass;

    // Explicitly import BaseClass member variables so we don't have to use
    // 'this' pointer because of 2-phase lookup (see here for more information:
    // http://stackoverflow.com/a/4643295)
    using BaseClass::nelements_;
    using BaseClass::nbytes_;
    using BaseClass::data_;
    using BaseClass::dims;

    Matrix() : BaseClass() {}

    Matrix(Dtype fill_value) : BaseClass()
    {
        std::fill(std::begin(data_), std::end(data_), fill_value);
    }

    Matrix(std::initializer_list<std::initializer_list<Dtype>> list)
        : BaseClass()
    {
        assert(int32_t(list.size()) == Rows && "size mismatch");
        for (const auto l : list) {
            assert(int32_t(l.size()) == Cols && "size mismatch");
        }

        int32_t i = 0;
        for (const auto l : list) {
            std::copy(std::begin(l), std::end(l),
                      std::begin(data_) + i++ * l.size());
        }
    }

    template <typename OtherType>
    Matrix<OtherType, Rows, Cols> as_type() const
    {
        return apply([](auto e) { return OtherType(e); });
    }

    // Template magic from: http://stackoverflow.com/a/26383814
    template <typename UnaryFunctor,
              typename OutputType =
                  typename std::result_of<UnaryFunctor&(Dtype)>::type>
    decltype(auto) apply(UnaryFunctor f) const
    {
        Matrix<OutputType, Rows, Cols> new_m;
        std::transform(std::begin(*this), std::end(*this), std::begin(new_m),
                       f);
        return new_m;
    }

    Matrix rescale(Dtype new_min, Dtype new_max) const
    {
        return apply([ min = this->min(), max = this->max(), new_min, new_max ](
            auto e) {
            return Dtype(((new_max - new_min) / double(max - min)) * e +
                         ((new_min * max + min * new_max) / double(max - min)));
        });
    }

    Matrix clip(Dtype new_min, Dtype new_max) const
    {
        Matrix new_m;
        for (int32_t i = 0; i < this->size(); ++i) {
            auto e = std::round((*this)[i]);
            if (e < new_min) {
                new_m[i] = new_min;
            } else if (e > new_max) {
                new_m[i] = new_max;
            } else {
                new_m[i] = e;
            }
        }

        return new_m;
    }
};

// Specialization of the above for dynamically-allocated matrices.
template <typename Dtype>
class Matrix<Dtype, Dynamic, Dynamic>
    : public MatrixBase<Dtype,
                        Dynamic,
                        Dynamic,
                        DynamicArrayWrapper<Dtype, Dynamic>>
{
public:
    using ContainerType = DynamicArrayWrapper<Dtype, Dynamic>;
    using BaseClass = MatrixBase<Dtype, Dynamic, Dynamic, ContainerType>;
    using BaseClass::BaseClass;

    using BaseClass::nelements_;
    using BaseClass::nbytes_;
    using BaseClass::data_;
    using BaseClass::dims;

    Matrix(int32_t rows, int32_t cols)
    {
        nelements_ = rows * cols;
        data_ = ContainerType(nelements_);
        nbytes_ = nelements_ * int32_t(sizeof(Dtype));
        dims = {rows, cols};
    }

    Matrix(int32_t rows, int32_t cols, Dtype fill_value)
    {
        nelements_ = rows * cols;
        data_ = ContainerType(nelements_);
        nbytes_ = nelements_ * int32_t(sizeof(Dtype));
        dims = {rows, cols};
        std::fill(std::begin(data_), std::end(data_), fill_value);
    }

    Matrix(std::array<int32_t, 2> new_dims, Dtype fill_value)
    {
        nelements_ = new_dims[0] * new_dims[1];
        data_ = ContainerType(nelements_);
        nbytes_ = nelements_ * int32_t(sizeof(Dtype));
        dims = new_dims;
        std::fill(std::begin(data_), std::end(data_), fill_value);
    }

    Matrix(std::array<int32_t, 2> new_dims)
    {
        nelements_ = new_dims[0] * new_dims[1];
        data_ = ContainerType(nelements_);
        nbytes_ = nelements_ * int32_t(sizeof(Dtype));
        dims = new_dims;
    }

    Matrix(std::initializer_list<std::initializer_list<Dtype>> list)
    {
        int32_t nrows = list.size();
        int32_t ncols = list.begin()->size();
        for (const auto l : list) {
            assert(int32_t(l.size()) == ncols &&
                   "initializer_list size mismatch");
        }

        nelements_ = nrows * ncols;
        nbytes_ = nelements_ * int32_t(sizeof(Dtype));
        data_ = ContainerType(nelements_);
        dims = {nrows, ncols};
        int32_t i = 0;
        for (const auto l : list) {
            std::copy(std::begin(l), std::end(l),
                      std::begin(data_) + i++ * l.size());
        }
    }

    // Extract a patch centered at (center_y, center_x) with radius ry and
    // rx. Change boundaries depending on BorderType
    Matrix patch(int32_t center_y,
                 int32_t center_x,
                 int32_t ry,
                 int32_t rx,
                 const BorderType border_type = BorderType::REPLICATE) const
    {
        assert(center_y >= 0 && center_y < dims[0] && "center_y out of bounds");
        assert(center_x >= 0 && center_x < dims[1] && "center_x out of bounds");

        Matrix patch(2 * ry + 1, 2 * rx + 1);
        for (int32_t y = center_y - ry, r = 0; y <= center_y + ry; ++y, ++r) {
            for (int32_t x = center_x - rx, c = 0; x <= center_x + rx;
                 ++x, ++c) {
                switch (border_type) {
                case BorderType::REPLICATE:
                    patch(r, c) = (*this)(this->clamp_row_index(y),
                                          this->clamp_col_index(x));
                }
            }
        }
        return patch;
    }

    template <typename OtherType>
    Matrix<OtherType, Dynamic, Dynamic> as_type() const
    {
        return apply([](auto e) { return OtherType(e); });
    }

    // Template magic from: http://stackoverflow.com/a/26383814
    template <typename UnaryFunctor,
              typename OutputType =
                  typename std::result_of<UnaryFunctor&(Dtype)>::type>
    decltype(auto) apply(UnaryFunctor f) const
    {
        Matrix<OutputType, Dynamic, Dynamic> new_m(dims);
        std::transform(std::begin(*this), std::end(*this), std::begin(new_m),
                       f);
        return new_m;
    }

    Matrix clip(Dtype new_min, Dtype new_max) const
    {
        Matrix new_m(dims);
        for (int32_t i = 0; i < this->size(); ++i) {
            auto e = std::round((*this)[i]);
            if (e < new_min) {
                new_m[i] = new_min;
            } else if (e > new_max) {
                new_m[i] = new_max;
            } else {
                new_m[i] = e;
            }
        }

        return new_m;
    }

    Matrix rescale(Dtype new_min, Dtype new_max) const
    {
        return apply([ min = this->min(), max = this->max(), new_min, new_max ](
            auto e) {
            return Dtype(((new_max - new_min) / double(max - min)) * e +
                         ((new_min * max + min * new_max) / double(max - min)));
        });
    }
};

// Specialization of the above for dynamically-allocated Matrix with Vector
// elements.
// XXX Only specialized for fixed-sized Vector elements at this time
template <typename Dtype, int32_t Length>
class Matrix<Vector<Dtype, Length>, Dynamic, Dynamic>
    : public MatrixBase<Vector<Dtype, Length>,
                        Dynamic,
                        Dynamic,
                        DynamicArrayWrapper<Vector<Dtype, Length>, Length>>
{
public:
    using value_type = Vector<Dtype, Length>;
    using ContainerType = DynamicArrayWrapper<value_type, Length>;
    using BaseClass = MatrixBase<value_type, Dynamic, Dynamic, ContainerType>;
    using BaseClass::BaseClass;

    using BaseClass::nelements_;
    using BaseClass::nbytes_;
    using BaseClass::data_;
    using BaseClass::dims;

    Matrix(std::array<int32_t, 2> dims_)
    {
        nelements_ = dims_[0] * dims_[1];
        data_ = ContainerType(nelements_);
        nbytes_ = nelements_ * int32_t(sizeof(Dtype));
        dims = dims_;
    }

    Matrix(std::array<int32_t, 2> dims_, Dtype fillval)
    {
        nelements_ = dims_[0] * dims_[1];
        data_ = ContainerType(nelements_);
        nbytes_ = nelements_ * int32_t(sizeof(Dtype));
        dims = dims_;
        std::fill(std::begin(data_), std::end(data_), fillval);
    }

    Matrix(int32_t rows, int32_t cols)
    {
        nelements_ = rows * cols;
        nbytes_ = nelements_ * Length * int32_t(sizeof(Dtype));
        data_ = ContainerType(nelements_);
        dims = {rows, cols};
    }

    Matrix(int32_t rows, int32_t cols, value_type fill_value)
    {
        nelements_ = rows * cols;
        data_ = ContainerType(nelements_);
        nbytes_ = nelements_ * Length * int32_t(sizeof(Dtype));
        dims = {rows, cols};
        std::fill(std::begin(data_), std::end(data_), fill_value);
    }

    // Extract a patch centered at (center_y, center_x) with radius ry and
    // rx. Change boundaries depending on BorderType
    Matrix patch(int32_t center_y,
                 int32_t center_x,
                 int32_t ry,
                 int32_t rx,
                 const BorderType border_type = BorderType::REPLICATE) const
    {
        assert(center_y >= 0 && center_y < dims[0] && "center_y out of bounds");
        assert(center_x >= 0 && center_x < dims[1] && "center_x out of bounds");

        Matrix patch(2 * ry + 1, 2 * rx + 1);
        for (int32_t y = center_y - ry, r = 0; y <= center_y + ry; ++y, ++r) {
            for (int32_t x = center_x - rx, c = 0; x <= center_x + rx;
                 ++x, ++c) {
                switch (border_type) {
                case BorderType::REPLICATE:
                    patch(r, c) = (*this)(this->clamp_row_index(y),
                                          this->clamp_col_index(x));
                }
            }
        }
        return patch;
    }

    template <typename OtherType>
    Matrix<OtherType, Dynamic, Dynamic> as_type() const
    {
        return apply([](auto e) { return OtherType(e); });
    }

    Matrix clip(Dtype new_min, Dtype new_max) const
    {
        Matrix new_m(dims);
        for (int32_t i = 0; i < this->size(); ++i) {
            for (int32_t c = 0; c < new_m[0].size(); ++c) {
                auto e = std::round((*this)[i][c]);
                if (e < new_min) {
                    new_m[i][c] = new_min;
                } else if (e > new_max) {
                    new_m[i][c] = new_max;
                } else {
                    new_m[i][c] = e;
                }
            }
        }

        return new_m;
    }

    std::string str() const
    {
        std::stringstream ss;
        ss << "[";
        for (int32_t i = 0; i < dims[0] - 1; ++i) {
            for (int32_t j = 0; j < dims[1] - 1; ++j) {
                ss << (*this)(i, j) << " ";
            }
            ss << (*this)(i, dims[1] - 1) << ";" << std::endl;
        }
        for (int32_t j = 0; j < dims[1] - 1; ++j) {
            ss << (*this)(dims[0] - 1, j) << " ";
        }
        ss << this->back() << "]";
        return ss.str();
    }

    // Template magic from: http://stackoverflow.com/a/26383814
    template <typename UnaryFunctor,
              typename OutputType = typename std::result_of<
                  UnaryFunctor&(Vector<Dtype, Length>)>::type>
    decltype(auto) apply(UnaryFunctor f) const
    {
        Matrix<OutputType, Dynamic, Dynamic> new_m(dims);
        std::transform(std::begin(*this), std::end(*this), std::begin(new_m),
                       f);
        return new_m;
    }
};

// Static aliases
template <typename T>
using Matrix44 = Matrix<T, 4, 4>;
using Matrix44d = Matrix44<double>;
template <typename T>
using Matrix33 = Matrix<T, 3, 3>;
using Matrix33i = Matrix33<int32_t>;
using Matrix33d = Matrix33<double>;
using Matrix33f = Matrix33<float>;
using Matrix33b = Matrix33<uint8_t>;

// Dynamic aliases
template <typename T>
using MatrixX = Matrix<T, Dynamic, Dynamic>;
using MatrixXd = MatrixX<double>;
using MatrixXi = MatrixX<int32_t>;
using MatrixXb = MatrixX<uint8_t>;
}

#endif
<commit_msg>Add static Ones() function<commit_after>#pragma once

#ifndef SIPL_MATRIX_MATRIX_H
#define SIPL_MATRIX_MATRIX_H

#include "matrix/MatrixBase.hpp"
#include "matrix/Vector.hpp"
#include "matrix/Wrappers.hpp"
#include <cmath>
#include <iostream>

namespace sipl
{

// Need to use 'this' pointer below because templated base class members are
// not visible in a certain phase of compilation. See here:
// http://stackoverflow.com/a/6592617

// Initial class for statically-allocated matrices
template <typename Dtype, int32_t Rows, int32_t Cols>
class Matrix : public MatrixBase<Dtype,
                                 Rows,
                                 Cols,
                                 StaticArrayWrapper<Dtype, Rows * Cols>>
{
public:
    using ContainerType = StaticArrayWrapper<Dtype, Rows * Cols>;
    using BaseClass = MatrixBase<Dtype, Rows, Cols, ContainerType>;
    using BaseClass::BaseClass;

    // Explicitly import BaseClass member variables so we don't have to use
    // 'this' pointer because of 2-phase lookup (see here for more information:
    // http://stackoverflow.com/a/4643295)
    using BaseClass::nelements_;
    using BaseClass::nbytes_;
    using BaseClass::data_;
    using BaseClass::dims;

    Matrix() : BaseClass() {}

    Matrix(Dtype fill_value) : BaseClass()
    {
        std::fill(std::begin(data_), std::end(data_), fill_value);
    }

    Matrix(std::initializer_list<std::initializer_list<Dtype>> list)
        : BaseClass()
    {
        assert(int32_t(list.size()) == Rows && "size mismatch");
        for (const auto l : list) {
            assert(int32_t(l.size()) == Cols && "size mismatch");
        }

        int32_t i = 0;
        for (const auto l : list) {
            std::copy(std::begin(l), std::end(l),
                      std::begin(data_) + i++ * l.size());
        }
    }

    template <typename OtherType>
    Matrix<OtherType, Rows, Cols> as_type() const
    {
        return apply([](auto e) { return OtherType(e); });
    }

    // Template magic from: http://stackoverflow.com/a/26383814
    template <typename UnaryFunctor,
              typename OutputType =
                  typename std::result_of<UnaryFunctor&(Dtype)>::type>
    decltype(auto) apply(UnaryFunctor f) const
    {
        Matrix<OutputType, Rows, Cols> new_m;
        std::transform(std::begin(*this), std::end(*this), std::begin(new_m),
                       f);
        return new_m;
    }

    Matrix rescale(Dtype new_min, Dtype new_max) const
    {
        return apply([ min = this->min(), max = this->max(), new_min,
                       new_max ](auto e) {
            return Dtype(((new_max - new_min) / double(max - min)) * e +
                         ((new_min * max + min * new_max) / double(max - min)));
        });
    }

    Matrix clip(Dtype new_min, Dtype new_max) const
    {
        Matrix new_m;
        for (int32_t i = 0; i < this->size(); ++i) {
            auto e = std::round((*this)[i]);
            if (e < new_min) {
                new_m[i] = new_min;
            } else if (e > new_max) {
                new_m[i] = new_max;
            } else {
                new_m[i] = e;
            }
        }

        return new_m;
    }

    static Matrix Ones() { return Matrix(Dtype(1)); }
};

// Specialization of the above for dynamically-allocated matrices.
template <typename Dtype>
class Matrix<Dtype, Dynamic, Dynamic>
    : public MatrixBase<Dtype,
                        Dynamic,
                        Dynamic,
                        DynamicArrayWrapper<Dtype, Dynamic>>
{
public:
    using ContainerType = DynamicArrayWrapper<Dtype, Dynamic>;
    using BaseClass = MatrixBase<Dtype, Dynamic, Dynamic, ContainerType>;
    using BaseClass::BaseClass;

    using BaseClass::nelements_;
    using BaseClass::nbytes_;
    using BaseClass::data_;
    using BaseClass::dims;

    Matrix(int32_t rows, int32_t cols)
    {
        nelements_ = rows * cols;
        data_ = ContainerType(nelements_);
        nbytes_ = nelements_ * int32_t(sizeof(Dtype));
        dims = {rows, cols};
    }

    Matrix(int32_t rows, int32_t cols, Dtype fill_value)
    {
        nelements_ = rows * cols;
        data_ = ContainerType(nelements_);
        nbytes_ = nelements_ * int32_t(sizeof(Dtype));
        dims = {rows, cols};
        std::fill(std::begin(data_), std::end(data_), fill_value);
    }

    Matrix(std::array<int32_t, 2> new_dims, Dtype fill_value)
    {
        nelements_ = new_dims[0] * new_dims[1];
        data_ = ContainerType(nelements_);
        nbytes_ = nelements_ * int32_t(sizeof(Dtype));
        dims = new_dims;
        std::fill(std::begin(data_), std::end(data_), fill_value);
    }

    Matrix(std::array<int32_t, 2> new_dims)
    {
        nelements_ = new_dims[0] * new_dims[1];
        data_ = ContainerType(nelements_);
        nbytes_ = nelements_ * int32_t(sizeof(Dtype));
        dims = new_dims;
    }

    Matrix(std::initializer_list<std::initializer_list<Dtype>> list)
    {
        int32_t nrows = list.size();
        int32_t ncols = list.begin()->size();
        for (const auto l : list) {
            assert(int32_t(l.size()) == ncols &&
                   "initializer_list size mismatch");
        }

        nelements_ = nrows * ncols;
        nbytes_ = nelements_ * int32_t(sizeof(Dtype));
        data_ = ContainerType(nelements_);
        dims = {nrows, ncols};
        int32_t i = 0;
        for (const auto l : list) {
            std::copy(std::begin(l), std::end(l),
                      std::begin(data_) + i++ * l.size());
        }
    }

    // Extract a patch centered at (center_y, center_x) with radius ry and
    // rx. Change boundaries depending on BorderType
    Matrix patch(int32_t center_y,
                 int32_t center_x,
                 int32_t ry,
                 int32_t rx,
                 const BorderType border_type = BorderType::REPLICATE) const
    {
        assert(center_y >= 0 && center_y < dims[0] && "center_y out of bounds");
        assert(center_x >= 0 && center_x < dims[1] && "center_x out of bounds");

        Matrix patch(2 * ry + 1, 2 * rx + 1);
        for (int32_t y = center_y - ry, r = 0; y <= center_y + ry; ++y, ++r) {
            for (int32_t x = center_x - rx, c = 0; x <= center_x + rx;
                 ++x, ++c) {
                switch (border_type) {
                case BorderType::REPLICATE:
                    patch(r, c) = (*this)(this->clamp_row_index(y),
                                          this->clamp_col_index(x));
                }
            }
        }
        return patch;
    }

    template <typename OtherType>
    Matrix<OtherType, Dynamic, Dynamic> as_type() const
    {
        return apply([](auto e) { return OtherType(e); });
    }

    // Template magic from: http://stackoverflow.com/a/26383814
    template <typename UnaryFunctor,
              typename OutputType =
                  typename std::result_of<UnaryFunctor&(Dtype)>::type>
    decltype(auto) apply(UnaryFunctor f) const
    {
        Matrix<OutputType, Dynamic, Dynamic> new_m(dims);
        std::transform(std::begin(*this), std::end(*this), std::begin(new_m),
                       f);
        return new_m;
    }

    Matrix clip(Dtype new_min, Dtype new_max) const
    {
        Matrix new_m(dims);
        for (int32_t i = 0; i < this->size(); ++i) {
            auto e = std::round((*this)[i]);
            if (e < new_min) {
                new_m[i] = new_min;
            } else if (e > new_max) {
                new_m[i] = new_max;
            } else {
                new_m[i] = e;
            }
        }

        return new_m;
    }

    Matrix rescale(Dtype new_min, Dtype new_max) const
    {
        return apply([ min = this->min(), max = this->max(), new_min,
                       new_max ](auto e) {
            return Dtype(((new_max - new_min) / double(max - min)) * e +
                         ((new_min * max + min * new_max) / double(max - min)));
        });
    }

    static Matrix Ones(int32_t height, int32_t width)
    {
        return Matrix(height, width, Dtype(1));
    }

    static Matrix Ones(const std::array<int32_t, 2>& dims)
    {
        return Matrix(dims, Dtype(1));
    }
};

// Specialization of the above for dynamically-allocated Matrix with Vector
// elements.
// XXX Only specialized for fixed-sized Vector elements at this time
template <typename Dtype, int32_t Length>
class Matrix<Vector<Dtype, Length>, Dynamic, Dynamic>
    : public MatrixBase<Vector<Dtype, Length>,
                        Dynamic,
                        Dynamic,
                        DynamicArrayWrapper<Vector<Dtype, Length>, Length>>
{
public:
    using value_type = Vector<Dtype, Length>;
    using ContainerType = DynamicArrayWrapper<value_type, Length>;
    using BaseClass = MatrixBase<value_type, Dynamic, Dynamic, ContainerType>;
    using BaseClass::BaseClass;

    using BaseClass::nelements_;
    using BaseClass::nbytes_;
    using BaseClass::data_;
    using BaseClass::dims;

    Matrix(std::array<int32_t, 2> dims_)
    {
        nelements_ = dims_[0] * dims_[1];
        data_ = ContainerType(nelements_);
        nbytes_ = nelements_ * int32_t(sizeof(Dtype));
        dims = dims_;
    }

    Matrix(std::array<int32_t, 2> dims_, Dtype fillval)
    {
        nelements_ = dims_[0] * dims_[1];
        data_ = ContainerType(nelements_);
        nbytes_ = nelements_ * int32_t(sizeof(Dtype));
        dims = dims_;
        std::fill(std::begin(data_), std::end(data_), fillval);
    }

    Matrix(int32_t rows, int32_t cols)
    {
        nelements_ = rows * cols;
        nbytes_ = nelements_ * Length * int32_t(sizeof(Dtype));
        data_ = ContainerType(nelements_);
        dims = {rows, cols};
    }

    Matrix(int32_t rows, int32_t cols, value_type fill_value)
    {
        nelements_ = rows * cols;
        data_ = ContainerType(nelements_);
        nbytes_ = nelements_ * Length * int32_t(sizeof(Dtype));
        dims = {rows, cols};
        std::fill(std::begin(data_), std::end(data_), fill_value);
    }

    // Extract a patch centered at (center_y, center_x) with radius ry and
    // rx. Change boundaries depending on BorderType
    Matrix patch(int32_t center_y,
                 int32_t center_x,
                 int32_t ry,
                 int32_t rx,
                 const BorderType border_type = BorderType::REPLICATE) const
    {
        assert(center_y >= 0 && center_y < dims[0] && "center_y out of bounds");
        assert(center_x >= 0 && center_x < dims[1] && "center_x out of bounds");

        Matrix patch(2 * ry + 1, 2 * rx + 1);
        for (int32_t y = center_y - ry, r = 0; y <= center_y + ry; ++y, ++r) {
            for (int32_t x = center_x - rx, c = 0; x <= center_x + rx;
                 ++x, ++c) {
                switch (border_type) {
                case BorderType::REPLICATE:
                    patch(r, c) = (*this)(this->clamp_row_index(y),
                                          this->clamp_col_index(x));
                }
            }
        }
        return patch;
    }

    template <typename OtherType>
    Matrix<OtherType, Dynamic, Dynamic> as_type() const
    {
        return apply([](auto e) { return OtherType(e); });
    }

    Matrix clip(Dtype new_min, Dtype new_max) const
    {
        Matrix new_m(dims);
        for (int32_t i = 0; i < this->size(); ++i) {
            for (int32_t c = 0; c < new_m[0].size(); ++c) {
                auto e = std::round((*this)[i][c]);
                if (e < new_min) {
                    new_m[i][c] = new_min;
                } else if (e > new_max) {
                    new_m[i][c] = new_max;
                } else {
                    new_m[i][c] = e;
                }
            }
        }

        return new_m;
    }

    std::string str() const
    {
        std::stringstream ss;
        ss << "[";
        for (int32_t i = 0; i < dims[0] - 1; ++i) {
            for (int32_t j = 0; j < dims[1] - 1; ++j) {
                ss << (*this)(i, j) << " ";
            }
            ss << (*this)(i, dims[1] - 1) << ";" << std::endl;
        }
        for (int32_t j = 0; j < dims[1] - 1; ++j) {
            ss << (*this)(dims[0] - 1, j) << " ";
        }
        ss << this->back() << "]";
        return ss.str();
    }

    // Template magic from: http://stackoverflow.com/a/26383814
    template <typename UnaryFunctor,
              typename OutputType = typename std::result_of<
                  UnaryFunctor&(Vector<Dtype, Length>)>::type>
    decltype(auto) apply(UnaryFunctor f) const
    {
        Matrix<OutputType, Dynamic, Dynamic> new_m(dims);
        std::transform(std::begin(*this), std::end(*this), std::begin(new_m),
                       f);
        return new_m;
    }
};

// Static aliases
template <typename T>
using Matrix44 = Matrix<T, 4, 4>;
using Matrix44d = Matrix44<double>;
template <typename T>
using Matrix33 = Matrix<T, 3, 3>;
using Matrix33i = Matrix33<int32_t>;
using Matrix33d = Matrix33<double>;
using Matrix33f = Matrix33<float>;
using Matrix33b = Matrix33<uint8_t>;

// Dynamic aliases
template <typename T>
using MatrixX = Matrix<T, Dynamic, Dynamic>;
using MatrixXd = MatrixX<double>;
using MatrixXi = MatrixX<int32_t>;
using MatrixXb = MatrixX<uint8_t>;
}

#endif
<|endoftext|>