<commit_before>/*
This file is part of Groho, a simulator for inter-planetary travel and warfare.
Copyright (c) 2017-2018 by Kaushik Ghose. Some rights reserved, see LICENSE

We have two types of objects in the simulation - Orrery bodies
(planets/moons/asteroid) and spaceships. They behave quite differently and
are described as separate classes of objects with some elements in common

*/

#pragma once

#include <cstdint>
#include <optional>
#include <string>
#include <unordered_map>
#include <vector>

#include "naifbody.hpp"
#include "vector.hpp"

namespace sim {

struct RockLike {

    struct Property {
        NAIFbody naif;

        float GM; // GM value for body
        float r;  // Radius of body (for collision tests)

        uint32_t color; // For display purposes
    };

    struct State {
        Vector pos; // Position referenced to solar system barycenter
        double t_s;
    };
};

struct ShipLike {

    struct Property {
        NAIFbody naif;

        float max_acc;   // Maximum acceleration possible for ship m/s^2
        float max_fuel;  // Maximum fuel reserve (U)
        float burn_rate; // Fuel consumption rate (U/ (m/s^2))

        uint32_t color; // For display purposes
    };

    struct State {
        Vector pos;  // Position referenced to solar system barycenter
        Vector vel;  // Velocity
        Vector att;  // Attitude
        float  fuel; // Current fuel reserve (U)
        float  acc;  // Current acceleration km/s^2
        double t_s;
    };
};

template <typename T> struct SnapShot {
    typename T::Property property;
    typename T::State    state;
};

// This allows us to do lazy computation of rock velocities.
// Time will tell if we were too clever here
template <typename T> struct SnapShotV {

    SnapShotV(int& N, typename T::Property _property)
        : _N(N)
    {
        property = _property;
    }

    typename T::Property property;
    typename T::State    _state[2];

    double& t_s() { return _state[_N].t_s; }
    double  t_s() const { return _state[_N].t_s; }

    Vector&       pos() { return _state[_N].pos; }
    const Vector& pos() const { return _state[_N].pos; }
    Vector        vel() const
    {
        return (_state[_N].pos - _state[1 - _N].pos)
            / (_state[_N].t_s - _state[1 - _N].t_s);
    }

    const typename T::State& state() const { return _state[_N]; }

    int& _N;
};

template <typename T> struct BaseCollection {
    std::vector<T>                       bodies;
    std::unordered_map<NAIFbody, size_t> lookup;

    size_t size() const { return bodies.size(); }

    T& operator[](size_t idx) { return bodies[idx]; }
    T& operator[](const NAIFbody& id) { return bodies[lookup[id]]; }

    const T& operator[](size_t idx) const { return bodies[idx]; }
    const T& operator[](const NAIFbody& id) const { return bodies[lookup[id]]; }

    virtual void push_back(const T& body)
    {
        bodies.push_back(body);
        lookup[body.property.naif] = bodies.size() - 1;
    }
};

template <typename T> struct Collection : public BaseCollection<T> {
};

// template <>
template <typename T>
struct Collection<SnapShotV<T>> : public BaseCollection<SnapShotV<T>> {

    int N = 0;

    void push_back(const typename T::Property& property)
    {
        // using BaseCollection<SnapShotV<T>>::bodies;
        // using BaseCollection<SnapShotV<T>>::lookup;

        bodies.push_back({ N, property });
        lookup[property.naif] = bodies.size() - 1;
    }

private:
    using BaseCollection<SnapShotV<T>>::push_back;
};

template <template <typename> class Trock, template <typename> class Tship>
struct RocksAndShips {
    Collection<Trock<RockLike>> system;
    Collection<Tship<ShipLike>> fleet;
};

/*
// TODO: deprecate
// This allows us to do lazy computation of rock velocities.
// Time will tell if we were too clever here
struct RockSnapShotWithVel {

    RockSnapShotWithVel(int& N, RockLike::Property _property)
        : _N(N)
    {
        property = _property;
    }

    RockLike::Property property;
    RockLike::State    _state[2];

    double& t_s() { return _state[_N].t_s; }
    double  t_s() const { return _state[_N].t_s; }

    Vector&       pos() { return _state[_N].pos; }
    const Vector& pos() const { return _state[_N].pos; }
    Vector        vel() const
    {
        return (_state[_N].pos - _state[1 - _N].pos)
            / (_state[_N].t_s - _state[1 - _N].t_s);
    }

    constexpr const RockLike::State& state() const { return _state[_N]; }

    int& _N;
};

template <typename T> struct BaseCollection {
    std::vector<T>                       bodies;
    std::unordered_map<NAIFbody, size_t> lookup;

    constexpr size_t size() const { return bodies.size(); }

    constexpr T& operator[](size_t idx) { return bodies[idx]; }
    constexpr T& operator[](const NAIFbody& id) { return bodies[lookup[id]]; }

    constexpr const T& operator[](size_t idx) const { return bodies[idx]; }

    virtual void push_back(const T& body)
    {
        bodies.push_back(body);
        lookup[body.property.naif] = bodies.size() - 1;
    }
};

template <typename T> struct Collection : public BaseCollection<T> {
};

template <>
struct Collection<RockSnapShotWithVel>
    : public BaseCollection<RockSnapShotWithVel> {

    int N = 0;

    void push_back(const RockLike::Property& property)
    {
        bodies.push_back({ N, property });
        lookup[property.naif] = bodies.size() - 1;
    }

private:
    using BaseCollection<RockSnapShotWithVel>::push_back;
};

template <template <typename> class T> struct RocksAndShips {
    Collection<T<RockLike>> system;
    Collection<T<ShipLike>> fleet;
};
*/

template <typename T>
inline std::optional<size_t>
find(const std::vector<T>& bodies, const NAIFbody& naif)
{
    for (size_t i = 0; i < bodies.size(); i++) {
        if (bodies[i].property.naif == naif) {
            return i;
        }
    }
    return {};
}
}
<commit_msg>Fixed template specialization<commit_after>/*
This file is part of Groho, a simulator for inter-planetary travel and warfare.
Copyright (c) 2017-2018 by Kaushik Ghose. Some rights reserved, see LICENSE

We have two types of objects in the simulation - Orrery bodies
(planets/moons/asteroid) and spaceships. They behave quite differently and
are described as separate classes of objects with some elements in common

*/

#pragma once

#include <cstdint>
#include <optional>
#include <string>
#include <unordered_map>
#include <vector>

#include "naifbody.hpp"
#include "vector.hpp"

namespace sim {

struct RockLike {

    struct Property {
        NAIFbody naif;

        float GM; // GM value for body
        float r;  // Radius of body (for collision tests)

        uint32_t color; // For display purposes
    };

    struct State {
        Vector pos; // Position referenced to solar system barycenter
        double t_s;
    };
};

struct ShipLike {

    struct Property {
        NAIFbody naif;

        float max_acc;   // Maximum acceleration possible for ship m/s^2
        float max_fuel;  // Maximum fuel reserve (U)
        float burn_rate; // Fuel consumption rate (U/ (m/s^2))

        uint32_t color; // For display purposes
    };

    struct State {
        Vector pos;  // Position referenced to solar system barycenter
        Vector vel;  // Velocity
        Vector att;  // Attitude
        float  fuel; // Current fuel reserve (U)
        float  acc;  // Current acceleration km/s^2
        double t_s;
    };
};

template <typename T> struct SnapShot {
    typename T::Property property;
    typename T::State    state;
};

// This allows us to do lazy computation of rock velocities.
// Time will tell if we were too clever here
template <typename T> struct SnapShotV {

    SnapShotV(int& N, typename T::Property _property)
        : _N(N)
    {
        property = _property;
    }

    typename T::Property property;
    typename T::State    _state[2];

    double& t_s() { return _state[_N].t_s; }
    double  t_s() const { return _state[_N].t_s; }

    Vector&       pos() { return _state[_N].pos; }
    const Vector& pos() const { return _state[_N].pos; }
    Vector        vel() const
    {
        return (_state[_N].pos - _state[1 - _N].pos)
            / (_state[_N].t_s - _state[1 - _N].t_s);
    }

    const typename T::State& state() const { return _state[_N]; }

    int& _N;
};

template <typename T> struct BaseCollection {
    std::vector<T>                       bodies;
    std::unordered_map<NAIFbody, size_t> lookup;

    size_t size() const { return bodies.size(); }

    T& operator[](size_t idx) { return bodies[idx]; }
    T& operator[](const NAIFbody& id) { return bodies[lookup[id]]; }

    const T& operator[](size_t idx) const { return bodies[idx]; }
    const T& operator[](const NAIFbody& id) const { return bodies[lookup[id]]; }

    virtual void push_back(const T& body)
    {
        bodies.push_back(body);
        lookup[body.property.naif] = bodies.size() - 1;
    }
};

template <typename T> struct Collection : public BaseCollection<T> {
};

template <typename T>
struct Collection<SnapShotV<T>> : public BaseCollection<SnapShotV<T>> {

    using BaseCollection<SnapShotV<T>>::bodies;
    using BaseCollection<SnapShotV<T>>::lookup;

    int N = 0;

    void push_back(const typename T::Property& property)
    {
        bodies.push_back({ N, property });
        lookup[property.naif] = bodies.size() - 1;
    }

private:
    using BaseCollection<SnapShotV<T>>::push_back;
};

template <template <typename> class Trock, template <typename> class Tship>
struct RocksAndShips {
    Collection<Trock<RockLike>> system;
    Collection<Tship<ShipLike>> fleet;
};

/*
// TODO: deprecate
// This allows us to do lazy computation of rock velocities.
// Time will tell if we were too clever here
struct RockSnapShotWithVel {

    RockSnapShotWithVel(int& N, RockLike::Property _property)
        : _N(N)
    {
        property = _property;
    }

    RockLike::Property property;
    RockLike::State    _state[2];

    double& t_s() { return _state[_N].t_s; }
    double  t_s() const { return _state[_N].t_s; }

    Vector&       pos() { return _state[_N].pos; }
    const Vector& pos() const { return _state[_N].pos; }
    Vector        vel() const
    {
        return (_state[_N].pos - _state[1 - _N].pos)
            / (_state[_N].t_s - _state[1 - _N].t_s);
    }

    constexpr const RockLike::State& state() const { return _state[_N]; }

    int& _N;
};

template <typename T> struct BaseCollection {
    std::vector<T>                       bodies;
    std::unordered_map<NAIFbody, size_t> lookup;

    constexpr size_t size() const { return bodies.size(); }

    constexpr T& operator[](size_t idx) { return bodies[idx]; }
    constexpr T& operator[](const NAIFbody& id) { return bodies[lookup[id]]; }

    constexpr const T& operator[](size_t idx) const { return bodies[idx]; }

    virtual void push_back(const T& body)
    {
        bodies.push_back(body);
        lookup[body.property.naif] = bodies.size() - 1;
    }
};

template <typename T> struct Collection : public BaseCollection<T> {
};

template <>
struct Collection<RockSnapShotWithVel>
    : public BaseCollection<RockSnapShotWithVel> {

    int N = 0;

    void push_back(const RockLike::Property& property)
    {
        bodies.push_back({ N, property });
        lookup[property.naif] = bodies.size() - 1;
    }

private:
    using BaseCollection<RockSnapShotWithVel>::push_back;
};

template <template <typename> class T> struct RocksAndShips {
    Collection<T<RockLike>> system;
    Collection<T<ShipLike>> fleet;
};
*/

template <typename T>
inline std::optional<size_t>
find(const std::vector<T>& bodies, const NAIFbody& naif)
{
    for (size_t i = 0; i < bodies.size(); i++) {
        if (bodies[i].property.naif == naif) {
            return i;
        }
    }
    return {};
}
}
<|endoftext|>


<commit_before>/**
 ** \file scheduler/scheduler.cc
 ** \brief Implementation of scheduler::Scheduler.
 */

//#define ENABLE_DEBUG_TRACES

#include <algorithm>
#include <cassert>
#include <cstdlib>

#include <libport/compiler.hh>
#include <libport/containers.hh>
#include <libport/contract.hh>
#include <libport/foreach.hh>

#include <object/urbi-exception.hh>

#include <scheduler/scheduler.hh>
#include <scheduler/job.hh>

namespace scheduler
{

  Scheduler::Scheduler(boost::function0<libport::utime_t> get_time)
    : get_time_(get_time)
    , current_job_(0)
    , possible_side_effect_(true)
    , cycle_(0)
    , ready_to_die_(false)
    , real_time_behaviour_(false)
  {
    ECHO("Initializing main coroutine");
    coroutine_initialize_main(&coro_);
  }

  Scheduler::~Scheduler()
  {
    ECHO("Destroying scheduler");
  }

  // This function is required to start a new job using the libcoroutine.
  // Its only purpose is to create the context and start the execution
  // of the new job.
  static void
  run_job(Job* job)
  {
    job->run();
  }

  void
  Scheduler::add_job(rJob job)
  {
    assert(job);
    assert(!libport::has(jobs_, job));
    assert(!libport::has(pending_, job));
    // If we are currently in a job, add it to the pending_ queue so that
    // the job is started in the course of the current round. To make sure
    // that it is not started too late even if the creator is located after
    // the job that is causing the creation (think "at job handler" for
    // example), insert it right before the job which was right after the
    // current one. This way, jobs inserted successively will get queued
    // in the right order.
    if (current_job_)
      pending_.insert(next_job_p_, job);
    else
      jobs_.push_back(job);
  }

  libport::utime_t
  Scheduler::work()
  {
    ++cycle_;
    ECHO("======================================================== cycle "
	  << cycle_);

    libport::utime_t deadline = execute_round();

#ifdef ENABLE_DEBUG_TRACES
    if (deadline)
      ECHO("Scheduler asking to be woken up in "
	    << (deadline - get_time_()) / 1000000L << " seconds");
    else
      ECHO("Scheduler asking to be woken up ASAP");
#endif

    return deadline;
  }

  static bool prio_gt(const rJob& j1, const rJob& j2)
  {
    return j2->prio_get() < j1->prio_get();
  }

  libport::utime_t
  Scheduler::execute_round()
  {
    // Run all the jobs in the run queue once. If any job declares upon entry or
    // return that it is not side-effect free, we remember that for the next
    // cycle.
    pending_.clear();
    std::swap(pending_, jobs_);

    // Sort all the jobs according to their priority.
    if (real_time_behaviour_)
    {
      static std::vector<rJob> tmp;
      tmp.reserve(pending_.size());
      tmp.clear();
      foreach(rJob job, pending_)
	tmp.push_back(job);
      std::stable_sort(tmp.begin(), tmp.end(), prio_gt);
      pending_.clear();
      foreach(rJob job, tmp)
	pending_.push_back(job);
    }

    // By default, wake us up after one hour and consider that we have no
    // new job to start. Also, run waiting jobs only if the previous round
    // may have add a side effect and reset this indication for the current
    // job.
    libport::utime_t start_time = get_time_();
    libport::utime_t deadline = start_time +  3600000000LL;
    bool start_waiting = possible_side_effect_;
    possible_side_effect_ = false;
    bool at_least_one_started = false;

    ECHO(pending_.size() << " jobs in the queue for this round");

    // Do not use libport::foreach here, as the list of jobs may grow if
    // add_job() is called during the iteration.
    for (jobs_type::iterator job_p = pending_.begin();
	 job_p != pending_.end();
	 ++job_p)
    {
      rJob& job = *job_p;
      // Store the next job so that insertions happen before it.
      next_job_p_ = job_p;
      ++next_job_p_;
      // If the job has terminated during the previous round, remove the
      // references we have on it by just skipping it.
      if (job->terminated())
	continue;

      // Should the job be started?
      bool start = false;

      // Save the current time since we will use it several times during this job
      // analysis.
      libport::utime_t current_time = get_time_();

      ECHO("Considering " << *job << " in state " << state_name(job->state_get()));

      switch (job->state_get())
      {
      case to_start:
      {
        // New job. Start its coroutine but do not start the job as it
        // would be queued twice otherwise. It will start doing real
        // work at the next cycle, so set deadline to 0. Note that we
        // use "continue" here to avoid having the job requeued
        // because it hasn't been started by setting "start".
        //
        // The code below takes care of destroying the rJob reference
        // to the job, so that it does not stay in the call stack as a
        // local variable. If it did, the job would never be
        // destroyed. However, to prevent the job from being
        // prematurely destroyed, we set current_job_ (global to the
        // scheduler) to the rJob.
	ECHO("Starting job " << *job);
	current_job_ = job;
	ECHO("Job " << *job << " is starting");
	job = 0;
	coroutine_start(&coro_,
                        current_job_->coro_get(), run_job, current_job_.get());
	current_job_ = 0;
	deadline = SCHED_IMMEDIATE;
	at_least_one_started = true;
	continue;
      }
      case zombie:
	abort();
	break;
      case running:
	start = true;
	break;
      case sleeping:
	{
	  libport::utime_t job_deadline = job->deadline_get();

	  // If the job has been frozen, extend its deadline by the
	  // corresponding amount of time. The deadline will be adjusted
	  // later when the job is unfrozen using notice_not_frozen(),
	  // but in the meantime we do not want to cause an early wakeup.
	  if (libport::utime_t frozen_since = job->frozen_since_get())
	    job_deadline += current_time - frozen_since;

	  if (job_deadline <= current_time)
	    start = true;
	  else
	    deadline = std::min(deadline, job_deadline);
	}
	break;
      case waiting:
	// Since jobs keep their orders in the queue, start waiting jobs if
	// previous jobs in the run have had a possible side effect or if
	// the previous run may have had some. Without it, we may miss some
	// changes if the watching job is after the modifying job in the queue
	// and the watched condition gets true for only one cycle.
	start = start_waiting || possible_side_effect_;
	break;
      case joining:
	break;
      }

      // Tell the job whether it is frozen or not so that it can remember
      // since when it has been in this state.
      if (job->frozen())
	job->notice_frozen(current_time);
      else
	job->notice_not_frozen(current_time);

      // A job with an exception will start unconditionally.
      if (start || job->has_pending_exception())
      {
	at_least_one_started = true;
	ECHO("will resume job " << *job
	      << (job->side_effect_free_get() ? " (side-effect free)" : ""));
	possible_side_effect_ |= !job->side_effect_free_get();
	assert(!current_job_);
	coroutine_switch_to(&coro_, job->coro_get());
	assert(current_job_);
	current_job_ = 0;
	possible_side_effect_ |= !job->side_effect_free_get();
	ECHO("back from job " << *job
	      << (job->side_effect_free_get() ? " (side-effect free)" : ""));
	switch (job->state_get())
	{
	case running:
	  deadline = SCHED_IMMEDIATE;
	  break;
	case sleeping:
	  deadline = std::min(deadline, job->deadline_get());
	  break;
	default:
	  break;
	}
      }
      else
	jobs_.push_back(job);   // Job not started, keep it in queue
    }

    // If during this cycle a new job has been created by an existing job,
    // start it. Also start if a possible side effect happened, it may have
    // occurred later then the waiting jobs in the cycle.
    if (possible_side_effect_)
      deadline = SCHED_IMMEDIATE;

    // If we are ready to die and there are no jobs left, then die.
    if (ready_to_die_ && jobs_.empty())
      deadline = SCHED_EXIT;

    // Compute statistics
    if (at_least_one_started)
      stats_.add_sample(get_time_() - start_time);

    return deadline;
  }

  void
  Scheduler::resume_scheduler(rJob job)
  {
    // If the job has not terminated and is side-effect free, then we
    // assume it will not take a long time as we are probably evaluating
    // a condition. In order to reduce the number of cycles spent to evaluate
    // the condition, continue until it asks to be suspended in another
    // way or until it is no longer side-effect free.

    bool side_effect_free_save = job->side_effect_free_get();

    if (job->state_get() == running && side_effect_free_save)
      return;

    if (job->state_get() == running &&
	job->prio_get() >= UPRIO_RT_MIN &&
	!job->frozen())
      return;

    // We may have to suspend the job several time in case it makes no sense
    // to start it back. Let's do it in a loop and we'll break when we want
    // to resume the job.

    while (true)
    {
      // Add the job at the end of the scheduler queue unless the job has
      // already terminated.
      if (!job->terminated())
	jobs_.push_back(job);

      // Switch back to the scheduler. But in the case this job has been
      // destroyed, erase the local variable first so that it doesn't keep
      // a reference on it which will never be destroyed.
      assert(current_job_ == job);
      ECHO(*job << " has " << (job->terminated() ? "" : "not ") << "terminated\n\t"
	   << "state: " << state_name(job->state_get()) << std::endl);
      Coro* current_coro = job->coro_get();
      if (job->terminated())
	job = 0;
      coroutine_switch_to(current_coro, &coro_);

      // If we regain control, we are not dead.
      assert(job);

      // We regained control, we are again in the context of the job.
      assert(!current_job_);
      current_job_ = job;
      ECHO("job " << *job << " resumed");

      // Execute a deferred exception if any; this may break out of this loop
      job->check_for_pending_exception();

      // If we are not frozen, it is time to resume regular execution
      if (!job->frozen())
	break;

      // Ok, we are frozen. Let's requeue ourselves after setting
      // the side_effect_free flag, and we will be in waiting mode.
      job->side_effect_free_set(true);
      job->state_set(waiting);
    }

    // Check that we are not near exhausting the stack space.
    job->check_stack_space();

    // Restore the side_effect_free flag
    job->side_effect_free_set(side_effect_free_save);

    // Resume job execution
  }

  void
  Scheduler::killall_jobs()
  {
    ECHO("killing all jobs!");

    // Mark the scheduler as ready to die when all the jobs are
    // really dead.
    ready_to_die_ = true;

    // Since killing the current job will result in its immediate
    // termination, kill all other jobs before.
    foreach (const rJob& job, jobs_get())
      if (job != current_job_)
	job->terminate_now();
    if (current_job_)
      current_job_->terminate_now();
  }

  void
  Scheduler::signal_stop(const Tag& tag, const boost::any& payload)
  {
    // Tell the jobs that a tag has been stopped, ending with
    // the current job to avoid interrupting this method early.
    foreach (const rJob& job, jobs_get())
    {
      // Job started at this cycle, reset to avoid stack references.
      if (!job)
	continue;
      // Job to be started during this cycle.
      if (job->state_get() == to_start)
      {
	// Check if this job deserves to be removed.
	foreach (const rTag& t, job->tags_get())
	  if (t->derives_from(tag))
	  {
	    pending_.remove(job);
	    continue;
	  }
      }
      // Any other non-current job.
      else if (job != current_job_)
	job->register_stopped_tag(tag, payload);
    }
    if (current_job_)
      current_job_->register_stopped_tag(tag, payload);
  }

  jobs_type
  Scheduler::jobs_get() const
  {
    // If this method is called from within a job, return the currently
    // executing jobs (pending_), otherwise return the jobs_ content which
    // is complete.
    return current_job_ ? pending_ : jobs_;
  }

  const scheduler_stats_type&
  Scheduler::stats_get() const
  {
    return stats_;
  }

} // namespace scheduler
<commit_msg>Do not store 0 in jobs list.<commit_after>/**
 ** \file scheduler/scheduler.cc
 ** \brief Implementation of scheduler::Scheduler.
 */

//#define ENABLE_DEBUG_TRACES

#include <algorithm>
#include <cassert>
#include <cstdlib>

#include <libport/compiler.hh>
#include <libport/containers.hh>
#include <libport/contract.hh>
#include <libport/foreach.hh>

#include <object/urbi-exception.hh>

#include <scheduler/scheduler.hh>
#include <scheduler/job.hh>

namespace scheduler
{

  Scheduler::Scheduler(boost::function0<libport::utime_t> get_time)
    : get_time_(get_time)
    , current_job_(0)
    , possible_side_effect_(true)
    , cycle_(0)
    , ready_to_die_(false)
    , real_time_behaviour_(false)
  {
    ECHO("Initializing main coroutine");
    coroutine_initialize_main(&coro_);
  }

  Scheduler::~Scheduler()
  {
    ECHO("Destroying scheduler");
  }

  // This function is required to start a new job using the libcoroutine.
  // Its only purpose is to create the context and start the execution
  // of the new job.
  static void
  run_job(Job* job)
  {
    job->run();
  }

  void
  Scheduler::add_job(rJob job)
  {
    assert(job);
    assert(!libport::has(jobs_, job));
    assert(!libport::has(pending_, job));
    // If we are currently in a job, add it to the pending_ queue so that
    // the job is started in the course of the current round. To make sure
    // that it is not started too late even if the creator is located after
    // the job that is causing the creation (think "at job handler" for
    // example), insert it right before the job which was right after the
    // current one. This way, jobs inserted successively will get queued
    // in the right order.
    if (current_job_)
      pending_.insert(next_job_p_, job);
    else
      jobs_.push_back(job);
  }

  libport::utime_t
  Scheduler::work()
  {
    ++cycle_;
    ECHO("======================================================== cycle "
	  << cycle_);

    libport::utime_t deadline = execute_round();

#ifdef ENABLE_DEBUG_TRACES
    if (deadline)
      ECHO("Scheduler asking to be woken up in "
	    << (deadline - get_time_()) / 1000000L << " seconds");
    else
      ECHO("Scheduler asking to be woken up ASAP");
#endif

    return deadline;
  }

  static bool prio_gt(const rJob& j1, const rJob& j2)
  {
    return j2->prio_get() < j1->prio_get();
  }

  libport::utime_t
  Scheduler::execute_round()
  {
    // Run all the jobs in the run queue once. If any job declares upon entry or
    // return that it is not side-effect free, we remember that for the next
    // cycle.
    pending_.clear();
    std::swap(pending_, jobs_);

    // Sort all the jobs according to their priority.
    if (real_time_behaviour_)
    {
      static std::vector<rJob> tmp;
      tmp.reserve(pending_.size());
      tmp.clear();
      foreach(rJob job, pending_)
	tmp.push_back(job);
      std::stable_sort(tmp.begin(), tmp.end(), prio_gt);
      pending_.clear();
      foreach(rJob job, tmp)
	pending_.push_back(job);
    }

    // By default, wake us up after one hour and consider that we have no
    // new job to start. Also, run waiting jobs only if the previous round
    // may have add a side effect and reset this indication for the current
    // job.
    libport::utime_t start_time = get_time_();
    libport::utime_t deadline = start_time +  3600000000LL;
    bool start_waiting = possible_side_effect_;
    possible_side_effect_ = false;
    bool at_least_one_started = false;

    ECHO(pending_.size() << " jobs in the queue for this round");

    // Do not use libport::foreach here, as the list of jobs may grow if
    // add_job() is called during the iteration.
    for (jobs_type::iterator job_p = pending_.begin();
	 job_p != pending_.end();
	 ++job_p)
    {
      rJob job = *job_p;
      // Store the next job so that insertions happen before it.
      next_job_p_ = job_p;
      ++next_job_p_;
      // If the job has terminated during the previous round, remove the
      // references we have on it by just skipping it.
      if (job->terminated())
	continue;

      // Should the job be started?
      bool start = false;

      // Save the current time since we will use it several times during this job
      // analysis.
      libport::utime_t current_time = get_time_();

      ECHO("Considering " << *job << " in state " << state_name(job->state_get()));

      switch (job->state_get())
      {
      case to_start:
      {
        // New job. Start its coroutine but do not start the job as it
        // would be queued twice otherwise. It will start doing real
        // work at the next cycle, so set deadline to 0. Note that we
        // use "continue" here to avoid having the job requeued
        // because it hasn't been started by setting "start".
        //
        // The code below takes care of destroying the rJob reference
        // to the job, so that it does not stay in the call stack as a
        // local variable. If it did, the job would never be
        // destroyed. However, to prevent the job from being
        // prematurely destroyed, we set current_job_ (global to the
        // scheduler) to the rJob.
	ECHO("Starting job " << *job);
	current_job_ = job;
	ECHO("Job " << *job << " is starting");
	job = 0;
	coroutine_start(&coro_,
                        current_job_->coro_get(), run_job, current_job_.get());
	current_job_ = 0;
	deadline = SCHED_IMMEDIATE;
	at_least_one_started = true;
	continue;
      }
      case zombie:
	abort();
	break;
      case running:
	start = true;
	break;
      case sleeping:
	{
	  libport::utime_t job_deadline = job->deadline_get();

	  // If the job has been frozen, extend its deadline by the
	  // corresponding amount of time. The deadline will be adjusted
	  // later when the job is unfrozen using notice_not_frozen(),
	  // but in the meantime we do not want to cause an early wakeup.
	  if (libport::utime_t frozen_since = job->frozen_since_get())
	    job_deadline += current_time - frozen_since;

	  if (job_deadline <= current_time)
	    start = true;
	  else
	    deadline = std::min(deadline, job_deadline);
	}
	break;
      case waiting:
	// Since jobs keep their orders in the queue, start waiting jobs if
	// previous jobs in the run have had a possible side effect or if
	// the previous run may have had some. Without it, we may miss some
	// changes if the watching job is after the modifying job in the queue
	// and the watched condition gets true for only one cycle.
	start = start_waiting || possible_side_effect_;
	break;
      case joining:
	break;
      }

      // Tell the job whether it is frozen or not so that it can remember
      // since when it has been in this state.
      if (job->frozen())
	job->notice_frozen(current_time);
      else
	job->notice_not_frozen(current_time);

      // A job with an exception will start unconditionally.
      if (start || job->has_pending_exception())
      {
	at_least_one_started = true;
	ECHO("will resume job " << *job
	      << (job->side_effect_free_get() ? " (side-effect free)" : ""));
	possible_side_effect_ |= !job->side_effect_free_get();
	assert(!current_job_);
	coroutine_switch_to(&coro_, job->coro_get());
	assert(current_job_);
	current_job_ = 0;
	possible_side_effect_ |= !job->side_effect_free_get();
	ECHO("back from job " << *job
	      << (job->side_effect_free_get() ? " (side-effect free)" : ""));
	switch (job->state_get())
	{
	case running:
	  deadline = SCHED_IMMEDIATE;
	  break;
	case sleeping:
	  deadline = std::min(deadline, job->deadline_get());
	  break;
	default:
	  break;
	}
      }
      else
	jobs_.push_back(job);   // Job not started, keep it in queue
    }

    // If during this cycle a new job has been created by an existing job,
    // start it. Also start if a possible side effect happened, it may have
    // occurred later then the waiting jobs in the cycle.
    if (possible_side_effect_)
      deadline = SCHED_IMMEDIATE;

    // If we are ready to die and there are no jobs left, then die.
    if (ready_to_die_ && jobs_.empty())
      deadline = SCHED_EXIT;

    // Compute statistics
    if (at_least_one_started)
      stats_.add_sample(get_time_() - start_time);

    return deadline;
  }

  void
  Scheduler::resume_scheduler(rJob job)
  {
    // If the job has not terminated and is side-effect free, then we
    // assume it will not take a long time as we are probably evaluating
    // a condition. In order to reduce the number of cycles spent to evaluate
    // the condition, continue until it asks to be suspended in another
    // way or until it is no longer side-effect free.

    bool side_effect_free_save = job->side_effect_free_get();

    if (job->state_get() == running && side_effect_free_save)
      return;

    if (job->state_get() == running &&
	job->prio_get() >= UPRIO_RT_MIN &&
	!job->frozen())
      return;

    // We may have to suspend the job several time in case it makes no sense
    // to start it back. Let's do it in a loop and we'll break when we want
    // to resume the job.

    while (true)
    {
      // Add the job at the end of the scheduler queue unless the job has
      // already terminated.
      if (!job->terminated())
	jobs_.push_back(job);

      // Switch back to the scheduler. But in the case this job has been
      // destroyed, erase the local variable first so that it doesn't keep
      // a reference on it which will never be destroyed.
      assert(current_job_ == job);
      ECHO(*job << " has " << (job->terminated() ? "" : "not ") << "terminated\n\t"
	   << "state: " << state_name(job->state_get()) << std::endl);
      Coro* current_coro = job->coro_get();
      if (job->terminated())
	job = 0;
      coroutine_switch_to(current_coro, &coro_);

      // If we regain control, we are not dead.
      assert(job);

      // We regained control, we are again in the context of the job.
      assert(!current_job_);
      current_job_ = job;
      ECHO("job " << *job << " resumed");

      // Execute a deferred exception if any; this may break out of this loop
      job->check_for_pending_exception();

      // If we are not frozen, it is time to resume regular execution
      if (!job->frozen())
	break;

      // Ok, we are frozen. Let's requeue ourselves after setting
      // the side_effect_free flag, and we will be in waiting mode.
      job->side_effect_free_set(true);
      job->state_set(waiting);
    }

    // Check that we are not near exhausting the stack space.
    job->check_stack_space();

    // Restore the side_effect_free flag
    job->side_effect_free_set(side_effect_free_save);

    // Resume job execution
  }

  void
  Scheduler::killall_jobs()
  {
    ECHO("killing all jobs!");

    // Mark the scheduler as ready to die when all the jobs are
    // really dead.
    ready_to_die_ = true;

    // Since killing the current job will result in its immediate
    // termination, kill all other jobs before.
    foreach (const rJob& job, jobs_get())
      if (job != current_job_)
	job->terminate_now();
    if (current_job_)
      current_job_->terminate_now();
  }

  void
  Scheduler::signal_stop(const Tag& tag, const boost::any& payload)
  {
    // Tell the jobs that a tag has been stopped, ending with
    // the current job to avoid interrupting this method early.
    foreach (const rJob& job, jobs_get())
    {
      // The current job will be handled last.
      if (job == current_job_)
	continue;
      // Job to be started during this cycle.
      if (job->state_get() == to_start)
      {
	// Check if this job deserves to be removed.
	foreach (const rTag& t, job->tags_get())
	  if (t->derives_from(tag))
	  {
	    pending_.remove(job);
	    continue;
	  }
      }
      // Any other job.
      else
	job->register_stopped_tag(tag, payload);
    }
    // Handle the current job situation.
    if (current_job_)
      current_job_->register_stopped_tag(tag, payload);
  }

  jobs_type
  Scheduler::jobs_get() const
  {
    // If this method is called from within a job, return the currently
    // executing jobs (pending_), otherwise return the jobs_ content which
    // is complete.
    return current_job_ ? pending_ : jobs_;
  }

  const scheduler_stats_type&
  Scheduler::stats_get() const
  {
    return stats_;
  }

} // namespace scheduler
<|endoftext|>


<commit_before>#include <groupby.hpp>

#include <vector>
#include <iostream>
#include <string>

using iter::groupby;

int length(std::string s)
{
    return s.length();
}

int main()
{
    std::vector<std::string> vec = {
        "hi", "ab", "ho",
        "abc", "def",
        "abcde", "efghi"
    };

    for (auto gb : groupby(vec, &length)) {
        std::cout << "key: " << gb.first << '\n';
        std::cout << "content: ";
        for (auto s : gb.second) {
            std::cout << s << "  ";
        }
        std::cout << '\n';
    }

    std::cout << "skipping length of 3\n";
    for (auto gb : groupby(vec, &length)) {
        if (gb.first == 3) {
            continue;
        }
        std::cout << "key: " << gb.first << '\n';
        std::cout << "content: ";
        for (auto s : gb.second) {
            std::cout << s << "  ";
        }
        std::cout << '\n';
    }

    std::vector<int> ivec = {5, 5, 6, 6, 19, 19, 19, 19, 69, 0, 10, 10};
    for (auto gb : groupby(ivec)) {
        std::cout << "key: " << gb.first << '\n';
        std::cout << "content: ";
        for (auto s : gb.second) {
            std::cout << s << "  ";
        }
        std::cout << '\n';
    }

    for (auto gb : groupby("aabbccccdd", [] (const char c) {return c < 'c';})){
        std::cout << "key: " << gb.first << '\n';
        std::cout << "content: ";
        for (auto s : gb.second) {
            std::cout << s << "  ";
        }
        std::cout << '\n';
    }

    for (auto gb : groupby({'a', 'a', 'b', 'b', 'c'})) {
        std::cout << "key: " << gb.first << '\n';
        std::cout << "content: ";
        for (auto s : gb.second) {
            std::cout << s << "  ";
        }
        std::cout << '\n';
    }

    return 0;
}

<commit_msg>Adds test with initializer_list and defalt Key<commit_after>#include <groupby.hpp>

#include <vector>
#include <iostream>
#include <string>

using iter::groupby;

int length(std::string s)
{
    return s.length();
}

int main()
{
    std::vector<std::string> vec = {
        "hi", "ab", "ho",
        "abc", "def",
        "abcde", "efghi"
    };

    for (auto gb : groupby(vec, &length)) {
        std::cout << "key: " << gb.first << '\n';
        std::cout << "content: ";
        for (auto s : gb.second) {
            std::cout << s << "  ";
        }
        std::cout << '\n';
    }

    std::cout << "skipping length of 3\n";
    for (auto gb : groupby(vec, &length)) {
        if (gb.first == 3) {
            continue;
        }
        std::cout << "key: " << gb.first << '\n';
        std::cout << "content: ";
        for (auto s : gb.second) {
            std::cout << s << "  ";
        }
        std::cout << '\n';
    }

    std::vector<int> ivec = {5, 5, 6, 6, 19, 19, 19, 19, 69, 0, 10, 10};
    for (auto gb : groupby(ivec)) {
        std::cout << "key: " << gb.first << '\n';
        std::cout << "content: ";
        for (auto s : gb.second) {
            std::cout << s << "  ";
        }
        std::cout << '\n';
    }

    for (auto gb : groupby("aabbccccdd", [] (const char c) {return c < 'c';})){
        std::cout << "key: " << gb.first << '\n';
        std::cout << "content: ";
        for (auto s : gb.second) {
            std::cout << s << "  ";
        }
        std::cout << '\n';
    }

    for (auto gb : groupby({'a', 'a', 'b', 'b', 'c'})) {
        std::cout << "key: " << gb.first << '\n';
        std::cout << "content: ";
        for (auto s : gb.second) {
            std::cout << s << "  ";
        }
        std::cout << '\n';
    }

    for (auto gb : groupby({'a', 'a', 'b', 'b', 'c', 'c', 'd', 'd'},
                [] (const char c) {return c < 'c'; })) {
        std::cout << "key: " << gb.first << '\n';
        std::cout << "content: ";
        for (auto s : gb.second) {
            std::cout << s << "  ";
        }
        std::cout << '\n';
    }

    return 0;
}

<|endoftext|>


<commit_before>#define BOOST_TEST_MODULE VectorView
#include <boost/test/unit_test.hpp>
#include "context_setup.hpp"

BOOST_AUTO_TEST_CASE(vector_view)
{
    const size_t N = 1024;

    std::vector<cl::CommandQueue> queue(1, ctx.queue(0));

    std::vector<double> x = random_vector<double>(2 * N);
    vex::vector<double> X(queue, x);
    vex::vector<double> Y(queue, N);

    cl_ulong size   = N;
    cl_long  stride = 2;

    vex::gslice<1> slice(0, &size, &stride);

    Y = slice(X);

    check_sample(Y, [&](size_t idx, double v) { BOOST_CHECK(v == x[idx * 2]); });
}

BOOST_AUTO_TEST_SUITE_END()
<commit_msg>Added test for 2D slicing<commit_after>#define BOOST_TEST_MODULE VectorView
#include <valarray>
#include <boost/test/unit_test.hpp>
#include "context_setup.hpp"

BOOST_AUTO_TEST_CASE(vector_view_1d)
{
    const size_t N = 1024;

    std::vector<cl::CommandQueue> queue(1, ctx.queue(0));

    std::vector<double> x = random_vector<double>(2 * N);
    vex::vector<double> X(queue, x);
    vex::vector<double> Y(queue, N);

    cl_ulong size   = N;
    cl_long  stride = 2;

    vex::gslice<1> slice(0, &size, &stride);

    Y = slice(X);

    check_sample(Y, [&](size_t idx, double v) { BOOST_CHECK(v == x[idx * 2]); });
}

BOOST_AUTO_TEST_CASE(vector_view_2)
{
    const size_t N = 32;

    std::vector<cl::CommandQueue> queue(1, ctx.queue(0));

    std::valarray<double> x(N * N);
    std::iota(&x[0], &x[N * N], 0);

    // Select every even point from sub-block [(2,4) - (10,10)]:
    size_t start    = 2 * N + 4;
    size_t size[]   = {5, 4};
    size_t stride[] = {2, 2};

    std::gslice std_slice(start, std::valarray<size_t>(size, 2), std::valarray<size_t>(stride, 2));

    std::valarray<double> y = x[std_slice];

    vex::vector<double> X(queue, N * N, &x[0]);
    vex::vector<double> Y(queue, size[0] * size[1]);

    vex::gslice<2> vex_slice(start, size, stride);

    Y = vex_slice(X);

    check_sample(Y, [&](size_t idx, double v) { BOOST_CHECK_EQUAL(v, y[idx]); });
}

BOOST_AUTO_TEST_SUITE_END()
<|endoftext|>


<commit_before>#include <eeros/core/PeriodicThread.hpp>

#include <sched.h>
#include <time.h>
#include <sys/mman.h>

#define RT_PRIORITY (49) /* we use 49 as the PRREMPT_RT use 50 as the priority of kernel tasklets and interrupt handler by default */
#define MAX_SAFE_STACK (8*1024) /* The maximum stack size which is guaranteed safe to access without faulting */

using namespace eeros;

void stack_prefault() {
	unsigned char dummy[MAX_SAFE_STACK] = {};
}

PeriodicThread::PeriodicThread(double period, double delay, bool realtime, status start, int priority) :
	counter(period),
	rt(realtime),
	period(period),
	delay(delay),
	s(start),
	Thread([this, priority]() {
		struct timespec time;
		uint64_t period_ns = to_ns(this->period);
		std::string id = getId();

		log.trace() << "Periodic thread '" << id << "' started.";

		int r = clock_gettime(CLOCK_MONOTONIC, &time);
		if (r != 0) {
			log.error() << "failed to get time " << errno;
			return;
		}
		time.tv_nsec += to_ns(this->delay);

		if(this->rt) {
			log.trace() << "Periodic thread '" << id << "' configured for realtime scheduling.";
			struct sched_param schedulingParam;
			schedulingParam.sched_priority = RT_PRIORITY + priority; // TODO use sched_get_priority_max
			if(sched_setscheduler(0, SCHED_FIFO, &schedulingParam) == -1) { // TODO add support for SCHED_DEADLINE
				log.error() << "Periodic thread '" << id << "': failed to set real time scheduler!";
			}

			/* Lock memory */
			if(mlockall(MCL_CURRENT | MCL_FUTURE) == -1) {
				log.error() << "Periodic thread '" << id << "': failed to lock memory allocation!";
			}

			/* Pre-fault our stack */
			stack_prefault(); // TODO should we use pthread_attr_setstacksize() ?
		}

		while(time.tv_nsec >= to_ns(1)) {
			time.tv_nsec -= to_ns(1);
			time.tv_sec++;
		}
		while(s != stopping) {
			do {
				r = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &time, NULL);
			} while (r == EINTR);
			if (r != 0) {
				log.error() << "failed to sleep " << r;
				break;
			}
			counter.tick();
			if(s != paused) this->run();
			counter.tock();
			time.tv_nsec += period_ns;
			while(time.tv_nsec >= to_ns(1)) {
				time.tv_nsec -= to_ns(1);
				time.tv_sec++;
			}
		}
		log.trace() << "Periodic thread '" << id << "' stopped.";
		s = stopped;
	}) {
}

PeriodicThread::~PeriodicThread() {
	stop();
	join();
}

PeriodicThread::status PeriodicThread::getStatus() const {
	return s;
}

double PeriodicThread::getPeriod() const {
	return period;
}

void PeriodicThread::start() {
	s = running;
}

void PeriodicThread::pause() {
	s = paused;
}

void PeriodicThread::stop() {
	s = stopping;
}
<commit_msg>error message fixed<commit_after>#include <eeros/core/PeriodicThread.hpp>

#include <sched.h>
#include <time.h>
#include <sys/mman.h>

#define RT_PRIORITY (49) /* we use 49 as the PRREMPT_RT use 50 as the priority of kernel tasklets and interrupt handler by default */
#define MAX_SAFE_STACK (8*1024) /* The maximum stack size which is guaranteed safe to access without faulting */

using namespace eeros;

void stack_prefault() {
	unsigned char dummy[MAX_SAFE_STACK] = {};
}

PeriodicThread::PeriodicThread(double period, double delay, bool realtime, status start, int priority) :
	counter(period),
	rt(realtime),
	period(period),
	delay(delay),
	s(start),
	Thread([this, priority]() {
		struct timespec time;
		uint64_t period_ns = to_ns(this->period);
		std::string id = getId();

		log.trace() << "Periodic thread '" << id << "' started.";

		int r = clock_gettime(CLOCK_MONOTONIC, &time);
		if (r != 0) {
			log.error() << "failed to get time " << errno;
			return;
		}
		time.tv_nsec += to_ns(this->delay);

		if(this->rt) {
			log.trace() << "Periodic thread '" << id << "' configured for realtime scheduling.";
			struct sched_param schedulingParam;
			schedulingParam.sched_priority = RT_PRIORITY + priority; // TODO use sched_get_priority_max
			if(sched_setscheduler(0, SCHED_FIFO, &schedulingParam) == -1) { // TODO add support for SCHED_DEADLINE
				log.error() << "Periodic thread '" << id << "': failed to set real time scheduler!";
			}

			/* Lock memory */
			if(mlockall(MCL_CURRENT | MCL_FUTURE) == -1) {
				log.error() << "Periodic thread '" << id << "': failed to lock memory in RAM!";
			}

			/* Pre-fault our stack */
			stack_prefault(); // TODO should we use pthread_attr_setstacksize() ?
		}

		while(time.tv_nsec >= to_ns(1)) {
			time.tv_nsec -= to_ns(1);
			time.tv_sec++;
		}
		while(s != stopping) {
			do {
				r = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &time, NULL);
			} while (r == EINTR);
			if (r != 0) {
				log.error() << "failed to sleep " << r;
				break;
			}
			counter.tick();
			if(s != paused) this->run();
			counter.tock();
			time.tv_nsec += period_ns;
			while(time.tv_nsec >= to_ns(1)) {
				time.tv_nsec -= to_ns(1);
				time.tv_sec++;
			}
		}
		log.trace() << "Periodic thread '" << id << "' stopped.";
		s = stopped;
	}) {
}

PeriodicThread::~PeriodicThread() {
	stop();
	join();
}

PeriodicThread::status PeriodicThread::getStatus() const {
	return s;
}

double PeriodicThread::getPeriod() const {
	return period;
}

void PeriodicThread::start() {
	s = running;
}

void PeriodicThread::pause() {
	s = paused;
}

void PeriodicThread::stop() {
	s = stopping;
}
<|endoftext|>


<commit_before>#include "../include/tile.hpp"

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

tile::tile():
    neighbors_(4, nullptr),
    adjacents_(4, nullptr),
    tile_color_(NEUTRAL_TILE_COLOR),
    tile_type_(tileType::NEUTRAL)
{}

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

tile::~tile()
{}

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

void tile::addFriend(tile *n, const dir direction)
{
    switch (direction)
    {
        case dir::UP:         neighbors_[0] = n; break;
        case dir::DOWN:       neighbors_[1] = n; break;
        case dir::LEFT:       neighbors_[2] = n; break;
        case dir::RIGHT:      neighbors_[3] = n; break;

        case dir::UP_LEFT:    adjacents_[0] = n; break;
        case dir::UP_RIGHT:   adjacents_[1] = n; break;
        case dir::DOWN_LEFT:  adjacents_[2] = n; break;
        case dir::DOWN_RIGHT: adjacents_[3] = n; break;

        default: break;
    }
}

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

tile* tile::getFriend(const dir direction) const
{
    switch (direction)
    {
        case dir::UP:         return neighbors_[0];
        case dir::DOWN:       return neighbors_[1];
        case dir::LEFT:       return neighbors_[2];
        case dir::RIGHT:      return neighbors_[3];

        case dir::UP_LEFT:    return adjacents_[0];
        case dir::UP_RIGHT:   return adjacents_[1];
        case dir::DOWN_LEFT:  return adjacents_[2];
        case dir::DOWN_RIGHT: return adjacents_[3];

        default: return nullptr;
    }
}

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

bool tile::isAdjacentTo(const tile *n) const
{
    for (const tile* i : adjacents_)
        if (i==n) return true;

    return false;
}

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

void tile::appendVertices(std::vector<ALLEGRO_VERTEX> &v, const double cx, const double cy, const double width, const double border, const double max_x, const double max_y) const
{
    // The tile is drawed using two triangles
    float border_px = border*width;
    float width_2   = width/2;

    ALLEGRO_VERTEX points[4] = {
        {float((cx - width_2)+border_px), float((cy + width_2)-border_px), 0.0f, 0.0f, 0.0f, tile_color_},
        {float((cx + width_2)-border_px), float((cy + width_2)-border_px), 0.0f, 0.0f, 0.0f, tile_color_},
        {float((cx + width_2)-border_px), float((cy - width_2)+border_px), 0.0f, 0.0f, 0.0f, tile_color_},
        {float((cx - width_2)+border_px), float((cy - width_2)+border_px), 0.0f, 0.0f, 0.0f, tile_color_}
    };

    v.push_back(points[0]);
    v.push_back(points[1]);
    v.push_back(points[3]);

    v.push_back(points[2]);
    v.push_back(points[1]);
    v.push_back(points[3]);
}

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

void tile::setType(const tileType type)
{
    tile_type_ = type;

    switch (type)
    {
        case tileType::WATER: tile_color_ = WATER_TILE_COLOR;   break;
        default:              tile_color_ = NEUTRAL_TILE_COLOR; break;
    }
}

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

bool tile::isBorder() const
{
    return isAdjacentTo(nullptr);
}

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

bool tile::isWater() const
{
    return tile_type_ == tileType::WATER;
}

////////////////////////////////////////////////////////////////////////////////
<commit_msg>Comprobar vertices<commit_after>#include "../include/tile.hpp"

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

tile::tile():
    neighbors_(4, nullptr),
    adjacents_(4, nullptr),
    tile_color_(NEUTRAL_TILE_COLOR),
    tile_type_(tileType::NEUTRAL)
{}

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

tile::~tile()
{}

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

void tile::addFriend(tile *n, const dir direction)
{
    switch (direction)
    {
        case dir::UP:         neighbors_[0] = n; break;
        case dir::DOWN:       neighbors_[1] = n; break;
        case dir::LEFT:       neighbors_[2] = n; break;
        case dir::RIGHT:      neighbors_[3] = n; break;

        case dir::UP_LEFT:    adjacents_[0] = n; break;
        case dir::UP_RIGHT:   adjacents_[1] = n; break;
        case dir::DOWN_LEFT:  adjacents_[2] = n; break;
        case dir::DOWN_RIGHT: adjacents_[3] = n; break;

        default: break;
    }
}

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

tile* tile::getFriend(const dir direction) const
{
    switch (direction)
    {
        case dir::UP:         return neighbors_[0];
        case dir::DOWN:       return neighbors_[1];
        case dir::LEFT:       return neighbors_[2];
        case dir::RIGHT:      return neighbors_[3];

        case dir::UP_LEFT:    return adjacents_[0];
        case dir::UP_RIGHT:   return adjacents_[1];
        case dir::DOWN_LEFT:  return adjacents_[2];
        case dir::DOWN_RIGHT: return adjacents_[3];

        default: return nullptr;
    }
}

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

bool tile::isAdjacentTo(const tile *n) const
{
    for (const tile* i : adjacents_)
        if (i==n) return true;

    return false;
}

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

void tile::appendVertices(std::vector<ALLEGRO_VERTEX> &v, const double cx, const double cy, const double width, const double border, const double max_x, const double max_y) const
{
    // The tile is drawed using two triangles
    float border_px = border*width;
    float width_2   = width/2;

    ALLEGRO_VERTEX points[4] = {
        {float((cx - width_2)+border_px), float((cy + width_2)-border_px), 0.0f, 0.0f, 0.0f, tile_color_},
        {float((cx + width_2)-border_px), float((cy + width_2)-border_px), 0.0f, 0.0f, 0.0f, tile_color_},
        {float((cx + width_2)-border_px), float((cy - width_2)+border_px), 0.0f, 0.0f, 0.0f, tile_color_},
        {float((cx - width_2)+border_px), float((cy - width_2)+border_px), 0.0f, 0.0f, 0.0f, tile_color_}
    };

    if (points[0].y >= 0 && points[1].y <= max_y && points[0].x <= max_x && points[3].x >= 0){
        v.push_back(points[0]);
        v.push_back(points[1]);
        v.push_back(points[3]);
    }
    if (points[2].y >= 0 && points[1].y <= max_y && points[2].x <= max_x && points[3].x >= 0){
        v.push_back(points[2]);
        v.push_back(points[1]);
        v.push_back(points[3]);
    }
}

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

void tile::setType(const tileType type)
{
    tile_type_ = type;

    switch (type)
    {
        case tileType::WATER: tile_color_ = WATER_TILE_COLOR;   break;
        default:              tile_color_ = NEUTRAL_TILE_COLOR; break;
    }
}

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

bool tile::isBorder() const
{
    return isAdjacentTo(nullptr);
}

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

bool tile::isWater() const
{
    return tile_type_ == tileType::WATER;
}

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


<commit_before>#include "ghost/config.h"
#include "ghost/types.h"
#include "ghost/densemat.h"
#include "ghost/util.h"
#include "ghost/math.h"
#include "ghost/tsmm.h"
#include "ghost/tsmm_gen.h"
#include "ghost/tsmm_avx_gen.h"
#include "ghost/tsmm_sse_gen.h"
#include "ghost/tsmm_cu_gen.h"
#include "ghost/timing.h"

#include <map>

using namespace std;

static bool operator<(const ghost_tsmm_parameters_t &a, const ghost_tsmm_parameters_t &b)
{
    return ghost_hash(a.dt,a.xcols,ghost_hash(a.vcols,a.impl,ghost_hash(a.xstor,a.wstor,a.alignment))) < ghost_hash(b.dt,b.xcols,ghost_hash(b.vcols,b.impl,ghost_hash(b.xstor,b.wstor,b.alignment)));
}

static map<ghost_tsmm_parameters_t, ghost_tsmm_kernel_t> ghost_tsmm_kernels;

ghost_error_t ghost_tsmm_valid(ghost_densemat_t *x, ghost_densemat_t *v,  const char * transv,
ghost_densemat_t *w, const char *transw, void *alpha, void *beta, int reduce, int printerror)
{
    if (x->traits.datatype != v->traits.datatype || x->traits.datatype != w->traits.datatype) {
        if (printerror) {
            ERROR_LOG("Different data types!");
        }
        return GHOST_ERR_INVALID_ARG;
    }
    if (x == v) {
        if (printerror) {
           ERROR_LOG("x must not be equal to v!");
        }
        return GHOST_ERR_INVALID_ARG;
    }
    if ((x->traits.flags & GHOST_DENSEMAT_SCATTERED) || (v->traits.flags & GHOST_DENSEMAT_SCATTERED) || (w->traits.flags & GHOST_DENSEMAT_SCATTERED)) {
        if (printerror) {
            ERROR_LOG("Scattered views not supported!");
        }
        return GHOST_ERR_INVALID_ARG;
    }
    if (reduce != GHOST_GEMM_NO_REDUCE) {
        if (printerror) {
            ERROR_LOG("Only NO_REDUCE valid!");
        }
        return GHOST_ERR_INVALID_ARG;
    }
    if (strncasecmp(transv,"N",1)) {
        if (printerror) {
            ERROR_LOG("v must not be transposed!");
        }
        return GHOST_ERR_INVALID_ARG;
    }
    if (strncasecmp(transw,"N",1)) {
        if (printerror) {
            ERROR_LOG("w must not be transposed!");
        }
        return GHOST_ERR_INVALID_ARG;
    }

    UNUSED(alpha);
    UNUSED(beta);

    return GHOST_SUCCESS;
}

ghost_error_t ghost_tsmm(ghost_densemat_t *x, ghost_densemat_t *v, ghost_densemat_t *w, void *alpha, void *beta)
{
    GHOST_FUNC_ENTER(GHOST_FUNCTYPE_MATH);
    ghost_error_t ret;

    if ((ret = ghost_tsmm_valid(x,v,"N",w,"N",alpha,beta,GHOST_GEMM_NO_REDUCE,1)) != GHOST_SUCCESS) {
        INFO_LOG("TSMM cannot be applied. Checking whether GEMM is fine!");
        if ((ret = ghost_gemm_valid(x,v,"N",w,"N",alpha,beta,GHOST_GEMM_NO_REDUCE,GHOST_GEMM_DEFAULT,1)) != GHOST_SUCCESS) {
            ERROR_LOG("GEMM cannot be applied!");
            return ret;
        } else {
            return ghost_gemm(x,v,"N",w,"N",alpha,beta,GHOST_GEMM_NO_REDUCE,GHOST_GEMM_NOT_SPECIAL);
        }
    }

    if (ghost_tsmm_kernels.empty()) {
#include "tsmm.def"
#include "tsmm_avx.def"
#include "tsmm_sse.def"
#ifdef GHOST_HAVE_CUDA
#include "tsmm_cu.def"
#endif
    }

    ghost_tsmm_parameters_t p;
    p.dt = x->traits.datatype;
    p.alignment = GHOST_ALIGNED;

    ghost_tsmm_kernel_t kernel = NULL;
#ifdef GHOST_HAVE_MIC
    p.impl = GHOST_IMPLEMENTATION_MIC;
#elif defined(GHOST_HAVE_AVX)
    p.impl = GHOST_IMPLEMENTATION_AVX;
#elif defined(GHOST_HAVE_SSE)
    p.impl = GHOST_IMPLEMENTATION_SSE;
#else
    p.impl = GHOST_IMPLEMENTATION_PLAIN;
#endif
#ifdef GHOST_HAVE_CUDA
    if (x->traits.location & GHOST_LOCATION_DEVICE) {
        p.impl = GHOST_IMPLEMENTATION_CUDA;
        p.dt = GHOST_DT_ANY;
        p.alignment = GHOST_UNALIGNED;
    }
#endif

    p.xstor = x->traits.storage;
    p.wstor = w->traits.storage;

    p.xcols = x->traits.ncols;
    p.vcols = v->traits.ncols;

    if (p.vcols < 4 || p.xcols < 4) {
        p.impl = GHOST_IMPLEMENTATION_PLAIN;
    } else if (p.vcols % 4 || p.xcols % 4) {
        if (!(x->traits.flags & GHOST_DENSEMAT_VIEW) && (!(v->traits.ncolspadded % 4) && !(x->traits.ncolspadded % 4))) {
            p.vcols = v->traits.ncolspadded;
            p.xcols = x->traits.ncolspadded;
        } else {
            if (p.xcols == 2) {
#ifdef GHOST_HAVE_SSE
                PERFWARNING_LOG("Use SSE for ncols==2");
                p.impl = GHOST_IMPLEMENTATION_SSE;
#endif
            }
            if (p.xcols == 1) {
                PERFWARNING_LOG("Use plain for ncols==1");
                p.impl = GHOST_IMPLEMENTATION_PLAIN;
            }
            if ((p.xcols % 4 || p.vcols % 4) && (p.impl != GHOST_IMPLEMENTATION_CUDA)) {
                PERFWARNING_LOG("Use SSE for non-multiple of four");
                p.impl = GHOST_IMPLEMENTATION_SSE;
            }
            if ((p.xcols % 2 || p.vcols % 2) && (p.impl != GHOST_IMPLEMENTATION_CUDA)) {
                PERFWARNING_LOG("Use plain for non-even column count");
                p.impl = GHOST_IMPLEMENTATION_PLAIN;
            }
        }
    }

    if (p.impl == GHOST_IMPLEMENTATION_SSE) {
        if (!IS_ALIGNED(x->val,16) || !IS_ALIGNED(v->val,16) || !IS_ALIGNED(w->val,16) ||
                (x->stride*x->elSize)%16 || (v->stride*v->elSize)%16 || (w->stride*w->elSize)%16) {
            p.alignment = GHOST_UNALIGNED;
            PERFWARNING_LOG("Switching to the unaligned kernel!");
        }
    }
    if (p.impl == GHOST_IMPLEMENTATION_AVX) {
        if (!IS_ALIGNED(x->val,32) || !IS_ALIGNED(v->val,32) || !IS_ALIGNED(w->val,32) ||
                (x->stride*x->elSize)%32 || (v->stride*v->elSize)%32 || (w->stride*w->elSize)%32) {
            p.alignment = GHOST_UNALIGNED;
            PERFWARNING_LOG("Switching to the unaligned kernel!");
        }
    }
    if (p.impl == GHOST_IMPLEMENTATION_PLAIN || p.impl == GHOST_IMPLEMENTATION_MIC) {
        if (!IS_ALIGNED(x->val,64) || !IS_ALIGNED(v->val,64) || !IS_ALIGNED(w->val,64) ||
                (x->stride*x->elSize)%64 || (v->stride*v->elSize)%64 || (w->stride*w->elSize)%64) {
            p.alignment = GHOST_UNALIGNED;
            PERFWARNING_LOG("Switching to the unaligned kernel!");
        }
    }
    INFO_LOG("Initial search for kernel %d %d %d %d %d %d %d!",p.alignment,p.impl,p.dt,p.xcols,p.vcols,p.xstor,p.wstor);
    kernel = ghost_tsmm_kernels[p];

    if (!kernel) {
        PERFWARNING_LOG("Try kernel with fixed xcols and arbitrary vcols");
        p.xcols = x->traits.ncols;
        p.vcols = -1;
        kernel = ghost_tsmm_kernels[p];
    }

    if (!kernel) {
        PERFWARNING_LOG("Try kernel with fixed vcols and arbitrary xcols");
        p.xcols = -1;
        p.vcols = v->traits.ncols;
        kernel = ghost_tsmm_kernels[p];
    }

    if (!kernel) {
        PERFWARNING_LOG("Try kernel with arbitrary block sizes");
        p.xcols = -1;
        p.vcols = -1;
        kernel = ghost_tsmm_kernels[p];
    }

    if (!kernel) {
        PERFWARNING_LOG("Try plain implementation");
        p.impl = GHOST_IMPLEMENTATION_PLAIN;
        if (!IS_ALIGNED(x->val,64) || !IS_ALIGNED(v->val,64) || !IS_ALIGNED(w->val,64) ||
                (x->stride*x->elSize)%64 || (v->stride*v->elSize)%64 || (w->stride*w->elSize)%64) {
            p.alignment = GHOST_UNALIGNED;
            PERFWARNING_LOG("Switching to the unaligned kernel!");
        } else {
            p.alignment = GHOST_ALIGNED;
        }
        p.xcols = x->traits.ncols;
        p.vcols = v->traits.ncols;
        kernel = ghost_tsmm_kernels[p];
    }
    if (!kernel) {
        PERFWARNING_LOG("Try kernel with fixed xcols and arbitrary vcols");
        p.xcols = x->traits.ncols;
        p.vcols = -1;
        kernel = ghost_tsmm_kernels[p];
    }

    if (!kernel) {
        PERFWARNING_LOG("Try kernel with fixed vcols and arbitrary xcols");
        p.xcols = -1;
        p.vcols = v->traits.ncols;
        kernel = ghost_tsmm_kernels[p];
    }

    if (!kernel) {
        PERFWARNING_LOG("Try kernel with arbitrary block sizes");
        p.xcols = -1;
        p.vcols = -1;
        kernel = ghost_tsmm_kernels[p];
    }

    if (!kernel) {
        PERFWARNING_LOG("Try unaligned kernel");
        p.alignment = GHOST_UNALIGNED;
        kernel = ghost_tsmm_kernels[p];
    }

    if (!kernel) {
        INFO_LOG("Could not find TSMM kernel with %d %d %d %d %d %d %d!",p.alignment,p.impl,p.dt,p.xcols,p.vcols,p.xstor,p.wstor);
        return GHOST_ERR_INVALID_ARG;
    }

    ret = kernel(x,v,w,alpha,beta);

#ifdef GHOST_HAVE_INSTR_TIMING
    ghost_gemm_perf_args_t tsmm_perfargs;
    tsmm_perfargs.n = p.xcols;
    tsmm_perfargs.k = p.vcols;
    if (v->context) {
        tsmm_perfargs.m = v->context->gnrows;
    } else {
        tsmm_perfargs.m = v->traits.nrows;
    }
    tsmm_perfargs.dt = x->traits.datatype;
    tsmm_perfargs.betaiszero = ghost_iszero(beta,p.dt);
    tsmm_perfargs.alphaisone = ghost_isone(alpha,p.dt);
    ghost_timing_set_perfFunc(__ghost_functag,ghost_gemm_perf_GBs,(void *)&tsmm_perfargs,sizeof(tsmm_perfargs),"GB/s");
    ghost_timing_set_perfFunc(__ghost_functag,ghost_gemm_perf_GFs,(void *)&tsmm_perfargs,sizeof(tsmm_perfargs),"GF/s");
#endif

    GHOST_FUNC_EXIT(GHOST_FUNCTYPE_MATH);

    return ret;
}

<commit_msg>the small matrix does not have to be aligned as we can do this in the kernel.<commit_after>#include "ghost/config.h"
#include "ghost/types.h"
#include "ghost/densemat.h"
#include "ghost/util.h"
#include "ghost/math.h"
#include "ghost/tsmm.h"
#include "ghost/tsmm_gen.h"
#include "ghost/tsmm_avx_gen.h"
#include "ghost/tsmm_sse_gen.h"
#include "ghost/tsmm_cu_gen.h"
#include "ghost/timing.h"

#include <map>

using namespace std;

static bool operator<(const ghost_tsmm_parameters_t &a, const ghost_tsmm_parameters_t &b)
{
    return ghost_hash(a.dt,a.xcols,ghost_hash(a.vcols,a.impl,ghost_hash(a.xstor,a.wstor,a.alignment))) < ghost_hash(b.dt,b.xcols,ghost_hash(b.vcols,b.impl,ghost_hash(b.xstor,b.wstor,b.alignment)));
}

static map<ghost_tsmm_parameters_t, ghost_tsmm_kernel_t> ghost_tsmm_kernels;

ghost_error_t ghost_tsmm_valid(ghost_densemat_t *x, ghost_densemat_t *v,  const char * transv,
ghost_densemat_t *w, const char *transw, void *alpha, void *beta, int reduce, int printerror)
{
    if (x->traits.datatype != v->traits.datatype || x->traits.datatype != w->traits.datatype) {
        if (printerror) {
            ERROR_LOG("Different data types!");
        }
        return GHOST_ERR_INVALID_ARG;
    }
    if (x == v) {
        if (printerror) {
           ERROR_LOG("x must not be equal to v!");
        }
        return GHOST_ERR_INVALID_ARG;
    }
    if ((x->traits.flags & GHOST_DENSEMAT_SCATTERED) || (v->traits.flags & GHOST_DENSEMAT_SCATTERED) || (w->traits.flags & GHOST_DENSEMAT_SCATTERED)) {
        if (printerror) {
            ERROR_LOG("Scattered views not supported!");
        }
        return GHOST_ERR_INVALID_ARG;
    }
    if (reduce != GHOST_GEMM_NO_REDUCE) {
        if (printerror) {
            ERROR_LOG("Only NO_REDUCE valid!");
        }
        return GHOST_ERR_INVALID_ARG;
    }
    if (strncasecmp(transv,"N",1)) {
        if (printerror) {
            ERROR_LOG("v must not be transposed!");
        }
        return GHOST_ERR_INVALID_ARG;
    }
    if (strncasecmp(transw,"N",1)) {
        if (printerror) {
            ERROR_LOG("w must not be transposed!");
        }
        return GHOST_ERR_INVALID_ARG;
    }

    UNUSED(alpha);
    UNUSED(beta);

    return GHOST_SUCCESS;
}

ghost_error_t ghost_tsmm(ghost_densemat_t *x, ghost_densemat_t *v, ghost_densemat_t *w, void *alpha, void *beta)
{
    GHOST_FUNC_ENTER(GHOST_FUNCTYPE_MATH);
    ghost_error_t ret;

    if ((ret = ghost_tsmm_valid(x,v,"N",w,"N",alpha,beta,GHOST_GEMM_NO_REDUCE,1)) != GHOST_SUCCESS) {
        INFO_LOG("TSMM cannot be applied. Checking whether GEMM is fine!");
        if ((ret = ghost_gemm_valid(x,v,"N",w,"N",alpha,beta,GHOST_GEMM_NO_REDUCE,GHOST_GEMM_DEFAULT,1)) != GHOST_SUCCESS) {
            ERROR_LOG("GEMM cannot be applied!");
            return ret;
        } else {
            return ghost_gemm(x,v,"N",w,"N",alpha,beta,GHOST_GEMM_NO_REDUCE,GHOST_GEMM_NOT_SPECIAL);
        }
    }

    if (ghost_tsmm_kernels.empty()) {
#include "tsmm.def"
#include "tsmm_avx.def"
#include "tsmm_sse.def"
#ifdef GHOST_HAVE_CUDA
#include "tsmm_cu.def"
#endif
    }

    ghost_tsmm_parameters_t p;
    p.dt = x->traits.datatype;
    p.alignment = GHOST_ALIGNED;

    ghost_tsmm_kernel_t kernel = NULL;
#ifdef GHOST_HAVE_MIC
    p.impl = GHOST_IMPLEMENTATION_MIC;
#elif defined(GHOST_HAVE_AVX)
    p.impl = GHOST_IMPLEMENTATION_AVX;
#elif defined(GHOST_HAVE_SSE)
    p.impl = GHOST_IMPLEMENTATION_SSE;
#else
    p.impl = GHOST_IMPLEMENTATION_PLAIN;
#endif
#ifdef GHOST_HAVE_CUDA
    if (x->traits.location & GHOST_LOCATION_DEVICE) {
        p.impl = GHOST_IMPLEMENTATION_CUDA;
        p.dt = GHOST_DT_ANY;
        p.alignment = GHOST_UNALIGNED;
    }
#endif

    p.xstor = x->traits.storage;
    p.wstor = w->traits.storage;

    p.xcols = x->traits.ncols;
    p.vcols = v->traits.ncols;

    if (p.vcols < 4 || p.xcols < 4) {
        p.impl = GHOST_IMPLEMENTATION_PLAIN;
    } else if (p.vcols % 4 || p.xcols % 4) {
        if (!(x->traits.flags & GHOST_DENSEMAT_VIEW) && (!(v->traits.ncolspadded % 4) && !(x->traits.ncolspadded % 4))) {
            p.vcols = v->traits.ncolspadded;
            p.xcols = x->traits.ncolspadded;
        } else {
            if (p.xcols == 2) {
#ifdef GHOST_HAVE_SSE
                PERFWARNING_LOG("Use SSE for ncols==2");
                p.impl = GHOST_IMPLEMENTATION_SSE;
#endif
            }
            if (p.xcols == 1) {
                PERFWARNING_LOG("Use plain for ncols==1");
                p.impl = GHOST_IMPLEMENTATION_PLAIN;
            }
            if ((p.xcols % 4 || p.vcols % 4) && (p.impl != GHOST_IMPLEMENTATION_CUDA)) {
                PERFWARNING_LOG("Use SSE for non-multiple of four");
                p.impl = GHOST_IMPLEMENTATION_SSE;
            }
            if ((p.xcols % 2 || p.vcols % 2) && (p.impl != GHOST_IMPLEMENTATION_CUDA)) {
                PERFWARNING_LOG("Use plain for non-even column count");
                p.impl = GHOST_IMPLEMENTATION_PLAIN;
            }
        }
    }

    if (p.impl == GHOST_IMPLEMENTATION_SSE) {
        if (!IS_ALIGNED(x->val,16) || !IS_ALIGNED(v->val,16) || !IS_ALIGNED(w->val,16) ||
                (x->stride*x->elSize)%16 || (v->stride*v->elSize)%16 || (w->stride*w->elSize)%16) {
            p.alignment = GHOST_UNALIGNED;
            PERFWARNING_LOG("Switching to the unaligned kernel!");
        }
    }
    if (p.impl == GHOST_IMPLEMENTATION_AVX) {
        if (!IS_ALIGNED(x->val,32) || !IS_ALIGNED(v->val,32) || !IS_ALIGNED(w->val,32) ||
                (x->stride*x->elSize)%32 || (v->stride*v->elSize)%32 || (w->stride*w->elSize)%32) {
            p.alignment = GHOST_UNALIGNED;
            PERFWARNING_LOG("Switching to the unaligned kernel!");
        }
    }
    if (p.impl == GHOST_IMPLEMENTATION_PLAIN || p.impl == GHOST_IMPLEMENTATION_MIC) {
        if (!IS_ALIGNED(x->val,64) || !IS_ALIGNED(v->val,64) ||
                (x->stride*x->elSize)%64 || (v->stride*v->elSize)%64) {
            p.alignment = GHOST_UNALIGNED;
            PERFWARNING_LOG("Switching to the unaligned kernel!");
        }
    }
    INFO_LOG("Initial search for kernel %d %d %d %d %d %d %d!",p.alignment,p.impl,p.dt,p.xcols,p.vcols,p.xstor,p.wstor);
    kernel = ghost_tsmm_kernels[p];

    if (!kernel) {
        PERFWARNING_LOG("Try kernel with fixed xcols and arbitrary vcols");
        p.xcols = x->traits.ncols;
        p.vcols = -1;
        kernel = ghost_tsmm_kernels[p];
    }

    if (!kernel) {
        PERFWARNING_LOG("Try kernel with fixed vcols and arbitrary xcols");
        p.xcols = -1;
        p.vcols = v->traits.ncols;
        kernel = ghost_tsmm_kernels[p];
    }

    if (!kernel) {
        PERFWARNING_LOG("Try kernel with arbitrary block sizes");
        p.xcols = -1;
        p.vcols = -1;
        kernel = ghost_tsmm_kernels[p];
    }

    if (!kernel) {
        PERFWARNING_LOG("Try plain implementation");
        p.impl = GHOST_IMPLEMENTATION_PLAIN;
        if (!IS_ALIGNED(x->val,64) || !IS_ALIGNED(v->val,64) || !IS_ALIGNED(w->val,64) ||
                (x->stride*x->elSize)%64 || (v->stride*v->elSize)%64 || (w->stride*w->elSize)%64) {
            p.alignment = GHOST_UNALIGNED;
            PERFWARNING_LOG("Switching to the unaligned kernel!");
        } else {
            p.alignment = GHOST_ALIGNED;
        }
        p.xcols = x->traits.ncols;
        p.vcols = v->traits.ncols;
        kernel = ghost_tsmm_kernels[p];
    }
    if (!kernel) {
        PERFWARNING_LOG("Try kernel with fixed xcols and arbitrary vcols");
        p.xcols = x->traits.ncols;
        p.vcols = -1;
        kernel = ghost_tsmm_kernels[p];
    }

    if (!kernel) {
        PERFWARNING_LOG("Try kernel with fixed vcols and arbitrary xcols");
        p.xcols = -1;
        p.vcols = v->traits.ncols;
        kernel = ghost_tsmm_kernels[p];
    }

    if (!kernel) {
        PERFWARNING_LOG("Try kernel with arbitrary block sizes");
        p.xcols = -1;
        p.vcols = -1;
        kernel = ghost_tsmm_kernels[p];
    }

    if (!kernel) {
        PERFWARNING_LOG("Try unaligned kernel");
        p.alignment = GHOST_UNALIGNED;
        kernel = ghost_tsmm_kernels[p];
    }

    if (!kernel) {
        INFO_LOG("Could not find TSMM kernel with %d %d %d %d %d %d %d!",p.alignment,p.impl,p.dt,p.xcols,p.vcols,p.xstor,p.wstor);
        return GHOST_ERR_INVALID_ARG;
    }

    ret = kernel(x,v,w,alpha,beta);

#ifdef GHOST_HAVE_INSTR_TIMING
    ghost_gemm_perf_args_t tsmm_perfargs;
    tsmm_perfargs.n = p.xcols;
    tsmm_perfargs.k = p.vcols;
    if (v->context) {
        tsmm_perfargs.m = v->context->gnrows;
    } else {
        tsmm_perfargs.m = v->traits.nrows;
    }
    tsmm_perfargs.dt = x->traits.datatype;
    tsmm_perfargs.betaiszero = ghost_iszero(beta,p.dt);
    tsmm_perfargs.alphaisone = ghost_isone(alpha,p.dt);
    ghost_timing_set_perfFunc(__ghost_functag,ghost_gemm_perf_GBs,(void *)&tsmm_perfargs,sizeof(tsmm_perfargs),"GB/s");
    ghost_timing_set_perfFunc(__ghost_functag,ghost_gemm_perf_GFs,(void *)&tsmm_perfargs,sizeof(tsmm_perfargs),"GF/s");
#endif

    GHOST_FUNC_EXIT(GHOST_FUNCTYPE_MATH);

    return ret;
}

<|endoftext|>


<commit_before>/*
 * StringUtils.cpp
 *
 * Copyright (C) 2009-12 by RStudio, Inc.
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#include <core/StringUtils.hpp>
#include <tests/TestThat.hpp>

namespace core {
namespace string_utils {

test_that("isSubsequence works")
{
   expect_true(isSubsequence("", ""));
}

} // end namespace string_utils
} // end namespace core
<commit_msg>add a couple more simple tests<commit_after>/*
 * StringUtils.cpp
 *
 * Copyright (C) 2009-12 by RStudio, Inc.
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#include <core/StringUtils.hpp>
#include <tests/TestThat.hpp>

namespace core {
namespace string_utils {

test_that("isSubsequence works")
{
   expect_true(isSubsequence("", ""));
   expect_true(isSubsequence("annnbnnnc", "abc"));
   expect_false(isSubsequence("abcdef", "abdcef"));
   expect_true(isSubsequence("abcdef", "AeF", true));
}

} // end namespace string_utils
} // end namespace core
<|endoftext|>


<commit_before>#ifndef VEXCL_SYMBOLIC_HPP
#define VEXCL_SYMBOLIC_HPP

#include <iostream>
#include <string>
#include <sstream>
#include <map>
#include <exception>
#include <type_traits>
#include <vexcl/util.hpp>

namespace vex {

namespace generator {

template <bool dummy = true>
class recorder {
    public:
	static void set(std::ostream &s) {
	    os = &s;
	}

	static std::ostream& get() {
	    return os ? *os : std::cout;
	}

    private:
	static std::ostream *os;
};

template <bool dummy>
std::ostream* recorder<dummy>::os = 0;

inline void set_recorder(std::ostream &os) {
    recorder<>::set(os);
}

inline std::ostream& get_recorder() {
    return recorder<>::get();
}

template <class T, class Enable = void>
struct terminal {
    static std::string get(const T &v) {
	std::ostringstream s;
	s << std::scientific << std::setprecision(18) << v;
	return s.str();
    }
};

template <class T>
struct terminal< T, typename std::enable_if<T::is_symbolic>::type > {
    static std::string get(const T &v) {
	return v.get_string();
    }
};

template <typename T>
class symbolic {
    public:
	enum scope_type {
	    LocalVar  = 0,
	    Parameter = 1
	};

	enum dimension_type {
	    Scalar = 0,
	    Vector = 1
	};

	enum constness_type {
	    NonConst = 0,
	    Const = 1
	};

	static const bool is_symbolic = true;

	symbolic(
		scope_type scope = LocalVar,
		dimension_type dimension = Vector,
		constness_type constness = Const
		)
	    : num(index++), scope(scope), dimension(dimension), constness(constness)
	{
	    if (scope == LocalVar) {
		get_recorder()
		    << type_name<T>() << " " << get_string() << ";\n";
	    }
	}

	symbolic(const symbolic &s) : num(index++) {
	    get_recorder()
		<< type_name<T>() << " " << get_string() << " = "
		<< s.get_string() << ";\n";
	}

	std::string get_string() const {
	    std::ostringstream s;
	    s << "var" << num;
	    return s.str();
	}

	const symbolic& operator=(const symbolic &s) {
	    get_recorder()
		<< get_string() << " = " << s.get_string() << ";\n";
	    return *this;
	}

	template <class Expr>
	const symbolic& operator=(const Expr &expr) const {
	    get_recorder()
		<< get_string() << " = " << terminal<Expr>::get(expr) << ";\n";
	    return *this;
	}

#define COMPOUND_ASSIGNMENT(cop, op) \
	template <class Expr> \
	const symbolic& operator cop(const Expr &expr) { \
	    return *this = *this op expr; \
	}

	COMPOUND_ASSIGNMENT(+=, +);
	COMPOUND_ASSIGNMENT(-=, -);
	COMPOUND_ASSIGNMENT(*=, *);
	COMPOUND_ASSIGNMENT(/=, /);
	COMPOUND_ASSIGNMENT(%=, %);
	COMPOUND_ASSIGNMENT(&=, &);
	COMPOUND_ASSIGNMENT(|=, |);
	COMPOUND_ASSIGNMENT(^=, ^);
	COMPOUND_ASSIGNMENT(<<=, <<);
	COMPOUND_ASSIGNMENT(>>=, >>);

#undef COMPOUND_ASSIGNMENT

	std::string read() const {
	    std::ostringstream s;
	    s << type_name<T>() << " " << get_string() << " = p_" << get_string();

	    switch (dimension) {
		case Vector:
		    s << "[idx];\n";
		    break;
		case Scalar:
		    s << ";\n";
		    break;
	    }

	    return s.str();
	}

	std::string write() const {
	    std::ostringstream s;

	    if (dimension == Vector && constness == NonConst)
		s << "p_" << get_string() << "[idx] = " << get_string() << ";\n";

	    return s.str();
	}

	std::string prmdecl() const {
	    std::ostringstream s;

	    if (dimension == Vector)
		s << "global ";

	    if (constness == Const)
		s << "const ";

	    s << type_name<T>();

	    if (dimension == Vector)
		s << "* ";

	    s << "p_" << get_string();

	    return s.str();
	}
    private:
	static size_t index;
	size_t num;

	scope_type     scope;
	dimension_type dimension;
	constness_type constness;
};

template <typename T>
size_t symbolic<T>::index = 0;

template <class LHS, binop::kind OP, class RHS>
struct symbolic_expression {
    static const bool is_symbolic = true;

    symbolic_expression(const LHS &lhs, const RHS &rhs) : lhs(lhs), rhs(rhs) {}

    const LHS &lhs;
    const RHS &rhs;

    std::string get_string() const {
	std::ostringstream s;
	s << "(" << terminal<LHS>::get(lhs) << " " << binop::traits<OP>::oper()
	  << " " << terminal<RHS>::get(rhs) << ")";
	return s.str();
    }
};

template <class T, class Enable = void>
struct valid_symb
    : public std::false_type {};

template <class T>
struct valid_symb<T, typename std::enable_if<std::is_arithmetic<T>::value>::type>
    : std::true_type {};

template <class T>
struct valid_symb<T, typename std::enable_if<T::is_symbolic>::type>
    : std::true_type {};

#define DEFINE_BINARY_OP(kind, oper) \
template <class LHS, class RHS> \
typename std::enable_if<valid_symb<LHS>::value && valid_symb<RHS>::value, \
symbolic_expression<LHS, kind, RHS> \
>::type \
operator oper(const LHS &lhs, const RHS &rhs) { \
    return symbolic_expression<LHS, kind, RHS>(lhs, rhs); \
}

DEFINE_BINARY_OP(binop::Add,          + )
DEFINE_BINARY_OP(binop::Subtract,     - )
DEFINE_BINARY_OP(binop::Multiply,     * )
DEFINE_BINARY_OP(binop::Divide,       / )
DEFINE_BINARY_OP(binop::Remainder,    % )
DEFINE_BINARY_OP(binop::Greater,      > )
DEFINE_BINARY_OP(binop::Less,         < )
DEFINE_BINARY_OP(binop::GreaterEqual, >=)
DEFINE_BINARY_OP(binop::LessEqual,    <=)
DEFINE_BINARY_OP(binop::Equal,        ==)
DEFINE_BINARY_OP(binop::NotEqual,     !=)
DEFINE_BINARY_OP(binop::BitwiseAnd,   & )
DEFINE_BINARY_OP(binop::BitwiseOr,    | )
DEFINE_BINARY_OP(binop::BitwiseXor,   ^ )
DEFINE_BINARY_OP(binop::LogicalAnd,   &&)
DEFINE_BINARY_OP(binop::LogicalOr,    ||)
DEFINE_BINARY_OP(binop::RightShift,   >>)
DEFINE_BINARY_OP(binop::LeftShift,    <<)

#undef DEFINE_BINARY_OP

template <class... Args>
class Kernel {
    public:
	Kernel(
		const std::vector<cl::CommandQueue> &queue,
		const std::string &name, const std::string &body,
		const Args&... args
	      ) : queue(queue)
	{
	    std::ostringstream source;

	    source
		<< standard_kernel_header
		<< "kernel void " << name << "(\n"
		<< "\t" << type_name<size_t>() << " n";

	    declare_params(source, args...);

	    source
		<< "\n\t)\n{\n"
		<< "size_t idx = get_global_id(0);\n"
		<< "if (idx < n) {\n";

	    read_params(source, args...);

	    source << body;

	    write_params(source, args...);

	    source << "}\n}\n";

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

	    for(auto q = queue.begin(); q != queue.end(); q++) {
		cl::Context context = q->getInfo<CL_QUEUE_CONTEXT>();
		cl::Device  device  = q->getInfo<CL_QUEUE_DEVICE>();

		auto program = build_sources(context, source.str());

		krn[context()] = cl::Kernel(program, name.c_str());
		wgs[context()] = kernel_workgroup_size(krn[context()], device);
	    }
	}

	template <class... Param>
	void operator()(const Param&... param) {
	    static_assert(
		    sizeof...(Param) == sizeof...(Args),
		    "Wrong number of kernel parameters"
		    );

	    for(uint d = 0; d < queue.size(); d++) {
		if (size_t psize = prm_size(d, param...)) {
		    cl::Context context = queue[d].getInfo<CL_QUEUE_CONTEXT>();

		    uint pos = 0;
		    krn[context()].setArg(pos++, psize);

		    set_params(krn[context()], d, pos, param...);

		    queue[d].enqueueNDRangeKernel(
			    krn[context()],
			    cl::NullRange,
			    alignup(psize, wgs[context()]),
			    wgs[context()]
			    );
		}
	    }
	}

    private:
	std::vector<cl::CommandQueue> queue;

	std::map<cl_context, cl::Kernel> krn;
	std::map<cl_context, uint> wgs;

	void declare_params(std::ostream &os) const {}

	template <class Head, class... Tail>
	void declare_params(std::ostream &os, const Head &head, const Tail&... tail) {
	    os << ",\n\t" << head.prmdecl();
	    declare_params(os, tail...);
	}

	void read_params(std::ostream &os) const {}

	template <class Head, class... Tail>
	void read_params(std::ostream &os, const Head &head, const Tail&... tail) {
	    os << head.read();
	    read_params(os, tail...);
	}

	void write_params(std::ostream &os) const {}

	template <class Head, class... Tail>
	void write_params(std::ostream &os, const Head &head, const Tail&... tail) {
	    os << head.write();
	    write_params(os, tail...);
	}

	size_t prm_size(uint d) const {
	    throw std::logic_error(
		    "Kernel has to have at least one vector parameter"
		    );
	}

	template <class Head, class... Tail>
	size_t prm_size(uint d, const Head &head, const Tail&... tail) const {
	    if (std::is_arithmetic<Head>::value)
		return prm_size(d, tail...);
	    else
		return KernelGenerator<Head>(head).part_size(d);
	}

	void set_params(cl::Kernel &k, uint d, uint &p) const {}

	template <class Head, class... Tail>
	void set_params(cl::Kernel &k, uint d, uint &p, const Head &head,
		const Tail&... tail) const
	{
	    KernelGenerator<Head>(head).kernel_args(k, d, p);
	    set_params(k, d, p, tail...);
	}
};

template <class... Args>
Kernel<Args...> build_kernel(
	const std::vector<cl::CommandQueue> &queue,
	const std::string &name, const std::string& body, const Args&... args
	)
{
    return Kernel<Args...>(queue, name, body, args...);
}

} // namespace generator;

} // namespace vex;

#endif
<commit_msg>nonconst symbolic by default<commit_after>#ifndef VEXCL_SYMBOLIC_HPP
#define VEXCL_SYMBOLIC_HPP

#include <iostream>
#include <string>
#include <sstream>
#include <map>
#include <exception>
#include <type_traits>
#include <vexcl/util.hpp>

namespace vex {

namespace generator {

template <bool dummy = true>
class recorder {
    public:
	static void set(std::ostream &s) {
	    os = &s;
	}

	static std::ostream& get() {
	    return os ? *os : std::cout;
	}

    private:
	static std::ostream *os;
};

template <bool dummy>
std::ostream* recorder<dummy>::os = 0;

inline void set_recorder(std::ostream &os) {
    recorder<>::set(os);
}

inline std::ostream& get_recorder() {
    return recorder<>::get();
}

template <class T, class Enable = void>
struct terminal {
    static std::string get(const T &v) {
	std::ostringstream s;
	s << std::scientific << std::setprecision(18) << v;
	return s.str();
    }
};

template <class T>
struct terminal< T, typename std::enable_if<T::is_symbolic>::type > {
    static std::string get(const T &v) {
	return v.get_string();
    }
};

template <typename T>
class symbolic {
    public:
	enum scope_type {
	    LocalVar  = 0,
	    Parameter = 1
	};

	enum dimension_type {
	    Scalar = 0,
	    Vector = 1
	};

	enum constness_type {
	    NonConst = 0,
	    Const = 1
	};

	static const bool is_symbolic = true;

	symbolic(
		scope_type scope = LocalVar,
		dimension_type dimension = Vector,
		constness_type constness = NonConst
		)
	    : num(index++), scope(scope), dimension(dimension), constness(constness)
	{
	    if (scope == LocalVar) {
		get_recorder()
		    << type_name<T>() << " " << get_string() << ";\n";
	    }
	}

	symbolic(const symbolic &s) : num(index++) {
	    get_recorder()
		<< type_name<T>() << " " << get_string() << " = "
		<< s.get_string() << ";\n";
	}

	std::string get_string() const {
	    std::ostringstream s;
	    s << "var" << num;
	    return s.str();
	}

	const symbolic& operator=(const symbolic &s) {
	    get_recorder()
		<< get_string() << " = " << s.get_string() << ";\n";
	    return *this;
	}

	template <class Expr>
	const symbolic& operator=(const Expr &expr) const {
	    get_recorder()
		<< get_string() << " = " << terminal<Expr>::get(expr) << ";\n";
	    return *this;
	}

#define COMPOUND_ASSIGNMENT(cop, op) \
	template <class Expr> \
	const symbolic& operator cop(const Expr &expr) { \
	    return *this = *this op expr; \
	}

	COMPOUND_ASSIGNMENT(+=, +);
	COMPOUND_ASSIGNMENT(-=, -);
	COMPOUND_ASSIGNMENT(*=, *);
	COMPOUND_ASSIGNMENT(/=, /);
	COMPOUND_ASSIGNMENT(%=, %);
	COMPOUND_ASSIGNMENT(&=, &);
	COMPOUND_ASSIGNMENT(|=, |);
	COMPOUND_ASSIGNMENT(^=, ^);
	COMPOUND_ASSIGNMENT(<<=, <<);
	COMPOUND_ASSIGNMENT(>>=, >>);

#undef COMPOUND_ASSIGNMENT

	std::string read() const {
	    std::ostringstream s;
	    s << type_name<T>() << " " << get_string() << " = p_" << get_string();

	    switch (dimension) {
		case Vector:
		    s << "[idx];\n";
		    break;
		case Scalar:
		    s << ";\n";
		    break;
	    }

	    return s.str();
	}

	std::string write() const {
	    std::ostringstream s;

	    if (dimension == Vector && constness == NonConst)
		s << "p_" << get_string() << "[idx] = " << get_string() << ";\n";

	    return s.str();
	}

	std::string prmdecl() const {
	    std::ostringstream s;

	    if (dimension == Vector)
		s << "global ";

	    if (constness == Const)
		s << "const ";

	    s << type_name<T>();

	    if (dimension == Vector)
		s << "* ";

	    s << "p_" << get_string();

	    return s.str();
	}
    private:
	static size_t index;
	size_t num;

	scope_type     scope;
	dimension_type dimension;
	constness_type constness;
};

template <typename T>
size_t symbolic<T>::index = 0;

template <class LHS, binop::kind OP, class RHS>
struct symbolic_expression {
    static const bool is_symbolic = true;

    symbolic_expression(const LHS &lhs, const RHS &rhs) : lhs(lhs), rhs(rhs) {}

    const LHS &lhs;
    const RHS &rhs;

    std::string get_string() const {
	std::ostringstream s;
	s << "(" << terminal<LHS>::get(lhs) << " " << binop::traits<OP>::oper()
	  << " " << terminal<RHS>::get(rhs) << ")";
	return s.str();
    }
};

template <class T, class Enable = void>
struct valid_symb
    : public std::false_type {};

template <class T>
struct valid_symb<T, typename std::enable_if<std::is_arithmetic<T>::value>::type>
    : std::true_type {};

template <class T>
struct valid_symb<T, typename std::enable_if<T::is_symbolic>::type>
    : std::true_type {};

#define DEFINE_BINARY_OP(kind, oper) \
template <class LHS, class RHS> \
typename std::enable_if<valid_symb<LHS>::value && valid_symb<RHS>::value, \
symbolic_expression<LHS, kind, RHS> \
>::type \
operator oper(const LHS &lhs, const RHS &rhs) { \
    return symbolic_expression<LHS, kind, RHS>(lhs, rhs); \
}

DEFINE_BINARY_OP(binop::Add,          + )
DEFINE_BINARY_OP(binop::Subtract,     - )
DEFINE_BINARY_OP(binop::Multiply,     * )
DEFINE_BINARY_OP(binop::Divide,       / )
DEFINE_BINARY_OP(binop::Remainder,    % )
DEFINE_BINARY_OP(binop::Greater,      > )
DEFINE_BINARY_OP(binop::Less,         < )
DEFINE_BINARY_OP(binop::GreaterEqual, >=)
DEFINE_BINARY_OP(binop::LessEqual,    <=)
DEFINE_BINARY_OP(binop::Equal,        ==)
DEFINE_BINARY_OP(binop::NotEqual,     !=)
DEFINE_BINARY_OP(binop::BitwiseAnd,   & )
DEFINE_BINARY_OP(binop::BitwiseOr,    | )
DEFINE_BINARY_OP(binop::BitwiseXor,   ^ )
DEFINE_BINARY_OP(binop::LogicalAnd,   &&)
DEFINE_BINARY_OP(binop::LogicalOr,    ||)
DEFINE_BINARY_OP(binop::RightShift,   >>)
DEFINE_BINARY_OP(binop::LeftShift,    <<)

#undef DEFINE_BINARY_OP

template <class... Args>
class Kernel {
    public:
	Kernel(
		const std::vector<cl::CommandQueue> &queue,
		const std::string &name, const std::string &body,
		const Args&... args
	      ) : queue(queue)
	{
	    std::ostringstream source;

	    source
		<< standard_kernel_header
		<< "kernel void " << name << "(\n"
		<< "\t" << type_name<size_t>() << " n";

	    declare_params(source, args...);

	    source
		<< "\n\t)\n{\n"
		<< "size_t idx = get_global_id(0);\n"
		<< "if (idx < n) {\n";

	    read_params(source, args...);

	    source << body;

	    write_params(source, args...);

	    source << "}\n}\n";

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

	    for(auto q = queue.begin(); q != queue.end(); q++) {
		cl::Context context = q->getInfo<CL_QUEUE_CONTEXT>();
		cl::Device  device  = q->getInfo<CL_QUEUE_DEVICE>();

		auto program = build_sources(context, source.str());

		krn[context()] = cl::Kernel(program, name.c_str());
		wgs[context()] = kernel_workgroup_size(krn[context()], device);
	    }
	}

	template <class... Param>
	void operator()(const Param&... param) {
	    static_assert(
		    sizeof...(Param) == sizeof...(Args),
		    "Wrong number of kernel parameters"
		    );

	    for(uint d = 0; d < queue.size(); d++) {
		if (size_t psize = prm_size(d, param...)) {
		    cl::Context context = queue[d].getInfo<CL_QUEUE_CONTEXT>();

		    uint pos = 0;
		    krn[context()].setArg(pos++, psize);

		    set_params(krn[context()], d, pos, param...);

		    queue[d].enqueueNDRangeKernel(
			    krn[context()],
			    cl::NullRange,
			    alignup(psize, wgs[context()]),
			    wgs[context()]
			    );
		}
	    }
	}

    private:
	std::vector<cl::CommandQueue> queue;

	std::map<cl_context, cl::Kernel> krn;
	std::map<cl_context, uint> wgs;

	void declare_params(std::ostream &os) const {}

	template <class Head, class... Tail>
	void declare_params(std::ostream &os, const Head &head, const Tail&... tail) {
	    os << ",\n\t" << head.prmdecl();
	    declare_params(os, tail...);
	}

	void read_params(std::ostream &os) const {}

	template <class Head, class... Tail>
	void read_params(std::ostream &os, const Head &head, const Tail&... tail) {
	    os << head.read();
	    read_params(os, tail...);
	}

	void write_params(std::ostream &os) const {}

	template <class Head, class... Tail>
	void write_params(std::ostream &os, const Head &head, const Tail&... tail) {
	    os << head.write();
	    write_params(os, tail...);
	}

	size_t prm_size(uint d) const {
	    throw std::logic_error(
		    "Kernel has to have at least one vector parameter"
		    );
	}

	template <class Head, class... Tail>
	size_t prm_size(uint d, const Head &head, const Tail&... tail) const {
	    if (std::is_arithmetic<Head>::value)
		return prm_size(d, tail...);
	    else
		return KernelGenerator<Head>(head).part_size(d);
	}

	void set_params(cl::Kernel &k, uint d, uint &p) const {}

	template <class Head, class... Tail>
	void set_params(cl::Kernel &k, uint d, uint &p, const Head &head,
		const Tail&... tail) const
	{
	    KernelGenerator<Head>(head).kernel_args(k, d, p);
	    set_params(k, d, p, tail...);
	}
};

template <class... Args>
Kernel<Args...> build_kernel(
	const std::vector<cl::CommandQueue> &queue,
	const std::string &name, const std::string& body, const Args&... args
	)
{
    return Kernel<Args...>(queue, name, body, args...);
}

} // namespace generator;

} // namespace vex;

#endif
<|endoftext|>