<commit_before>/*
 * PrimeSieveGUI.cpp -- This file is part of primesieve
 *
 * Copyright (C) 2012 Kim Walisch, <kim.walisch@gmail.com>
 *
 * 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 "PrimeSieveGUI.h"
#include "ui_PrimeSieveGUI.h"
#include "PrimeSieveProcess.h"
#include "../soe/ParallelPrimeSieve.h"
#include "../expr/ExpressionParser.h"

#include <QtGlobal>
#include <QCoreApplication>
#include <QByteArray>
#include <QTextStream>
#include <QFile>
#include <QSize>
#include <QMessageBox>
#include <QTextCursor>
#include <stdexcept>

PrimeSieveGUI::PrimeSieveGUI(QWidget *parent) :
  QMainWindow(parent), ui(new Ui::PrimeSieveGUI), validator_(0),
  primeSieveProcess_(0), saveAct_(0), quitAct_(0), aboutAct_(0),
  alignmentGroup_(0) {
  ui->setupUi(this);
  primeText_.push_back("Prime numbers");
  primeText_.push_back("Twin primes");
  primeText_.push_back("Prime triplets");
  primeText_.push_back("Prime quadruplets");
  primeText_.push_back("Prime quintuplets");
  primeText_.push_back("Prime sextuplets");
  primeText_.push_back("Prime septuplets");
  this->initGUI();
  this->initConnections();
}

PrimeSieveGUI::~PrimeSieveGUI() {
  this->cleanUp();
  delete validator_;
  delete saveAct_;
  delete quitAct_;
  delete aboutAct_;
  delete alignmentGroup_;
  for (; !countAct_.isEmpty(); countAct_.pop_back())
    delete countAct_.back();
  for (; !printAct_.isEmpty(); printAct_.pop_back())
    delete printAct_.back();
  // Qt code
  delete ui;
}

void PrimeSieveGUI::changeEvent(QEvent *e) {
  QMainWindow::changeEvent(e);
  switch (e->type()) {
  case QEvent::LanguageChange:
    ui->retranslateUi(this);
    break;
  default:
    break;
  }
}

void PrimeSieveGUI::initGUI() {
  this->setWindowTitle(APPLICATION_NAME + " " + APPLICATION_VERSION);
  this->createMenu(primeText_);

  // fill the sieveSizeComboBox with power of 2 values <= "4096 KB"
  for (int i = MINIMUM_SIEVE_SIZE; i <= MAXIMUM_SIEVE_SIZE; i *= 2)
    ui->sieveSizeComboBox->addItem(QString::number(i) + " KB");
  this->setTo(ui->sieveSizeComboBox, DEFAULT_SIEVE_SIZE);

  // fill the threadsComboBox with power of 2 values <= maxThreads_
  maxThreads_ = ParallelPrimeSieve::getMaxThreads();
  for (int i = 1; i < maxThreads_; i *= 2)
    ui->threadsComboBox->addItem(QString::number(i));
  ui->threadsComboBox->addItem(QString::number(maxThreads_));
  this->setTo(ui->threadsComboBox, "1");

  // set an ideal ComboBox width
  int width = ui->sieveSizeComboBox->minimumSizeHint().width();
  ui->sieveSizeComboBox->setFixedWidth(width);
  ui->threadsComboBox->setFixedWidth(width);

  // set a nice GUI size
  QSize size = this->sizeHint();
  size.setWidth(this->minimumSizeHint().width());
#if defined(Q_OS_WIN)
  size.setHeight(size.height() - size.height() / 10);
#endif
  this->resize(size);

  // limit input for arithmetic expressions
  QRegExp rx("[0-9\\+\\-\\*\\/\\%\\^\\(\\)\\e\\E]*");
  validator_ = new QRegExpValidator(rx, this);
  ui->lowerBoundLineEdit->setValidator(validator_);
  ui->upperBoundLineEdit->setValidator(validator_);
}

void PrimeSieveGUI::initConnections() {
  connect(&progressBarTimer_,     SIGNAL(timeout()),                    this, SLOT(advanceProgressBar()));
  connect(ui->lowerBoundLineEdit, SIGNAL(textChanged(const QString &)), this, SLOT(autoSetThreads()));
  connect(ui->upperBoundLineEdit, SIGNAL(textChanged(const QString &)), this, SLOT(autoSetThreads()));
  connect(ui->autoSetCheckBox,    SIGNAL(toggled(bool)),                this, SLOT(autoSetThreads()));
  connect(saveAct_,               SIGNAL(triggered()),                  this, SLOT(saveToFile()));
  connect(quitAct_,               SIGNAL(triggered()),                  qApp, SLOT(closeAllWindows()));
  connect(alignmentGroup_,        SIGNAL(triggered(QAction*)),          this, SLOT(printMenuClicked(QAction*)));
  connect(aboutAct_,              SIGNAL(triggered()),                  this, SLOT(showAboutDialog()));
}

/**
 * Get the sieve size in kilobytes from the sieveSizeComboBox.
 * @post sieveSize >= 1 && sieveSize <= 4096.
 */
int PrimeSieveGUI::getSieveSize() {
  QString sieveSize(ui->sieveSizeComboBox->currentText());
  // remove " KB"
  sieveSize.chop(3);
  return sieveSize.toInt();
}

/**
 * Get the number of threads from the threadsComboBox.
 */
int PrimeSieveGUI::getThreads() {
  return ui->threadsComboBox->currentText().toInt();
}

quint64 PrimeSieveGUI::getNumber(const QString& str) {
  if (str.isEmpty())
    throw std::invalid_argument("Please enter a lower and upper bound for prime sieving.");

  ExpressionParser<quint64> parser;
  quint64 result = 0;
  try {
    result = parser.eval(str.toAscii().data());
  }
  catch (parser_error& e) {
    throw std::invalid_argument(e.what());
  }

  int digits = str.count(QRegExp("[0-9]"));
  if (result >= UPPER_BOUND_LIMIT || (
      digits == str.size() && (
      digits >  UPPER_BOUND_STR.size() || (
      digits == UPPER_BOUND_STR.size() &&
      str    >= UPPER_BOUND_STR))))
    throw std::invalid_argument("Please use positive integers < 2^64 - 2^32*10.");

  return result;
}

void PrimeSieveGUI::setTo(QComboBox* comboBox, const QString& text) {
  comboBox->setCurrentIndex(comboBox->findText(text));
}

/**
 * If "Auto set" is enabled set an ideal number of threads for the
 * current lower bound, upper bound in the threadsComboBox.
 */
void PrimeSieveGUI::autoSetThreads() {
  if (ui->autoSetCheckBox->isEnabled() && ui->autoSetCheckBox->isChecked()) {
    try {
      quint64 lowerBound = this->getNumber(ui->lowerBoundLineEdit->text());
      quint64 upperBound = this->getNumber(ui->upperBoundLineEdit->text());
      ParallelPrimeSieve pps;
      pps.setStart(lowerBound);
      pps.setStop(upperBound);
      int idealNumThreads = pps.getNumThreads();
      if (idealNumThreads < maxThreads_) {
        // floor to the next power of 2 value
        int p = 1;
        for (; p <= idealNumThreads; p *= 2)
          ;
        idealNumThreads = p / 2;
      }
      this->setTo(ui->threadsComboBox, QString::number(idealNumThreads));
    } catch (...) {
      this->setTo(ui->threadsComboBox, "1");
    }
  }
}

/**
 * The user has chosen a custom number of threads, disable "Auto set".
 */
void PrimeSieveGUI::on_threadsComboBox_activated() {
  ui->autoSetCheckBox->setChecked(false);
}

/**
 * Start sieving primes.
 */
void PrimeSieveGUI::on_sieveButton_clicked() {
  // invert buttons, reset upon cleanUp()
  ui->sieveButton->setDisabled(true);
  ui->cancelButton->setEnabled(true);
  try {
    flags_ = this->getMenuSettings() | CALCULATE_STATUS;
    if ((flags_ & (COUNT_FLAGS | PRINT_FLAGS)) == 0)
      throw std::invalid_argument("Nothing to do, no count or print options selected.");

    quint64 lowerBound = this->getNumber(ui->lowerBoundLineEdit->text());
    quint64 upperBound = this->getNumber(ui->upperBoundLineEdit->text());
    if (lowerBound > upperBound)
      throw std::invalid_argument("The lower bound must not be greater than the upper bound.");

    // reset the GUI widgets
    ui->progressBar->setValue(ui->progressBar->minimum());
    ui->textEdit->clear();
    progressBarTimer_.start(25);

    // start a new process for sieving (avoids cancel
    // trouble with multiple threads)
    primeSieveProcess_ = new PrimeSieveProcess(this);
    if (flags_ & PRINT_FLAGS)
      connect(primeSieveProcess_, SIGNAL(readyReadStandardOutput()),
          this, SLOT(printProcessOutput()));
    connect(primeSieveProcess_, SIGNAL(finished(int, QProcess::ExitStatus)),
        this, SLOT(processFinished(int, QProcess::ExitStatus)));
    primeSieveProcess_->start(lowerBound, upperBound, this->getSieveSize(),
        flags_, this->getThreads());

  } catch (std::invalid_argument& ex) {
    this->cleanUp();
    QMessageBox::warning(this, APPLICATION_NAME, ex.what());
  } catch (std::exception& ex) {
    this->cleanUp();
    QMessageBox::critical(this, APPLICATION_NAME, ex.what());
  }
}

void PrimeSieveGUI::advanceProgressBar() {
  int permil = static_cast<int>(primeSieveProcess_->getStatus() * 10.0);
  ui->progressBar->setValue(permil);
}

/**
 * Redirects the standard output (prime numbers or prime k-tuplets) of
 * the primeSieveProcess_ to the TextEdit.
 */
void PrimeSieveGUI::printProcessOutput() {
  QByteArray buffer;
  buffer.reserve(PRINT_BUFFER_SIZE + 256);
  while (ui->cancelButton->isEnabled() && primeSieveProcess_->canReadLine()) {
    buffer.clear();
    while (primeSieveProcess_->canReadLine() && buffer.size() < PRINT_BUFFER_SIZE)
      buffer.append(primeSieveProcess_->readLine(256));
    // remove "\r\n" or '\n', '\r' at the back
    while (buffer.endsWith('\n') ||
           buffer.endsWith('\r'))
      buffer.chop(1);
    if (!buffer.isEmpty())
      ui->textEdit->appendPlainText(buffer);
/// Keep the GUI responsive.
/// @warning QApplication::processEvents() must not be used on
///          operating systems that use signal recursion (like Linux
///          X11) otherwise the stack will explode!
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
    QApplication::processEvents();
#else
    ui->textEdit->repaint();
#endif
  }
}

/**
 * Is executed when the primeSieveProcess_ finishes, checks for
 * process errors and calls this->printResults().
 */
void PrimeSieveGUI::processFinished(int exitCode,
    QProcess::ExitStatus exitStatus) {
  // the process did not exit normally, i.e. threw and exception
  if (exitCode != 0) {
    // Qt uses '/' internally, even for Windows
    QString path = QCoreApplication::applicationDirPath() + "/" + APPLICATION_NAME + "_error.txt";
    QFile error_log(path);
    if (error_log.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) {
      QTextStream out(&error_log);
      out << primeSieveProcess_->readAllStandardError();
      error_log.close();
    }
    this->cleanUp();
    QMessageBox::critical(this, APPLICATION_NAME,
        "The PrimeSieveProcess reported an error (see primesieve_error.txt), sieving has been aborted.");
  }
  // the PrimeSieveProcess has been interrupted by a signal (SIGTERM,
  // SIGKILL, ...) or a segmentation fault
  else if (exitStatus == QProcess::CrashExit) {
    this->cleanUp();
    QMessageBox::critical(this, APPLICATION_NAME, "The PrimeSieveProcess crashed, sieving has been aborted.");
  }
  // the PrimeSieveProcess has finished correctly
  else {
    ui->progressBar->setValue(ui->progressBar->maximum());
    // print results if not canceled lately
    if (ui->cancelButton->isEnabled())
      this->printResults();
    this->cleanUp();
  }
}

/**
 * Print the sieving results.
 */
void PrimeSieveGUI::printResults() {
  if (!ui->textEdit->toPlainText().isEmpty())
    ui->textEdit->appendPlainText("");

  // hack to get the count results aligned using tabs
  QString maxSizeText;
  for (int i = 0; i < COUNTS_SIZE; i++) {
    if ((flags_ & (COUNT_PRIMES << i)) && maxSizeText.size() < primeText_[i].size())
      maxSizeText = primeText_[i];
  }
  ui->textEdit->insertPlainText(maxSizeText + ": ");
  int maxWidth = ui->textEdit->cursorRect().left();
  ui->textEdit->moveCursor(QTextCursor::StartOfLine, QTextCursor::KeepAnchor);
  ui->textEdit->textCursor().removeSelectedText();
  ui->textEdit->setTabStopWidth(maxWidth);

  // print prime counts & time elapsed
  for (int i = 0; i < COUNTS_SIZE; i++) {
    if (flags_ & (COUNT_PRIMES << i))
      ui->textEdit->appendPlainText(primeText_[i] + ":\t" + QString::number(primeSieveProcess_->getCounts(i)));
  }
  if (flags_ & COUNT_KTUPLETS)
    ui->textEdit->appendPlainText("");
  QString time("Elapsed time:\t" + QString::number(primeSieveProcess_->getSeconds(), 'f', 2) + " sec");
  ui->textEdit->appendPlainText(time);
}

/**
 * Cancel sieving.
 */
void PrimeSieveGUI::on_cancelButton_clicked() {
  ui->cancelButton->setDisabled(true);
  ui->progressBar->setValue(0);
  // too late to abort
  if ((flags_ & PRINT_FLAGS) && primeSieveProcess_->isFinished())
    return;
  this->cleanUp();
}

/**
 * Clean up after sieving is finished or canceled (abort the
 * PrimeSieveProcess if still running).
 */
void PrimeSieveGUI::cleanUp() {
  progressBarTimer_.stop();
  if (primeSieveProcess_ != 0)
    delete primeSieveProcess_;
  primeSieveProcess_ = 0;
  // invert buttons
  ui->cancelButton->setDisabled(true);
  ui->sieveButton->setEnabled(true);
  // force repainting widgets
  this->repaint();
}
<commit_msg>improved error handling<commit_after>/*
 * PrimeSieveGUI.cpp -- This file is part of primesieve
 *
 * Copyright (C) 2012 Kim Walisch, <kim.walisch@gmail.com>
 *
 * 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 "PrimeSieveGUI.h"
#include "ui_PrimeSieveGUI.h"
#include "PrimeSieveProcess.h"
#include "../soe/ParallelPrimeSieve.h"
#include "../expr/ExpressionParser.h"

#include <QtGlobal>
#include <QCoreApplication>
#include <QByteArray>
#include <QTextStream>
#include <QFile>
#include <QSize>
#include <QMessageBox>
#include <QTextCursor>
#include <stdexcept>

PrimeSieveGUI::PrimeSieveGUI(QWidget *parent) :
  QMainWindow(parent), ui(new Ui::PrimeSieveGUI), validator_(0),
  primeSieveProcess_(0), saveAct_(0), quitAct_(0), aboutAct_(0),
  alignmentGroup_(0) {
  ui->setupUi(this);
  primeText_.push_back("Prime numbers");
  primeText_.push_back("Twin primes");
  primeText_.push_back("Prime triplets");
  primeText_.push_back("Prime quadruplets");
  primeText_.push_back("Prime quintuplets");
  primeText_.push_back("Prime sextuplets");
  primeText_.push_back("Prime septuplets");
  this->initGUI();
  this->initConnections();
}

PrimeSieveGUI::~PrimeSieveGUI() {
  this->cleanUp();
  delete validator_;
  delete saveAct_;
  delete quitAct_;
  delete aboutAct_;
  delete alignmentGroup_;
  for (; !countAct_.isEmpty(); countAct_.pop_back())
    delete countAct_.back();
  for (; !printAct_.isEmpty(); printAct_.pop_back())
    delete printAct_.back();
  // Qt code
  delete ui;
}

void PrimeSieveGUI::changeEvent(QEvent *e) {
  QMainWindow::changeEvent(e);
  switch (e->type()) {
  case QEvent::LanguageChange:
    ui->retranslateUi(this);
    break;
  default:
    break;
  }
}

void PrimeSieveGUI::initGUI() {
  this->setWindowTitle(APPLICATION_NAME + " " + APPLICATION_VERSION);
  this->createMenu(primeText_);

  // fill the sieveSizeComboBox with power of 2 values <= "4096 KB"
  for (int i = MINIMUM_SIEVE_SIZE; i <= MAXIMUM_SIEVE_SIZE; i *= 2)
    ui->sieveSizeComboBox->addItem(QString::number(i) + " KB");
  this->setTo(ui->sieveSizeComboBox, DEFAULT_SIEVE_SIZE);

  // fill the threadsComboBox with power of 2 values <= maxThreads_
  maxThreads_ = ParallelPrimeSieve::getMaxThreads();
  for (int i = 1; i < maxThreads_; i *= 2)
    ui->threadsComboBox->addItem(QString::number(i));
  ui->threadsComboBox->addItem(QString::number(maxThreads_));
  this->setTo(ui->threadsComboBox, "1");

  // set an ideal ComboBox width
  int width = ui->sieveSizeComboBox->minimumSizeHint().width();
  ui->sieveSizeComboBox->setFixedWidth(width);
  ui->threadsComboBox->setFixedWidth(width);

  // set a nice GUI size
  QSize size = this->sizeHint();
  size.setWidth(this->minimumSizeHint().width());
#if defined(Q_OS_WIN)
  size.setHeight(size.height() - size.height() / 10);
#endif
  this->resize(size);

  // limit input for arithmetic expressions
  QRegExp rx("[0-9\\+\\-\\*\\/\\%\\^\\(\\)\\e\\E]*");
  validator_ = new QRegExpValidator(rx, this);
  ui->lowerBoundLineEdit->setValidator(validator_);
  ui->upperBoundLineEdit->setValidator(validator_);
}

void PrimeSieveGUI::initConnections() {
  connect(&progressBarTimer_,     SIGNAL(timeout()),                    this, SLOT(advanceProgressBar()));
  connect(ui->lowerBoundLineEdit, SIGNAL(textChanged(const QString &)), this, SLOT(autoSetThreads()));
  connect(ui->upperBoundLineEdit, SIGNAL(textChanged(const QString &)), this, SLOT(autoSetThreads()));
  connect(ui->autoSetCheckBox,    SIGNAL(toggled(bool)),                this, SLOT(autoSetThreads()));
  connect(saveAct_,               SIGNAL(triggered()),                  this, SLOT(saveToFile()));
  connect(quitAct_,               SIGNAL(triggered()),                  qApp, SLOT(closeAllWindows()));
  connect(alignmentGroup_,        SIGNAL(triggered(QAction*)),          this, SLOT(printMenuClicked(QAction*)));
  connect(aboutAct_,              SIGNAL(triggered()),                  this, SLOT(showAboutDialog()));
}

/**
 * Get the sieve size in kilobytes from the sieveSizeComboBox.
 * @post sieveSize >= 1 && sieveSize <= 4096.
 */
int PrimeSieveGUI::getSieveSize() {
  QString sieveSize(ui->sieveSizeComboBox->currentText());
  // remove " KB"
  sieveSize.chop(3);
  return sieveSize.toInt();
}

/**
 * Get the number of threads from the threadsComboBox.
 */
int PrimeSieveGUI::getThreads() {
  return ui->threadsComboBox->currentText().toInt();
}

quint64 PrimeSieveGUI::getNumber(const QString& str) {
  if (str.isEmpty())
    throw std::invalid_argument("Please enter a lower and upper bound for prime sieving.");

  ExpressionParser<quint64> parser;
  quint64 result = 0;
  try {
    result = parser.eval(str.toAscii().data());
  }
  catch (parser_error& e) {
    throw std::invalid_argument((("\"" + e.expression()) + "\"\n") + e.what());
  }

  int digits = str.count(QRegExp("[0-9]"));
  if (result >= UPPER_BOUND_LIMIT || (
      digits == str.size() && (
      digits >  UPPER_BOUND_STR.size() || (
      digits == UPPER_BOUND_STR.size() &&
      str    >= UPPER_BOUND_STR))))
    throw std::invalid_argument("Please use positive integers < 2^64 - 2^32*10.");

  return result;
}

void PrimeSieveGUI::setTo(QComboBox* comboBox, const QString& text) {
  comboBox->setCurrentIndex(comboBox->findText(text));
}

/**
 * If "Auto set" is enabled set an ideal number of threads for the
 * current lower bound, upper bound in the threadsComboBox.
 */
void PrimeSieveGUI::autoSetThreads() {
  if (ui->autoSetCheckBox->isEnabled() && ui->autoSetCheckBox->isChecked()) {
    try {
      quint64 lowerBound = this->getNumber(ui->lowerBoundLineEdit->text());
      quint64 upperBound = this->getNumber(ui->upperBoundLineEdit->text());
      ParallelPrimeSieve pps;
      pps.setStart(lowerBound);
      pps.setStop(upperBound);
      int idealNumThreads = pps.getNumThreads();
      if (idealNumThreads < maxThreads_) {
        // floor to the next power of 2 value
        int p = 1;
        for (; p <= idealNumThreads; p *= 2)
          ;
        idealNumThreads = p / 2;
      }
      this->setTo(ui->threadsComboBox, QString::number(idealNumThreads));
    } catch (...) {
      this->setTo(ui->threadsComboBox, "1");
    }
  }
}

/**
 * The user has chosen a custom number of threads, disable "Auto set".
 */
void PrimeSieveGUI::on_threadsComboBox_activated() {
  ui->autoSetCheckBox->setChecked(false);
}

/**
 * Start sieving primes.
 */
void PrimeSieveGUI::on_sieveButton_clicked() {
  // invert buttons, reset upon cleanUp()
  ui->sieveButton->setDisabled(true);
  ui->cancelButton->setEnabled(true);
  try {
    flags_ = this->getMenuSettings() | CALCULATE_STATUS;
    if ((flags_ & (COUNT_FLAGS | PRINT_FLAGS)) == 0)
      throw std::invalid_argument("Nothing to do, no count or print options selected.");

    quint64 lowerBound = this->getNumber(ui->lowerBoundLineEdit->text());
    quint64 upperBound = this->getNumber(ui->upperBoundLineEdit->text());
    if (lowerBound > upperBound)
      throw std::invalid_argument("The lower bound must not be greater than the upper bound.");

    // reset the GUI widgets
    ui->progressBar->setValue(ui->progressBar->minimum());
    ui->textEdit->clear();
    progressBarTimer_.start(25);

    // start a new process for sieving (avoids cancel
    // trouble with multiple threads)
    primeSieveProcess_ = new PrimeSieveProcess(this);
    if (flags_ & PRINT_FLAGS)
      connect(primeSieveProcess_, SIGNAL(readyReadStandardOutput()),
          this, SLOT(printProcessOutput()));
    connect(primeSieveProcess_, SIGNAL(finished(int, QProcess::ExitStatus)),
        this, SLOT(processFinished(int, QProcess::ExitStatus)));
    primeSieveProcess_->start(lowerBound, upperBound, this->getSieveSize(),
        flags_, this->getThreads());

  } catch (std::invalid_argument& ex) {
    this->cleanUp();
    QMessageBox::warning(this, APPLICATION_NAME, ex.what());
  } catch (std::exception& ex) {
    this->cleanUp();
    QMessageBox::critical(this, APPLICATION_NAME, ex.what());
  }
}

void PrimeSieveGUI::advanceProgressBar() {
  int permil = static_cast<int>(primeSieveProcess_->getStatus() * 10.0);
  ui->progressBar->setValue(permil);
}

/**
 * Redirects the standard output (prime numbers or prime k-tuplets) of
 * the primeSieveProcess_ to the TextEdit.
 */
void PrimeSieveGUI::printProcessOutput() {
  QByteArray buffer;
  buffer.reserve(PRINT_BUFFER_SIZE + 256);
  while (ui->cancelButton->isEnabled() && primeSieveProcess_->canReadLine()) {
    buffer.clear();
    while (primeSieveProcess_->canReadLine() && buffer.size() < PRINT_BUFFER_SIZE)
      buffer.append(primeSieveProcess_->readLine(256));
    // remove "\r\n" or '\n', '\r' at the back
    while (buffer.endsWith('\n') ||
           buffer.endsWith('\r'))
      buffer.chop(1);
    if (!buffer.isEmpty())
      ui->textEdit->appendPlainText(buffer);
/// Keep the GUI responsive.
/// @warning QApplication::processEvents() must not be used on
///          operating systems that use signal recursion (like Linux
///          X11) otherwise the stack will explode!
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
    QApplication::processEvents();
#else
    ui->textEdit->repaint();
#endif
  }
}

/**
 * Is executed when the primeSieveProcess_ finishes, checks for
 * process errors and calls this->printResults().
 */
void PrimeSieveGUI::processFinished(int exitCode,
    QProcess::ExitStatus exitStatus) {
  // the process did not exit normally, i.e. threw and exception
  if (exitCode != 0) {
    // Qt uses '/' internally, even for Windows
    QString path = QCoreApplication::applicationDirPath() + "/" + APPLICATION_NAME + "_error.txt";
    QFile error_log(path);
    if (error_log.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) {
      QTextStream out(&error_log);
      out << primeSieveProcess_->readAllStandardError();
      error_log.close();
    }
    this->cleanUp();
    QMessageBox::critical(this, APPLICATION_NAME,
        "The PrimeSieveProcess reported an error (see primesieve_error.txt), sieving has been aborted.");
  }
  // the PrimeSieveProcess has been interrupted by a signal (SIGTERM,
  // SIGKILL, ...) or a segmentation fault
  else if (exitStatus == QProcess::CrashExit) {
    this->cleanUp();
    QMessageBox::critical(this, APPLICATION_NAME, "The PrimeSieveProcess crashed, sieving has been aborted.");
  }
  // the PrimeSieveProcess has finished correctly
  else {
    ui->progressBar->setValue(ui->progressBar->maximum());
    // print results if not canceled lately
    if (ui->cancelButton->isEnabled())
      this->printResults();
    this->cleanUp();
  }
}

/**
 * Print the sieving results.
 */
void PrimeSieveGUI::printResults() {
  if (!ui->textEdit->toPlainText().isEmpty())
    ui->textEdit->appendPlainText("");

  // hack to get the count results aligned using tabs
  QString maxSizeText;
  for (int i = 0; i < COUNTS_SIZE; i++) {
    if ((flags_ & (COUNT_PRIMES << i)) && maxSizeText.size() < primeText_[i].size())
      maxSizeText = primeText_[i];
  }
  ui->textEdit->insertPlainText(maxSizeText + ": ");
  int maxWidth = ui->textEdit->cursorRect().left();
  ui->textEdit->moveCursor(QTextCursor::StartOfLine, QTextCursor::KeepAnchor);
  ui->textEdit->textCursor().removeSelectedText();
  ui->textEdit->setTabStopWidth(maxWidth);

  // print prime counts & time elapsed
  for (int i = 0; i < COUNTS_SIZE; i++) {
    if (flags_ & (COUNT_PRIMES << i))
      ui->textEdit->appendPlainText(primeText_[i] + ":\t" + QString::number(primeSieveProcess_->getCounts(i)));
  }
  if (flags_ & COUNT_KTUPLETS)
    ui->textEdit->appendPlainText("");
  QString time("Elapsed time:\t" + QString::number(primeSieveProcess_->getSeconds(), 'f', 2) + " sec");
  ui->textEdit->appendPlainText(time);
}

/**
 * Cancel sieving.
 */
void PrimeSieveGUI::on_cancelButton_clicked() {
  ui->cancelButton->setDisabled(true);
  ui->progressBar->setValue(0);
  // too late to abort
  if ((flags_ & PRINT_FLAGS) && primeSieveProcess_->isFinished())
    return;
  this->cleanUp();
}

/**
 * Clean up after sieving is finished or canceled (abort the
 * PrimeSieveProcess if still running).
 */
void PrimeSieveGUI::cleanUp() {
  progressBarTimer_.stop();
  if (primeSieveProcess_ != 0)
    delete primeSieveProcess_;
  primeSieveProcess_ = 0;
  // invert buttons
  ui->cancelButton->setDisabled(true);
  ui->sieveButton->setEnabled(true);
  // force repainting widgets
  this->repaint();
}
<|endoftext|>


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

#include <boost/test/unit_test.hpp>

#include <chainparams.h>
#include <consensus/merkle.h>
#include <consensus/validation.h>
#include <miner.h>
#include <pow.h>
#include <random.h>
#include <test/setup_common.h>
#include <validation.h>
#include <validationinterface.h>

struct RegtestingSetup : public TestingSetup {
    RegtestingSetup() : TestingSetup(CBaseChainParams::REGTEST) {}
};

BOOST_FIXTURE_TEST_SUITE(validation_block_tests, RegtestingSetup)

struct TestSubscriber : public CValidationInterface {
    uint256 m_expected_tip;

    explicit TestSubscriber(uint256 tip) : m_expected_tip(tip) {}

    void UpdatedBlockTip(const CBlockIndex* pindexNew, const CBlockIndex* pindexFork, bool fInitialDownload) override
    {
        BOOST_CHECK_EQUAL(m_expected_tip, pindexNew->GetBlockHash());
    }

    void BlockConnected(const std::shared_ptr<const CBlock>& block, const CBlockIndex* pindex, const std::vector<CTransactionRef>& txnConflicted) override
    {
        BOOST_CHECK_EQUAL(m_expected_tip, block->hashPrevBlock);
        BOOST_CHECK_EQUAL(m_expected_tip, pindex->pprev->GetBlockHash());

        m_expected_tip = block->GetHash();
    }

    void BlockDisconnected(const std::shared_ptr<const CBlock>& block) override
    {
        BOOST_CHECK_EQUAL(m_expected_tip, block->GetHash());

        m_expected_tip = block->hashPrevBlock;
    }
};

std::shared_ptr<CBlock> Block(const uint256& prev_hash)
{
    static int i = 0;
    static uint64_t time = Params().GenesisBlock().nTime;

    CScript pubKey;
    pubKey << i++ << OP_TRUE;

    auto ptemplate = BlockAssembler(Params()).CreateNewBlock(pubKey);
    auto pblock = std::make_shared<CBlock>(ptemplate->block);
    pblock->hashPrevBlock = prev_hash;
    pblock->nTime = ++time;

    CMutableTransaction txCoinbase(*pblock->vtx[0]);
    txCoinbase.vout.resize(1);
    txCoinbase.vin[0].scriptWitness.SetNull();
    pblock->vtx[0] = MakeTransactionRef(std::move(txCoinbase));

    return pblock;
}

std::shared_ptr<CBlock> FinalizeBlock(std::shared_ptr<CBlock> pblock)
{
    pblock->hashMerkleRoot = BlockMerkleRoot(*pblock);

    while (!CheckProofOfWork(pblock->GetHash(), pblock->nBits, Params().GetConsensus())) {
        ++(pblock->nNonce);
    }

    return pblock;
}

// construct a valid block
const std::shared_ptr<const CBlock> GoodBlock(const uint256& prev_hash)
{
    return FinalizeBlock(Block(prev_hash));
}

// construct an invalid block (but with a valid header)
const std::shared_ptr<const CBlock> BadBlock(const uint256& prev_hash)
{
    auto pblock = Block(prev_hash);

    CMutableTransaction coinbase_spend;
    coinbase_spend.vin.push_back(CTxIn(COutPoint(pblock->vtx[0]->GetHash(), 0), CScript(), 0));
    coinbase_spend.vout.push_back(pblock->vtx[0]->vout[0]);

    CTransactionRef tx = MakeTransactionRef(coinbase_spend);
    pblock->vtx.push_back(tx);

    auto ret = FinalizeBlock(pblock);
    return ret;
}

void BuildChain(const uint256& root, int height, const unsigned int invalid_rate, const unsigned int branch_rate, const unsigned int max_size, std::vector<std::shared_ptr<const CBlock>>& blocks)
{
    if (height <= 0 || blocks.size() >= max_size) return;

    bool gen_invalid = InsecureRandRange(100) < invalid_rate;
    bool gen_fork = InsecureRandRange(100) < branch_rate;

    const std::shared_ptr<const CBlock> pblock = gen_invalid ? BadBlock(root) : GoodBlock(root);
    blocks.push_back(pblock);
    if (!gen_invalid) {
        BuildChain(pblock->GetHash(), height - 1, invalid_rate, branch_rate, max_size, blocks);
    }

    if (gen_fork) {
        blocks.push_back(GoodBlock(root));
        BuildChain(blocks.back()->GetHash(), height - 1, invalid_rate, branch_rate, max_size, blocks);
    }
}

BOOST_AUTO_TEST_CASE(processnewblock_signals_ordering)
{
    // build a large-ish chain that's likely to have some forks
    std::vector<std::shared_ptr<const CBlock>> blocks;
    while (blocks.size() < 50) {
        blocks.clear();
        BuildChain(Params().GenesisBlock().GetHash(), 100, 15, 10, 500, blocks);
    }

    bool ignored;
    CValidationState state;
    std::vector<CBlockHeader> headers;
    std::transform(blocks.begin(), blocks.end(), std::back_inserter(headers), [](std::shared_ptr<const CBlock> b) { return b->GetBlockHeader(); });

    // Process all the headers so we understand the toplogy of the chain
    BOOST_CHECK(ProcessNewBlockHeaders(headers, state, Params()));

    // Connect the genesis block and drain any outstanding events
    BOOST_CHECK(ProcessNewBlock(Params(), std::make_shared<CBlock>(Params().GenesisBlock()), true, &ignored));
    SyncWithValidationInterfaceQueue();

    // subscribe to events (this subscriber will validate event ordering)
    const CBlockIndex* initial_tip = nullptr;
    {
        LOCK(cs_main);
        initial_tip = ::ChainActive().Tip();
    }
    TestSubscriber sub(initial_tip->GetBlockHash());
    RegisterValidationInterface(&sub);

    // create a bunch of threads that repeatedly process a block generated above at random
    // this will create parallelism and randomness inside validation - the ValidationInterface
    // will subscribe to events generated during block validation and assert on ordering invariance
    std::vector<std::thread> threads;
    for (int i = 0; i < 10; i++) {
        threads.emplace_back([&blocks]() {
            bool ignored;
            FastRandomContext insecure;
            for (int i = 0; i < 1000; i++) {
                auto block = blocks[insecure.randrange(blocks.size() - 1)];
                ProcessNewBlock(Params(), block, true, &ignored);
            }

            // to make sure that eventually we process the full chain - do it here
            for (auto block : blocks) {
                if (block->vtx.size() == 1) {
                    bool processed = ProcessNewBlock(Params(), block, true, &ignored);
                    assert(processed);
                }
            }
        });
    }

    for (auto& t : threads) {
        t.join();
    }
    while (GetMainSignals().CallbacksPending() > 0) {
        MilliSleep(100);
    }

    UnregisterValidationInterface(&sub);

    LOCK(cs_main);
    BOOST_CHECK_EQUAL(sub.m_expected_tip, ::ChainActive().Tip()->GetBlockHash());
}

BOOST_AUTO_TEST_SUITE_END()
<commit_msg>[test] Add test to check mempool consistency in case of reorgs<commit_after>// Copyright (c) 2018-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <boost/test/unit_test.hpp>

#include <chainparams.h>
#include <consensus/merkle.h>
#include <consensus/validation.h>
#include <miner.h>
#include <pow.h>
#include <random.h>
#include <script/standard.h>
#include <test/setup_common.h>
#include <validation.h>
#include <validationinterface.h>

struct RegtestingSetup : public TestingSetup {
    RegtestingSetup() : TestingSetup(CBaseChainParams::REGTEST) {}
};

static const std::vector<unsigned char> V_OP_TRUE{OP_TRUE};

BOOST_FIXTURE_TEST_SUITE(validation_block_tests, RegtestingSetup)

struct TestSubscriber : public CValidationInterface {
    uint256 m_expected_tip;

    explicit TestSubscriber(uint256 tip) : m_expected_tip(tip) {}

    void UpdatedBlockTip(const CBlockIndex* pindexNew, const CBlockIndex* pindexFork, bool fInitialDownload) override
    {
        BOOST_CHECK_EQUAL(m_expected_tip, pindexNew->GetBlockHash());
    }

    void BlockConnected(const std::shared_ptr<const CBlock>& block, const CBlockIndex* pindex, const std::vector<CTransactionRef>& txnConflicted) override
    {
        BOOST_CHECK_EQUAL(m_expected_tip, block->hashPrevBlock);
        BOOST_CHECK_EQUAL(m_expected_tip, pindex->pprev->GetBlockHash());

        m_expected_tip = block->GetHash();
    }

    void BlockDisconnected(const std::shared_ptr<const CBlock>& block) override
    {
        BOOST_CHECK_EQUAL(m_expected_tip, block->GetHash());

        m_expected_tip = block->hashPrevBlock;
    }
};

std::shared_ptr<CBlock> Block(const uint256& prev_hash)
{
    static int i = 0;
    static uint64_t time = Params().GenesisBlock().nTime;

    CScript pubKey;
    pubKey << i++ << OP_TRUE;

    auto ptemplate = BlockAssembler(Params()).CreateNewBlock(pubKey);
    auto pblock = std::make_shared<CBlock>(ptemplate->block);
    pblock->hashPrevBlock = prev_hash;
    pblock->nTime = ++time;

    pubKey.clear();
    {
        WitnessV0ScriptHash witness_program;
        CSHA256().Write(&V_OP_TRUE[0], V_OP_TRUE.size()).Finalize(witness_program.begin());
        pubKey << OP_0 << ToByteVector(witness_program);
    }

    // Make the coinbase transaction with two outputs:
    // One zero-value one that has a unique pubkey to make sure that blocks at the same height can have a different hash
    // Another one that has the coinbase reward in a P2WSH with OP_TRUE as witness program to make it easy to spend
    CMutableTransaction txCoinbase(*pblock->vtx[0]);
    txCoinbase.vout.resize(2);
    txCoinbase.vout[1].scriptPubKey = pubKey;
    txCoinbase.vout[1].nValue = txCoinbase.vout[0].nValue;
    txCoinbase.vout[0].nValue = 0;
    txCoinbase.vin[0].scriptWitness.SetNull();
    pblock->vtx[0] = MakeTransactionRef(std::move(txCoinbase));

    return pblock;
}

std::shared_ptr<CBlock> FinalizeBlock(std::shared_ptr<CBlock> pblock)
{
    LOCK(cs_main); // For LookupBlockIndex
    GenerateCoinbaseCommitment(*pblock, LookupBlockIndex(pblock->hashPrevBlock), Params().GetConsensus());

    pblock->hashMerkleRoot = BlockMerkleRoot(*pblock);

    while (!CheckProofOfWork(pblock->GetHash(), pblock->nBits, Params().GetConsensus())) {
        ++(pblock->nNonce);
    }

    return pblock;
}

// construct a valid block
std::shared_ptr<const CBlock> GoodBlock(const uint256& prev_hash)
{
    return FinalizeBlock(Block(prev_hash));
}

// construct an invalid block (but with a valid header)
std::shared_ptr<const CBlock> BadBlock(const uint256& prev_hash)
{
    auto pblock = Block(prev_hash);

    CMutableTransaction coinbase_spend;
    coinbase_spend.vin.push_back(CTxIn(COutPoint(pblock->vtx[0]->GetHash(), 0), CScript(), 0));
    coinbase_spend.vout.push_back(pblock->vtx[0]->vout[0]);

    CTransactionRef tx = MakeTransactionRef(coinbase_spend);
    pblock->vtx.push_back(tx);

    auto ret = FinalizeBlock(pblock);
    return ret;
}

void BuildChain(const uint256& root, int height, const unsigned int invalid_rate, const unsigned int branch_rate, const unsigned int max_size, std::vector<std::shared_ptr<const CBlock>>& blocks)
{
    if (height <= 0 || blocks.size() >= max_size) return;

    bool gen_invalid = InsecureRandRange(100) < invalid_rate;
    bool gen_fork = InsecureRandRange(100) < branch_rate;

    const std::shared_ptr<const CBlock> pblock = gen_invalid ? BadBlock(root) : GoodBlock(root);
    blocks.push_back(pblock);
    if (!gen_invalid) {
        BuildChain(pblock->GetHash(), height - 1, invalid_rate, branch_rate, max_size, blocks);
    }

    if (gen_fork) {
        blocks.push_back(GoodBlock(root));
        BuildChain(blocks.back()->GetHash(), height - 1, invalid_rate, branch_rate, max_size, blocks);
    }
}

BOOST_AUTO_TEST_CASE(processnewblock_signals_ordering)
{
    // build a large-ish chain that's likely to have some forks
    std::vector<std::shared_ptr<const CBlock>> blocks;
    while (blocks.size() < 50) {
        blocks.clear();
        BuildChain(Params().GenesisBlock().GetHash(), 100, 15, 10, 500, blocks);
    }

    bool ignored;
    CValidationState state;
    std::vector<CBlockHeader> headers;
    std::transform(blocks.begin(), blocks.end(), std::back_inserter(headers), [](std::shared_ptr<const CBlock> b) { return b->GetBlockHeader(); });

    // Process all the headers so we understand the toplogy of the chain
    BOOST_CHECK(ProcessNewBlockHeaders(headers, state, Params()));

    // Connect the genesis block and drain any outstanding events
    BOOST_CHECK(ProcessNewBlock(Params(), std::make_shared<CBlock>(Params().GenesisBlock()), true, &ignored));
    SyncWithValidationInterfaceQueue();

    // subscribe to events (this subscriber will validate event ordering)
    const CBlockIndex* initial_tip = nullptr;
    {
        LOCK(cs_main);
        initial_tip = ::ChainActive().Tip();
    }
    TestSubscriber sub(initial_tip->GetBlockHash());
    RegisterValidationInterface(&sub);

    // create a bunch of threads that repeatedly process a block generated above at random
    // this will create parallelism and randomness inside validation - the ValidationInterface
    // will subscribe to events generated during block validation and assert on ordering invariance
    std::vector<std::thread> threads;
    for (int i = 0; i < 10; i++) {
        threads.emplace_back([&blocks]() {
            bool ignored;
            FastRandomContext insecure;
            for (int i = 0; i < 1000; i++) {
                auto block = blocks[insecure.randrange(blocks.size() - 1)];
                ProcessNewBlock(Params(), block, true, &ignored);
            }

            // to make sure that eventually we process the full chain - do it here
            for (auto block : blocks) {
                if (block->vtx.size() == 1) {
                    bool processed = ProcessNewBlock(Params(), block, true, &ignored);
                    assert(processed);
                }
            }
        });
    }

    for (auto& t : threads) {
        t.join();
    }
    while (GetMainSignals().CallbacksPending() > 0) {
        MilliSleep(100);
    }

    UnregisterValidationInterface(&sub);

    LOCK(cs_main);
    BOOST_CHECK_EQUAL(sub.m_expected_tip, ::ChainActive().Tip()->GetBlockHash());
}

/**
 * Test that mempool updates happen atomically with reorgs.
 *
 * This prevents RPC clients, among others, from retrieving immediately-out-of-date mempool data
 * during large reorgs.
 *
 * The test verifies this by creating a chain of `num_txs` blocks, matures their coinbases, and then
 * submits txns spending from their coinbase to the mempool. A fork chain is then processed,
 * invalidating the txns and evicting them from the mempool.
 *
 * We verify that the mempool updates atomically by polling it continuously
 * from another thread during the reorg and checking that its size only changes
 * once. The size changing exactly once indicates that the polling thread's
 * view of the mempool is either consistent with the chain state before reorg,
 * or consistent with the chain state after the reorg, and not just consistent
 * with some intermediate state during the reorg.
 */
BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
{
    bool ignored;
    auto ProcessBlock = [&ignored](std::shared_ptr<const CBlock> block) -> bool {
        return ProcessNewBlock(Params(), block, /* fForceProcessing */ true, /* fNewBlock */ &ignored);
    };

    // Process all mined blocks
    BOOST_REQUIRE(ProcessBlock(std::make_shared<CBlock>(Params().GenesisBlock())));
    auto last_mined = GoodBlock(Params().GenesisBlock().GetHash());
    BOOST_REQUIRE(ProcessBlock(last_mined));

    // Run the test multiple times
    for (int test_runs = 3; test_runs > 0; --test_runs) {
        BOOST_CHECK_EQUAL(last_mined->GetHash(), ::ChainActive().Tip()->GetBlockHash());

        // Later on split from here
        const uint256 split_hash{last_mined->hashPrevBlock};

        // Create a bunch of transactions to spend the miner rewards of the
        // most recent blocks
        std::vector<CTransactionRef> txs;
        for (int num_txs = 22; num_txs > 0; --num_txs) {
            CMutableTransaction mtx;
            mtx.vin.push_back(CTxIn{COutPoint{last_mined->vtx[0]->GetHash(), 1}, CScript{}});
            mtx.vin[0].scriptWitness.stack.push_back(V_OP_TRUE);
            mtx.vout.push_back(last_mined->vtx[0]->vout[1]);
            mtx.vout[0].nValue -= 1000;
            txs.push_back(MakeTransactionRef(mtx));

            last_mined = GoodBlock(last_mined->GetHash());
            BOOST_REQUIRE(ProcessBlock(last_mined));
        }

        // Mature the inputs of the txs
        for (int j = COINBASE_MATURITY; j > 0; --j) {
            last_mined = GoodBlock(last_mined->GetHash());
            BOOST_REQUIRE(ProcessBlock(last_mined));
        }

        // Mine a reorg (and hold it back) before adding the txs to the mempool
        const uint256 tip_init{last_mined->GetHash()};

        std::vector<std::shared_ptr<const CBlock>> reorg;
        last_mined = GoodBlock(split_hash);
        reorg.push_back(last_mined);
        for (size_t j = COINBASE_MATURITY + txs.size() + 1; j > 0; --j) {
            last_mined = GoodBlock(last_mined->GetHash());
            reorg.push_back(last_mined);
        }

        // Add the txs to the tx pool
        {
            LOCK(cs_main);
            CValidationState state;
            std::list<CTransactionRef> plTxnReplaced;
            for (const auto& tx : txs) {
                BOOST_REQUIRE(AcceptToMemoryPool(
                    ::mempool,
                    state,
                    tx,
                    /* pfMissingInputs */ &ignored,
                    &plTxnReplaced,
                    /* bypass_limits */ false,
                    /* nAbsurdFee */ 0));
            }
        }

        // Check that all txs are in the pool
        {
            LOCK(::mempool.cs);
            BOOST_CHECK_EQUAL(::mempool.mapTx.size(), txs.size());
        }

        // Run a thread that simulates an RPC caller that is polling while
        // validation is doing a reorg
        std::thread rpc_thread{[&]() {
            // This thread is checking that the mempool either contains all of
            // the transactions invalidated by the reorg, or none of them, and
            // not some intermediate amount.
            while (true) {
                LOCK(::mempool.cs);
                if (::mempool.mapTx.size() == 0) {
                    // We are done with the reorg
                    break;
                }
                // Internally, we might be in the middle of the reorg, but
                // externally the reorg to the most-proof-of-work chain should
                // be atomic. So the caller assumes that the returned mempool
                // is consistent. That is, it has all txs that were there
                // before the reorg.
                assert(::mempool.mapTx.size() == txs.size());
                continue;
            }
            LOCK(cs_main);
            // We are done with the reorg, so the tip must have changed
            assert(tip_init != ::ChainActive().Tip()->GetBlockHash());
        }};

        // Submit the reorg in this thread to invalidate and remove the txs from the tx pool
        for (const auto& b : reorg) {
            ProcessBlock(b);
        }
        // Check that the reorg was eventually successful
        BOOST_CHECK_EQUAL(last_mined->GetHash(), ::ChainActive().Tip()->GetBlockHash());

        // We can join the other thread, which returns when the reorg was successful
        rpc_thread.join();
    }
}
BOOST_AUTO_TEST_SUITE_END()
<|endoftext|>


<commit_before>/** @file
    @brief Implementation

    @date 2015, 2016

    @author
    Sensics, Inc.
    <http://sensics.com/osvr>
*/

// Copyright 2015, 2016 Sensics, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//        http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Internal Includes
#include "PoseEstimator_RANSAC.h"
#include "CameraParameters.h"
#include "LED.h"
#include "UsefulQuaternions.h"
#include "cvToEigen.h"

// Library/third-party includes
#include <opencv2/calib3d/calib3d.hpp>
#include <opencv2/core/affine.hpp>
#include <opencv2/core/core.hpp>

// Standard includes
#include <algorithm>

/// This was enabled/primarily useful to reduce jitter when RANSAC was the only
/// tracking method, for culling a little tighter than the RANSAC PNP itself
/// did.
#undef OSVR_UVBI_TEST_RANSAC_REPROJECTION

namespace osvr {
namespace vbtracker {
    bool RANSACPoseEstimator::operator()(CameraParameters const &camParams,
                                         LedPtrList const &leds,
                                         BeaconStateVec const &beacons,
                                         std::vector<BeaconData> &beaconDebug,
                                         Eigen::Vector3d &outXlate,
                                         Eigen::Quaterniond &outQuat) {

        // We need to get a pair of matched vectors of points: 2D locations
        // with in the image and 3D locations in model space.  There needs to
        // be a correspondence between the points in these vectors, such that
        // the ith element in one matches the ith element in the other.  We
        // make these by looking up the locations of LEDs with known identifiers
        // and pushing both onto the vectors at the same time.

        std::vector<cv::Point3f> objectPoints;
        std::vector<cv::Point2f> imagePoints;
        std::vector<ZeroBasedBeaconId> beaconIds;
        for (auto const &led : leds) {
            auto id = makeZeroBased(led->getID());
            auto index = asIndex(id);
            beaconDebug[index].variance = -1;
            beaconDebug[index].measurement = led->getLocationForTracking();
            beaconIds.push_back(id);

            /// Effectively invert the image points here so we get the output of
            /// a coordinate system we want.
            imagePoints.push_back(led->getLocationForTracking());
            objectPoints.push_back(
                vec3dToCVPoint3f(beacons[index]->stateVector()));
        }

        // Make sure we have enough points to do our estimation.
        if (objectPoints.size() < m_permittedOutliers + m_requiredInliers) {
            return false;
        }

        // Produce an estimate of the translation and rotation needed to take
        // points from model space into camera space.  We allow for at most
        // m_permittedOutliers outliers. Even in simulation data, we sometimes
        // find duplicate IDs for LEDs, indicating that we are getting
        // mis-identified ones sometimes.
        // We tried using the previous guess to reduce the amount of computation
        // being done, but this got us stuck in infinite locations.  We seem to
        // do okay without using it, so leaving it out.
        // @todo Make number of iterations into a parameter.
        bool usePreviousGuess = false;
        int iterationsCount = 5;
        float maxReprojectionError = 6.f;
        cv::Mat inlierIndices;

        cv::Mat rvec;
        cv::Mat tvec;
#if CV_MAJOR_VERSION == 2
        cv::solvePnPRansac(
            objectPoints, imagePoints, camParams.cameraMatrix,
            camParams.distortionParameters, rvec, tvec, usePreviousGuess,
            iterationsCount, maxReprojectionError,
            static_cast<int>(objectPoints.size() - m_permittedOutliers),
            inlierIndices);
#elif CV_MAJOR_VERSION == 3
        // parameter added to the OpenCV 3.0 interface in place of the number of
        // inliers
        /// @todo how to determine this requested confidence from the data we're
        /// given?
        double confidence = 0.99;
        auto ransacResult = cv::solvePnPRansac(
            objectPoints, imagePoints, camParams.cameraMatrix,
            camParams.distortionParameters, rvec, tvec, usePreviousGuess,
            iterationsCount, maxReprojectionError, confidence, inlierIndices);
        if (!ransacResult) {
            return false;
        }
#else
#error "Unrecognized OpenCV version!"
#endif

        //==========================================================================
        // Make sure we got all the inliers we needed.  Otherwise, reject this
        // pose.
        if (inlierIndices.rows < static_cast<int>(m_requiredInliers)) {
            return false;
        }

        if (inlierIndices.rows > 0) {

#ifdef OSVR_UVBI_TEST_RANSAC_REPROJECTION
            //==========================================================================
            // Reproject the inliers into the image and make sure they are
            // actually
            // close to the expected location; otherwise, we have a bad pose.
            const double pixelReprojectionErrorForSingleAxisMax = 4;
            std::vector<cv::Point3f> inlierObjectPoints;
            std::vector<cv::Point2f> inlierImagePoints;
            for (int i = 0; i < inlierIndices.rows; i++) {
                inlierObjectPoints.push_back(objectPoints[i]);
                inlierImagePoints.push_back(imagePoints[i]);
            }
            std::vector<cv::Point2f> reprojectedPoints;
            cv::projectPoints(
                inlierObjectPoints, rvec, tvec, camParams.cameraMatrix,
                camParams.distortionParameters, reprojectedPoints);

            for (size_t i = 0; i < reprojectedPoints.size(); i++) {
                if (reprojectedPoints[i].x - inlierImagePoints[i].x >
                    pixelReprojectionErrorForSingleAxisMax) {
                    std::cout << "Reject on reprojected beacon id "
                              << makeOneBased(inlierBeaconIds[i]).value()
                              << " x axis." << std::endl;
                    return false;
                }
                if (reprojectedPoints[i].y - inlierImagePoints[i].y >
                    pixelReprojectionErrorForSingleAxisMax) {
                    std::cout << "Reject on reprojected beacon id "
                              << makeOneBased(inlierBeaconIds[i]).value()
                              << " y axis." << std::endl;
                    return false;
                }
            }
#endif

            /// Make a vector of the inlier beacon IDs.
            std::vector<ZeroBasedBeaconId> inlierBeaconIds;
            for (int i = 0; i < inlierIndices.rows; i++) {
                inlierBeaconIds.push_back(beaconIds[i]);
            }

            /// Now, we will sort that vector of inlier beacon IDs so we can
            /// rapidly binary search it to flag the LEDs we used.

            // Need a custom comparator for the ID type.
            auto idComparator = [](ZeroBasedBeaconId const &lhs,
                                   ZeroBasedBeaconId const &rhs) {
                return lhs.value() < rhs.value();
            };
            std::sort(begin(inlierBeaconIds), end(inlierBeaconIds),
                      idComparator);
            // This lambda wraps binary_search to do what it says: check to see
            // if a given beacon ID is in the list of inlier beacons.
            auto isAnInlierBeacon = [&inlierBeaconIds, &idComparator](
                ZeroBasedBeaconId const &needle) {
                return std::binary_search(begin(inlierBeaconIds),
                                          end(inlierBeaconIds), needle,
                                          idComparator);
            };
            for (auto &led : leds) {
                if (isAnInlierBeacon(led->getID())) {
                    led->markAsUsed();
                }
            }
        }

        //==========================================================================
        // Convert this into an OSVR representation of the transformation that
        // gives the pose of the HDK origin in the camera coordinate system,
        // switching units to meters and encoding the angle in a unit
        // quaternion.
        // The matrix described by rvec and tvec takes points in model space
        // (the space where the LEDs are defined, which is in mm away from an
        // implied origin) into a coordinate system where the center is at the
        // camera's origin, with X to the right, Y down, and Z in the direction
        // that the camera is facing (but still in the original units of mm):
        //  |Xc|   |r11 r12 r13 t1| |Xm|
        //  |Yc| = |r21 r22 r23 t2|*|Ym|
        //  |Zc|   |r31 r32 r33 t3| |Zm|
        //                          |1 |
        //  That is, it rotates into the camera coordinate system and then adds
        // the translation, which is in the camera coordinate system.
        //  This is the transformation we want, since it reports the sensor's
        // position and orientation in camera space, except that we want to
        // convert
        // the units into meters and the orientation into a Quaternion.
        //  NOTE: This is a right-handed coordinate system with X pointing
        // towards the right from the camera center of projection, Y pointing
        // down, and Z pointing along the camera viewing direction, if the input
        // points are not inverted.

        if (tvec.at<double>(2) < 0) {
// -z means the wrong side of the pinhole
/// @todo find out why OpenCV is now returning these values sometimes
#if 0
            std::cout << "On the wrong side of the looking glass:" << tvec
                      << std::endl;
#endif
            // So, we invert translation, and apply 180 rotation (to rotation)
            // about z.
            tvec *= -1;
            /// Make a rotation matrix for 180 about z
            auto rotMat = cv::Affine3d::Mat3::eye();
            rotMat(0, 0) = -1;
            rotMat(1, 1) = -1;
            /// Apply the transform and get the rvec out again.
            rvec = cv::Mat(cv::Affine3d(rvec) * (cv::Affine3d(rotMat)).rvec());
        }
        outXlate = cvToVector3d(tvec);
        outQuat = cvRotVecToQuat(rvec);
        return true;
    }

    /// Variance in Meters^2
    static const double InitialPositionStateError = 0.;
    /// Variance in Radians^2
    static const double InitialOrientationStateError = 0.5;
    static const double InitialVelocityStateError = 0.;
    static const double InitialAngVelStateError = 0.;
    static const double InitialStateError[] = {
        InitialPositionStateError,    InitialPositionStateError,
        InitialPositionStateError,    InitialOrientationStateError,
        InitialOrientationStateError, InitialOrientationStateError,
        InitialVelocityStateError,    InitialVelocityStateError,
        InitialVelocityStateError,    InitialAngVelStateError,
        InitialAngVelStateError,      InitialAngVelStateError};
    bool RANSACPoseEstimator::operator()(EstimatorInOutParams const &p,
                                         LedPtrList const &leds) {
        Eigen::Vector3d xlate;
        Eigen::Quaterniond quat;
        /// Call the main pose estimation to get the vector and quat.
        {
            auto ret = (*this)(p.camParams, leds, p.beacons, p.beaconDebug,
                               xlate, quat);
            if (!ret) {
                return false;
            }
        }
        /// OK, so if we're here, estimation succeeded and we have valid data in
        /// xlate and quat.
        p.state.position() = xlate;
        p.state.setQuaternion(quat);
/// Zero things we can't measure.
#if 1
        p.state.incrementalOrientation() = Eigen::Vector3d::Zero();
        p.state.velocity() = Eigen::Vector3d::Zero();
        p.state.angularVelocity() = Eigen::Vector3d::Zero();
#endif
        using StateVec = kalman::types::DimVector<BodyState>;
        using StateSquareMatrix = kalman::types::DimSquareMatrix<BodyState>;

        StateSquareMatrix covariance = StateVec(InitialStateError).asDiagonal();
        /// @todo Copy the existing angular velocity error covariance
        /*
        covariance.bottomRightCorner<3, 3>() =
            p.state.errorCovariance().bottomRightCorner<3, 3>();
            */
        p.state.setErrorCovariance(covariance);
        return true;
    }

} // namespace vbtracker
} // namespace osvr
<commit_msg>Missing include when testing for flips.<commit_after>/** @file
    @brief Implementation

    @date 2015, 2016

    @author
    Sensics, Inc.
    <http://sensics.com/osvr>
*/

// Copyright 2015, 2016 Sensics, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//        http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Internal Includes
#include "PoseEstimator_RANSAC.h"
#include "CameraParameters.h"
#include "LED.h"
#include "UsefulQuaternions.h"
#include "cvToEigen.h"

// Library/third-party includes
#include <opencv2/calib3d/calib3d.hpp>
#include <opencv2/core/affine.hpp>
#include <opencv2/core/core.hpp>

// Standard includes
#include <algorithm>
#include <iostream>

/// This was enabled/primarily useful to reduce jitter when RANSAC was the only
/// tracking method, for culling a little tighter than the RANSAC PNP itself
/// did.
#undef OSVR_UVBI_TEST_RANSAC_REPROJECTION

namespace osvr {
namespace vbtracker {
    bool RANSACPoseEstimator::operator()(CameraParameters const &camParams,
                                         LedPtrList const &leds,
                                         BeaconStateVec const &beacons,
                                         std::vector<BeaconData> &beaconDebug,
                                         Eigen::Vector3d &outXlate,
                                         Eigen::Quaterniond &outQuat) {

        // We need to get a pair of matched vectors of points: 2D locations
        // with in the image and 3D locations in model space.  There needs to
        // be a correspondence between the points in these vectors, such that
        // the ith element in one matches the ith element in the other.  We
        // make these by looking up the locations of LEDs with known identifiers
        // and pushing both onto the vectors at the same time.

        std::vector<cv::Point3f> objectPoints;
        std::vector<cv::Point2f> imagePoints;
        std::vector<ZeroBasedBeaconId> beaconIds;
        for (auto const &led : leds) {
            auto id = makeZeroBased(led->getID());
            auto index = asIndex(id);
            beaconDebug[index].variance = -1;
            beaconDebug[index].measurement = led->getLocationForTracking();
            beaconIds.push_back(id);

            /// Effectively invert the image points here so we get the output of
            /// a coordinate system we want.
            imagePoints.push_back(led->getLocationForTracking());
            objectPoints.push_back(
                vec3dToCVPoint3f(beacons[index]->stateVector()));
        }

        // Make sure we have enough points to do our estimation.
        if (objectPoints.size() < m_permittedOutliers + m_requiredInliers) {
            return false;
        }

        // Produce an estimate of the translation and rotation needed to take
        // points from model space into camera space.  We allow for at most
        // m_permittedOutliers outliers. Even in simulation data, we sometimes
        // find duplicate IDs for LEDs, indicating that we are getting
        // mis-identified ones sometimes.
        // We tried using the previous guess to reduce the amount of computation
        // being done, but this got us stuck in infinite locations.  We seem to
        // do okay without using it, so leaving it out.
        // @todo Make number of iterations into a parameter.
        bool usePreviousGuess = false;
        int iterationsCount = 5;
        float maxReprojectionError = 6.f;
        cv::Mat inlierIndices;

        cv::Mat rvec;
        cv::Mat tvec;
#if CV_MAJOR_VERSION == 2
        cv::solvePnPRansac(
            objectPoints, imagePoints, camParams.cameraMatrix,
            camParams.distortionParameters, rvec, tvec, usePreviousGuess,
            iterationsCount, maxReprojectionError,
            static_cast<int>(objectPoints.size() - m_permittedOutliers),
            inlierIndices);
#elif CV_MAJOR_VERSION == 3
        // parameter added to the OpenCV 3.0 interface in place of the number of
        // inliers
        /// @todo how to determine this requested confidence from the data we're
        /// given?
        double confidence = 0.99;
        auto ransacResult = cv::solvePnPRansac(
            objectPoints, imagePoints, camParams.cameraMatrix,
            camParams.distortionParameters, rvec, tvec, usePreviousGuess,
            iterationsCount, maxReprojectionError, confidence, inlierIndices);
        if (!ransacResult) {
            return false;
        }
#else
#error "Unrecognized OpenCV version!"
#endif

        //==========================================================================
        // Make sure we got all the inliers we needed.  Otherwise, reject this
        // pose.
        if (inlierIndices.rows < static_cast<int>(m_requiredInliers)) {
            return false;
        }

        if (inlierIndices.rows > 0) {

#ifdef OSVR_UVBI_TEST_RANSAC_REPROJECTION
            //==========================================================================
            // Reproject the inliers into the image and make sure they are
            // actually
            // close to the expected location; otherwise, we have a bad pose.
            const double pixelReprojectionErrorForSingleAxisMax = 4;
            std::vector<cv::Point3f> inlierObjectPoints;
            std::vector<cv::Point2f> inlierImagePoints;
            for (int i = 0; i < inlierIndices.rows; i++) {
                inlierObjectPoints.push_back(objectPoints[i]);
                inlierImagePoints.push_back(imagePoints[i]);
            }
            std::vector<cv::Point2f> reprojectedPoints;
            cv::projectPoints(
                inlierObjectPoints, rvec, tvec, camParams.cameraMatrix,
                camParams.distortionParameters, reprojectedPoints);

            for (size_t i = 0; i < reprojectedPoints.size(); i++) {
                if (reprojectedPoints[i].x - inlierImagePoints[i].x >
                    pixelReprojectionErrorForSingleAxisMax) {
                    std::cout << "Reject on reprojected beacon id "
                              << makeOneBased(inlierBeaconIds[i]).value()
                              << " x axis." << std::endl;
                    return false;
                }
                if (reprojectedPoints[i].y - inlierImagePoints[i].y >
                    pixelReprojectionErrorForSingleAxisMax) {
                    std::cout << "Reject on reprojected beacon id "
                              << makeOneBased(inlierBeaconIds[i]).value()
                              << " y axis." << std::endl;
                    return false;
                }
            }
#endif

            /// Make a vector of the inlier beacon IDs.
            std::vector<ZeroBasedBeaconId> inlierBeaconIds;
            for (int i = 0; i < inlierIndices.rows; i++) {
                inlierBeaconIds.push_back(beaconIds[i]);
            }

            /// Now, we will sort that vector of inlier beacon IDs so we can
            /// rapidly binary search it to flag the LEDs we used.

            // Need a custom comparator for the ID type.
            auto idComparator = [](ZeroBasedBeaconId const &lhs,
                                   ZeroBasedBeaconId const &rhs) {
                return lhs.value() < rhs.value();
            };
            std::sort(begin(inlierBeaconIds), end(inlierBeaconIds),
                      idComparator);
            // This lambda wraps binary_search to do what it says: check to see
            // if a given beacon ID is in the list of inlier beacons.
            auto isAnInlierBeacon = [&inlierBeaconIds, &idComparator](
                ZeroBasedBeaconId const &needle) {
                return std::binary_search(begin(inlierBeaconIds),
                                          end(inlierBeaconIds), needle,
                                          idComparator);
            };
            for (auto &led : leds) {
                if (isAnInlierBeacon(led->getID())) {
                    led->markAsUsed();
                }
            }
        }

        //==========================================================================
        // Convert this into an OSVR representation of the transformation that
        // gives the pose of the HDK origin in the camera coordinate system,
        // switching units to meters and encoding the angle in a unit
        // quaternion.
        // The matrix described by rvec and tvec takes points in model space
        // (the space where the LEDs are defined, which is in mm away from an
        // implied origin) into a coordinate system where the center is at the
        // camera's origin, with X to the right, Y down, and Z in the direction
        // that the camera is facing (but still in the original units of mm):
        //  |Xc|   |r11 r12 r13 t1| |Xm|
        //  |Yc| = |r21 r22 r23 t2|*|Ym|
        //  |Zc|   |r31 r32 r33 t3| |Zm|
        //                          |1 |
        //  That is, it rotates into the camera coordinate system and then adds
        // the translation, which is in the camera coordinate system.
        //  This is the transformation we want, since it reports the sensor's
        // position and orientation in camera space, except that we want to
        // convert
        // the units into meters and the orientation into a Quaternion.
        //  NOTE: This is a right-handed coordinate system with X pointing
        // towards the right from the camera center of projection, Y pointing
        // down, and Z pointing along the camera viewing direction, if the input
        // points are not inverted.

        if (tvec.at<double>(2) < 0) {
// -z means the wrong side of the pinhole
/// @todo find out why OpenCV is now returning these values sometimes
#if 0
            std::cout << "On the wrong side of the looking glass:" << tvec
                      << std::endl;
#endif
            // So, we invert translation, and apply 180 rotation (to rotation)
            // about z.
            tvec *= -1;
            /// Make a rotation matrix for 180 about z
            auto rotMat = cv::Affine3d::Mat3::eye();
            rotMat(0, 0) = -1;
            rotMat(1, 1) = -1;
            /// Apply the transform and get the rvec out again.
            rvec = cv::Mat(cv::Affine3d(rvec) * (cv::Affine3d(rotMat)).rvec());
        }
        outXlate = cvToVector3d(tvec);
        outQuat = cvRotVecToQuat(rvec);
        return true;
    }

    /// Variance in Meters^2
    static const double InitialPositionStateError = 0.;
    /// Variance in Radians^2
    static const double InitialOrientationStateError = 0.5;
    static const double InitialVelocityStateError = 0.;
    static const double InitialAngVelStateError = 0.;
    static const double InitialStateError[] = {
        InitialPositionStateError,    InitialPositionStateError,
        InitialPositionStateError,    InitialOrientationStateError,
        InitialOrientationStateError, InitialOrientationStateError,
        InitialVelocityStateError,    InitialVelocityStateError,
        InitialVelocityStateError,    InitialAngVelStateError,
        InitialAngVelStateError,      InitialAngVelStateError};
    bool RANSACPoseEstimator::operator()(EstimatorInOutParams const &p,
                                         LedPtrList const &leds) {
        Eigen::Vector3d xlate;
        Eigen::Quaterniond quat;
        /// Call the main pose estimation to get the vector and quat.
        {
            auto ret = (*this)(p.camParams, leds, p.beacons, p.beaconDebug,
                               xlate, quat);
            if (!ret) {
                return false;
            }
        }
        /// OK, so if we're here, estimation succeeded and we have valid data in
        /// xlate and quat.
        p.state.position() = xlate;
        p.state.setQuaternion(quat);
/// Zero things we can't measure.
#if 1
        p.state.incrementalOrientation() = Eigen::Vector3d::Zero();
        p.state.velocity() = Eigen::Vector3d::Zero();
        p.state.angularVelocity() = Eigen::Vector3d::Zero();
#endif
        using StateVec = kalman::types::DimVector<BodyState>;
        using StateSquareMatrix = kalman::types::DimSquareMatrix<BodyState>;

        StateSquareMatrix covariance = StateVec(InitialStateError).asDiagonal();
        /// @todo Copy the existing angular velocity error covariance
        /*
        covariance.bottomRightCorner<3, 3>() =
            p.state.errorCovariance().bottomRightCorner<3, 3>();
            */
        p.state.setErrorCovariance(covariance);
        return true;
    }

} // namespace vbtracker
} // namespace osvr
<|endoftext|>


<commit_before>/*
 *
 *  This currently a work in progress
 *
 */

#include "mex.h"
#include "hdf5.h"
#include <string>
#include "enum_map_info.h"

#if defined(_MSC_VER) || defined(__BORLANDC__)
typedef __int64 int64;
typedef __int32 int32;
typedef __int16 int16;
typedef __int8 int8;
typedef unsigned __int64 uint64;
typedef unsigned __int32 uint32;
typedef unsigned __int16 uint16;
typedef unsigned __int8 uint8;
#else /* LINUX + LCC, CAUTION: not tested by the author */
typedef long long int int64;
typedef long int int32;
typedef short int16;
typedef char int8;
typedef unsigned long long int uint64;
typedef unsigned long int uint32;
typedef unsigned short uint16;
typedef unsigned char uint8;
#endif

#define H5FILE_NAME "Attributes.h5";

void setInt8Output(mxArray *plhs[],int index, int8 value)
{

    plhs[index] = mxCreateNumericMatrix(1,1,mxINT8_CLASS,mxREAL);
    int8 *p_value;
    p_value    = (int8 *)mxGetData(plhs[index]);
    p_value[0] = value;
}

void setDoubleOutput(mxArray *plhs[],int index, double value)
{

    //Sets a single scalar value at the output location specified.
    //
    //  Inputs:
    //  -------
    //  plhs  : The output data
    //  index : Which index to set the data for (0 based)
    //  value : The value to assign to that particular output
    //
    //  Example: (In Matlab)
    // [a,b] = testFunction()
    //
    //  Let's say we are trying to set b to 5
    //
    //  setDoubleOutput(plhs,1,5)

    //NOTE: We've hardcoded the output to be a scalar
    plhs[index] = mxCreateNumericMatrix(1,1,mxDOUBLE_CLASS,mxREAL);
    double *p_value;
    p_value    = (double *)mxGetData(plhs[index]);
    p_value[0] = value;
}

//=========================================================================
void mexFunction( int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
    if (nrhs < 1 || !mxIsChar(prhs[0]))
        mexErrMsgTxt("First parameter must be the command (a string)");

    char cmd[100];
	mxGetString(prhs[0],cmd,100);

    if (!strcmp("H5Fcreate",cmd)){
       //See h5f
       //
       //file_id = hdf5_mex('H5Fcreate',file_name);
       //
       //TODO: Eventually this will be changed to allow
       //all inputs, not just the file
       hid_t file;
       file = H5Fcreate("Attributes.h5", H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);

    } else if (!strcmp("H5Fopen",cmd)){

	} else if (!strcmp("H5Fclose",cmd)){

    } else if (!strcmp("H5Zfilter_avail",cmd)){
      //http://www.hdfgroup.org/HDF5/doc/RM/RM_H5Z.html#Compression-FilterAvail
      //
      //    status = hdf5_mex('H5Zfilter_avail',filter_id);

      htri_t status;
      //TODO: Remove hard coded filter values
      // - this requires enumeration support
      status = H5Zfilter_avail(H5Z_FILTER_SZIP);

      setInt8Output(plhs,0,(int8)status);
    } else if (!strcmp("H5Zget_filter_info",cmd)){
      //http://www.hdfgroup.org/HDF5/doc/RM/RM_H5Z.html#Compression-GetFilterInfo
      //H5Z_filter_t filter, unsigned int *filter_config
      //
      //    [err,config] = hdf5_mex('H5Zget_filter_info',filter_id);

      unsigned int filter_config = 0;
      herr_t function_error;
      //TODO: Replace hardcoded value
      function_error = H5Zget_filter_info(H5Z_FILTER_SZIP,&filter_config);
      setInt8Output(plhs,0,(int8)function_error);
      setInt8Output(plhs,0,(int8)filter_config);
    } else if (!strcmp("H5MLget_constant_value",cmd)){
        char *enum_string;
        enum_string = mxArrayToString(prhs[1]);

        //TODO: I'm still working on this. Once this code works I'll need
        //to create a function that will support translation of a given
        //input with the following features
        // - handling string or number
        // - handling invalid options - i.e. bad strings - throw error
        // - handling all number types
        mexPrintf("Test: %d\n",ENUM_MAP.at("H5D_CHUNKED"));

        //map<string, int>::iterator p;
        //p = ENUM_MAP.find("H5D_CHUNKED");
        //mexPrintf("Test: %d",ENUM_MAP["H5D_CHUNKED"]);
        //setDoubleOutput(plhs,0,(double)(ENUM_MAP[enum_string]));
    }

    //herr_t  ret;

    //ret  = H5Fclose(file);
}<commit_msg>working on enumerations<commit_after>/*
 *
 *  This currently a work in progress
 *
 */

#include "mex.h"
#include "hdf5.h"
#include <string>
#include "enum_map_info.h"

#if defined(_MSC_VER) || defined(__BORLANDC__)
typedef __int64 int64;
typedef __int32 int32;
typedef __int16 int16;
typedef __int8 int8;
typedef unsigned __int64 uint64;
typedef unsigned __int32 uint32;
typedef unsigned __int16 uint16;
typedef unsigned __int8 uint8;
#else /* LINUX + LCC, CAUTION: not tested by the author */
typedef long long int int64;
typedef long int int32;
typedef short int16;
typedef char int8;
typedef unsigned long long int uint64;
typedef unsigned long int uint32;
typedef unsigned short uint16;
typedef unsigned char uint8;
#endif

#define H5FILE_NAME "Attributes.h5";

void setInt8Output(mxArray *plhs[],int index, int8 value)
{

    plhs[index] = mxCreateNumericMatrix(1,1,mxINT8_CLASS,mxREAL);
    int8 *p_value;
    p_value    = (int8 *)mxGetData(plhs[index]);
    p_value[0] = value;
}

void setDoubleOutput(mxArray *plhs[],int index, double value)
{

    //Sets a single scalar value at the output location specified.
    //
    //  Inputs:
    //  -------
    //  plhs  : The output data
    //  index : Which index to set the data for (0 based)
    //  value : The value to assign to that particular output
    //
    //  Example: (In Matlab)
    // [a,b] = testFunction()
    //
    //  Let's say we are trying to set b to 5
    //
    //  setDoubleOutput(plhs,1,5)

    //NOTE: We've hardcoded the output to be a scalar
    plhs[index] = mxCreateNumericMatrix(1,1,mxDOUBLE_CLASS,mxREAL);
    double *p_value;
    p_value    = (double *)mxGetData(plhs[index]);
    p_value[0] = value;
}

//=========================================================================
void mexFunction( int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
    if (nrhs < 1 || !mxIsChar(prhs[0]))
        mexErrMsgTxt("First parameter must be the command (a string)");

    char cmd[100];
	mxGetString(prhs[0],cmd,100);

    if (!strcmp("H5Fcreate",cmd)){
       //See h5f
       //
       //file_id = hdf5_mex('H5Fcreate',file_name);
       //
       //TODO: Eventually this will be changed to allow
       //all inputs, not just the file
       hid_t file;
       file = H5Fcreate("Attributes.h5", H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);

    } else if (!strcmp("H5Fopen",cmd)){

	} else if (!strcmp("H5Fclose",cmd)){

    } else if (!strcmp("H5Zfilter_avail",cmd)){
      //http://www.hdfgroup.org/HDF5/doc/RM/RM_H5Z.html#Compression-FilterAvail
      //
      //    status = hdf5_mex('H5Zfilter_avail',filter_id);

      htri_t status;
      //TODO: Remove hard coded filter values
      // - this requires enumeration support
      status = H5Zfilter_avail(H5Z_FILTER_SZIP);

      setInt8Output(plhs,0,(int8)status);
    } else if (!strcmp("H5Zget_filter_info",cmd)){
      //http://www.hdfgroup.org/HDF5/doc/RM/RM_H5Z.html#Compression-GetFilterInfo
      //H5Z_filter_t filter, unsigned int *filter_config
      //
      //    [err,config] = hdf5_mex('H5Zget_filter_info',filter_id);

      unsigned int filter_config = 0;
      herr_t function_error;
      //TODO: Replace hardcoded value
      function_error = H5Zget_filter_info(H5Z_FILTER_SZIP,&filter_config);
      setInt8Output(plhs,0,(int8)function_error);
      setInt8Output(plhs,0,(int8)filter_config);
    } else if (!strcmp("H5MLget_constant_value",cmd)){
        char *enum_string;
        enum_string = mxArrayToString(prhs[1]);

        //TODO: I'm still working on this. Once this code works I'll need
        //to create a function that will support translation of a given
        //input with the following features
        // - handling string or number
        // - handling invalid options - i.e. bad strings - throw error
        // - handling all number types
        //mexPrintf("Test: %d\n",ENUM_MAP.at("H5D_CHUNKED"));

        //map<string, int>::iterator p;
        //p = ENUM_MAP.find("H5D_CHUNKED");
        //mexPrintf("Test: %d",ENUM_MAP["H5D_CHUNKED"]);
        setDoubleOutput(plhs,0,(double)(ENUM_MAP[enum_string]));
    }

    //herr_t  ret;

    //ret  = H5Fclose(file);
}<|endoftext|>


<commit_before>#include "rendering/birdseyecamera.hpp"

#include <SFML/Graphics/RenderTarget.hpp>

namespace qrw
{

BirdsEyeCamera::BirdsEyeCamera()
	: center_(0, 0)
{
}

BirdsEyeCamera::~BirdsEyeCamera()
{
}

void BirdsEyeCamera::applyTo(sf::RenderTarget& renderTarget)
{
	sf::View view = renderTarget.getView();
	view.setCenter(center_);
	renderTarget.setView(view);
}

bool BirdsEyeCamera::handleEvent(const IEvent& event)
{
	return false;
}

} // namespace qrw
<commit_msg>Added basic scrolling capabilities<commit_after>#include "rendering/birdseyecamera.hpp"

#include <SFML/Graphics/RenderTarget.hpp>

#include "eventsystem/inputevents.hpp"

namespace qrw
{

BirdsEyeCamera::BirdsEyeCamera()
	: center_(0, 0)
{
}

BirdsEyeCamera::~BirdsEyeCamera()
{
}

void BirdsEyeCamera::applyTo(sf::RenderTarget& renderTarget)
{
	sf::View view = renderTarget.getView();
	view.setCenter(center_);
	renderTarget.setView(view);
}

bool BirdsEyeCamera::handleEvent(const IEvent& event)
{
	if(event.getName() == KeyPressedEvent::name)
	{
		KeyPressedEvent::Key key = static_cast<const KeyPressedEvent&>(event).key;

		sf::Vector2f movementDirection(0, 0);

		switch(key)
		{
		case KeyPressedEvent::Key::Right: //ft
		case KeyPressedEvent::Key::D:
			++movementDirection.x;
			break;
		case KeyPressedEvent::Key::Left: // ft
		case KeyPressedEvent::Key::A:
			--movementDirection.x;
			break;
		case KeyPressedEvent::Key::Up: //ft
		case KeyPressedEvent::Key::W:
			--movementDirection.y;
			break;
		case KeyPressedEvent::Key::Down: // ft
		case KeyPressedEvent::Key::S:
			++movementDirection.y;
			break;
		default:
			break;
		}

		center_ += movementDirection * 10.0f;
	}
	return false;
}

} // namespace qrw
<|endoftext|>