<commit_before>/* -*- Mode: C; indent-tabs-mode: s; c-basic-offset: 4; tab-width: 4 -*- */
/* vim:set et ai sw=4 ts=4 sts=4: tw=80 cino="(0,W2s,i2s,t0,l1,:0" */
#include "dcpappletbuttons.h"

#include <Pages>
#include <DcpAppletDb>
#include <DcpBriefComponent>
#include <DcpAppletMetadata>
#include <DcpApplet>

#include <QCoreApplication>
#include <QtDebug>
#include <QTimer>
#include <DuiSceneManager>
#include <DuiGridLayoutPolicy>
#include <DuiLinearLayoutPolicy>
#include <QPropertyAnimation>
#include "maintranslations.h"

#define DEBUG
#include "../../../lib/src/dcpdebug.h"

/*!
 * \class DcpAppletButtons
 * \brief A container which contains buttons that represents the applets.
 */
DcpAppletButtons::DcpAppletButtons (
        const QString      &logicalId,
        const QString      &categoryName,
        const QString      &title,
        QGraphicsWidget    *parent)
: DcpMainCategory (title, parent, logicalId),
    m_CategoryName (categoryName),
    m_LogicalId (logicalId),
    m_CategoryInfo (0)
{
    setCreateSeparators (true);
    setMaxColumns (2);
    createContents();
    setMattiID ("DcpAppletButtons::" + logicalId + "::" + categoryName);
}

DcpAppletButtons::DcpAppletButtons (
        const DcpCategoryInfo  *categoryInfo,
        const QString          &title,
        QGraphicsWidget        *parent)
: DcpMainCategory (title, parent, categoryInfo->titleId),
    m_CategoryName (categoryInfo->appletCategory),
    m_CategoryInfo (categoryInfo)
{
    setCreateSeparators (true);
    setMaxColumns (2);
    createContents ();
    setMattiID (
            QString ("DcpAppletButtons::") +
            categoryInfo->titleId + "::" +
            categoryInfo->appletCategory);
}

void
DcpAppletButtons::createContents ()
{
    DCP_DEBUG ("");
    /*
     * Getting the list of applet variants (metadata objects) that will go into
     * this widget.
     */
    if (logicalId() == DcpMain::mostRecentUsedTitleId) {
        // additional copy (with implicit sharing) to be able to clear whole mem up
        m_LoadingMetadatas = new DcpAppletMetadataList(
                                    DcpAppletDb::instance()->listMostUsed ());
        m_PortraitLayout->setObjectName ("MostUsedItems");
        m_LandscapeLayout->setObjectName ("MostUsedItems");
    } else {
        bool        withUncategorized;
        const char *names[3];

        withUncategorized = m_CategoryInfo &&
            m_CategoryInfo->subPageId == PageHandle::Applications;

        if (m_CategoryInfo) {
            names[0] = m_CategoryInfo->titleId;
            names[1] = m_CategoryInfo->appletCategory;
        } else {
            names[0] = DCP_STR (m_LogicalId);
            names[1] = DCP_STR (m_CategoryName);
        }
        names[2] = 0;

        m_LoadingMetadatas = new DcpAppletMetadataList(
                DcpAppletDb::instance()->listByCategory (names, 2,
                withUncategorized ? dcp_category_name_enlisted : NULL));
    }

    /*
     * If we have a category info that might contain static elements, like the
     * 'accounts & applications' contain the 'service accounts' and
     * 'applications' static elements.
     */
    if (m_CategoryInfo && m_CategoryInfo->staticElements) {
        const DcpCategoryInfo *element;

        for (int cnt = 0; ; ++cnt) {
            element = &m_CategoryInfo->staticElements[cnt];
            if (element->titleId == 0)
                break;

            addComponent (
                    element->appletCategory,
                    "",
                    element->subPageId);
        }
    }

}

/*! \brief start the loading
 * It is called from outside so that the caller can connect on
 * loadingFinished signal before calling the method, if it wants
 * to get notified when loading finished.
 */
void
DcpAppletButtons::startLoading()
{
    /*
     * Adding the applet variants to the widget.
     */
    m_LoadPosition = 0;
    if (m_LoadingMetadatas->count() > 0) {
        QTimer::singleShot(0, this, SLOT(continueLoading()));
    } else {
        stopLoading();
    }
}

void
DcpAppletButtons::stopLoading()
{
    delete m_LoadingMetadatas;
    m_LoadingMetadatas = 0;
    m_LoadPosition = 0;
    emit loadingFinished();
}

/* \returns true if the container contains loaded or loading items */
bool
DcpAppletButtons::hasLoadingItems()
{
    if (m_LoadingMetadatas) {
        return m_LoadingMetadatas->count() > 0;
    } else {
        return getItemCount() > 0;
    }
}

void
DcpAppletButtons::continueLoading ()
{
    DcpAppletMetadata*& item = (*m_LoadingMetadatas)[m_LoadPosition];
    addComponent (item);
    item = 0;
    m_LoadPosition++;
    if (m_LoadPosition < m_LoadingMetadatas->count()) {
        QTimer::singleShot(0, this, SLOT(continueLoading()));
    } else {
        stopLoading();
    }
}

void
DcpAppletButtons::addComponent (
        DcpAppletMetadata *metadata)
{
    DcpBriefComponent *component;

    component = new DcpBriefComponent (metadata, this, logicalId());

    // this creates a fade in animation
    component->setOpacity(0);
    QPropertyAnimation* animation =
        new QPropertyAnimation(component, "opacity");
    animation->setDuration(1000);
    animation->setStartValue(0.0);
    animation->setEndValue(1.0);
    animation->start(QAbstractAnimation::DeleteWhenStopped);
/*
    DuiWidgetAnimation* a = new DuiWidgetAnimation(NULL);
    a->setTargetOpacity(component, 1.0);
    a->setDuration(1000, "opacity");
    a->start(QAbstractAnimation::DeleteWhenStopped);
    */

    component->setSubPage (PageHandle::APPLET, metadata->name());

    appendWidget (component);
}

void
DcpAppletButtons::addComponent (
        const QString       &briefTitleText,
        const QString       &briefSecondaryText,
        const PageHandle    &pageHandle)
{
    DcpBriefComponent *component;

    component = new DcpBriefComponent (
            briefTitleText,
            briefSecondaryText,
            this, logicalId());
    component->setSubPage (pageHandle);

    appendWidget (component);
}

void
DcpAppletButtons::reload ()
{
//    This is used only for reloading the sequence of the most used items
//
//    FIXME, maybe we could make it more optimal with reusing the existing
//    widgets (at most one additional widget is coming, at most one is to be
//    removed)
//
    deleteItems ();
    createContents ();
    startLoading ();
}

QString
DcpAppletButtons::mattiID ()
{
    return m_mattiID;
}

void
DcpAppletButtons::setMattiID (
        const QString &mattiID)
{
    m_mattiID = mattiID;
}

<commit_msg>disable testing fade animation<commit_after>/* -*- Mode: C; indent-tabs-mode: s; c-basic-offset: 4; tab-width: 4 -*- */
/* vim:set et ai sw=4 ts=4 sts=4: tw=80 cino="(0,W2s,i2s,t0,l1,:0" */
#include "dcpappletbuttons.h"

#include <Pages>
#include <DcpAppletDb>
#include <DcpBriefComponent>
#include <DcpAppletMetadata>
#include <DcpApplet>

#include <QCoreApplication>
#include <QtDebug>
#include <QTimer>
#include <DuiSceneManager>
#include <DuiGridLayoutPolicy>
#include <DuiLinearLayoutPolicy>
#include "maintranslations.h"

#define DEBUG
#include "../../../lib/src/dcpdebug.h"

/*!
 * \class DcpAppletButtons
 * \brief A container which contains buttons that represents the applets.
 */
DcpAppletButtons::DcpAppletButtons (
        const QString      &logicalId,
        const QString      &categoryName,
        const QString      &title,
        QGraphicsWidget    *parent)
: DcpMainCategory (title, parent, logicalId),
    m_CategoryName (categoryName),
    m_LogicalId (logicalId),
    m_CategoryInfo (0)
{
    setCreateSeparators (true);
    setMaxColumns (2);
    createContents();
    setMattiID ("DcpAppletButtons::" + logicalId + "::" + categoryName);
}

DcpAppletButtons::DcpAppletButtons (
        const DcpCategoryInfo  *categoryInfo,
        const QString          &title,
        QGraphicsWidget        *parent)
: DcpMainCategory (title, parent, categoryInfo->titleId),
    m_CategoryName (categoryInfo->appletCategory),
    m_CategoryInfo (categoryInfo)
{
    setCreateSeparators (true);
    setMaxColumns (2);
    createContents ();
    setMattiID (
            QString ("DcpAppletButtons::") +
            categoryInfo->titleId + "::" +
            categoryInfo->appletCategory);
}

void
DcpAppletButtons::createContents ()
{
    DCP_DEBUG ("");
    /*
     * Getting the list of applet variants (metadata objects) that will go into
     * this widget.
     */
    if (logicalId() == DcpMain::mostRecentUsedTitleId) {
        // additional copy (with implicit sharing) to be able to clear whole mem up
        m_LoadingMetadatas = new DcpAppletMetadataList(
                                    DcpAppletDb::instance()->listMostUsed ());
        m_PortraitLayout->setObjectName ("MostUsedItems");
        m_LandscapeLayout->setObjectName ("MostUsedItems");
    } else {
        bool        withUncategorized;
        const char *names[3];

        withUncategorized = m_CategoryInfo &&
            m_CategoryInfo->subPageId == PageHandle::Applications;

        if (m_CategoryInfo) {
            names[0] = m_CategoryInfo->titleId;
            names[1] = m_CategoryInfo->appletCategory;
        } else {
            names[0] = DCP_STR (m_LogicalId);
            names[1] = DCP_STR (m_CategoryName);
        }
        names[2] = 0;

        m_LoadingMetadatas = new DcpAppletMetadataList(
                DcpAppletDb::instance()->listByCategory (names, 2,
                withUncategorized ? dcp_category_name_enlisted : NULL));
    }

    /*
     * If we have a category info that might contain static elements, like the
     * 'accounts & applications' contain the 'service accounts' and
     * 'applications' static elements.
     */
    if (m_CategoryInfo && m_CategoryInfo->staticElements) {
        const DcpCategoryInfo *element;

        for (int cnt = 0; ; ++cnt) {
            element = &m_CategoryInfo->staticElements[cnt];
            if (element->titleId == 0)
                break;

            addComponent (
                    element->appletCategory,
                    "",
                    element->subPageId);
        }
    }

}

/*! \brief start the loading
 * It is called from outside so that the caller can connect on
 * loadingFinished signal before calling the method, if it wants
 * to get notified when loading finished.
 */
void
DcpAppletButtons::startLoading()
{
    /*
     * Adding the applet variants to the widget.
     */
    m_LoadPosition = 0;
    if (m_LoadingMetadatas->count() > 0) {
        QTimer::singleShot(0, this, SLOT(continueLoading()));
    } else {
        stopLoading();
    }
}

void
DcpAppletButtons::stopLoading()
{
    delete m_LoadingMetadatas;
    m_LoadingMetadatas = 0;
    m_LoadPosition = 0;
    emit loadingFinished();
}

/* \returns true if the container contains loaded or loading items */
bool
DcpAppletButtons::hasLoadingItems()
{
    if (m_LoadingMetadatas) {
        return m_LoadingMetadatas->count() > 0;
    } else {
        return getItemCount() > 0;
    }
}

void
DcpAppletButtons::continueLoading ()
{
    DcpAppletMetadata*& item = (*m_LoadingMetadatas)[m_LoadPosition];
    addComponent (item);
    item = 0;
    m_LoadPosition++;
    if (m_LoadPosition < m_LoadingMetadatas->count()) {
        QTimer::singleShot(0, this, SLOT(continueLoading()));
    } else {
        stopLoading();
    }
}

void
DcpAppletButtons::addComponent (
        DcpAppletMetadata *metadata)
{
    DcpBriefComponent *component;
    component = new DcpBriefComponent (metadata, this, logicalId());
    component->setSubPage (PageHandle::APPLET, metadata->name());

    appendWidget (component);
}

void
DcpAppletButtons::addComponent (
        const QString       &briefTitleText,
        const QString       &briefSecondaryText,
        const PageHandle    &pageHandle)
{
    DcpBriefComponent *component;

    component = new DcpBriefComponent (
            briefTitleText,
            briefSecondaryText,
            this, logicalId());
    component->setSubPage (pageHandle);

    appendWidget (component);
}

void
DcpAppletButtons::reload ()
{
//    This is used only for reloading the sequence of the most used items
//
//    FIXME, maybe we could make it more optimal with reusing the existing
//    widgets (at most one additional widget is coming, at most one is to be
//    removed)
//
    deleteItems ();
    createContents ();
    startLoading ();
}

QString
DcpAppletButtons::mattiID ()
{
    return m_mattiID;
}

void
DcpAppletButtons::setMattiID (
        const QString &mattiID)
{
    m_mattiID = mattiID;
}

<|endoftext|>


<commit_before>//
// Created by marandil on 08.09.15.
//

#ifndef MDLUTILS_MULTITHREADING_THREAD_POOL_HPP
#define MDLUTILS_MULTITHREADING_THREAD_POOL_HPP

#include <mutex>
#include <queue>
#include <thread>
#include <atomic>
#include <future>
#include <vector>
#include <exception>
#include <functional>

#include <mdlutils/exceptions.hpp>
#include <mdlutils/accessor/const_accessor.hpp>
#include <mdlutils/types/const_vector.hpp>
#include <mdlutils/multithreading/helpers.hpp>
#include <mdlutils/multithreading/handler.hpp>
#include <mdlutils/multithreading/looper.hpp>

namespace mdl
{
    class thread_pool : protected mdl::exception_handler, protected mdl::handler
    {
    public:
        enum class strategy
        {
            round_robin,
            dynamic
        };

    protected:
        struct thread_handler : public looper_thread
        {
            thread_pool &parent;
            unsigned id;

            thread_handler(unsigned id, thread_pool &parent) :
                    id(id),
                    parent(parent),
                    looper_thread(static_cast<mdl::exception_handler &>(parent),
                                  static_cast<mdl::handler &>(parent)) { }
        };

        virtual void handle_exception(std::exception_ptr);

        virtual bool handle_message(mdl::message_ptr);

        void initialize_queues();

        typedef mdl::const_vector<thread_handler> pool_type;
        pool_type pool;
        pool_type::iterator next_robin;

        std::mutex exception_queue_lock;
        std::queue<std::exception_ptr> exception_queue;

        std::mutex task_queue_lock;
        std::queue<message_ptr> task_queue;

        void throw_if_nonempty();

        void stop_and_join();

        void send_message(message_ptr);

        unsigned processes;
        strategy task_assigning_strategy;

    public:
        thread_pool(unsigned processes = helper::hw_concurrency(),
                    strategy task_assigning_strategy = strategy::round_robin) :
                processes(processes),
                task_assigning_strategy(task_assigning_strategy),
                pool(pool_type::make_indexed(processes, *this)),
                next_robin(pool.begin())
        {
            initialize_queues();
        }

        ~thread_pool(void)
        {
            stop_and_join();
        }

        mdl::const_accessor<unsigned> workers{processes};

        template<typename Fn, typename... Args, typename T = typename std::result_of<Fn(Args...)>::type>
        std::future<T>
        async(Fn &&fn, Args &&... args)
        {
            std::shared_ptr<std::promise<T>> promise = std::make_shared<std::promise<T>>();
            auto lambda = [promise, fn, args...](void)
                { promise->set_value(fn(args...)); };
            send_message(std::make_shared<post_call>(lambda));
            return promise->get_future();
        };

        template<typename RandomAccessIteratorIn, typename RandomAccessIteratorOut, typename Fn>
        std::future<bool> map(RandomAccessIteratorIn first, RandomAccessIteratorIn last, RandomAccessIteratorOut output_first, Fn function)
        {
            typedef typename std::iterator_traits<RandomAccessIteratorIn>::value_type value_type;
            typedef typename std::iterator_traits<RandomAccessIteratorOut>::value_type result_type;
            typedef typename std::result_of<Fn(value_type)>::type function_result_type;

            static_assert(std::is_convertible<function_result_type, result_type>::value, "Function return type not convertible to iterator value_type");

            // Count the distance between last and first:
            ptrdiff_t n = std::distance(first, last);
            if(n < 0) mdl_throw(make_ia_exception, "Invalid iterator range", "first, last", std::make_pair(first, last));
            // Create n async tasks for all elements in range:
            auto futures = std::make_shared<const_vector<std::future<bool>>>(n);
            for(size_t i = 0; first != last; ++first, ++output_first, ++i)
            {
                futures->at(i) = async([first, output_first, function] ()
                    {
                        *output_first = function(*first);
                        return true;
                    });
            }
            return std::async([futures]()
                                  {
                                      for(auto& job : (*futures))
                                          job.get();
                                      return true;
                                  });
        }
    };
}

#endif //MDLUTILS_MULTITHREADING_THREAD_POOL_HPP
<commit_msg>Documented thread_pool<commit_after>//
// Created by marandil on 08.09.15.
//

#ifndef MDLUTILS_MULTITHREADING_THREAD_POOL_HPP
#define MDLUTILS_MULTITHREADING_THREAD_POOL_HPP

#include <mutex>
#include <queue>
#include <thread>
#include <atomic>
#include <future>
#include <vector>
#include <exception>
#include <functional>

#include <mdlutils/exceptions.hpp>
#include <mdlutils/accessor/const_accessor.hpp>
#include <mdlutils/types/const_vector.hpp>
#include <mdlutils/multithreading/helpers.hpp>
#include <mdlutils/multithreading/handler.hpp>
#include <mdlutils/multithreading/looper.hpp>

namespace mdl
{
    /* Class providing a pool of workers, which can asynchronously perform selected tasks.
     *
     * Currently, there are two strategies at which the tasks can be assigned and two
     * methods of creating tasks.
     *
     * Strategies: (see <thread_pool::strategy>)
     * Methods: <async>, <map>
     */
    class thread_pool : protected mdl::exception_handler, protected mdl::handler
    {
    public:
        /* Strategies that can be used to assign tasks to workers */
        enum class strategy
        {
            /* In this strategy, each worker performs equal numbers of tasks.
             * Whenever a new task is created, it's posted to the next worker in queue
             * and the worker is requeued.
             * This strategy might cause some workers to be idle for extended periods of time,
             * if their jobs are less time consuming than the others'.
             */
            round_robin,
            /* In this strategy, each worker keeps track of the end of it's message queue.
             * Once it's reached, it asks the <thread_pool> for a new task and becomes temporarily
             * idle, if no tasks are queued in <thread_pool>. This aims at counteracting the situation
             * in which some tasks have long task queues, while others are idle, as the tasks are
             * assigned one-at-a-time.
             */
            dynamic
        };

    protected:
        /* Implementation of <looper_thread> that keeps track of it's parent, and registers him as
         * exception and message handler
         */
        struct thread_handler : public looper_thread
        {
            thread_pool &parent;
            unsigned id;

            thread_handler(unsigned id, thread_pool &parent) :
                    id(id),
                    parent(parent),
                    looper_thread(static_cast<mdl::exception_handler &>(parent),
                                  static_cast<mdl::handler &>(parent)) { }
        };

        // Implementation of <exception_handler> interface. Will enqueue pending exceptions in exception_queue.
        virtual void handle_exception(std::exception_ptr);

        // Implementation of <handler> interface. <thread_pool> keeps track of empty_queue_guard messages in case of
        // dynamic task allocation.
        virtual bool handle_message(mdl::message_ptr);

        // Constructor helper, enqueues empty_queue_guard messages for all workers.
        void initialize_queues();

        typedef mdl::const_vector<thread_handler> pool_type;
        // Pool of all available <thread_handler>s.
        pool_type pool;
        // Iterator to the next handler in case of round_robin task assignment strategy.
        pool_type::iterator next_robin;

        std::mutex exception_queue_lock;
        // Queue holding exception_ptr to all exceptions thrown in the workers.
        std::queue<std::exception_ptr> exception_queue;

        std::mutex task_queue_lock;
        // Queue of messages to be assigned to workers in case of dynamic task assignment.
        std::queue<message_ptr> task_queue;

        void throw_if_nonempty();

        void stop_and_join();

        void send_message(message_ptr);

        unsigned processes;
        strategy task_assigning_strategy;

    public:
        /* Create a <thread_pool> with <processes> workers and <task_assigning_strategy> strategy.
         * @processes Number of worker threads to create. Defaults to std::thread::hardware_concurrency() or 1, if undefined.
         * @task_assigning_strategy Strategy of assigning tasks to the workers. Defaults to strategy::round_robin.
         */
        thread_pool(unsigned processes = helper::hw_concurrency(),
                    strategy task_assigning_strategy = strategy::round_robin) :
                processes(processes),
                task_assigning_strategy(task_assigning_strategy),
                pool(pool_type::make_indexed(processes, *this)),
                next_robin(pool.begin())
        {
            initialize_queues();
        }

        // Destructor. Joins all workers and blocks untill all the tasks are done.
        ~thread_pool(void)
        {
            stop_and_join();
        }

        // The number of workers (threads).
        mdl::const_accessor<unsigned> workers{processes};

        /* Execute the function asynchronously.
         * @fn Function to call.
         * @args... Function arguments.
         *
         * Reassembles the standard function std::async, with few exceptions.
         * First of all, the work will be run on one of the workers in the <thread_pool>.
         * Second, the arguments passed to the function have to be packed inside a lambda expression,
         *   which might cause problems with some complex or uncopyable types, so it's better to
         *   explicitly envelop all references with reference_wrappers, etc.
         *
         * @return std::future<T>, where T is the result type of Fn(Args...), which will hold the
         * result of the fn(args...) once finished.
         */
        template<typename Fn, typename... Args, typename T = typename std::result_of<Fn(Args...)>::type>
        std::future<T>
        async(Fn &&fn, Args &&... args)
        {
            std::shared_ptr<std::promise<T>> promise = std::make_shared<std::promise<T>>();
            auto lambda = [promise, fn, args...](void)
                { promise->set_value(fn(std::move(args)...)); };
            send_message(std::make_shared<post_call>(lambda));
            return promise->get_future();
        };

        /* Map all values from one range into another asynchronously.
         * @first Random access iterator pointing to the first element of the range (inkl.).
         * @last Random access iterator pointing to the last element of the range (excl.).
         * @output_first Random access iterator pointing to the first element of the output range.
         * @function Function to call on all elements of range [first, last), which results will
         *  be stored in [output_first, output_first + (last - first))
         *
         * Each task is created with an <async> call. One additional thread is spawned (using std::async)
         * to keep track of promise of completing the map operation.
         *
         * @return std::future that becomes fulfilled once all elements are mapped.
         */
        template<typename RandomAccessIteratorIn, typename RandomAccessIteratorOut, typename Fn>
        std::future<void>
        map(RandomAccessIteratorIn first, RandomAccessIteratorIn last, RandomAccessIteratorOut output_first, Fn function)
        {
            typedef typename std::iterator_traits<RandomAccessIteratorIn>::value_type value_type;
            typedef typename std::iterator_traits<RandomAccessIteratorOut>::value_type result_type;
            typedef typename std::result_of<Fn(value_type)>::type function_result_type;

            static_assert(std::is_convertible<function_result_type, result_type>::value, "Function return type not convertible to iterator value_type");

            // Count the distance between last and first:
            ptrdiff_t n = std::distance(first, last);
            if(n < 0) mdl_throw(make_ia_exception, "Invalid iterator range", "first, last", std::make_pair(first, last));
            // Create n async tasks for all elements in range:
            auto futures = std::make_shared<const_vector<std::future<bool>>>(n);
            for(size_t i = 0; first != last; ++first, ++output_first, ++i)
            {
                futures->at(i) = async([first, output_first, function] ()
                    {
                        *output_first = function(*first);
                        return true;
                    });
            }
            return std::async([futures]()
                                  {
                                      for(auto& job : (*futures))
                                          job.get();
                                      return;
                                  });
        }
    };
}

#endif //MDLUTILS_MULTITHREADING_THREAD_POOL_HPP
<|endoftext|>


<commit_before>#include <QUrl>
#include "kernel.h"
#include "errorobject.h"
#include "mastercatalog.h"
#include "connectorinterface.h"
#include "ilwisobjectconnector.h"
#include "catalogexplorer.h"
#include "catalogconnector.h"
#include "factory.h"
#include "abstractfactory.h"
#include "connectorfactory.h"

using namespace Ilwis;

IlwisObjectConnector::IlwisObjectConnector(const Ilwis::Resource &resource, bool, const IOOptions &) : _resource(resource), _binaryIsLoaded(false)
{
    const ConnectorFactory *factory = kernel()->factory<ConnectorFactory>("ConnectorFactory",resource);

    if ( factory && resource.url().isValid() &&
         resource.container().isValid() &&
         mastercatalog()->usesContainers(resource.url())){
         _incontainerconnector.reset(dynamic_cast<CatalogConnector *>(factory->createContainerConnector(Resource(resource.container().url(), itCATALOG))));
    }
}

IlwisObjectConnector::~IlwisObjectConnector()
{

}

IlwisTypes IlwisObjectConnector::type() const
{
    return _resource.ilwisType();
}

Resource &IlwisObjectConnector::source()
{
    return _resource;
}

const Resource& IlwisObjectConnector::source() const{
    return _resource;
}

bool IlwisObjectConnector::dataIsLoaded() const
{
    return _binaryIsLoaded;
}

UPCatalogConnector &IlwisObjectConnector::containerConnector(IlwisObject::ConnectorMode mode)
{
    if ( hasType(mode,IlwisObject::cmINPUT) && _incontainerconnector)
        return _incontainerconnector;
    else if ( hasType(mode ,IlwisObject::cmOUTPUT) && _outcontainerconnector)
        return _outcontainerconnector;
    if (!_incontainerconnector)
        throw ErrorObject(TR("Using uninitialized container"));

    return _incontainerconnector;
}

const UPCatalogConnector &IlwisObjectConnector::containerConnector(IlwisObject::ConnectorMode mode) const
{
    if ( hasType(mode,IlwisObject::cmINPUT) && _incontainerconnector)
        return _incontainerconnector;
    else if ( hasType(mode,IlwisObject::cmOUTPUT) && _outcontainerconnector)
        return _outcontainerconnector;
    if (!_incontainerconnector)
        throw ErrorObject(TR("Using uninitialized container"));

    return _incontainerconnector;
}
<commit_msg>guard against inifinte loops<commit_after>#include <QUrl>
#include "kernel.h"
#include "errorobject.h"
#include "mastercatalog.h"
#include "connectorinterface.h"
#include "ilwisobjectconnector.h"
#include "catalogexplorer.h"
#include "catalogconnector.h"
#include "factory.h"
#include "abstractfactory.h"
#include "connectorfactory.h"

using namespace Ilwis;

IlwisObjectConnector::IlwisObjectConnector(const Ilwis::Resource &resource, bool, const IOOptions &) : _resource(resource), _binaryIsLoaded(false)
{
    const ConnectorFactory *factory = kernel()->factory<ConnectorFactory>("ConnectorFactory",resource);

    if ( factory && resource.url().isValid() &&
         resource.container().isValid() &&
         mastercatalog()->usesContainers(resource.url()) &&
         resource.container() != resource.url()){ // else we get infinite recursion; should not happen and is probably corrupt resource but better safe then sorry
         _incontainerconnector.reset(dynamic_cast<CatalogConnector *>(factory->createContainerConnector(Resource(resource.container().url(), itCATALOG))));
    }
}

IlwisObjectConnector::~IlwisObjectConnector()
{

}

IlwisTypes IlwisObjectConnector::type() const
{
    return _resource.ilwisType();
}

Resource &IlwisObjectConnector::source()
{
    return _resource;
}

const Resource& IlwisObjectConnector::source() const{
    return _resource;
}

bool IlwisObjectConnector::dataIsLoaded() const
{
    return _binaryIsLoaded;
}

UPCatalogConnector &IlwisObjectConnector::containerConnector(IlwisObject::ConnectorMode mode)
{
    if ( hasType(mode,IlwisObject::cmINPUT) && _incontainerconnector)
        return _incontainerconnector;
    else if ( hasType(mode ,IlwisObject::cmOUTPUT) && _outcontainerconnector)
        return _outcontainerconnector;
    if (!_incontainerconnector)
        throw ErrorObject(TR("Using uninitialized container"));

    return _incontainerconnector;
}

const UPCatalogConnector &IlwisObjectConnector::containerConnector(IlwisObject::ConnectorMode mode) const
{
    if ( hasType(mode,IlwisObject::cmINPUT) && _incontainerconnector)
        return _incontainerconnector;
    else if ( hasType(mode,IlwisObject::cmOUTPUT) && _outcontainerconnector)
        return _outcontainerconnector;
    if (!_incontainerconnector)
        throw ErrorObject(TR("Using uninitialized container"));

    return _incontainerconnector;
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2008-2016 the MRtrix3 contributors
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/
 *
 * MRtrix 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.
 *
 * For more details, see www.mrtrix.org
 *
 */

#include <sstream>

#include "command.h"
#include "progressbar.h"
#include "algo/threaded_loop.h"
#include "image.h"
#include "math/SH.h"
#include "dwi/directions/predefined.h"
#include "filter/reslice.h"
#include "interp/cubic.h"

using namespace MR;
using namespace App;

#define DEFAULT_LUM_CR 0.3
#define DEFAULT_LUM_CG 0.5
#define DEFAULT_LUM_CB 0.2
#define DEFAULT_LUM_GAMMA 2.2

void usage ()
{
  AUTHOR = "Thijs Dhollander (thijs.dhollander@gmail.com)";

  COPYRIGHT =
    "Copyright (C) 2014 The Florey Institute of Neuroscience and Mental Health, Melbourne, Australia. "
    "This is free software; see the source for copying conditions. "
    "There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.";

  DESCRIPTION
    + "Generate FOD-based DEC maps, with optional panchromatic sharpening and/or luminance/perception correction."
    + "By default, the FOD-based DEC is weighted by the integral of the FOD. To weight by another scalar map, use the outputmap option. This option can also be used for panchromatic sharpening, e.g., by supplying a T1 (or other sensible) anatomical volume with a higher spatial resolution.";

  REFERENCES
    + "Dhollander T, Smith RE, Tournier JD, Jeurissen B, Connelly A. " // Internal
      "Time to move on: an FOD-based DEC map to replace DTI's trademark DEC FA. "
      "Proc Intl Soc Mag Reson Med, 2015, 23, 1027."
    + "Dhollander T, Raffelt D, Smith RE, Connelly A. " // Internal
      "Panchromatic sharpening of FOD-based DEC maps by structural T1 information. "
      "Proc Intl Soc Mag Reson Med, 2015, 23, 566.";

  ARGUMENTS
    + Argument ("input","The input FOD image (spherical harmonic coefficients).").type_image_in ()
    + Argument ("output","The output DEC image (weighted RGB triplets).").type_image_out ();

  OPTIONS
    + Option ("mask","Only perform DEC computation within the specified mask image.")
    + Argument ("image").type_image_in()

    + Option ("threshold","FOD amplitudes below the threshold value are considered zero.")
    + Argument ("value").type_float()

    + Option ("outputmap","Weight the computed DEC map by a provided outputmap. If the outputmap has a different grid, the DEC map is first resliced and renormalised. To achieve panchromatic sharpening, provide an image with a higher spatial resolution than the input FOD image; e.g., a T1 anatomical volume. Only the DEC is subject to the mask, so as to allow for partial colouring of the outputmap. \nDefault when this option is *not* provided: integral of input FOD, subject to the same mask/threshold as used for DEC computation.")
    + Argument ("image").type_image_in()

    + Option ("no-weight","Do not weight the DEC map (reslicing and renormalising still possible by explicitly providing the outputmap option as a template).")

    + Option ("lum","Correct for luminance/perception, using default values Cr,Cg,Cb = " + str(DEFAULT_LUM_CR, 2) + "," + str(DEFAULT_LUM_CG, 2) + "," + str(DEFAULT_LUM_CB, 2) + " and gamma = " + str(DEFAULT_LUM_GAMMA, 2) + " (*not* correcting is the theoretical equivalent of Cr,Cg,Cb = 1,1,1 and gamma = 2).")

    + Option ("lum-coefs","The coefficients Cr,Cg,Cb to correct for luminance/perception. \nNote: this implicitly switches on luminance/perception correction, using a default gamma = " + str(DEFAULT_LUM_GAMMA, 2) + " unless specified otherwise.")
    + Argument ("values").type_sequence_float()

    + Option ("lum-gamma","The gamma value to correct for luminance/perception. \nNote: this implicitly switches on luminance/perception correction, using a default Cr,Cg,Cb = " + str(DEFAULT_LUM_CR, 2) + "," + str(DEFAULT_LUM_CG, 2) + "," + str(DEFAULT_LUM_CB, 2) + " unless specified otherwise.")
    + Argument ("value").type_float();
}

typedef float value_type;
const value_type UNIT = 1.0 / std::sqrt(3.0);

class DecTransform {

  public:

  Eigen::MatrixXd sht;
  Eigen::Matrix<double, Eigen::Dynamic, 3> decs;
  double thresh;

  DecTransform (int lmax, const Eigen::Matrix<double, Eigen::Dynamic, 2>& dirs, double thresh) :
    sht (Math::SH::init_transform(dirs, lmax)),
    decs (Math::SH::spherical2cartesian(dirs).cwiseAbs()),
    thresh (thresh) { }

};

class DecComputer {

  private:

  const DecTransform& dectrans;
  Image<bool> mask_img;
  Image<value_type> int_img;
  Eigen::VectorXd amps, fod;
  Eigen::RowVector3d dec;
  double ampsum;
  double decnorm;

  public:

  DecComputer (const DecTransform& dectrans, Image<bool>& mask_img, Image<value_type>& int_img) :
    dectrans (dectrans),
    mask_img (mask_img),
    int_img (int_img),
    amps (dectrans.sht.rows()),
    fod (dectrans.sht.cols()) { }

  void operator() (Image<value_type>& fod_img, Image<value_type>& dec_img) {

    if (mask_img.valid()) {
      assign_pos_of(fod_img, 0, 3).to(mask_img);
      if (!mask_img.value()) {
        dec_img.row(3) = UNIT;
        return;
      }
    }

    fod = fod_img.row(3);
    amps.noalias() = dectrans.sht * fod;

    dec.setZero();
    ampsum = 0.0;
    for (ssize_t i = 0; i < amps.rows(); i++) {
      if (!std::isnan(dectrans.thresh) && amps(i) < dectrans.thresh)
        continue;
      dec += Eigen::Vector3(dectrans.decs.row(i)) * amps(i);
      ampsum += amps(i);
    }
    dec = dec.cwiseMax(0.0);
    ampsum = std::max(ampsum, 0.0);

    decnorm = dec.norm();

    if (decnorm == 0.0)
      dec_img.row(3) = UNIT;
    else
      dec_img.row(3) = dec / decnorm;

    if (int_img.valid()) {
      assign_pos_of(fod_img, 0, 3).to(int_img);
      int_img.value() = (ampsum / amps.rows()) * 4.0 * Math::pi;
    }

  }

};

class DecWeighter {

  private:

  Eigen::Array<value_type, 3, 1> coefs;
  value_type gamma;
  Image<value_type> w_img;
  value_type grey;
  Eigen::Array<value_type, 3, 1> dec;
  value_type w;
  value_type br;

  public:

  DecWeighter (Eigen::Matrix<value_type, 3, 1> coefs, value_type gamma, Image<value_type>& w_img) :
    coefs (coefs),
    gamma (gamma),
    w_img (w_img),
    grey (1.0 / std::pow(coefs.sum(), 1.0 / gamma)) {}

  void operator() (Image<value_type>& dec_img) {

    w = 1.0;
    if (w_img.valid()) {
      assign_pos_of(dec_img, 0, 3).to(w_img);
      w = w_img.value();
      if (w <= 0.0) {
        for (auto l = Loop (3) (dec_img); l; ++l)
          dec_img.value() = 0.0;
        return;
      }
    }

    for (auto l = Loop (3) (dec_img); l; ++l)
      dec[dec_img.index(3)] = dec_img.value();

    dec = dec.cwiseMax(0.0);

    br = std::pow((dec.pow(gamma) * coefs).sum() , 1.0 / gamma);

    if (br == 0.0)
      dec.fill(grey * w);
    else
      dec = dec * (w / br);

    for (auto l = Loop (3) (dec_img); l; ++l)
       dec_img.value() = dec[dec_img.index(3)];

  }

};

void run () {

  auto fod_hdr = Header::open(argument[0]);
  Math::SH::check(fod_hdr);

  auto mask_hdr = Header();
  auto optm = get_options("mask");
  if (optm.size()) {
    mask_hdr = Header::open(optm[0][0]);
    check_dimensions (mask_hdr, fod_hdr, 0, 3);
  }

  float thresh = get_option_value ("threshold", NAN);

  bool needtolum = false;
  Eigen::Array<value_type, 3, 1> coefs (1.0, 1.0, 1.0);
  value_type gamma = 2.0;
  auto optlc = get_options("lum-coefs");
  auto optlg = get_options("lum-gamma");
  if (get_options("lum").size() || optlc.size() || optlg.size()) {
    needtolum = true;
    coefs << DEFAULT_LUM_CR , DEFAULT_LUM_CG , DEFAULT_LUM_CB; gamma = DEFAULT_LUM_GAMMA;
    if (optlc.size()) {
      auto lc = parse_floats(optlc[0][0]);
      if (lc.size() != 3)
        throw Exception ("expecting exactly 3 coefficients for the lum-coefs option, provided as a comma-separated list Cr,Cg,Cb ; e.g., " + str(DEFAULT_LUM_CR, 2) + "," + str(DEFAULT_LUM_CG, 2) + "," + str(DEFAULT_LUM_CB, 2) + "");
      coefs(0) = lc[0]; coefs(1) = lc[1]; coefs(2) = lc[2];
    }
    if (optlg.size())
      gamma = optlg[0][0];
  }

  bool needtoslice = false;
  auto map_hdr = Header();
  auto opto = get_options ("outputmap");
  if (opto.size()) {
    map_hdr = Header::open(opto[0][0]);
    if (!dimensions_match(map_hdr, fod_hdr, 0, 3) ||
        !spacings_match(map_hdr, fod_hdr, 0, 3) ||
        !map_hdr.transform().isApprox(map_hdr.transform(),1e-42))
      needtoslice = true;
  }

  auto out_img = Image<value_type>();
  auto w_img = Image<value_type>();

  {
    auto dec_img = Image<value_type>();

    {
      auto fod_img = fod_hdr.get_image<value_type>().with_direct_io(3);

      auto dec_hdr = Header(fod_img);
      dec_hdr.ndim() = 4;
      dec_hdr.size(3) = 3;
      Stride::set (dec_hdr, Stride::contiguous_along_axis (3, dec_hdr));
      dec_img = Image<value_type>::scratch(dec_hdr,"DEC map");

      Eigen::Matrix<double, 1281, 2> dirs = DWI::Directions::tesselation_1281();

      auto mask_img = Image<bool>();
      if (mask_hdr.valid())
        mask_img = mask_hdr.get_image<bool>();

      if (!get_options("no-weight").size() && !map_hdr) {
        auto int_hdr = Header(dec_img);
        int_hdr.size(3) = 1;
        w_img = Image<value_type>::scratch(int_hdr,"FOD integral map");
      }

      ThreadedLoop ("computing colours", fod_img, 0, 3)
        .run (DecComputer (DecTransform (Math::SH::LforN(fod_img.size(3)), dirs, thresh), mask_img, w_img), fod_img, dec_img);
    }

    auto out_hdr = map_hdr.valid() ? Header(map_hdr) : Header(dec_img);
    out_hdr.datatype() = DataType::Float32;
    out_hdr.ndim() = 4;
    out_hdr.size(3) = 3;
    Stride::set (out_hdr, Stride::contiguous_along_axis (3, out_hdr));
    out_img = Image<value_type>::create(argument[1],out_hdr);

    if (needtoslice)
      Filter::reslice<Interp::Cubic> (dec_img, out_img, Adapter::NoTransform, Adapter::AutoOverSample, UNIT);
    else
      copy (dec_img, out_img);
  }

  if (!get_options("no-weight").size() && map_hdr.valid())
    w_img = map_hdr.get_image<value_type>();

  if (w_img.valid() || needtolum || needtoslice)
    ThreadedLoop ("(re)weighting", out_img, 0, 3, 2)
      .run (DecWeighter (coefs, gamma, w_img), out_img);

}

<commit_msg>dwi2fod: fix assign to row issue<commit_after>/*
 * Copyright (c) 2008-2016 the MRtrix3 contributors
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/
 *
 * MRtrix 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.
 *
 * For more details, see www.mrtrix.org
 *
 */

#include <sstream>

#include "command.h"
#include "progressbar.h"
#include "algo/threaded_loop.h"
#include "image.h"
#include "math/SH.h"
#include "dwi/directions/predefined.h"
#include "filter/reslice.h"
#include "interp/cubic.h"

using namespace MR;
using namespace App;

#define DEFAULT_LUM_CR 0.3
#define DEFAULT_LUM_CG 0.5
#define DEFAULT_LUM_CB 0.2
#define DEFAULT_LUM_GAMMA 2.2

void usage ()
{
  AUTHOR = "Thijs Dhollander (thijs.dhollander@gmail.com)";

  COPYRIGHT =
    "Copyright (C) 2014 The Florey Institute of Neuroscience and Mental Health, Melbourne, Australia. "
    "This is free software; see the source for copying conditions. "
    "There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.";

  DESCRIPTION
    + "Generate FOD-based DEC maps, with optional panchromatic sharpening and/or luminance/perception correction."
    + "By default, the FOD-based DEC is weighted by the integral of the FOD. To weight by another scalar map, use the outputmap option. This option can also be used for panchromatic sharpening, e.g., by supplying a T1 (or other sensible) anatomical volume with a higher spatial resolution.";

  REFERENCES
    + "Dhollander T, Smith RE, Tournier JD, Jeurissen B, Connelly A. " // Internal
      "Time to move on: an FOD-based DEC map to replace DTI's trademark DEC FA. "
      "Proc Intl Soc Mag Reson Med, 2015, 23, 1027."
    + "Dhollander T, Raffelt D, Smith RE, Connelly A. " // Internal
      "Panchromatic sharpening of FOD-based DEC maps by structural T1 information. "
      "Proc Intl Soc Mag Reson Med, 2015, 23, 566.";

  ARGUMENTS
    + Argument ("input","The input FOD image (spherical harmonic coefficients).").type_image_in ()
    + Argument ("output","The output DEC image (weighted RGB triplets).").type_image_out ();

  OPTIONS
    + Option ("mask","Only perform DEC computation within the specified mask image.")
    + Argument ("image").type_image_in()

    + Option ("threshold","FOD amplitudes below the threshold value are considered zero.")
    + Argument ("value").type_float()

    + Option ("outputmap","Weight the computed DEC map by a provided outputmap. If the outputmap has a different grid, the DEC map is first resliced and renormalised. To achieve panchromatic sharpening, provide an image with a higher spatial resolution than the input FOD image; e.g., a T1 anatomical volume. Only the DEC is subject to the mask, so as to allow for partial colouring of the outputmap. \nDefault when this option is *not* provided: integral of input FOD, subject to the same mask/threshold as used for DEC computation.")
    + Argument ("image").type_image_in()

    + Option ("no-weight","Do not weight the DEC map (reslicing and renormalising still possible by explicitly providing the outputmap option as a template).")

    + Option ("lum","Correct for luminance/perception, using default values Cr,Cg,Cb = " + str(DEFAULT_LUM_CR, 2) + "," + str(DEFAULT_LUM_CG, 2) + "," + str(DEFAULT_LUM_CB, 2) + " and gamma = " + str(DEFAULT_LUM_GAMMA, 2) + " (*not* correcting is the theoretical equivalent of Cr,Cg,Cb = 1,1,1 and gamma = 2).")

    + Option ("lum-coefs","The coefficients Cr,Cg,Cb to correct for luminance/perception. \nNote: this implicitly switches on luminance/perception correction, using a default gamma = " + str(DEFAULT_LUM_GAMMA, 2) + " unless specified otherwise.")
    + Argument ("values").type_sequence_float()

    + Option ("lum-gamma","The gamma value to correct for luminance/perception. \nNote: this implicitly switches on luminance/perception correction, using a default Cr,Cg,Cb = " + str(DEFAULT_LUM_CR, 2) + "," + str(DEFAULT_LUM_CG, 2) + "," + str(DEFAULT_LUM_CB, 2) + " unless specified otherwise.")
    + Argument ("value").type_float();
}

typedef float value_type;
constexpr value_type UNIT = 1.0 / std::sqrt(3.0);

class DecTransform {

  public:

  Eigen::MatrixXd sht;
  Eigen::Matrix<double, Eigen::Dynamic, 3> decs;
  double thresh;

  DecTransform (int lmax, const Eigen::Matrix<double, Eigen::Dynamic, 2>& dirs, double thresh) :
    sht (Math::SH::init_transform(dirs, lmax)),
    decs (Math::SH::spherical2cartesian(dirs).cwiseAbs()),
    thresh (thresh) { }

};

class DecComputer {

  private:

  const DecTransform& dectrans;
  Image<bool> mask_img;
  Image<value_type> int_img;
  Eigen::VectorXd amps, fod;

  public:

  DecComputer (const DecTransform& dectrans, Image<bool>& mask_img, Image<value_type>& int_img) :
    dectrans (dectrans),
    mask_img (mask_img),
    int_img (int_img),
    amps (dectrans.sht.rows()),
    fod (dectrans.sht.cols()) { }

  void operator() (Image<value_type>& fod_img, Image<value_type>& dec_img) {

    if (mask_img.valid()) {
      assign_pos_of(fod_img, 0, 3).to(mask_img);
      if (!mask_img.value()) {
        dec_img.row(3) = UNIT;
        return;
      }
    }

    fod = fod_img.row(3);
    amps.noalias() = dectrans.sht * fod;

    Eigen::Vector3d dec = Eigen::Vector3d::Zero();
    double ampsum = 0.0;
    for (ssize_t i = 0; i < amps.rows(); i++) {
      if (!std::isnan(dectrans.thresh) && amps(i) < dectrans.thresh)
        continue;
      dec += dectrans.decs.row(i).transpose() * amps(i);
      ampsum += amps(i);
    }
    dec = dec.cwiseMax(0.0);
    ampsum = std::max(ampsum, 0.0);

    double decnorm = dec.norm();

    if (decnorm == 0.0)
      dec_img.row(3) = UNIT;
    else
      dec_img.row(3) = dec / decnorm;

    if (int_img.valid()) {
      assign_pos_of(fod_img, 0, 3).to(int_img);
      int_img.value() = (ampsum / amps.rows()) * 4.0 * Math::pi;
    }

  }

};

class DecWeighter {

  private:

  Eigen::Array<value_type, 3, 1> coefs;
  value_type gamma;
  Image<value_type> w_img;
  value_type grey;

  public:

  DecWeighter (Eigen::Matrix<value_type, 3, 1> coefs, value_type gamma, Image<value_type>& w_img) :
    coefs (coefs),
    gamma (gamma),
    w_img (w_img),
    grey (1.0 / std::pow(coefs.sum(), 1.0 / gamma)) {}

  void operator() (Image<value_type>& dec_img) {

    value_type w = 1.0;
    if (w_img.valid()) {
      assign_pos_of(dec_img, 0, 3).to(w_img);
      w = w_img.value();
      if (w <= 0.0) {
        for (auto l = Loop (3) (dec_img); l; ++l)
          dec_img.value() = 0.0;
        return;
      }
    }

    Eigen::Array<value_type, 3, 1> dec;
    for (auto l = Loop (3) (dec_img); l; ++l)
      dec[dec_img.index(3)] = dec_img.value();

    dec = dec.cwiseMax(0.0);

    value_type br = std::pow((dec.pow(gamma) * coefs).sum() , 1.0 / gamma);

    if (br == 0.0)
      dec.fill(grey * w);
    else
      dec = dec * (w / br);

    for (auto l = Loop (3) (dec_img); l; ++l)
       dec_img.value() = dec[dec_img.index(3)];

  }

};

void run () {

  auto fod_hdr = Header::open(argument[0]);
  Math::SH::check(fod_hdr);

  auto mask_hdr = Header();
  auto optm = get_options("mask");
  if (optm.size()) {
    mask_hdr = Header::open(optm[0][0]);
    check_dimensions (mask_hdr, fod_hdr, 0, 3);
  }

  float thresh = get_option_value ("threshold", NAN);

  bool needtolum = false;
  Eigen::Array<value_type, 3, 1> coefs (1.0, 1.0, 1.0);
  value_type gamma = 2.0;
  auto optlc = get_options("lum-coefs");
  auto optlg = get_options("lum-gamma");
  if (get_options("lum").size() || optlc.size() || optlg.size()) {
    needtolum = true;
    coefs << DEFAULT_LUM_CR , DEFAULT_LUM_CG , DEFAULT_LUM_CB; gamma = DEFAULT_LUM_GAMMA;
    if (optlc.size()) {
      auto lc = parse_floats(optlc[0][0]);
      if (lc.size() != 3)
        throw Exception ("expecting exactly 3 coefficients for the lum-coefs option, provided as a comma-separated list Cr,Cg,Cb ; e.g., " + str(DEFAULT_LUM_CR, 2) + "," + str(DEFAULT_LUM_CG, 2) + "," + str(DEFAULT_LUM_CB, 2) + "");
      coefs(0) = lc[0]; coefs(1) = lc[1]; coefs(2) = lc[2];
    }
    if (optlg.size())
      gamma = optlg[0][0];
  }

  bool needtoslice = false;
  auto map_hdr = Header();
  auto opto = get_options ("outputmap");
  if (opto.size()) {
    map_hdr = Header::open(opto[0][0]);
    if (!dimensions_match(map_hdr, fod_hdr, 0, 3) ||
        !spacings_match(map_hdr, fod_hdr, 0, 3) ||
        !map_hdr.transform().isApprox(map_hdr.transform(),1e-42))
      needtoslice = true;
  }

  auto out_img = Image<value_type>();
  auto w_img = Image<value_type>();

  {
    auto dec_img = Image<value_type>();

    {
      auto fod_img = fod_hdr.get_image<value_type>().with_direct_io(3);

      auto dec_hdr = Header(fod_img);
      dec_hdr.ndim() = 4;
      dec_hdr.size(3) = 3;
      Stride::set (dec_hdr, Stride::contiguous_along_axis (3, dec_hdr));
      dec_img = Image<value_type>::scratch(dec_hdr,"DEC map");

      Eigen::Matrix<double, 1281, 2> dirs = DWI::Directions::tesselation_1281();

      auto mask_img = Image<bool>();
      if (mask_hdr.valid())
        mask_img = mask_hdr.get_image<bool>();

      if (!get_options("no-weight").size() && !map_hdr) {
        auto int_hdr = Header(dec_img);
        int_hdr.size(3) = 1;
        w_img = Image<value_type>::scratch(int_hdr,"FOD integral map");
      }

      ThreadedLoop ("computing colours", fod_img, 0, 3)
        .run (DecComputer (DecTransform (Math::SH::LforN(fod_img.size(3)), dirs, thresh), mask_img, w_img), fod_img, dec_img);
    }

    auto out_hdr = map_hdr.valid() ? Header(map_hdr) : Header(dec_img);
    out_hdr.datatype() = DataType::Float32;
    out_hdr.ndim() = 4;
    out_hdr.size(3) = 3;
    Stride::set (out_hdr, Stride::contiguous_along_axis (3, out_hdr));
    out_img = Image<value_type>::create(argument[1],out_hdr);

    if (needtoslice)
      Filter::reslice<Interp::Cubic> (dec_img, out_img, Adapter::NoTransform, Adapter::AutoOverSample, UNIT);
    else
      copy (dec_img, out_img);
  }

  if (!get_options("no-weight").size() && map_hdr.valid())
    w_img = map_hdr.get_image<value_type>();

  if (w_img.valid() || needtolum || needtoslice)
    ThreadedLoop ("(re)weighting", out_img, 0, 3, 2)
      .run (DecWeighter (coefs, gamma, w_img), out_img);

}

<|endoftext|>


<commit_before>#include <ncurses.h>
#include <locale>
#include <chrono>
#include <thread>
#include <string>
#include <vector>
#include <regex>
#include <iostream>
#include <stdexcept>
using namespace std::literals::string_literals;
using std::string;
using std::tuple;
using std::vector;

bool is_utf8_first(uint8_t ch) {
  if ((ch & 0x80) == 0) {
    return true;  // ascii (0b0.......)
  }

  if ((ch & 0xC0) == 0x80) {
    return false; // 0b10......
  }

  if ((ch & 0xFE) == 0xFE) {
    return false; // 0b1111111.
  }

  return true;    // 0b11......
}

bool is_utf8_cont(uint8_t ch) {
  return (ch & 0xC0) == 0x80;
}

size_t get_utf8_char_length(uint8_t ch)
{
  if (!is_utf8_first(ch)) {
    throw std::runtime_error(string(__FUNCTION__) + ": the first byte is not UTF8");
  }

	for (int i = 0; i < 6; ++i) {
		if ((ch & (0x01 << (7 - i))) == 0) {
	    return std::max(1, i);
		}
	}

  throw std::runtime_error(string(__FUNCTION__) + ": unreachable");
}

std::string get_utf8_char() {
  std::array<uint8_t, 6> buf{0};

  auto ch0 = static_cast<uint8_t>(::getch() & 0x000000FF);
  size_t len = get_utf8_char_length(ch0);
  buf[0] = ch0;

  for (size_t i = 1; i < len; ++i) {
    auto ch = static_cast<uint8_t>(::getch() & 0x000000FF);
    if (!is_utf8_cont(ch)) {
      throw std::runtime_error(string(__FUNCTION__) + ": wrong byte exists");
    }
    buf[i] = ch;
  }

  return std::string(buf.data(), buf.data() + len);
}

void pop_back_utf8(std::string& str)
{
  if (str.empty())
    return;

  for (size_t len = str.size() - 1; len >= 0; --len) {
    if (!is_utf8_cont(str[len])) {
      str.resize(len);
      return;
    }
  }
}

class Coco {
  enum class Status {
    Selected, Escaped, Continue,
  };

  std::vector<std::string> const& lines;
  std::string query;
  size_t cursor = 0;

public:
  Coco(std::vector<std::string> const& lines)
    : lines(lines)
  {
  }

  std::tuple<bool, std::string> run();

private:
  void render_screen();
  Status handle_key_event();
};

void Coco::render_screen()
{
  std::string query_str = "QUERY> " + query;

  ::werase(stdscr);

 for (int y = 0; y < lines.size(); ++y) {
    mvwaddstr(stdscr, y+1, 1, lines[y].c_str());
  }
  mvwaddstr(stdscr, cursor+1, 0, ">");

  mvwaddstr(stdscr, 0, 0, query_str.c_str());

  ::wrefresh(stdscr);
}

auto Coco::handle_key_event() -> Status
{
  int ch = ::getch();
  if (ch == 10) { // 10 = enter
    return Status::Selected;
  }
  else if (ch == 27) {
    ::nodelay(stdscr, true);
    int ch = ::getch();
    if (ch == -1) { // Escape
      return Status::Escaped;
    }
    ::ungetch(ch);
    ::nodelay(stdscr, false);
    return Status::Continue;
  }
  else if (ch == KEY_UP) {
    if (cursor > 0) {
      cursor--;
    }
    return Status::Continue;
  }
  else if (ch == KEY_DOWN) {
    if (cursor < lines.size() - 1) {
      cursor++;
    }
    return Status::Continue;
  }
  else if (ch == 127) {  // 127 = backspace
    if (!query.empty()) {
      pop_back_utf8(query);
    }
    return Status::Continue;
  }
  else if (is_utf8_first(ch & 0xFF)) { // utf-8 character
    ::ungetch(ch);
    auto ch = get_utf8_char();
    query += ch;
    return Status::Continue;
  }

  throw std::logic_error(std::string(__FUNCTION__) + ": unreachable");
}

std::tuple<bool, std::string> Coco::run()
{
  render_screen();

  while (true) {
    auto result = handle_key_event();
    if (result == Status::Selected) {
      return std::make_tuple(true, lines[cursor]);
    } else if (result == Status::Escaped) {
      break;
    }
    render_screen();
  }

  return std::make_tuple(false, ""s);
}

class Ncurses {
public:
  Ncurses() {
    freopen("/dev/tty", "rw", stdin);
    ::initscr();
    ::noecho();
    ::cbreak();
    ::keypad(stdscr, true);
    ::ESCDELAY = 25;
  }
  ~Ncurses() {
    ::endwin();
  }
};

int main()
{
  std::setlocale(LC_ALL, "");

  try {
    // read lines from stdin.
    std::regex ansi(R"(\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K])");
    std::vector<std::string> lines;
    for (std::string line; std::getline(std::cin, line);) {
      lines.push_back(std::regex_replace(line, ansi, ""));
    }

    bool is_selected;
    std::string selection;
    {
      Ncurses ncurses;
      Coco coco{lines};
      std::tie(is_selected, selection) = coco.run();
    }

    if (is_selected) {
      std::cout << selection << std::endl;
    }
    return 0;
  }
  catch (std::exception& e) {
    std::cerr << "An error is thrown: " << e.what() << std::endl;
    return -1;
  }
}<commit_msg>support query<commit_after>#include <ncurses.h>
#include <locale>
#include <chrono>
#include <thread>
#include <string>
#include <vector>
#include <regex>
#include <iostream>
#include <stdexcept>
using namespace std::literals::string_literals;
using std::string;
using std::tuple;
using std::vector;

bool is_utf8_first(uint8_t ch) {
  if ((ch & 0x80) == 0) {
    return true;  // ascii (0b0.......)
  }

  if ((ch & 0xC0) == 0x80) {
    return false; // 0b10......
  }

  if ((ch & 0xFE) == 0xFE) {
    return false; // 0b1111111.
  }

  return true;    // 0b11......
}

bool is_utf8_cont(uint8_t ch) {
  return (ch & 0xC0) == 0x80;
}

size_t get_utf8_char_length(uint8_t ch)
{
  if (!is_utf8_first(ch)) {
    throw std::runtime_error(string(__FUNCTION__) + ": the first byte is not UTF8");
  }

	for (int i = 0; i < 6; ++i) {
		if ((ch & (0x01 << (7 - i))) == 0) {
	    return std::max(1, i);
		}
	}

  throw std::runtime_error(string(__FUNCTION__) + ": unreachable");
}

std::string get_utf8_char() {
  std::array<uint8_t, 6> buf{0};

  auto ch0 = static_cast<uint8_t>(::getch() & 0x000000FF);
  size_t len = get_utf8_char_length(ch0);
  buf[0] = ch0;

  for (size_t i = 1; i < len; ++i) {
    auto ch = static_cast<uint8_t>(::getch() & 0x000000FF);
    if (!is_utf8_cont(ch)) {
      throw std::runtime_error(string(__FUNCTION__) + ": wrong byte exists");
    }
    buf[i] = ch;
  }

  return std::string(buf.data(), buf.data() + len);
}

void pop_back_utf8(std::string& str)
{
  if (str.empty())
    return;

  for (size_t len = str.size() - 1; len >= 0; --len) {
    if (!is_utf8_cont(str[len])) {
      str.resize(len);
      return;
    }
  }
}

class Coco {
  enum class Status {
    Selected, Escaped, Continue,
  };

  std::vector<std::string> const& lines;
  std::vector<std::string> filtered;
  std::string query;
  size_t cursor = 0;

public:
  Coco(std::vector<std::string> const& lines)
    : lines(lines), filtered(lines)
  {
  }

  std::tuple<bool, std::string> run();

private:
  void render_screen();
  Status handle_key_event();

  void filter_by_query();
};

void Coco::render_screen()
{
  std::string query_str = "QUERY> " + query;

  ::werase(stdscr);

 for (size_t y = 0; y < filtered.size(); ++y) {
    mvwaddstr(stdscr, y+1, 1, filtered[y].c_str());
  }
  mvwaddstr(stdscr, cursor+1, 0, ">");

  mvwaddstr(stdscr, 0, 0, query_str.c_str());

  ::wrefresh(stdscr);
}

auto Coco::handle_key_event() -> Status
{
  int ch = ::getch();
  if (ch == 10) { // 10 = enter
    return Status::Selected;
  }
  else if (ch == 27) {
    ::nodelay(stdscr, true);
    int ch = ::getch();
    if (ch == -1) { // Escape
      return Status::Escaped;
    }
    ::ungetch(ch);
    ::nodelay(stdscr, false);
    return Status::Continue;
  }
  else if (ch == KEY_UP) {
    if (cursor > 0) {
      cursor--;
    }
    return Status::Continue;
  }
  else if (ch == KEY_DOWN) {
    if (cursor < filtered.size() - 1) {
      cursor++;
    }
    return Status::Continue;
  }
  else if (ch == 127) {  // 127 = backspace
    if (!query.empty()) {
      pop_back_utf8(query);
      filter_by_query();
    }
    return Status::Continue;
  }
  else if (is_utf8_first(ch & 0xFF)) { // utf-8 character
    ::ungetch(ch);
    auto ch = get_utf8_char();
    query += ch;
    filter_by_query();
    return Status::Continue;
  }

  throw std::logic_error(std::string(__FUNCTION__) + ": unreachable");
}

std::tuple<bool, std::string> Coco::run()
{
  render_screen();

  while (true) {
    auto result = handle_key_event();
    if (result == Status::Selected) {
      return std::make_tuple(true, filtered[cursor]);
    } else if (result == Status::Escaped) {
      break;
    }
    render_screen();
  }

  return std::make_tuple(false, ""s);
}

void Coco::filter_by_query()
{
  cursor = 0;

  if (query.empty()) {
    filtered = lines;
  }
  else {
    std::regex re(query);
    filtered.resize(0);
    for (auto&& line: lines) {
      if (std::regex_search(line, re)) {
        filtered.push_back(line);
      }
    }
  }
}

class Ncurses {
public:
  Ncurses() {
    freopen("/dev/tty", "rw", stdin);
    ::initscr();
    ::noecho();
    ::cbreak();
    ::keypad(stdscr, true);
    ::ESCDELAY = 25;
  }
  ~Ncurses() {
    ::endwin();
  }
};

int main()
{
  std::setlocale(LC_ALL, "");

  try {
    // read lines from stdin.
    std::regex ansi(R"(\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K])");
    std::vector<std::string> lines;
    for (std::string line; std::getline(std::cin, line);) {
      lines.push_back(std::regex_replace(line, ansi, ""));
    }

    bool is_selected;
    std::string selection;
    {
      Ncurses ncurses;
      Coco coco{lines};
      std::tie(is_selected, selection) = coco.run();
    }

    if (is_selected) {
      std::cout << selection << std::endl;
    }
    return 0;
  }
  catch (std::exception& e) {
    std::cerr << "An error is thrown: " << e.what() << std::endl;
    return -1;
  }
}<|endoftext|>


<commit_before>// Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at
// the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights
// reserved. See file COPYRIGHT for details.
//
// This file is part of the MFEM library. For more information and source code
// availability see http://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License (as published by the Free
// Software Foundation) version 2.1 dated February 1999.

#ifndef MFEM_MEM_MANAGER
#define MFEM_MEM_MANAGER

#include "globals.hpp"

namespace mfem
{

// Implementation of MFEM's lightweight host/device memory manager designed
// to work seamlessly with the okina device kernel interface.

/// The memory manager class
class MemoryManager
{
public:
   MemoryManager();
   ~MemoryManager();

   /// Adds an address in the map
   void *Insert(void *ptr, const std::size_t bytes);

   /// Remove the address from the map, as well as all the address' aliases
   void *Erase(void *ptr);

   /// Return a host or device address, coresponding to the Device::mode
   void *Ptr(void *ptr);
   const void *Ptr(const void *ptr);

   /// Data will be pushed/pulled before the copy happens on the H or the D
   void* Memcpy(void *dst, const void *src,
                std::size_t bytes, const bool async = false);

   /// Return the bytes of the memory region which base address is ptr
   std::size_t Bytes(const void *ptr);

   /// Return true if the registered pointer is on the host side
   bool IsOnHost(const void *ptr);

   /// Return true if the pointer has been registered
   bool IsKnown(const void *ptr);

   /// Return true if the pointer is an alias inside a registered memory region
   bool IsAlias(const void *ptr);

   /// Push the data to the device
   void Push(const void *ptr, const std::size_t bytes =0);

   /// Pull the data from the device
   void Pull(const void *ptr, const std::size_t bytes =0);

   /// Return the coresponding device pointer of ptr, allocating and moving the
   /// data if needed (used in OccaPtr)
   void *GetDevicePtr(const void *ptr);

   /// Registers external host pointer in the memory manager. The mm will manage
   /// the corresponding device pointer (but not the provided host pointer).
   template<class T>
   void RegisterHostPtr(T *ptr_host, const std::size_t size)
   {
      Insert(ptr_host, size*sizeof(T));
#ifdef MFEM_DEBUG
      RegisterCheck(ptr_host);
#endif
   }

   /// Registers external host and device pointers in the memory manager.
   template<class T>
   void RegisterHostAndDevicePtr(T *ptr_host, T *ptr_device,
                                 const std::size_t size, const bool host)
   {
      RegisterHostPtr(ptr_host, size);
      SetHostDevicePtr(ptr_host, ptr_device, host);
   }

   /// Set the host h_ptr, device d_ptr and mode host of the memory region just
   /// been registered with h_ptr (see RegisterHostAndDevicePtr)
   void SetHostDevicePtr(void *h_ptr, void *d_ptr, const bool host);

   /// Unregisters the host pointer from the memory manager. To be used with
   /// memory not allocated by the memory manager.
   template<class T>
   void UnregisterHostPtr(T *ptr) { Erase(ptr); }

   /// Check if pointer has been registered in the memory manager
   void RegisterCheck(void *ptr);

   /// Prints all pointers known by the memory manager
   void PrintPtrs(void);

   /// Copies all memory to the current memory space
   void GetAll(void);
};

extern bool mm_destroyed;
extern MemoryManager mm;

/// Main malloc template function. Allocates n*size bytes and returns a
/// pointer to the allocated memory.
template<class T>
inline T *New(const std::size_t n)
{ return static_cast<T*>(mm.Insert(new T[n], n*sizeof(T))); }

/// Frees the memory space pointed to by ptr, which must have been returned
/// by a previous call to mm::New.
template<class T>
inline void Delete(T *ptr)
{
   static_assert(!std::is_void<T>::value, "Cannot Delete a void pointer. "
                 "Explicitly provide the correct type as a template parameter.");
   if (!ptr) { return; }
   delete [] ptr;
   if (mm_destroyed) { return; }
   mm.Erase(ptr);
}

/// Return a host or device address coresponding to current memory space
template <class T>
inline T *Ptr(T *a) { return static_cast<T*>(mm.Ptr(a)); }

/// Data will be pushed/pulled before the copy happens on the H or the D
inline void* Memcpy(void *dst, const void *src,
                    std::size_t bytes, const bool async = false)
{ return mm.Memcpy(dst, src, bytes, async); }

/// Push the data to the device
inline void Push(const void *ptr, const std::size_t bytes =0)
{ return mm.Push(ptr, bytes); }

/// Pull the data from the device
inline void Pull(const void *ptr, const std::size_t bytes =0)
{ return mm.Pull(ptr, bytes); }

} // namespace mfem

#endif // MFEM_MEM_MANAGER
<commit_msg>Remove the reference to mm in the comments<commit_after>// Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at
// the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights
// reserved. See file COPYRIGHT for details.
//
// This file is part of the MFEM library. For more information and source code
// availability see http://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License (as published by the Free
// Software Foundation) version 2.1 dated February 1999.

#ifndef MFEM_MEM_MANAGER
#define MFEM_MEM_MANAGER

#include "globals.hpp"

namespace mfem
{

// Implementation of MFEM's lightweight host/device memory manager designed
// to work seamlessly with the okina device kernel interface.

/// The memory manager class
class MemoryManager
{
public:
   MemoryManager();
   ~MemoryManager();

   /// Adds an address in the map
   void *Insert(void *ptr, const std::size_t bytes);

   /// Remove the address from the map, as well as all the address' aliases
   void *Erase(void *ptr);

   /// Return a host or device address, coresponding to the Device::mode
   void *Ptr(void *ptr);
   const void *Ptr(const void *ptr);

   /// Data will be pushed/pulled before the copy happens on the H or the D
   void* Memcpy(void *dst, const void *src,
                std::size_t bytes, const bool async = false);

   /// Return the bytes of the memory region which base address is ptr
   std::size_t Bytes(const void *ptr);

   /// Return true if the registered pointer is on the host side
   bool IsOnHost(const void *ptr);

   /// Return true if the pointer has been registered
   bool IsKnown(const void *ptr);

   /// Return true if the pointer is an alias inside a registered memory region
   bool IsAlias(const void *ptr);

   /// Push the data to the device
   void Push(const void *ptr, const std::size_t bytes =0);

   /// Pull the data from the device
   void Pull(const void *ptr, const std::size_t bytes =0);

   /// Return the coresponding device pointer of ptr, allocating and moving the
   /// data if needed (used in OccaPtr)
   void *GetDevicePtr(const void *ptr);

   /// Registers external host pointer in the memory manager which will manage
   /// the corresponding device pointer (but not the provided host pointer).
   template<class T>
   void RegisterHostPtr(T *ptr_host, const std::size_t size)
   {
      Insert(ptr_host, size*sizeof(T));
#ifdef MFEM_DEBUG
      RegisterCheck(ptr_host);
#endif
   }

   /// Registers external host and device pointers in the memory manager.
   template<class T>
   void RegisterHostAndDevicePtr(T *ptr_host, T *ptr_device,
                                 const std::size_t size, const bool host)
   {
      RegisterHostPtr(ptr_host, size);
      SetHostDevicePtr(ptr_host, ptr_device, host);
   }

   /// Set the host h_ptr, device d_ptr and mode host of the memory region just
   /// been registered with h_ptr (see RegisterHostAndDevicePtr)
   void SetHostDevicePtr(void *h_ptr, void *d_ptr, const bool host);

   /// Unregisters the host pointer from the memory manager. To be used with
   /// memory not allocated by the memory manager.
   template<class T>
   void UnregisterHostPtr(T *ptr) { Erase(ptr); }

   /// Check if pointer has been registered in the memory manager
   void RegisterCheck(void *ptr);

   /// Prints all pointers known by the memory manager
   void PrintPtrs(void);

   /// Copies all memory to the current memory space
   void GetAll(void);
};

extern bool mm_destroyed;
extern MemoryManager mm;

/// Main malloc template function. Allocates n*size bytes and returns a
/// pointer to the allocated memory.
template<class T>
inline T *New(const std::size_t n)
{ return static_cast<T*>(mm.Insert(new T[n], n*sizeof(T))); }

/// Frees the memory space pointed to by ptr, which must have been returned
/// by a previous call to mfem::New.
template<class T>
inline void Delete(T *ptr)
{
   static_assert(!std::is_void<T>::value, "Cannot Delete a void pointer. "
                 "Explicitly provide the correct type as a template parameter.");
   if (!ptr) { return; }
   delete [] ptr;
   if (mm_destroyed) { return; }
   mm.Erase(ptr);
}

/// Return a host or device address coresponding to current memory space
template <class T>
inline T *Ptr(T *a) { return static_cast<T*>(mm.Ptr(a)); }

/// Data will be pushed/pulled before the copy happens on the H or the D
inline void* Memcpy(void *dst, const void *src,
                    std::size_t bytes, const bool async = false)
{ return mm.Memcpy(dst, src, bytes, async); }

/// Push the data to the device
inline void Push(const void *ptr, const std::size_t bytes =0)
{ return mm.Push(ptr, bytes); }

/// Pull the data from the device
inline void Pull(const void *ptr, const std::size_t bytes =0)
{ return mm.Pull(ptr, bytes); }

} // namespace mfem

#endif // MFEM_MEM_MANAGER
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2015-2018 dubalu.com LLC. All rights reserved.
 *
 * 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.
 */
#pragma once

#include <cstddef>
#include <cstdint>
#include <limits>
#include <stdexcept>

namespace mph {

template <typename T>
auto constexpr log(T v) {
	std::size_t n = 0;
	while (v > 1) {
		n += 1;
		v >>= 1;
	}
	return n;
}

constexpr std::size_t bit_weight(std::size_t n) {
	return (
		(n <= 8 * sizeof(unsigned int)) +
		(n <= 8 * sizeof(unsigned long)) +
		(n <= 8 * sizeof(unsigned long long)) +
		(n <= 128)
	);
}

unsigned int select_uint_least(std::integral_constant<std::size_t, 4>);
unsigned long select_uint_least(std::integral_constant<std::size_t, 3>);
unsigned long long select_uint_least(std::integral_constant<std::size_t, 2>);
template<std::size_t N>
unsigned long long select_uint_least(std::integral_constant<std::size_t, N>) {
	static_assert(N < 2, "Unsupported type size");
	return {};
}
template<std::size_t N>
using select_uint_least_t = decltype(select_uint_least(std::integral_constant<std::size_t, bit_weight(N)>()));

template <typename T, T a, T c, T m>
class linear_congruential_engine {
	static_assert(std::is_unsigned<T>::value, "Only supports unsigned integral types");

public:
	using result_type = T;
	static constexpr result_type multiplier = a;
	static constexpr result_type increment = c;
	static constexpr result_type modulus = m;
	static constexpr result_type default_seed = 1u;

	linear_congruential_engine() = default;
	constexpr linear_congruential_engine(result_type s) { seed(s); }

	void seed(result_type s = default_seed) { state_ = s; }
	constexpr result_type operator()() {
		using uint_least_t = select_uint_least_t<log(a) + log(m) + 4>;
		uint_least_t tmp = static_cast<uint_least_t>(multiplier) * state_ + increment;
		if(modulus)
			state_ = tmp % modulus;
		else
			state_ = tmp;
		return state_;
	}
	constexpr void discard(unsigned long long n) {
		while (n--)
			operator()();
	}
	static constexpr result_type min() { return increment == 0u ? 1u : 0u; };
	static constexpr result_type max() { return modulus - 1u; };
	friend constexpr bool operator==(const linear_congruential_engine& self,
									 const linear_congruential_engine& other) {
		return self.state_ == other.state_;
	}
	friend constexpr bool operator!=(const linear_congruential_engine& self,
									 const linear_congruential_engine& other) {
		return !(self == other);
	}

private:
	result_type state_ = default_seed;
};

using minstd_rand0 = linear_congruential_engine<std::uint_fast32_t, 16807, 0, 2147483647>;
using minstd_rand = linear_congruential_engine<std::uint_fast32_t, 48271, 0, 2147483647>;
using default_prg_t = minstd_rand;

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

template <class T>
constexpr void swap(T& a, T& b) {
	auto tmp = a;
	a = b;
	b = tmp;
}

template <typename It>
constexpr It partition(It left, It right) {
	auto pivot = left + (right - left) / 2;
	auto value = *pivot;
	swap(*right, *pivot);
	for (auto it = left; it < right; ++it) {
		if (*it < value) {
			swap(*it, *left);
			++left;
		}
	}
	swap(*right, *left);
	return left;
}

template <typename It>
constexpr void quicksort(It left, It right) {
	if (left < right) {
		auto pivot = partition(left, right);
		quicksort(left, pivot);
		quicksort(pivot + 1, right);
	}
}

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

#define MPH_RESORT

template <typename T, std::size_t N, typename RNG = default_prg_t>
class mph {
	static_assert(N > 0, "Must have at least one element");
	static_assert(std::is_unsigned<T>::value, "Only supports unsigned integral types");

public:
	constexpr static int max_first_tries = 100;
	constexpr static int max_second_tries = 100;
	constexpr static int max_clashes = 2 * (1u << (log(N) / 2));
	constexpr static auto npos = std::numeric_limits<std::size_t>::max();

	struct bucket_t {
		T item;
		std::size_t pos;

		constexpr bucket_t() : item(0), pos{npos} { }
	};

private:

	struct hashed_item_t {
		std::size_t cnt;
		T slot1;
		T slot2;
		T hash;
		T item;
		std::size_t pos;

		constexpr hashed_item_t() : cnt{0}, slot1{0}, slot2{0}, hash{0}, item{0}, pos{npos} { }

		constexpr hashed_item_t(const hashed_item_t& other) :
			cnt{other.cnt},
			slot1{other.slot1},
			slot2{other.slot2},
			hash{other.hash},
			item{other.item},
			pos{other.pos} { }

		constexpr hashed_item_t(hashed_item_t&& other) noexcept :
			cnt{std::move(other.cnt)},
			slot1{std::move(other.slot1)},
			slot2{std::move(other.slot2)},
			hash{std::move(other.hash)},
			item{std::move(other.item)},
			pos{std::move(other.pos)} { }

		constexpr hashed_item_t& operator=(const hashed_item_t& other) {
			cnt = other.cnt;
			slot1 = other.slot1;
			slot2 = other.slot2;
			hash = other.hash;
			item = other.item;
			pos = other.pos;
			return *this;
		}

		constexpr hashed_item_t& operator=(hashed_item_t&& other) noexcept {
			cnt = std::move(other.cnt);
			slot1 = std::move(other.slot1);
			slot2 = std::move(other.slot2);
			hash = std::move(other.hash);
			item = std::move(other.item);
			pos = std::move(other.pos);
			return *this;
		}

		constexpr bool operator<(const hashed_item_t& other) const {
			if (cnt == other.cnt) {
				return slot1 < other.slot1;
			}
			return cnt > other.cnt;
		}
	};

	constexpr static std::size_t hash(const T &value, std::size_t seed) {
		std::size_t key = seed ^ value;
		key = (~key) + (key << 21); // key = (key << 21) - key - 1;
		key = key ^ (key >> 24);
		key = (key + (key << 3)) + (key << 8); // key * 265
		key = key ^ (key >> 14);
		key = (key + (key << 2)) + (key << 4); // key * 21
		key = key ^ (key >> 28);
		key = key + (key << 31);
		return key;
	}

	std::size_t _seed;
	bucket_t _first[N];
	bucket_t _second[N];

public:
	constexpr mph(const T (&items)[N]) {
		RNG prg;
		hashed_item_t hashed_items[N];

		for (int try_first = 0; try_first < max_first_tries; ++try_first) {
			_seed = prg();
			for (std::size_t pos = 0; pos < N; ++pos) {
				auto& item = items[pos];
				auto& hashed_item = hashed_items[pos];
				auto hashed = hash(item, _seed);
				hashed_item.item = item;
				hashed_item.hash = hashed;
				hashed_item.slot1 = hashed % N;
				hashed_item.pos = pos;
				hashed_item.cnt = 0;
			}

			quicksort(&hashed_items[0], &hashed_items[N - 1]);

			auto end = &hashed_items[N];

			///

			auto frm = &hashed_items[0];
			auto to = frm;
			bool last = false;

#ifdef MPH_RESORT
			while (!last) {
				++to;
				last = to == end;
				if (last || frm->slot1 != to->slot1) {
					auto cnt = to - frm;
					if (cnt > max_clashes) {
						break;
					}
					for (; frm != to; ++frm) {
						frm->cnt = cnt;
					}
				}
			}
			if (!last) {
				continue;
			}
			quicksort(&hashed_items[0], &hashed_items[N - 1]);

			///
			frm = &hashed_items[0];
			to = frm;
			last = false;
#endif

			while (!last) {
				++to;
				last = to == end;

				if (last || frm->slot1 != to->slot1) {
					auto& first_bucket = _first[frm->slot1];
					auto cnt = to - frm;
					if (cnt > 1) {
						// slot clash
#ifndef MPH_RESORT
						if (cnt > max_clashes) {
							break;
						}
#endif
						int try_second = 0;
						for (; try_second < max_second_tries; ++try_second) {
							auto seed = prg();
							first_bucket.item = seed;

							auto frm_ = frm;
							for (; frm_ != to; ++frm_) {
								auto hashed = hash(frm_->item, seed);
								frm_->slot2 = hashed % N;
								auto& second_bucket = _second[frm_->slot2];
								if (second_bucket.pos != npos) {
									break;
								}
								second_bucket.item = frm_->item;
								second_bucket.pos = frm_->pos;
							}
							if (frm_ == to) {
								frm = frm_;
								break;
							}
							// rollback, it failed to place all items in empty slots
							for (auto frm__ = frm; frm__ != frm_; ++frm__) {
								auto& second_bucket = _second[frm__->slot2];
								second_bucket.pos = npos;
							}
						}
						if (try_second == max_second_tries) {
							break;
						}
					} else {
						// no slot clash
						first_bucket.item = frm->item;
						first_bucket.pos = frm->pos;
						++frm;
					}
				}
			}
			if (last) {
				return;
			}
		}
		throw std::invalid_argument("Cannot figure out a suitable MPH table");
	}

	constexpr std::size_t operator[](const T& item) const {
		auto pos = find(item);
		if (pos == npos) {
			throw std::out_of_range("Item not found");
		}
		return pos;
	}

	constexpr auto size() const {
		return N;
	}

	constexpr std::size_t find(const T& item) const {
		auto hashed = hash(item, _seed);
		const auto& first_bucket = _first[hashed % N];
		if (first_bucket.pos != npos) {
			if (first_bucket.item != item) {
				return npos;
			}
			return first_bucket.pos;
		}
		hashed = hash(item, first_bucket.item);
		const auto& second_bucket = _second[hashed % N];
		if (second_bucket.pos != npos) {
			if (hashed)
			if (second_bucket.item != item) {
				return npos;
			}
			return second_bucket.pos;
		}
		return npos;
	}

};

template <typename T, std::size_t N>
constexpr static auto
init(const T (&items)[N]) {
	return mph<T, N>(items);
}

} // namespace mph

#pragma GCC diagnostic ignored "-Wvariadic-macros"
#pragma GCC diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"

#define MPH_INIT_BEGIN(name) static constexpr auto mph_##name = mph::init({
#define MPH_OPTION_INIT(option, arg) fnv1ah32::hash(#option),
#define MPH_INIT_END(name) });

#define MPH_SWITCH_BEGIN(arg, name) switch (mph_##name.find(fnv1ah32::hash(arg))) {
#define MPH_OPTION_CASE(option, name) case mph_##name.find(fnv1ah32::hash(#option))
#define MPH_OPTION_CASE_RETURN_STRING(option, name) MPH_OPTION_CASE(option, name): { static const std::string _(#option); return _; }
#define MPH_OPTION_CASE_DISPATCH(option, name, args...) MPH_OPTION_CASE(option, name): return _##name##_dispatcher_ ##option(args);
#define MPH_SWITCH_END(arg) }
<commit_msg>MPH code cleanup and tweaks<commit_after>/*
 * Copyright (C) 2015-2018 dubalu.com LLC. All rights reserved.
 *
 * 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.
 */
#pragma once

#include <cstddef>
#include <cstdint>
#include <limits>
#include <stdexcept>

namespace mph {

////////////////////////////////////////////////////////////////////////////////
// Constexpr linear congruential random number engine

constexpr std::size_t bit_weight(std::size_t n) {
	return (
		(n <= 8 * sizeof(unsigned int)) +
		(n <= 8 * sizeof(unsigned long)) +
		(n <= 8 * sizeof(unsigned long long)) +
		(n <= 128)
	);
}

unsigned int select_uint_least(std::integral_constant<std::size_t, 4>);
unsigned long select_uint_least(std::integral_constant<std::size_t, 3>);
unsigned long long select_uint_least(std::integral_constant<std::size_t, 2>);
template<std::size_t N>
unsigned long long select_uint_least(std::integral_constant<std::size_t, N>) {
	static_assert(N < 2, "Unsupported type size");
	return {};
}
template<std::size_t N>
using select_uint_least_t = decltype(select_uint_least(std::integral_constant<std::size_t, bit_weight(N)>()));

template <typename T>
auto constexpr log(T v) {
	std::size_t n = 0;
	while (v > 1) {
		n += 1;
		v >>= 1;
	}
	return n;
}

template <typename T, T a, T c, T m>
class linear_congruential_engine {
	static_assert(std::is_unsigned<T>::value, "Only supports unsigned integral types");

public:
	using result_type = T;
	static constexpr result_type multiplier = a;
	static constexpr result_type increment = c;
	static constexpr result_type modulus = m;
	static constexpr result_type default_seed = 1u;

	linear_congruential_engine() = default;

	constexpr linear_congruential_engine(result_type s) {
		seed(s);
	}

	void seed(result_type s = default_seed) {
		state_ = s;
	}

	constexpr result_type operator()() {
		using uint_least_t = select_uint_least_t<log(a) + log(m) + 4>;
		uint_least_t tmp = static_cast<uint_least_t>(multiplier) * state_ + increment;
		if (modulus) {
			state_ = tmp % modulus;
		} else {
			state_ = tmp;
		}
		return state_;
	}

	constexpr void discard(unsigned long long n) {
		while (n--) {
			operator()();
		}
	}

	static constexpr result_type min() {
		return increment == 0u ? 1u : 0u;
	};

	static constexpr result_type max() {
		return modulus - 1u;
	};

	friend constexpr bool operator==(const linear_congruential_engine& self,
									 const linear_congruential_engine& other) {
		return self.state_ == other.state_;
	}
	friend constexpr bool operator!=(const linear_congruential_engine& self,
									 const linear_congruential_engine& other) {
		return !(self == other);
	}

private:
	result_type state_ = default_seed;
};

using minstd_rand0 = linear_congruential_engine<std::uint_fast32_t, 16807, 0, 2147483647>;
using minstd_rand = linear_congruential_engine<std::uint_fast32_t, 48271, 0, 2147483647>;

////////////////////////////////////////////////////////////////////////////////
// Constexpr quicksort

template <class T>
constexpr void swap(T& a, T& b) {
	auto tmp = a;
	a = b;
	b = tmp;
}

template <typename It>
constexpr It partition(It left, It right) {
	auto pivot = left + (right - left) / 2;
	auto value = *pivot;
	swap(*right, *pivot);
	for (auto it = left; it < right; ++it) {
		if (*it < value) {
			swap(*it, *left);
			++left;
		}
	}
	swap(*right, *left);
	return left;
}

template <typename It>
constexpr void quicksort(It left, It right) {
	if (left < right) {
		auto pivot = partition(left, right);
		quicksort(left, pivot);
		quicksort(pivot + 1, right);
	}
}

////////////////////////////////////////////////////////////////////////////////
// Computes a constexpr minimal perfect hash table
// Based on:
// "Practical minimal perfect hash functions for large databases", CACM, 35(1):105-121
// Edward A. Fox, Lenwood S. Heath, Qi Fan Chen and Amjad M. Daoud,

#define MPH_SORT_CLASHES

constexpr static auto npos = std::numeric_limits<std::size_t>::max();

template <typename T, std::size_t N, typename RNG = minstd_rand>
class mph {
	static_assert(N > 0, "Must have at least one element");
	static_assert(std::is_unsigned<T>::value, "Only supports unsigned integral types");

public:
	constexpr static int max_clashes = 2 * (1u << (log(N) / 2));

	struct bucket_t {
		T item;
		std::size_t pos;

		constexpr bucket_t() : item(0), pos{npos} { }
	};

private:

	struct hashed_item_t {
		std::size_t cnt;
		T slot1;
		T slot2;
		T hash;
		T item;
		std::size_t pos;

		constexpr hashed_item_t() : cnt{0}, slot1{0}, slot2{0}, hash{0}, item{0}, pos{npos} { }

		constexpr hashed_item_t(const hashed_item_t& other) :
			cnt{other.cnt},
			slot1{other.slot1},
			slot2{other.slot2},
			hash{other.hash},
			item{other.item},
			pos{other.pos} { }

		constexpr hashed_item_t(hashed_item_t&& other) noexcept :
			cnt{std::move(other.cnt)},
			slot1{std::move(other.slot1)},
			slot2{std::move(other.slot2)},
			hash{std::move(other.hash)},
			item{std::move(other.item)},
			pos{std::move(other.pos)} { }

		constexpr hashed_item_t& operator=(const hashed_item_t& other) {
			cnt = other.cnt;
			slot1 = other.slot1;
			slot2 = other.slot2;
			hash = other.hash;
			item = other.item;
			pos = other.pos;
			return *this;
		}

		constexpr hashed_item_t& operator=(hashed_item_t&& other) noexcept {
			cnt = std::move(other.cnt);
			slot1 = std::move(other.slot1);
			slot2 = std::move(other.slot2);
			hash = std::move(other.hash);
			item = std::move(other.item);
			pos = std::move(other.pos);
			return *this;
		}

		constexpr bool operator<(const hashed_item_t& other) const {
			if (cnt == other.cnt) {
				return slot1 < other.slot1;
			}
			return cnt > other.cnt;
		}
	};

	constexpr static std::size_t hash(const T &value, std::size_t seed) {
		std::size_t key = seed ^ value;
		key = (~key) + (key << 21); // key = (key << 21) - key - 1;
		key = key ^ (key >> 24);
		key = (key + (key << 3)) + (key << 8); // key * 265
		key = key ^ (key >> 14);
		key = (key + (key << 2)) + (key << 4); // key * 21
		key = key ^ (key >> 28);
		key = key + (key << 31);
		return key;
	}

	std::size_t _seed;
	bucket_t _first[N];
	bucket_t _second[N];

public:
	constexpr mph(const T (&items)[N]) {
		RNG rng;
		hashed_item_t hashed_items[N];

		for (int retry = 1000; retry; --retry) {
			_seed = rng();

			for (std::size_t pos = 0; pos < N; ++pos) {
				auto& item = items[pos];
				auto& hashed_item = hashed_items[pos];
				auto hashed = hash(item, _seed);
				hashed_item.item = item;
				hashed_item.hash = hashed;
				hashed_item.slot1 = hashed % N;
				hashed_item.pos = pos;
				hashed_item.cnt = 0;
			}

			quicksort(&hashed_items[0], &hashed_items[N - 1]);

			auto end = &hashed_items[N];

			///

			auto frm = &hashed_items[0];
			auto to = frm;

#ifdef MPH_SORT_CLASHES
			do {
				++to;
				if (to == end || frm->slot1 != to->slot1) {
					auto cnt = to - frm;
					if (cnt > max_clashes) {
						break;
					}
					for (; frm != to; ++frm) {
						frm->cnt = cnt;
					}
				}
			} while (to != end);
			if (to != end) {
				continue;
			}

			quicksort(&hashed_items[0], &hashed_items[N - 1]);

			///
			frm = &hashed_items[0];
			to = frm;
#endif

			do {
				++to;
				if (to == end || frm->slot1 != to->slot1) {
					auto& first_bucket = _first[frm->slot1];
					auto cnt = to - frm;
					if (cnt > 1) {
						// slot clash
						if (cnt > max_clashes) {
							break;
						}
						for (int retry = 1000; retry; --retry) {
							auto seed = rng();
							first_bucket.item = seed;

							auto frm_ = frm;
							for (; frm_ != to; ++frm_) {
								auto hashed = hash(frm_->item, seed);
								frm_->slot2 = hashed % N;
								auto& second_bucket = _second[frm_->slot2];
								if (second_bucket.pos != npos) {
									break;
								}
								second_bucket.item = frm_->item;
								second_bucket.pos = frm_->pos;
							}
							if (frm_ == to) {
								frm = frm_;
								break;
							}
							// rollback, it failed to place all items in empty slots
							for (auto frm__ = frm; frm__ != frm_; ++frm__) {
								auto& second_bucket = _second[frm__->slot2];
								second_bucket.pos = npos;
							}
						}
						if (frm != to) {
							break;
						}
					} else {
						// no slot clash
						first_bucket.item = frm->item;
						first_bucket.pos = frm->pos;
						++frm;
					}
				}
			} while (to != end);
			if (to != end) {
				continue;
			}
			return;
		}

		throw std::invalid_argument("Cannot figure out a suitable MPH table");
	}

	constexpr std::size_t operator[](const T& item) const {
		auto pos = find(item);
		if (pos == npos) {
			throw std::out_of_range("Item not found");
		}
		return pos;
	}

	constexpr std::size_t find(const T& item) const {
		auto hashed = hash(item, _seed);
		const auto& first_bucket = _first[hashed % N];
		if (first_bucket.pos != npos) {
			if (first_bucket.item != item) {
				return npos;
			}
			return first_bucket.pos;
		}
		hashed = hash(item, first_bucket.item);
		const auto& second_bucket = _second[hashed % N];
		if (second_bucket.pos != npos) {
			if (hashed)
			if (second_bucket.item != item) {
				return npos;
			}
			return second_bucket.pos;
		}
		return npos;
	}

	constexpr auto size() const {
		return N;
	}
};

template <typename T, std::size_t N>
constexpr static auto
init(const T (&items)[N]) {
	return mph<T, N>(items);
}

} // namespace mph

#pragma GCC diagnostic ignored "-Wvariadic-macros"
#pragma GCC diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"

#define MPH_VAR(name) mph_##name
#define MPH_INIT_BEGIN(name) static constexpr auto MPH_VAR(name) = mph::init({
#define MPH_OPTION_INIT(option, arg) fnv1ah32::hash(#option),
#define MPH_INIT_END(name) });

#define MPH_SWITCH_BEGIN(arg, name) switch (MPH_VAR(name).find(fnv1ah32::hash(arg))) {
#define MPH_OPTION_CASE(option, name) case MPH_VAR(name).find(fnv1ah32::hash(#option))
#define MPH_OPTION_CASE_RETURN_STRING(option, name) MPH_OPTION_CASE(option, name): { static const std::string _(#option); return _; }
#define MPH_OPTION_CASE_DISPATCH(option, name, args...) MPH_OPTION_CASE(option, name): return _##name##_dispatcher_ ##option(args);
#define MPH_SWITCH_END(arg) }
<|endoftext|>