<commit_before>#include "eventbackend.h"

#define MAX_EVENTS 1024

// PDTK
#include <cxxutils/vterm.h>

lockable<std::unordered_multimap<posix::fd_t, EventBackend::callback_info_t>> EventBackend::queue;
std::list<std::pair<posix::fd_t, native_flags_t>> EventBackend::results;

#if defined(__linux__)

// epoll needs kernel 2.5.44

// Linux
#include <sys/epoll.h>

struct EventBackend::platform_dependant // poll notification (epoll)
{
  posix::fd_t fd;
  struct epoll_event output[MAX_EVENTS];

  platform_dependant(void) noexcept
    : fd(posix::invalid_descriptor)
  {
    fd = ::epoll_create(MAX_EVENTS);
    flaw(fd == posix::invalid_descriptor, terminal::critical, std::exit(errno),,
         "Unable to create an instance of epoll! %s", std::strerror(errno))
  }

  ~platform_dependant(void) noexcept
  {
    posix::close(fd);
    fd = posix::invalid_descriptor;
  }

  bool add(posix::fd_t wd, native_flags_t flags) noexcept
  {
    struct epoll_event native_event;
    native_event.data.fd = wd;
    native_event.events = flags; // be sure to convert to native events
    return ::epoll_ctl(fd, EPOLL_CTL_ADD, wd, &native_event) == posix::success_response || // add new event OR
        (errno == EEXIST && ::epoll_ctl(fd, EPOLL_CTL_MOD, wd, &native_event) == posix::success_response); // modify existing event
  }

  bool remove(posix::fd_t wd) noexcept
  {
    struct epoll_event event;
    return ::epoll_ctl(fd, EPOLL_CTL_DEL, wd, &event) == posix::success_response; // try to delete entry
  }
} EventBackend::s_platform;

const native_flags_t EventBackend::SimplePollReadFlags = EPOLLIN;

bool EventBackend::poll(int timeout) noexcept
{
  int count = ::epoll_wait(s_platform.fd, s_platform.output, MAX_EVENTS, timeout); // wait for new results
  results.clear(); // clear old results

  if(count == posix::error_response) // if error/timeout occurred
    return false; //fail

  const epoll_event* end = s_platform.output + count;
  for(epoll_event* pos = s_platform.output; pos != end; ++pos) // iterate through results
    results.emplace_back(std::make_pair(posix::fd_t(pos->data.fd), native_flags_t(pos->events))); // save result (in native format)
  return true;
}

#elif defined(__APPLE__)      /* Darwin 7+     */ || \
      defined(__FreeBSD__)    /* FreeBSD 4.1+  */ || \
      defined(__DragonFly__)  /* DragonFly BSD */ || \
      defined(__OpenBSD__)    /* OpenBSD 2.9+  */ || \
      defined(__NetBSD__)     /* NetBSD 2+     */

// BSD
#include <sys/time.h>
#include <sys/event.h>

// POSIX
#include <sys/socket.h>
#include <unistd.h>

// POSIX++
#include <cstdlib>
#include <cstdio>
#include <climits>
#include <cstring>

// STL
#include <vector>
#include <algorithm>
#include <iterator>

// PDTK
#include <cxxutils/vterm.h>
#include <cxxutils/error_helpers.h>

struct EventBackend::platform_dependant // poll notification (epoll)
{
  posix::fd_t kq;
  std::vector<struct kevent> koutput;   // events that were triggered

  static constexpr native_flags_t composite_flag(uint16_t actions, int16_t filters, uint32_t flags) noexcept
    { return native_flags_t(actions) | (uint16_t(filters) << 16) | (flags << 32); }

  static constexpr short extract_actions(native_flags_t flags) noexcept
    { return flags & 0xFFFF; }

  static constexpr ushort extract_filter(native_flags_t flags) noexcept
    { return (flags >> 16) & 0xFFFF; }

  static constexpr ushort extract_flags(native_flags_t flags) noexcept
    { return flags >> 32; }

  platform_dependant(void)
  {
    kq = posix::ignore_interruption(::kqueue);
    flaw(kq == posix::error_response, terminal::critical, std::exit(errno),,
         "Unable to create a new kqueue: %s", std::strerror(errno))
    koutput.resize(1024);
  }

  ~platform_dependant(void)
  {
    posix::close(kq);
  }

  bool add(posix::fd_t fd, native_flags_t flags) noexcept
  {
    struct kevent ev;
    EV_SET(&ev, fd, extract_filter(flags), EV_ADD | extract_actions(flags), extract_flags(flags), 0, nullptr);
    return kevent(kq, &ev, 1, nullptr, 0, nullptr) == posix::success_response;
  }

  bool remove(posix::fd_t fd) noexcept
  {
    struct kevent ev;
    EV_SET(&ev, fd, 0, EV_DELETE, 0, 0, nullptr);
    return kevent(kq, &ev, 1, nullptr, 0, nullptr) == posix::success_response;
  }

} EventBackend::s_platform;

const native_flags_t EventBackend::SimplePollReadFlags = platform_dependant::composite_flag(0, EVFILT_READ, 0);

bool EventBackend::poll(int timeout) noexcept
{
  uint32_t data;
  timespec tout;
  tout.tv_sec = timeout / 1000;
  tout.tv_nsec = (timeout % 1000) * 1000;

  int count = kevent(s_platform.kq, nullptr, 0, s_platform.koutput.data(), s_platform.koutput.size(), &tout);
  if(count <= 0)
    return false;

  struct kevent* end = s_platform.koutput.data() + count;
  for(struct kevent* pos = s_platform.koutput.data(); pos != end; ++pos) // iterate through results
    results.emplace_back(std::make_pair(posix::fd_t(pos->ident), platform_dependant::composite_flag(pos->filter, pos->flags)));
  return true;
}

#elif defined(__sun) && defined(__SVR4) // Solaris / OpenSolaris / OpenIndiana / illumos

#pragma message Not implemented, yet!
#pragma message See: http://docs.oracle.com/cd/E19253-01/816-5168/port-get-3c/index.html
#error The backend code in PDTK for Solaris / OpenSolaris / OpenIndiana / illumos is non-functional!  Please submit a patch!

// Solaris
#include <port.h>

// POSIX
#include <sys/socket.h>
#include <unistd.h>

// POSIX++
#include <cstdlib>
#include <cstdio>
#include <climits>
#include <cstring>

// STL
#include <vector>

// PDTK
#include <cxxutils/vterm.h>
#include <cxxutils/error_helpers.h>

struct platform_dependant
{
  posix::fd_t port;
  std::vector<port_event_t> pinput;   // events we want to monitor
  std::vector<port_event_t> poutput;  // events that were triggered

  platform_dependant(void)
  {
    port = ::port_create();
    flaw(port == posix::error_response, terminal::critical, std::exit(errno),,
         "Unable to create a new kqueue: %s", std::strerror(errno))
   pinput .reserve(1024);
   poutput.reserve(1024);
  }

  ~platform_dependant(void)
  {
    posix::close(port);
  }
} EventBackend::s_platform;

bool EventBackend::add(posix::fd_t fd, native_flags_t flags, callback_t function) noexcept
{
  port_event_t pev;

  s_platform.pinput.push_back(pev);
  queue.emplace(fd, (callback_info_t){flags, function});
  return true;
}

bool EventBackend::remove(posix::fd_t fd) noexcept
{
  return false;
}

bool EventBackend::poll(int timeout) noexcept
{
  native_flags_t flags;
  uint_t count = 0;
  timespec tout;
  tout.tv_sec = timeout / 1000;
  tout.tv_nsec = (timeout % 1000) * 1000;

  if(::port_getn(s_platform.port,
                 &s_platform.pinput.data(), s_platform.pinput.size(),
                 s_platform.poutput, MAX_EVENTS, &count
                 &tout) == posix::error_response)
    return false;

  port_event_t* end = s_platform.poutput + count;

  for(port_event_t* pos = s_platform.poutput; pos != end; ++pos) // iterate through results
  {
    //flags = from_kevent(*pos);

    results.emplace(posix::fd_t(pos->ident), flags);
  }
  return true;
}

#elif defined(__minix) // MINIX
#error No event backend code exists in PDTK for MINIX!  Please submit a patch!

#elif defined(__QNX__) // QNX
// QNX docs: http://www.qnx.com/developers/docs/7.0.0/index.html#com.qnx.doc.neutrino.devctl/topic/about.html
#error No event backend code exists in PDTK for QNX!  Please submit a patch!

#elif defined(__hpux) // HP-UX
// see http://nixdoc.net/man-pages/HP-UX/man7/poll.7.html
// and https://www.freebsd.org/cgi/man.cgi?query=poll&sektion=7&apropos=0&manpath=HP-UX+11.22
// uses /dev/poll
#error No event backend code exists in PDTK for HP-UX!  Please submit a patch!

#include <sys/devpoll.h>

#elif defined(_AIX) // IBM AIX
// see https://www.ibm.com/support/knowledgecenter/ssw_aix_61/com.ibm.aix.basetrf1/pollset.htm
// uses pollset_* functions

#include <sys/poll.h>
#include <sys/pollset.h>

  pollset_t n;
  n = pollset_create(-1);
  pollset_destroy(n);

#error No event backend code exists in PDTK for IBM AIX!  Please submit a patch!

#elif defined(BSD)
#error Unrecognized BSD derivative!

#elif defined(__unix__)
#error Unrecognized UNIX variant!

#else
#error This platform is not supported.
#endif

bool EventBackend::add(posix::fd_t fd, native_flags_t flags, callback_t function) noexcept
{
  native_flags_t total_flags = flags;
  bool found = false;
  auto entries = queue.equal_range(fd); // find modified entry!
  for(auto& pos = entries.first; pos != entries.second; ++pos)
  {
    total_flags |= pos->second.flags;
    found |= &(pos->second.function) == &function; // save if function was ever found
    if(&(pos->second.function) == &function) // if the FD is in the queue and it's callback function matches
      pos->second.flags |= flags; // simply modify the flags to include the current
  }

  if(!found) // function wasn't found
    queue.emplace(fd, (callback_info_t){flags, function}); // make a new entry for this FD

  return s_platform.add(fd, total_flags);
}

bool EventBackend::remove(posix::fd_t fd, native_flags_t flags) noexcept
{
  native_flags_t remaining_flags = 0;
  auto entries = queue.equal_range(fd); // find modified entry!
  for(auto& pos = entries.first; pos != entries.second; ++pos)
  {
    if((pos->second.flags & flags) == pos->second.flags) // if all flags match
      queue.erase(pos);
    else if(pos->second.flags & flags) // if only some flags match
    {
      pos->second.flags ^= pos->second.flags & flags; // remove flags
      remaining_flags |= pos->second.flags; // accumulate remaining flags
    }
  }
  return remaining_flags
      ? s_platform.add(fd, remaining_flags)
      : s_platform.remove(fd);
}

<commit_msg>fix flag compiter<commit_after>#include "eventbackend.h"

#define MAX_EVENTS 1024

// PDTK
#include <cxxutils/vterm.h>

lockable<std::unordered_multimap<posix::fd_t, EventBackend::callback_info_t>> EventBackend::queue;
std::list<std::pair<posix::fd_t, native_flags_t>> EventBackend::results;

#if defined(__linux__)

// epoll needs kernel 2.5.44

// Linux
#include <sys/epoll.h>

struct EventBackend::platform_dependant // poll notification (epoll)
{
  posix::fd_t fd;
  struct epoll_event output[MAX_EVENTS];

  platform_dependant(void) noexcept
    : fd(posix::invalid_descriptor)
  {
    fd = ::epoll_create(MAX_EVENTS);
    flaw(fd == posix::invalid_descriptor, terminal::critical, std::exit(errno),,
         "Unable to create an instance of epoll! %s", std::strerror(errno))
  }

  ~platform_dependant(void) noexcept
  {
    posix::close(fd);
    fd = posix::invalid_descriptor;
  }

  bool add(posix::fd_t wd, native_flags_t flags) noexcept
  {
    struct epoll_event native_event;
    native_event.data.fd = wd;
    native_event.events = flags; // be sure to convert to native events
    return ::epoll_ctl(fd, EPOLL_CTL_ADD, wd, &native_event) == posix::success_response || // add new event OR
        (errno == EEXIST && ::epoll_ctl(fd, EPOLL_CTL_MOD, wd, &native_event) == posix::success_response); // modify existing event
  }

  bool remove(posix::fd_t wd) noexcept
  {
    struct epoll_event event;
    return ::epoll_ctl(fd, EPOLL_CTL_DEL, wd, &event) == posix::success_response; // try to delete entry
  }
} EventBackend::s_platform;

const native_flags_t EventBackend::SimplePollReadFlags = EPOLLIN;

bool EventBackend::poll(int timeout) noexcept
{
  int count = ::epoll_wait(s_platform.fd, s_platform.output, MAX_EVENTS, timeout); // wait for new results
  results.clear(); // clear old results

  if(count == posix::error_response) // if error/timeout occurred
    return false; //fail

  const epoll_event* end = s_platform.output + count;
  for(epoll_event* pos = s_platform.output; pos != end; ++pos) // iterate through results
    results.emplace_back(std::make_pair(posix::fd_t(pos->data.fd), native_flags_t(pos->events))); // save result (in native format)
  return true;
}

#elif defined(__APPLE__)      /* Darwin 7+     */ || \
      defined(__FreeBSD__)    /* FreeBSD 4.1+  */ || \
      defined(__DragonFly__)  /* DragonFly BSD */ || \
      defined(__OpenBSD__)    /* OpenBSD 2.9+  */ || \
      defined(__NetBSD__)     /* NetBSD 2+     */

// BSD
#include <sys/time.h>
#include <sys/event.h>

// POSIX
#include <sys/socket.h>
#include <unistd.h>

// POSIX++
#include <cstdlib>
#include <cstdio>
#include <climits>
#include <cstring>

// STL
#include <vector>
#include <algorithm>
#include <iterator>

// PDTK
#include <cxxutils/vterm.h>
#include <cxxutils/error_helpers.h>

struct EventBackend::platform_dependant // poll notification (epoll)
{
  posix::fd_t kq;
  std::vector<struct kevent> koutput;   // events that were triggered

  static constexpr native_flags_t composite_flag(uint16_t actions, int16_t filters, uint32_t flags) noexcept
    { return native_flags_t(actions) | (uint16_t(filters) << 16) | (flags << 32); }

  static constexpr short extract_actions(native_flags_t flags) noexcept
    { return flags & 0xFFFF; }

  static constexpr ushort extract_filter(native_flags_t flags) noexcept
    { return (flags >> 16) & 0xFFFF; }

  static constexpr ushort extract_flags(native_flags_t flags) noexcept
    { return flags >> 32; }

  platform_dependant(void)
  {
    kq = posix::ignore_interruption(::kqueue);
    flaw(kq == posix::error_response, terminal::critical, std::exit(errno),,
         "Unable to create a new kqueue: %s", std::strerror(errno))
    koutput.resize(1024);
  }

  ~platform_dependant(void)
  {
    posix::close(kq);
  }

  bool add(posix::fd_t fd, native_flags_t flags) noexcept
  {
    struct kevent ev;
    EV_SET(&ev, fd, extract_filter(flags), EV_ADD | extract_actions(flags), extract_flags(flags), 0, nullptr);
    return kevent(kq, &ev, 1, nullptr, 0, nullptr) == posix::success_response;
  }

  bool remove(posix::fd_t fd) noexcept
  {
    struct kevent ev;
    EV_SET(&ev, fd, 0, EV_DELETE, 0, 0, nullptr);
    return kevent(kq, &ev, 1, nullptr, 0, nullptr) == posix::success_response;
  }

} EventBackend::s_platform;

const native_flags_t EventBackend::SimplePollReadFlags = platform_dependant::composite_flag(0, EVFILT_READ, 0);

bool EventBackend::poll(int timeout) noexcept
{
  uint32_t data;
  timespec tout;
  tout.tv_sec = timeout / 1000;
  tout.tv_nsec = (timeout % 1000) * 1000;

  int count = kevent(s_platform.kq, nullptr, 0, s_platform.koutput.data(), s_platform.koutput.size(), &tout);
  if(count <= 0)
    return false;

  struct kevent* end = s_platform.koutput.data() + count;
  for(struct kevent* pos = s_platform.koutput.data(); pos != end; ++pos) // iterate through results
    results.emplace_back(std::make_pair(posix::fd_t(pos->ident), platform_dependant::composite_flag(pos->flags, pos->filter, pos->fflags)));
  return true;
}

#elif defined(__sun) && defined(__SVR4) // Solaris / OpenSolaris / OpenIndiana / illumos

#pragma message Not implemented, yet!
#pragma message See: http://docs.oracle.com/cd/E19253-01/816-5168/port-get-3c/index.html
#error The backend code in PDTK for Solaris / OpenSolaris / OpenIndiana / illumos is non-functional!  Please submit a patch!

// Solaris
#include <port.h>

// POSIX
#include <sys/socket.h>
#include <unistd.h>

// POSIX++
#include <cstdlib>
#include <cstdio>
#include <climits>
#include <cstring>

// STL
#include <vector>

// PDTK
#include <cxxutils/vterm.h>
#include <cxxutils/error_helpers.h>

struct platform_dependant
{
  posix::fd_t port;
  std::vector<port_event_t> pinput;   // events we want to monitor
  std::vector<port_event_t> poutput;  // events that were triggered

  platform_dependant(void)
  {
    port = ::port_create();
    flaw(port == posix::error_response, terminal::critical, std::exit(errno),,
         "Unable to create a new kqueue: %s", std::strerror(errno))
   pinput .reserve(1024);
   poutput.reserve(1024);
  }

  ~platform_dependant(void)
  {
    posix::close(port);
  }
} EventBackend::s_platform;

bool EventBackend::add(posix::fd_t fd, native_flags_t flags, callback_t function) noexcept
{
  port_event_t pev;

  s_platform.pinput.push_back(pev);
  queue.emplace(fd, (callback_info_t){flags, function});
  return true;
}

bool EventBackend::remove(posix::fd_t fd) noexcept
{
  return false;
}

bool EventBackend::poll(int timeout) noexcept
{
  native_flags_t flags;
  uint_t count = 0;
  timespec tout;
  tout.tv_sec = timeout / 1000;
  tout.tv_nsec = (timeout % 1000) * 1000;

  if(::port_getn(s_platform.port,
                 &s_platform.pinput.data(), s_platform.pinput.size(),
                 s_platform.poutput, MAX_EVENTS, &count
                 &tout) == posix::error_response)
    return false;

  port_event_t* end = s_platform.poutput + count;

  for(port_event_t* pos = s_platform.poutput; pos != end; ++pos) // iterate through results
  {
    //flags = from_kevent(*pos);

    results.emplace(posix::fd_t(pos->ident), flags);
  }
  return true;
}

#elif defined(__minix) // MINIX
#error No event backend code exists in PDTK for MINIX!  Please submit a patch!

#elif defined(__QNX__) // QNX
// QNX docs: http://www.qnx.com/developers/docs/7.0.0/index.html#com.qnx.doc.neutrino.devctl/topic/about.html
#error No event backend code exists in PDTK for QNX!  Please submit a patch!

#elif defined(__hpux) // HP-UX
// see http://nixdoc.net/man-pages/HP-UX/man7/poll.7.html
// and https://www.freebsd.org/cgi/man.cgi?query=poll&sektion=7&apropos=0&manpath=HP-UX+11.22
// uses /dev/poll
#error No event backend code exists in PDTK for HP-UX!  Please submit a patch!

#include <sys/devpoll.h>

#elif defined(_AIX) // IBM AIX
// see https://www.ibm.com/support/knowledgecenter/ssw_aix_61/com.ibm.aix.basetrf1/pollset.htm
// uses pollset_* functions

#include <sys/poll.h>
#include <sys/pollset.h>

  pollset_t n;
  n = pollset_create(-1);
  pollset_destroy(n);

#error No event backend code exists in PDTK for IBM AIX!  Please submit a patch!

#elif defined(BSD)
#error Unrecognized BSD derivative!

#elif defined(__unix__)
#error Unrecognized UNIX variant!

#else
#error This platform is not supported.
#endif

bool EventBackend::add(posix::fd_t fd, native_flags_t flags, callback_t function) noexcept
{
  native_flags_t total_flags = flags;
  bool found = false;
  auto entries = queue.equal_range(fd); // find modified entry!
  for(auto& pos = entries.first; pos != entries.second; ++pos)
  {
    total_flags |= pos->second.flags;
    found |= &(pos->second.function) == &function; // save if function was ever found
    if(&(pos->second.function) == &function) // if the FD is in the queue and it's callback function matches
      pos->second.flags |= flags; // simply modify the flags to include the current
  }

  if(!found) // function wasn't found
    queue.emplace(fd, (callback_info_t){flags, function}); // make a new entry for this FD

  return s_platform.add(fd, total_flags);
}

bool EventBackend::remove(posix::fd_t fd, native_flags_t flags) noexcept
{
  native_flags_t remaining_flags = 0;
  auto entries = queue.equal_range(fd); // find modified entry!
  for(auto& pos = entries.first; pos != entries.second; ++pos)
  {
    if((pos->second.flags & flags) == pos->second.flags) // if all flags match
      queue.erase(pos);
    else if(pos->second.flags & flags) // if only some flags match
    {
      pos->second.flags ^= pos->second.flags & flags; // remove flags
      remaining_flags |= pos->second.flags; // accumulate remaining flags
    }
  }
  return remaining_flags
      ? s_platform.add(fd, remaining_flags)
      : s_platform.remove(fd);
}

<|endoftext|>


<commit_before>#include <bits/stdc++.h>
using namespace std;

long long horner(int n, int c[], int x)
{
    long long res = c[0];
    for(int i=0; i<n; i++) res = res*x + c[i+1];

    return res;
}

int main()
{
    unsigned k, cases = 0;
    int n, x, coe[1001];

    ios_base::sync_with_stdio(0);

    // polynomial
    while(cin >> n)
    {
        if(n == -1) break;

        cout << "Case " << ++cases << ":\n";
        for(int i=0; i<=n; i++) cin >> coe[i];

        // points
        cin >> k;
        while(k--)
        {
            cin >> x;
            cout << horner(n, coe, x) << "\n";
        }
    }

    return 0;
}<commit_msg>spoj/00-classical/POLEVAL.cc<commit_after>#include <bits/stdc++.h>
using namespace std;

long long horner(int n, int c[], int x)
{
    long long res = c[0];
    for(int i=0; i<n; i++) res = res*x + c[i+1];

    return res;
}

int main()
{
    int n, x, coe[1001];
    unsigned k, cases = 0;

    cin.tie(NULL);
    ios_base::sync_with_stdio(0);

    // polynomial
    while(cin >> n)
    {
        if(n == -1) break;

        cout << "Case " << ++cases << ":\n";
        for(int i=0; i<=n; i++) cin >> coe[i];

        // points
        cin >> k;
        while(k--)
        {
            cin >> x;
            cout << horner(n, coe, x) << "\n";
        }
    }

    return 0;
}<|endoftext|>


<commit_before>/* ndhs.c - DHCPv4/DHCPv6 and IPv6 router advertisement server
 *
 * Copyright 2014-2017 Nicholas J. Kain <njkain at gmail dot com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#define NDHS_VERSION "2.0"
#define LEASEFILE_PATH "/store/dynlease.txt"

#include <memory>
#include <string>
#include <vector>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <ctype.h>
#include <pwd.h>
#include <grp.h>
#include <signal.h>
#include <errno.h>
#include <asio.hpp>
#include <fmt/format.h>
#include <nk/optionarg.hpp>
#include <nk/from_string.hpp>
#include <nk/prng.hpp>
extern "C" {
#include "nk/log.h"
#include "nk/privs.h"
}
#include "nlsocket.hpp"
#include "dhcp6.hpp"
#include "dhcp4.hpp"
#include "dhcp_state.hpp"
#include "dynlease.hpp"
#include "duid.hpp"

static asio::io_service io_service;
static asio::signal_set asio_signal_set(io_service);
static std::string configfile{"/etc/ndhs.conf"};
static std::string chroot_path;
static uid_t ndhs_uid;
static gid_t ndhs_gid;

std::unique_ptr<NLSocket> nl_socket;

static std::vector<std::unique_ptr<D6Listener>> v6_listeners;
static std::vector<std::unique_ptr<D4Listener>> v4_listeners;

extern void parse_config(const std::string &path);

static void init_listeners()
{
    auto v6l = &v6_listeners;
    auto v4l = &v4_listeners;
    bound_interfaces_foreach([v6l, v4l](const std::string &i, bool use_v4, bool use_v6,
                                        uint8_t preference) {
        if (use_v6) {
            v6l->emplace_back(std::make_unique<D6Listener>());
            if (!v6l->back()->init(i, preference)) {
                v6l->pop_back();
                fmt::print(stderr, "Can't bind to v6 interface: {}\n", i);
            }
        }
        if (use_v4) {
            v4l->emplace_back(std::make_unique<D4Listener>());
            if (!v4l->back()->init(i)) {
                v4l->pop_back();
                fmt::print(stderr, "Can't bind to v4 interface: {}\n", i);
            }
        }
    });
}

int64_t get_current_ts()
{
    struct timespec ts;
    if (clock_gettime(CLOCK_MONOTONIC, &ts))
        suicide("clock_gettime failed");
    return ts.tv_sec;
}

void set_user_runas(size_t /* linenum */, std::string &&username)
{
    if (nk_uidgidbyname(username.c_str(), &ndhs_uid, &ndhs_gid)) {
        fmt::print(stderr, "invalid user '{}' specified\n", username);
        std::exit(EXIT_FAILURE);
    }
}
void set_chroot_path(size_t /* linenum */, std::string &&path)
{
    chroot_path = std::move(path);
}

static void process_signals()
{
    sigset_t mask;
    sigemptyset(&mask);
    sigaddset(&mask, SIGCHLD);
    sigaddset(&mask, SIGPIPE);
    sigaddset(&mask, SIGUSR1);
    sigaddset(&mask, SIGUSR2);
    sigaddset(&mask, SIGTSTP);
    sigaddset(&mask, SIGTTIN);
    sigaddset(&mask, SIGHUP);
    if (sigprocmask(SIG_BLOCK, &mask, nullptr) < 0) {
        fmt::print(stderr, "sigprocmask failed\n");
        std::exit(EXIT_FAILURE);
    }
    asio_signal_set.add(SIGINT);
    asio_signal_set.add(SIGTERM);
    asio_signal_set.async_wait([](const std::error_code &, int) { io_service.stop(); });
}

static void print_version(void)
{
    fmt::print(stderr, "ndhs " NDHS_VERSION ", ipv6 router advertisment and dhcp server.\n"
               "Copyright 2014-2017 Nicholas J. Kain\n"
               "All rights reserved.\n\n"
               "Redistribution and use in source and binary forms, with or without\n"
               "modification, are permitted provided that the following conditions are met:\n\n"
               "- Redistributions of source code must retain the above copyright notice,\n"
               "  this list of conditions and the following disclaimer.\n"
               "- Redistributions in binary form must reproduce the above copyright notice,\n"
               "  this list of conditions and the following disclaimer in the documentation\n"
               "  and/or other materials provided with the distribution.\n\n"
               "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n"
               "AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n"
               "IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n"
               "ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE\n"
               "LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n"
               "CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n"
               "SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n"
               "INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n"
               "CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n"
               "ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n"
               "POSSIBILITY OF SUCH DAMAGE.\n");
}

enum OpIdx {
    OPT_UNKNOWN, OPT_HELP, OPT_VERSION, OPT_CONFIG, OPT_QUIET
};
static const option::Descriptor usage[] = {
    { OPT_UNKNOWN,    0,  "",           "", Arg::Unknown,
        "ndhs " NDHS_VERSION ", DHCPv4/DHCPv6 and IPv6 Router Advertisement server.\n"
        "Copyright 2014-2017 Nicholas J. Kain\n"
        "ndhs [options] [configfile]...\n\nOptions:" },
    { OPT_HELP,       0, "h",            "help",    Arg::None, "\t-h, \t--help  \tPrint usage and exit." },
    { OPT_VERSION,    0, "v",         "version",    Arg::None, "\t-v, \t--version  \tPrint version and exit." },
    { OPT_CONFIG,     0, "c",          "config",  Arg::String, "\t-c, \t--config  \tPath to configuration file (default: /etc/ndhs.conf)."},
    { OPT_QUIET,      0, "q",           "quiet",    Arg::None, "\t-q, \t--quiet  \tDon't log to std(out|err) or syslog." },
    {0,0,0,0,0,0}
};
static void process_options(int ac, char *av[])
{
    ac-=ac>0; av+=ac>0;
    option::Stats stats(usage, ac, av);
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wvla"
    option::Option options[stats.options_max], buffer[stats.buffer_max];
#pragma GCC diagnostic pop
    option::Parser parse(usage, ac, av, options, buffer);
#else
    auto options = std::make_unique<option::Option[]>(stats.options_max);
    auto buffer = std::make_unique<option::Option[]>(stats.buffer_max);
    option::Parser parse(usage, ac, av, options.get(), buffer.get());
#endif
    if (parse.error())
        std::exit(EXIT_FAILURE);
    if (options[OPT_HELP]) {
        uint16_t col{80};
        if (const auto cols = getenv("COLUMNS")) {
            if (auto t = nk::from_string<uint16_t>(cols)) col = *t;
        }
        option::printUsage(fwrite, stdout, usage, col);
        std::exit(EXIT_FAILURE);
    }
    if (options[OPT_VERSION]) {
        print_version();
        std::exit(EXIT_FAILURE);
    }

    std::vector<std::string> addrlist;

    for (int i = 0; i < parse.optionsCount(); ++i) {
        option::Option &opt = buffer[i];
        switch (opt.index()) {
            case OPT_CONFIG: configfile = std::string(opt.arg); break;
            case OPT_QUIET: gflags_quiet = 1; break;
        }
    }

    if (configfile.size())
        parse_config(configfile);

    for (int i = 0; i < parse.nonOptionsCount(); ++i)
        parse_config(parse.nonOption(i));

    if (!bound_interfaces_count()) {
        fmt::print(stderr, "No interfaces have been bound\n");
        std::exit(EXIT_FAILURE);
    }
    if (!ndhs_uid || !ndhs_gid) {
        fmt::print(stderr, "No non-root user account is specified.\n");
        std::exit(EXIT_FAILURE);
    }
    if (chroot_path.empty()) {
        fmt::print(stderr, "No chroot path is specified.\n");
        std::exit(EXIT_FAILURE);
    }

    nl_socket = std::make_unique<NLSocket>();
    init_listeners();

    umask(077);
    process_signals();

    nk_set_chroot(chroot_path.c_str());
    duid_load_from_file();
    dynlease_deserialize(LEASEFILE_PATH);
    nk_set_uidgid(ndhs_uid, ndhs_gid, nullptr, 0);
}

int main(int ac, char *av[])
{
    gflags_log_name = const_cast<char *>("ndhs");

    process_options(ac, av);

    io_service.run();

    dynlease_serialize(LEASEFILE_PATH);

    std::exit(EXIT_SUCCESS);
}

<commit_msg>ndhs: Remove asio::io_service and just use poll and posix signal api.<commit_after>/* ndhs.c - DHCPv4/DHCPv6 and IPv6 router advertisement server
 *
 * Copyright 2014-2020 Nicholas J. Kain <njkain at gmail dot com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#define NDHS_VERSION "2.0"
#define LEASEFILE_PATH "/store/dynlease.txt"

#include <memory>
#include <string>
#include <vector>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <ctype.h>
#include <pwd.h>
#include <grp.h>
#include <signal.h>
#include <poll.h>
#include <errno.h>
#include <fmt/format.h>
#include <nk/optionarg.hpp>
#include <nk/from_string.hpp>
extern "C" {
#include "nk/log.h"
#include "nk/privs.h"
}
#include "nlsocket.hpp"
#include "dhcp6.hpp"
#include "dhcp4.hpp"
#include "dhcp_state.hpp"
#include "dynlease.hpp"
#include "duid.hpp"

static std::string configfile{"/etc/ndhs.conf"};
static std::string chroot_path;
static uid_t ndhs_uid;
static gid_t ndhs_gid;

std::unique_ptr<NLSocket> nl_socket;

static std::vector<std::unique_ptr<D6Listener>> v6_listeners;
static std::vector<std::unique_ptr<D4Listener>> v4_listeners;

extern void parse_config(const std::string &path);

static void init_listeners()
{
    auto v6l = &v6_listeners;
    auto v4l = &v4_listeners;
    bound_interfaces_foreach([v6l, v4l](const std::string &i, bool use_v4, bool use_v6,
                                        uint8_t preference) {
        if (use_v6) {
            v6l->emplace_back(std::make_unique<D6Listener>());
            if (!v6l->back()->init(i, preference)) {
                v6l->pop_back();
                fmt::print(stderr, "Can't bind to v6 interface: {}\n", i);
            }
        }
        if (use_v4) {
            v4l->emplace_back(std::make_unique<D4Listener>());
            if (!v4l->back()->init(i)) {
                v4l->pop_back();
                fmt::print(stderr, "Can't bind to v4 interface: {}\n", i);
            }
        }
    });
}

int64_t get_current_ts()
{
    struct timespec ts;
    if (clock_gettime(CLOCK_MONOTONIC, &ts))
        suicide("clock_gettime failed");
    return ts.tv_sec;
}

void set_user_runas(size_t /* linenum */, std::string &&username)
{
    if (nk_uidgidbyname(username.c_str(), &ndhs_uid, &ndhs_gid)) {
        fmt::print(stderr, "invalid user '{}' specified\n", username);
        std::exit(EXIT_FAILURE);
    }
}
void set_chroot_path(size_t /* linenum */, std::string &&path)
{
    chroot_path = std::move(path);
}

static volatile sig_atomic_t l_signal_exit;
static void signal_handler(int signo)
{
    switch (signo) {
    case SIGCHLD: {
        while (waitpid(-1, nullptr, WNOHANG) > -1);
        break;
    }
    case SIGINT:
    case SIGTERM: l_signal_exit = 1; break;
    default: break;
    }
}

static void setup_signals_ndhs()
{
    static const int ss[] = {
        SIGCHLD, SIGINT, SIGTERM, SIGKILL
    };
    sigset_t mask;
    if (sigprocmask(0, 0, &mask) < 0)
        suicide("sigprocmask failed");
    for (int i = 0; ss[i] != SIGKILL; ++i)
        if (sigdelset(&mask, ss[i]))
            suicide("sigdelset failed");
    if (sigaddset(&mask, SIGPIPE))
        suicide("sigaddset failed");
    if (sigprocmask(SIG_SETMASK, &mask, static_cast<sigset_t *>(nullptr)) < 0)
        suicide("sigprocmask failed");

    struct sigaction sa;
    memset(&sa, 0, sizeof sa);
    sa.sa_handler = signal_handler;
    sa.sa_flags = SA_RESTART;
    if (sigemptyset(&sa.sa_mask))
        suicide("sigemptyset failed");
    for (int i = 0; ss[i] != SIGKILL; ++i)
        if (sigaction(ss[i], &sa, NULL))
            suicide("sigaction failed");
}

static void print_version(void)
{
    fmt::print(stderr, "ndhs " NDHS_VERSION ", ipv6 router advertisment and dhcp server.\n"
               "Copyright 2014-2017 Nicholas J. Kain\n"
               "All rights reserved.\n\n"
               "Redistribution and use in source and binary forms, with or without\n"
               "modification, are permitted provided that the following conditions are met:\n\n"
               "- Redistributions of source code must retain the above copyright notice,\n"
               "  this list of conditions and the following disclaimer.\n"
               "- Redistributions in binary form must reproduce the above copyright notice,\n"
               "  this list of conditions and the following disclaimer in the documentation\n"
               "  and/or other materials provided with the distribution.\n\n"
               "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n"
               "AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n"
               "IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n"
               "ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE\n"
               "LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n"
               "CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n"
               "SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n"
               "INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n"
               "CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n"
               "ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n"
               "POSSIBILITY OF SUCH DAMAGE.\n");
}

enum OpIdx {
    OPT_UNKNOWN, OPT_HELP, OPT_VERSION, OPT_CONFIG, OPT_QUIET
};
static const option::Descriptor usage[] = {
    { OPT_UNKNOWN,    0,  "",           "", Arg::Unknown,
        "ndhs " NDHS_VERSION ", DHCPv4/DHCPv6 and IPv6 Router Advertisement server.\n"
        "Copyright 2014-2017 Nicholas J. Kain\n"
        "ndhs [options] [configfile]...\n\nOptions:" },
    { OPT_HELP,       0, "h",            "help",    Arg::None, "\t-h, \t--help  \tPrint usage and exit." },
    { OPT_VERSION,    0, "v",         "version",    Arg::None, "\t-v, \t--version  \tPrint version and exit." },
    { OPT_CONFIG,     0, "c",          "config",  Arg::String, "\t-c, \t--config  \tPath to configuration file (default: /etc/ndhs.conf)."},
    { OPT_QUIET,      0, "q",           "quiet",    Arg::None, "\t-q, \t--quiet  \tDon't log to std(out|err) or syslog." },
    {0,0,0,0,0,0}
};
static void process_options(int ac, char *av[])
{
    ac-=ac>0; av+=ac>0;
    option::Stats stats(usage, ac, av);
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wvla"
    option::Option options[stats.options_max], buffer[stats.buffer_max];
#pragma GCC diagnostic pop
    option::Parser parse(usage, ac, av, options, buffer);
#else
    auto options = std::make_unique<option::Option[]>(stats.options_max);
    auto buffer = std::make_unique<option::Option[]>(stats.buffer_max);
    option::Parser parse(usage, ac, av, options.get(), buffer.get());
#endif
    if (parse.error())
        std::exit(EXIT_FAILURE);
    if (options[OPT_HELP]) {
        uint16_t col{80};
        if (const auto cols = getenv("COLUMNS")) {
            if (auto t = nk::from_string<uint16_t>(cols)) col = *t;
        }
        option::printUsage(fwrite, stdout, usage, col);
        std::exit(EXIT_FAILURE);
    }
    if (options[OPT_VERSION]) {
        print_version();
        std::exit(EXIT_FAILURE);
    }

    std::vector<std::string> addrlist;

    for (int i = 0; i < parse.optionsCount(); ++i) {
        option::Option &opt = buffer[i];
        switch (opt.index()) {
            case OPT_CONFIG: configfile = std::string(opt.arg); break;
            case OPT_QUIET: gflags_quiet = 1; break;
        }
    }

    if (configfile.size())
        parse_config(configfile);

    for (int i = 0; i < parse.nonOptionsCount(); ++i)
        parse_config(parse.nonOption(i));

    if (!bound_interfaces_count()) {
        fmt::print(stderr, "No interfaces have been bound\n");
        std::exit(EXIT_FAILURE);
    }
    if (!ndhs_uid || !ndhs_gid) {
        fmt::print(stderr, "No non-root user account is specified.\n");
        std::exit(EXIT_FAILURE);
    }
    if (chroot_path.empty()) {
        fmt::print(stderr, "No chroot path is specified.\n");
        std::exit(EXIT_FAILURE);
    }

    nl_socket = std::make_unique<NLSocket>();
    init_listeners();

    umask(077);
    setup_signals_ndhs();

    nk_set_chroot(chroot_path.c_str());
    duid_load_from_file();
    dynlease_deserialize(LEASEFILE_PATH);
    nk_set_uidgid(ndhs_uid, ndhs_gid, nullptr, 0);
}

int main(int ac, char *av[])
{
    gflags_log_name = const_cast<char *>("ndhs");

    process_options(ac, av);

    struct pollfd pfds[1];
    memset(pfds, 0, sizeof pfds);
    pfds[0].fd = -1;
    pfds[0].events = POLLIN|POLLHUP|POLLERR|POLLRDHUP;

    for (;;) {
        if (poll(pfds, 1, -1) < 0) {
            if (errno != EINTR)
                suicide("poll failed");
        }
        if (l_signal_exit) break;
    }

    dynlease_serialize(LEASEFILE_PATH);

    std::exit(EXIT_SUCCESS);
}

<|endoftext|>


<commit_before>#include "plot.h"
#include "curvedata.h"
#include "signaldata.h"
#include <qwt_plot_grid.h>
#include <qwt_plot_layout.h>
#include <qwt_plot_canvas.h>
#include <qwt_plot_marker.h>
#include <qwt_plot_curve.h>
#include <qwt_plot_directpainter.h>
#include <qwt_curve_fitter.h>
#include <qwt_painter.h>
#include <qwt_scale_engine.h>
#include <qwt_scale_draw.h>
#include <qwt_plot_zoomer.h>
#include <qwt_plot_panner.h>
#include <qwt_plot_magnifier.h>
#include <qwt_text.h>

#include <qevent.h>

#include <cassert>

#define DEFAULT_CURVE_STYLE QwtPlotCurve::Dots

class MyPanner : public QObject
{
public:

  QPoint mInitialPos;
  bool mEnabled;
  int mMouseButton;
  int mKeyboardButton;
  Plot* mPlot;

  MyPanner(Plot* plot) : QObject(plot)
  {
    mEnabled = false;
    mMouseButton = Qt::LeftButton;
    mKeyboardButton = Qt::NoButton;
    mPlot = plot;
    mPlot->canvas()->installEventFilter(this);
  }

  bool eventFilter( QObject *object, QEvent *event )
  {
    switch ( event->type() )
    {
      case QEvent::MouseButtonPress:
      {
        widgetMousePressEvent( ( QMouseEvent * )event );
        break;
      }
      case QEvent::MouseMove:
      {
        widgetMouseMoveEvent( ( QMouseEvent * )event );
        break;
      }
      case QEvent::MouseButtonRelease:
      {
        widgetMouseReleaseEvent( ( QMouseEvent * )event );
        break;
      }
    }

    return false;
  }

  void moveCanvas( int dx, int dy )
  {
    if ( dx == 0 && dy == 0 )
        return;

    if (!mPlot->isStopped())
      dx = 0;

    for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
    {
      const QwtScaleMap map = mPlot->canvasMap( axis );
      const double p1 = map.transform( mPlot->axisScaleDiv( axis )->lowerBound() );
      const double p2 = map.transform( mPlot->axisScaleDiv( axis )->upperBound() );

      double d1, d2;
      if ( axis == QwtPlot::xBottom || axis == QwtPlot::xTop )
      {
        d1 = map.invTransform( p1 - dx );
        d2 = map.invTransform( p2 - dx );
      }
      else
      {
        d1 = map.invTransform( p1 - dy );
        d2 = map.invTransform( p2 - dy );
      }
      mPlot->setAxisScale( axis, d1, d2 );
    }

    mPlot->flagAxisSyncRequired();
    mPlot->replot();
  }

  void widgetMousePressEvent( QMouseEvent *mouseEvent )
  {
    if (mouseEvent->button() != mMouseButton)
    {
      return;
    }

    if ((mouseEvent->modifiers() & Qt::KeyboardModifierMask) != (mKeyboardButton & Qt::KeyboardModifierMask))
    {
      return;
    }

    mInitialPos = mouseEvent->pos();
    mEnabled = true;
  }

  void widgetMouseMoveEvent( QMouseEvent *mouseEvent )
  {
    if (!mEnabled)
      return;

    QPoint pos = mouseEvent->pos();
    if (pos != mInitialPos)
    {
      moveCanvas(pos.x() - mInitialPos.x(), pos.y() - mInitialPos.y());
      mInitialPos = mouseEvent->pos();
    }
  }

  void widgetMouseReleaseEvent( QMouseEvent *mouseEvent )
  {
    mEnabled = false;
  }

};

class MyScaleDraw : public QwtScaleDraw
{
  virtual QwtText label(double value) const
  {
    return QString::number(value, 'f', 2);
  }
};

class MyZoomer: public QwtPlotZoomer
{
public:

    Plot* mPlot;

    MyZoomer(Plot *plot) : QwtPlotZoomer(plot->canvas())
    {
      mPlot = plot;
      setTrackerMode(AlwaysOn);
    }

    virtual QwtText trackerTextF(const QPointF &pos) const
    {
      QColor bg(Qt::white);
      bg.setAlpha(200);

      QwtText text = QwtPlotZoomer::trackerTextF(pos);
      text.setBackgroundBrush( QBrush( bg ));
      return text;
    }

protected:

    virtual void rescale()
    {
      mPlot->flagAxisSyncRequired();
      QwtPlotZoomer::rescale();
    }

};

class MyMagnifier: public QwtPlotMagnifier
{
public:
    Plot* mPlot;

    MyMagnifier(Plot* plot) : QwtPlotMagnifier(plot->canvas())
    {
      mPlot = plot;
    }

protected:

    // Normally, a value < 1.0 zooms in, a value > 1.0 zooms out.
    // This function is overloaded to invert the magnification direction.
    virtual void rescale( double factor )
    {
      factor = qAbs( factor );
      factor = (1-factor) + 1;

      mPlot->flagAxisSyncRequired();
      this->QwtPlotMagnifier::rescale(factor);
    }

};

Plot::Plot(QWidget *parent):
  QwtPlot(parent),
  d_origin(0),
  d_grid(0),
  mStopped(true),
  mAxisSyncRequired(false),
  mColorMode(0),
  mTimeWindow(10.0)
{
  setAutoReplot(false);

  // The backing store is important, when working with widget
  // overlays ( f.e rubberbands for zooming ).
  // Here we don't have them and the internal
  // backing store of QWidget is good enough.

  canvas()->setPaintAttribute(QwtPlotCanvas::BackingStore, false);

#if defined(Q_WS_X11)
  // Even if not recommended by TrollTech, Qt::WA_PaintOutsidePaintEvent
  // works on X11. This has a nice effect on the performance.

  canvas()->setAttribute(Qt::WA_PaintOutsidePaintEvent, true);

  // Disabling the backing store of Qt improves the performance
  // for the direct painter even more, but the canvas becomes
  // a native window of the window system, receiving paint events
  // for resize and expose operations. Those might be expensive
  // when there are many points and the backing store of
  // the canvas is disabled. So in this application
  // we better don't both backing stores.

  if ( canvas()->testPaintAttribute( QwtPlotCanvas::BackingStore ) )
  {
    canvas()->setAttribute(Qt::WA_PaintOnScreen, true);
    canvas()->setAttribute(Qt::WA_NoSystemBackground, true);
  }

#endif

  plotLayout()->setAlignCanvasToScales(true);

  setAxisAutoScale(QwtPlot::xBottom, false);
  setAxisAutoScale(QwtPlot::yLeft, false);

  setAxisTitle(QwtPlot::xBottom, "Time [s]");
  setAxisScale(QwtPlot::xBottom, 0, 10);
  setAxisScale(QwtPlot::yLeft, -4.0, 4.0);

  setAxisScaleDraw(QwtPlot::xBottom, new MyScaleDraw);

  initBackground();

  QwtPlotZoomer* zoomer = new MyZoomer(this);
  zoomer->setMousePattern(QwtEventPattern::QwtEventPattern::MouseSelect1,
      Qt::LeftButton, Qt::ShiftModifier);
  // disable MouseSelect3 action of the zoomer
  zoomer->setMousePattern(QwtEventPattern::MouseSelect3, 0);

  MyPanner *panner = new MyPanner(this);

  // zoom in/out with the wheel
  mMagnifier = new MyMagnifier(this);
  mMagnifier->setMouseButton(Qt::MiddleButton);

  const QColor c(Qt::darkBlue);
  zoomer->setRubberBandPen(c);
  zoomer->setTrackerPen(c);

  this->setMinimumHeight(200);
}

Plot::~Plot()
{

}

void Plot::addSignal(SignalData* signalData, QColor color)
{
  QwtPlotCurve* d_curve = new QwtPlotCurve();
  d_curve->setStyle(DEFAULT_CURVE_STYLE);

  QPen curvePen(color);
  curvePen.setWidth(0);

  d_curve->setPen(curvePen);

#if 1
  d_curve->setRenderHint(QwtPlotItem::RenderAntialiased, true);
#endif
#if 1
  d_curve->setPaintAttribute(QwtPlotCurve::ClipPolygons, false);
#endif
  d_curve->setData(new CurveData(signalData));
  d_curve->attach(this);

  mSignals[signalData] = d_curve;
}

void Plot::removeSignal(SignalData* signalData)
{
  if (!signalData)
  {
    return;
  }

  QwtPlotCurve* curve = mSignals.value(signalData);
  assert(curve);
  curve->detach();
  delete curve;
  mSignals.remove(signalData);
}

void Plot::setSignalVisible(SignalData* signalData, bool visible)
{
  if (!signalData)
  {
    return;
  }

  QwtPlotCurve* curve = mSignals.value(signalData);
  assert(curve);

  if (visible)
  {
    curve->attach(this);
  }
  else
  {
    curve->detach();
  }

  this->replot();
}

void Plot::setSignalColor(SignalData* signalData, QColor color)
{
  if (!signalData)
  {
    return;
  }

  QwtPlotCurve* curve = mSignals.value(signalData);
  assert(curve);
  curve->setPen(QPen(color));
}

void Plot::setPointSize(double pointSize)
{
  foreach (QwtPlotCurve* curve, mSignals.values())
  {
    QPen curvePen = curve->pen();
    curvePen.setWidth(pointSize);
    curve->setPen(curvePen);
  }
}

void Plot::setCurveStyle(QwtPlotCurve::CurveStyle style)
{
  foreach (QwtPlotCurve* curve, mSignals.values())
  {
    curve->setStyle(style);
  }
}

void Plot::setBackgroundColor(QString color)
{
  if (color == "White")
  {
    mColorMode = 0;
  }
  else if (color == "Black")
  {
    mColorMode = 1;
  }

  this->initBackground();
}

void Plot::initBackground()
{
  if (!d_grid)
  {
    d_grid = new QwtPlotGrid();
    d_grid->enableX(false);
    d_grid->enableXMin(false);
    d_grid->enableY(true);
    d_grid->enableYMin(false);
    d_grid->attach(this);
  }

  if (!d_origin)
  {
    d_origin = new QwtPlotMarker();
    d_origin->setLineStyle(QwtPlotMarker::HLine);
    d_origin->setValue(0.0, 0.0);
    d_origin->attach(this);
  }

  QColor backgroundColor = Qt::white;
  QColor gridColor = Qt::gray;
  if (mColorMode == 1)
  {
    backgroundColor = Qt::black;
    gridColor = QColor(100, 100, 100);
  }

  QPalette pal = canvas()->palette();
  pal.setBrush(QPalette::Window, QBrush(backgroundColor));
  canvas()->setPalette(pal);

  d_grid->setPen(QPen(gridColor, 0.0, Qt::DotLine));
  d_origin->setLinePen(QPen(gridColor, 0.0, Qt::DashLine));
}

void Plot::start()
{
  mStopped = false;
  mMagnifier->setAxisEnabled(QwtPlot::xBottom, mStopped);
}

void Plot::stop()
{
  mStopped = true;
  mMagnifier->setAxisEnabled(QwtPlot::xBottom, mStopped);
}

bool Plot::isStopped()
{
  return mStopped;
}

void Plot::replot()
{
  this->updateTicks();

  // Lock the signal data objects, then plot the data and unlock.
  QList<SignalData*> signalDataList = mSignals.keys();
  foreach (SignalData* signalData, signalDataList)
  {
    signalData->lock();
  }

  QwtPlot::replot();

  foreach (SignalData* signalData, signalDataList)
  {
    signalData->unlock();
  }
}

void Plot::flagAxisSyncRequired()
{
  mAxisSyncRequired = true;
}

double Plot::timeWindow()
{
  return mTimeWindow;
}

void Plot::setTimeWindow(double interval)
{
  if ( interval > 0.0 && interval != mTimeWindow)
  {
    mTimeWindow = interval;
    QwtInterval xinterval = this->axisInterval(QwtPlot::xBottom);
    xinterval.setMinValue(xinterval.maxValue() - interval);
    this->setAxisScale(QwtPlot::xBottom, xinterval.minValue(), xinterval.maxValue());
    this->replot();
  }
}

void Plot::setYScale(double scale)
{
  setAxisScale(QwtPlot::yLeft, -scale, scale);
  this->replot();
}

void Plot::setEndTime(double endTime)
{
  QwtInterval xinterval = this->axisInterval(QwtPlot::xBottom);
  if (xinterval.maxValue() == endTime)
  {
    return;
  }

  xinterval = QwtInterval(endTime - xinterval.width(), endTime);
  this->setAxisScale(QwtPlot::xBottom, xinterval.minValue(), xinterval.maxValue());
}

void Plot::updateTicks()
{
  this->updateAxes();
  QwtInterval xinterval = this->axisInterval(QwtPlot::xBottom);

  // when playing, always use the requested time window
  // when paused, the user may zoom in/out, changing the time window
  if (!this->isStopped())
  {
    xinterval.setMinValue(xinterval.maxValue() - mTimeWindow);
  }

  QwtScaleEngine* engine = axisScaleEngine(QwtPlot::xBottom);
  QwtScaleDiv scaleDiv = engine->divideScale(xinterval.minValue(), xinterval.maxValue(), 0, 0);

  QList<double> majorTicks;
  double majorStep = scaleDiv.range() / 5.0;
  for (int i = 0; i <= 5; ++i)
  {
    majorTicks << scaleDiv.lowerBound() + i*majorStep;
  }
  majorTicks.back() = scaleDiv.upperBound();

  QList<double> minorTicks;
  double minorStep = scaleDiv.range() / 25.0;
  for (int i = 0; i <= 25; ++i)
  {
    minorTicks << scaleDiv.lowerBound() + i*minorStep;
  }
  minorTicks.back() = scaleDiv.upperBound();

  scaleDiv.setTicks(QwtScaleDiv::MajorTick, majorTicks);
  scaleDiv.setTicks(QwtScaleDiv::MinorTick, minorTicks);
  setAxisScaleDiv(QwtPlot::xBottom, scaleDiv);

  if (mAxisSyncRequired)
  {
    emit this->syncXAxisScale(xinterval.minValue(), xinterval.maxValue());
    mAxisSyncRequired = false;
  }
}
<commit_msg>call replot after changing point size or curve style<commit_after>#include "plot.h"
#include "curvedata.h"
#include "signaldata.h"
#include <qwt_plot_grid.h>
#include <qwt_plot_layout.h>
#include <qwt_plot_canvas.h>
#include <qwt_plot_marker.h>
#include <qwt_plot_curve.h>
#include <qwt_plot_directpainter.h>
#include <qwt_curve_fitter.h>
#include <qwt_painter.h>
#include <qwt_scale_engine.h>
#include <qwt_scale_draw.h>
#include <qwt_plot_zoomer.h>
#include <qwt_plot_panner.h>
#include <qwt_plot_magnifier.h>
#include <qwt_text.h>

#include <qevent.h>

#include <cassert>

#define DEFAULT_CURVE_STYLE QwtPlotCurve::Dots

class MyPanner : public QObject
{
public:

  QPoint mInitialPos;
  bool mEnabled;
  int mMouseButton;
  int mKeyboardButton;
  Plot* mPlot;

  MyPanner(Plot* plot) : QObject(plot)
  {
    mEnabled = false;
    mMouseButton = Qt::LeftButton;
    mKeyboardButton = Qt::NoButton;
    mPlot = plot;
    mPlot->canvas()->installEventFilter(this);
  }

  bool eventFilter( QObject *object, QEvent *event )
  {
    switch ( event->type() )
    {
      case QEvent::MouseButtonPress:
      {
        widgetMousePressEvent( ( QMouseEvent * )event );
        break;
      }
      case QEvent::MouseMove:
      {
        widgetMouseMoveEvent( ( QMouseEvent * )event );
        break;
      }
      case QEvent::MouseButtonRelease:
      {
        widgetMouseReleaseEvent( ( QMouseEvent * )event );
        break;
      }
    }

    return false;
  }

  void moveCanvas( int dx, int dy )
  {
    if ( dx == 0 && dy == 0 )
        return;

    if (!mPlot->isStopped())
      dx = 0;

    for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ )
    {
      const QwtScaleMap map = mPlot->canvasMap( axis );
      const double p1 = map.transform( mPlot->axisScaleDiv( axis )->lowerBound() );
      const double p2 = map.transform( mPlot->axisScaleDiv( axis )->upperBound() );

      double d1, d2;
      if ( axis == QwtPlot::xBottom || axis == QwtPlot::xTop )
      {
        d1 = map.invTransform( p1 - dx );
        d2 = map.invTransform( p2 - dx );
      }
      else
      {
        d1 = map.invTransform( p1 - dy );
        d2 = map.invTransform( p2 - dy );
      }
      mPlot->setAxisScale( axis, d1, d2 );
    }

    mPlot->flagAxisSyncRequired();
    mPlot->replot();
  }

  void widgetMousePressEvent( QMouseEvent *mouseEvent )
  {
    if (mouseEvent->button() != mMouseButton)
    {
      return;
    }

    if ((mouseEvent->modifiers() & Qt::KeyboardModifierMask) != (mKeyboardButton & Qt::KeyboardModifierMask))
    {
      return;
    }

    mInitialPos = mouseEvent->pos();
    mEnabled = true;
  }

  void widgetMouseMoveEvent( QMouseEvent *mouseEvent )
  {
    if (!mEnabled)
      return;

    QPoint pos = mouseEvent->pos();
    if (pos != mInitialPos)
    {
      moveCanvas(pos.x() - mInitialPos.x(), pos.y() - mInitialPos.y());
      mInitialPos = mouseEvent->pos();
    }
  }

  void widgetMouseReleaseEvent( QMouseEvent *mouseEvent )
  {
    mEnabled = false;
  }

};

class MyScaleDraw : public QwtScaleDraw
{
  virtual QwtText label(double value) const
  {
    return QString::number(value, 'f', 2);
  }
};

class MyZoomer: public QwtPlotZoomer
{
public:

    Plot* mPlot;

    MyZoomer(Plot *plot) : QwtPlotZoomer(plot->canvas())
    {
      mPlot = plot;
      setTrackerMode(AlwaysOn);
    }

    virtual QwtText trackerTextF(const QPointF &pos) const
    {
      QColor bg(Qt::white);
      bg.setAlpha(200);

      QwtText text = QwtPlotZoomer::trackerTextF(pos);
      text.setBackgroundBrush( QBrush( bg ));
      return text;
    }

protected:

    virtual void rescale()
    {
      mPlot->flagAxisSyncRequired();
      QwtPlotZoomer::rescale();
    }

};

class MyMagnifier: public QwtPlotMagnifier
{
public:
    Plot* mPlot;

    MyMagnifier(Plot* plot) : QwtPlotMagnifier(plot->canvas())
    {
      mPlot = plot;
    }

protected:

    // Normally, a value < 1.0 zooms in, a value > 1.0 zooms out.
    // This function is overloaded to invert the magnification direction.
    virtual void rescale( double factor )
    {
      factor = qAbs( factor );
      factor = (1-factor) + 1;

      mPlot->flagAxisSyncRequired();
      this->QwtPlotMagnifier::rescale(factor);
    }

};

Plot::Plot(QWidget *parent):
  QwtPlot(parent),
  d_origin(0),
  d_grid(0),
  mStopped(true),
  mAxisSyncRequired(false),
  mColorMode(0),
  mTimeWindow(10.0)
{
  setAutoReplot(false);

  // The backing store is important, when working with widget
  // overlays ( f.e rubberbands for zooming ).
  // Here we don't have them and the internal
  // backing store of QWidget is good enough.

  canvas()->setPaintAttribute(QwtPlotCanvas::BackingStore, false);

#if defined(Q_WS_X11)
  // Even if not recommended by TrollTech, Qt::WA_PaintOutsidePaintEvent
  // works on X11. This has a nice effect on the performance.

  canvas()->setAttribute(Qt::WA_PaintOutsidePaintEvent, true);

  // Disabling the backing store of Qt improves the performance
  // for the direct painter even more, but the canvas becomes
  // a native window of the window system, receiving paint events
  // for resize and expose operations. Those might be expensive
  // when there are many points and the backing store of
  // the canvas is disabled. So in this application
  // we better don't both backing stores.

  if ( canvas()->testPaintAttribute( QwtPlotCanvas::BackingStore ) )
  {
    canvas()->setAttribute(Qt::WA_PaintOnScreen, true);
    canvas()->setAttribute(Qt::WA_NoSystemBackground, true);
  }

#endif

  plotLayout()->setAlignCanvasToScales(true);

  setAxisAutoScale(QwtPlot::xBottom, false);
  setAxisAutoScale(QwtPlot::yLeft, false);

  setAxisTitle(QwtPlot::xBottom, "Time [s]");
  setAxisScale(QwtPlot::xBottom, 0, 10);
  setAxisScale(QwtPlot::yLeft, -4.0, 4.0);

  setAxisScaleDraw(QwtPlot::xBottom, new MyScaleDraw);

  initBackground();

  QwtPlotZoomer* zoomer = new MyZoomer(this);
  zoomer->setMousePattern(QwtEventPattern::QwtEventPattern::MouseSelect1,
      Qt::LeftButton, Qt::ShiftModifier);
  // disable MouseSelect3 action of the zoomer
  zoomer->setMousePattern(QwtEventPattern::MouseSelect3, 0);

  MyPanner *panner = new MyPanner(this);

  // zoom in/out with the wheel
  mMagnifier = new MyMagnifier(this);
  mMagnifier->setMouseButton(Qt::MiddleButton);

  const QColor c(Qt::darkBlue);
  zoomer->setRubberBandPen(c);
  zoomer->setTrackerPen(c);

  this->setMinimumHeight(200);
}

Plot::~Plot()
{

}

void Plot::addSignal(SignalData* signalData, QColor color)
{
  QwtPlotCurve* d_curve = new QwtPlotCurve();
  d_curve->setStyle(DEFAULT_CURVE_STYLE);

  QPen curvePen(color);
  curvePen.setWidth(0);

  d_curve->setPen(curvePen);

#if 1
  d_curve->setRenderHint(QwtPlotItem::RenderAntialiased, true);
#endif
#if 1
  d_curve->setPaintAttribute(QwtPlotCurve::ClipPolygons, false);
#endif
  d_curve->setData(new CurveData(signalData));
  d_curve->attach(this);

  mSignals[signalData] = d_curve;
}

void Plot::removeSignal(SignalData* signalData)
{
  if (!signalData)
  {
    return;
  }

  QwtPlotCurve* curve = mSignals.value(signalData);
  assert(curve);
  curve->detach();
  delete curve;
  mSignals.remove(signalData);
}

void Plot::setSignalVisible(SignalData* signalData, bool visible)
{
  if (!signalData)
  {
    return;
  }

  QwtPlotCurve* curve = mSignals.value(signalData);
  assert(curve);

  if (visible)
  {
    curve->attach(this);
  }
  else
  {
    curve->detach();
  }

  this->replot();
}

void Plot::setSignalColor(SignalData* signalData, QColor color)
{
  if (!signalData)
  {
    return;
  }

  QwtPlotCurve* curve = mSignals.value(signalData);
  assert(curve);
  curve->setPen(QPen(color));
}

void Plot::setPointSize(double pointSize)
{
  foreach (QwtPlotCurve* curve, mSignals.values())
  {
    QPen curvePen = curve->pen();
    curvePen.setWidth(pointSize);
    curve->setPen(curvePen);
  }
  this->replot();
}

void Plot::setCurveStyle(QwtPlotCurve::CurveStyle style)
{
  foreach (QwtPlotCurve* curve, mSignals.values())
  {
    curve->setStyle(style);
  }
  this->replot();
}

void Plot::setBackgroundColor(QString color)
{
  if (color == "White")
  {
    mColorMode = 0;
  }
  else if (color == "Black")
  {
    mColorMode = 1;
  }

  this->initBackground();
}

void Plot::initBackground()
{
  if (!d_grid)
  {
    d_grid = new QwtPlotGrid();
    d_grid->enableX(false);
    d_grid->enableXMin(false);
    d_grid->enableY(true);
    d_grid->enableYMin(false);
    d_grid->attach(this);
  }

  if (!d_origin)
  {
    d_origin = new QwtPlotMarker();
    d_origin->setLineStyle(QwtPlotMarker::HLine);
    d_origin->setValue(0.0, 0.0);
    d_origin->attach(this);
  }

  QColor backgroundColor = Qt::white;
  QColor gridColor = Qt::gray;
  if (mColorMode == 1)
  {
    backgroundColor = Qt::black;
    gridColor = QColor(100, 100, 100);
  }

  QPalette pal = canvas()->palette();
  pal.setBrush(QPalette::Window, QBrush(backgroundColor));
  canvas()->setPalette(pal);

  d_grid->setPen(QPen(gridColor, 0.0, Qt::DotLine));
  d_origin->setLinePen(QPen(gridColor, 0.0, Qt::DashLine));
}

void Plot::start()
{
  mStopped = false;
  mMagnifier->setAxisEnabled(QwtPlot::xBottom, mStopped);
}

void Plot::stop()
{
  mStopped = true;
  mMagnifier->setAxisEnabled(QwtPlot::xBottom, mStopped);
}

bool Plot::isStopped()
{
  return mStopped;
}

void Plot::replot()
{
  this->updateTicks();

  // Lock the signal data objects, then plot the data and unlock.
  QList<SignalData*> signalDataList = mSignals.keys();
  foreach (SignalData* signalData, signalDataList)
  {
    signalData->lock();
  }

  QwtPlot::replot();

  foreach (SignalData* signalData, signalDataList)
  {
    signalData->unlock();
  }
}

void Plot::flagAxisSyncRequired()
{
  mAxisSyncRequired = true;
}

double Plot::timeWindow()
{
  return mTimeWindow;
}

void Plot::setTimeWindow(double interval)
{
  if ( interval > 0.0 && interval != mTimeWindow)
  {
    mTimeWindow = interval;
    QwtInterval xinterval = this->axisInterval(QwtPlot::xBottom);
    xinterval.setMinValue(xinterval.maxValue() - interval);
    this->setAxisScale(QwtPlot::xBottom, xinterval.minValue(), xinterval.maxValue());
    this->replot();
  }
}

void Plot::setYScale(double scale)
{
  setAxisScale(QwtPlot::yLeft, -scale, scale);
  this->replot();
}

void Plot::setEndTime(double endTime)
{
  QwtInterval xinterval = this->axisInterval(QwtPlot::xBottom);
  if (xinterval.maxValue() == endTime)
  {
    return;
  }

  xinterval = QwtInterval(endTime - xinterval.width(), endTime);
  this->setAxisScale(QwtPlot::xBottom, xinterval.minValue(), xinterval.maxValue());
}

void Plot::updateTicks()
{
  this->updateAxes();
  QwtInterval xinterval = this->axisInterval(QwtPlot::xBottom);

  // when playing, always use the requested time window
  // when paused, the user may zoom in/out, changing the time window
  if (!this->isStopped())
  {
    xinterval.setMinValue(xinterval.maxValue() - mTimeWindow);
  }

  QwtScaleEngine* engine = axisScaleEngine(QwtPlot::xBottom);
  QwtScaleDiv scaleDiv = engine->divideScale(xinterval.minValue(), xinterval.maxValue(), 0, 0);

  QList<double> majorTicks;
  double majorStep = scaleDiv.range() / 5.0;
  for (int i = 0; i <= 5; ++i)
  {
    majorTicks << scaleDiv.lowerBound() + i*majorStep;
  }
  majorTicks.back() = scaleDiv.upperBound();

  QList<double> minorTicks;
  double minorStep = scaleDiv.range() / 25.0;
  for (int i = 0; i <= 25; ++i)
  {
    minorTicks << scaleDiv.lowerBound() + i*minorStep;
  }
  minorTicks.back() = scaleDiv.upperBound();

  scaleDiv.setTicks(QwtScaleDiv::MajorTick, majorTicks);
  scaleDiv.setTicks(QwtScaleDiv::MinorTick, minorTicks);
  setAxisScaleDiv(QwtPlot::xBottom, scaleDiv);

  if (mAxisSyncRequired)
  {
    emit this->syncXAxisScale(xinterval.minValue(), xinterval.maxValue());
    mAxisSyncRequired = false;
  }
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2012-2013 ARM Limited
 * All rights reserved
 *
 * The license below extends only to copyright in the software and shall
 * not be construed as granting a license to any other intellectual
 * property including but not limited to intellectual property relating
 * to a hardware implementation of the functionality of the software
 * licensed hereunder.  You may use the software subject to the license
 * terms below provided that you ensure that this notice is replicated
 * unmodified and in its entirety in all distributions of the software,
 * modified or unmodified, in source code or in binary form.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Andreas Hansson
 */

/**
 * @file
 * ClockedObject declaration and implementation.
 */

#ifndef __SIM_CLOCKED_OBJECT_HH__
#define __SIM_CLOCKED_OBJECT_HH__

#include "base/intmath.hh"
#include "base/misc.hh"
#include "params/ClockedObject.hh"
#include "sim/core.hh"
#include "sim/clock_domain.hh"
#include "sim/sim_object.hh"

/**
 * The ClockedObject class extends the SimObject with a clock and
 * accessor functions to relate ticks to the cycles of the object.
 */
class ClockedObject : public SimObject
{

  private:

    // the tick value of the next clock edge (>= curTick()) at the
    // time of the last call to update()
    mutable Tick tick;

    // The cycle counter value corresponding to the current value of
    // 'tick'
    mutable Cycles cycle;

    /**
     * Prevent inadvertent use of the copy constructor and assignment
     * operator by making them private.
     */
    ClockedObject(ClockedObject&);
    ClockedObject& operator=(ClockedObject&);

    /**
     *  Align cycle and tick to the next clock edge if not already done.
     */
    void update() const
    {
        // both tick and cycle are up-to-date and we are done, note
        // that the >= is important as it captures cases where tick
        // has already passed curTick()
        if (tick >= curTick())
            return;

        // optimise for the common case and see if the tick should be
        // advanced by a single clock period
        tick += clockPeriod();
        ++cycle;

        // see if we are done at this point
        if (tick >= curTick())
            return;

        // if not, we have to recalculate the cycle and tick, we
        // perform the calculations in terms of relative cycles to
        // allow changes to the clock period in the future
        Cycles elapsedCycles(divCeil(curTick() - tick, clockPeriod()));
        cycle += elapsedCycles;
        tick += elapsedCycles * clockPeriod();
    }

    /**
     * The clock domain this clocked object belongs to
     */
    ClockDomain &clockDomain;

  protected:

    /**
     * Create a clocked object and set the clock domain based on the
     * parameters.
     */
    ClockedObject(const ClockedObjectParams* p) :
        SimObject(p), tick(0), cycle(0), clockDomain(*p->clk_domain)
    {
    }

    /**
     * Virtual destructor due to inheritance.
     */
    virtual ~ClockedObject() { }

    /**
     * Reset the object's clock using the current global tick value. Likely
     * to be used only when the global clock is reset. Currently, this done
     * only when Ruby is done warming up the memory system.
     */
    void resetClock() const
    {
        Cycles elapsedCycles(divCeil(curTick(), clockPeriod()));
        cycle = elapsedCycles;
        tick = elapsedCycles * clockPeriod();
    }

  public:

    /**
     * Determine the tick when a cycle begins, by default the current
     * one, but the argument also enables the caller to determine a
     * future cycle.
     *
     * @param cycles The number of cycles into the future
     *
     * @return The tick when the clock edge occurs
     */
    inline Tick clockEdge(Cycles cycles = Cycles(0)) const
    {
        // align tick to the next clock edge
        update();

        // figure out when this future cycle is
        return tick + clockPeriod() * cycles;
    }

    /**
     * Determine the current cycle, corresponding to a tick aligned to
     * a clock edge.
     *
     * @return The current cycle count
     */
    inline Cycles curCycle() const
    {
        // align cycle to the next clock edge.
        update();

        return cycle;
    }

    /**
     * Based on the clock of the object, determine the tick when the next
     * cycle begins, in other words, return the next clock edge.
     * (This can never be the current tick.)
     *
     * @return The tick when the next cycle starts
     */
    Tick nextCycle() const
    { return clockEdge(Cycles(1)); }

    inline uint64_t frequency() const
    {
        return SimClock::Frequency / clockPeriod();
    }

    inline Tick clockPeriod() const
    {
        return clockDomain.clockPeriod();
    }

    inline Cycles ticksToCycles(Tick t) const
    { return Cycles(t / clockPeriod()); }

};

#endif //__SIM_CLOCKED_OBJECT_HH__
<commit_msg>sim: correct ticksToCycles() function.<commit_after>/*
 * Copyright (c) 2012-2013 ARM Limited
 * All rights reserved
 *
 * The license below extends only to copyright in the software and shall
 * not be construed as granting a license to any other intellectual
 * property including but not limited to intellectual property relating
 * to a hardware implementation of the functionality of the software
 * licensed hereunder.  You may use the software subject to the license
 * terms below provided that you ensure that this notice is replicated
 * unmodified and in its entirety in all distributions of the software,
 * modified or unmodified, in source code or in binary form.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Andreas Hansson
 */

/**
 * @file
 * ClockedObject declaration and implementation.
 */

#ifndef __SIM_CLOCKED_OBJECT_HH__
#define __SIM_CLOCKED_OBJECT_HH__

#include "base/intmath.hh"
#include "base/misc.hh"
#include "params/ClockedObject.hh"
#include "sim/core.hh"
#include "sim/clock_domain.hh"
#include "sim/sim_object.hh"

/**
 * The ClockedObject class extends the SimObject with a clock and
 * accessor functions to relate ticks to the cycles of the object.
 */
class ClockedObject : public SimObject
{

  private:

    // the tick value of the next clock edge (>= curTick()) at the
    // time of the last call to update()
    mutable Tick tick;

    // The cycle counter value corresponding to the current value of
    // 'tick'
    mutable Cycles cycle;

    /**
     * Prevent inadvertent use of the copy constructor and assignment
     * operator by making them private.
     */
    ClockedObject(ClockedObject&);
    ClockedObject& operator=(ClockedObject&);

    /**
     *  Align cycle and tick to the next clock edge if not already done.
     */
    void update() const
    {
        // both tick and cycle are up-to-date and we are done, note
        // that the >= is important as it captures cases where tick
        // has already passed curTick()
        if (tick >= curTick())
            return;

        // optimise for the common case and see if the tick should be
        // advanced by a single clock period
        tick += clockPeriod();
        ++cycle;

        // see if we are done at this point
        if (tick >= curTick())
            return;

        // if not, we have to recalculate the cycle and tick, we
        // perform the calculations in terms of relative cycles to
        // allow changes to the clock period in the future
        Cycles elapsedCycles(divCeil(curTick() - tick, clockPeriod()));
        cycle += elapsedCycles;
        tick += elapsedCycles * clockPeriod();
    }

    /**
     * The clock domain this clocked object belongs to
     */
    ClockDomain &clockDomain;

  protected:

    /**
     * Create a clocked object and set the clock domain based on the
     * parameters.
     */
    ClockedObject(const ClockedObjectParams* p) :
        SimObject(p), tick(0), cycle(0), clockDomain(*p->clk_domain)
    {
    }

    /**
     * Virtual destructor due to inheritance.
     */
    virtual ~ClockedObject() { }

    /**
     * Reset the object's clock using the current global tick value. Likely
     * to be used only when the global clock is reset. Currently, this done
     * only when Ruby is done warming up the memory system.
     */
    void resetClock() const
    {
        Cycles elapsedCycles(divCeil(curTick(), clockPeriod()));
        cycle = elapsedCycles;
        tick = elapsedCycles * clockPeriod();
    }

  public:

    /**
     * Determine the tick when a cycle begins, by default the current
     * one, but the argument also enables the caller to determine a
     * future cycle.
     *
     * @param cycles The number of cycles into the future
     *
     * @return The tick when the clock edge occurs
     */
    inline Tick clockEdge(Cycles cycles = Cycles(0)) const
    {
        // align tick to the next clock edge
        update();

        // figure out when this future cycle is
        return tick + clockPeriod() * cycles;
    }

    /**
     * Determine the current cycle, corresponding to a tick aligned to
     * a clock edge.
     *
     * @return The current cycle count
     */
    inline Cycles curCycle() const
    {
        // align cycle to the next clock edge.
        update();

        return cycle;
    }

    /**
     * Based on the clock of the object, determine the tick when the next
     * cycle begins, in other words, return the next clock edge.
     * (This can never be the current tick.)
     *
     * @return The tick when the next cycle starts
     */
    Tick nextCycle() const
    { return clockEdge(Cycles(1)); }

    inline uint64_t frequency() const
    {
        return SimClock::Frequency / clockPeriod();
    }

    inline Tick clockPeriod() const
    {
        return clockDomain.clockPeriod();
    }

    inline Cycles ticksToCycles(Tick t) const
    { return Cycles(divCeil(t, clockPeriod())); }

};

#endif //__SIM_CLOCKED_OBJECT_HH__
<|endoftext|>


<commit_before>
#include <cublas_v2.h>
#include <cuda_runtime.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <numeric>

using namespace std;

static void CheckCudaErrorAux(const char* file, unsigned line, const char* statement, cudaError_t err) {
    if (err == cudaSuccess) return;
    std::cerr << statement << " returned " << cudaGetErrorString(err) << "(" << err << ") at " << file << ":" << line << std::endl;
    exit(1);
}

#define CUDA_CHECK_RETURN(value) CheckCudaErrorAux(__FILE__, __LINE__, #value, value)

__global__ void calcZ_v4(const int *dimensions, const int dim_product, const int maxDataPerRow, const signed char *laplace_matrix, const float *p, float *z) {
    extern __shared__ int diagonalOffsets[];

    // Build diagonalOffsets on the first thread of each block and write it to shared memory
    if(threadIdx.x == 0) {
        const int diagonal = maxDataPerRow / 2;
        diagonalOffsets[diagonal] = 0;
        int factor = 1;

        for(int i = 0, offset = 1; i < diagonal; i++, offset++) {
            diagonalOffsets[diagonal - offset] = -factor;
            diagonalOffsets[diagonal + offset] = factor;
            factor *= dimensions[i];
        }
    }
    __syncthreads();

    const int row = blockIdx.x * blockDim.x + threadIdx.x;
    if (row < dim_product) {
        const int diagonal = row * maxDataPerRow;
        float tmp = 0;
        for(int i = diagonal; i < diagonal + maxDataPerRow; i++) {
            // when accessing out of bound memory in p, laplace_matrix[i] is always zero. So no illegal mem-access will be made.
            // If this causes problems add :
            // if(row + offsets[i - diagonalOffsets] >= 0 && row + offsets[i - diagonalOffsets] < dim_product)
            tmp += (signed char)laplace_matrix[i] * p[row + diagonalOffsets[i - diagonal]]; // No modulo here (as the general way in the thesis suggests)
        }
        z[row] = tmp;
    }
}

__global__ void checkResiduum(const int dim_product, const float* r, const float threshold, bool *threshold_reached) {
    for (int row = blockIdx.x * blockDim.x + threadIdx.x; row < dim_product; row += blockDim.x * gridDim.x) {
        if (r[row] >= threshold) {
          *threshold_reached = false;
          break;
        }
    }
}

__global__ void initVariablesWithGuess(const int dim_product, const float *divergence, float* A_times_x_0, float *p, float *r, bool *threshold_reached) {
    const int row = blockIdx.x * blockDim.x + threadIdx.x;
    if (row < dim_product) {
        float tmp = divergence[row] - A_times_x_0[row];
        p[row] = tmp;
        r[row] = tmp;

    }
    if(row == 0) *threshold_reached = false;
}

// global blas handle, initialize only once (warning - currently not free'd!)
bool           initBlasHandle = true;
cublasHandle_t blasHandle;

void LaunchPressureKernel(const int* dimensions, const int dim_product, const int dim_size,
                          const signed char *laplace_matrix,
                          float* p, float* z, float* r, float* divergence, float* x,
                          const float *oneVector,
                          bool* threshold_reached,
                          const float accuracy,
                          const int max_iterations,
                          const int batch_size,
                          int* iterations_gpu)
{
//       printf("Address of laplace_matrix is %p\n", (void *)laplace_matrix);
//       printf("Address of oneVector is %p\n", (void *)oneVector);
//       printf("Address of x is %p\n", (void *)x);
//       printf("Address of p is %p\n", (void *)p);
//       printf("Address of z is %p\n", (void *)z);
//       printf("Address of r is %p\n", (void *)r);
//       printf("Address of divergence is %p\n", (void *)divergence);

    if(initBlasHandle) {
        cublasCreate_v2(&blasHandle);
        cublasSetPointerMode_v2(blasHandle, CUBLAS_POINTER_MODE_HOST);
        initBlasHandle = false;
    }

    // CG helper variables variables init
    float *alpha = new float[batch_size], *beta = new float[batch_size];
    const float oneScalar = 1.0f;
    bool *threshold_reached_cpu = new bool[batch_size];
    float *p_r = new float[batch_size], *p_z = new float[batch_size], *r_z = new float[batch_size];

    // get block and gridSize to theoretically get best occupancy
    int blockSize;
    int minGridSize;
    int gridSize;

    // Initialize the helper variables
    cudaOccupancyMaxPotentialBlockSize( &minGridSize, &blockSize, calcZ_v4, 0, 0);
    gridSize = (dim_product + blockSize - 1) / blockSize;

    // First calc A * x_0, save result to z:
    for(int i = 0; i < batch_size; i++) {
        calcZ_v4<<<gridSize, blockSize, dim_size * 2 + 1>>>(dimensions,
                                                            dim_product,
                                                            dim_size * 2 + 1,
                                                            laplace_matrix,
                                                            x + i * dim_product,
                                                            z + i * dim_product);
    }
    CUDA_CHECK_RETURN(cudaDeviceSynchronize());

    cudaOccupancyMaxPotentialBlockSize( &minGridSize, &blockSize, initVariablesWithGuess, 0, 0);
    gridSize = (dim_product + blockSize - 1) / blockSize;

    // Second apply result to the helper variables
    for(int i = 0; i < batch_size; i++) {
        int offset = i * dim_product;
        initVariablesWithGuess<<<gridSize, blockSize>>>(dim_product,
                                                        divergence + offset,
                                                        z + offset,
                                                        p + offset,
                                                        r + offset,
                                                        threshold_reached + i);
    }
    CUDA_CHECK_RETURN(cudaDeviceSynchronize());

    // Init residuum checker variables
    CUDA_CHECK_RETURN(cudaMemcpy(threshold_reached_cpu, threshold_reached, sizeof(bool) * batch_size, cudaMemcpyDeviceToHost));
    CUDA_CHECK_RETURN(cudaDeviceSynchronize());

    cudaOccupancyMaxPotentialBlockSize( &minGridSize, &blockSize,
                              calcZ_v4, 0, 0);
    gridSize = (dim_product + blockSize - 1) / blockSize;

    // Do CG-Solve
    int checker = 1;
    int iterations = 0;
    for (; iterations < max_iterations; iterations++) {
        for(int i = 0; i < batch_size; i++) {
            if(threshold_reached_cpu[i]) continue;
            calcZ_v4<<<gridSize, blockSize, dim_size * 2 + 1>>>(dimensions, dim_product, dim_size * 2 + 1, laplace_matrix, p + i * dim_product, z + i * dim_product);
        }
        CUDA_CHECK_RETURN(cudaDeviceSynchronize());

        for(int i = 0; i < batch_size; i++) {
            if(threshold_reached_cpu[i]) continue;
            cublasSdot_v2(blasHandle, dim_product, p + i * dim_product, 1, r + i * dim_product, 1, p_r + i);
            cublasSdot_v2(blasHandle, dim_product, p + i * dim_product, 1, z + i * dim_product, 1, p_z + i);
        }
        CUDA_CHECK_RETURN(cudaDeviceSynchronize());

        for(int i = 0; i < batch_size; i++) {
            if(threshold_reached_cpu[i]) continue;
            alpha[i] = p_r[i] / p_z[i];
            cublasSaxpy_v2(blasHandle, dim_product, alpha + i, p + i * dim_product, 1, x + i * dim_product, 1);

            alpha[i] = -alpha[i];
            cublasSaxpy_v2(blasHandle, dim_product, alpha + i, z + i * dim_product, 1, r + i * dim_product, 1);

        }
        CUDA_CHECK_RETURN(cudaDeviceSynchronize());

        // Check the residuum every 5 steps to keep memcopys between H&D low
        // Tests have shown, that 5 is a good avg trade-of between memcopys and extra computation and increases the performance
        if (checker % 5 == 0) {
            for(int i = 0; i < batch_size; i++) {
                if(threshold_reached_cpu[i]) continue;
                // Use fewer occupancy here, because in most cases residual will be to high and therefore
                checkResiduum<<<8, blockSize>>>(dim_product, r + i * dim_product, accuracy, threshold_reached + i);
            }
            CUDA_CHECK_RETURN(cudaDeviceSynchronize());

            CUDA_CHECK_RETURN(cudaMemcpy(threshold_reached_cpu, threshold_reached, sizeof(bool) * batch_size, cudaMemcpyDeviceToHost));
            CUDA_CHECK_RETURN(cudaDeviceSynchronize());

            bool done = true;
            for(int i = 0; i < batch_size; i++) {
                if (!threshold_reached_cpu[i]) {
                    done = false;
                    break;
                }
            }
            if(done){
                iterations++;
                break;
            }
            CUDA_CHECK_RETURN(cudaMemset(threshold_reached, 1, sizeof(bool) * batch_size));
        }
        checker++;

        for(int i = 0; i < batch_size; i++) {
            if(threshold_reached_cpu[i]) continue;
            cublasSdot_v2(blasHandle, dim_product, r + i * dim_product, 1, z + i * dim_product, 1, r_z + i);
        }
        CUDA_CHECK_RETURN(cudaDeviceSynchronize());

        for(int i = 0; i < batch_size; i++) {
            if(threshold_reached_cpu[i]) continue;
            beta[i] = -r_z[i] / p_z[i];
            cublasSscal_v2(blasHandle, dim_product, beta + i, p + i * dim_product, 1);
            cublasSaxpy_v2(blasHandle, dim_product, &oneScalar, r + i * dim_product, 1, p + i * dim_product, 1);
        }
        CUDA_CHECK_RETURN(cudaDeviceSynchronize());
    }

    delete[] alpha, beta, threshold_reached_cpu, p_r, p_z, r_z;
//    printf("I: %i\n", iterations);

    CUDA_CHECK_RETURN(cudaMemcpy(iterations_gpu, &iterations, sizeof(int), cudaMemcpyHostToDevice));
    CUDA_CHECK_RETURN(cudaDeviceSynchronize());

}
<commit_msg>added safe divide for alpha calculation in CUDA CG solver<commit_after>
#include <cublas_v2.h>
#include <cuda_runtime.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <numeric>

using namespace std;

static void CheckCudaErrorAux(const char* file, unsigned line, const char* statement, cudaError_t err) {
    if (err == cudaSuccess) return;
    std::cerr << statement << " returned " << cudaGetErrorString(err) << "(" << err << ") at " << file << ":" << line << std::endl;
    exit(1);
}

#define CUDA_CHECK_RETURN(value) CheckCudaErrorAux(__FILE__, __LINE__, #value, value)

__global__ void calcZ_v4(const int *dimensions, const int dim_product, const int maxDataPerRow, const signed char *laplace_matrix, const float *p, float *z) {
    extern __shared__ int diagonalOffsets[];

    // Build diagonalOffsets on the first thread of each block and write it to shared memory
    if(threadIdx.x == 0) {
        const int diagonal = maxDataPerRow / 2;
        diagonalOffsets[diagonal] = 0;
        int factor = 1;

        for(int i = 0, offset = 1; i < diagonal; i++, offset++) {
            diagonalOffsets[diagonal - offset] = -factor;
            diagonalOffsets[diagonal + offset] = factor;
            factor *= dimensions[i];
        }
    }
    __syncthreads();

    const int row = blockIdx.x * blockDim.x + threadIdx.x;
    if (row < dim_product) {
        const int diagonal = row * maxDataPerRow;
        float tmp = 0;
        for(int i = diagonal; i < diagonal + maxDataPerRow; i++) {
            // when accessing out of bound memory in p, laplace_matrix[i] is always zero. So no illegal mem-access will be made.
            // If this causes problems add :
            // if(row + offsets[i - diagonalOffsets] >= 0 && row + offsets[i - diagonalOffsets] < dim_product)
            tmp += (signed char)laplace_matrix[i] * p[row + diagonalOffsets[i - diagonal]]; // No modulo here (as the general way in the thesis suggests)
        }
        z[row] = tmp;
    }
}

__global__ void checkResiduum(const int dim_product, const float* r, const float threshold, bool *threshold_reached) {
    for (int row = blockIdx.x * blockDim.x + threadIdx.x; row < dim_product; row += blockDim.x * gridDim.x) {
        if (r[row] >= threshold) {
          *threshold_reached = false;
          break;
        }
    }
}

__global__ void initVariablesWithGuess(const int dim_product, const float *divergence, float* A_times_x_0, float *p, float *r, bool *threshold_reached) {
    const int row = blockIdx.x * blockDim.x + threadIdx.x;
    if (row < dim_product) {
        float tmp = divergence[row] - A_times_x_0[row];
        p[row] = tmp;
        r[row] = tmp;

    }
    if(row == 0) *threshold_reached = false;
}

// global blas handle, initialize only once (warning - currently not free'd!)
bool           initBlasHandle = true;
cublasHandle_t blasHandle;

void LaunchPressureKernel(const int* dimensions, const int dim_product, const int dim_size,
                          const signed char *laplace_matrix,
                          float* p, float* z, float* r, float* divergence, float* x,
                          const float *oneVector,
                          bool* threshold_reached,
                          const float accuracy,
                          const int max_iterations,
                          const int batch_size,
                          int* iterations_gpu)
{
//       printf("Address of laplace_matrix is %p\n", (void *)laplace_matrix);
//       printf("Address of oneVector is %p\n", (void *)oneVector);
//       printf("Address of x is %p\n", (void *)x);
//       printf("Address of p is %p\n", (void *)p);
//       printf("Address of z is %p\n", (void *)z);
//       printf("Address of r is %p\n", (void *)r);
//       printf("Address of divergence is %p\n", (void *)divergence);

    if(initBlasHandle) {
        cublasCreate_v2(&blasHandle);
        cublasSetPointerMode_v2(blasHandle, CUBLAS_POINTER_MODE_HOST);
        initBlasHandle = false;
    }

    // CG helper variables variables init
    float *alpha = new float[batch_size], *beta = new float[batch_size];
    const float oneScalar = 1.0f;
    bool *threshold_reached_cpu = new bool[batch_size];
    float *p_r = new float[batch_size], *p_z = new float[batch_size], *r_z = new float[batch_size];

    // get block and gridSize to theoretically get best occupancy
    int blockSize;
    int minGridSize;
    int gridSize;

    // Initialize the helper variables
    cudaOccupancyMaxPotentialBlockSize( &minGridSize, &blockSize, calcZ_v4, 0, 0);
    gridSize = (dim_product + blockSize - 1) / blockSize;

    // First calc A * x_0, save result to z:
    for(int i = 0; i < batch_size; i++) {
        calcZ_v4<<<gridSize, blockSize, dim_size * 2 + 1>>>(dimensions,
                                                            dim_product,
                                                            dim_size * 2 + 1,
                                                            laplace_matrix,
                                                            x + i * dim_product,
                                                            z + i * dim_product);
    }
    CUDA_CHECK_RETURN(cudaDeviceSynchronize());

    cudaOccupancyMaxPotentialBlockSize( &minGridSize, &blockSize, initVariablesWithGuess, 0, 0);
    gridSize = (dim_product + blockSize - 1) / blockSize;

    // Second apply result to the helper variables
    for(int i = 0; i < batch_size; i++) {
        int offset = i * dim_product;
        initVariablesWithGuess<<<gridSize, blockSize>>>(dim_product,
                                                        divergence + offset,
                                                        z + offset,
                                                        p + offset,
                                                        r + offset,
                                                        threshold_reached + i);
    }
    CUDA_CHECK_RETURN(cudaDeviceSynchronize());

    // Init residuum checker variables
    CUDA_CHECK_RETURN(cudaMemcpy(threshold_reached_cpu, threshold_reached, sizeof(bool) * batch_size, cudaMemcpyDeviceToHost));
    CUDA_CHECK_RETURN(cudaDeviceSynchronize());

    cudaOccupancyMaxPotentialBlockSize( &minGridSize, &blockSize,
                              calcZ_v4, 0, 0);
    gridSize = (dim_product + blockSize - 1) / blockSize;

    // Do CG-Solve
    int checker = 1;
    int iterations = 0;
    for (; iterations < max_iterations; iterations++) {
        for(int i = 0; i < batch_size; i++) {
            if(threshold_reached_cpu[i]) continue;
            calcZ_v4<<<gridSize, blockSize, dim_size * 2 + 1>>>(dimensions, dim_product, dim_size * 2 + 1, laplace_matrix, p + i * dim_product, z + i * dim_product);
        }
        CUDA_CHECK_RETURN(cudaDeviceSynchronize());

        for(int i = 0; i < batch_size; i++) {
            if(threshold_reached_cpu[i]) continue;
            cublasSdot_v2(blasHandle, dim_product, p + i * dim_product, 1, r + i * dim_product, 1, p_r + i);
            cublasSdot_v2(blasHandle, dim_product, p + i * dim_product, 1, z + i * dim_product, 1, p_z + i);
        }
        CUDA_CHECK_RETURN(cudaDeviceSynchronize());

        for(int i = 0; i < batch_size; i++) {
            if(threshold_reached_cpu[i]) continue;
            alpha[i] = 0.;
            if(fabs(p_z[i])>0.) alpha[i] = p_r[i] / p_z[i];
            cublasSaxpy_v2(blasHandle, dim_product, alpha + i, p + i * dim_product, 1, x + i * dim_product, 1);

            alpha[i] = -alpha[i];
            cublasSaxpy_v2(blasHandle, dim_product, alpha + i, z + i * dim_product, 1, r + i * dim_product, 1);

        }
        CUDA_CHECK_RETURN(cudaDeviceSynchronize());

        // Check the residuum every 5 steps to keep memcopys between H&D low
        // Tests have shown, that 5 is a good avg trade-of between memcopys and extra computation and increases the performance
        if (checker % 5 == 0) {
            for(int i = 0; i < batch_size; i++) {
                if(threshold_reached_cpu[i]) continue;
                // Use fewer occupancy here, because in most cases residual will be to high and therefore
                checkResiduum<<<8, blockSize>>>(dim_product, r + i * dim_product, accuracy, threshold_reached + i);
            }
            CUDA_CHECK_RETURN(cudaDeviceSynchronize());

            CUDA_CHECK_RETURN(cudaMemcpy(threshold_reached_cpu, threshold_reached, sizeof(bool) * batch_size, cudaMemcpyDeviceToHost));
            CUDA_CHECK_RETURN(cudaDeviceSynchronize());

            bool done = true;
            for(int i = 0; i < batch_size; i++) {
                if (!threshold_reached_cpu[i]) {
                    done = false;
                    break;
                }
            }
            if(done){
                iterations++;
                break;
            }
            CUDA_CHECK_RETURN(cudaMemset(threshold_reached, 1, sizeof(bool) * batch_size));
        }
        checker++;

        for(int i = 0; i < batch_size; i++) {
            if(threshold_reached_cpu[i]) continue;
            cublasSdot_v2(blasHandle, dim_product, r + i * dim_product, 1, z + i * dim_product, 1, r_z + i);
        }
        CUDA_CHECK_RETURN(cudaDeviceSynchronize());

        for(int i = 0; i < batch_size; i++) {
            if(threshold_reached_cpu[i]) continue;
            beta[i] = -r_z[i] / p_z[i];
            cublasSscal_v2(blasHandle, dim_product, beta + i, p + i * dim_product, 1);
            cublasSaxpy_v2(blasHandle, dim_product, &oneScalar, r + i * dim_product, 1, p + i * dim_product, 1);
        }
        CUDA_CHECK_RETURN(cudaDeviceSynchronize());
    }

    delete[] alpha, beta, threshold_reached_cpu, p_r, p_z, r_z;
//    printf("I: %i\n", iterations);

    CUDA_CHECK_RETURN(cudaMemcpy(iterations_gpu, &iterations, sizeof(int), cudaMemcpyHostToDevice));
    CUDA_CHECK_RETURN(cudaDeviceSynchronize());

}
<|endoftext|>