<commit_before>
#include <string>
#include <vector>

#include "lex.h"

namespace hit
{

const std::string digits = "0123456789";
const std::string alpha = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
const std::string space = " \t";
const std::string allspace = " \t\n\r";
const std::string newline = "\n\r";
const std::string alphanumeric = digits + alpha;
const std::string identchars = alphanumeric + "_./:<>-+*";

_LexFunc::_LexFunc(LexFunc pp) : p(pp) {}
_LexFunc::operator LexFunc() { return p; }

bool
charIn(char c, const std::string & valid)
{
  return valid.find(c) != std::string::npos;
}

// the EOF macro wreaks havok on our TokType enum which has an EOF member. So undefine it and the
// redefine it at the end of the file.
#define TMPEOF EOF
#undef EOF

int
lineCount(const std::string & input)
{
  int n = 0;
  size_t pos = input.find("\n", 0); // first occurrence
  while (pos < std::string::npos)
  {
    n++;
    pos = input.find("\n", pos + 1);
  }
  return n;
}

std::string
tokTypeName(TokType t)
{
// clang-format off
  #define tokcase(type) case TokType::type: return #type;
  switch (t)
    {
      tokcase(Error)
      tokcase(Equals)
      tokcase(LeftBracket)
      tokcase(RightBracket)
      tokcase(Ident)
      tokcase(Path)
      tokcase(Number)
      tokcase(String)
      tokcase(Comment)
      tokcase(InlineComment)
      tokcase(BlankLine)
      tokcase(EOF)
      default : return std::to_string((int)t);
    }
  #undef tokcase
  // clang-format on
}

Token::Token(TokType t, const std::string & val, size_t offset, int line)
  : type(t), val(val), offset(offset), line(line)
{
}

std::string
Token::str()
{
  if (type == TokType::String || type == TokType::Error)
    return tokTypeName(type) + ":" + val;
  return tokTypeName(type) + ":'" + val + "'";
}

Lexer::Lexer(const std::string & name, const std::string & input) : _name(name), _input(input) {}

std::vector<Token> &
Lexer::tokens()
{
  return _tokens;
}

std::vector<Token>
Lexer::run(LexFunc start)
{
  LexFunc state = start;
  while (state != nullptr)
    state = state(this);
  return _tokens;
}

void
Lexer::rewind()
{
  if (!peek()) // don't do anything if we are at EOF
    return;

  auto tmp = lastToken();
  if (tmp >= _start)
    return;

  // subtract newlines that may have been ignored
  _line_count -= lineCount(_input.substr(tmp, _start - tmp));
  _pos = tmp;
  if (_pos < _start)
    _start = _pos;
}

void
Lexer::emit(TokType type)
{
  auto substr = _input.substr(_start, _pos - _start);
  _tokens.push_back(Token(type, substr, _start, _line_count));
  _line_count += lineCount(substr);
  _start = _pos;
}

size_t
Lexer::lastToken()
{
  return _tokens.back().offset + _tokens.back().val.size();
}

void
Lexer::ignore()
{
  auto substr = _input.substr(_start, _pos - _start);
  _line_count += lineCount(substr);
  _start = _pos;
}

LexFunc
Lexer::error(const std::string & msg)
{
  _tokens.push_back(Token(TokType::Error, msg, _start, _line_count));
  return nullptr;
}

char
Lexer::next()
{
  if (_pos >= _input.size())
  {
    _width = 0;
    return 0;
  }

  char c = _input[_pos];
  _width = 1;
  _pos += _width;
  return c;
}

bool
Lexer::accept(const std::string & valid)
{
  if (charIn(next(), valid))
    return true;
  backup();
  return false;
}

int
Lexer::acceptRun(const std::string & valid)
{
  int n = 0;
  while (true)
  {
    size_t index = valid.find(next());
    if (index == std::string::npos)
      break;
    n++;
  }
  backup();
  return n;
}

char
Lexer::peek()
{
  char c = next();
  backup();
  return c;
}

void
Lexer::backup()
{
  _pos = std::max(_start, _pos - _width);
}

std::string
Lexer::input()
{
  return _input;
}
size_t
Lexer::start()
{
  return _start;
}
size_t
Lexer::pos()
{
  return _pos;
}

_LexFunc lexHit(Lexer *);
_LexFunc lexNumber(Lexer *);
_LexFunc lexString(Lexer *);

_LexFunc
lexPath(Lexer * l)
{
  l->acceptRun(space);
  l->ignore();
  l->acceptRun(identchars);
  l->emit(TokType::Path);

  auto n = l->acceptRun(space);
  l->ignore();
  bool got_close = l->accept("]");
  if (n == 0 && !got_close)
    return l->error("invalid section path character '" + std::string(1, l->peek()) + "'");
  else if (n > 0 && !got_close)
    return l->error("spaces are not allowed in section paths");

  l->emit(TokType::RightBracket);
  return lexHit;
}

int
consumeWhitespace(Lexer * l)
{
  size_t start = 0;
  while (true)
  {
    start = l->pos();
    l->acceptRun(space);
    l->ignore();

    if (l->accept("\n"))
    {
      l->ignore();
      int n = 0;
      while (l->accept("\n"))
      {
        if (n == 0)
          l->emit(TokType::BlankLine);
        n++;
      }
    }
    if (l->pos() == start)
      break;
  }

  l->acceptRun(allspace);
  l->ignore();
  return l->pos() - start;
}

void
consumeToNewline(Lexer * l)
{
  while (true)
  {
    char c = l->next();
    if (c == '\0' || charIn(c, "\n\r"))
      break;
  }
  l->backup();
}

void
lexComments(Lexer * l)
{
  // The first comment in a file can't be an inline comment.
  if (l->start() > 0 && l->tokens().back().type != TokType::BlankLine)
  {
    l->acceptRun(space);
    l->ignore();
    if (l->accept("#"))
    {
      consumeToNewline(l);
      l->emit(TokType::InlineComment);
    }
  }

  while (true)
  {
    consumeWhitespace(l);
    if (!l->accept("#"))
      break;
    consumeToNewline(l);
    l->emit(TokType::Comment);
  }
}

_LexFunc
lexEq(Lexer * l)
{
  l->acceptRun(space);
  l->ignore();
  if (!l->accept("="))
    return l->error("expected '=' after parameter name '" + l->tokens().back().val + "', got '" +
                    std::string(1, l->next()) + "'");
  l->emit(TokType::Equals);

  l->acceptRun(allspace);
  l->ignore();

  // uncomment this to allow commentw between '=' and field value
  // lexComments(l);
  // l->acceptRun(allspace);
  // l->ignore();

  if (charIn(l->peek(), digits + "-+.eE"))
    return lexNumber;
  return lexString;
}

void
consumeUnquotedString(Lexer * l)
{
  while (true)
  {
    char c = l->next();
    // '#' is always a comment outside of quoted string
    if (c == '\0' || charIn(c, allspace + "[#"))
      break;
  }
  l->backup();
}

_LexFunc
lexString(Lexer * l)
{
  l->acceptRun(allspace);
  l->ignore();

  if (!charIn(l->peek(), "'\""))
  {
    consumeUnquotedString(l);
    l->emit(TokType::String);
    return lexHit;
  }

  std::string quote = "";
  if (l->peek() == '"')
    quote = "\"";
  else if (l->peek() == '\'')
    quote = "'";
  else
    return l->error("the parser is horribly broken");

  // this is a loop in order to enable consecutive string literals to be parsed
  while (l->accept(quote))
  {
    char c = l->input()[l->start()];
    char prev;
    while (true)
    {
      prev = c;
      c = l->next();
      if (c == quote[0] && prev != '\\')
        break;
      else if (c == '\0')
        return l->error("unterminated string");
    }
    l->emit(TokType::String);
    consumeWhitespace(l);
  }

  // unlex the last run of whitespace - we need to skip whitespace between consecutive string
  // literals, but not the final run of whitespace.  Carefully tracking/handling whitespace
  // is is important for determining how to place/format comment tokens.
  l->rewind();

  return lexHit;
}

_LexFunc
lexNumber(Lexer * l)
{
  l->accept("+-");
  int n = l->acceptRun(digits);
  if (l->accept("."))
    n += l->acceptRun(digits);

  if (l->accept("eE"))
  {
    n += l->accept("-+");
    n += l->acceptRun(digits);
  }
  if (n == 0)
  {
    // fall back to string
    consumeUnquotedString(l);
    l->emit(TokType::String);
    return lexHit;
  }

  if (!charIn(l->peek(), allspace + "[") && l->peek() != '\0')
  {
    // fall back to string
    consumeUnquotedString(l);
    l->emit(TokType::String);
    return lexHit;
  }

  l->emit(TokType::Number);
  return lexHit;
}

_LexFunc
lexHit(Lexer * l)
{
  lexComments(l);
  consumeWhitespace(l);
  char c = l->next();
  if (c == '[')
  {
    l->emit(TokType::LeftBracket);
    return lexPath;
  }
  else if (charIn(c, identchars))
  {
    l->acceptRun(identchars);
    l->emit(TokType::Ident);
    return lexEq;
  }
  else if (c == '\0')
  {
    l->emit(TokType::EOF);
    return NULL;
  }
  return l->error("invalid character '" + std::string(1, c) + "'");
}

} // namespace hit

#define EOF TMPEOF
<commit_msg>missing include for hit bindings<commit_after>
#include <string>
#include <vector>
#include <algorithm>

#include "lex.h"

namespace hit
{

const std::string digits = "0123456789";
const std::string alpha = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
const std::string space = " \t";
const std::string allspace = " \t\n\r";
const std::string newline = "\n\r";
const std::string alphanumeric = digits + alpha;
const std::string identchars = alphanumeric + "_./:<>-+*";

_LexFunc::_LexFunc(LexFunc pp) : p(pp) {}
_LexFunc::operator LexFunc() { return p; }

bool
charIn(char c, const std::string & valid)
{
  return valid.find(c) != std::string::npos;
}

// the EOF macro wreaks havok on our TokType enum which has an EOF member. So undefine it and the
// redefine it at the end of the file.
#define TMPEOF EOF
#undef EOF

int
lineCount(const std::string & input)
{
  int n = 0;
  size_t pos = input.find("\n", 0); // first occurrence
  while (pos < std::string::npos)
  {
    n++;
    pos = input.find("\n", pos + 1);
  }
  return n;
}

std::string
tokTypeName(TokType t)
{
// clang-format off
  #define tokcase(type) case TokType::type: return #type;
  switch (t)
    {
      tokcase(Error)
      tokcase(Equals)
      tokcase(LeftBracket)
      tokcase(RightBracket)
      tokcase(Ident)
      tokcase(Path)
      tokcase(Number)
      tokcase(String)
      tokcase(Comment)
      tokcase(InlineComment)
      tokcase(BlankLine)
      tokcase(EOF)
      default : return std::to_string((int)t);
    }
  #undef tokcase
  // clang-format on
}

Token::Token(TokType t, const std::string & val, size_t offset, int line)
  : type(t), val(val), offset(offset), line(line)
{
}

std::string
Token::str()
{
  if (type == TokType::String || type == TokType::Error)
    return tokTypeName(type) + ":" + val;
  return tokTypeName(type) + ":'" + val + "'";
}

Lexer::Lexer(const std::string & name, const std::string & input) : _name(name), _input(input) {}

std::vector<Token> &
Lexer::tokens()
{
  return _tokens;
}

std::vector<Token>
Lexer::run(LexFunc start)
{
  LexFunc state = start;
  while (state != nullptr)
    state = state(this);
  return _tokens;
}

void
Lexer::rewind()
{
  if (!peek()) // don't do anything if we are at EOF
    return;

  auto tmp = lastToken();
  if (tmp >= _start)
    return;

  // subtract newlines that may have been ignored
  _line_count -= lineCount(_input.substr(tmp, _start - tmp));
  _pos = tmp;
  if (_pos < _start)
    _start = _pos;
}

void
Lexer::emit(TokType type)
{
  auto substr = _input.substr(_start, _pos - _start);
  _tokens.push_back(Token(type, substr, _start, _line_count));
  _line_count += lineCount(substr);
  _start = _pos;
}

size_t
Lexer::lastToken()
{
  return _tokens.back().offset + _tokens.back().val.size();
}

void
Lexer::ignore()
{
  auto substr = _input.substr(_start, _pos - _start);
  _line_count += lineCount(substr);
  _start = _pos;
}

LexFunc
Lexer::error(const std::string & msg)
{
  _tokens.push_back(Token(TokType::Error, msg, _start, _line_count));
  return nullptr;
}

char
Lexer::next()
{
  if (_pos >= _input.size())
  {
    _width = 0;
    return 0;
  }

  char c = _input[_pos];
  _width = 1;
  _pos += _width;
  return c;
}

bool
Lexer::accept(const std::string & valid)
{
  if (charIn(next(), valid))
    return true;
  backup();
  return false;
}

int
Lexer::acceptRun(const std::string & valid)
{
  int n = 0;
  while (true)
  {
    size_t index = valid.find(next());
    if (index == std::string::npos)
      break;
    n++;
  }
  backup();
  return n;
}

char
Lexer::peek()
{
  char c = next();
  backup();
  return c;
}

void
Lexer::backup()
{
  _pos = std::max(_start, _pos - _width);
}

std::string
Lexer::input()
{
  return _input;
}
size_t
Lexer::start()
{
  return _start;
}
size_t
Lexer::pos()
{
  return _pos;
}

_LexFunc lexHit(Lexer *);
_LexFunc lexNumber(Lexer *);
_LexFunc lexString(Lexer *);

_LexFunc
lexPath(Lexer * l)
{
  l->acceptRun(space);
  l->ignore();
  l->acceptRun(identchars);
  l->emit(TokType::Path);

  auto n = l->acceptRun(space);
  l->ignore();
  bool got_close = l->accept("]");
  if (n == 0 && !got_close)
    return l->error("invalid section path character '" + std::string(1, l->peek()) + "'");
  else if (n > 0 && !got_close)
    return l->error("spaces are not allowed in section paths");

  l->emit(TokType::RightBracket);
  return lexHit;
}

int
consumeWhitespace(Lexer * l)
{
  size_t start = 0;
  while (true)
  {
    start = l->pos();
    l->acceptRun(space);
    l->ignore();

    if (l->accept("\n"))
    {
      l->ignore();
      int n = 0;
      while (l->accept("\n"))
      {
        if (n == 0)
          l->emit(TokType::BlankLine);
        n++;
      }
    }
    if (l->pos() == start)
      break;
  }

  l->acceptRun(allspace);
  l->ignore();
  return l->pos() - start;
}

void
consumeToNewline(Lexer * l)
{
  while (true)
  {
    char c = l->next();
    if (c == '\0' || charIn(c, "\n\r"))
      break;
  }
  l->backup();
}

void
lexComments(Lexer * l)
{
  // The first comment in a file can't be an inline comment.
  if (l->start() > 0 && l->tokens().back().type != TokType::BlankLine)
  {
    l->acceptRun(space);
    l->ignore();
    if (l->accept("#"))
    {
      consumeToNewline(l);
      l->emit(TokType::InlineComment);
    }
  }

  while (true)
  {
    consumeWhitespace(l);
    if (!l->accept("#"))
      break;
    consumeToNewline(l);
    l->emit(TokType::Comment);
  }
}

_LexFunc
lexEq(Lexer * l)
{
  l->acceptRun(space);
  l->ignore();
  if (!l->accept("="))
    return l->error("expected '=' after parameter name '" + l->tokens().back().val + "', got '" +
                    std::string(1, l->next()) + "'");
  l->emit(TokType::Equals);

  l->acceptRun(allspace);
  l->ignore();

  // uncomment this to allow commentw between '=' and field value
  // lexComments(l);
  // l->acceptRun(allspace);
  // l->ignore();

  if (charIn(l->peek(), digits + "-+.eE"))
    return lexNumber;
  return lexString;
}

void
consumeUnquotedString(Lexer * l)
{
  while (true)
  {
    char c = l->next();
    // '#' is always a comment outside of quoted string
    if (c == '\0' || charIn(c, allspace + "[#"))
      break;
  }
  l->backup();
}

_LexFunc
lexString(Lexer * l)
{
  l->acceptRun(allspace);
  l->ignore();

  if (!charIn(l->peek(), "'\""))
  {
    consumeUnquotedString(l);
    l->emit(TokType::String);
    return lexHit;
  }

  std::string quote = "";
  if (l->peek() == '"')
    quote = "\"";
  else if (l->peek() == '\'')
    quote = "'";
  else
    return l->error("the parser is horribly broken");

  // this is a loop in order to enable consecutive string literals to be parsed
  while (l->accept(quote))
  {
    char c = l->input()[l->start()];
    char prev;
    while (true)
    {
      prev = c;
      c = l->next();
      if (c == quote[0] && prev != '\\')
        break;
      else if (c == '\0')
        return l->error("unterminated string");
    }
    l->emit(TokType::String);
    consumeWhitespace(l);
  }

  // unlex the last run of whitespace - we need to skip whitespace between consecutive string
  // literals, but not the final run of whitespace.  Carefully tracking/handling whitespace
  // is is important for determining how to place/format comment tokens.
  l->rewind();

  return lexHit;
}

_LexFunc
lexNumber(Lexer * l)
{
  l->accept("+-");
  int n = l->acceptRun(digits);
  if (l->accept("."))
    n += l->acceptRun(digits);

  if (l->accept("eE"))
  {
    n += l->accept("-+");
    n += l->acceptRun(digits);
  }
  if (n == 0)
  {
    // fall back to string
    consumeUnquotedString(l);
    l->emit(TokType::String);
    return lexHit;
  }

  if (!charIn(l->peek(), allspace + "[") && l->peek() != '\0')
  {
    // fall back to string
    consumeUnquotedString(l);
    l->emit(TokType::String);
    return lexHit;
  }

  l->emit(TokType::Number);
  return lexHit;
}

_LexFunc
lexHit(Lexer * l)
{
  lexComments(l);
  consumeWhitespace(l);
  char c = l->next();
  if (c == '[')
  {
    l->emit(TokType::LeftBracket);
    return lexPath;
  }
  else if (charIn(c, identchars))
  {
    l->acceptRun(identchars);
    l->emit(TokType::Ident);
    return lexEq;
  }
  else if (c == '\0')
  {
    l->emit(TokType::EOF);
    return NULL;
  }
  return l->error("invalid character '" + std::string(1, c) + "'");
}

} // namespace hit

#define EOF TMPEOF
<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////
// This file is part of Grappa, a system for scaling irregular
// applications on commodity clusters.

// Copyright (C) 2010-2014 University of Washington and Battelle
// Memorial Institute. University of Washington authorizes use of this
// Grappa software.

// Grappa is free software: you can redistribute it and/or modify it
// under the terms of the Affero General Public License as published
// by Affero, Inc., either version 1 of the License, or (at your
// option) any later version.

// Grappa is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// Affero General Public License for more details.

// You should have received a copy of the Affero General Public
// License along with this program. If not, you may obtain one from
// http://www.affero.org/oagpl.html.
////////////////////////////////////////////////////////////////////////

#ifndef COLLECTIVE_HPP
#define COLLECTIVE_HPP

//#include "common.hpp"
#include "CompletionEvent.hpp"
#include "Message.hpp"
#include "MessagePool.hpp"
#include "Tasking.hpp"
#include "CountingSemaphoreLocal.hpp"
#include "Barrier.hpp"

#include <functional>
#include <algorithm>

// TODO/FIXME: use actual max message size (have Communicator be able to tell us)
const size_t MAX_MESSAGE_SIZE = 3192;

#define COLL_MAX &collective_max
#define COLL_MIN &collective_min
#define COLL_ADD &collective_add
#define COLL_MULT &collective_mult

template< typename T >
T collective_add(const T& a, const T& b) {
  return a+b;
}
template< typename T >
T collective_max(const T& a, const T& b) {
  return (a>b) ? a : b;
}
template< typename T >
T collective_min(const T& a, const T& b) {
  return (a<b) ? a : b;
}
template< typename T >
T collective_mult(const T& a, const T& b) {
  return a*b;
}
template< typename T >
T collective_or(const T& a, const T& b) {
  return a || b;
}
template< typename T >
T collective_and(const T& a, const T& b) {
  return a && b;
}

namespace Grappa {

  namespace impl {
    const Core HOME_CORE = 0;
  }

  /// @addtogroup Collectives
  /// @{

  /// Call message (work that cannot block) on all cores, block until ack received from all.
  /// Like Grappa::on_all_cores() but does @a not spawn tasks on each core.
  /// Can safely be called concurrently with others.
  template<typename F>
  void call_on_all_cores(F work) {
    Core origin = mycore();
    CompletionEvent ce(cores()-1);

    auto lsz = [&ce,origin,work]{};
    MessagePool pool(cores()*(sizeof(Message<decltype(lsz)>)));

    for (Core c = 0; c < cores(); c++) if (c != mycore()) {
      pool.send_message(c, [&ce, origin, work] {
        work();
        send_heap_message(origin, [&ce]{ ce.complete(); });
      });
    }
    work(); // do my core's work
    ce.wait();
  }

  /// Spawn a private task on each core, block until all complete.
  /// To be used for any SPMD-style work (e.g. initializing globals).
  /// Also used as a primitive in Grappa system code where anything is done on all cores.
  ///
  /// @b Example:
  /// @code
  ///   int x[Grappa::cores()];
  ///   GlobalAddress<int> x_base = make_global(x);
  ///   Grappa::on_all_cores([x_base]{
  ///     Grappa::delegate::write(x_base+Grappa::mycore(), 1);
  ///   });
  /// @endcode
  template<typename F>
  void on_all_cores(F work) {

    CompletionEvent ce(cores());
    auto ce_addr = make_global(&ce);

    auto lsz = [ce_addr,work]{};
    MessagePool pool(cores()*(sizeof(Message<decltype(lsz)>)));

    for (Core c = 0; c < cores(); c++) {
      pool.send_message(c, [ce_addr, work] {
        spawn([ce_addr, work] {
          work();
          complete(ce_addr);
        });
      });
    }
    ce.wait();
  }

  namespace impl {

    template<typename T>
    struct Reduction {
      static FullEmpty<T> result;
    };
    template<typename T> FullEmpty<T> Reduction<T>::result;

    template< typename T, T (*ReduceOp)(const T&, const T&) >
    void collect_reduction(const T& val) {
      static T total;
      static Core cores_in = 0;

      DCHECK(mycore() == HOME_CORE);

      if (cores_in == 0) {
        total = val;
      } else {
        total = ReduceOp(total, val);
      }

      cores_in++;
      DVLOG(4) << "cores_in: " << cores_in;

      if (cores_in == cores()) {
        cores_in = 0;
        T tmp_total = total;
        for (Core c = 0; c < cores(); c++) {
          send_heap_message(c, [tmp_total] {
            Reduction<T>::result.writeXF(tmp_total);
          });
        }
      }
    }

    template<typename T, T (*ReduceOp)(const T&, const T&) >
    class InplaceReduction {
    protected:
      CompletionEvent * ce;
      T * array;
      Core elems_in = 0;
      size_t nelem;
    public:
      /// SPMD, must be called on static/file-global object on all cores
      /// blocks until reduction is complete
      void call_allreduce(T * in_array, size_t nelem) {
        // setup everything (block to make sure HOME_CORE is done)
        this->array = in_array;
        this->nelem = nelem;

        size_t n_per_msg = MAX_MESSAGE_SIZE / sizeof(T);
        size_t nmsg = nelem / n_per_msg + (nelem % n_per_msg ? 1 : 0);
        auto nmsg_total = nmsg*(cores()-1);

        CompletionEvent local_ce;
        this->ce = &local_ce;
        this->ce->enroll( (mycore() == HOME_CORE) ? nmsg_total : nmsg );
        barrier();

        if (mycore() != HOME_CORE) {
          for (size_t k=0; k<nelem; k+=n_per_msg) {
            size_t this_nelem = std::min(n_per_msg, nelem-k);

            // everyone sends their contribution to HOME_CORE, last one wakes HOME_CORE
            send_heap_message(HOME_CORE, [this,k](void * payload, size_t payload_size) {
              DCHECK(mycore() == HOME_CORE);

              auto in_array = static_cast<T*>(payload);
              auto in_n = payload_size/sizeof(T);
              auto total = this->array+k;

              for (size_t i=0; i<in_n; i++) {
                total[i] = ReduceOp(total[i], in_array[i]);
              }
              DVLOG(3) << "incrementing HOME sem, now at " << ce->get_count();
              this->ce->complete();
            }, (void*)(in_array+k), sizeof(T)*this_nelem);
          }

          DVLOG(3) << "about to block for " << nelem << " with sem == " << ce->get_count();           this->ce->wait();

        } else {
          auto nmsg_total = nmsg*(cores()-1);

          // home core waits until woken by last received message from other cores
          this->ce->wait();
          DVLOG(3) << "woke with sem == " << ce->get_count();

          // send total to everyone else and wake them
          char msg_buf[(cores()-1)*sizeof(PayloadMessage<std::function<void(decltype(this),size_t)>>)];
          MessagePool pool(msg_buf, sizeof(msg_buf));
          for (Core c = 0; c < cores(); c++) {
            if (c != HOME_CORE) {
              // send totals back to all the other cores
              size_t n_per_msg = MAX_MESSAGE_SIZE / sizeof(T);
              for (size_t k=0; k<nelem; k+=n_per_msg) {
                size_t this_nelem = std::min(n_per_msg, nelem-k);
                pool.send_message(c, [this,k](void * payload, size_t psz){
                  auto total_k = static_cast<T*>(payload);
                  auto in_n = psz / sizeof(T);
                  for (size_t i=0; i<in_n; i++) {
                    this->array[k+i] = total_k[i];
                  }
                  this->ce->complete();
                  DVLOG(3) << "incrementing sem, now at " << ce->get_count();
                }, this->array+k, sizeof(T)*this_nelem);
              }
            }
          }
          // once all messages are sent, HOME_CORE's task continues
        }
      }
    };

  } // namespace impl

  /// Called from SPMD context, reduces values from all cores calling `allreduce` and returns reduced
  /// values to everyone. Blocks until reduction is complete, so suffices as a global barrier.
  ///
  /// @warning May only one with a given type/op combination may be used at a time,
  ///          uses a function-private static variable.
  ///
  /// @b Example:
  /// @code
  ///   Grappa::on_all_cores([]{
  ///     int value = foo();
  ///     int total = Grappa::allreduce<int,collective_add>(value);
  ///   });
  /// @endcode
  template< typename T, T (*ReduceOp)(const T&, const T&) >
  T allreduce(T myval) {
    impl::Reduction<T>::result.reset();

    send_message(impl::HOME_CORE, [myval]{
      impl::collect_reduction<T,ReduceOp>(myval);
    });

    return impl::Reduction<T>::result.readFF();
  }

  /// Called from SPMD context.
  /// Do an in-place allreduce (works on arrays). All elements of the array will be
  /// overwritten by the operation with the total from all cores.
  ///
  /// @warning May only one with a given type/op combination may be used at a time,
  ///          uses a function-private static variable.
  template< typename T, T (*ReduceOp)(const T&, const T&) >
  void allreduce_inplace(T * array, size_t nelem = 1) {
    static impl::InplaceReduction<T,ReduceOp> reducer;
    reducer.call_allreduce(array, nelem);
  }

  /// Called from a single task (usually user_main), reduces values from all cores onto the calling node.
  /// Blocks until reduction is complete.
  /// Safe to use any number of these concurrently.
  ///
  /// @b Example:
  /// @code
  ///   static int x;
  ///   void user_main() {
  ///     on_all_cores([]{ x = foo(); });
  ///     int total = reduce<int,collective_add>(&x);
  ///   }
  /// @endcode
  template< typename T, T (*ReduceOp)(const T&, const T&) >
  T reduce(const T * global_ptr) {
    //NOTE: this is written in a continuation passing
    //style to avoid the use of a GCE which async delegates only support
    CompletionEvent ce(cores()-1);
    // TODO: look into optionally stack-allocating pool storage like in IncoherentAcquirer.
    MessagePool pool(cores() * sizeof(Message<std::function<void(T*)>>));

    T total = *global_ptr;
    Core origin = mycore();

    for (Core c=0; c<cores(); c++) {
      if (c != origin) {
        pool.send_message(c, [global_ptr, &ce, &total, origin]{
          T val = *global_ptr;
          send_heap_message(origin, [val,&ce,&total] {
            total = ReduceOp(total, val);
            ce.complete();
          });
        });
      }
    }
    ce.wait();
    return total;
  }

  /// Reduce over a symmetrically allocated object.
  /// Blocks until reduction is complete.
  /// Safe to use any number of these concurrently.
  //
  /// @b Example:
  /// @code
  ///   void user_main() {
  ///     auto x = Grappa::symmetric_global_alloc<BlockAlignedInt>();
  ///     on_all_cores([]{ x = foo(); });
  ///     int total = reduce<int,collective_add>(x);
  ///   }
  /// @endcode
  template< typename T, T (*ReduceOp)(const T&, const T&)>
   T reduce( GlobalAddress<T> localizable ) {
     CompletionEvent ce(cores() - 1);

     T total = *(localizable.localize());
     Core origin = mycore();

     for (Core c=0; c<cores(); c++) {
      if (c != origin) {
        send_heap_message(c, [localizable, &ce, &total, origin]{
          T val = *(localizable.localize());
          send_heap_message(origin, [val,&ce,&total] {
            total = ReduceOp(total, val);
            ce.complete();
          });
        });
      }
     }
    ce.wait();
    return total;
   }

  /// Reduce over a member of a symmetrically allocated object.
  /// The Accessor function is used to pull out the member.
  /// Blocks until reduction is complete.
  /// Safe to use any number of these concurrently.
  ///
  /// @b Example:
  /// @code
  ///   struct BlockAlignedObj {
  ///     int x;
  ///   } GRAPPA_BLOCK_ALIGNED;
  ///   int getX(GlobalAddress<BlockAlignedObj> o) {
  ///     return o->x;
  ///   }
  ///   void user_main() {
  ///     auto x = Grappa::symmetric_global_alloc<BlockAlignedObj>();
  ///     on_all_cores([]{ x = foo(); });
  ///     int total = reduce<int,BlockedAlignedObj,collective_add,&getX>(x);
  ///   }
  /// @endcode
  template< typename T, typename P, T (*ReduceOp)(const T&, const T&), T (*Accessor)(GlobalAddress<P>)>
   T reduce( GlobalAddress<P> localizable ) {
     CompletionEvent ce(cores() - 1);

     T total = Accessor(localizable);
     Core origin = mycore();

     for (Core c=0; c<cores(); c++) {
      if (c != origin) {
        send_heap_message(c, [localizable, &ce, &total, origin]{
          T val = Accessor(localizable);
          send_heap_message(origin, [val,&ce,&total] {
            total = ReduceOp(total, val);
            ce.complete();
          });
        });
      }
     }
    ce.wait();
    return total;
  }

  template< typename F = nullptr_t >
  auto sum_all_cores(F func) -> decltype(func()) {
    decltype(func()) total = func();
    CompletionEvent _ce(cores()-1);
    auto ce = make_global(&_ce);
    for (Core c=0; c < cores(); c++) if (c != mycore()) {
      send_heap_message(c, [ce,func,&total]{
        auto r = func();
        send_heap_message(ce.core(), [ce,r,&total]{
          total += r;
          ce->complete();
        });
      });
    }
    ce->wait();
    return total;
  }

  /// @}
} // namespace Grappa

#endif // COLLECTIVE_HPP

<commit_msg>remove message pool<commit_after>////////////////////////////////////////////////////////////////////////
// This file is part of Grappa, a system for scaling irregular
// applications on commodity clusters.

// Copyright (C) 2010-2014 University of Washington and Battelle
// Memorial Institute. University of Washington authorizes use of this
// Grappa software.

// Grappa is free software: you can redistribute it and/or modify it
// under the terms of the Affero General Public License as published
// by Affero, Inc., either version 1 of the License, or (at your
// option) any later version.

// Grappa is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// Affero General Public License for more details.

// You should have received a copy of the Affero General Public
// License along with this program. If not, you may obtain one from
// http://www.affero.org/oagpl.html.
////////////////////////////////////////////////////////////////////////

#ifndef COLLECTIVE_HPP
#define COLLECTIVE_HPP

//#include "common.hpp"
#include "CompletionEvent.hpp"
#include "Message.hpp"
#include "Tasking.hpp"
#include "CountingSemaphoreLocal.hpp"
#include "Barrier.hpp"

#include <functional>
#include <algorithm>

// TODO/FIXME: use actual max message size (have Communicator be able to tell us)
const size_t MAX_MESSAGE_SIZE = 3192;

#define COLL_MAX &collective_max
#define COLL_MIN &collective_min
#define COLL_ADD &collective_add
#define COLL_MULT &collective_mult

template< typename T >
T collective_add(const T& a, const T& b) {
  return a+b;
}
template< typename T >
T collective_max(const T& a, const T& b) {
  return (a>b) ? a : b;
}
template< typename T >
T collective_min(const T& a, const T& b) {
  return (a<b) ? a : b;
}
template< typename T >
T collective_mult(const T& a, const T& b) {
  return a*b;
}
template< typename T >
T collective_or(const T& a, const T& b) {
  return a || b;
}
template< typename T >
T collective_and(const T& a, const T& b) {
  return a && b;
}

namespace Grappa {

  namespace impl {
    const Core HOME_CORE = 0;
  }

  /// @addtogroup Collectives
  /// @{

  /// Call message (work that cannot block) on all cores, block until ack received from all.
  /// Like Grappa::on_all_cores() but does @a not spawn tasks on each core.
  /// Can safely be called concurrently with others.
  template<typename F>
  void call_on_all_cores(F work) {
    Core origin = mycore();
    CompletionEvent ce(cores()-1);

    auto lsz = [&ce,origin,work]{};

    for (Core c = 0; c < cores(); c++) if (c != mycore()) {
      send_heap_message(c, [&ce, origin, work] {
        work();
        send_heap_message(origin, [&ce]{ ce.complete(); });
      });
    }
    work(); // do my core's work
    ce.wait();
  }

  /// Spawn a private task on each core, block until all complete.
  /// To be used for any SPMD-style work (e.g. initializing globals).
  /// Also used as a primitive in Grappa system code where anything is done on all cores.
  ///
  /// @b Example:
  /// @code
  ///   int x[Grappa::cores()];
  ///   GlobalAddress<int> x_base = make_global(x);
  ///   Grappa::on_all_cores([x_base]{
  ///     Grappa::delegate::write(x_base+Grappa::mycore(), 1);
  ///   });
  /// @endcode
  template<typename F>
  void on_all_cores(F work) {

    CompletionEvent ce(cores());
    auto ce_addr = make_global(&ce);

    auto lsz = [ce_addr,work]{};

    for (Core c = 0; c < cores(); c++) {
      send_heap_message(c, [ce_addr, work] {
        spawn([ce_addr, work] {
          work();
          complete(ce_addr);
        });
      });
    }
    ce.wait();
  }

  namespace impl {

    template<typename T>
    struct Reduction {
      static FullEmpty<T> result;
    };
    template<typename T> FullEmpty<T> Reduction<T>::result;

    template< typename T, T (*ReduceOp)(const T&, const T&) >
    void collect_reduction(const T& val) {
      static T total;
      static Core cores_in = 0;

      DCHECK(mycore() == HOME_CORE);

      if (cores_in == 0) {
        total = val;
      } else {
        total = ReduceOp(total, val);
      }

      cores_in++;
      DVLOG(4) << "cores_in: " << cores_in;

      if (cores_in == cores()) {
        cores_in = 0;
        T tmp_total = total;
        for (Core c = 0; c < cores(); c++) {
          send_heap_message(c, [tmp_total] {
            Reduction<T>::result.writeXF(tmp_total);
          });
        }
      }
    }

    template<typename T, T (*ReduceOp)(const T&, const T&) >
    class InplaceReduction {
    protected:
      CompletionEvent * ce;
      T * array;
      Core elems_in = 0;
      size_t nelem;
    public:
      /// SPMD, must be called on static/file-global object on all cores
      /// blocks until reduction is complete
      void call_allreduce(T * in_array, size_t nelem) {
        // setup everything (block to make sure HOME_CORE is done)
        this->array = in_array;
        this->nelem = nelem;

        size_t n_per_msg = MAX_MESSAGE_SIZE / sizeof(T);
        size_t nmsg = nelem / n_per_msg + (nelem % n_per_msg ? 1 : 0);
        auto nmsg_total = nmsg*(cores()-1);

        CompletionEvent local_ce;
        this->ce = &local_ce;
        this->ce->enroll( (mycore() == HOME_CORE) ? nmsg_total : nmsg );
        barrier();

        if (mycore() != HOME_CORE) {
          for (size_t k=0; k<nelem; k+=n_per_msg) {
            size_t this_nelem = std::min(n_per_msg, nelem-k);

            // everyone sends their contribution to HOME_CORE, last one wakes HOME_CORE
            send_heap_message(HOME_CORE, [this,k](void * payload, size_t payload_size) {
              DCHECK(mycore() == HOME_CORE);

              auto in_array = static_cast<T*>(payload);
              auto in_n = payload_size/sizeof(T);
              auto total = this->array+k;

              for (size_t i=0; i<in_n; i++) {
                total[i] = ReduceOp(total[i], in_array[i]);
              }
              DVLOG(3) << "incrementing HOME sem, now at " << ce->get_count();
              this->ce->complete();
            }, (void*)(in_array+k), sizeof(T)*this_nelem);
          }

          DVLOG(3) << "about to block for " << nelem << " with sem == " << ce->get_count();           this->ce->wait();

        } else {
          auto nmsg_total = nmsg*(cores()-1);

          // home core waits until woken by last received message from other cores
          this->ce->wait();
          DVLOG(3) << "woke with sem == " << ce->get_count();

          // send total to everyone else and wake them
          char msg_buf[(cores()-1)*sizeof(PayloadMessage<std::function<void(decltype(this),size_t)>>)];
          for (Core c = 0; c < cores(); c++) {
            if (c != HOME_CORE) {
              // send totals back to all the other cores
              size_t n_per_msg = MAX_MESSAGE_SIZE / sizeof(T);
              for (size_t k=0; k<nelem; k+=n_per_msg) {
                size_t this_nelem = std::min(n_per_msg, nelem-k);
                send_heap_message(c, [this,k](void * payload, size_t psz){
                  auto total_k = static_cast<T*>(payload);
                  auto in_n = psz / sizeof(T);
                  for (size_t i=0; i<in_n; i++) {
                    this->array[k+i] = total_k[i];
                  }
                  this->ce->complete();
                  DVLOG(3) << "incrementing sem, now at " << ce->get_count();
                }, this->array+k, sizeof(T)*this_nelem);
              }
            }
          }
          // once all messages are sent, HOME_CORE's task continues
        }
      }
    };

  } // namespace impl

  /// Called from SPMD context, reduces values from all cores calling `allreduce` and returns reduced
  /// values to everyone. Blocks until reduction is complete, so suffices as a global barrier.
  ///
  /// @warning May only one with a given type/op combination may be used at a time,
  ///          uses a function-private static variable.
  ///
  /// @b Example:
  /// @code
  ///   Grappa::on_all_cores([]{
  ///     int value = foo();
  ///     int total = Grappa::allreduce<int,collective_add>(value);
  ///   });
  /// @endcode
  template< typename T, T (*ReduceOp)(const T&, const T&) >
  T allreduce(T myval) {
    impl::Reduction<T>::result.reset();

    send_message(impl::HOME_CORE, [myval]{
      impl::collect_reduction<T,ReduceOp>(myval);
    });

    return impl::Reduction<T>::result.readFF();
  }

  /// Called from SPMD context.
  /// Do an in-place allreduce (works on arrays). All elements of the array will be
  /// overwritten by the operation with the total from all cores.
  ///
  /// @warning May only one with a given type/op combination may be used at a time,
  ///          uses a function-private static variable.
  template< typename T, T (*ReduceOp)(const T&, const T&) >
  void allreduce_inplace(T * array, size_t nelem = 1) {
    static impl::InplaceReduction<T,ReduceOp> reducer;
    reducer.call_allreduce(array, nelem);
  }

  /// Called from a single task (usually user_main), reduces values from all cores onto the calling node.
  /// Blocks until reduction is complete.
  /// Safe to use any number of these concurrently.
  ///
  /// @b Example:
  /// @code
  ///   static int x;
  ///   void user_main() {
  ///     on_all_cores([]{ x = foo(); });
  ///     int total = reduce<int,collective_add>(&x);
  ///   }
  /// @endcode
  template< typename T, T (*ReduceOp)(const T&, const T&) >
  T reduce(const T * global_ptr) {
    //NOTE: this is written in a continuation passing
    //style to avoid the use of a GCE which async delegates only support
    CompletionEvent ce(cores()-1);

    T total = *global_ptr;
    Core origin = mycore();

    for (Core c=0; c<cores(); c++) {
      if (c != origin) {
        send_heap_message(c, [global_ptr, &ce, &total, origin]{
          T val = *global_ptr;
          send_heap_message(origin, [val,&ce,&total] {
            total = ReduceOp(total, val);
            ce.complete();
          });
        });
      }
    }
    ce.wait();
    return total;
  }

  /// Reduce over a symmetrically allocated object.
  /// Blocks until reduction is complete.
  /// Safe to use any number of these concurrently.
  //
  /// @b Example:
  /// @code
  ///   void user_main() {
  ///     auto x = Grappa::symmetric_global_alloc<BlockAlignedInt>();
  ///     on_all_cores([]{ x = foo(); });
  ///     int total = reduce<int,collective_add>(x);
  ///   }
  /// @endcode
  template< typename T, T (*ReduceOp)(const T&, const T&)>
   T reduce( GlobalAddress<T> localizable ) {
     CompletionEvent ce(cores() - 1);

     T total = *(localizable.localize());
     Core origin = mycore();

     for (Core c=0; c<cores(); c++) {
      if (c != origin) {
        send_heap_message(c, [localizable, &ce, &total, origin]{
          T val = *(localizable.localize());
          send_heap_message(origin, [val,&ce,&total] {
            total = ReduceOp(total, val);
            ce.complete();
          });
        });
      }
     }
    ce.wait();
    return total;
   }

  /// Reduce over a member of a symmetrically allocated object.
  /// The Accessor function is used to pull out the member.
  /// Blocks until reduction is complete.
  /// Safe to use any number of these concurrently.
  ///
  /// @b Example:
  /// @code
  ///   struct BlockAlignedObj {
  ///     int x;
  ///   } GRAPPA_BLOCK_ALIGNED;
  ///   int getX(GlobalAddress<BlockAlignedObj> o) {
  ///     return o->x;
  ///   }
  ///   void user_main() {
  ///     auto x = Grappa::symmetric_global_alloc<BlockAlignedObj>();
  ///     on_all_cores([]{ x = foo(); });
  ///     int total = reduce<int,BlockedAlignedObj,collective_add,&getX>(x);
  ///   }
  /// @endcode
  template< typename T, typename P, T (*ReduceOp)(const T&, const T&), T (*Accessor)(GlobalAddress<P>)>
   T reduce( GlobalAddress<P> localizable ) {
     CompletionEvent ce(cores() - 1);

     T total = Accessor(localizable);
     Core origin = mycore();

     for (Core c=0; c<cores(); c++) {
      if (c != origin) {
        send_heap_message(c, [localizable, &ce, &total, origin]{
          T val = Accessor(localizable);
          send_heap_message(origin, [val,&ce,&total] {
            total = ReduceOp(total, val);
            ce.complete();
          });
        });
      }
     }
    ce.wait();
    return total;
  }

  template< typename F = nullptr_t >
  auto sum_all_cores(F func) -> decltype(func()) {
    decltype(func()) total = func();
    CompletionEvent _ce(cores()-1);
    auto ce = make_global(&_ce);
    for (Core c=0; c < cores(); c++) if (c != mycore()) {
      send_heap_message(c, [ce,func,&total]{
        auto r = func();
        send_heap_message(ce.core(), [ce,r,&total]{
          total += r;
          ce->complete();
        });
      });
    }
    ce->wait();
    return total;
  }

  /// @}
} // namespace Grappa

#endif // COLLECTIVE_HPP

<|endoftext|>


<commit_before>// rogueGenocide.cpp : Defines the entry point for the DLL application.
//

#include "bzfsAPI.h"
#include <string>
#include <map>

BZ_GET_PLUGIN_VERSION

// event handler callback
class RogueDeathHandler : public bz_EventHandler
{
public:
        RogueDeathHandler();
        virtual ~RogueDeathHandler();

        virtual void process ( bz_EventData *eventData );

        virtual bool autoDelete ( void ) { return false;} // this will be used for more then one event
protected:
};

RogueDeathHandler	deathHandler;

BZF_PLUGIN_CALL int bz_Load ( const char* commandLine )
{
	bz_debugMessage(4,"rogueGenocide plugin loaded");
	bz_registerEvent(bz_ePlayerDieEvent,&deathHandler);

	return 0;
}

BZF_PLUGIN_CALL int bz_Unload ( void )
{
	bz_removeEvent(bz_ePlayerDieEvent,&deathHandler);
	bz_debugMessage(4,"rogueGenocide plugin unloaded");
	return 0;
}

RogueDeathHandler::RogueDeathHandler()
{
}

RogueDeathHandler::~RogueDeathHandler()
{
}

void RogueDeathHandler::process ( bz_EventData *eventData )
{
	switch (eventData->eventType)
	{
		default:
			// no clue
			break;

		// wait for a tank death and start checking for genocide and rogues
		case bz_ePlayerDieEvent:
		{
			bz_PlayerDieEventData	*dieData = (bz_PlayerDieEventData*)eventData;
			//if its not a genocide kill, dont care
			if (dieData->flagKilledWith != "G" )
				break;
			// if the tank killed was not a rogue, let the server/client do the normal killing
			if (dieData->team != eRogueTeam )
				break;
			// if the tank killed was a rogue, and the killer was a rogue, kill the lousy tk'er
			if (dieData->killerTeam == eRogueTeam )
			{
			//	bz_killPlayer ( dieData->killerID, 0, dieData->killerID, "G" );
				bz_sendTextMessage(BZ_SERVER,dieData->killerID,"You should be more careful with Genocide");
			}
			// if the tank killed was a rogue, and the killer wasnt, kill all rogues.
			// note the possible issue if the rogue tank being killed has not spawned
			// and if all else fails, just kill all rogues.....nothing wrong with that

			bzAPIIntList	*playerList = bz_newIntList();

			bz_getPlayerIndexList(playerList);

			for ( unsigned int i = 0; i < playerList->size(); i++)
			{
				int targetID = (*playerList)[i];
				bz_PlayerRecord *playRec = bz_getPlayerByIndex ( targetID );
				if (!playRec) continue;

				if ( playRec->spawned && playRec->team == eRogueTeam )
				{
					bz_killPlayer( targetID, false, dieData->killerID, "G" );
					bz_sendTextMessage(BZ_SERVER, targetID, "You were a victim of Rogue Genocide");
				}
				bz_freePlayerRecord(playRec);
			}
			bz_deleteIntList(playerList);
		}
		break;

	}
}
<commit_msg>don't send out "you should be more careful" messages more than once per geno-killing.  also clean up a bunch of cruft and incorrect comments.<commit_after>// rogueGenocide.cpp : Defines the entry point for the DLL application.
//

#include "bzfsAPI.h"
#include <string>
#include <map>

BZ_GET_PLUGIN_VERSION

// event handler callback
class RogueDeathHandler : public bz_EventHandler
{
public:
        RogueDeathHandler();
        virtual ~RogueDeathHandler();

        virtual void process ( bz_EventData *eventData );

        virtual bool autoDelete ( void ) { return false;} // this will be used for more then one event
protected:
};

RogueDeathHandler	deathHandler;

BZF_PLUGIN_CALL int bz_Load ( const char* commandLine )
{
	bz_debugMessage(4,"rogueGenocide plugin loaded");
	bz_registerEvent(bz_ePlayerDieEvent,&deathHandler);

	return 0;
}

BZF_PLUGIN_CALL int bz_Unload ( void )
{
	bz_removeEvent(bz_ePlayerDieEvent,&deathHandler);
	bz_debugMessage(4,"rogueGenocide plugin unloaded");
	return 0;
}

RogueDeathHandler::RogueDeathHandler()
{
}

RogueDeathHandler::~RogueDeathHandler()
{
}

void RogueDeathHandler::process ( bz_EventData *eventData )
{
	switch (eventData->eventType)
	{
		default:
			// no clue
			break;

		// wait for a tank death and start checking for genocide and rogues
		case bz_ePlayerDieEvent:
		{
			bz_PlayerDieEventData	*dieData = (bz_PlayerDieEventData*)eventData;
			//if its not a genocide kill, dont care
			if (dieData->flagKilledWith != "G" )
				break;
			// if the tank killed was not a rogue, let the server/client do the normal killing
			if (dieData->team != eRogueTeam )
				break;

			// if the tank killed was a rogue, kill all rogues.
			bzAPIIntList	*playerList = bz_newIntList();

			bz_getPlayerIndexList(playerList);

			for ( unsigned int i = 0; i < playerList->size(); i++)
			{
				int targetID = (*playerList)[i];
				bz_PlayerRecord *playRec = bz_getPlayerByIndex ( targetID );
				if (!playRec) continue;

				// the sucker is a spawned rogue, kill him.  This generates another death event,
				// so if you kill another rogue with geno while you are a rogue you end up dead too.
				// and you get both messages (victim and be careful)
				if ( playRec->spawned && playRec->team == eRogueTeam )
				{
					bz_killPlayer( targetID, false, dieData->killerID, "G" );
					bz_sendTextMessage(BZ_SERVER, targetID, "You were a victim of Rogue Genocide");
				}

				// oops, I ended up killing myself (directly or indirectly) with Genocide!
				if ( playRec->team == eRogueTeam && targetID == dieData->killerID )
				{
					bz_sendTextMessage(BZ_SERVER, targetID, "You should be more careful with Genocide!");
				}

				bz_freePlayerRecord(playRec);
			}
			bz_deleteIntList(playerList);
		}
		break;

	}
}
<|endoftext|>


<commit_before>/** This file is part of Contacts daemon
 **
 ** Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies).
 **
 ** Contact:  Nokia Corporation (info@qt.nokia.com)
 **
 ** GNU Lesser General Public License Usage
 ** This file may be used under the terms of the GNU Lesser General Public License
 ** version 2.1 as published by the Free Software Foundation and appearing in the
 ** file LICENSE.LGPL included in the packaging of this file.  Please review the
 ** following information to ensure the GNU Lesser General Public License version
 ** 2.1 requirements will be met:
 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
 **
 ** In addition, as a special exception, Nokia gives you certain additional rights.
 ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included
 ** in the file LGPL_EXCEPTION.txt in this package.
 **
 ** Other Usage
 ** Alternatively, this file may be used in accordance with the terms and
 ** conditions contained in a signed written agreement between you and Nokia.
 **/

#include <QStringBuilder>

#include <QContactName>
#include <QContactBirthday>

#include <MLocale>

#include <recurrencerule.h>

#include "cdbirthdaycalendar.h"
#include "debug.h"

using namespace Contactsd;

// A random ID.
const QLatin1String calNotebookId("b1376da7-5555-1111-2222-227549c4e570");
const QLatin1String calNotebookColor("#e00080"); // Pink

CDBirthdayCalendar::CDBirthdayCalendar(SyncMode syncMode, QObject *parent) :
    QObject(parent),
    mCalendar(0),
    mStorage(0)
{
    mCalendar = mKCal::ExtendedCalendar::Ptr(new mKCal::ExtendedCalendar(KDateTime::Spec::LocalZone()));
    mStorage = mKCal::ExtendedCalendar::defaultStorage(mCalendar);

    MLocale * const locale = new MLocale(this);

    if (not locale->isInstalledTrCatalog(QLatin1String("calendar"))) {
        locale->installTrCatalog(QLatin1String("calendar"));
    }

    locale->connectSettings();
    connect(locale, SIGNAL(settingsChanged()), this, SLOT(onLocaleChanged()));

    MLocale::setDefault(*locale);

    mStorage->open();

    mKCal::Notebook::Ptr notebook = mStorage->notebook(calNotebookId);

    if (notebook.isNull()) {
        notebook = createNotebook();
        mStorage->addNotebook(notebook);
    } else {
        // Clear the calendar database if and only if restoring from a backup.
        switch(syncMode) {
        case Incremental:
            // Force calendar name update, if a locale change happened while contactsd was not running.
            onLocaleChanged();
            break;

        case FullSync:
            mStorage->deleteNotebook(notebook);
            notebook = createNotebook();
            mStorage->addNotebook(notebook);
            break;
        }
    }
}

CDBirthdayCalendar::~CDBirthdayCalendar()
{
    if (mStorage) {
        mStorage->close();
    }

    debug() << "Destroyed birthday calendar";
}

mKCal::Notebook::Ptr CDBirthdayCalendar::createNotebook()
{
    return mKCal::Notebook::Ptr(new mKCal::Notebook(calNotebookId,
                                                    qtTrId("qtn_caln_birthdays"),
                                                    QLatin1String(""),
                                                    calNotebookColor,
                                                    false, // Not shared.
                                                    true, // Is master.
                                                    false, // Not synced to Ovi.
                                                    false, // Writable.
                                                    true, // Visible.
                                                    QLatin1String("Birthday-Nokia"),
                                                    QLatin1String(""),
                                                    0));
}

void CDBirthdayCalendar::updateBirthday(const QContact &contact)
{
    // Retrieve contact details.
    const QContactDisplayLabel displayName = contact.detail<QContactDisplayLabel>();
    const QContactBirthday contactBirthday = contact.detail<QContactBirthday>();

    if (displayName.isEmpty() || contactBirthday.isEmpty()) {
        warning() << Q_FUNC_INFO << "Contact without name or birthday, local ID: "
                  << contact.localId();
        return;
    }

    // Retrieve birthday event.
    if (not mStorage->isValidNotebook(calNotebookId)) {
        warning() << Q_FUNC_INFO << "Invalid notebook ID: " << calNotebookId;
        return;
    }

    KCalCore::Event::Ptr event = calendarEvent(contact.localId());

    if (event.isNull()) {
        // Add a new event.
        event = KCalCore::Event::Ptr(new KCalCore::Event());
        event->startUpdates();
        event->setUid(calendarEventId(contact.localId()));
        event->setAllDay(true);

        // Ensure events appear as birthdays in the calendar, NB#259710.
        event->setCategories(QStringList() << QLatin1String("BIRTHDAY"));

        if (not mCalendar->addEvent(event, calNotebookId)) {
            warning() << Q_FUNC_INFO << "Failed to add event to calendar";
            return;
        }
    } else {
        // Update the existing event.
        event->setReadOnly(false);
        event->startUpdates();
    }

    // Transfer birthday details from contact to calendar event.
    event->setSummary(displayName.label());

    // Event has only date information, no time.
    event->setDtStart(KDateTime(contactBirthday.date(), QTime(), KDateTime::ClockTime));
    event->setDtEnd(KDateTime(contactBirthday.date().addDays(1), QTime(), KDateTime::ClockTime));

    // Must always set the recurrence as it depends on the event date.
    KCalCore::Recurrence * const rule = event->recurrence();
    rule->setStartDateTime(event->dtStart());

    // This is not a boolean, but the frequency of the recurrence: every 1 year.
    rule->setYearly(1);

    // Set the alarms on the event
    event->clearAlarms();
    KCalCore::Alarm::Ptr alarm = event->newAlarm();
    alarm->setType(KCalCore::Alarm::Audio);
    alarm->setEnabled(true);
    alarm->setDisplayAlarm(event->summary());
    alarm->setStartOffset(KCalCore::Duration(-36 * 3600 /* seconds */));

    event->setReadOnly(true);
    event->endUpdates();

    debug() << "Updated birthday event in calendar, local ID: " << contact.localId();
}

void CDBirthdayCalendar::deleteBirthday(QContactLocalId contactId)
{
    KCalCore::Event::Ptr event = calendarEvent(contactId);

    if (event.isNull()) {
        debug() << Q_FUNC_INFO << "Not found in calendar:" << contactId;
        return;
    }

    mCalendar->deleteEvent(event);

    debug() << "Deleted birthday event in calendar, local ID: " << event->uid();
}

void CDBirthdayCalendar::save()
{
    if (not mStorage->save()) {
        warning() << Q_FUNC_INFO << "Failed to update birthdays in calendar";
    }
}

QDate CDBirthdayCalendar::birthdayDate(QContactLocalId contactId)
{
    KCalCore::Event::Ptr event = calendarEvent(contactId);

    if (event.isNull()) {
        return QDate();
    }

    return event->dtStart().date();
}

QString CDBirthdayCalendar::summary(QContactLocalId contactId)
{
    KCalCore::Event::Ptr event = calendarEvent(contactId);

    if (event.isNull()) {
        return QString();
    }

    return event->summary();
}

QString CDBirthdayCalendar::calendarEventId(QContactLocalId contactId)
{
    static const QLatin1String calIdExtension("com.nokia.birthday/");
    return calIdExtension + QString::number(contactId);
}

KCalCore::Event::Ptr CDBirthdayCalendar::calendarEvent(QContactLocalId contactId)
{
    const QString eventId = calendarEventId(contactId);

    if (not mStorage->load(eventId)) {
        warning() << Q_FUNC_INFO << "Unable to load event from calendar";
        return KCalCore::Event::Ptr();
    }

    KCalCore::Event::Ptr event = mCalendar->event(eventId);

    if (event.isNull()) {
        debug() << Q_FUNC_INFO << "Not found in calendar:" << contactId;
    }

    return event;
}

void CDBirthdayCalendar::onLocaleChanged()
{
    mKCal::Notebook::Ptr notebook = mStorage->notebook(calNotebookId);

    if (notebook.isNull()) {
        warning() << Q_FUNC_INFO << "Calendar not found while changing locale";
        return;
    }

    const QString name = qtTrId("qtn_caln_birthdays");

    debug() << Q_FUNC_INFO << "Updating calendar name to" << name;
    notebook->setName(name);

    if (not mStorage->updateNotebook(notebook)) {
        warning() << Q_FUNC_INFO << "Could not save calendar";
    }
}
<commit_msg>Changes: Change contactBirthday from QContactBirthday to QDate<commit_after>/** This file is part of Contacts daemon
 **
 ** Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies).
 **
 ** Contact:  Nokia Corporation (info@qt.nokia.com)
 **
 ** GNU Lesser General Public License Usage
 ** This file may be used under the terms of the GNU Lesser General Public License
 ** version 2.1 as published by the Free Software Foundation and appearing in the
 ** file LICENSE.LGPL included in the packaging of this file.  Please review the
 ** following information to ensure the GNU Lesser General Public License version
 ** 2.1 requirements will be met:
 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
 **
 ** In addition, as a special exception, Nokia gives you certain additional rights.
 ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included
 ** in the file LGPL_EXCEPTION.txt in this package.
 **
 ** Other Usage
 ** Alternatively, this file may be used in accordance with the terms and
 ** conditions contained in a signed written agreement between you and Nokia.
 **/

#include <QStringBuilder>

#include <QContactName>
#include <QContactBirthday>

#include <MLocale>

#include <recurrencerule.h>

#include "cdbirthdaycalendar.h"
#include "debug.h"

using namespace Contactsd;

// A random ID.
const QLatin1String calNotebookId("b1376da7-5555-1111-2222-227549c4e570");
const QLatin1String calNotebookColor("#e00080"); // Pink

CDBirthdayCalendar::CDBirthdayCalendar(SyncMode syncMode, QObject *parent) :
    QObject(parent),
    mCalendar(0),
    mStorage(0)
{
    mCalendar = mKCal::ExtendedCalendar::Ptr(new mKCal::ExtendedCalendar(KDateTime::Spec::LocalZone()));
    mStorage = mKCal::ExtendedCalendar::defaultStorage(mCalendar);

    MLocale * const locale = new MLocale(this);

    if (not locale->isInstalledTrCatalog(QLatin1String("calendar"))) {
        locale->installTrCatalog(QLatin1String("calendar"));
    }

    locale->connectSettings();
    connect(locale, SIGNAL(settingsChanged()), this, SLOT(onLocaleChanged()));

    MLocale::setDefault(*locale);

    mStorage->open();

    mKCal::Notebook::Ptr notebook = mStorage->notebook(calNotebookId);

    if (notebook.isNull()) {
        notebook = createNotebook();
        mStorage->addNotebook(notebook);
    } else {
        // Clear the calendar database if and only if restoring from a backup.
        switch(syncMode) {
        case Incremental:
            // Force calendar name update, if a locale change happened while contactsd was not running.
            onLocaleChanged();
            break;

        case FullSync:
            mStorage->deleteNotebook(notebook);
            notebook = createNotebook();
            mStorage->addNotebook(notebook);
            break;
        }
    }
}

CDBirthdayCalendar::~CDBirthdayCalendar()
{
    if (mStorage) {
        mStorage->close();
    }

    debug() << "Destroyed birthday calendar";
}

mKCal::Notebook::Ptr CDBirthdayCalendar::createNotebook()
{
    return mKCal::Notebook::Ptr(new mKCal::Notebook(calNotebookId,
                                                    qtTrId("qtn_caln_birthdays"),
                                                    QLatin1String(""),
                                                    calNotebookColor,
                                                    false, // Not shared.
                                                    true, // Is master.
                                                    false, // Not synced to Ovi.
                                                    false, // Writable.
                                                    true, // Visible.
                                                    QLatin1String("Birthday-Nokia"),
                                                    QLatin1String(""),
                                                    0));
}

void CDBirthdayCalendar::updateBirthday(const QContact &contact)
{
    // Retrieve contact details.
    const QContactDisplayLabel displayName = contact.detail<QContactDisplayLabel>();
    const QDate contactBirthday = contact.detail<QContactBirthday>().date();

    if (displayName.isEmpty() || contactBirthday.isNull()) {
        warning() << Q_FUNC_INFO << "Contact without name or birthday, local ID: "
                  << contact.localId();
        return;
    }

    // Retrieve birthday event.
    if (not mStorage->isValidNotebook(calNotebookId)) {
        warning() << Q_FUNC_INFO << "Invalid notebook ID: " << calNotebookId;
        return;
    }

    KCalCore::Event::Ptr event = calendarEvent(contact.localId());

    if (event.isNull()) {
        // Add a new event.
        event = KCalCore::Event::Ptr(new KCalCore::Event());
        event->startUpdates();
        event->setUid(calendarEventId(contact.localId()));
        event->setAllDay(true);

        // Ensure events appear as birthdays in the calendar, NB#259710.
        event->setCategories(QStringList() << QLatin1String("BIRTHDAY"));

        if (not mCalendar->addEvent(event, calNotebookId)) {
            warning() << Q_FUNC_INFO << "Failed to add event to calendar";
            return;
        }
    } else {
        // Update the existing event.
        event->setReadOnly(false);
        event->startUpdates();
    }

    // Transfer birthday details from contact to calendar event.
    event->setSummary(displayName.label());

    // Event has only date information, no time.
    event->setDtStart(KDateTime(contactBirthday, QTime(), KDateTime::ClockTime));
    event->setDtEnd(KDateTime(contactBirthday.addDays(1), QTime(), KDateTime::ClockTime));

    // Must always set the recurrence as it depends on the event date.
    KCalCore::Recurrence * const rule = event->recurrence();
    rule->setStartDateTime(event->dtStart());

    // This is not a boolean, but the frequency of the recurrence: every 1 year.
    rule->setYearly(1);

    // Set the alarms on the event
    event->clearAlarms();
    KCalCore::Alarm::Ptr alarm = event->newAlarm();
    alarm->setType(KCalCore::Alarm::Audio);
    alarm->setEnabled(true);
    alarm->setDisplayAlarm(event->summary());
    alarm->setStartOffset(KCalCore::Duration(-36 * 3600 /* seconds */));

    event->setReadOnly(true);
    event->endUpdates();

    debug() << "Updated birthday event in calendar, local ID: " << contact.localId();
}

void CDBirthdayCalendar::deleteBirthday(QContactLocalId contactId)
{
    KCalCore::Event::Ptr event = calendarEvent(contactId);

    if (event.isNull()) {
        debug() << Q_FUNC_INFO << "Not found in calendar:" << contactId;
        return;
    }

    mCalendar->deleteEvent(event);

    debug() << "Deleted birthday event in calendar, local ID: " << event->uid();
}

void CDBirthdayCalendar::save()
{
    if (not mStorage->save()) {
        warning() << Q_FUNC_INFO << "Failed to update birthdays in calendar";
    }
}

QDate CDBirthdayCalendar::birthdayDate(QContactLocalId contactId)
{
    KCalCore::Event::Ptr event = calendarEvent(contactId);

    if (event.isNull()) {
        return QDate();
    }

    return event->dtStart().date();
}

QString CDBirthdayCalendar::summary(QContactLocalId contactId)
{
    KCalCore::Event::Ptr event = calendarEvent(contactId);

    if (event.isNull()) {
        return QString();
    }

    return event->summary();
}

QString CDBirthdayCalendar::calendarEventId(QContactLocalId contactId)
{
    static const QLatin1String calIdExtension("com.nokia.birthday/");
    return calIdExtension + QString::number(contactId);
}

KCalCore::Event::Ptr CDBirthdayCalendar::calendarEvent(QContactLocalId contactId)
{
    const QString eventId = calendarEventId(contactId);

    if (not mStorage->load(eventId)) {
        warning() << Q_FUNC_INFO << "Unable to load event from calendar";
        return KCalCore::Event::Ptr();
    }

    KCalCore::Event::Ptr event = mCalendar->event(eventId);

    if (event.isNull()) {
        debug() << Q_FUNC_INFO << "Not found in calendar:" << contactId;
    }

    return event;
}

void CDBirthdayCalendar::onLocaleChanged()
{
    mKCal::Notebook::Ptr notebook = mStorage->notebook(calNotebookId);

    if (notebook.isNull()) {
        warning() << Q_FUNC_INFO << "Calendar not found while changing locale";
        return;
    }

    const QString name = qtTrId("qtn_caln_birthdays");

    debug() << Q_FUNC_INFO << "Updating calendar name to" << name;
    notebook->setName(name);

    if (not mStorage->updateNotebook(notebook)) {
        warning() << Q_FUNC_INFO << "Could not save calendar";
    }
}
<|endoftext|>


<commit_before>#ifndef VEXCL_FFT_KERNELS_HPP
#define VEXCL_FFT_KERNELS_HPP

/*
The MIT License

Copyright (c) 2012-2013 Denis Demidov <ddemidov@ksu.ru>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

/**
 * \file   fft/kernels.hpp
 * \author Pascal Germroth <pascal@ensieve.org>
 * \brief  Kernel generator for FFT.
 */

// OpenCL FFT kernels, original by Eric Bainville

namespace vex {
namespace fft {

struct kernel_call {
    cl::Program program;
    cl::Kernel kernel;
    cl::NDRange global, local;
    kernel_call(cl::Program p, cl::Kernel k, cl::NDRange g, cl::NDRange l) : program(p), kernel(k), global(g), local(l) {}
};

/// Return the greatest k = 2^m <= n
static int pow2_floor(int n) {
    if(n == 0) return 0;
    for(size_t m = 0 ; ; m++)
        if((1 << (m + 1)) > n)
            return 1 << m;
}

// return the `s` most significant bits from an integer in reverse.
// <http://graphics.stanford.edu/~seander/bithacks.html#BitReverseObvious>
template <class T>
T bit_reverse(T v, size_t s = sizeof(T) * CHAR_BIT) {
    T r = v & 1;
    s--;
    for(v >>= 1 ; v ; v >>= 1) {
        r <<= 1;
        r |= v & 1;
        s--;
    }
    return r << s;
}

// calculate log2(2^k) = k.
template <class T>
T log2(T n) {
    int k = 0;
    if(n <= 0) throw std::logic_error("not a positive power of 2.");
    while(n != 1) {
        if(n & 1) throw std::logic_error("not a power of 2.");
        n >>= 1;
        k++;
    }
    return k;
}

// generates "(vfrom,vfrom+1,...,vto)"
void param_list(std::ostringstream &o, size_t from, size_t to) {
    o << '(';
    for(size_t i = from ; i != to ; i++) {
        if(i != from) o << ',';
        o << 'v' << i;
    } o << ')';
}

template <class T>
void in_place_dft(std::ostringstream &o, bool invert, size_t radix) {
    // inline DFT macro.
    if(radix == 2) {
        o << "#define DFT2(v0,v1) {"
                "real2_t tmp = v0 - v1;"
                "v0 += v1;"
                "v1 = tmp;"
            "}";
    } else {
        const size_t half_radix = radix / 2;
        // parameters
        o << "#define DFT" << radix;
        param_list(o, 0, radix);
        o << '{';
        // leaves
        for(size_t i = 0 ; i < half_radix ; i++) {
            o << "DFT2(v" << i << ",v" << (i + half_radix) << ");";
            if(i != 0) {
                const size_t j = i + half_radix;
                if(2 * i == half_radix) {
                    o << 'v' << j << "=twiddle_1_2(v" << j << ");";
                } else {
                    T factor = (invert ? 1 : -1) * (T)M_PI * i / half_radix;
                    typename cl_vector_of<T,2>::type twiddle =
                        {{std::cos(factor), std::sin(factor)}};
                    o << 'v' << j << "=mul(v" << j << ',' << std::setprecision(25) << twiddle << ");";
                }
            }
        }
        // next stage
        o << "DFT" << half_radix; param_list(o, 0, half_radix); o << ';';
        o << "DFT" << half_radix ; param_list(o, half_radix, radix); o << ';';
        o << "}\n";
    }
}

template <class T>
void kernel_radix(std::ostringstream &o, bool invert, size_t radix, size_t p, size_t threads) {
    for(size_t r = radix ; r >= 2 ; r /= 2)
        in_place_dft<T>(o, invert, r);

    // kernel.
    o << "__kernel void radix(__global const real2_t *x, __global real2_t *y) {";
    o << "const size_t i = get_global_id(0);"
        << "const size_t k = i & " << (p - 1) << ";" // index in input sequence, in 0..P-1
        << "const size_t j = ((i - k) * " << radix << ") + k;" // output index
        << "const size_t batch_offset = get_global_id(1) * " << (threads * radix) << ';'
        << "x += i + batch_offset; y += j + batch_offset;";

    // read
    for(size_t i = 0 ; i < radix ; i++)
        o << "real2_t v" << i << " = x[" << (i * threads) << "];";
    // twiddle
    for(size_t i = 1 ; i < radix ; i++) {
        const T alpha = -M_PI * i / (p * radix / 2);
        o << "v" << i << "=twiddle(v" << i << ",(real_t)" << std::setprecision(25) << alpha << " * k);";
    }
    // inplace DFT
    o << "DFT" << radix; param_list(o, 0, radix); o << ';';
    // write back
    for(size_t i = 0 ; i < radix ; i++) {
        size_t j = bit_reverse(i, log2(radix));
        o << "y[" << (i * p) << "]=v" << j << ';';
    }
    o << "}\n";
}

template <class T>
void kernel_common(std::ostringstream &o) {
    if(std::is_same<T, cl_double>::value) {
        o << standard_kernel_header;
        o << "typedef double real_t; typedef double2 real2_t;\n";
        o << "#define FFT_PI M_PI\n";
    } else {
        o << "typedef float real_t; typedef float2 real2_t;\n";
        o << "#define FFT_PI M_PI_F\n";
    }
}

template <class T>
kernel_call radix_kernel(cl::CommandQueue &queue, size_t n, size_t batch, bool invert, size_t radix, size_t p, cl::Buffer in, cl::Buffer out) {
    std::ostringstream o;
    kernel_common<T>(o);

    // Return A*B (complex multiplication)
    o << "real2_t mul(real2_t a, real2_t b) {"
            "return (real2_t)(a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x);"
        "}";

    // A * exp(alpha * I) == A  * (cos(alpha) + I * sin(alpha))
    o << "real2_t twiddle(real2_t a, real_t alpha) {"
        << "real_t cs, sn;"
        << "sn = sincos(" << (invert ? '-' : ' ') << "alpha, &cs);"
        << "return mul(a, (real2_t)(cs, sn));"
        << "}";

    // real(twiddle) = 0
    o << "real2_t twiddle_1_2(real2_t a){"
        << "return (real2_t)(" << (invert ? "-a.y, a.x" : "a.y, -a.x") << ");}\n";

    const size_t m = n / radix;
    kernel_radix<T>(o, invert, radix, p, m);

    auto program = build_sources(qctx(queue), o.str(), "-cl-mad-enable -cl-fast-relaxed-math");
    cl::Kernel kernel(program, "radix");
    kernel.setArg(0, in);
    kernel.setArg(1, out);

    size_t wg = pow2_floor(std::min(m,
        (size_t)kernel_workgroup_size(kernel, qdev(queue))));

    return kernel_call(program, kernel, cl::NDRange(m, batch), cl::NDRange(wg, 1));
}

template <class T>
kernel_call transpose_kernel(cl::CommandQueue &queue, size_t width, size_t height, cl::Buffer in, cl::Buffer out) {
    std::ostringstream o;
    kernel_common<T>(o);

    // determine max block size to fit into __local memory.
    size_t block_size = 128;
    const auto local_size = qdev(queue).getInfo<CL_DEVICE_LOCAL_MEM_SIZE>();
    while(block_size * block_size * sizeof(T) * 2 > local_size) block_size /= 2;
    block_size /= 2;

    // from NVIDIA SDK.
    o << "__kernel void transpose("
        "__global const real2_t *input, __global real2_t *output) {"
        "const size_t "
        "global_x = get_global_id(0), global_y = get_global_id(1),"
        "local_x = get_local_id(0), local_y = get_local_id(1),"
        "group_x = get_group_id(0), group_y = get_group_id(1),"
        "width =" << width << ", height = " << height << ", block_size = " << block_size << ","
        "target_x = local_y + group_y * block_size,"
        "target_y = local_x + group_x * block_size;"
        // local memory
        "__local real2_t block[" << (block_size * block_size) << "];"
        // copy from input to local memory
        "block[local_x + local_y * block_size] = input[global_x + global_y * width];"
        // wait until the whole block is filled
        "barrier(CLK_LOCAL_MEM_FENCE);"
        // transpose local block to target
        "output[target_x + target_y * height] = block[local_x + local_y * block_size];"
        "}";

    auto program = build_sources(qctx(queue), o.str());
    cl::Kernel kernel(program, "transpose");
    kernel.setArg(0, in);
    kernel.setArg(1, out);

    return kernel_call(program, kernel, cl::NDRange(width, height),
        cl::NDRange(std::min(width, block_size), std::min(height, block_size)));
}

} // namespace fft
} // namespace vex

#endif
<commit_msg>Fix for incompete macro definition in FFT kernel<commit_after>#ifndef VEXCL_FFT_KERNELS_HPP
#define VEXCL_FFT_KERNELS_HPP

/*
The MIT License

Copyright (c) 2012-2013 Denis Demidov <ddemidov@ksu.ru>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

/**
 * \file   fft/kernels.hpp
 * \author Pascal Germroth <pascal@ensieve.org>
 * \brief  Kernel generator for FFT.
 */

// OpenCL FFT kernels, original by Eric Bainville

namespace vex {
namespace fft {

struct kernel_call {
    cl::Program program;
    cl::Kernel kernel;
    cl::NDRange global, local;
    kernel_call(cl::Program p, cl::Kernel k, cl::NDRange g, cl::NDRange l) : program(p), kernel(k), global(g), local(l) {}
};

/// Return the greatest k = 2^m <= n
static int pow2_floor(int n) {
    if(n == 0) return 0;
    for(size_t m = 0 ; ; m++)
        if((1 << (m + 1)) > n)
            return 1 << m;
}

// return the `s` most significant bits from an integer in reverse.
// <http://graphics.stanford.edu/~seander/bithacks.html#BitReverseObvious>
template <class T>
T bit_reverse(T v, size_t s = sizeof(T) * CHAR_BIT) {
    T r = v & 1;
    s--;
    for(v >>= 1 ; v ; v >>= 1) {
        r <<= 1;
        r |= v & 1;
        s--;
    }
    return r << s;
}

// calculate log2(2^k) = k.
template <class T>
T log2(T n) {
    int k = 0;
    if(n <= 0) throw std::logic_error("not a positive power of 2.");
    while(n != 1) {
        if(n & 1) throw std::logic_error("not a power of 2.");
        n >>= 1;
        k++;
    }
    return k;
}

// generates "(vfrom,vfrom+1,...,vto)"
void param_list(std::ostringstream &o, size_t from, size_t to) {
    o << '(';
    for(size_t i = from ; i != to ; i++) {
        if(i != from) o << ',';
        o << 'v' << i;
    } o << ')';
}

template <class T>
void in_place_dft(std::ostringstream &o, bool invert, size_t radix) {
    // inline DFT macro.
    if(radix == 2) {
        o << "#define DFT2(v0,v1) { \\\n"
             "\t\treal2_t tmp = v0 - v1; \\\n"
             "\t\tv0 += v1; \\\n"
             "\t\tv1 = tmp; \\\n"
             "\t}\n\n";
    } else {
        const size_t half_radix = radix / 2;
        // parameters
        o << "#define DFT" << radix;
        param_list(o, 0, radix);
        o << "{ \\\n";
        // leaves
        for(size_t i = 0 ; i < half_radix ; i++) {
            o << "\t\tDFT2(v" << i << ",v" << (i + half_radix) << "); \\\n";
            if(i != 0) {
                const size_t j = i + half_radix;
                if(2 * i == half_radix) {
                    o << "\t\tv" << j << " = twiddle_1_2(v" << j << "); \\\n";
                } else {
                    T factor = (invert ? 1 : -1) * (T)M_PI * i / half_radix;
                    typename cl_vector_of<T,2>::type twiddle =
                        {{std::cos(factor), std::sin(factor)}};
                    o << "\t\tv" << j << " = mul(v" << j << ',' << std::setprecision(25) << twiddle << "); \\\n";
                }
            }
        }
        // next stage
        o << "\t\tDFT" << half_radix; param_list(o, 0, half_radix); o << "; \\\n";
        o << "\t\tDFT" << half_radix ; param_list(o, half_radix, radix); o << "; \\\n";
        o << "\t}\n\n";
    }
}

template <class T>
void kernel_radix(std::ostringstream &o, bool invert, size_t radix, size_t p, size_t threads) {
    for(size_t r = radix ; r >= 2 ; r /= 2)
        in_place_dft<T>(o, invert, r);

    // kernel.
    o << "kernel void radix(global const real2_t *x, global real2_t *y) {\n";
    o << "\tconst size_t i = get_global_id(0);\n"
      << "\tconst size_t k = i & " << (p - 1) << ";\n" // index in input sequence, in 0..P-1
      << "\tconst size_t j = ((i - k) * " << radix << ") + k;\n" // output index
      << "\tconst size_t batch_offset = get_global_id(1) * " << (threads * radix) << ";\n"
      << "\tx += i + batch_offset; y += j + batch_offset;\n";

    // read
    for(size_t i = 0 ; i < radix ; i++)
        o << "\treal2_t v" << i << " = x[" << (i * threads) << "];\n";
    // twiddle
    for(size_t i = 1 ; i < radix ; i++) {
        const T alpha = -M_PI * i / (p * radix / 2);
        o << "\tv" << i << "=twiddle(v" << i << ",(real_t)" << std::setprecision(25) << alpha << " * k);\n";
    }
    // inplace DFT
    o << "\tDFT" << radix; param_list(o, 0, radix); o << ";\n";
    // write back
    for(size_t i = 0 ; i < radix ; i++) {
        size_t j = bit_reverse(i, log2(radix));
        o << "\ty[" << (i * p) << "]=v" << j << ";\n";
    }
    o << "}\n\n";
}

template <class T>
void kernel_common(std::ostringstream &o) {
    if(std::is_same<T, cl_double>::value) {
        o << standard_kernel_header;
        o << "typedef double real_t; typedef double2 real2_t;\n";
        o << "#define FFT_PI M_PI\n";
    } else {
        o << "typedef float real_t; typedef float2 real2_t;\n";
        o << "#define FFT_PI M_PI_F\n";
    }
}

template <class T>
kernel_call radix_kernel(cl::CommandQueue &queue, size_t n, size_t batch, bool invert, size_t radix, size_t p, cl::Buffer in, cl::Buffer out) {
    std::ostringstream o;
    kernel_common<T>(o);

    // Return A*B (complex multiplication)
    o << "real2_t mul(real2_t a, real2_t b) {\n"
         "\treturn (real2_t)(a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x);\n"
         "}\n\n";

    // A * exp(alpha * I) == A  * (cos(alpha) + I * sin(alpha))
    o << "real2_t twiddle(real2_t a, real_t alpha) {\n"
      << "\treal_t cs, sn;\n"
      << "\tsn = sincos(" << (invert ? '-' : ' ') << "alpha, &cs);\n"
      << "\treturn mul(a, (real2_t)(cs, sn));\n"
      << "}\n\n";

    // real(twiddle) = 0
    o << "real2_t twiddle_1_2(real2_t a) {\n"
      << "\treturn (real2_t)(" << (invert ? "-a.y, a.x" : "a.y, -a.x") << ");\n}\n\n";

    const size_t m = n / radix;
    kernel_radix<T>(o, invert, radix, p, m);

#ifdef VEXCL_SHOW_KERNELS
    std::cout << o.str() << std::endl;
#endif

    auto program = build_sources(qctx(queue), o.str(), "-cl-mad-enable -cl-fast-relaxed-math");
    cl::Kernel kernel(program, "radix");
    kernel.setArg(0, in);
    kernel.setArg(1, out);

    size_t wg = pow2_floor(std::min(m,
        (size_t)kernel_workgroup_size(kernel, qdev(queue))));

    return kernel_call(program, kernel, cl::NDRange(m, batch), cl::NDRange(wg, 1));
}

template <class T>
kernel_call transpose_kernel(cl::CommandQueue &queue, size_t width, size_t height, cl::Buffer in, cl::Buffer out) {
    std::ostringstream o;
    kernel_common<T>(o);

    // determine max block size to fit into local memory.
    size_t block_size = 128;
    const auto local_size = qdev(queue).getInfo<CL_DEVICE_LOCAL_MEM_SIZE>();
    while(block_size * block_size * sizeof(T) * 2 > local_size) block_size /= 2;
    block_size /= 2;

    // from NVIDIA SDK.
    o << "kernel void transpose(\n"
        "\tglobal const real2_t *input, global real2_t *output) {\n"
        "\tconst size_t "
        "\tglobal_x = get_global_id(0), global_y = get_global_id(1),\n"
        "\t\tlocal_x = get_local_id(0), local_y = get_local_id(1),\n"
        "\t\tgroup_x = get_group_id(0), group_y = get_group_id(1),\n"
        "\t\twidth =" << width << ", height = " << height << ", block_size = " << block_size << ",\n"
        "\t\ttarget_x = local_y + group_y * block_size,\n"
        "\t\ttarget_y = local_x + group_x * block_size;\n"
        // local memory
        "\tlocal real2_t block[" << (block_size * block_size) << "];\n"
        // copy from input to local memory
        "\tblock[local_x + local_y * block_size] = input[global_x + global_y * width];\n"
        // wait until the whole block is filled
        "\tbarrier(CLK_LOCAL_MEM_FENCE);\n"
        // transpose local block to target
        "\toutput[target_x + target_y * height] = block[local_x + local_y * block_size];\n"
        "}\n\n";

#ifdef VEXCL_SHOW_KERNELS
    std::cout << o.str() << std::endl;
#endif

    auto program = build_sources(qctx(queue), o.str());
    cl::Kernel kernel(program, "transpose");
    kernel.setArg(0, in);
    kernel.setArg(1, out);

    return kernel_call(program, kernel, cl::NDRange(width, height),
        cl::NDRange(std::min(width, block_size), std::min(height, block_size)));
}

} // namespace fft
} // namespace vex

#endif
<|endoftext|>