<commit_before>#include "evpp/inner_pre.h"
#include "evpp/libevent_headers.h"
#include "evpp/event_loop.h"
#include "evpp/event_loop_thread_pool.h"

#include "udp_server.h"

namespace evpp {
namespace udp {

enum Status {
    kRunning = 1,
    kPaused = 2,
    kStopping = 3,
    kStopped = 4,
};

class Server::RecvThread {
public:
    RecvThread(Server* srv)
        : fd_(INVALID_SOCKET), server_(srv), port_(-1), status_(kStopped) {
    }

    ~RecvThread() {
        EVUTIL_CLOSESOCKET(fd_);
        fd_ = INVALID_SOCKET;
        if (this->thread_ && this->thread_->joinable()) {
            this->thread_->join();
        }
    }

    bool Start(int p) {
        this->port_ = p;
        this->fd_ = sock::CreateUDPServer(p);
        this->status_ = kRunning;
        this->thread_.reset(new std::thread(std::bind(&Server::RecvingLoop, this->server_, this)));
        sock::SetTimeout(this->fd_, 500);
        LOG_TRACE << "start udp server at 0.0.0.0:" << port_;
        return true;
    }

    void Stop() {
        assert(IsRunning() || IsPaused());
        status_ = kStopping;
    }

    void Pause() {
        assert(IsRunning());
        status_ = kPaused;
    }

    void Continue() {
        assert(IsPaused());
        status_ = kRunning;
    }

    bool IsRunning() const {
        return status_ == kRunning;
    }

    bool IsStopped() const {
        return status_ == kStopped;
    }

    bool IsPaused() const {
        return status_ == kPaused;
    }

    void SetStatus(Status s) {
        status_ = s;
    }

    int fd() const {
        return fd_;
    }

    int port() const {
        return port_;
    }

    Server* server() const {
        return server_;
    }
private:
    int fd_;
    Server* server_;
    int port_;
    std::shared_ptr<std::thread> thread_;
    Status status_;
};

Server::Server() : recv_buf_size_(1472) {}

Server::~Server() {
}

bool Server::Start(std::vector<int> ports) {
    for (auto it = ports.begin(); it != ports.end(); ++it) {
        if (!Start(*it)) {
            return false;
        }
    }
    return true;
}

bool Server::Start(int port) {
    if (!message_handler_) {
        LOG_ERROR << "MessageHandler DO NOT set!";
        return false;
    }

    RecvThreadPtr t(new RecvThread(this));
    bool ret = t->Start(port);
    assert(ret);
    recv_threads_.push_back(t);
    return ret;
}

void Server::Stop(bool wait_thread_exit) {
    for (auto it = recv_threads_.begin(); it != recv_threads_.end(); it++) {
        (*it)->Stop();
    }

    if (wait_thread_exit) {
        while (!IsStopped()) {
            usleep(1);
        }
    }
}

void Server::Pause() {
    for (auto it = recv_threads_.begin(); it != recv_threads_.end(); it++) {
        (*it)->Pause();
    }
}

void Server::Continue() {
    for (auto it = recv_threads_.begin(); it != recv_threads_.end(); it++) {
        (*it)->Continue();
    }
}

bool Server::IsRunning() const {
    bool rc = true;
    for (auto it = recv_threads_.begin(); it != recv_threads_.end(); it++) {
        rc = rc && (*it)->IsRunning();
    }

    return rc;
}

bool Server::IsStopped() const {
    bool rc = true;
    for (auto it = recv_threads_.begin(); it != recv_threads_.end(); it++) {
        rc = rc && (*it)->IsStopped();
    }

    return rc;
}

void Server::RecvingLoop(RecvThread* thread) {
    while (true) {
        if (thread->IsPaused()) {
            usleep(1);
            continue;
        }

        if (!thread->IsRunning()) {
            break;
        }

        MessagePtr recv_msg(new Message(thread->fd(), recv_buf_size_));
        socklen_t addr_len = sizeof(struct sockaddr);
        int readn = ::recvfrom(thread->fd(), (char*)recv_msg->WriteBegin(), recv_buf_size_, 0, recv_msg->mutable_remote_addr(), &addr_len);
        LOG_TRACE << "fd=" << thread->fd() << " port=" << thread->port()
                  << " recv len=" << readn << " from " << sock::ToIPPort(recv_msg->remote_addr());

        if (readn >= 0) {
            recv_msg->WriteBytes(readn);
            if (tpool_) {
                EventLoop* loop = NULL;
                if (IsRoundRobin()) {
                    loop = tpool_->GetNextLoop();
                } else {
                    loop = tpool_->GetNextLoopWithHash(sock::sockaddr_in_cast(recv_msg->remote_addr())->sin_addr.s_addr);
                }
                loop->RunInLoop(std::bind(this->message_handler_, recv_msg));
            } else {
                this->message_handler_(recv_msg);
            }
        } else {
            int eno = errno;
            if (EVUTIL_ERR_RW_RETRIABLE(eno)) {
                continue;
            }

            LOG_ERROR << "errno=" << eno << " " << strerror(eno);
        }
    }

    LOG_INFO << "fd=" << thread->fd() << " port=" << thread->port() << " UDP server existed.";
    thread->SetStatus(kStopped);
}

}
}

/*
ܲݣIntel(R) Xeon(R) CPU E5-2630 0 @ 2.30GHz 24

ƿrecvfrom߳ϣ23̺߳ѹ

udp messageͬµQPS
0.1k 9w
1k   8w

17:20:19       idgm/s    odgm/s  noport/s idgmerr/s
17:20:20     95572.00  95571.00      0.00      0.00
17:20:21     93522.00  93522.00      0.00      0.00
17:20:22     91669.00  91664.00      0.00      0.00
17:20:23     97165.00  97171.00      0.00      0.00
17:20:24     91225.00  91224.00      0.00      0.00
17:20:25     89659.00  89659.00      0.00      0.00
17:20:26     93199.00  93198.00      0.00      0.00
17:20:27     90758.00  90758.00      0.00      0.00
17:20:28     86891.00  86891.00      0.00      1.00
17:20:29     90346.00  90347.00      0.00      0.00
17:20:30     90675.00  90674.00      0.00      0.00
17:20:31     96589.00  96590.00      0.00      0.00
17:20:32     93739.00  93739.00      0.00      0.00
17:20:33     91374.00  91375.00      0.00      0.00
17:20:34     97162.00  97160.00      0.00      0.00
17:20:35     94281.00  94281.00      0.00      0.00
17:20:36     93101.00  93102.00      0.00      0.00
17:20:37     94741.00  94741.00      0.00      0.00
17:20:38     96064.00  96064.00      0.00      0.00
17:20:39     92436.00  92435.00      0.00      0.00
17:20:40     92432.00  92434.00      0.00      0.00
17:20:41     88199.00  88198.00      0.00      0.00
17:20:42     99852.00  99852.00      0.00      0.00
17:20:43     97449.00  97449.00      0.00      0.00
17:20:44     99380.00  99380.00      0.00      0.00
17:20:45     95372.00  95372.00      0.00      0.00
17:20:46     98629.00  98629.00      0.00      0.00
17:20:47     98187.00  98187.00      0.00      0.00
17:20:48     97442.00  97441.00      0.00      0.00
17:20:49     97482.00  97483.00      0.00      0.00
17:20:50     99234.00  99233.00      0.00      0.00
17:20:51     97863.00  97865.00      0.00      0.00
17:20:52     94651.00  94650.00      0.00      0.00
17:20:53     95824.00  95824.00      0.00      0.00
17:20:54     93032.00  93032.00      0.00      0.00
17:20:55     99538.00  99538.00      0.00      0.00
17:20:56     94344.00  94344.00      0.00      0.00
17:20:57    101252.00 101252.00      0.00      0.00
17:20:58     84523.00  84523.00      0.00      0.00
17:20:59     72918.00  72918.00      0.00      0.00
17:21:00     82758.00  82758.00      0.00      0.00
17:21:01     85132.00  85132.00      0.00      0.00
17:21:02     89151.00  89152.00      0.00      0.00
17:21:03     81456.00  81455.00      0.00      0.00
17:21:04     93089.00  93089.00      0.00      0.00
17:21:05     84559.00  84558.00      0.00      0.00
17:21:06     96240.00  96240.00      0.00      0.00
17:21:07     97439.00  97440.00      0.00      0.00
17:21:08     90971.00  90972.00      0.00      0.00
17:21:09     92930.00  92928.00      0.00      0.00
17:21:10     94668.00  94669.00      0.00      0.00
17:21:11     91937.00  91938.00      0.00      0.00
17:21:12     93480.00  93478.00      0.00      0.00
17:21:13     95384.00  95385.00      0.00      0.00
17:21:14     96525.00  96525.00      0.00      0.00
17:21:15    103088.00 103088.00      0.00      0.00
17:21:16     97340.00  97340.00      0.00      0.00
17:21:17     95124.00  95124.00      0.00      0.00
17:21:18     90760.00  90758.00      0.00      0.00
17:21:19     93717.00  93719.00      0.00      0.00
17:21:20     95226.00  95226.00      0.00      0.00
17:21:21     98472.00  98472.00      0.00      0.00
17:21:22     95193.00  95192.00      0.00      0.00
17:21:23     95296.00  95297.00      0.00      0.00
17:21:24     95517.00  95516.00      0.00      0.00
17:21:25     96691.00  96692.00      0.00      0.00
17:21:26     96200.00  96198.00      0.00      0.00
17:21:27     97431.00  97432.00      0.00      0.00

*/
<commit_msg>Add try-catch for thread::join<commit_after>#include "evpp/inner_pre.h"
#include "evpp/libevent_headers.h"
#include "evpp/event_loop.h"
#include "evpp/event_loop_thread_pool.h"

#include "udp_server.h"

namespace evpp {
namespace udp {

enum Status {
    kRunning = 1,
    kPaused = 2,
    kStopping = 3,
    kStopped = 4,
};

class Server::RecvThread {
public:
    RecvThread(Server* srv)
        : fd_(INVALID_SOCKET), server_(srv), port_(-1), status_(kStopped) {
    }

    ~RecvThread() {
        EVUTIL_CLOSESOCKET(fd_);
        fd_ = INVALID_SOCKET;
        if (this->thread_ && this->thread_->joinable()) {
            try {
                thread_->join();
            } catch (const std::system_error& e) {
                LOG_ERROR << "Caught a system_error:" << e.what();
            }
        }
    }

    bool Start(int p) {
        this->port_ = p;
        this->fd_ = sock::CreateUDPServer(p);
        this->status_ = kRunning;
        this->thread_.reset(new std::thread(std::bind(&Server::RecvingLoop, this->server_, this)));
        sock::SetTimeout(this->fd_, 500);
        LOG_TRACE << "start udp server at 0.0.0.0:" << port_;
        return true;
    }

    void Stop() {
        assert(IsRunning() || IsPaused());
        status_ = kStopping;
    }

    void Pause() {
        assert(IsRunning());
        status_ = kPaused;
    }

    void Continue() {
        assert(IsPaused());
        status_ = kRunning;
    }

    bool IsRunning() const {
        return status_ == kRunning;
    }

    bool IsStopped() const {
        return status_ == kStopped;
    }

    bool IsPaused() const {
        return status_ == kPaused;
    }

    void SetStatus(Status s) {
        status_ = s;
    }

    int fd() const {
        return fd_;
    }

    int port() const {
        return port_;
    }

    Server* server() const {
        return server_;
    }
private:
    int fd_;
    Server* server_;
    int port_;
    std::shared_ptr<std::thread> thread_;
    Status status_;
};

Server::Server() : recv_buf_size_(1472) {}

Server::~Server() {
}

bool Server::Start(std::vector<int> ports) {
    for (auto it = ports.begin(); it != ports.end(); ++it) {
        if (!Start(*it)) {
            return false;
        }
    }
    return true;
}

bool Server::Start(int port) {
    if (!message_handler_) {
        LOG_ERROR << "MessageHandler DO NOT set!";
        return false;
    }

    RecvThreadPtr t(new RecvThread(this));
    bool ret = t->Start(port);
    assert(ret);
    recv_threads_.push_back(t);
    return ret;
}

void Server::Stop(bool wait_thread_exit) {
    for (auto it = recv_threads_.begin(); it != recv_threads_.end(); it++) {
        (*it)->Stop();
    }

    if (wait_thread_exit) {
        while (!IsStopped()) {
            usleep(1);
        }
    }
}

void Server::Pause() {
    for (auto it = recv_threads_.begin(); it != recv_threads_.end(); it++) {
        (*it)->Pause();
    }
}

void Server::Continue() {
    for (auto it = recv_threads_.begin(); it != recv_threads_.end(); it++) {
        (*it)->Continue();
    }
}

bool Server::IsRunning() const {
    bool rc = true;
    for (auto it = recv_threads_.begin(); it != recv_threads_.end(); it++) {
        rc = rc && (*it)->IsRunning();
    }

    return rc;
}

bool Server::IsStopped() const {
    bool rc = true;
    for (auto it = recv_threads_.begin(); it != recv_threads_.end(); it++) {
        rc = rc && (*it)->IsStopped();
    }

    return rc;
}

void Server::RecvingLoop(RecvThread* thread) {
    while (true) {
        if (thread->IsPaused()) {
            usleep(1);
            continue;
        }

        if (!thread->IsRunning()) {
            break;
        }

        MessagePtr recv_msg(new Message(thread->fd(), recv_buf_size_));
        socklen_t addr_len = sizeof(struct sockaddr);
        int readn = ::recvfrom(thread->fd(), (char*)recv_msg->WriteBegin(), recv_buf_size_, 0, recv_msg->mutable_remote_addr(), &addr_len);
        LOG_TRACE << "fd=" << thread->fd() << " port=" << thread->port()
                  << " recv len=" << readn << " from " << sock::ToIPPort(recv_msg->remote_addr());

        if (readn >= 0) {
            recv_msg->WriteBytes(readn);
            if (tpool_) {
                EventLoop* loop = NULL;
                if (IsRoundRobin()) {
                    loop = tpool_->GetNextLoop();
                } else {
                    loop = tpool_->GetNextLoopWithHash(sock::sockaddr_in_cast(recv_msg->remote_addr())->sin_addr.s_addr);
                }
                loop->RunInLoop(std::bind(this->message_handler_, recv_msg));
            } else {
                this->message_handler_(recv_msg);
            }
        } else {
            int eno = errno;
            if (EVUTIL_ERR_RW_RETRIABLE(eno)) {
                continue;
            }

            LOG_ERROR << "errno=" << eno << " " << strerror(eno);
        }
    }

    LOG_INFO << "fd=" << thread->fd() << " port=" << thread->port() << " UDP server existed.";
    thread->SetStatus(kStopped);
}

}
}

/*
ܲݣIntel(R) Xeon(R) CPU E5-2630 0 @ 2.30GHz 24

ƿrecvfrom߳ϣ23̺߳ѹ

udp messageͬµQPS
0.1k 9w
1k   8w

17:20:19       idgm/s    odgm/s  noport/s idgmerr/s
17:20:20     95572.00  95571.00      0.00      0.00
17:20:21     93522.00  93522.00      0.00      0.00
17:20:22     91669.00  91664.00      0.00      0.00
17:20:23     97165.00  97171.00      0.00      0.00
17:20:24     91225.00  91224.00      0.00      0.00
17:20:25     89659.00  89659.00      0.00      0.00
17:20:26     93199.00  93198.00      0.00      0.00
17:20:27     90758.00  90758.00      0.00      0.00
17:20:28     86891.00  86891.00      0.00      1.00
17:20:29     90346.00  90347.00      0.00      0.00
17:20:30     90675.00  90674.00      0.00      0.00
17:20:31     96589.00  96590.00      0.00      0.00
17:20:32     93739.00  93739.00      0.00      0.00
17:20:33     91374.00  91375.00      0.00      0.00
17:20:34     97162.00  97160.00      0.00      0.00
17:20:35     94281.00  94281.00      0.00      0.00
17:20:36     93101.00  93102.00      0.00      0.00
17:20:37     94741.00  94741.00      0.00      0.00
17:20:38     96064.00  96064.00      0.00      0.00
17:20:39     92436.00  92435.00      0.00      0.00
17:20:40     92432.00  92434.00      0.00      0.00
17:20:41     88199.00  88198.00      0.00      0.00
17:20:42     99852.00  99852.00      0.00      0.00
17:20:43     97449.00  97449.00      0.00      0.00
17:20:44     99380.00  99380.00      0.00      0.00
17:20:45     95372.00  95372.00      0.00      0.00
17:20:46     98629.00  98629.00      0.00      0.00
17:20:47     98187.00  98187.00      0.00      0.00
17:20:48     97442.00  97441.00      0.00      0.00
17:20:49     97482.00  97483.00      0.00      0.00
17:20:50     99234.00  99233.00      0.00      0.00
17:20:51     97863.00  97865.00      0.00      0.00
17:20:52     94651.00  94650.00      0.00      0.00
17:20:53     95824.00  95824.00      0.00      0.00
17:20:54     93032.00  93032.00      0.00      0.00
17:20:55     99538.00  99538.00      0.00      0.00
17:20:56     94344.00  94344.00      0.00      0.00
17:20:57    101252.00 101252.00      0.00      0.00
17:20:58     84523.00  84523.00      0.00      0.00
17:20:59     72918.00  72918.00      0.00      0.00
17:21:00     82758.00  82758.00      0.00      0.00
17:21:01     85132.00  85132.00      0.00      0.00
17:21:02     89151.00  89152.00      0.00      0.00
17:21:03     81456.00  81455.00      0.00      0.00
17:21:04     93089.00  93089.00      0.00      0.00
17:21:05     84559.00  84558.00      0.00      0.00
17:21:06     96240.00  96240.00      0.00      0.00
17:21:07     97439.00  97440.00      0.00      0.00
17:21:08     90971.00  90972.00      0.00      0.00
17:21:09     92930.00  92928.00      0.00      0.00
17:21:10     94668.00  94669.00      0.00      0.00
17:21:11     91937.00  91938.00      0.00      0.00
17:21:12     93480.00  93478.00      0.00      0.00
17:21:13     95384.00  95385.00      0.00      0.00
17:21:14     96525.00  96525.00      0.00      0.00
17:21:15    103088.00 103088.00      0.00      0.00
17:21:16     97340.00  97340.00      0.00      0.00
17:21:17     95124.00  95124.00      0.00      0.00
17:21:18     90760.00  90758.00      0.00      0.00
17:21:19     93717.00  93719.00      0.00      0.00
17:21:20     95226.00  95226.00      0.00      0.00
17:21:21     98472.00  98472.00      0.00      0.00
17:21:22     95193.00  95192.00      0.00      0.00
17:21:23     95296.00  95297.00      0.00      0.00
17:21:24     95517.00  95516.00      0.00      0.00
17:21:25     96691.00  96692.00      0.00      0.00
17:21:26     96200.00  96198.00      0.00      0.00
17:21:27     97431.00  97432.00      0.00      0.00

*/
<|endoftext|>


<commit_before>/*
 * ====================================================================
 * Copyright (c) 2002-2005 The RapidSvn Group.  All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library (in the file LGPL.txt); if not,
 * write to the Free Software Foundation, Inc., 51 Franklin St,
 * Fifth Floor, Boston, MA  02110-1301  USA
 *
 * This software consists of voluntary contributions made by many
 * individuals.  For exact contribution history, see the revision
 * history and logs, available at http://rapidsvn.tigris.org/.
 * ====================================================================
 */

// subversion api
#include "svn_path.h"

// apr api
#include "apr_file_io.h"

// svncpp
#include "svnqt/path.hpp"
#include "svnqt/pool.hpp"
#include "svnqt/url.hpp"
#include "svnqt/svnqt_defines.hpp"

#include <qurl.h>

namespace svn
{
  Path::Path (const char * path)
  {
    init(QString::FROMUTF8(path));
  }

  Path::Path (const QString & path)
  {
    init (path);
  }

  Path::Path (const Path & path)
    : m_path(path.m_path)
  {
  }

  void
  Path::init (const QString& path)
  {
    Pool pool;

    if (path.isEmpty()) {
      m_path = "";
    } else {
      const char * int_path = svn_path_internal_style (path.TOUTF8(), pool.pool () );
      if (Url::isValid(path) ) {
        if (!svn_path_is_uri_safe(int_path)) {
            int_path = svn_path_uri_encode(int_path,pool);
        }
      }
      m_path = QString::FROMUTF8(int_path);
      if (Url::isValid(path) && m_path.find("@")!=-1 ) {
        /// @todo make sure that "@" is never used as revision paramter
        QUrl uri = m_path;
        m_path = uri.path();
        m_path.replace("@","%40");
        m_path = uri.protocol()+"://"+(uri.hasUser()?uri.user()+(uri.hasPassword()?":"+uri.hasPassword():"")+"@":"")
                +uri.host()+m_path;
        if (m_path.endsWith("/")) {
            int_path = svn_path_internal_style (path.TOUTF8(), pool.pool () );
            m_path = QString::FROMUTF8(int_path);
        }
      }
    }
  }

  const QString &
  Path::path () const
  {
    return m_path;
  }

  Path::operator const QString&()const
  {
    return m_path;
  }

  QString Path::prettyPath()const
  {
    if (!Url::isValid(m_path)) {
        return m_path;
    }
    Pool pool;
    const char * int_path = svn_path_uri_decode(m_path.TOUTF8(), pool.pool () );
    QString _p = QString::FROMUTF8(int_path);
    _p.replace("%40","@");
    return _p;
  }

  const QByteArray
  Path::cstr() const
  {
    return m_path.TOUTF8();
  }

  Path&
  Path::operator=(const Path & path)
  {
    if (this == &path)
      return *this;
    m_path = path.path();
    return *this;
  }

  bool
  Path::isset () const
  {
    return m_path.length () > 0;
  }

  void
  Path::addComponent (const QString& component)
  {
      Pool pool;

    if (Url::isValid (m_path))
    {
      const char * newPath =
          svn_path_url_add_component (m_path.TOUTF8(), component.TOUTF8(), pool);
      m_path = QString::FROMUTF8(newPath);
    }
    else
    {
      svn_stringbuf_t * pathStringbuf =
          svn_stringbuf_create (m_path.TOUTF8(), pool);

      svn_path_add_component (pathStringbuf,
                              component.TOUTF8());

      m_path = QString::FROMUTF8(pathStringbuf->data);
    }
  }

  void
  Path::addComponent (const char* component)
  {
    addComponent (QString::FROMUTF8(component));
  }

  void
  Path::split (QString & dirpath, QString & basename) const
  {
    Pool pool;

    const char * cdirpath;
    const char * cbasename;

    svn_path_split (m_path.TOUTF8(), &cdirpath, &cbasename, pool);
    dirpath = QString::FROMUTF8(cdirpath);
    basename = QString::FROMUTF8(cbasename);
  }

  void
  Path::split (QString & dir, QString & filename, QString & ext) const
  {
    QString basename;

    // first split path into dir and filename+ext
    split (dir, basename);

    // next search for last .
#if QT_VERSION < 0x040000
    int pos = basename.findRev(QChar('.'));
#else
    int pos = basename.lastIndexOf(QChar('.'));
#endif

    if (pos == -1)
    {
      filename = basename;
      ext = QString::fromLatin1("");
    }
    else
    {
      filename = basename.left(pos);
      ext = basename.mid(pos+1);
    }
  }

  Path
  Path::getTempDir ()
  {
    const char * tempdir = 0;
    Pool pool;

    if (apr_temp_dir_get (&tempdir, pool) != APR_SUCCESS)
    {
      tempdir = 0;
    }

    return tempdir;
  }

  unsigned int
  Path::length () const
  {
    return m_path.length ();
  }

  QString
  Path::native () const
  {
    Pool pool;

    return QString::FROMUTF8(svn_path_local_style (m_path.TOUTF8(), pool));
  }

}

/* -----------------------------------------------------------------
 * local variables:
 * eval: (load-file "../../rapidsvn-dev.el")
 * end:
 */
<commit_msg>path.cpp now build with qt4 again<commit_after>/*
 * ====================================================================
 * Copyright (c) 2002-2005 The RapidSvn Group.  All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library (in the file LGPL.txt); if not,
 * write to the Free Software Foundation, Inc., 51 Franklin St,
 * Fifth Floor, Boston, MA  02110-1301  USA
 *
 * This software consists of voluntary contributions made by many
 * individuals.  For exact contribution history, see the revision
 * history and logs, available at http://rapidsvn.tigris.org/.
 * ====================================================================
 */

// subversion api
#include "svn_path.h"

// apr api
#include "apr_file_io.h"

// svncpp
#include "svnqt/path.hpp"
#include "svnqt/pool.hpp"
#include "svnqt/url.hpp"
#include "svnqt/svnqt_defines.hpp"

#include <qurl.h>

namespace svn
{
  Path::Path (const char * path)
  {
    init(QString::FROMUTF8(path));
  }

  Path::Path (const QString & path)
  {
    init (path);
  }

  Path::Path (const Path & path)
    : m_path(path.m_path)
  {
  }

  void
  Path::init (const QString& path)
  {
    Pool pool;

    if (path.isEmpty()) {
      m_path = "";
    } else {
      const char * int_path = svn_path_internal_style (path.TOUTF8(), pool.pool () );
      if (Url::isValid(path) ) {
        if (!svn_path_is_uri_safe(int_path)) {
            int_path = svn_path_uri_encode(int_path,pool);
        }
      }
      m_path = QString::FROMUTF8(int_path);
#if QT_VERSION < 0x040000
      if (Url::isValid(path) && m_path.find("@")!=-1 ) {
#else
      if (Url::isValid(path) && m_path.indexOf("@")!=-1 ) {
#endif
        /// @todo make sure that "@" is never used as revision paramter
        QUrl uri = m_path;
        m_path = uri.path();
        m_path.replace("@","%40");
#if QT_VERSION < 0x040000
        m_path = uri.protocol()+"://"+(uri.hasUser()?uri.user()+(uri.hasPassword()?":"+uri.hasPassword():"")+"@":"")
                +uri.host()+m_path;
#else
        m_path = uri.scheme()+"://"+uri.authority()+m_path;
#endif
        if (m_path.endsWith("/")) {
            int_path = svn_path_internal_style (path.TOUTF8(), pool.pool () );
            m_path = QString::FROMUTF8(int_path);
        }
      }
    }
  }

  const QString &
  Path::path () const
  {
    return m_path;
  }

  Path::operator const QString&()const
  {
    return m_path;
  }

  QString Path::prettyPath()const
  {
    if (!Url::isValid(m_path)) {
        return m_path;
    }
    Pool pool;
    const char * int_path = svn_path_uri_decode(m_path.TOUTF8(), pool.pool () );
    QString _p = QString::FROMUTF8(int_path);
    _p.replace("%40","@");
    return _p;
  }

  const QByteArray
  Path::cstr() const
  {
    return m_path.TOUTF8();
  }

  Path&
  Path::operator=(const Path & path)
  {
    if (this == &path)
      return *this;
    m_path = path.path();
    return *this;
  }

  bool
  Path::isset () const
  {
    return m_path.length () > 0;
  }

  void
  Path::addComponent (const QString& component)
  {
      Pool pool;

    if (Url::isValid (m_path))
    {
      const char * newPath =
          svn_path_url_add_component (m_path.TOUTF8(), component.TOUTF8(), pool);
      m_path = QString::FROMUTF8(newPath);
    }
    else
    {
      svn_stringbuf_t * pathStringbuf =
          svn_stringbuf_create (m_path.TOUTF8(), pool);

      svn_path_add_component (pathStringbuf,
                              component.TOUTF8());

      m_path = QString::FROMUTF8(pathStringbuf->data);
    }
  }

  void
  Path::addComponent (const char* component)
  {
    addComponent (QString::FROMUTF8(component));
  }

  void
  Path::split (QString & dirpath, QString & basename) const
  {
    Pool pool;

    const char * cdirpath;
    const char * cbasename;

    svn_path_split (m_path.TOUTF8(), &cdirpath, &cbasename, pool);
    dirpath = QString::FROMUTF8(cdirpath);
    basename = QString::FROMUTF8(cbasename);
  }

  void
  Path::split (QString & dir, QString & filename, QString & ext) const
  {
    QString basename;

    // first split path into dir and filename+ext
    split (dir, basename);

    // next search for last .
#if QT_VERSION < 0x040000
    int pos = basename.findRev(QChar('.'));
#else
    int pos = basename.lastIndexOf(QChar('.'));
#endif

    if (pos == -1)
    {
      filename = basename;
      ext = QString::fromLatin1("");
    }
    else
    {
      filename = basename.left(pos);
      ext = basename.mid(pos+1);
    }
  }

  Path
  Path::getTempDir ()
  {
    const char * tempdir = 0;
    Pool pool;

    if (apr_temp_dir_get (&tempdir, pool) != APR_SUCCESS)
    {
      tempdir = 0;
    }

    return tempdir;
  }

  unsigned int
  Path::length () const
  {
    return m_path.length ();
  }

  QString
  Path::native () const
  {
    Pool pool;

    return QString::FROMUTF8(svn_path_local_style (m_path.TOUTF8(), pool));
  }

}

/* -----------------------------------------------------------------
 * local variables:
 * eval: (load-file "../../rapidsvn-dev.el")
 * end:
 */
<|endoftext|>


<commit_before>/** \addtogroup examples
  * @{
  * \defgroup algebraic_multigrid algebraic_multigrid
  * @{
  * \brief Benchmark for smoothed algebraic multgrid
  */
#include <ctf.hpp>
using namespace CTF;

void smooth_jacobi(Matrix<> & A, Vector<> & x, Vector <> & b){
  Vector<> d(x.len, *x.wrld);
  d["i"] = A["ii"];
  Transform<>([](double & d){ d= fabs(d) > 0.0 ? 1./d : 0.0; })(d["i"]);
  Matrix<> R(A);
  R["ii"] = 0.0;

  for (int i=0; i<20; i++){
    x["i"] = -1.0*R["ij"]*x["j"];
    x["i"] += b["i"];
    x["i"] *= d["i"];
  }
}

void vcycle(Matrix<> & A, Vector<> & x, Vector<> & b, Matrix<> * T, int n, int nlevel){
  smooth_jacobi(A,x,b);
  Vector<> r(b);
  r["i"] -= A["ij"]*x["j"];
  double rnorm = r.norm2();
  if (A.wrld->rank == 0) printf("residual norm is %E after initial smooth at level %d\n",rnorm,nlevel);
  if (nlevel == 0) return;
  int m = T[0].lens[1];

  Matrix<> P(T[0].lens[0], T[0].lens[1], SP, *T[0].wrld);

  Vector<> d(x.len, *x.wrld);
//  d["i"] = A["ii"];
//  P["ik"] -= d["i"]*A["ij"]*T[0]["jk"];
  Matrix<> D(n,n,SP,*A.wrld);
  Matrix<> P1(T[0].lens[0], T[0].lens[1], SP, *T[0].wrld);
  D["ii"] = A["ii"];
  double omega=.1;
  Transform<>([=](double & d){ d= omega/d; })(D["ii"]);
  P1["ik"] = A["ij"]*T[0]["jk"];
  P["ik"] -=  D["il"]*P1["lk"];
  P["ij"] += T[0]["ij"];

  Vector<> PTr(m, *x.wrld);
  Vector<> zb(m, *b.wrld);

  PTr["i"] = P["ji"]*r["j"];
//  PTb["i"] = P["ji"]*b["j"];

  int atr = 0;
  if (A.is_sparse){
    atr = atr | SP;
  }
  //atr = atr | A.symm;
  Matrix<> AP(n, m, atr, *x.wrld);
  Matrix<> PTAP(m, m, atr, *x.wrld);

  AP["lj"] = A["lk"]*P["kj"];
  PTAP["ij"] = P["li"]*AP["lj"];

  vcycle(PTAP, PTr, zb, T+1, m, nlevel-1);

  x["i"] += P["ij"]*PTr["j"];

  smooth_jacobi(A,x,b);
  r["i"] = b["i"];
  r["i"] -= A["ij"]*x["j"];
  double rnorm2 = r.norm2();
  if (A.wrld->rank == 0) printf("residual norm is %E after final smooth at level %d\n",rnorm2,nlevel);
}

/**
 * \brief computes Multigrid for a 3D regular discretization
 */
int algebraic_multigrid(int     n,
                        double  sp_frac,
                        int     nlvl,
                        int     ndiv,
                        int     decay_exp,
                        World & dw){

  Vector<> x(n, dw);
  Vector<> b(n, dw);
  x.fill_random(0.0, 1.0);
  b.fill_random(0.0, 1.0);
  Matrix<> A(n, n, SP, dw);
  A.fill_sp_random(0.0, 1.0, sp_frac);

  A["ij"] += A["ji"];

  A["ii"] += sqrt((double)n);

//  Transform<>([](double & d){ d = fabs(d).; })(A["ii"]);

//  A.print();

  Vector<int> N(n, dw);
  int64_t * inds;
  int * vals;
  int64_t nvals;
  N.read_local(&nvals, &inds, &vals);
  for (int i=0; i<nvals; i++){
    vals[i] = (int)inds[i];
  }
  N.write(nvals, inds, vals);

  free(vals);
  free(inds);

  int curootn = (int)(pow((double)n,1./3.)+.001);
//  printf("curootn = %d\n",curootn);

  Matrix<std::pair<double, int>> B(n,n,SP,dw,Set<std::pair<double, int>>());

  B["ij"] = Function< double, std::pair<double,int> >([](double d){ return std::pair<double,int>(d,0); })(A["ij"]);

  Transform< int, std::pair<double,int> >([](int i, std::pair<double,int> & d){ d.second = i; } )(N["i"], B["ij"]);
  Transform< int, std::pair<double,int> >([](int i, std::pair<double,int> & d){ d.second = abs(d.second-d.first); } )(N["j"], B["ij"]);
  Transform< std::pair<double,int> >([=](std::pair<double,int> & d){
    int x =  d.second % curootn;
    int y = (d.second / curootn) % curootn;
    int z =  d.second / curootn  / curootn;
    if (x+y+z > 0)
      d.first = d.first/pow((double)(x+y+z),decay_exp/2.);
    }
  )(B["ij"]);

  A["ij"] = Function< std::pair<double,int>, double >([](std::pair<double,int> p){ return p.first; })(B["ij"]);

//  A.print();

  Vector<> r(b);
  r["i"] -= A["ij"]*x["j"];
  double err = r.norm2();

  Matrix<> * T = new Matrix<>[nlvl];
  int m=n;
  for (int i=0; i<nlvl; i++){
    int m2 = m/ndiv;
    T[i] = Matrix<>(m, m2, SP, dw);
    std::vector< Pair<> > pairs;
    for (int64_t j=dw.rank; j<m2; j+=dw.np){
      for (int k=0; k<ndiv; k++){
        pairs.push_back(Pair<>(j*m+j*ndiv+k, 1.0));
      }
    }
    T[i].write(pairs.size(), &(pairs[0]));
//    T[i].print();
    m = m2;
  }

  vcycle(A, x, b, T, n, nlvl);

  delete [] T;

  r["i"]  = b["i"];
  r["i"] -= A["ij"]*x["j"];
  double err2 = r.norm2();

  bool pass = err2 < err;

  if (dw.rank == 0){
#ifndef TEST_SUITE
    printf("original err = %E, new err = %E\n",err,err2);
#endif
    if (pass)
      printf("{ algebraic multigrid method } passed \n");
    else
      printf("{ algebraic multigrid method } failed \n");
  }
  return pass;
}

#ifndef TEST_SUITE
char* getCmdOption(char ** begin,
                   char ** end,
                   const   std::string & option){
  char ** itr = std::find(begin, end, option);
  if (itr != end && ++itr != end){
    return *itr;
  }
  return 0;
}

int main(int argc, char ** argv){
  int rank, np, n, pass, nlvl, ndiv, decay_exp;
  double sp_frac;
  int const in_num = argc;
  char ** input_str = argv;

  MPI_Init(&argc, &argv);
  MPI_Comm_rank(MPI_COMM_WORLD, &rank);
  MPI_Comm_size(MPI_COMM_WORLD, &np);

  if (getCmdOption(input_str, input_str+in_num, "-n")){
    n = atoi(getCmdOption(input_str, input_str+in_num, "-n"));
    if (n < 0) n = 16;
  } else n = 16;

  if (getCmdOption(input_str, input_str+in_num, "-nlvl")){
    nlvl = atoi(getCmdOption(input_str, input_str+in_num, "-nlvl"));
    if (nlvl < 0) nlvl = 3;
  } else nlvl = 3;

  if (getCmdOption(input_str, input_str+in_num, "-ndiv")){
    ndiv = atoi(getCmdOption(input_str, input_str+in_num, "-ndiv"));
    if (ndiv < 0) ndiv = 2;
  } else ndiv = 2;

  if (getCmdOption(input_str, input_str+in_num, "-decay_exp")){
    decay_exp = atoi(getCmdOption(input_str, input_str+in_num, "-decay_exp"));
    if (decay_exp < 0) decay_exp = 3;
  } else decay_exp = 3;

  if (getCmdOption(input_str, input_str+in_num, "-sp_frac")){
    sp_frac = atof(getCmdOption(input_str, input_str+in_num, "-sp_frac"));
    if (sp_frac < 0) sp_frac = .01;
  } else sp_frac = .01;

  int tot_ndiv=1;
  for (int i=0; i<nlvl; i++){ tot_ndiv *= ndiv; }

  assert(n%tot_ndiv == 0);

  {
    World dw(argc, argv);

    if (rank == 0){
      printf("Running algebraic smoothed multigrid method with %d levels with divisor %d in V-cycle, %d elements, decayed based on 3D indexing with decay exponent of %d\n",nlvl,ndiv,n,decay_exp);
    }
    pass = algebraic_multigrid(n, sp_frac, nlvl, ndiv, decay_exp, dw);
    assert(pass);
  }

  MPI_Finalize();
  return 0;
}
/**
 * @}
 * @}
 */

#endif
<commit_msg>now really doing something sensible in algebraic multigrid<commit_after>/** \addtogroup examples
  * @{
  * \defgroup algebraic_multigrid algebraic_multigrid
  * @{
  * \brief Benchmark for smoothed algebraic multgrid
  */
#include <ctf.hpp>
using namespace CTF;

void smooth_jacobi(Matrix<> & A, Vector<> & x, Vector <> & b, int nsmooth){
  Vector<> d(x.len, *x.wrld);
  d["i"] = A["ii"];
  Transform<>([](double & d){ d= fabs(d) > 0.0 ? 1./d : 0.0; })(d["i"]);
  Matrix<> R(A);
  R["ii"] = 0.0;

  //20 iterations of Jacobi, should probably be a parameter or some convergence check instead
  for (int i=0; i<nsmooth; i++){
    x["i"] = -1.0*R["ij"]*x["j"];
    x["i"] += b["i"];
    x["i"] *= d["i"];
  }
}

void vcycle(Matrix<> & A, Vector<> & x, Vector<> & b, Matrix<> * T, int n, int nlevel, int nsmooth){
  //do smoothing using Jacobi
  Vector<> r(b);
  r["i"] -= A["ij"]*x["j"];
  double rnorm0 = r.norm2();
  smooth_jacobi(A,x,b,nsmooth);
  r["i"] = b["i"];
  r["i"] -= A["ij"]*x["j"];
  double rnorm = r.norm2();
  if (nlevel == 0){
    if (A.wrld->rank == 0) printf("At level %d (coarsest level), residual norm was %1.2E initially\n",nlevel,rnorm0);
    if (A.wrld->rank == 0) printf("At level %d (coarsest level), residual norm was %1.2E after smooth\n",nlevel,rnorm);
    return;
  }
  Vector<> x2(x);
  Vector<> r2(x);

  smooth_jacobi(A,x2,b,nsmooth);
  r2["i"] = b["i"];
  r2["i"] -= A["ij"]*x2["j"];
  double rnorm_alt = r2.norm2();

  int m = T[0].lens[1];

//  Vector<> d(x.len, *x.wrld);
//  d["i"] = A["ii"];
//  P["ik"] -= d["i"]*A["ij"]*T[0]["jk"];
  //smooth the restriction/interpolation operator P = (I-omega*diag(A)^{-1}*A)T
  Matrix<> P(T[0].lens[0], T[0].lens[1], SP, *T[0].wrld);
  Matrix<> D(n,n,SP,*A.wrld);
  D["ii"] = A["ii"];
  double omega=.1;
  Transform<>([=](double & d){ d= omega/d; })(D["ii"]);
  P["ik"] = A["ij"]*T[0]["jk"];
  P["ik"] =  D["il"]*P["lk"];
  P["ij"] += T[0]["ij"];

  //restrict residual vector
  Vector<> PTr(m, *x.wrld);
  PTr["i"] = P["ji"]*r["j"];

  //coarses initial guess should be zeros
  Vector<> zx(m, *b.wrld);

  int atr = 0;
  if (A.is_sparse){
    atr = atr | SP;
  }
  //atr = atr | A.symm;
  Matrix<> AP(n, m, atr, *x.wrld);
  Matrix<> PTAP(m, m, atr, *x.wrld);

  //restrict A via triple matrix product, should probably be done outside v-cycle
  AP["lj"] = A["lk"]*P["kj"];
  PTAP["ij"] = P["li"]*AP["lj"];

  //recurse into coarser level
  vcycle(PTAP, zx, PTr, T+1, m, nlevel-1, nsmooth);

//  zx.print();

  //interpolate solution to residual equation at coraser level back
  x["i"] += P["ij"]*zx["j"];

  //smooth new solution
  r["i"] = b["i"];
  r["i"] -= A["ij"]*x["j"];
  double rnorm2 = r.norm2();
  smooth_jacobi(A,x,b,nsmooth);
  r["i"] = b["i"];
  r["i"] -= A["ij"]*x["j"];
  double rnorm3 = r.norm2();
    if (A.wrld->rank == 0) printf("At level %d, residual norm was %1.2E initially\n",nlevel,rnorm0);
  if (A.wrld->rank == 0) printf("At level %d, residual norm was %1.2E after initial smooth\n",nlevel,rnorm);
  if (A.wrld->rank == 0) printf("At level %d, residual norm was %1.2E after coarse recursion\n",nlevel,rnorm2);
  if (A.wrld->rank == 0) printf("At level %d, residual norm was %1.2E after final smooth\n",nlevel,rnorm3);
  if (A.wrld->rank == 0) printf("At level %d, residual norm was %1.2E if we skipped the coarsening\n",nlevel,rnorm_alt);
}

/**
 * \brief computes Multigrid for a 3D regular discretization
 */
int algebraic_multigrid(int     n,
                        double  sp_frac,
                        int     nlvl,
                        int     ndiv,
                        int     nsmooth,
                        int     decay_exp,
                        World & dw){

  Vector<> x(n, dw);
  Vector<> b(n, dw);
  x.fill_random(0.0, 1.0);
  b.fill_random(0.0, 1.0);
  Matrix<> A(n, n, SP, dw);
  A.fill_sp_random(0.0, 1.0, sp_frac);

  A["ij"] += A["ji"];

  A["ii"] += sqrt((double)n);

//  Transform<>([](double & d){ d = fabs(d).; })(A["ii"]);

//  A.print();

  Vector<int> N(n, dw);
  int64_t * inds;
  int * vals;
  int64_t nvals;
  N.read_local(&nvals, &inds, &vals);
  for (int i=0; i<nvals; i++){
    vals[i] = (int)inds[i];
  }
  N.write(nvals, inds, vals);

  free(vals);
  free(inds);

  int curootn = (int)(pow((double)n,1./3.)+.001);
//  printf("curootn = %d\n",curootn);

  Matrix<std::pair<double, int>> B(n,n,SP,dw,Set<std::pair<double, int>>());

  B["ij"] = Function< double, std::pair<double,int> >([](double d){ return std::pair<double,int>(d,0); })(A["ij"]);

  Transform< int, std::pair<double,int> >([](int i, std::pair<double,int> & d){ d.second = i; } )(N["i"], B["ij"]);
  Transform< int, std::pair<double,int> >([](int i, std::pair<double,int> & d){ d.second = abs(d.second-d.first); } )(N["j"], B["ij"]);
  Transform< std::pair<double,int> >([=](std::pair<double,int> & d){
    int x =  d.second % curootn;
    int y = (d.second / curootn) % curootn;
    int z =  d.second / curootn  / curootn;
    if (x+y+z > 0)
      d.first = d.first/pow((double)(x+y+z),decay_exp/2.);
    }
  )(B["ij"]);

  A["ij"] = Function< std::pair<double,int>, double >([](std::pair<double,int> p){ return p.first; })(B["ij"]);

  Vector<> r(b);
  r["i"] -= A["ij"]*x["j"];
  double err = r.norm2();

  Matrix<> * T = new Matrix<>[nlvl];
  int m=n;
  for (int i=0; i<nlvl; i++){
    int m2 = m/ndiv;
    T[i] = Matrix<>(m, m2, SP, dw);
    std::vector< Pair<> > pairs;
    for (int64_t j=dw.rank; j<m2; j+=dw.np){
      for (int k=0; k<ndiv; k++){
        pairs.push_back(Pair<>(j*m+j*ndiv+k, 1.0));
      }
    }
    T[i].write(pairs.size(), &(pairs[0]));
    m = m2;
  }

  vcycle(A, x, b, T, n, nlvl, nsmooth);

  delete [] T;

  r["i"]  = b["i"];
  r["i"] -= A["ij"]*x["j"];
  double err2 = r.norm2();

  bool pass = err2 < err;

  if (dw.rank == 0){
#ifndef TEST_SUITE
    printf("original err = %E, new err = %E\n",err,err2);
#endif
    if (pass)
      printf("{ algebraic multigrid method } passed \n");
    else
      printf("{ algebraic multigrid method } failed \n");
  }
  return pass;
}

#ifndef TEST_SUITE
char* getCmdOption(char ** begin,
                   char ** end,
                   const   std::string & option){
  char ** itr = std::find(begin, end, option);
  if (itr != end && ++itr != end){
    return *itr;
  }
  return 0;
}

int main(int argc, char ** argv){
  int rank, np, n, pass, nlvl, ndiv, decay_exp, nsmooth;
  double sp_frac;
  int const in_num = argc;
  char ** input_str = argv;

  MPI_Init(&argc, &argv);
  MPI_Comm_rank(MPI_COMM_WORLD, &rank);
  MPI_Comm_size(MPI_COMM_WORLD, &np);

  if (getCmdOption(input_str, input_str+in_num, "-n")){
    n = atoi(getCmdOption(input_str, input_str+in_num, "-n"));
    if (n < 0) n = 16;
  } else n = 16;

  if (getCmdOption(input_str, input_str+in_num, "-nlvl")){
    nlvl = atoi(getCmdOption(input_str, input_str+in_num, "-nlvl"));
    if (nlvl < 0) nlvl = 3;
  } else nlvl = 3;

  if (getCmdOption(input_str, input_str+in_num, "-ndiv")){
    ndiv = atoi(getCmdOption(input_str, input_str+in_num, "-ndiv"));
    if (ndiv < 0) ndiv = 2;
  } else ndiv = 2;

  if (getCmdOption(input_str, input_str+in_num, "-nsmooth")){
    nsmooth = atoi(getCmdOption(input_str, input_str+in_num, "-nsmooth"));
    if (nsmooth < 0) nsmooth = 3;
  } else nsmooth = 3;

  if (getCmdOption(input_str, input_str+in_num, "-decay_exp")){
    decay_exp = atoi(getCmdOption(input_str, input_str+in_num, "-decay_exp"));
    if (decay_exp < 0) decay_exp = 3;
  } else decay_exp = 3;

  if (getCmdOption(input_str, input_str+in_num, "-sp_frac")){
    sp_frac = atof(getCmdOption(input_str, input_str+in_num, "-sp_frac"));
    if (sp_frac < 0) sp_frac = .01;
  } else sp_frac = .01;

  int tot_ndiv=1;
  for (int i=0; i<nlvl; i++){ tot_ndiv *= ndiv; }

  assert(n%tot_ndiv == 0);

  {
    World dw(argc, argv);

    if (rank == 0){
      printf("Running algebraic smoothed multigrid method with %d levels with divisor %d in V-cycle, %d elements, %d smooth iterations, decayed based on 3D indexing with decay exponent of %d\n",nlvl,ndiv,n,nsmooth, decay_exp);
    }
    pass = algebraic_multigrid(n, sp_frac, nlvl, ndiv, nsmooth, decay_exp, dw);
    assert(pass);
  }

  MPI_Finalize();
  return 0;
}
/**
 * @}
 * @}
 */

#endif
<|endoftext|>


<commit_before>/*
 * HyPerCol.cpp
 *
 *  Created on: Jul 30, 2008
 *      Author: Craig Rasmussen
 */

#define TIMER_ON
#undef TIMESTEP_OUTPUT

#include "HyPerCol.hpp"
#include "InterColComm.hpp"
#include "../connections/PVConnection.h"
#include "../io/clock.h"
#include "../io/imageio.hpp"
#include "../io/io.h"
#include "../utils/pv_random.h"

#include <assert.h>
#include <math.h>
#include <memory.h>
#include <stdio.h>
#include <stdlib.h>

namespace PV {

HyPerCol::HyPerCol(const char * name, int argc, char * argv[])
         : warmStart(false), isInitialized(false)
{
   int opencl_device = 1;  // default to CPU for now

   // TODO - fix these numbers to dynamically grow
   maxLayers = MAX_LAYERS;
   maxConnections = MAX_CONNECTIONS;

   this->name = strdup(name);
   this->runTimer = new Timer();

   char * param_file;
   simTime = 0;
   numLayers = 0;
   numConnections = 0;
   layers = (HyPerLayer **) malloc(maxLayers * sizeof(HyPerLayer *));
   connections = (HyPerConn **) malloc(maxConnections * sizeof(HyPerConn *));

   numSteps = 2;
   image_file = NULL;
   param_file = NULL;
   unsigned long random_seed = 0;
   parse_options(argc, argv, &image_file, &param_file, &numSteps, &opencl_device, &random_seed);

   // run only on CPU for now
   initializeThreads(opencl_device);

   // estimate for now
   // TODO -get rid of maxGroups
   int maxGroups = 2*(maxLayers + maxConnections);
   params = new PVParams(param_file, maxGroups);

   icComm = new InterColComm(&argc, &argv);

   // initialize random seed
   //
   random_seed = getRandomSeed();
   random_seed = params->value(name, "randomSeed", random_seed);
   pv_srandom(random_seed);

   if (param_file != NULL) free(param_file);

   deltaTime = DELTA_T;
   if (params->present(name, "dt")) deltaTime = params->value(name, "dt");

   int status = -1;
   if (image_file) {
      status = getImageInfo(image_file, icComm, &imageLoc);
   }

   if (status) {
      imageLoc.nxGlobal = (int) params->value(name, "nx");
      imageLoc.nyGlobal = (int) params->value(name, "ny");

      // set loc based on global parameters and processor partitioning
      //
      setLayerLoc(&imageLoc, 1.0f, 1.0f, 0, 1);
   }

   runDelegate = NULL;

   numProbes = 0;
   probes = NULL;
}

HyPerCol::~HyPerCol()
{
   int n;

   finalizeThreads();

   if (image_file != NULL) free(image_file);

   for (n = 0; n < numConnections; n++) {
      delete connections[n];
   }

   for (n = 0; n < numLayers; n++) {
      // TODO: check to see if finalize called
      if (layers[n] != NULL) {
         delete layers[n]; // will call *_finalize
      }
      else {
         // TODO move finalize
         // PVLayer_finalize(getCLayer(n));
      }
   }

   delete icComm;

   printf("%16s: total time in %6s %10s: ", name, "column", "run");
   runTimer->elapsed_time();
   delete runTimer;

   free(connections);
   free(layers);
   free(name);
}

int HyPerCol::initFinish(void)
{
   int status = 0;

   for (int i = 0; i < this->numLayers; i++) {
      status = layers[i]->initFinish();
      if (status != 0) {
         fprintf(stderr, "[%d]: HyPerCol::initFinish: ERROR condition, exiting...\n", this->columnId());
         exit(status);
      }
   }

   log_parameters(numSteps, image_file);

   isInitialized = true;

   return status;
}

int HyPerCol::columnId()
{
   return icComm->commRank();
}

int HyPerCol::numberOfColumns()
{
   return icComm->numCommRows() * icComm->numCommColumns();
}

int HyPerCol::commColumn(int colId)
{
   return colId % icComm->numCommColumns();
}

int HyPerCol::commRow(int colId)
{
   return colId / icComm->numCommColumns();
}

int HyPerCol::setLayerLoc(PVLayerLoc * layerLoc,
                          float nxScale, float nyScale, int margin, int nf)
{
   layerLoc->nxGlobal = (int) (nxScale * imageLoc.nxGlobal);
   layerLoc->nyGlobal = (int) (nyScale * imageLoc.nyGlobal);

   // partition input space based on the number of processor
   // columns and rows
   //

   layerLoc->nx = layerLoc->nxGlobal / icComm->numCommColumns();
   layerLoc->ny = layerLoc->nyGlobal / icComm->numCommRows();

   assert(layerLoc->nxGlobal == layerLoc->nx * icComm->numCommColumns());
   assert(layerLoc->nyGlobal == layerLoc->ny * icComm->numCommRows());

   layerLoc->kx0 = layerLoc->nx * icComm->commColumn();
   layerLoc->ky0 = layerLoc->ny * icComm->commRow();

   layerLoc->nPad = margin;
   layerLoc->nBands = nf;

   return 0;
}

int HyPerCol::addLayer(HyPerLayer * l)
{
   assert(numLayers < maxLayers);
   l->columnWillAddLayer(icComm, numLayers);
   layers[numLayers++] = l;
   return (numLayers - 1);
}

int HyPerCol::addConnection(HyPerConn * conn)
{
   int connId = numConnections;

   assert(numConnections < maxConnections);

   // numConnections is the ID of this connection
   icComm->subscribe(conn);

   connections[numConnections++] = conn;

   return connId;
}

int HyPerCol::run(int nTimeSteps)
{
   checkMarginWidths();

   int step = 0;
   float stopTime = simTime + nTimeSteps * deltaTime;
   const bool exitOnFinish = false;

   if (!isInitialized) {
      initFinish();
   }

   numSteps = nTimeSteps;

#ifdef DEBUG_OUTPUT
   if (columnId() == 0) {
      printf("[0]: HyPerCol: running...\n");  fflush(stdout);
   }
#endif

   // publish initial conditions
   //
   for (int l = 0; l < numLayers; l++) {
      layers[l]->publish(icComm, simTime);
   }

   // wait for all published data to arrive
   //
   for (int l = 0; l < numLayers; l++) {
      icComm->wait(layers[l]->getLayerId());
   }

   if (runDelegate) {
      // let delegate advance the time
      //
      runDelegate->run(simTime, stopTime);
   }

#ifdef TIMER_ON
      start_clock();
#endif
   // time loop
   //
   while (simTime < stopTime) {
      simTime = advanceTime(simTime);
      step += 1;

#ifdef TIMER_ON
      if (step == 10) start_clock();
#endif

   }  // end time loop

#ifdef DEBUG_OUTPUT
   if (columnId() == 0) {
      printf("[0]: HyPerCol::run done...\n");  fflush(stdout);
   }
#endif

   exitRunLoop(exitOnFinish);

#ifdef TIMER_ON
      stop_clock();
#endif

   return 0;
}

float HyPerCol::advanceTime(float sim_time)
{
#ifdef TIMESTEP_OUTPUT
   int nstep = (int) (sim_time/getDeltaTime());
   if (nstep%2000 == 0 && columnId() == 0) {
      printf("   [%d]: time==%f\n", columnId(), sim_time);
   }
#endif

   runTimer->start();

   // At this point all activity from the previous time step have
   // been delivered to the data store.
   //

   // update the connections (weights)
   //
   for (int c = 0; c < numConnections; c++) {
      connections[c]->updateState(sim_time, deltaTime);
      connections[c]->outputState(sim_time);
   }

   // Update the layers (activity)
   //
   for (int l = 0; l < numLayers; l++) {
      layers[l]->outputState(sim_time);

      // deliver new synaptic activity to layer
      //
      icComm->deliver(this, layers[l]->getLayerId());

      // update layer and calculate new activity
      //
      layers[l]->updateState(sim_time, deltaTime);

      // TODO - move this to layer
      // Advance time level so we have a new place in data store
      // to copy the data.  This should be done immediately after before
      // publish so there is a place to publish and deliver the data to
      // but no one can access the data store (except to publish) until
      // wait has been called.  This should be fixed so that publish goes
      // to last time level and level is advanced only after wait.
      icComm->increaseTimeLevel(layers[l]->getLayerId());

      layers[l]->publish(icComm, sim_time);
   }

   // wait for all published data to arrive
   //
   for (int l = 0; l < numLayers; l++) {
      icComm->wait(layers[l]->getLayerId());
   }

   // make sure simTime is updated even if HyPerCol isn't running time loop

   float outputTime = simTime; // so that outputState is called with the correct time
                               // but doesn't effect runTimer

   simTime = sim_time + deltaTime;

   runTimer->stop();

   outputState(outputTime);

   return simTime;
}

int HyPerCol::exitRunLoop(bool exitOnFinish)
{
   int status = 0;

   // output final state of layers and connections
   //
   bool last = true;

   for (int l = 0; l < numLayers; l++) {
      layers[l]->writeState(layers[l]->getName(), simTime, last);
   }

   for (int c = 0; c < numConnections; c++) {
      connections[c]->outputState(simTime, last);
   }

   if (exitOnFinish) {
      delete this;
      exit(0);
   }

   return status;
}

int HyPerCol::initializeThreads(int device)
{
   clDevice = new CLDevice(device);
   return 0;
}

int HyPerCol::finalizeThreads()
{
   delete clDevice;
   return 0;
}

int HyPerCol::loadState()
{
   return 0;
}

int HyPerCol::writeState()
{
   for (int l = 0; l < numLayers; l++) {
      layers[l]->writeState(OUTPUT_PATH, simTime);
   }
   return 0;
}

int HyPerCol::insertProbe(ColProbe * p)
{
   ColProbe ** newprobes;
   newprobes = (ColProbe **) malloc((numProbes + 1) * sizeof(ColProbe *));
   assert(newprobes != NULL);

   for (int i = 0; i < numProbes; i++) {
      newprobes[i] = probes[i];
   }
   delete probes;

   probes = newprobes;
   probes[numProbes] = p;

   return ++numProbes;
}

int HyPerCol::outputState(float time)
{
   for( int n = 0; n < numProbes; n++ ) {
       probes[n]->outputState(time, this);
   }
   return EXIT_SUCCESS;
}

int HyPerCol::checkMarginWidths() {
   // For each connection, make sure that the post-synaptic margin width is
   // large enough for the patch size.

   // TODO instead of having marginWidth supplied to HyPerLayers in the
   // params.pv file, calculate them based on the patch sizes here.
   // Hard part:  numExtended-sized quantities (e.g. clayer->activity) can't
   // be allocated and initialized until after nPad is determined.

   int status = EXIT_SUCCESS;
   int status1, status2;
   for( int c=0; c < numConnections; c++ ) {
      HyPerConn * conn = connections[c];
      int padding = conn->pre->getLayerLoc()->nPad;

      int xScalePre = conn->preSynapticLayer()->getXScale();
      int xScalePost = conn->postSynapticLayer()->getXScale();
      status1 = zCheckMarginWidth(conn, "x", padding, conn->xPatchSize(), xScalePre, xScalePost, status);

      int yScalePre = conn->preSynapticLayer()->getYScale();
      int yScalePost = conn->postSynapticLayer()->getYScale();
      status2 = zCheckMarginWidth(conn, "y", padding, conn->yPatchSize(), yScalePre, yScalePost, status);
      status = (status == EXIT_SUCCESS && status1 == EXIT_SUCCESS && status2 == EXIT_SUCCESS) ?
               EXIT_SUCCESS : EXIT_FAILURE;
   }
   return status;
}  // end HyPerCol::checkMarginWidths()

int HyPerCol::zCheckMarginWidth(HyPerConn * conn, const char * dim, int padding, int patchSize, int scalePre, int scalePost, int prevStatus) {
   int status;
   int scaleDiff = scalePre - scalePost;
   // if post has higher neuronal density than pre, scaleDiff < 0.
   int needed = scaleDiff > 0 ? ( patchSize/( (int) powf(2,scaleDiff) )/2 ) :
                                ( (patchSize/2) * ( (int) powf(2,-scaleDiff) ) );
   if( padding < needed ) {
      if( prevStatus == EXIT_SUCCESS ) {
         fprintf(stderr, "Margin width error.\n");
      }
      fprintf(stderr, "Connection \"%s\", dimension %s:\n", conn->getName(), dim);
      fprintf(stderr, "    Margin width %d, patch size %d, presynaptic scale %d, postsynaptic scale %d\n",
              padding, patchSize, scalePre, scalePost);
      fprintf(stderr, "    Needed margin width=%d\n", needed);
      status = EXIT_FAILURE;
      if( numberOfColumns() > 1 || padding > 0 ) {
         fprintf(stderr, "Exiting.\n");
         exit(EXIT_FAILURE);
      }
      else {
         fprintf(stderr, "Continuing, but there may be undesirable edge effects.\n");
      }
   }
   else status = EXIT_SUCCESS;
   return status;
}

} // PV namespace
<commit_msg>Many code changes related to using OpenCL kernels.<commit_after>/*
 * HyPerCol.cpp
 *
 *  Created on: Jul 30, 2008
 *      Author: Craig Rasmussen
 */

#define TIMER_ON
#undef TIMESTEP_OUTPUT

#include "HyPerCol.hpp"
#include "InterColComm.hpp"
#include "../connections/PVConnection.h"
#include "../io/clock.h"
#include "../io/imageio.hpp"
#include "../io/io.h"
#include "../utils/pv_random.h"

#include <assert.h>
#include <math.h>
#include <memory.h>
#include <stdio.h>
#include <stdlib.h>

namespace PV {

HyPerCol::HyPerCol(const char * name, int argc, char * argv[])
         : warmStart(false), isInitialized(false)
{
   int opencl_device = 1;  // default to CPU for now

   // TODO - fix these numbers to dynamically grow
   maxLayers = MAX_LAYERS;
   maxConnections = MAX_CONNECTIONS;

   this->name = strdup(name);
   this->runTimer = new Timer();

   char * param_file;
   simTime = 0;
   numLayers = 0;
   numConnections = 0;
   layers = (HyPerLayer **) malloc(maxLayers * sizeof(HyPerLayer *));
   connections = (HyPerConn **) malloc(maxConnections * sizeof(HyPerConn *));

   numSteps = 2;
   image_file = NULL;
   param_file = NULL;
   unsigned long random_seed = 0;
   parse_options(argc, argv, &image_file, &param_file, &numSteps, &opencl_device, &random_seed);

   // run only on CPU for now
   initializeThreads(opencl_device);

   // estimate for now
   // TODO -get rid of maxGroups
   int maxGroups = 2*(maxLayers + maxConnections);
   params = new PVParams(param_file, maxGroups);

   icComm = new InterColComm(&argc, &argv);

   // initialize random seed
   //
   random_seed = getRandomSeed();
   random_seed = params->value(name, "randomSeed", random_seed);
   pv_srandom(random_seed);

   if (param_file != NULL) free(param_file);

   deltaTime = DELTA_T;
   if (params->present(name, "dt")) deltaTime = params->value(name, "dt");

   int status = -1;
   if (image_file) {
      status = getImageInfo(image_file, icComm, &imageLoc);
   }

   if (status) {
      imageLoc.nxGlobal = (int) params->value(name, "nx");
      imageLoc.nyGlobal = (int) params->value(name, "ny");

      // set loc based on global parameters and processor partitioning
      //
      setLayerLoc(&imageLoc, 1.0f, 1.0f, 0, 1);
   }

   runDelegate = NULL;

   numProbes = 0;
   probes = NULL;
}

HyPerCol::~HyPerCol()
{
   int n;

   finalizeThreads();

   if (image_file != NULL) free(image_file);

   for (n = 0; n < numConnections; n++) {
      delete connections[n];
   }

   for (n = 0; n < numLayers; n++) {
      // TODO: check to see if finalize called
      if (layers[n] != NULL) {
         delete layers[n]; // will call *_finalize
      }
      else {
         // TODO move finalize
         // PVLayer_finalize(getCLayer(n));
      }
   }

   delete icComm;

   printf("%16s: total time in %6s %10s: ", name, "column", "run");
   runTimer->elapsed_time();
   delete runTimer;

   free(connections);
   free(layers);
   free(name);
}

int HyPerCol::initFinish(void)
{
   int status = 0;

   for (int i = 0; i < this->numLayers; i++) {
      status = layers[i]->initFinish();
      if (status != 0) {
         fprintf(stderr, "[%d]: HyPerCol::initFinish: ERROR condition, exiting...\n", this->columnId());
         exit(status);
      }
   }

   log_parameters(numSteps, image_file);

   isInitialized = true;

   return status;
}

int HyPerCol::columnId()
{
   return icComm->commRank();
}

int HyPerCol::numberOfColumns()
{
   return icComm->numCommRows() * icComm->numCommColumns();
}

int HyPerCol::commColumn(int colId)
{
   return colId % icComm->numCommColumns();
}

int HyPerCol::commRow(int colId)
{
   return colId / icComm->numCommColumns();
}

int HyPerCol::setLayerLoc(PVLayerLoc * layerLoc,
                          float nxScale, float nyScale, int margin, int nf)
{
   layerLoc->nxGlobal = (int) (nxScale * imageLoc.nxGlobal);
   layerLoc->nyGlobal = (int) (nyScale * imageLoc.nyGlobal);

   // partition input space based on the number of processor
   // columns and rows
   //

   layerLoc->nx = layerLoc->nxGlobal / icComm->numCommColumns();
   layerLoc->ny = layerLoc->nyGlobal / icComm->numCommRows();

   assert(layerLoc->nxGlobal == layerLoc->nx * icComm->numCommColumns());
   assert(layerLoc->nyGlobal == layerLoc->ny * icComm->numCommRows());

   layerLoc->kx0 = layerLoc->nx * icComm->commColumn();
   layerLoc->ky0 = layerLoc->ny * icComm->commRow();

   layerLoc->nf = nf;
   layerLoc->nb = margin;

   layerLoc->halo.lt = margin;
   layerLoc->halo.rt = margin;
   layerLoc->halo.dn = margin;
   layerLoc->halo.up = margin;

   return 0;
}

int HyPerCol::addLayer(HyPerLayer * l)
{
   assert(numLayers < maxLayers);
   l->columnWillAddLayer(icComm, numLayers);
   layers[numLayers++] = l;
   return (numLayers - 1);
}

int HyPerCol::addConnection(HyPerConn * conn)
{
   int connId = numConnections;

   assert(numConnections < maxConnections);

   // numConnections is the ID of this connection
   icComm->subscribe(conn);

   connections[numConnections++] = conn;

   return connId;
}

int HyPerCol::run(int nTimeSteps)
{
   checkMarginWidths();

   int step = 0;
   float stopTime = simTime + nTimeSteps * deltaTime;
   const bool exitOnFinish = false;

   if (!isInitialized) {
      initFinish();
   }

   numSteps = nTimeSteps;

#ifdef DEBUG_OUTPUT
   if (columnId() == 0) {
      printf("[0]: HyPerCol: running...\n");  fflush(stdout);
   }
#endif

   // publish initial conditions
   //
   for (int l = 0; l < numLayers; l++) {
      layers[l]->publish(icComm, simTime);
   }

   // wait for all published data to arrive
   //
   for (int l = 0; l < numLayers; l++) {
      icComm->wait(layers[l]->getLayerId());
   }

   if (runDelegate) {
      // let delegate advance the time
      //
      runDelegate->run(simTime, stopTime);
   }

#ifdef TIMER_ON
      start_clock();
#endif
   // time loop
   //
   while (simTime < stopTime) {
      simTime = advanceTime(simTime);
      step += 1;

#ifdef TIMER_ON
      if (step == 10) start_clock();
#endif

   }  // end time loop

#ifdef DEBUG_OUTPUT
   if (columnId() == 0) {
      printf("[0]: HyPerCol::run done...\n");  fflush(stdout);
   }
#endif

   exitRunLoop(exitOnFinish);

#ifdef TIMER_ON
      stop_clock();
#endif

   return 0;
}

float HyPerCol::advanceTime(float sim_time)
{
#ifdef TIMESTEP_OUTPUT
   int nstep = (int) (sim_time/getDeltaTime());
   if (nstep%2000 == 0 && columnId() == 0) {
      printf("   [%d]: time==%f\n", columnId(), sim_time);
   }
#endif

   runTimer->start();

   // At this point all activity from the previous time step have
   // been delivered to the data store.
   //

   // update the connections (weights)
   //
   for (int c = 0; c < numConnections; c++) {
      connections[c]->updateState(sim_time, deltaTime);
      connections[c]->outputState(sim_time);
   }

   // Update the layers (activity)
   //
   for (int l = 0; l < numLayers; l++) {
      layers[l]->outputState(sim_time);

      // deliver new synaptic activity to layer
      //
      layers[l]->triggerReceive(icComm);

      // update layer and calculate new activity
      //
      layers[l]->updateState(sim_time, deltaTime);
   }

   // This loop separate from the update layer loop above
   // to provide time for layer data to be copied from
   // the OpenCL device.
   //
   for (int l = 0; l < numLayers; l++) {
      layers[l]->updateBorder(sim_time, deltaTime);

      // TODO - move this to layer
      // Advance time level so we have a new place in data store
      // to copy the data.  This should be done immediately before
      // publish so there is a place to publish and deliver the data to.
      // No one can access the data store (except to publish) until
      // wait has been called.  This should be fixed so that publish goes
      // to last time level and level is advanced only after wait.
      icComm->increaseTimeLevel(layers[l]->getLayerId());

      layers[l]->publish(icComm, sim_time);
   }

   // wait for all published data to arrive
   //
   for (int l = 0; l < numLayers; l++) {
      layers[l]->waitOnPublish(icComm);
   }

   // make sure simTime is updated even if HyPerCol isn't running time loop

   float outputTime = simTime; // so that outputState is called with the correct time
                               // but doesn't effect runTimer

   simTime = sim_time + deltaTime;

   runTimer->stop();

   outputState(outputTime);

   return simTime;
}

int HyPerCol::exitRunLoop(bool exitOnFinish)
{
   int status = 0;

   // output final state of layers and connections
   //
   bool last = true;

   for (int l = 0; l < numLayers; l++) {
      layers[l]->writeState(layers[l]->getName(), simTime, last);
   }

   for (int c = 0; c < numConnections; c++) {
      connections[c]->outputState(simTime, last);
   }

   if (exitOnFinish) {
      delete this;
      exit(0);
   }

   return status;
}

int HyPerCol::initializeThreads(int device)
{
   clDevice = new CLDevice(device);
   return 0;
}

int HyPerCol::finalizeThreads()
{
   delete clDevice;
   return 0;
}

int HyPerCol::loadState()
{
   return 0;
}

int HyPerCol::writeState()
{
   for (int l = 0; l < numLayers; l++) {
      layers[l]->writeState(OUTPUT_PATH, simTime);
   }
   return 0;
}

int HyPerCol::insertProbe(ColProbe * p)
{
   ColProbe ** newprobes;
   newprobes = (ColProbe **) malloc((numProbes + 1) * sizeof(ColProbe *));
   assert(newprobes != NULL);

   for (int i = 0; i < numProbes; i++) {
      newprobes[i] = probes[i];
   }
   delete probes;

   probes = newprobes;
   probes[numProbes] = p;

   return ++numProbes;
}

int HyPerCol::outputState(float time)
{
   for( int n = 0; n < numProbes; n++ ) {
       probes[n]->outputState(time, this);
   }
   return EXIT_SUCCESS;
}

int HyPerCol::checkMarginWidths() {
   // For each connection, make sure that the post-synaptic margin width is
   // large enough for the patch size.

   // TODO instead of having marginWidth supplied to HyPerLayers in the
   // params.pv file, calculate them based on the patch sizes here.
   // Hard part:  numExtended-sized quantities (e.g. clayer->activity) can't
   // be allocated and initialized until after nPad is determined.

   int status = EXIT_SUCCESS;
   int status1, status2;
   for( int c=0; c < numConnections; c++ ) {
      HyPerConn * conn = connections[c];
      int padding = conn->pre->getLayerLoc()->nb;

      int xScalePre = conn->preSynapticLayer()->getXScale();
      int xScalePost = conn->postSynapticLayer()->getXScale();
      status1 = zCheckMarginWidth(conn, "x", padding, conn->xPatchSize(), xScalePre, xScalePost, status);

      int yScalePre = conn->preSynapticLayer()->getYScale();
      int yScalePost = conn->postSynapticLayer()->getYScale();
      status2 = zCheckMarginWidth(conn, "y", padding, conn->yPatchSize(), yScalePre, yScalePost, status);
      status = (status == EXIT_SUCCESS && status1 == EXIT_SUCCESS && status2 == EXIT_SUCCESS) ?
               EXIT_SUCCESS : EXIT_FAILURE;
   }
   return status;
}  // end HyPerCol::checkMarginWidths()

int HyPerCol::zCheckMarginWidth(HyPerConn * conn, const char * dim, int padding, int patchSize, int scalePre, int scalePost, int prevStatus) {
   int status;
   int scaleDiff = scalePre - scalePost;
   // if post has higher neuronal density than pre, scaleDiff < 0.
   int needed = scaleDiff > 0 ? ( patchSize/( (int) powf(2,scaleDiff) )/2 ) :
                                ( (patchSize/2) * ( (int) powf(2,-scaleDiff) ) );
   if( padding < needed ) {
      if( prevStatus == EXIT_SUCCESS ) {
         fprintf(stderr, "Margin width error.\n");
      }
      fprintf(stderr, "Connection \"%s\", dimension %s:\n", conn->getName(), dim);
      fprintf(stderr, "    Margin width %d, patch size %d, presynaptic scale %d, postsynaptic scale %d\n",
              padding, patchSize, scalePre, scalePost);
      fprintf(stderr, "    Needed margin width=%d\n", needed);
      status = EXIT_FAILURE;
      if( numberOfColumns() > 1 || padding > 0 ) {
         fprintf(stderr, "Exiting.\n");
         exit(EXIT_FAILURE);
      }
      else {
         fprintf(stderr, "Continuing, but there may be undesirable edge effects.\n");
      }
   }
   else status = EXIT_SUCCESS;
   return status;
}

} // PV namespace
<|endoftext|>


<commit_before>#include "editorView.h"

#include <QtCore/QTimeLine>

using namespace qReal;

int const zoomAnimationInterval = 20;
int const zoomAnimationTimes = 4;

EditorView::EditorView(QWidget *parent)
	: QGraphicsView(parent)
	, mMouseOldPosition()
	, mWheelPressed(false)
	, mZoom(0)
	, mTouchManager(this)
{
	setRenderHint(QPainter::Antialiasing, true);

	mScene = new EditorViewScene(this);
	connect(mScene, SIGNAL(zoomIn()), this, SLOT(zoomIn()));
	connect(mScene, SIGNAL(zoomOut()), this, SLOT(zoomOut()));

	setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
	setResizeAnchor(QGraphicsView::AnchorUnderMouse);

	mMVIface = new EditorViewMViface(this, mScene);
	setScene(mScene);

	setAcceptDrops(true);
	setDragMode(RubberBandDrag);
	setEnabled(false);

	setMouseTracking(true);

	setAlignment(Qt::AlignCenter);

	connect(&mTouchManager, SIGNAL(gestureStarted()), mScene, SLOT(deleteGesture()));
}

EditorView::~EditorView()
{
	delete mMVIface;
	delete mScene;
}

EditorViewMViface *EditorView::mvIface() const
{
	return mMVIface;
}

EditorViewScene *EditorView::editorViewScene() const
{
	return mScene;
}

void EditorView::toggleAntialiasing(bool checked)
{
	setRenderHint(QPainter::Antialiasing, checked);
	setRenderHint(QPainter::SmoothPixmapTransform, checked);
}

void EditorView::zoomIn()
{
	if (mWheelPressed || mZoom >= SettingsManager::value("maxZoom").toInt()) {
		return;
	}

	startAnimation(SLOT(zoomInTime()));
	++mZoom;
}

void EditorView::zoomOut()
{
	if (mWheelPressed || mZoom <= SettingsManager::value("minZoom").toInt()) {
		return;
	}

	startAnimation(SLOT(zoomOutTime()));
	--mZoom;
}

void EditorView::checkGrid()
{
	if (SettingsManager::value("ShowGrid").toBool()) {
		mScene->setNeedDrawGrid(mScene->realIndexGrid() >= 2 && mScene->realIndexGrid() <= 380);
	}
}

void EditorView::startAnimation(char const *slot)
{
	QTimeLine *anim = new QTimeLine(zoomAnimationTimes * zoomAnimationInterval, this);
	anim->setUpdateInterval(zoomAnimationInterval);

	connect(anim, SIGNAL(valueChanged(qreal)), this, slot);
	connect(anim, SIGNAL(finished()), this, SLOT(animFinished()));
	anim->start();
}

void EditorView::setMainWindow(qReal::MainWindow *mainWindow)
{
	mMVIface->scene()->setMainWindow(mainWindow);
}

void EditorView::setDrawSceneGrid(bool show)
{
	mScene->setNeedDrawGrid(show);
	mScene->invalidate();
}

void EditorView::mouseMoveEvent(QMouseEvent *event)
{
	if (mWheelPressed) {
		if (mMouseOldPosition != QPointF()) {
			QRectF rect = sceneRect();
			qreal dx = (event->localPos().x() - mMouseOldPosition.x());
			qreal dy = (event->localPos().y() - mMouseOldPosition.y());
			rect.moveLeft(rect.left() - dx);
			rect.moveTop(rect.top() - dy);
			setSceneRect(rect);
			translate(dx, dy);
		}
		mMouseOldPosition = event->localPos();
	}
	QGraphicsView::mouseMoveEvent(event);
	if (event->buttons() & Qt::RightButton) {
		setDragMode(NoDrag);
	} else {
		if ((event->buttons() & Qt::LeftButton) && (event->modifiers() & Qt::ControlModifier)) {
			setDragMode(RubberBandDrag);
			mScene->itemSelectUpdate();
		/*} else 	if ((event->buttons() & Qt::LeftButton) && (event->modifiers() & Qt::ShiftModifier)) {
			setDragMode(ScrollHandDrag); //  (see #615)
			mScene->itemSelectUpdate();*/
		} else if (event->buttons() & Qt::LeftButton ) {
			EdgeElement *newEdgeEl = dynamic_cast<EdgeElement *>(itemAt(event->pos()));
			if (newEdgeEl && newEdgeEl->isBreakPointPressed()) {
				newEdgeEl->breakPointUnpressed();
				setDragMode(NoDrag);
			}
		}
	}
	if (mScene->getNeedDrawGrid())
		mScene->invalidate();
}

void EditorView::mouseReleaseEvent(QMouseEvent *event)
{
	if (!(event->buttons() & Qt::MidButton)) {
		mWheelPressed = false;
		mMouseOldPosition = QPointF();
	}
	QGraphicsView::mouseReleaseEvent(event);
	if (mScene->getNeedDrawGrid()) {
		mScene->invalidate();
	}
}

void EditorView::mousePressEvent(QMouseEvent *event)
{
	mWheelPressed = (event->buttons() & Qt::MidButton);
	mMouseOldPosition = QPointF();
	if (!mWheelPressed) {
		QGraphicsView::mousePressEvent(event);
	}
	if (event->buttons() & Qt::LeftButton) {
		if (!(event->buttons() & Qt::RightButton) && !mTouchManager.isGestureRunning()
				&& !itemAt(event->pos())) {
			setDragMode(RubberBandDrag);
		}
		if (event->modifiers() & Qt::ControlModifier) {
			mScene->itemSelectUpdate();
		}
	}
}

void EditorView::scrollContentsBy(int dx, int dy)
{
	QGraphicsView::scrollContentsBy(dx, dy);
	if (mScene->getNeedDrawGrid()) {
		mScene->invalidate();
	}
}

void EditorView::keyPressEvent(QKeyEvent *event)
{
	QGraphicsView::keyPressEvent(event);
	if (event->key() == Qt::Key_Space) {
		setDragMode(QGraphicsView::ScrollHandDrag);
		event->accept();
	}
}

void EditorView::keyReleaseEvent(QKeyEvent *event)
{
	if (event->key() == Qt::Key_Space) {
		setDragMode(QGraphicsView::RubberBandDrag);
	} else {
		QGraphicsView::keyPressEvent(event);
	}
}

bool EditorView::viewportEvent(QEvent *event)
{
	switch (event->type()) {
	case QEvent::TouchBegin:
	case QEvent::TouchUpdate:
	case QEvent::TouchEnd:
		// For some reason touch viewport events can`t be processed in manual event
		// filters, so catching them here
		return mTouchManager.processTouchEvent(static_cast<QTouchEvent *>(event));
	default:
		break;
	}
	return QGraphicsView::viewportEvent(event);
}

void EditorView::invalidateScene()
{
	scene()->invalidate();
}

void EditorView::ensureElementVisible(Element const * const element)
{
	if (element) {
		qreal const widgetWidth = size().width();
		qreal const widgetHeight = size().height();
		qreal const elementWidth = element->boundingRect().width();
		qreal const elementHeight = element->boundingRect().height();
		ensureVisible(element, (widgetWidth - elementWidth) / 2, (widgetHeight - elementHeight) / 2);
	}
}

void EditorView::ensureElementVisible(Element const * const element
		, int xMargin, int yMargin)
{
	if (element) {
		ensureVisible(element, xMargin, yMargin);
	}
}

void EditorView::setTitlesVisible(bool visible)
{
	mScene->setTitlesVisible(visible);
}

void EditorView::zoomInTime()
{
	qreal const zoomFactor = static_cast<qreal>(SettingsManager::value("zoomFactor").toInt()) / (10 + 15) + 1;
	zoom(zoomFactor);
}

void EditorView::zoomOutTime()
{
	qreal const zoomFactor = 1 / (static_cast<qreal>(SettingsManager::value("zoomFactor").toInt()) / (10 + 15) + 1);
	zoom(zoomFactor);
}

void EditorView::animFinished()
{
	delete sender();
}

void EditorView::zoom(qreal const zoomFactor)
{
	setSceneRect(mScene->sceneRect());
	scale(zoomFactor, zoomFactor);

	if (SettingsManager::value("ShowGrid").toBool()) {
		mScene->setRealIndexGrid(mScene->realIndexGrid() * zoomFactor);
	}
	checkGrid();
}
<commit_msg>Fixed #1146<commit_after>#include "editorView.h"

#include <QtCore/QTimeLine>

using namespace qReal;

int const zoomAnimationInterval = 20;
int const zoomAnimationTimes = 4;

EditorView::EditorView(QWidget *parent)
	: QGraphicsView(parent)
	, mMouseOldPosition()
	, mWheelPressed(false)
	, mZoom(0)
	, mTouchManager(this)
{
	setRenderHint(QPainter::Antialiasing, true);

	mScene = new EditorViewScene(this);
	connect(mScene, SIGNAL(zoomIn()), this, SLOT(zoomIn()));
	connect(mScene, SIGNAL(zoomOut()), this, SLOT(zoomOut()));

	setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
	setResizeAnchor(QGraphicsView::AnchorUnderMouse);

	mMVIface = new EditorViewMViface(this, mScene);
	setScene(mScene);

	setAcceptDrops(true);
	setDragMode(RubberBandDrag);
	setEnabled(false);

	setMouseTracking(true);

	setAlignment(Qt::AlignCenter);

	connect(&mTouchManager, SIGNAL(gestureStarted()), mScene, SLOT(deleteGesture()));
}

EditorView::~EditorView()
{
	delete mMVIface;
	delete mScene;
}

EditorViewMViface *EditorView::mvIface() const
{
	return mMVIface;
}

EditorViewScene *EditorView::editorViewScene() const
{
	return mScene;
}

void EditorView::toggleAntialiasing(bool checked)
{
	setRenderHint(QPainter::Antialiasing, checked);
	setRenderHint(QPainter::SmoothPixmapTransform, checked);
}

void EditorView::zoomIn()
{
	if (mWheelPressed || mZoom >= SettingsManager::value("maxZoom").toInt()) {
		return;
	}

	startAnimation(SLOT(zoomInTime()));
	++mZoom;
}

void EditorView::zoomOut()
{
	if (mWheelPressed || mZoom <= SettingsManager::value("minZoom").toInt()) {
		return;
	}

	startAnimation(SLOT(zoomOutTime()));
	--mZoom;
}

void EditorView::checkGrid()
{
	if (SettingsManager::value("ShowGrid").toBool()) {
		mScene->setNeedDrawGrid(mScene->realIndexGrid() >= 2 && mScene->realIndexGrid() <= 380);
	}
}

void EditorView::startAnimation(char const *slot)
{
	QTimeLine *anim = new QTimeLine(zoomAnimationTimes * zoomAnimationInterval, this);
	anim->setUpdateInterval(zoomAnimationInterval);

	connect(anim, SIGNAL(valueChanged(qreal)), this, slot);
	connect(anim, SIGNAL(finished()), this, SLOT(animFinished()));
	anim->start();
}

void EditorView::setMainWindow(qReal::MainWindow *mainWindow)
{
	mMVIface->scene()->setMainWindow(mainWindow);
}

void EditorView::setDrawSceneGrid(bool show)
{
	mScene->setNeedDrawGrid(show);
	mScene->invalidate();
}

void EditorView::mouseMoveEvent(QMouseEvent *event)
{
	if (mWheelPressed) {
		if (mMouseOldPosition != QPointF()) {
			QRectF rect = sceneRect();
			qreal dx = (event->localPos().x() - mMouseOldPosition.x());
			qreal dy = (event->localPos().y() - mMouseOldPosition.y());
			rect.moveLeft(rect.left() - dx);
			rect.moveTop(rect.top() - dy);
			setSceneRect(rect);
			translate(dx, dy);
		}
		mMouseOldPosition = event->localPos();
	}
	QGraphicsView::mouseMoveEvent(event);
	if (event->buttons() & Qt::RightButton) {
		setDragMode(NoDrag);
	} else {
		if ((event->buttons() & Qt::LeftButton) && (event->modifiers() & Qt::ControlModifier)) {
			setDragMode(RubberBandDrag);
			mScene->itemSelectUpdate();
		/*} else 	if ((event->buttons() & Qt::LeftButton) && (event->modifiers() & Qt::ShiftModifier)) {
			setDragMode(ScrollHandDrag); //  (see #615)
			mScene->itemSelectUpdate();*/
		} else if (event->buttons() & Qt::LeftButton ) {
			EdgeElement *newEdgeEl = dynamic_cast<EdgeElement *>(itemAt(event->pos()));
			if (newEdgeEl && newEdgeEl->isBreakPointPressed()) {
				newEdgeEl->breakPointUnpressed();
				setDragMode(NoDrag);
			}
		}
	}
	if (mScene->getNeedDrawGrid())
		mScene->invalidate();
}

void EditorView::mouseReleaseEvent(QMouseEvent *event)
{
	if (!(event->buttons() & Qt::MidButton)) {
		mWheelPressed = false;
		mMouseOldPosition = QPointF();
	}
	QGraphicsView::mouseReleaseEvent(event);
	if (mScene->getNeedDrawGrid()) {
		mScene->invalidate();
	}
}

void EditorView::mousePressEvent(QMouseEvent *event)
{
	mWheelPressed = (event->buttons() & Qt::MidButton);
	mMouseOldPosition = QPointF();
	if (!mWheelPressed) {
		QGraphicsView::mousePressEvent(event);
	}

	if (event->buttons() == Qt::RightButton) {
		setDragMode(NoDrag);
	}

	if (event->buttons() & Qt::LeftButton) {
		if (!(event->buttons() & Qt::RightButton) && !mTouchManager.isGestureRunning()
				&& !itemAt(event->pos())) {
			setDragMode(RubberBandDrag);
		}
		if (event->modifiers() & Qt::ControlModifier) {
			mScene->itemSelectUpdate();
		}
	}
}

void EditorView::scrollContentsBy(int dx, int dy)
{
	QGraphicsView::scrollContentsBy(dx, dy);
	if (mScene->getNeedDrawGrid()) {
		mScene->invalidate();
	}
}

void EditorView::keyPressEvent(QKeyEvent *event)
{
	QGraphicsView::keyPressEvent(event);
	if (event->key() == Qt::Key_Space) {
		setDragMode(QGraphicsView::ScrollHandDrag);
		event->accept();
	}
}

void EditorView::keyReleaseEvent(QKeyEvent *event)
{
	if (event->key() == Qt::Key_Space) {
		setDragMode(QGraphicsView::RubberBandDrag);
	} else {
		QGraphicsView::keyPressEvent(event);
	}
}

bool EditorView::viewportEvent(QEvent *event)
{
	switch (event->type()) {
	case QEvent::TouchBegin:
	case QEvent::TouchUpdate:
	case QEvent::TouchEnd:
		// For some reason touch viewport events can`t be processed in manual event
		// filters, so catching them here
		return mTouchManager.processTouchEvent(static_cast<QTouchEvent *>(event));
	default:
		break;
	}
	return QGraphicsView::viewportEvent(event);
}

void EditorView::invalidateScene()
{
	scene()->invalidate();
}

void EditorView::ensureElementVisible(Element const * const element)
{
	if (element) {
		qreal const widgetWidth = size().width();
		qreal const widgetHeight = size().height();
		qreal const elementWidth = element->boundingRect().width();
		qreal const elementHeight = element->boundingRect().height();
		ensureVisible(element, (widgetWidth - elementWidth) / 2, (widgetHeight - elementHeight) / 2);
	}
}

void EditorView::ensureElementVisible(Element const * const element
		, int xMargin, int yMargin)
{
	if (element) {
		ensureVisible(element, xMargin, yMargin);
	}
}

void EditorView::setTitlesVisible(bool visible)
{
	mScene->setTitlesVisible(visible);
}

void EditorView::zoomInTime()
{
	qreal const zoomFactor = static_cast<qreal>(SettingsManager::value("zoomFactor").toInt()) / (10 + 15) + 1;
	zoom(zoomFactor);
}

void EditorView::zoomOutTime()
{
	qreal const zoomFactor = 1 / (static_cast<qreal>(SettingsManager::value("zoomFactor").toInt()) / (10 + 15) + 1);
	zoom(zoomFactor);
}

void EditorView::animFinished()
{
	delete sender();
}

void EditorView::zoom(qreal const zoomFactor)
{
	setSceneRect(mScene->sceneRect());
	scale(zoomFactor, zoomFactor);

	if (SettingsManager::value("ShowGrid").toBool()) {
		mScene->setRealIndexGrid(mScene->realIndexGrid() * zoomFactor);
	}
	checkGrid();
}
<|endoftext|>


<commit_before>#include <catch.hpp>
#include <nanikanizer/nanikanizer.hpp>

TEST_CASE("padding")
{
	nnk::variable<double> x =
	{
		1.0, 2.0, 3.0,
		4.0, 5.0, 6.0,
	};

	auto y = nnk::padding_2d(x.expr(), 2, 3, 1, 2, 1);

	nnk::evaluator<double> ev(y);

	auto result = ev.forward();

	REQUIRE(result.size() == 30);
	CHECK(result[0] == Approx(0.0));
	CHECK(result[1] == Approx(0.0));
	CHECK(result[2] == Approx(0.0));
	CHECK(result[3] == Approx(0.0));
	CHECK(result[4] == Approx(0.0));
	CHECK(result[5] == Approx(0.0));
	CHECK(result[6] == Approx(0.0));
	CHECK(result[7] == Approx(0.0));
	CHECK(result[8] == Approx(0.0));
	CHECK(result[9] == Approx(0.0));
	CHECK(result[10] == Approx(0.0));
	CHECK(result[11] == Approx(1.0));
	CHECK(result[12] == Approx(2.0));
	CHECK(result[13] == Approx(3.0));
	CHECK(result[14] == Approx(0.0));
	CHECK(result[15] == Approx(0.0));
	CHECK(result[16] == Approx(4.0));
	CHECK(result[17] == Approx(5.0));
	CHECK(result[18] == Approx(6.0));
	CHECK(result[19] == Approx(0.0));
	CHECK(result[20] == Approx(0.0));
	CHECK(result[21] == Approx(0.0));
	CHECK(result[22] == Approx(0.0));
	CHECK(result[23] == Approx(0.0));
	CHECK(result[24] == Approx(0.0));
	CHECK(result[25] == Approx(0.0));
	CHECK(result[26] == Approx(0.0));
	CHECK(result[27] == Approx(0.0));
	CHECK(result[28] == Approx(0.0));
	CHECK(result[29] == Approx(0.0));

	ev.backward(
	{
		1.0, 1.0, 1.0, 1.0, 1.0,
		1.0, 1.0, 1.0, 1.0, 1.0,
		1.0, 1.0, 1.0, 1.0, 1.0,
		1.0, 1.0, 1.0, 1.0, 1.0,
		1.0, 1.0, 1.0, 1.0, 1.0,
	});

	REQUIRE(x.grad().size() == 6);
	CHECK(x.grad()[0] == Approx(1.0));
	CHECK(x.grad()[1] == Approx(1.0));
	CHECK(x.grad()[2] == Approx(1.0));
	CHECK(x.grad()[3] == Approx(1.0));
	CHECK(x.grad()[4] == Approx(1.0));
	CHECK(x.grad()[5] == Approx(1.0));
}
<commit_msg>Fix padding_test<commit_after>#include <catch.hpp>
#include <nanikanizer/nanikanizer.hpp>

TEST_CASE("padding")
{
	nnk::variable<double> x =
	{
		1.0, 2.0, 3.0,
		4.0, 5.0, 6.0,
	};

	auto y = nnk::padding_2d(x.expr(), 2, 3, 1, 2, 1);

	nnk::evaluator<double> ev(y);

	auto result = ev.forward();

	REQUIRE(result.size() == 30);
	CHECK(result[0] == Approx(0.0));
	CHECK(result[1] == Approx(0.0));
	CHECK(result[2] == Approx(0.0));
	CHECK(result[3] == Approx(0.0));
	CHECK(result[4] == Approx(0.0));
	CHECK(result[5] == Approx(0.0));
	CHECK(result[6] == Approx(0.0));
	CHECK(result[7] == Approx(0.0));
	CHECK(result[8] == Approx(0.0));
	CHECK(result[9] == Approx(0.0));
	CHECK(result[10] == Approx(0.0));
	CHECK(result[11] == Approx(1.0));
	CHECK(result[12] == Approx(2.0));
	CHECK(result[13] == Approx(3.0));
	CHECK(result[14] == Approx(0.0));
	CHECK(result[15] == Approx(0.0));
	CHECK(result[16] == Approx(4.0));
	CHECK(result[17] == Approx(5.0));
	CHECK(result[18] == Approx(6.0));
	CHECK(result[19] == Approx(0.0));
	CHECK(result[20] == Approx(0.0));
	CHECK(result[21] == Approx(0.0));
	CHECK(result[22] == Approx(0.0));
	CHECK(result[23] == Approx(0.0));
	CHECK(result[24] == Approx(0.0));
	CHECK(result[25] == Approx(0.0));
	CHECK(result[26] == Approx(0.0));
	CHECK(result[27] == Approx(0.0));
	CHECK(result[28] == Approx(0.0));
	CHECK(result[29] == Approx(0.0));

	ev.backward(
	{
		1.0, 1.0, 1.0, 1.0, 1.0,
		1.0, 1.0, 1.0, 1.0, 1.0,
		1.0, 1.0, 1.0, 1.0, 1.0,
		1.0, 1.0, 1.0, 1.0, 1.0,
		1.0, 1.0, 1.0, 1.0, 1.0,
		1.0, 1.0, 1.0, 1.0, 1.0,
	});

	REQUIRE(x.grad().size() == 6);
	CHECK(x.grad()[0] == Approx(1.0));
	CHECK(x.grad()[1] == Approx(1.0));
	CHECK(x.grad()[2] == Approx(1.0));
	CHECK(x.grad()[3] == Approx(1.0));
	CHECK(x.grad()[4] == Approx(1.0));
	CHECK(x.grad()[5] == Approx(1.0));
}
<|endoftext|>


<commit_before>#include "../../source/thewizardplusplus/wizard_parser/parser/ast_node.hpp"
#include "../../source/thewizardplusplus/wizard_parser/lexer/token.hpp"
#include "../../source/thewizardplusplus/wizard_parser/parser/rule_parser.hpp"
#include "../../source/thewizardplusplus/wizard_parser/parser/parse.hpp"
#include "../../source/thewizardplusplus/wizard_parser/transformers/transformers.hpp"
#include "../vendor/catch/catch.hpp"
#include "../vendor/fakeit/fakeit.hpp"

TEST_CASE("parser::parse() function", "[parser]") {
	using namespace thewizardplusplus::wizard_parser;

	SECTION("without handlers") {
		const auto sequence_type = (+parser::ast_node_type::sequence)._to_string();
		const auto nothing_type = (+parser::ast_node_type::nothing)._to_string();
		auto tokens = lexer::token_group{
			{"identifier", "one", 1},
			{"comma", ",", 4},
			{"identifier", "two", 5},
			{"comma", ",", 8},
			{"identifier", "three", 9},
			{"semicolon", ";", 14}
		};
		auto mock_parser = fakeit::Mock<parser::rule_parser>{};
		fakeit::When(Method(mock_parser, parse))
			.Return(parser::parsing_result{
				parser::ast_node{sequence_type, {}, {
					parser::ast_node{"identifier", "one", {}, 1},
					parser::ast_node{sequence_type, {}, {
						parser::ast_node{sequence_type, {}, {
							parser::ast_node{nothing_type, {}, {}, 4},
							parser::ast_node{"identifier", "two", {}, 5},
						}},
						parser::ast_node{sequence_type, {}, {
							parser::ast_node{sequence_type, {}, {
								parser::ast_node{nothing_type, {}, {}, 8},
								parser::ast_node{"identifier", "three", {}, 9},
							}},
							parser::ast_node{sequence_type, {}, {}, 14}
						}}
					}}
				}},
				lexer::token_span{tokens}.subspan(5)
			});
		fakeit::Fake(Dtor(mock_parser));

		const auto mock_parser_pointer =
			parser::rule_parser::pointer{&mock_parser.get()};
		const auto [ast, rest_tokens] = parser::parse(
			mock_parser_pointer,
			tokens,
			{}
		);
		REQUIRE(ast.has_value());
		CHECK(*ast == parser::ast_node{sequence_type, {}, {
			parser::ast_node{"identifier", "one", {}, 1},
			parser::ast_node{sequence_type, {}, {
				parser::ast_node{sequence_type, {}, {
					parser::ast_node{nothing_type, {}, {}, 4},
					parser::ast_node{"identifier", "two", {}, 5},
				}},
				parser::ast_node{sequence_type, {}, {
					parser::ast_node{sequence_type, {}, {
						parser::ast_node{nothing_type, {}, {}, 8},
						parser::ast_node{"identifier", "three", {}, 9},
					}},
					parser::ast_node{sequence_type, {}, {}, 14}
				}}
			}}
		}});
		CHECK(rest_tokens == lexer::token_span{tokens}.subspan(5));

		fakeit::Verify(Method(mock_parser, parse)).Once();
	}

	SECTION("with any handlers") {
		const auto sequence_type = (+parser::ast_node_type::sequence)._to_string();
		const auto nothing_type = (+parser::ast_node_type::nothing)._to_string();
		auto tokens = lexer::token_group{
			{"identifier", "one", 1},
			{"comma", ",", 4},
			{"identifier", "two", 5},
			{"comma", ",", 8},
			{"identifier", "three", 9},
			{"semicolon", ";", 14}
		};
		auto mock_parser = fakeit::Mock<parser::rule_parser>{};
		fakeit::When(Method(mock_parser, parse))
			.Return(parser::parsing_result{
				parser::ast_node{sequence_type, {}, {
					parser::ast_node{"identifier", "one", {}, 1},
					parser::ast_node{sequence_type, {}, {
						parser::ast_node{sequence_type, {}, {
							parser::ast_node{nothing_type, {}, {}, 4},
							parser::ast_node{"identifier", "two", {}, 5},
						}},
						parser::ast_node{sequence_type, {}, {
							parser::ast_node{sequence_type, {}, {
								parser::ast_node{nothing_type, {}, {}, 8},
								parser::ast_node{"identifier", "three", {}, 9},
							}},
							parser::ast_node{sequence_type, {}, {}, 14}
						}}
					}}
				}},
				lexer::token_span{tokens}.subspan(5)
			});
		fakeit::Fake(Dtor(mock_parser));

		const auto mock_parser_pointer =
			parser::rule_parser::pointer{&mock_parser.get()};
		const auto [ast, rest_tokens] = parser::parse(
			mock_parser_pointer,
			tokens,
			{transformers::join_sequences}
		);
		REQUIRE(ast.has_value());
		CHECK(*ast == parser::ast_node{sequence_type, {}, {
			parser::ast_node{"identifier", "one", {}, 1},
			parser::ast_node{nothing_type, {}, {}, 4},
			parser::ast_node{"identifier", "two", {}, 5},
			parser::ast_node{nothing_type, {}, {}, 8},
			parser::ast_node{"identifier", "three", {}, 9}
		}});
		CHECK(rest_tokens == lexer::token_span{tokens}.subspan(5));

		fakeit::Verify(Method(mock_parser, parse)).Once();
	}

	SECTION("with default handlers") {
		const auto sequence_type = (+parser::ast_node_type::sequence)._to_string();
		const auto nothing_type = (+parser::ast_node_type::nothing)._to_string();
		auto tokens = lexer::token_group{
			{"identifier", "one", 1},
			{"comma", ",", 4},
			{"identifier", "two", 5},
			{"comma", ",", 8},
			{"identifier", "three", 9},
			{"semicolon", ";", 14}
		};
		auto mock_parser = fakeit::Mock<parser::rule_parser>{};
		fakeit::When(Method(mock_parser, parse))
			.Return(parser::parsing_result{
				parser::ast_node{sequence_type, {}, {
					parser::ast_node{"identifier", "one", {}, 1},
					parser::ast_node{sequence_type, {}, {
						parser::ast_node{sequence_type, {}, {
							parser::ast_node{nothing_type, {}, {}, 4},
							parser::ast_node{"identifier", "two", {}, 5},
						}},
						parser::ast_node{sequence_type, {}, {
							parser::ast_node{sequence_type, {}, {
								parser::ast_node{nothing_type, {}, {}, 8},
								parser::ast_node{"identifier", "three", {}, 9},
							}},
							parser::ast_node{sequence_type, {}, {}, 14}
						}}
					}}
				}},
				lexer::token_span{tokens}.subspan(5)
			});
		fakeit::Fake(Dtor(mock_parser));

		const auto mock_parser_pointer =
			parser::rule_parser::pointer{&mock_parser.get()};
		const auto [ast, rest_tokens] = parser::parse(mock_parser_pointer, tokens);
		REQUIRE(ast.has_value());
		CHECK(*ast == parser::ast_node{sequence_type, {}, {
			parser::ast_node{"identifier", "one", {}, 1},
			parser::ast_node{"identifier", "two", {}, 5},
			parser::ast_node{"identifier", "three", {}, 9}
		}});
		CHECK(rest_tokens == lexer::token_span{tokens}.subspan(5));

		fakeit::Verify(Method(mock_parser, parse)).Once();
	}
}
<commit_msg>Complete checking of methods calls in the tests of the `parser::parse()` function<commit_after>#include "../../source/thewizardplusplus/wizard_parser/parser/ast_node.hpp"
#include "../../source/thewizardplusplus/wizard_parser/lexer/token.hpp"
#include "../../source/thewizardplusplus/wizard_parser/parser/rule_parser.hpp"
#include "../../source/thewizardplusplus/wizard_parser/parser/parse.hpp"
#include "../../source/thewizardplusplus/wizard_parser/transformers/transformers.hpp"
#include "../vendor/catch/catch.hpp"
#include "../vendor/fakeit/fakeit.hpp"

TEST_CASE("parser::parse() function", "[parser]") {
	using namespace thewizardplusplus::wizard_parser;

	SECTION("without handlers") {
		const auto sequence_type = (+parser::ast_node_type::sequence)._to_string();
		const auto nothing_type = (+parser::ast_node_type::nothing)._to_string();
		auto tokens = lexer::token_group{
			{"identifier", "one", 1},
			{"comma", ",", 4},
			{"identifier", "two", 5},
			{"comma", ",", 8},
			{"identifier", "three", 9},
			{"semicolon", ";", 14}
		};
		auto mock_parser = fakeit::Mock<parser::rule_parser>{};
		fakeit::When(Method(mock_parser, parse))
			.Return(parser::parsing_result{
				parser::ast_node{sequence_type, {}, {
					parser::ast_node{"identifier", "one", {}, 1},
					parser::ast_node{sequence_type, {}, {
						parser::ast_node{sequence_type, {}, {
							parser::ast_node{nothing_type, {}, {}, 4},
							parser::ast_node{"identifier", "two", {}, 5},
						}},
						parser::ast_node{sequence_type, {}, {
							parser::ast_node{sequence_type, {}, {
								parser::ast_node{nothing_type, {}, {}, 8},
								parser::ast_node{"identifier", "three", {}, 9},
							}},
							parser::ast_node{sequence_type, {}, {}, 14}
						}}
					}}
				}},
				lexer::token_span{tokens}.subspan(5)
			});
		fakeit::Fake(Dtor(mock_parser));

		const auto mock_parser_pointer =
			parser::rule_parser::pointer{&mock_parser.get()};
		const auto [ast, rest_tokens] = parser::parse(
			mock_parser_pointer,
			tokens,
			{}
		);
		REQUIRE(ast.has_value());
		CHECK(*ast == parser::ast_node{sequence_type, {}, {
			parser::ast_node{"identifier", "one", {}, 1},
			parser::ast_node{sequence_type, {}, {
				parser::ast_node{sequence_type, {}, {
					parser::ast_node{nothing_type, {}, {}, 4},
					parser::ast_node{"identifier", "two", {}, 5},
				}},
				parser::ast_node{sequence_type, {}, {
					parser::ast_node{sequence_type, {}, {
						parser::ast_node{nothing_type, {}, {}, 8},
						parser::ast_node{"identifier", "three", {}, 9},
					}},
					parser::ast_node{sequence_type, {}, {}, 14}
				}}
			}}
		}});
		CHECK(rest_tokens == lexer::token_span{tokens}.subspan(5));

		fakeit::Verify(Method(mock_parser, parse)).Once();
		fakeit::VerifyNoOtherInvocations(mock_parser);
	}

	SECTION("with any handlers") {
		const auto sequence_type = (+parser::ast_node_type::sequence)._to_string();
		const auto nothing_type = (+parser::ast_node_type::nothing)._to_string();
		auto tokens = lexer::token_group{
			{"identifier", "one", 1},
			{"comma", ",", 4},
			{"identifier", "two", 5},
			{"comma", ",", 8},
			{"identifier", "three", 9},
			{"semicolon", ";", 14}
		};
		auto mock_parser = fakeit::Mock<parser::rule_parser>{};
		fakeit::When(Method(mock_parser, parse))
			.Return(parser::parsing_result{
				parser::ast_node{sequence_type, {}, {
					parser::ast_node{"identifier", "one", {}, 1},
					parser::ast_node{sequence_type, {}, {
						parser::ast_node{sequence_type, {}, {
							parser::ast_node{nothing_type, {}, {}, 4},
							parser::ast_node{"identifier", "two", {}, 5},
						}},
						parser::ast_node{sequence_type, {}, {
							parser::ast_node{sequence_type, {}, {
								parser::ast_node{nothing_type, {}, {}, 8},
								parser::ast_node{"identifier", "three", {}, 9},
							}},
							parser::ast_node{sequence_type, {}, {}, 14}
						}}
					}}
				}},
				lexer::token_span{tokens}.subspan(5)
			});
		fakeit::Fake(Dtor(mock_parser));

		const auto mock_parser_pointer =
			parser::rule_parser::pointer{&mock_parser.get()};
		const auto [ast, rest_tokens] = parser::parse(
			mock_parser_pointer,
			tokens,
			{transformers::join_sequences}
		);
		REQUIRE(ast.has_value());
		CHECK(*ast == parser::ast_node{sequence_type, {}, {
			parser::ast_node{"identifier", "one", {}, 1},
			parser::ast_node{nothing_type, {}, {}, 4},
			parser::ast_node{"identifier", "two", {}, 5},
			parser::ast_node{nothing_type, {}, {}, 8},
			parser::ast_node{"identifier", "three", {}, 9}
		}});
		CHECK(rest_tokens == lexer::token_span{tokens}.subspan(5));

		fakeit::Verify(Method(mock_parser, parse)).Once();
		fakeit::VerifyNoOtherInvocations(mock_parser);
	}

	SECTION("with default handlers") {
		const auto sequence_type = (+parser::ast_node_type::sequence)._to_string();
		const auto nothing_type = (+parser::ast_node_type::nothing)._to_string();
		auto tokens = lexer::token_group{
			{"identifier", "one", 1},
			{"comma", ",", 4},
			{"identifier", "two", 5},
			{"comma", ",", 8},
			{"identifier", "three", 9},
			{"semicolon", ";", 14}
		};
		auto mock_parser = fakeit::Mock<parser::rule_parser>{};
		fakeit::When(Method(mock_parser, parse))
			.Return(parser::parsing_result{
				parser::ast_node{sequence_type, {}, {
					parser::ast_node{"identifier", "one", {}, 1},
					parser::ast_node{sequence_type, {}, {
						parser::ast_node{sequence_type, {}, {
							parser::ast_node{nothing_type, {}, {}, 4},
							parser::ast_node{"identifier", "two", {}, 5},
						}},
						parser::ast_node{sequence_type, {}, {
							parser::ast_node{sequence_type, {}, {
								parser::ast_node{nothing_type, {}, {}, 8},
								parser::ast_node{"identifier", "three", {}, 9},
							}},
							parser::ast_node{sequence_type, {}, {}, 14}
						}}
					}}
				}},
				lexer::token_span{tokens}.subspan(5)
			});
		fakeit::Fake(Dtor(mock_parser));

		const auto mock_parser_pointer =
			parser::rule_parser::pointer{&mock_parser.get()};
		const auto [ast, rest_tokens] = parser::parse(mock_parser_pointer, tokens);
		REQUIRE(ast.has_value());
		CHECK(*ast == parser::ast_node{sequence_type, {}, {
			parser::ast_node{"identifier", "one", {}, 1},
			parser::ast_node{"identifier", "two", {}, 5},
			parser::ast_node{"identifier", "three", {}, 9}
		}});
		CHECK(rest_tokens == lexer::token_span{tokens}.subspan(5));

		fakeit::Verify(Method(mock_parser, parse)).Once();
		fakeit::VerifyNoOtherInvocations(mock_parser);
	}
}
<|endoftext|>