<commit_before>/*
	This file is part of cpp-ethereum.

	cpp-ethereum 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.

	cpp-ethereum 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 cpp-ethereum.  If not, see <http://www.gnu.org/licenses/>.
*/
/** @file FileIo.cpp
 * @author Arkadiy Paronyan arkadiy@ethdev.com
 * @date 2015
 * Ethereum IDE client.
 */

#include <QFileSystemWatcher>
#include <QDebug>
#include <QDesktopServices>
#include <QMimeDatabase>
#include <QDirIterator>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QTextStream>
#include <QUrl>
#include <json/json.h>
#include <libdevcrypto/CryptoPP.h>
#include <libdevcrypto/Common.h>
#include <libdevcore/RLP.h>
#include <libdevcore/SHA3.h>
#include "FileIo.h"

using namespace dev;
using namespace dev::crypto;
using namespace dev::mix;

FileIo::FileIo(): m_watcher(new QFileSystemWatcher(this))
{
	connect(m_watcher, &QFileSystemWatcher::fileChanged, this, &FileIo::fileChanged);
}

void FileIo::openFileBrowser(QString const& _dir)
{
	QDesktopServices::openUrl(QUrl(_dir));
}

QString FileIo::pathFromUrl(QString const& _url)
{
	QUrl url(_url);
	QString path(url.path());
	if (url.scheme() == "qrc")
		path = ":" + path;
#ifdef WIN32
	if (url.scheme() == "file")
	{
		if (path.startsWith("/"))
			path = path.right(path.length() - 1);
		if (!url.host().isEmpty())
			path = url.host() + ":/" + path;
	}
#endif
	return path;
}

void FileIo::makeDir(QString const& _url)
{
	QDir dirPath(pathFromUrl(_url));
	if (dirPath.exists())
		dirPath.removeRecursively();
	dirPath.mkpath(dirPath.path());
}

void FileIo::deleteDir(QString const& _url)
{
	QDir dirPath(pathFromUrl(_url));
	if (dirPath.exists())
		dirPath.removeRecursively();
}

QString FileIo::readFile(QString const& _url)
{
	QFile file(pathFromUrl(_url));
	if (file.open(QIODevice::ReadOnly | QIODevice::Text))
	{
		QTextStream stream(&file);
		QString data = stream.readAll();
		return data;
	}
	else
		error(tr("Error reading file %1").arg(_url));
	return QString();
}

void FileIo::writeFile(QString const& _url, QString const& _data)
{
	QString path = pathFromUrl(_url);
	m_watcher->removePath(path);
	QFile file(path);
	if (file.open(QIODevice::WriteOnly | QIODevice::Text))
	{
		QTextStream stream(&file);
		stream << _data;
	}
	else
		error(tr("Error writing file %1").arg(_url));
	file.close();
	m_watcher->addPath(path);
}

void FileIo::copyFile(QString const& _sourceUrl, QString const& _destUrl)
{
	if (QUrl(_sourceUrl).scheme() == "qrc")
	{
		writeFile(_destUrl, readFile(_sourceUrl));
		return;
	}

	if (!QFile::copy(pathFromUrl(_sourceUrl), pathFromUrl(_destUrl)))
		error(tr("Error copying file %1 to %2").arg(_sourceUrl).arg(_destUrl));
}

QString FileIo::getHomePath() const
{
	return QDir::homePath();
}

void FileIo::moveFile(QString const& _sourceUrl, QString const& _destUrl)
{
	if (!QFile::rename(pathFromUrl(_sourceUrl), pathFromUrl(_destUrl)))
		error(tr("Error moving file %1 to %2").arg(_sourceUrl).arg(_destUrl));
}

bool FileIo::fileExists(QString const& _url)
{
	QFile file(pathFromUrl(_url));
	return file.exists();
}

QStringList FileIo::makePackage(QString const& _deploymentFolder)
{
	Json::Value manifest;
	Json::Value entries(Json::arrayValue);

	QDir deployDir = QDir(pathFromUrl(_deploymentFolder));
	dev::RLPStream rlpStr;
	int k = 1;
	std::vector<bytes> files;
	QMimeDatabase mimeDb;
	for (auto item: deployDir.entryInfoList(QDir::Files))
	{
		QFile qFile(item.filePath());
		if (qFile.open(QIODevice::ReadOnly))
		{
			k++;
			QFileInfo fileInfo = QFileInfo(qFile.fileName());
			Json::Value jsonValue;
			std::string path = fileInfo.fileName() == "index.html" ? "/" : fileInfo.fileName().toStdString();
			jsonValue["path"] = path; //TODO: Manage relative sub folder
			jsonValue["file"] = "/" + fileInfo.fileName().toStdString();
			jsonValue["contentType"] = mimeDb.mimeTypeForFile(qFile.fileName()).name().toStdString();
			QByteArray a = qFile.readAll();
			bytes data = bytes(a.begin(), a.end());
			files.push_back(data);
			jsonValue["hash"] = toHex(dev::sha3(data).ref());
			entries.append(jsonValue);
		}
		qFile.close();
	}
	rlpStr.appendList(k);

	manifest["entries"] = entries;
	std::stringstream jsonStr;
	jsonStr << manifest;
	QByteArray b =  QString::fromStdString(jsonStr.str()).toUtf8();
	rlpStr.append(bytesConstRef((const unsigned char*)b.data(), b.size()));

	for (unsigned int k = 0; k < files.size(); k++)
		rlpStr.append(files.at(k));

	bytes dapp = rlpStr.out();
	dev::h256 dappHash = dev::sha3(dapp);
	//encrypt
	KeyPair key((Secret(dappHash)));
	Secp256k1PP *enc = Secp256k1PP::get();
	enc->encrypt(key.pub(), dapp);

	QUrl url(_deploymentFolder + "package.dapp");
	QFile compressed(url.path());
	QByteArray qFileBytes((char*)dapp.data(), static_cast<int>(dapp.size()));
	if (compressed.open(QIODevice::WriteOnly | QIODevice::Truncate))
	{
		compressed.write(qFileBytes);
		compressed.flush();
	}
	else
		error(tr("Error creating package.dapp"));
	compressed.close();
	QStringList ret;
	ret.append(QString::fromStdString(toHex(dappHash.ref())));
	ret.append(qFileBytes.toBase64());
	ret.append(url.toString());
	return ret;
}

void FileIo::watchFileChanged(QString const& _path)
{
	m_watcher->addPath(pathFromUrl(_path));
}

void FileIo::stopWatching(QString const& _path)
{
	m_watcher->removePath(pathFromUrl(_path));
}

void FileIo::deleteFile(QString const& _path)
{
	QFile file(pathFromUrl(_path));
	file.remove();
}

QUrl FileIo::pathFolder(QString const& _path)
{
	QFileInfo info(_path);
	if (info.exists() && info.isDir())
		return QUrl::fromLocalFile(_path);
	return QUrl::fromLocalFile(QFileInfo(_path).absolutePath());
}
<commit_msg>Cleanup.<commit_after>/*
	This file is part of cpp-ethereum.

	cpp-ethereum 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.

	cpp-ethereum 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 cpp-ethereum.  If not, see <http://www.gnu.org/licenses/>.
*/
/** @file FileIo.cpp
 * @author Arkadiy Paronyan arkadiy@ethdev.com
 * @date 2015
 * Ethereum IDE client.
 */

#include <QFileSystemWatcher>
#include <QDebug>
#include <QDesktopServices>
#include <QMimeDatabase>
#include <QDirIterator>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QTextStream>
#include <QUrl>
#include <json/json.h>
#include <libdevcrypto/CryptoPP.h>
#include <libdevcrypto/Common.h>
#include <libdevcore/RLP.h>
#include <libdevcore/SHA3.h>
#include "FileIo.h"

using namespace dev;
using namespace dev::crypto;
using namespace dev::mix;

FileIo::FileIo(): m_watcher(new QFileSystemWatcher(this))
{
	connect(m_watcher, &QFileSystemWatcher::fileChanged, this, &FileIo::fileChanged);
}

void FileIo::openFileBrowser(QString const& _dir)
{
	QDesktopServices::openUrl(QUrl(_dir));
}

QString FileIo::pathFromUrl(QString const& _url)
{
	QUrl url(_url);
	QString path(url.path());
	if (url.scheme() == "qrc")
		path = ":" + path;
#ifdef WIN32
	if (url.scheme() == "file")
	{
		if (path.startsWith("/"))
			path = path.right(path.length() - 1);
		if (!url.host().isEmpty())
			path = url.host() + ":/" + path;
	}
#endif
	return path;
}

void FileIo::makeDir(QString const& _url)
{
	QDir dirPath(pathFromUrl(_url));
	if (dirPath.exists())
		dirPath.removeRecursively();
	dirPath.mkpath(dirPath.path());
}

void FileIo::deleteDir(QString const& _url)
{
	QDir dirPath(pathFromUrl(_url));
	if (dirPath.exists())
		dirPath.removeRecursively();
}

QString FileIo::readFile(QString const& _url)
{
	QFile file(pathFromUrl(_url));
	if (file.open(QIODevice::ReadOnly | QIODevice::Text))
	{
		QTextStream stream(&file);
		QString data = stream.readAll();
		return data;
	}
	else
		error(tr("Error reading file %1").arg(_url));
	return QString();
}

void FileIo::writeFile(QString const& _url, QString const& _data)
{
	QString path = pathFromUrl(_url);
	m_watcher->removePath(path);
	QFile file(path);
	if (file.open(QIODevice::WriteOnly | QIODevice::Text))
	{
		QTextStream stream(&file);
		stream << _data;
	}
	else
		error(tr("Error writing file %1").arg(_url));
	file.close();
	m_watcher->addPath(path);
}

void FileIo::copyFile(QString const& _sourceUrl, QString const& _destUrl)
{
	if (QUrl(_sourceUrl).scheme() == "qrc")
	{
		writeFile(_destUrl, readFile(_sourceUrl));
		return;
	}

	if (!QFile::copy(pathFromUrl(_sourceUrl), pathFromUrl(_destUrl)))
		error(tr("Error copying file %1 to %2").arg(_sourceUrl).arg(_destUrl));
}

QString FileIo::getHomePath() const
{
	return QDir::homePath();
}

void FileIo::moveFile(QString const& _sourceUrl, QString const& _destUrl)
{
	if (!QFile::rename(pathFromUrl(_sourceUrl), pathFromUrl(_destUrl)))
		error(tr("Error moving file %1 to %2").arg(_sourceUrl).arg(_destUrl));
}

bool FileIo::fileExists(QString const& _url)
{
	QFile file(pathFromUrl(_url));
	return file.exists();
}

QStringList FileIo::makePackage(QString const& _deploymentFolder)
{
	Json::Value manifest;
	Json::Value entries(Json::arrayValue);

	QDir deployDir = QDir(pathFromUrl(_deploymentFolder));
	dev::RLPStream rlpStr;
	int k = 1;
	std::vector<bytes> files;
	QMimeDatabase mimeDb;
	for (auto item: deployDir.entryInfoList(QDir::Files))
	{
		QFile qFile(item.filePath());
		if (qFile.open(QIODevice::ReadOnly))
		{
			k++;
			QFileInfo fileInfo = QFileInfo(qFile.fileName());
			Json::Value jsonValue;
			std::string path = fileInfo.fileName() == "index.html" ? "/" : fileInfo.fileName().toStdString();
			jsonValue["path"] = path; //TODO: Manage relative sub folder
			jsonValue["file"] = "/" + fileInfo.fileName().toStdString();
			jsonValue["contentType"] = mimeDb.mimeTypeForFile(qFile.fileName()).name().toStdString();
			QByteArray a = qFile.readAll();
			bytes data = bytes(a.begin(), a.end());
			files.push_back(data);
			jsonValue["hash"] = toHex(dev::sha3(data).ref());
			entries.append(jsonValue);
		}
		qFile.close();
	}
	rlpStr.appendList(k);

	manifest["entries"] = entries;
	std::stringstream jsonStr;
	jsonStr << manifest;
	QByteArray b =  QString::fromStdString(jsonStr.str()).toUtf8();
	rlpStr.append(bytesConstRef((const unsigned char*)b.data(), b.size()));

	for (unsigned int k = 0; k < files.size(); k++)
		rlpStr.append(files.at(k));

	bytes dapp = rlpStr.out();
	dev::h256 dappHash = dev::sha3(dapp);
	//encrypt
	KeyPair key((Secret(dappHash)));
	Secp256k1PP::get()->encrypt(key.pub(), dapp);

	QUrl url(_deploymentFolder + "package.dapp");
	QFile compressed(url.path());
	QByteArray qFileBytes((char*)dapp.data(), static_cast<int>(dapp.size()));
	if (compressed.open(QIODevice::WriteOnly | QIODevice::Truncate))
	{
		compressed.write(qFileBytes);
		compressed.flush();
	}
	else
		error(tr("Error creating package.dapp"));
	compressed.close();
	QStringList ret;
	ret.append(QString::fromStdString(toHex(dappHash.ref())));
	ret.append(qFileBytes.toBase64());
	ret.append(url.toString());
	return ret;
}

void FileIo::watchFileChanged(QString const& _path)
{
	m_watcher->addPath(pathFromUrl(_path));
}

void FileIo::stopWatching(QString const& _path)
{
	m_watcher->removePath(pathFromUrl(_path));
}

void FileIo::deleteFile(QString const& _path)
{
	QFile file(pathFromUrl(_path));
	file.remove();
}

QUrl FileIo::pathFolder(QString const& _path)
{
	QFileInfo info(_path);
	if (info.exists() && info.isDir())
		return QUrl::fromLocalFile(_path);
	return QUrl::fromLocalFile(QFileInfo(_path).absolutePath());
}
<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2014, Paul Beckingham, Federico Hernandez.
//
// 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.
//
// http://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////

#include <iostream> // TODO Remove
#include <cmake.h>
#include <Context.h>
#include <Eval.h>
#include <Variant.h>
#include <Dates.h>
#include <Filter.h>
#include <i18n.h>
#include <util.h>

extern Context context;

////////////////////////////////////////////////////////////////////////////////
// Const iterator that can be derefenced into a Task by domSource.
static Task dummy;
static Task& contextTask = dummy;

////////////////////////////////////////////////////////////////////////////////
static bool domSource (const std::string& identifier, Variant& value)
{
  std::string stringValue = context.dom.get (identifier, contextTask);
  if (stringValue != identifier)
  {
    value = Variant (stringValue);
    return true;
  }

  return false;
}

////////////////////////////////////////////////////////////////////////////////
Filter::Filter ()
: _startCount (0)
, _endCount (0)
{
}

////////////////////////////////////////////////////////////////////////////////
Filter::~Filter ()
{
}

////////////////////////////////////////////////////////////////////////////////
// Take an input set of tasks and filter into a subset.
void Filter::subset (const std::vector <Task>& input, std::vector <Task>& output)
{
  context.timer_filter.start ();

  if (context.config.getBoolean ("debug"))
  {
    Tree* t = context.parser.tree ();
    if (t)
      context.debug (t->dump ());
  }

  std::string filterExpr = context.parser.getFilterExpression ();
  context.debug ("\033[1;37;42mFILTER\033[0m " + filterExpr);

  if (filterExpr.length ())
  {
    Eval eval;
    eval.addSource (namedDates);
    eval.addSource (domSource);

    // Debug output from Eval during compilation is useful.  During evaluation
    // it is mostly noise.
    eval.debug (context.config.getBoolean ("debug"));
    eval.compileExpression (filterExpr);
    eval.debug (false);

    std::vector <Task>::const_iterator task;
    for (task = input.begin (); task != input.end (); ++task)
    {
      // Set up context for any DOM references.
      contextTask = *task;

      Variant var;
      eval.evaluateCompiledExpression (var);
      if (var.get_bool ())
        output.push_back (*task);
    }
  }
  else
    output = input;

  context.timer_filter.stop ();
}

////////////////////////////////////////////////////////////////////////////////
// Take the set of all tasks and filter into a subset.
void Filter::subset (std::vector <Task>& output)
{
  context.timer_filter.start ();

  if (context.config.getBoolean ("debug"))
  {
    Tree* t = context.parser.tree ();
    if (t)
      context.debug (t->dump ());
  }

  std::string filterExpr = context.parser.getFilterExpression ();
  context.debug ("\033[1;37;42mFILTER\033[0m " + filterExpr);

  if (filterExpr.length ())
  {
    context.timer_filter.stop ();
    const std::vector <Task>& pending = context.tdb2.pending.get_tasks ();
    context.timer_filter.start ();

    Eval eval;
    eval.addSource (namedDates);
    eval.addSource (domSource);

    // Debug output from Eval during compilation is useful.  During evaluation
    // it is mostly noise.
    eval.debug (context.config.getBoolean ("debug"));
    eval.compileExpression (filterExpr);
    eval.debug (false);

    output.clear ();
    std::vector <Task>::const_iterator task;

    for (task = pending.begin (); task != pending.end (); ++task)
    {
      // Set up context for any DOM references.
      contextTask = *task;

      Variant var;
      eval.evaluateCompiledExpression (var);
      if (var.get_bool ())
        output.push_back (*task);
    }

    if (! pendingOnly ())
    {
      context.timer_filter.stop ();
      const std::vector <Task>& completed = context.tdb2.completed.get_tasks (); // TODO Optional
      context.timer_filter.start ();

      for (task = completed.begin (); task != completed.end (); ++task)
      {
        // Set up context for any DOM references.
        contextTask = *task;

        Variant var;
        eval.evaluateCompiledExpression (var);
        if (var.get_bool ())
          output.push_back (*task);
      }
    }
  }
  else
  {
    safety ();

    context.timer_filter.stop ();
    const std::vector <Task>& pending   = context.tdb2.pending.get_tasks ();
    const std::vector <Task>& completed = context.tdb2.completed.get_tasks ();
    context.timer_filter.start ();

    std::vector <Task>::const_iterator task;
    for (task = pending.begin (); task != pending.end (); ++task)
      output.push_back (*task);

    for (task = completed.begin (); task != completed.end (); ++task)
      output.push_back (*task);
  }

  context.timer_filter.stop ();
}

////////////////////////////////////////////////////////////////////////////////
// If the filter contains the restriction "status:pending", as the first filter
// term, then completed.data does not need to be loaded.
bool Filter::pendingOnly ()
{
  Tree* tree = context.parser.tree ();

  // If the filter starts with "status:pending", the completed.data does not
  // need to be accessed..
  if (tree->_branches.size () > 0 &&
      tree->_branches[0]->attribute ("name")  == "status" &&
      tree->_branches[0]->attribute ("value") == "pending")
  {
    context.debug ("Filter::pendingOnly - skipping completed.data (status:pending first)");
    return true;
  }

  // Shortcut: If the filter contains no 'or' or 'xor' operators, IDs and no UUIDs.
  int countId   = 0;
  int countUUID = 0;
  int countOr   = 0;
  int countXor  = 0;

  std::vector <Tree*>::iterator i;
  for (i = tree->_branches.begin (); i != tree->_branches.end (); ++i)
  {
    if ((*i)->hasTag ("OP"))
    {
      if ((*i)->attribute ("canonical") == "or")  ++countOr;
      if ((*i)->attribute ("canonical") == "xor") ++countXor;
    }
    else if ((*i)->hasTag ("ID"))   ++countId;
    else if ((*i)->hasTag ("UUID")) ++countUUID;
  }

  if (countOr   == 0 &&
      countXor  == 0 &&
      countUUID == 0 &&
      countId    > 0)
  {
    context.debug ("Filter::pendingOnly - skipping completed.data (IDs, no OR, no XOR, no UUID)");
    return true;
  }

  return false;
}

////////////////////////////////////////////////////////////////////////////////
// Disaster avoidance mechanism. If a WRITECMD has no filter, then it can cause
// all tasks to be modified. This is usually not intended.
void Filter::safety ()
{
  Tree* tree = context.parser.tree ();
  std::vector <Tree*>::iterator i;
  for (i = tree->_branches.begin (); i != tree->_branches.end (); ++i)
  {
    if ((*i)->hasTag ("WRITECMD"))
    {
      if (context.parser.getFilterExpression () == "")
      {
        // If user is willing to be asked, this can be avoided.
        if (context.config.getBoolean ("confirmation") &&
            confirm (STRING_TASK_SAFETY_VALVE))
          return;

        // Sounds the alarm.
        throw std::string (STRING_TASK_SAFETY_FAIL);
      }

      // Nothing to see here. Move along.
      return;
    }
  }
}

////////////////////////////////////////////////////////////////////////////////
<commit_msg>Filter<commit_after>////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2014, Paul Beckingham, Federico Hernandez.
//
// 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.
//
// http://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////

#include <iostream> // TODO Remove
#include <cmake.h>
#include <Context.h>
#include <Eval.h>
#include <Variant.h>
#include <Dates.h>
#include <Filter.h>
#include <i18n.h>
#include <text.h>
#include <util.h>

extern Context context;

////////////////////////////////////////////////////////////////////////////////
// Const iterator that can be derefenced into a Task by domSource.
static Task dummy;
static Task& contextTask = dummy;

////////////////////////////////////////////////////////////////////////////////
static bool domSource (const std::string& identifier, Variant& value)
{
  std::string stringValue = context.dom.get (identifier, contextTask);
  if (stringValue != identifier)
  {
    value = Variant (stringValue);
    return true;
  }

  return false;
}

////////////////////////////////////////////////////////////////////////////////
Filter::Filter ()
: _startCount (0)
, _endCount (0)
{
}

////////////////////////////////////////////////////////////////////////////////
Filter::~Filter ()
{
}

////////////////////////////////////////////////////////////////////////////////
// Take an input set of tasks and filter into a subset.
void Filter::subset (const std::vector <Task>& input, std::vector <Task>& output)
{
  context.timer_filter.start ();

  _startCount = (int) input.size ();

  if (context.config.getBoolean ("debug"))
  {
    Tree* t = context.parser.tree ();
    if (t)
      context.debug (t->dump ());
  }

  std::string filterExpr = context.parser.getFilterExpression ();
  context.debug ("\033[1;37;42mFILTER\033[0m " + filterExpr);

  if (filterExpr.length ())
  {
    Eval eval;
    eval.addSource (namedDates);
    eval.addSource (domSource);

    // Debug output from Eval during compilation is useful.  During evaluation
    // it is mostly noise.
    eval.debug (context.config.getBoolean ("debug"));
    eval.compileExpression (filterExpr);
    eval.debug (false);

    std::vector <Task>::const_iterator task;
    for (task = input.begin (); task != input.end (); ++task)
    {
      // Set up context for any DOM references.
      contextTask = *task;

      Variant var;
      eval.evaluateCompiledExpression (var);
      if (var.get_bool ())
        output.push_back (*task);
    }
  }
  else
    output = input;

  _endCount = (int) output.size ();
  context.debug (format ("Filtered {1} tasks --> {2} tasks", _startCount, _endCount));
  context.timer_filter.stop ();
}

////////////////////////////////////////////////////////////////////////////////
// Take the set of all tasks and filter into a subset.
void Filter::subset (std::vector <Task>& output)
{
  context.timer_filter.start ();

  if (context.config.getBoolean ("debug"))
  {
    Tree* t = context.parser.tree ();
    if (t)
      context.debug (t->dump ());
  }

  std::string filterExpr = context.parser.getFilterExpression ();
  context.debug ("\033[1;37;42mFILTER\033[0m " + filterExpr);

  if (filterExpr.length ())
  {
    context.timer_filter.stop ();
    const std::vector <Task>& pending = context.tdb2.pending.get_tasks ();
    context.timer_filter.start ();
    _startCount = (int) pending.size ();

    Eval eval;
    eval.addSource (namedDates);
    eval.addSource (domSource);

    // Debug output from Eval during compilation is useful.  During evaluation
    // it is mostly noise.
    eval.debug (context.config.getBoolean ("debug"));
    eval.compileExpression (filterExpr);
    eval.debug (false);

    output.clear ();
    std::vector <Task>::const_iterator task;

    for (task = pending.begin (); task != pending.end (); ++task)
    {
      // Set up context for any DOM references.
      contextTask = *task;

      Variant var;
      eval.evaluateCompiledExpression (var);
      if (var.get_bool ())
        output.push_back (*task);
    }

    if (! pendingOnly ())
    {
      context.timer_filter.stop ();
      const std::vector <Task>& completed = context.tdb2.completed.get_tasks (); // TODO Optional
      context.timer_filter.start ();
      _startCount += (int) completed.size ();

      for (task = completed.begin (); task != completed.end (); ++task)
      {
        // Set up context for any DOM references.
        contextTask = *task;

        Variant var;
        eval.evaluateCompiledExpression (var);
        if (var.get_bool ())
          output.push_back (*task);
      }
    }
  }
  else
  {
    safety ();

    context.timer_filter.stop ();
    const std::vector <Task>& pending   = context.tdb2.pending.get_tasks ();
    const std::vector <Task>& completed = context.tdb2.completed.get_tasks ();
    context.timer_filter.start ();

    std::vector <Task>::const_iterator task;
    for (task = pending.begin (); task != pending.end (); ++task)
      output.push_back (*task);

    for (task = completed.begin (); task != completed.end (); ++task)
      output.push_back (*task);
  }

  _endCount = (int) output.size ();
  context.debug (format ("Filtered {1} tasks --> {2} tasks", _startCount, _endCount));
  context.timer_filter.stop ();
}

////////////////////////////////////////////////////////////////////////////////
// If the filter contains the restriction "status:pending", as the first filter
// term, then completed.data does not need to be loaded.
bool Filter::pendingOnly ()
{
  Tree* tree = context.parser.tree ();

  // If the filter starts with "status:pending", the completed.data does not
  // need to be accessed..
  if (tree->_branches.size () > 0 &&
      tree->_branches[0]->attribute ("name")  == "status" &&
      tree->_branches[0]->attribute ("value") == "pending")
  {
    context.debug ("Filter::pendingOnly - skipping completed.data (status:pending first)");
    return true;
  }

  // Shortcut: If the filter contains no 'or' or 'xor' operators, IDs and no UUIDs.
  int countId   = 0;
  int countUUID = 0;
  int countOr   = 0;
  int countXor  = 0;

  std::vector <Tree*>::iterator i;
  for (i = tree->_branches.begin (); i != tree->_branches.end (); ++i)
  {
    if ((*i)->hasTag ("OP"))
    {
      if ((*i)->attribute ("canonical") == "or")  ++countOr;
      if ((*i)->attribute ("canonical") == "xor") ++countXor;
    }
    else if ((*i)->hasTag ("ID"))   ++countId;
    else if ((*i)->hasTag ("UUID")) ++countUUID;
  }

  if (countOr   == 0 &&
      countXor  == 0 &&
      countUUID == 0 &&
      countId    > 0)
  {
    context.debug ("Filter::pendingOnly - skipping completed.data (IDs, no OR, no XOR, no UUID)");
    return true;
  }

  return false;
}

////////////////////////////////////////////////////////////////////////////////
// Disaster avoidance mechanism. If a WRITECMD has no filter, then it can cause
// all tasks to be modified. This is usually not intended.
void Filter::safety ()
{
  Tree* tree = context.parser.tree ();
  std::vector <Tree*>::iterator i;
  for (i = tree->_branches.begin (); i != tree->_branches.end (); ++i)
  {
    if ((*i)->hasTag ("WRITECMD"))
    {
      if (context.parser.getFilterExpression () == "")
      {
        // If user is willing to be asked, this can be avoided.
        if (context.config.getBoolean ("confirmation") &&
            confirm (STRING_TASK_SAFETY_VALVE))
          return;

        // Sounds the alarm.
        throw std::string (STRING_TASK_SAFETY_FAIL);
      }

      // Nothing to see here. Move along.
      return;
    }
  }
}

////////////////////////////////////////////////////////////////////////////////
<|endoftext|>


<commit_before>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2014.  All rights reserved
 */
#include    "../StroikaPreComp.h"

#include    "ProgressMonitor.h"

using   namespace   Stroika::Foundation;
using   namespace   Stroika::Foundation::Execution;

/*
 ********************************************************************************
 **************************** ProgressMonitor ***********************************
 ********************************************************************************
 */
ProgressMonitor::ProgressMonitor ()
    : fRep_ (new IRep_ ())
{
}

ProgressMonitor::ProgressMonitor (Thread workThread)
    : fRep_ (new IRep_ ())
{
}

ProgressMonitor::~ProgressMonitor ()
{
}

void    ProgressMonitor::AddOnProgressCallback (const ChangedCallbackType& progressChangedCallback)
{
    RequireNotNull (fRep_);
    fRep_->fCallbacks_.Append (progressChangedCallback);
}

void    ProgressMonitor::Cancel ()
{
    RequireNotNull (fRep_);
    fRep_->fCanceled_ = true;
    if (fRep_->fWorkThread_.GetStatus () != Thread::Status::eNull) {
        fRep_->fWorkThread_.Abort ();
    }
}

/*
 ********************************************************************************
 *************************** ProgressMonitor::Updater ***************************
 ********************************************************************************
 */
void    ProgressMonitor::Updater::CallNotifyProgress_ () const
{
    RequireNotNull (fRep_);
    for (ChangedCallbackType f : fRep_->fCallbacks_) {
        return f (ProgressMonitor (fRep_));
    }
}
<commit_msg>mistake in ProgressMonitor - premature return<commit_after>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2014.  All rights reserved
 */
#include    "../StroikaPreComp.h"

#include    "ProgressMonitor.h"

using   namespace   Stroika::Foundation;
using   namespace   Stroika::Foundation::Execution;

/*
 ********************************************************************************
 **************************** ProgressMonitor ***********************************
 ********************************************************************************
 */
ProgressMonitor::ProgressMonitor ()
    : fRep_ (new IRep_ ())
{
}

ProgressMonitor::ProgressMonitor (Thread workThread)
    : fRep_ (new IRep_ ())
{
}

ProgressMonitor::~ProgressMonitor ()
{
}

void    ProgressMonitor::AddOnProgressCallback (const ChangedCallbackType& progressChangedCallback)
{
    RequireNotNull (fRep_);
    fRep_->fCallbacks_.Append (progressChangedCallback);
}

void    ProgressMonitor::Cancel ()
{
    RequireNotNull (fRep_);
    fRep_->fCanceled_ = true;
    if (fRep_->fWorkThread_.GetStatus () != Thread::Status::eNull) {
        fRep_->fWorkThread_.Abort ();
    }
}

/*
 ********************************************************************************
 *************************** ProgressMonitor::Updater ***************************
 ********************************************************************************
 */
void    ProgressMonitor::Updater::CallNotifyProgress_ () const
{
    RequireNotNull (fRep_);
    for (ChangedCallbackType f : fRep_->fCallbacks_) {
        f (ProgressMonitor (fRep_));
    }
}
<|endoftext|>


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

The Medical Imaging Interaction Toolkit (MITK)

Copyright (c) German Cancer Research Center,
Division of Medical and Biological Informatics.
All rights reserved.

This software is distributed WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.

See LICENSE.txt or http://www.mitk.org for details.

===================================================================*/
#include "itkGibbsTrackingFilter.h"

// MITK
#include <itkOrientationDistributionFunction.h>
#include <itkDiffusionQballGeneralizedFaImageFilter.h>
#include <mitkStandardFileLocations.h>
#include <mitkFiberBuilder.h>
#include <mitkMetropolisHastingsSampler.h>
#include <mitkEnergyComputer.h>
#include <itkTensorImageToQBallImageFilter.h>

// ITK
#include <itkImageDuplicator.h>
#include <itkResampleImageFilter.h>

// MISC
#include <fstream>
#include <QFile>
#include <tinyxml.h>
#include <math.h>

namespace itk{

template< class ItkQBallImageType >
GibbsTrackingFilter< ItkQBallImageType >::GibbsTrackingFilter():
    m_StartTemperature(0.1),
    m_EndTemperature(0.001),
    m_Iterations(500000),
    m_ParticleWeight(0),
    m_ParticleWidth(0),
    m_ParticleLength(0),
    m_ConnectionPotential(10),
    m_InexBalance(0),
    m_ParticlePotential(0.2),
    m_MinFiberLength(10),
    m_AbortTracking(false),
    m_NumConnections(0),
    m_NumParticles(0),
    m_NumAcceptedFibers(0),
    m_CurrentStep(0),
    m_BuildFibers(false),
    m_Steps(10),
    m_ProposalAcceptance(0),
    m_CurvatureThreshold(0.7),
    m_DuplicateImage(true),
    m_RandomSeed(-1),
    m_ParameterFile(""),
    m_LutPath("")
{

}

template< class ItkQBallImageType >
GibbsTrackingFilter< ItkQBallImageType >::~GibbsTrackingFilter()
{

}

// fill output fiber bundle datastructure
template< class ItkQBallImageType >
typename GibbsTrackingFilter< ItkQBallImageType >::FiberPolyDataType GibbsTrackingFilter< ItkQBallImageType >::GetFiberBundle()
{
    if (!m_AbortTracking)
    {
        m_BuildFibers = true;
        while (m_BuildFibers){}
    }

    return m_FiberPolyData;
}

template< class ItkQBallImageType >
bool
GibbsTrackingFilter< ItkQBallImageType >
::EstimateParticleWeight()
{
    MITK_INFO << "GibbsTrackingFilter: estimating particle weight";
    typedef itk::DiffusionQballGeneralizedFaImageFilter<float,float,QBALL_ODFSIZE> GfaFilterType;
    GfaFilterType::Pointer gfaFilter = GfaFilterType::New();
    gfaFilter->SetInput(m_QBallImage);
    gfaFilter->SetComputationMethod(GfaFilterType::GFA_STANDARD);
    gfaFilter->Update();
    ItkFloatImageType::Pointer gfaImage = gfaFilter->GetOutput();

    float samplingStart = 1.0;
    float samplingStop = 0.66;

    // GFA iterator
    typedef ImageRegionIterator< ItkFloatImageType > GfaIteratorType;
    GfaIteratorType gfaIt(gfaImage, gfaImage->GetLargestPossibleRegion() );

    // Mask iterator
    typedef ImageRegionConstIterator< ItkFloatImageType > MaskIteratorType;
    MaskIteratorType mit(m_MaskImage, m_MaskImage->GetLargestPossibleRegion() );

    // Input iterator
    typedef ImageRegionConstIterator< ItkQBallImageType > InputIteratorType;
    InputIteratorType it(m_QBallImage, m_QBallImage->GetLargestPossibleRegion() );

    float upper = 0;
    int count = 0;
    for(float thr=samplingStart; thr>samplingStop; thr-=0.01)
    {
        it.GoToBegin();
        mit.GoToBegin();
        gfaIt.GoToBegin();

        while( !gfaIt.IsAtEnd() )
        {
            if(gfaIt.Get()>thr && mit.Get()>0)
            {
                itk::OrientationDistributionFunction<float, QBALL_ODFSIZE> odf(it.Get().GetDataPointer());
                upper += odf.GetMaxValue()-odf.GetMeanValue();
                ++count;
            }
            ++it;
            ++mit;
            ++gfaIt;
        }
    }

    if (count>0)
        upper /= count;
    else
        return false;

    m_ParticleWeight = upper/6;
    return true;
}

// perform global tracking
template< class ItkQBallImageType >
void GibbsTrackingFilter< ItkQBallImageType >::GenerateData()
{
    // check if input is qball or tensor image and generate qball if necessary
    if (m_QBallImage.IsNull() && m_TensorImage.IsNotNull())
    {
        TensorImageToQBallImageFilter<float,float>::Pointer filter = TensorImageToQBallImageFilter<float,float>::New();
        filter->SetInput( m_TensorImage );
        filter->Update();
        m_QBallImage = filter->GetOutput();
    }

    // generate local working copy of QBall image (if not disabled)
    if (m_DuplicateImage)
    {
        typedef itk::ImageDuplicator< ItkQBallImageType > DuplicateFilterType;
        typename DuplicateFilterType::Pointer duplicator = DuplicateFilterType::New();
        duplicator->SetInputImage( m_QBallImage );
        duplicator->Update();
        m_QBallImage = duplicator->GetOutput();
    }

    // perform mean subtraction on odfs
    typedef ImageRegionIterator< ItkQBallImageType > InputIteratorType;
    InputIteratorType it(m_QBallImage, m_QBallImage->GetLargestPossibleRegion() );
    it.GoToBegin();
    while (!it.IsAtEnd())
    {
        itk::OrientationDistributionFunction<float, QBALL_ODFSIZE> odf(it.Get().GetDataPointer());
        float mean = odf.GetMeanValue();
        odf -= mean;
        it.Set(odf.GetDataPointer());
        ++it;
    }

    // check if mask image is given if it needs resampling
    PrepareMaskImage();

    // load parameter file
    LoadParameters(m_ParameterFile);

    // prepare parameters
    float minSpacing;
    if(m_QBallImage->GetSpacing()[0]<m_QBallImage->GetSpacing()[1] && m_QBallImage->GetSpacing()[0]<m_QBallImage->GetSpacing()[2])
        minSpacing = m_QBallImage->GetSpacing()[0];
    else if (m_QBallImage->GetSpacing()[1] < m_QBallImage->GetSpacing()[2])
        minSpacing = m_QBallImage->GetSpacing()[1];
    else
        minSpacing = m_QBallImage->GetSpacing()[2];

    if(m_ParticleLength == 0)
        m_ParticleLength = 1.5*minSpacing;
    if(m_ParticleWidth == 0)
        m_ParticleWidth = 0.5*minSpacing;
    if(m_ParticleWeight == 0)
        if (!EstimateParticleWeight())
        {
            MITK_INFO << "GibbsTrackingFilter: could not estimate particle weight. using default value.";
            m_ParticleWeight = 0.0001;
        }
    float alpha = log(m_EndTemperature/m_StartTemperature);
    m_Steps = m_Iterations/10000;
    if (m_Steps<10)
        m_Steps = 10;
    if (m_Steps>m_Iterations)
    {
        MITK_INFO << "GibbsTrackingFilter: not enough iterations!";
        m_AbortTracking = true;
    }
    if (m_CurvatureThreshold < mitk::eps)
        m_CurvatureThreshold = 0;
    unsigned long singleIts = (unsigned long)((1.0*m_Iterations) / (1.0*m_Steps));

    // seed random generators
    Statistics::MersenneTwisterRandomVariateGenerator::Pointer randGen = Statistics::MersenneTwisterRandomVariateGenerator::New();
    if (m_RandomSeed>-1)
        randGen->SetSeed(m_RandomSeed);

    // load sphere interpolator to evaluate the ODFs
    SphereInterpolator* interpolator = new SphereInterpolator(m_LutPath);

    // initialize the actual tracking components (ParticleGrid, Metropolis Hastings Sampler and Energy Computer)
    ParticleGrid* particleGrid = new ParticleGrid(m_MaskImage, m_ParticleLength);

    EnergyComputer* encomp = new EnergyComputer(m_QBallImage, m_MaskImage, particleGrid, interpolator, randGen);
    encomp->SetParameters(m_ParticleWeight,m_ParticleWidth,m_ConnectionPotential*m_ParticleLength*m_ParticleLength,m_CurvatureThreshold,m_InexBalance,m_ParticlePotential);

    MetropolisHastingsSampler* sampler = new MetropolisHastingsSampler(particleGrid, encomp, randGen, m_CurvatureThreshold);

    MITK_INFO << "----------------------------------------";
    MITK_INFO << "Iterations: " << m_Iterations;
    MITK_INFO << "Steps: " << m_Steps;
    MITK_INFO << "Particle length: " << m_ParticleLength;
    MITK_INFO << "Particle width: " << m_ParticleWidth;
    MITK_INFO << "Particle weight: " << m_ParticleWeight;
    MITK_INFO << "Start temperature: " << m_StartTemperature;
    MITK_INFO << "End temperature: " << m_EndTemperature;
    MITK_INFO << "In/Ex balance: " << m_InexBalance;
    MITK_INFO << "Min. fiber length: " << m_MinFiberLength;
    MITK_INFO << "Curvature threshold: " << m_CurvatureThreshold;
    MITK_INFO << "Random seed: " << m_RandomSeed;
    MITK_INFO << "----------------------------------------";

    // main loop
    m_NumAcceptedFibers = 0;
    unsigned long counter = 1;
    for( m_CurrentStep = 1; m_CurrentStep <= m_Steps; m_CurrentStep++ )
    {
        // update temperatur for simulated annealing process
        float temperature = m_StartTemperature * exp(alpha*(((1.0)*m_CurrentStep)/((1.0)*m_Steps)));
        sampler->SetTemperature(temperature);

        for (unsigned long i=0; i<singleIts; i++)
        {
            if (m_AbortTracking)
                break;

            sampler->MakeProposal();

            if (m_BuildFibers || (i==singleIts-1 && m_CurrentStep==m_Steps))
            {
                m_ProposalAcceptance = (float)sampler->GetNumAcceptedProposals()/counter;
                m_NumParticles = particleGrid->m_NumParticles;
                m_NumConnections = particleGrid->m_NumConnections;

                FiberBuilder fiberBuilder(particleGrid, m_MaskImage);
                m_FiberPolyData = fiberBuilder.iterate(m_MinFiberLength);
                m_NumAcceptedFibers = m_FiberPolyData->GetNumberOfLines();
                m_BuildFibers = false;
            }
            counter++;
        }

        m_ProposalAcceptance = (float)sampler->GetNumAcceptedProposals()/counter;
        m_NumParticles = particleGrid->m_NumParticles;
        m_NumConnections = particleGrid->m_NumConnections;

        MITK_INFO << "GibbsTrackingFilter: proposal acceptance: " << 100*m_ProposalAcceptance << "%";
        MITK_INFO << "GibbsTrackingFilter: particles: " << m_NumParticles;
        MITK_INFO << "GibbsTrackingFilter: connections: " << m_NumConnections;
        MITK_INFO << "GibbsTrackingFilter: progress: " << 100*(float)m_CurrentStep/m_Steps << "%";
        MITK_INFO << "----------------------------------------";

        if (m_AbortTracking)
            break;
    }

    delete sampler;
    delete encomp;
    delete interpolator;
    delete particleGrid;
    m_AbortTracking = true;
    m_BuildFibers = false;

    MITK_INFO << "GibbsTrackingFilter: done generate data";
}

template< class ItkQBallImageType >
void GibbsTrackingFilter< ItkQBallImageType >::PrepareMaskImage()
{
    if(m_MaskImage.IsNull())
    {
        MITK_INFO << "GibbsTrackingFilter: generating default mask image";
        m_MaskImage = ItkFloatImageType::New();
        m_MaskImage->SetSpacing( m_QBallImage->GetSpacing() );
        m_MaskImage->SetOrigin( m_QBallImage->GetOrigin() );
        m_MaskImage->SetDirection( m_QBallImage->GetDirection() );
        m_MaskImage->SetRegions( m_QBallImage->GetLargestPossibleRegion() );
        m_MaskImage->Allocate();
        m_MaskImage->FillBuffer(1.0);
    }
    else if ( m_MaskImage->GetLargestPossibleRegion().GetSize()[0]!=m_QBallImage->GetLargestPossibleRegion().GetSize()[0] ||
         m_MaskImage->GetLargestPossibleRegion().GetSize()[1]!=m_QBallImage->GetLargestPossibleRegion().GetSize()[1] ||
         m_MaskImage->GetLargestPossibleRegion().GetSize()[2]!=m_QBallImage->GetLargestPossibleRegion().GetSize()[2] ||
         m_MaskImage->GetSpacing()[0]!=m_QBallImage->GetSpacing()[0] ||
         m_MaskImage->GetSpacing()[1]!=m_QBallImage->GetSpacing()[1] ||
         m_MaskImage->GetSpacing()[2]!=m_QBallImage->GetSpacing()[2] )
    {
        MITK_INFO << "GibbsTrackingFilter: resampling mask image";
        typedef itk::ResampleImageFilter< ItkFloatImageType, ItkFloatImageType, float > ResamplerType;
        ResamplerType::Pointer resampler = ResamplerType::New();
        resampler->SetOutputSpacing( m_QBallImage->GetSpacing() );
        resampler->SetOutputOrigin( m_QBallImage->GetOrigin() );
        resampler->SetOutputDirection( m_QBallImage->GetDirection() );
        resampler->SetSize( m_QBallImage->GetLargestPossibleRegion().GetSize() );

        resampler->SetInput( m_MaskImage );
        resampler->SetDefaultPixelValue(1.0);
        resampler->Update();
        m_MaskImage = resampler->GetOutput();
        MITK_INFO << "GibbsTrackingFilter: resampling finished";
    }
}

// load current tracking paramters from xml file (.gtp)
template< class ItkQBallImageType >
bool GibbsTrackingFilter< ItkQBallImageType >::LoadParameters(std::string filename)
{
    m_AbortTracking = true;
    try
    {
        if( filename.length()==0 )
        {
            m_AbortTracking = false;
            return true;
        }

        MITK_INFO << "GibbsTrackingFilter: loading parameter file " << filename;

        TiXmlDocument doc( filename );
        doc.LoadFile();

        TiXmlHandle hDoc(&doc);
        TiXmlElement* pElem;
        TiXmlHandle hRoot(0);

        pElem = hDoc.FirstChildElement().Element();
        hRoot = TiXmlHandle(pElem);
        pElem = hRoot.FirstChildElement("parameter_set").Element();

        QString iterations(pElem->Attribute("iterations"));
        m_Iterations = iterations.toULong();

        QString particleLength(pElem->Attribute("particle_length"));
        m_ParticleLength = particleLength.toFloat();

        QString particleWidth(pElem->Attribute("particle_width"));
        m_ParticleWidth = particleWidth.toFloat();

        QString partWeight(pElem->Attribute("particle_weight"));
        m_ParticleWeight = partWeight.toFloat();

        QString startTemp(pElem->Attribute("temp_start"));
        m_StartTemperature = startTemp.toFloat();

        QString endTemp(pElem->Attribute("temp_end"));
        m_EndTemperature = endTemp.toFloat();

        QString inExBalance(pElem->Attribute("inexbalance"));
        m_InexBalance = inExBalance.toFloat();

        QString fiberLength(pElem->Attribute("fiber_length"));
        m_MinFiberLength = fiberLength.toFloat();

        QString curvThres(pElem->Attribute("curvature_threshold"));
        m_CurvatureThreshold = cos(curvThres.toFloat()*M_PI/180);
        m_AbortTracking = false;
        MITK_INFO << "GibbsTrackingFilter: parameter file loaded successfully";
        return true;
    }
    catch(...)
    {
        MITK_INFO << "GibbsTrackingFilter: could not load parameter file";
        return false;
    }
}

}

<commit_msg>only copy image if it is qball<commit_after>/*===================================================================

The Medical Imaging Interaction Toolkit (MITK)

Copyright (c) German Cancer Research Center,
Division of Medical and Biological Informatics.
All rights reserved.

This software is distributed WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.

See LICENSE.txt or http://www.mitk.org for details.

===================================================================*/
#include "itkGibbsTrackingFilter.h"

// MITK
#include <itkOrientationDistributionFunction.h>
#include <itkDiffusionQballGeneralizedFaImageFilter.h>
#include <mitkStandardFileLocations.h>
#include <mitkFiberBuilder.h>
#include <mitkMetropolisHastingsSampler.h>
#include <mitkEnergyComputer.h>
#include <itkTensorImageToQBallImageFilter.h>

// ITK
#include <itkImageDuplicator.h>
#include <itkResampleImageFilter.h>

// MISC
#include <fstream>
#include <QFile>
#include <tinyxml.h>
#include <math.h>

namespace itk{

template< class ItkQBallImageType >
GibbsTrackingFilter< ItkQBallImageType >::GibbsTrackingFilter():
    m_StartTemperature(0.1),
    m_EndTemperature(0.001),
    m_Iterations(500000),
    m_ParticleWeight(0),
    m_ParticleWidth(0),
    m_ParticleLength(0),
    m_ConnectionPotential(10),
    m_InexBalance(0),
    m_ParticlePotential(0.2),
    m_MinFiberLength(10),
    m_AbortTracking(false),
    m_NumConnections(0),
    m_NumParticles(0),
    m_NumAcceptedFibers(0),
    m_CurrentStep(0),
    m_BuildFibers(false),
    m_Steps(10),
    m_ProposalAcceptance(0),
    m_CurvatureThreshold(0.7),
    m_DuplicateImage(true),
    m_RandomSeed(-1),
    m_ParameterFile(""),
    m_LutPath("")
{

}

template< class ItkQBallImageType >
GibbsTrackingFilter< ItkQBallImageType >::~GibbsTrackingFilter()
{

}

// fill output fiber bundle datastructure
template< class ItkQBallImageType >
typename GibbsTrackingFilter< ItkQBallImageType >::FiberPolyDataType GibbsTrackingFilter< ItkQBallImageType >::GetFiberBundle()
{
    if (!m_AbortTracking)
    {
        m_BuildFibers = true;
        while (m_BuildFibers){}
    }

    return m_FiberPolyData;
}

template< class ItkQBallImageType >
bool
GibbsTrackingFilter< ItkQBallImageType >
::EstimateParticleWeight()
{
    MITK_INFO << "GibbsTrackingFilter: estimating particle weight";
    typedef itk::DiffusionQballGeneralizedFaImageFilter<float,float,QBALL_ODFSIZE> GfaFilterType;
    GfaFilterType::Pointer gfaFilter = GfaFilterType::New();
    gfaFilter->SetInput(m_QBallImage);
    gfaFilter->SetComputationMethod(GfaFilterType::GFA_STANDARD);
    gfaFilter->Update();
    ItkFloatImageType::Pointer gfaImage = gfaFilter->GetOutput();

    float samplingStart = 1.0;
    float samplingStop = 0.66;

    // GFA iterator
    typedef ImageRegionIterator< ItkFloatImageType > GfaIteratorType;
    GfaIteratorType gfaIt(gfaImage, gfaImage->GetLargestPossibleRegion() );

    // Mask iterator
    typedef ImageRegionConstIterator< ItkFloatImageType > MaskIteratorType;
    MaskIteratorType mit(m_MaskImage, m_MaskImage->GetLargestPossibleRegion() );

    // Input iterator
    typedef ImageRegionConstIterator< ItkQBallImageType > InputIteratorType;
    InputIteratorType it(m_QBallImage, m_QBallImage->GetLargestPossibleRegion() );

    float upper = 0;
    int count = 0;
    for(float thr=samplingStart; thr>samplingStop; thr-=0.01)
    {
        it.GoToBegin();
        mit.GoToBegin();
        gfaIt.GoToBegin();

        while( !gfaIt.IsAtEnd() )
        {
            if(gfaIt.Get()>thr && mit.Get()>0)
            {
                itk::OrientationDistributionFunction<float, QBALL_ODFSIZE> odf(it.Get().GetDataPointer());
                upper += odf.GetMaxValue()-odf.GetMeanValue();
                ++count;
            }
            ++it;
            ++mit;
            ++gfaIt;
        }
    }

    if (count>0)
        upper /= count;
    else
        return false;

    m_ParticleWeight = upper/6;
    return true;
}

// perform global tracking
template< class ItkQBallImageType >
void GibbsTrackingFilter< ItkQBallImageType >::GenerateData()
{
    // check if input is qball or tensor image and generate qball if necessary
    if (m_QBallImage.IsNull() && m_TensorImage.IsNotNull())
    {
        TensorImageToQBallImageFilter<float,float>::Pointer filter = TensorImageToQBallImageFilter<float,float>::New();
        filter->SetInput( m_TensorImage );
        filter->Update();
        m_QBallImage = filter->GetOutput();
    }
    else if (m_DuplicateImage) // generate local working copy of QBall image (if not disabled)
    {
        typedef itk::ImageDuplicator< ItkQBallImageType > DuplicateFilterType;
        typename DuplicateFilterType::Pointer duplicator = DuplicateFilterType::New();
        duplicator->SetInputImage( m_QBallImage );
        duplicator->Update();
        m_QBallImage = duplicator->GetOutput();
    }

    // perform mean subtraction on odfs
    typedef ImageRegionIterator< ItkQBallImageType > InputIteratorType;
    InputIteratorType it(m_QBallImage, m_QBallImage->GetLargestPossibleRegion() );
    it.GoToBegin();
    while (!it.IsAtEnd())
    {
        itk::OrientationDistributionFunction<float, QBALL_ODFSIZE> odf(it.Get().GetDataPointer());
        float mean = odf.GetMeanValue();
        odf -= mean;
        it.Set(odf.GetDataPointer());
        ++it;
    }

    // check if mask image is given if it needs resampling
    PrepareMaskImage();

    // load parameter file
    LoadParameters(m_ParameterFile);

    // prepare parameters
    float minSpacing;
    if(m_QBallImage->GetSpacing()[0]<m_QBallImage->GetSpacing()[1] && m_QBallImage->GetSpacing()[0]<m_QBallImage->GetSpacing()[2])
        minSpacing = m_QBallImage->GetSpacing()[0];
    else if (m_QBallImage->GetSpacing()[1] < m_QBallImage->GetSpacing()[2])
        minSpacing = m_QBallImage->GetSpacing()[1];
    else
        minSpacing = m_QBallImage->GetSpacing()[2];

    if(m_ParticleLength == 0)
        m_ParticleLength = 1.5*minSpacing;
    if(m_ParticleWidth == 0)
        m_ParticleWidth = 0.5*minSpacing;
    if(m_ParticleWeight == 0)
        if (!EstimateParticleWeight())
        {
            MITK_INFO << "GibbsTrackingFilter: could not estimate particle weight. using default value.";
            m_ParticleWeight = 0.0001;
        }
    float alpha = log(m_EndTemperature/m_StartTemperature);
    m_Steps = m_Iterations/10000;
    if (m_Steps<10)
        m_Steps = 10;
    if (m_Steps>m_Iterations)
    {
        MITK_INFO << "GibbsTrackingFilter: not enough iterations!";
        m_AbortTracking = true;
    }
    if (m_CurvatureThreshold < mitk::eps)
        m_CurvatureThreshold = 0;
    unsigned long singleIts = (unsigned long)((1.0*m_Iterations) / (1.0*m_Steps));

    // seed random generators
    Statistics::MersenneTwisterRandomVariateGenerator::Pointer randGen = Statistics::MersenneTwisterRandomVariateGenerator::New();
    if (m_RandomSeed>-1)
        randGen->SetSeed(m_RandomSeed);

    // load sphere interpolator to evaluate the ODFs
    SphereInterpolator* interpolator = new SphereInterpolator(m_LutPath);

    // initialize the actual tracking components (ParticleGrid, Metropolis Hastings Sampler and Energy Computer)
    ParticleGrid* particleGrid = new ParticleGrid(m_MaskImage, m_ParticleLength);

    EnergyComputer* encomp = new EnergyComputer(m_QBallImage, m_MaskImage, particleGrid, interpolator, randGen);
    encomp->SetParameters(m_ParticleWeight,m_ParticleWidth,m_ConnectionPotential*m_ParticleLength*m_ParticleLength,m_CurvatureThreshold,m_InexBalance,m_ParticlePotential);

    MetropolisHastingsSampler* sampler = new MetropolisHastingsSampler(particleGrid, encomp, randGen, m_CurvatureThreshold);

    MITK_INFO << "----------------------------------------";
    MITK_INFO << "Iterations: " << m_Iterations;
    MITK_INFO << "Steps: " << m_Steps;
    MITK_INFO << "Particle length: " << m_ParticleLength;
    MITK_INFO << "Particle width: " << m_ParticleWidth;
    MITK_INFO << "Particle weight: " << m_ParticleWeight;
    MITK_INFO << "Start temperature: " << m_StartTemperature;
    MITK_INFO << "End temperature: " << m_EndTemperature;
    MITK_INFO << "In/Ex balance: " << m_InexBalance;
    MITK_INFO << "Min. fiber length: " << m_MinFiberLength;
    MITK_INFO << "Curvature threshold: " << m_CurvatureThreshold;
    MITK_INFO << "Random seed: " << m_RandomSeed;
    MITK_INFO << "----------------------------------------";

    // main loop
    m_NumAcceptedFibers = 0;
    unsigned long counter = 1;
    for( m_CurrentStep = 1; m_CurrentStep <= m_Steps; m_CurrentStep++ )
    {
        // update temperatur for simulated annealing process
        float temperature = m_StartTemperature * exp(alpha*(((1.0)*m_CurrentStep)/((1.0)*m_Steps)));
        sampler->SetTemperature(temperature);

        for (unsigned long i=0; i<singleIts; i++)
        {
            if (m_AbortTracking)
                break;

            sampler->MakeProposal();

            if (m_BuildFibers || (i==singleIts-1 && m_CurrentStep==m_Steps))
            {
                m_ProposalAcceptance = (float)sampler->GetNumAcceptedProposals()/counter;
                m_NumParticles = particleGrid->m_NumParticles;
                m_NumConnections = particleGrid->m_NumConnections;

                FiberBuilder fiberBuilder(particleGrid, m_MaskImage);
                m_FiberPolyData = fiberBuilder.iterate(m_MinFiberLength);
                m_NumAcceptedFibers = m_FiberPolyData->GetNumberOfLines();
                m_BuildFibers = false;
            }
            counter++;
        }

        m_ProposalAcceptance = (float)sampler->GetNumAcceptedProposals()/counter;
        m_NumParticles = particleGrid->m_NumParticles;
        m_NumConnections = particleGrid->m_NumConnections;

        MITK_INFO << "GibbsTrackingFilter: proposal acceptance: " << 100*m_ProposalAcceptance << "%";
        MITK_INFO << "GibbsTrackingFilter: particles: " << m_NumParticles;
        MITK_INFO << "GibbsTrackingFilter: connections: " << m_NumConnections;
        MITK_INFO << "GibbsTrackingFilter: progress: " << 100*(float)m_CurrentStep/m_Steps << "%";
        MITK_INFO << "GibbsTrackingFilter: cell overflows: " << particleGrid->m_NumCellOverflows;
        MITK_INFO << "----------------------------------------";

        if (m_AbortTracking)
            break;
    }

    delete sampler;
    delete encomp;
    delete interpolator;
    delete particleGrid;
    m_AbortTracking = true;
    m_BuildFibers = false;

    MITK_INFO << "GibbsTrackingFilter: done generate data";
}

template< class ItkQBallImageType >
void GibbsTrackingFilter< ItkQBallImageType >::PrepareMaskImage()
{
    if(m_MaskImage.IsNull())
    {
        MITK_INFO << "GibbsTrackingFilter: generating default mask image";
        m_MaskImage = ItkFloatImageType::New();
        m_MaskImage->SetSpacing( m_QBallImage->GetSpacing() );
        m_MaskImage->SetOrigin( m_QBallImage->GetOrigin() );
        m_MaskImage->SetDirection( m_QBallImage->GetDirection() );
        m_MaskImage->SetRegions( m_QBallImage->GetLargestPossibleRegion() );
        m_MaskImage->Allocate();
        m_MaskImage->FillBuffer(1.0);
    }
    else if ( m_MaskImage->GetLargestPossibleRegion().GetSize()[0]!=m_QBallImage->GetLargestPossibleRegion().GetSize()[0] ||
         m_MaskImage->GetLargestPossibleRegion().GetSize()[1]!=m_QBallImage->GetLargestPossibleRegion().GetSize()[1] ||
         m_MaskImage->GetLargestPossibleRegion().GetSize()[2]!=m_QBallImage->GetLargestPossibleRegion().GetSize()[2] ||
         m_MaskImage->GetSpacing()[0]!=m_QBallImage->GetSpacing()[0] ||
         m_MaskImage->GetSpacing()[1]!=m_QBallImage->GetSpacing()[1] ||
         m_MaskImage->GetSpacing()[2]!=m_QBallImage->GetSpacing()[2] )
    {
        MITK_INFO << "GibbsTrackingFilter: resampling mask image";
        typedef itk::ResampleImageFilter< ItkFloatImageType, ItkFloatImageType, float > ResamplerType;
        ResamplerType::Pointer resampler = ResamplerType::New();
        resampler->SetOutputSpacing( m_QBallImage->GetSpacing() );
        resampler->SetOutputOrigin( m_QBallImage->GetOrigin() );
        resampler->SetOutputDirection( m_QBallImage->GetDirection() );
        resampler->SetSize( m_QBallImage->GetLargestPossibleRegion().GetSize() );

        resampler->SetInput( m_MaskImage );
        resampler->SetDefaultPixelValue(1.0);
        resampler->Update();
        m_MaskImage = resampler->GetOutput();
        MITK_INFO << "GibbsTrackingFilter: resampling finished";
    }
}

// load current tracking paramters from xml file (.gtp)
template< class ItkQBallImageType >
bool GibbsTrackingFilter< ItkQBallImageType >::LoadParameters(std::string filename)
{
    m_AbortTracking = true;
    try
    {
        if( filename.length()==0 )
        {
            m_AbortTracking = false;
            return true;
        }

        MITK_INFO << "GibbsTrackingFilter: loading parameter file " << filename;

        TiXmlDocument doc( filename );
        doc.LoadFile();

        TiXmlHandle hDoc(&doc);
        TiXmlElement* pElem;
        TiXmlHandle hRoot(0);

        pElem = hDoc.FirstChildElement().Element();
        hRoot = TiXmlHandle(pElem);
        pElem = hRoot.FirstChildElement("parameter_set").Element();

        QString iterations(pElem->Attribute("iterations"));
        m_Iterations = iterations.toULong();

        QString particleLength(pElem->Attribute("particle_length"));
        m_ParticleLength = particleLength.toFloat();

        QString particleWidth(pElem->Attribute("particle_width"));
        m_ParticleWidth = particleWidth.toFloat();

        QString partWeight(pElem->Attribute("particle_weight"));
        m_ParticleWeight = partWeight.toFloat();

        QString startTemp(pElem->Attribute("temp_start"));
        m_StartTemperature = startTemp.toFloat();

        QString endTemp(pElem->Attribute("temp_end"));
        m_EndTemperature = endTemp.toFloat();

        QString inExBalance(pElem->Attribute("inexbalance"));
        m_InexBalance = inExBalance.toFloat();

        QString fiberLength(pElem->Attribute("fiber_length"));
        m_MinFiberLength = fiberLength.toFloat();

        QString curvThres(pElem->Attribute("curvature_threshold"));
        m_CurvatureThreshold = cos(curvThres.toFloat()*M_PI/180);
        m_AbortTracking = false;
        MITK_INFO << "GibbsTrackingFilter: parameter file loaded successfully";
        return true;
    }
    catch(...)
    {
        MITK_INFO << "GibbsTrackingFilter: could not load parameter file";
        return false;
    }
}

}

<|endoftext|>


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

The Medical Imaging Interaction Toolkit (MITK)

Copyright (c) German Cancer Research Center,
Division of Medical and Biological Informatics.
All rights reserved.

This software is distributed WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.

See LICENSE.txt or http://www.mitk.org for details.

===================================================================*/

#include "mitkNavigationDataToIGTLMessageFilter.h"

#include "igtlQuaternionTrackingDataMessage.h"
#include "igtlTrackingDataMessage.h"
#include "igtlTransformMessage.h"
#include "igtlPositionMessage.h"

#include <mitkInteractionConst.h>
#include <itksys/SystemTools.hxx>

mitk::NavigationDataToIGTLMessageFilter::NavigationDataToIGTLMessageFilter()
{
  mitk::IGTLMessage::Pointer output = mitk::IGTLMessage::New();
  this->SetNumberOfRequiredOutputs(1);
  this->SetNthOutput(0, output.GetPointer());

  this->SetNumberOfRequiredInputs(1);

  //  m_OperationMode = Mode3D;
  m_CurrentTimeStep = 0;
  //  m_RingBufferSize = 50; //the default ring buffer size
  //  m_NumberForMean = 100;
}

mitk::NavigationDataToIGTLMessageFilter::~NavigationDataToIGTLMessageFilter()
{
}

void mitk::NavigationDataToIGTLMessageFilter::GenerateData()
{
  switch (m_OperationMode)
  {
  case ModeSendQTDataMsg:
    this->GenerateDataModeSendQTDataMsg();
    break;
  case ModeSendTDataMsg:
    this->GenerateDataModeSendTDataMsg();
    break;
  case ModeSendQTransMsg:
    this->GenerateDataModeSendQTransMsg();
    break;
  case ModeSendTransMsg:
    this->GenerateDataModeSendTransMsg();
    break;
  default:
    break;
  }
  igtl::MessageBase::Pointer curMessage = this->GetOutput()->GetMessage();
  if (dynamic_cast<igtl::TrackingDataMessage*>(curMessage.GetPointer()) != nullptr)
  {
    igtl::TrackingDataMessage* tdMsg =
      (igtl::TrackingDataMessage*)(curMessage.GetPointer());
  }

}

void mitk::NavigationDataToIGTLMessageFilter::SetInput(const NavigationData* nd)
{
  // Process object is not const-correct so the const_cast is required here
  this->ProcessObject::SetNthInput(0, const_cast<NavigationData*>(nd));
  this->CreateOutputsForAllInputs();
}

void mitk::NavigationDataToIGTLMessageFilter::SetInput(unsigned int idx, const NavigationData* nd)
{
  // Process object is not const-correct so the const_cast is required here
  this->ProcessObject::SetNthInput(idx, const_cast<NavigationData*>(nd));
  this->CreateOutputsForAllInputs();
}

const mitk::NavigationData* mitk::NavigationDataToIGTLMessageFilter::GetInput(void)
{
  if (this->GetNumberOfInputs() < 1)
    return NULL;
  return static_cast<const NavigationData*>(this->ProcessObject::GetInput(0));
}

const mitk::NavigationData* mitk::NavigationDataToIGTLMessageFilter::GetInput(unsigned int idx)
{
  if (this->GetNumberOfInputs() < 1)
    return NULL;
  return static_cast<const NavigationData*>(this->ProcessObject::GetInput(idx));
}

void mitk::NavigationDataToIGTLMessageFilter::CreateOutputsForAllInputs()
{
  switch (m_OperationMode)
  {
  case ModeSendQTDataMsg:
    // create one message output for all navigation data inputs
    this->SetNumberOfIndexedOutputs(this->GetNumberOfIndexedInputs());
    // set the type for this filter
    this->SetType("QTDATA");
    break;
  case ModeSendTDataMsg:
    // create one message output for all navigation data inputs
    this->SetNumberOfIndexedOutputs(this->GetNumberOfIndexedInputs());
    // set the type for this filter
    this->SetType("TDATA");
    break;
  case ModeSendQTransMsg:
    // create one message output for all navigation data input together
    this->SetNumberOfIndexedOutputs(this->GetNumberOfIndexedInputs());
    // set the type for this filter
    this->SetType("POSITION");
    break;
  case ModeSendTransMsg:
    // create one message output for all navigation data input together
    this->SetNumberOfIndexedOutputs(this->GetNumberOfIndexedInputs());
    // set the type for this filter
    this->SetType("TRANS");
    break;
  default:
    break;
  }

  for (unsigned int idx = 0; idx < this->GetNumberOfIndexedOutputs(); ++idx)
  {
    if (this->GetOutput(idx) == NULL)
    {
      DataObjectPointer newOutput = this->MakeOutput(idx);
      this->SetNthOutput(idx, newOutput);
    }
    this->Modified();
  }
}

void ConvertAffineTransformationIntoIGTLMatrix(mitk::AffineTransform3D* trans,
  igtl::Matrix4x4 igtlTransform)
{
  const mitk::AffineTransform3D::MatrixType& matrix = trans->GetMatrix();
  mitk::Vector3D position = trans->GetOffset();
  //copy the data into a matrix type that igtl understands
  for (unsigned int r = 0; r < 3; r++)
  {
    for (unsigned int c = 0; c < 3; c++)
    {
      igtlTransform[r][c] = matrix(r, c);
    }
    igtlTransform[r][3] = position[r];
  }
  for (unsigned int c = 0; c < 3; c++)
  {
    igtlTransform[3][c] = 0.0;
  }
  igtlTransform[3][3] = 1.0;
}

void mitk::NavigationDataToIGTLMessageFilter::GenerateDataModeSendQTransMsg()
{
  // for each output message
  for (unsigned int i = 0; i < this->GetNumberOfIndexedOutputs(); ++i)
  {
    mitk::IGTLMessage* output = this->GetOutput(i);
    assert(output);
    const mitk::NavigationData* input = this->GetInput(i);
    assert(input);
    // do not add navigation data to message if input is invalid
    if (input->IsDataValid() == false)
      continue;

    //get the navigation data components
    mitk::NavigationData::PositionType pos = input->GetPosition();
    mitk::NavigationData::OrientationType ori = input->GetOrientation();

    //insert this information into the message
    igtl::PositionMessage::Pointer posMsg = igtl::PositionMessage::New();
    posMsg->SetPosition(pos[0], pos[1], pos[2]);
    posMsg->SetQuaternion(ori[0], ori[1], ori[2], ori[3]);
    posMsg->SetTimeStamp(ConvertToIGTLTimeStamp(input->GetIGTTimeStamp()));
    posMsg->SetDeviceName(input->GetName());
    posMsg->Pack();

    //add the igtl message to the mitk::IGTLMessage
    output->SetMessage(posMsg.GetPointer());
  }
}

void mitk::NavigationDataToIGTLMessageFilter::GenerateDataModeSendTransMsg()
{
  // for each output message
  for (unsigned int i = 0; i < this->GetNumberOfIndexedOutputs(); ++i)
  {
    mitk::IGTLMessage* output = this->GetOutput(i);
    assert(output);
    const mitk::NavigationData* input = this->GetInput(i);
    assert(input);
    // do not add navigation data to message if input is invalid
    if (input->IsDataValid() == false)
      continue;

    //get the navigation data components
    mitk::AffineTransform3D::Pointer transform = input->GetAffineTransform3D();
    mitk::NavigationData::PositionType position = transform->GetOffset();

    //convert the transform into a igtl type
    igtl::Matrix4x4 igtlTransform;
    ConvertAffineTransformationIntoIGTLMatrix(transform, igtlTransform);

    //insert this information into the message
    igtl::TransformMessage::Pointer transMsg = igtl::TransformMessage::New();
    transMsg->SetMatrix(igtlTransform);
    transMsg->SetPosition(position[0], position[1], position[2]);
    transMsg->SetTimeStamp(ConvertToIGTLTimeStamp(input->GetIGTTimeStamp()));
    transMsg->SetDeviceName(input->GetName());
    transMsg->Pack();

    //add the igtl message to the mitk::IGTLMessage
    output->SetMessage(transMsg.GetPointer());
  }
}
igtl::TimeStamp::Pointer mitk::NavigationDataToIGTLMessageFilter::ConvertToIGTLTimeStamp(double IGTTimeStamp)
{
  igtl::TimeStamp::Pointer timestamp = igtl::TimeStamp::New();
  timestamp->SetTime(IGTTimeStamp / 1000, (int)(IGTTimeStamp) % 1000);
  return timestamp;
}

void mitk::NavigationDataToIGTLMessageFilter::GenerateDataModeSendQTDataMsg()
{
  mitk::IGTLMessage* output = this->GetOutput();
  assert(output);

  //create a output igtl message
  igtl::QuaternionTrackingDataMessage::Pointer qtdMsg =
    igtl::QuaternionTrackingDataMessage::New();

  mitk::NavigationData::PositionType pos;
  mitk::NavigationData::OrientationType ori;

  for (unsigned int index = 0; index < this->GetNumberOfIndexedInputs(); index++)
  {
    const mitk::NavigationData* nd = GetInput(index);
    assert(nd);

    //get the navigation data components
    pos = nd->GetPosition();
    ori = nd->GetOrientation();

    //insert the information into the tracking element
    igtl::QuaternionTrackingDataElement::Pointer tde =
      igtl::QuaternionTrackingDataElement::New();
    tde->SetPosition(pos[0], pos[1], pos[2]);
    tde->SetQuaternion(ori[0], ori[1], ori[2], ori[3]);
    tde->SetName(nd->GetName());

    //insert this element into the tracking data message
    qtdMsg->AddQuaternionTrackingDataElement(tde);

    MITK_INFO << ConvertToIGTLTimeStamp(nd->GetIGTTimeStamp());
  }
  qtdMsg->Pack();

  //add the igtl message to the mitk::IGTLMessage
  output->SetMessage(qtdMsg.GetPointer());
}

void mitk::NavigationDataToIGTLMessageFilter::GenerateDataModeSendTDataMsg()
{
  bool isValidData = true;
  mitk::IGTLMessage* output = this->GetOutput();
  assert(output);

  //create a output igtl message
  igtl::TrackingDataMessage::Pointer tdMsg = igtl::TrackingDataMessage::New();

  mitk::AffineTransform3D::Pointer transform;
  Vector3D position;
  igtl::Matrix4x4 igtlTransform;
  vnl_matrix_fixed<ScalarType, 3, 3> rotationMatrix;
  vnl_matrix_fixed<ScalarType, 3, 3> rotationMatrixTransposed;

  for (unsigned int index = 0; index < this->GetNumberOfIndexedInputs(); index++)
  {
    const mitk::NavigationData* nd = GetInput(index);
    assert(nd);

    //create a new tracking element
    igtl::TrackingDataElement::Pointer tde = igtl::TrackingDataElement::New();

    //get the navigation data components
    transform = nd->GetAffineTransform3D();
    position = transform->GetOffset();

    //check the rotation matrix
    rotationMatrix = transform->GetMatrix().GetVnlMatrix();
    rotationMatrixTransposed = rotationMatrix.transpose();
    // a quadratic matrix is a rotation matrix exactly when determinant is 1
    // and transposed is inverse
    if (!Equal(1.0, vnl_det(rotationMatrix), 0.1)
      || !((rotationMatrix*rotationMatrixTransposed).is_identity(0.1)))
    {
      //the rotation matrix is not valid! => invalidate the current element
      isValidData = false;
    }

    //convert the transform into a igtl type
    ConvertAffineTransformationIntoIGTLMatrix(transform, igtlTransform);

    //fill the tracking element with life
    tde->SetMatrix(igtlTransform);
    tde->SetPosition(position[0], position[1], position[2]);
    std::stringstream name;
    name << nd->GetName();
    if (name.rdbuf()->in_avail() == 0)
    {
      name << "TrackingTool" << index;
    }
    tde->SetName(name.str().c_str());

    //insert this element into the tracking data message
    tdMsg->AddTrackingDataElement(tde);

    //copy the time stamp
    tdMsg->SetTimeStamp(ConvertToIGTLTimeStamp(nd->GetIGTTimeStamp()));
  }
  tdMsg->Pack();
  //add the igtl message to the mitk::IGTLMessage
  output->SetMessage(tdMsg.GetPointer());
  output->SetDataValid(isValidData);
}

void mitk::NavigationDataToIGTLMessageFilter::SetOperationMode(OperationMode mode)
{
  m_OperationMode = mode;
  this->Modified();
  this->CreateOutputsForAllInputs();
}

void mitk::NavigationDataToIGTLMessageFilter::ConnectTo(
  mitk::NavigationDataSource* UpstreamFilter)
{
  for (DataObjectPointerArraySizeType i = 0;
    i < UpstreamFilter->GetNumberOfOutputs(); i++)
  {
    this->SetInput(i, UpstreamFilter->GetOutput(i));
  }
}
<commit_msg>Removed CreateOutputsForAllInputs because it caused a buggy behavior<commit_after>/*===================================================================

The Medical Imaging Interaction Toolkit (MITK)

Copyright (c) German Cancer Research Center,
Division of Medical and Biological Informatics.
All rights reserved.

This software is distributed WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.

See LICENSE.txt or http://www.mitk.org for details.

===================================================================*/

#include "mitkNavigationDataToIGTLMessageFilter.h"

#include "igtlQuaternionTrackingDataMessage.h"
#include "igtlTrackingDataMessage.h"
#include "igtlTransformMessage.h"
#include "igtlPositionMessage.h"

#include <mitkInteractionConst.h>
#include <itksys/SystemTools.hxx>

mitk::NavigationDataToIGTLMessageFilter::NavigationDataToIGTLMessageFilter()
{
  mitk::IGTLMessage::Pointer output = mitk::IGTLMessage::New();
  this->SetNumberOfRequiredOutputs(1);
  this->SetNthOutput(0, output.GetPointer());
  this->SetNumberOfRequiredInputs(1);

  //  m_OperationMode = Mode3D;
  m_CurrentTimeStep = 0;
  //  m_RingBufferSize = 50; //the default ring buffer size
  //  m_NumberForMean = 100;
}

mitk::NavigationDataToIGTLMessageFilter::~NavigationDataToIGTLMessageFilter()
{
}

void mitk::NavigationDataToIGTLMessageFilter::GenerateData()
{
  switch (m_OperationMode)
  {
  case ModeSendQTDataMsg:
    this->GenerateDataModeSendQTDataMsg();
    break;
  case ModeSendTDataMsg:
    this->GenerateDataModeSendTDataMsg();
    break;
  case ModeSendQTransMsg:
    this->GenerateDataModeSendQTransMsg();
    break;
  case ModeSendTransMsg:
    this->GenerateDataModeSendTransMsg();
    break;
  default:
    break;
  }
  igtl::MessageBase::Pointer curMessage = this->GetOutput()->GetMessage();
  if (dynamic_cast<igtl::TrackingDataMessage*>(curMessage.GetPointer()) != nullptr)
  {
    igtl::TrackingDataMessage* tdMsg =
      (igtl::TrackingDataMessage*)(curMessage.GetPointer());
  }

}

void mitk::NavigationDataToIGTLMessageFilter::SetInput(const NavigationData* nd)
{
  // Process object is not const-correct so the const_cast is required here
  this->ProcessObject::SetNthInput(0, const_cast<NavigationData*>(nd));
  this->CreateOutputsForAllInputs();
}

void mitk::NavigationDataToIGTLMessageFilter::SetInput(unsigned int idx, const NavigationData* nd)
{
  // Process object is not const-correct so the const_cast is required here
  this->ProcessObject::SetNthInput(idx, const_cast<NavigationData*>(nd));
  this->CreateOutputsForAllInputs();
}

const mitk::NavigationData* mitk::NavigationDataToIGTLMessageFilter::GetInput(void)
{
  if (this->GetNumberOfInputs() < 1)
    return NULL;
  return static_cast<const NavigationData*>(this->ProcessObject::GetInput(0));
}

const mitk::NavigationData* mitk::NavigationDataToIGTLMessageFilter::GetInput(unsigned int idx)
{
  if (this->GetNumberOfInputs() < 1)
    return NULL;
  return static_cast<const NavigationData*>(this->ProcessObject::GetInput(idx));
}

void mitk::NavigationDataToIGTLMessageFilter::CreateOutputsForAllInputs()
{
  switch (m_OperationMode)
  {
  case ModeSendQTDataMsg:
    // create one message output for all navigation data inputs
    this->SetNumberOfIndexedOutputs(this->GetNumberOfIndexedInputs());
    // set the type for this filter
    this->SetType("QTDATA");
    break;
  case ModeSendTDataMsg:
    // create one message output for all navigation data inputs
    this->SetNumberOfIndexedOutputs(this->GetNumberOfIndexedInputs());
    // set the type for this filter
    this->SetType("TDATA");
    break;
  case ModeSendQTransMsg:
    // create one message output for all navigation data input together
    this->SetNumberOfIndexedOutputs(this->GetNumberOfIndexedInputs());
    // set the type for this filter
    this->SetType("POSITION");
    break;
  case ModeSendTransMsg:
    // create one message output for all navigation data input together
    this->SetNumberOfIndexedOutputs(this->GetNumberOfIndexedInputs());
    // set the type for this filter
    this->SetType("TRANS");
    break;
  default:
    break;
  }

  for (unsigned int idx = 0; idx < this->GetNumberOfIndexedOutputs(); ++idx)
  {
    if (this->GetOutput(idx) == NULL)
    {
      DataObjectPointer newOutput = this->MakeOutput(idx);
      this->SetNthOutput(idx, newOutput);
    }
    this->Modified();
  }
}

void ConvertAffineTransformationIntoIGTLMatrix(mitk::AffineTransform3D* trans,
  igtl::Matrix4x4 igtlTransform)
{
  const mitk::AffineTransform3D::MatrixType& matrix = trans->GetMatrix();
  mitk::Vector3D position = trans->GetOffset();
  //copy the data into a matrix type that igtl understands
  for (unsigned int r = 0; r < 3; r++)
  {
    for (unsigned int c = 0; c < 3; c++)
    {
      igtlTransform[r][c] = matrix(r, c);
    }
    igtlTransform[r][3] = position[r];
  }
  for (unsigned int c = 0; c < 3; c++)
  {
    igtlTransform[3][c] = 0.0;
  }
  igtlTransform[3][3] = 1.0;
}

void mitk::NavigationDataToIGTLMessageFilter::GenerateDataModeSendQTransMsg()
{
  // for each output message
  for (unsigned int i = 0; i < this->GetNumberOfIndexedInputs(); ++i)
  {
    mitk::IGTLMessage* output = this->GetOutput(i);
    assert(output);
    const mitk::NavigationData* input = this->GetInput(i);
    assert(input);
    // do not add navigation data to message if input is invalid
    if (input->IsDataValid() == false)
      continue;

    //get the navigation data components
    mitk::NavigationData::PositionType pos = input->GetPosition();
    mitk::NavigationData::OrientationType ori = input->GetOrientation();

    //insert this information into the message
    igtl::PositionMessage::Pointer posMsg = igtl::PositionMessage::New();
    posMsg->SetPosition(pos[0], pos[1], pos[2]);
    posMsg->SetQuaternion(ori[0], ori[1], ori[2], ori[3]);
    posMsg->SetTimeStamp(ConvertToIGTLTimeStamp(input->GetIGTTimeStamp()));
    posMsg->SetDeviceName(input->GetName());
    posMsg->Pack();

    //add the igtl message to the mitk::IGTLMessage
    output->SetMessage(posMsg.GetPointer());
  }
}

void mitk::NavigationDataToIGTLMessageFilter::GenerateDataModeSendTransMsg()
{
  // for each output message
  for (unsigned int i = 0; i < this->GetNumberOfIndexedInputs(); ++i)
  {
    mitk::IGTLMessage* output = this->GetOutput(i);
    assert(output);
    const mitk::NavigationData* input = this->GetInput(i);
    assert(input);
    // do not add navigation data to message if input is invalid
    if (input->IsDataValid() == false)
      continue;

    //get the navigation data components
    mitk::AffineTransform3D::Pointer transform = input->GetAffineTransform3D();
    mitk::NavigationData::PositionType position = transform->GetOffset();

    //convert the transform into a igtl type
    igtl::Matrix4x4 igtlTransform;
    ConvertAffineTransformationIntoIGTLMatrix(transform, igtlTransform);

    //insert this information into the message
    igtl::TransformMessage::Pointer transMsg = igtl::TransformMessage::New();
    transMsg->SetMatrix(igtlTransform);
    transMsg->SetPosition(position[0], position[1], position[2]);
    transMsg->SetTimeStamp(ConvertToIGTLTimeStamp(input->GetIGTTimeStamp()));
    transMsg->SetDeviceName(input->GetName());
    transMsg->Pack();

    //add the igtl message to the mitk::IGTLMessage
    output->SetMessage(transMsg.GetPointer());
  }
}
igtl::TimeStamp::Pointer mitk::NavigationDataToIGTLMessageFilter::ConvertToIGTLTimeStamp(double IGTTimeStamp)
{
  igtl::TimeStamp::Pointer timestamp = igtl::TimeStamp::New();
  timestamp->SetTime(IGTTimeStamp / 1000, (int)(IGTTimeStamp) % 1000);
  return timestamp;
}

void mitk::NavigationDataToIGTLMessageFilter::GenerateDataModeSendQTDataMsg()
{
  mitk::IGTLMessage* output = this->GetOutput();
  assert(output);

  //create a output igtl message
  igtl::QuaternionTrackingDataMessage::Pointer qtdMsg =
    igtl::QuaternionTrackingDataMessage::New();

  mitk::NavigationData::PositionType pos;
  mitk::NavigationData::OrientationType ori;

  for (unsigned int index = 0; index < this->GetNumberOfIndexedInputs(); index++)
  {
    const mitk::NavigationData* nd = GetInput(index);
    assert(nd);

    //get the navigation data components
    pos = nd->GetPosition();
    ori = nd->GetOrientation();

    //insert the information into the tracking element
    igtl::QuaternionTrackingDataElement::Pointer tde =
      igtl::QuaternionTrackingDataElement::New();
    tde->SetPosition(pos[0], pos[1], pos[2]);
    tde->SetQuaternion(ori[0], ori[1], ori[2], ori[3]);
    tde->SetName(nd->GetName());

    //insert this element into the tracking data message
    qtdMsg->AddQuaternionTrackingDataElement(tde);

    MITK_INFO << ConvertToIGTLTimeStamp(nd->GetIGTTimeStamp());
  }
  qtdMsg->Pack();

  //add the igtl message to the mitk::IGTLMessage
  output->SetMessage(qtdMsg.GetPointer());
}

void mitk::NavigationDataToIGTLMessageFilter::GenerateDataModeSendTDataMsg()
{
  bool isValidData = true;
  mitk::IGTLMessage* output = this->GetOutput();
  assert(output);

  //create a output igtl message
  igtl::TrackingDataMessage::Pointer tdMsg = igtl::TrackingDataMessage::New();

  mitk::AffineTransform3D::Pointer transform;
  Vector3D position;
  igtl::Matrix4x4 igtlTransform;
  vnl_matrix_fixed<ScalarType, 3, 3> rotationMatrix;
  vnl_matrix_fixed<ScalarType, 3, 3> rotationMatrixTransposed;

  for (unsigned int index = 0; index < this->GetNumberOfIndexedInputs(); index++)
  {
    const mitk::NavigationData* nd = GetInput(index);
    assert(nd);

    //create a new tracking element
    igtl::TrackingDataElement::Pointer tde = igtl::TrackingDataElement::New();

    //get the navigation data components
    transform = nd->GetAffineTransform3D();
    position = transform->GetOffset();

    //check the rotation matrix
    rotationMatrix = transform->GetMatrix().GetVnlMatrix();
    rotationMatrixTransposed = rotationMatrix.transpose();
    // a quadratic matrix is a rotation matrix exactly when determinant is 1
    // and transposed is inverse
    if (!Equal(1.0, vnl_det(rotationMatrix), 0.1)
      || !((rotationMatrix*rotationMatrixTransposed).is_identity(0.1)))
    {
      //the rotation matrix is not valid! => invalidate the current element
      isValidData = false;
    }

    //convert the transform into a igtl type
    ConvertAffineTransformationIntoIGTLMatrix(transform, igtlTransform);

    //fill the tracking element with life
    tde->SetMatrix(igtlTransform);
    tde->SetPosition(position[0], position[1], position[2]);
    std::stringstream name;
    name << nd->GetName();
    if (name.rdbuf()->in_avail() == 0)
    {
      name << "TrackingTool" << index;
    }
    tde->SetName(name.str().c_str());

    //insert this element into the tracking data message
    tdMsg->AddTrackingDataElement(tde);

    //copy the time stamp
    tdMsg->SetTimeStamp(ConvertToIGTLTimeStamp(nd->GetIGTTimeStamp()));
  }
  tdMsg->Pack();
  //add the igtl message to the mitk::IGTLMessage
  output->SetMessage(tdMsg.GetPointer());
  output->SetDataValid(isValidData);
}

void mitk::NavigationDataToIGTLMessageFilter::SetOperationMode(OperationMode mode)
{
  m_OperationMode = mode;
  this->Modified();
}

void mitk::NavigationDataToIGTLMessageFilter::ConnectTo(
  mitk::NavigationDataSource* UpstreamFilter)
{
  for (DataObjectPointerArraySizeType i = 0;
    i < UpstreamFilter->GetNumberOfOutputs(); i++)
  {
    this->SetInput(i, UpstreamFilter->GetOutput(i));
  }
}
<|endoftext|>


<commit_before>#include <QtCore>
#include <random>

#include "testhof.h"

QString runHof(const QString& program,
               bool* ok,
               bool verbose = false,
               int msecsToTimeout = 5000,
               bool expectTimeout = false
              )
{
    QDir bin(QCoreApplication::applicationDirPath());

    QProcess hof;
    hof.setProgram(bin.path() + QDir::separator() + "hof");

    QStringList args = QStringList()
      << "--program"
      << program;

    if (verbose) {
        args.append("--verbose");
        hof.setProcessChannelMode(QProcess::ForwardedErrorChannel);
    }

    hof.setArguments(args);
    hof.start();
    bool finished = hof.waitForFinished(msecsToTimeout);
    if (!finished) {
        hof.kill();
        hof.waitForFinished();
    }

    /* special value used to indicate stack depth exceeded */
    if (!expectTimeout) {
        *ok = hof.exitStatus() == QProcess::NormalExit &&
              (hof.exitCode() == EXIT_SUCCESS || hof.exitCode() == 2);
    } else {
        *ok = finished != expectTimeout;
    }

    if (!*ok) {
        qDebug() << "exit status:" << hof.exitStatus()
                 << " code:" << hof.exitCode()
                 << " error:" << hof.error();
    }

    return hof.readAll().trimmed();
}

#define I "I"
#define K "K"
#define S "S"
#define APPLY(X) "A" X
#define PRINT(X) "P" X
#define RANDOM(X, Y) "R" X Y
#define TRUE "K"
#define FALSE "V"
#define IF(X, Y, Z) X Y Z
#define IFNOT(X, Y, Z) X Z Y
#define AND(X, Y) X Y FALSE
#define OR(X, Y) X TRUE Y
#define ZERO FALSE
#define ONE I
#define SUCC(X) "AA" S "AA" S "A" K S K X
#define OMEGA S I I "AA" S I I

// standard beta recursion combinator
#define BETA_RECURSE(X) S "A" K X "AA" S I I "AA" S "A" K X "AA" S I I

// standard y combinator
#define YCOMBINATOR(X) S "A" K "AA" S I I "AA" S "AA" S "A" K S K "A" K "AA" S I I X
// S (K (S I I)) (S (S (K S) K) (K (S I I)))

// tromp's y combinator
#define YCOMBINATOR_1(X) S S K "A" S "A" K "AA" S S "A" S "AA" S S K K X

#define WHILE(COND, OPERATION, INITIAL) ""

void TestHof::testHof()
{
    bool ok = false;
    QString out;

    // print
    out = runHof(PRINT(I), &ok);
    QCOMPARE(out, QString(I));
    QVERIFY(ok);

    // if-then-else
    out = runHof(IF(TRUE, APPLY(PRINT(I)), APPLY(PRINT(K))), &ok);
    QCOMPARE(out, QString(I));
    QVERIFY(ok);

    out = runHof(IF(FALSE, APPLY(PRINT(I)), APPLY(PRINT(K))), &ok);
    QCOMPARE(out, QString(K));
    QVERIFY(ok);

    // if-not-then-else
    out = runHof(IFNOT(TRUE, APPLY(PRINT(I)), APPLY(PRINT(K))), &ok);
    QCOMPARE(out, QString(K));
    QVERIFY(ok);

    out = runHof(IFNOT(FALSE, APPLY(PRINT(I)), APPLY(PRINT(K))), &ok);
    QCOMPARE(out, QString(I));
    QVERIFY(ok);

    //if-and-then-else
    out = runHof(IF(AND(TRUE, TRUE), APPLY(PRINT(I)), APPLY(PRINT(K))), &ok);
    QCOMPARE(out, QString(I));
    QVERIFY(ok);

    out = runHof(IF(AND(TRUE, FALSE), APPLY(PRINT(I)), APPLY(PRINT(K))), &ok);
    QCOMPARE(out, QString(K));
    QVERIFY(ok);

    // if-or-then-else
    out = runHof(IF(OR(TRUE, FALSE), APPLY(PRINT(I)), APPLY(PRINT(K))), &ok);
    QCOMPARE(out, QString(I));
    QVERIFY(ok);

    out = runHof(IF(OR(FALSE, TRUE), APPLY(PRINT(I)), APPLY(PRINT(K))), &ok);
    QCOMPARE(out, QString(I));
    QVERIFY(ok);

    out = runHof(IF(OR(FALSE, FALSE), APPLY(PRINT(I)), APPLY(PRINT(K))), &ok);
    QCOMPARE(out, QString(K));
    QVERIFY(ok);

    // church numerals
    out = runHof(QString(SUCC(ONE)) + PRINT(I), &ok);
    QCOMPARE(out, QString("II"));
    QVERIFY(ok);

    out = runHof(QString(SUCC(SUCC(ONE))) + PRINT(I), &ok);
    QCOMPARE(out, QString("III"));
    QVERIFY(ok);

    out = runHof(QString(SUCC(SUCC(SUCC(ONE)))) + PRINT(I), &ok);
    QCOMPARE(out, QString("IIII"));
    QVERIFY(ok);

    out = runHof(QString(SUCC(SUCC(SUCC(SUCC(ONE))))) + PRINT(I), &ok);
    QCOMPARE(out, QString("IIIII"));
    QVERIFY(ok);

    // print random
    out = runHof(RANDOM(APPLY(PRINT(I)), APPLY(PRINT(K))), &ok);
    QVERIFY2(out == "I" || out == "K", qPrintable(out));
    QVERIFY(ok);
}

void TestHof::testY()
{
    bool ok = false;
    QString out;
    int timeout = 500;
    bool verbose = false;
    bool expectTimeout = true;

    out = runHof(YCOMBINATOR("API"), &ok, verbose, timeout, expectTimeout);
    QVERIFY(out.count('I') > 100);
    out.replace("I", "");
    QCOMPARE(out, QString());
    QVERIFY(ok);
}

void TestHof::testOmega()
{
    bool ok = false;
    QString out;
    int timeout = 500;
    bool verbose = false;
    bool expectTimeout = true;

    out = runHof(OMEGA, &ok, verbose, timeout, expectTimeout);
    QCOMPARE(out, QString());
    QVERIFY(ok);
}

void TestHof::testHofNoise()
{
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<int> distLength(0, 100);
    std::uniform_int_distribution<int> distLetter(0, 6);

    for (int i = 0; i < 100; ++i) {
        int length = distLength(gen);
        QString randomHofProgram;
        for (int j = 0; j < length; ++j) {
            int letter = distLetter(gen);
            QChar ch;
            switch (letter) {
            case 0: ch = 'I'; break;
            case 1: ch = 'K'; break;
            case 2: ch = 'S'; break;
            case 3: ch = 'V'; break;
            case 4: ch = 'A'; break;
            case 5: ch = 'P'; break;
            case 6: ch = 'R'; break;
            default:
              Q_ASSERT(false);
            }
            randomHofProgram.append(ch);
        }
        bool ok = false;
        runHof(randomHofProgram, &ok, false /*verbose*/);
        QVERIFY2(ok, qPrintable(randomHofProgram));
    }
}
<commit_msg>Add a crude benchmark for y combinator.<commit_after>#include <QtCore>
#include <random>

#include "testhof.h"

QString runHof(const QString& program,
               bool* ok,
               bool verbose = false,
               int msecsToTimeout = 5000,
               bool expectTimeout = false
              )
{
    QDir bin(QCoreApplication::applicationDirPath());

    QProcess hof;
    hof.setProgram(bin.path() + QDir::separator() + "hof");

    QStringList args = QStringList()
      << "--program"
      << program;

    if (verbose) {
        args.append("--verbose");
        hof.setProcessChannelMode(QProcess::ForwardedErrorChannel);
    }

    hof.setArguments(args);
    hof.start();
    bool finished = hof.waitForFinished(msecsToTimeout);
    if (!finished) {
        hof.kill();
        hof.waitForFinished();
    }

    /* special value used to indicate stack depth exceeded */
    if (!expectTimeout) {
        *ok = hof.exitStatus() == QProcess::NormalExit &&
              (hof.exitCode() == EXIT_SUCCESS || hof.exitCode() == 2);
    } else {
        *ok = finished != expectTimeout;
    }

    if (!*ok) {
        qDebug() << "exit status:" << hof.exitStatus()
                 << " code:" << hof.exitCode()
                 << " error:" << hof.error();
    }

    return hof.readAll().trimmed();
}

#define I "I"
#define K "K"
#define S "S"
#define APPLY(X) "A" X
#define PRINT(X) "P" X
#define RANDOM(X, Y) "R" X Y
#define TRUE "K"
#define FALSE "V"
#define IF(X, Y, Z) X Y Z
#define IFNOT(X, Y, Z) X Z Y
#define AND(X, Y) X Y FALSE
#define OR(X, Y) X TRUE Y
#define ZERO FALSE
#define ONE I
#define SUCC(X) "AA" S "AA" S "A" K S K X
#define OMEGA S I I "AA" S I I

// standard beta recursion combinator
#define BETA_RECURSE(X) S "A" K X "AA" S I I "AA" S "A" K X "AA" S I I

// standard y combinator
#define YCOMBINATOR(X) S "A" K "AA" S I I "AA" S "AA" S "A" K S K "A" K "AA" S I I X
// S (K (S I I)) (S (S (K S) K) (K (S I I)))

// tromp's y combinator
#define YCOMBINATOR_1(X) S S K "A" S "A" K "AA" S S "A" S "AA" S S K K X

#define WHILE(COND, OPERATION, INITIAL) ""

void TestHof::testHof()
{
    bool ok = false;
    QString out;

    // print
    out = runHof(PRINT(I), &ok);
    QCOMPARE(out, QString(I));
    QVERIFY(ok);

    // if-then-else
    out = runHof(IF(TRUE, APPLY(PRINT(I)), APPLY(PRINT(K))), &ok);
    QCOMPARE(out, QString(I));
    QVERIFY(ok);

    out = runHof(IF(FALSE, APPLY(PRINT(I)), APPLY(PRINT(K))), &ok);
    QCOMPARE(out, QString(K));
    QVERIFY(ok);

    // if-not-then-else
    out = runHof(IFNOT(TRUE, APPLY(PRINT(I)), APPLY(PRINT(K))), &ok);
    QCOMPARE(out, QString(K));
    QVERIFY(ok);

    out = runHof(IFNOT(FALSE, APPLY(PRINT(I)), APPLY(PRINT(K))), &ok);
    QCOMPARE(out, QString(I));
    QVERIFY(ok);

    //if-and-then-else
    out = runHof(IF(AND(TRUE, TRUE), APPLY(PRINT(I)), APPLY(PRINT(K))), &ok);
    QCOMPARE(out, QString(I));
    QVERIFY(ok);

    out = runHof(IF(AND(TRUE, FALSE), APPLY(PRINT(I)), APPLY(PRINT(K))), &ok);
    QCOMPARE(out, QString(K));
    QVERIFY(ok);

    // if-or-then-else
    out = runHof(IF(OR(TRUE, FALSE), APPLY(PRINT(I)), APPLY(PRINT(K))), &ok);
    QCOMPARE(out, QString(I));
    QVERIFY(ok);

    out = runHof(IF(OR(FALSE, TRUE), APPLY(PRINT(I)), APPLY(PRINT(K))), &ok);
    QCOMPARE(out, QString(I));
    QVERIFY(ok);

    out = runHof(IF(OR(FALSE, FALSE), APPLY(PRINT(I)), APPLY(PRINT(K))), &ok);
    QCOMPARE(out, QString(K));
    QVERIFY(ok);

    // church numerals
    out = runHof(QString(SUCC(ONE)) + PRINT(I), &ok);
    QCOMPARE(out, QString("II"));
    QVERIFY(ok);

    out = runHof(QString(SUCC(SUCC(ONE))) + PRINT(I), &ok);
    QCOMPARE(out, QString("III"));
    QVERIFY(ok);

    out = runHof(QString(SUCC(SUCC(SUCC(ONE)))) + PRINT(I), &ok);
    QCOMPARE(out, QString("IIII"));
    QVERIFY(ok);

    out = runHof(QString(SUCC(SUCC(SUCC(SUCC(ONE))))) + PRINT(I), &ok);
    QCOMPARE(out, QString("IIIII"));
    QVERIFY(ok);

    // print random
    out = runHof(RANDOM(APPLY(PRINT(I)), APPLY(PRINT(K))), &ok);
    QVERIFY2(out == "I" || out == "K", qPrintable(out));
    QVERIFY(ok);
}

void TestHof::testY()
{
    bool ok = false;
    QString out;
    int timeout = 500;
    bool verbose = false;
    bool expectTimeout = true;

    out = runHof(YCOMBINATOR("API"), &ok, verbose, timeout, expectTimeout);
    qDebug() << "iterations" << out.count('I');
    QVERIFY(out.count('I') > 2000); // crude benchmark in debug mode
    out.replace("I", "");
    QCOMPARE(out, QString());
    QVERIFY(ok);
}

void TestHof::testOmega()
{
    bool ok = false;
    QString out;
    int timeout = 500;
    bool verbose = false;
    bool expectTimeout = true;

    out = runHof(OMEGA, &ok, verbose, timeout, expectTimeout);
    QCOMPARE(out, QString());
    QVERIFY(ok);
}

void TestHof::testHofNoise()
{
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<int> distLength(0, 100);
    std::uniform_int_distribution<int> distLetter(0, 6);

    for (int i = 0; i < 100; ++i) {
        int length = distLength(gen);
        QString randomHofProgram;
        for (int j = 0; j < length; ++j) {
            int letter = distLetter(gen);
            QChar ch;
            switch (letter) {
            case 0: ch = 'I'; break;
            case 1: ch = 'K'; break;
            case 2: ch = 'S'; break;
            case 3: ch = 'V'; break;
            case 4: ch = 'A'; break;
            case 5: ch = 'P'; break;
            case 6: ch = 'R'; break;
            default:
              Q_ASSERT(false);
            }
            randomHofProgram.append(ch);
        }
        bool ok = false;
        runHof(randomHofProgram, &ok, false /*verbose*/);
        QVERIFY2(ok, qPrintable(randomHofProgram));
    }
}
<|endoftext|>