<commit_before>#include "system/van.h"
#include <string.h>
#include <zmq.h>
#include <libgen.h>
#include <stdlib.h>
#include <time.h>
#include <functional>
#include "ps/shared_array.h"
#include "system/manager.h"
#include "system/postoffice.h"
namespace ps {
DEFINE_int32(bind_to, 0, "binding port");
DEFINE_bool(local, false, "run in local");

DECLARE_string(my_node);
DECLARE_string(scheduler);
DECLARE_int32(num_workers);
DECLARE_int32(num_servers);

Van::~Van() {
  for (auto& it : senders_) zmq_close(it.second);
  zmq_close(receiver_);
  zmq_ctx_destroy(context_);
}

void Van::Init() {
  scheduler_ = ParseNode(FLAGS_scheduler);
  my_node_ = ParseNode(FLAGS_my_node);
  LOG(INFO) << "I'm [" << my_node_.ShortDebugString() << "]";

  context_ = zmq_ctx_new();
  CHECK(context_ != NULL) << "create 0mq context failed";

  // one need to "sudo ulimit -n 65536" or edit /etc/security/limits.conf
  zmq_ctx_set(context_, ZMQ_MAX_SOCKETS, 65536);
  // zmq_ctx_set(context_, ZMQ_IO_THREADS, 4);

  Bind();
  // connect(my_node_);
  Connect(scheduler_);

  // setup monitor
  if (IsScheduler()) {
    CHECK(!zmq_socket_monitor(receiver_, "inproc://monitor", ZMQ_EVENT_ALL));
  } else {
    CHECK(!zmq_socket_monitor(
        senders_[scheduler_.id()], "inproc://monitor", ZMQ_EVENT_ALL));
  }
  monitor_thread_ = new std::thread(&Van::Monitor, this);
  monitor_thread_->detach();
}

void Van::Bind() {
  receiver_ = zmq_socket(context_, ZMQ_ROUTER);
  CHECK(receiver_ != NULL)
      << "create receiver socket failed: " << zmq_strerror(errno);
  string addr = "tcp://*:";
  bool retry = false;
  if (FLAGS_bind_to) {
    addr += std::to_string(FLAGS_bind_to);
  } else {
    CHECK(my_node_.has_port()) << my_node_.ShortDebugString();
    if (!IsScheduler()) retry = true;
    addr += std::to_string(my_node_.port());
  }
  if (FLAGS_local) {
    addr = "ipc:///tmp/" + my_node_.id();
  }
  std::hash<std::string> hash;
  srand((int)time(NULL) + hash(my_node_.id()));
  int max_retry = retry ? 40 : 1;
  for (int i = 0; i < max_retry; ++i) {
    if (zmq_bind(receiver_, addr.c_str()) == 0) break;
    CHECK_NE(i, max_retry - 1)
        << "bind to " << addr << " failed: " << " " << zmq_strerror(errno);

    my_node_.set_port(10000 + rand() % 40000);
    addr = "tcp://*:" + std::to_string(my_node_.port());
  }

  VLOG(1) << "BIND address " << addr;
}

void Van::Disconnect(const Node& node) {
  CHECK(node.has_id()) << node.ShortDebugString();
  NodeID id = node.id();
  if (senders_.find(id) != senders_.end()) {
    zmq_close (senders_[id]);
  }
  senders_.erase(id);
  VLOG(1) << "DISCONNECT from " << node.id();
}

bool Van::Connect(const Node& node) {
  CHECK(node.has_id()) << node.ShortDebugString();
  CHECK(node.has_port()) << node.ShortDebugString();
  CHECK(node.has_hostname()) << node.ShortDebugString();
  NodeID id = node.id();
  if (id == my_node_.id()) {
    // update my node info
    my_node_ = node;
  }
  if (senders_.find(id) != senders_.end()) {
    return true;
  }
  void *sender = zmq_socket(context_, ZMQ_DEALER);
  CHECK(sender != NULL) << zmq_strerror(errno);
  string my_id = my_node_.id(); // address(my_node_);
  zmq_setsockopt (sender, ZMQ_IDENTITY, my_id.data(), my_id.size());

  // uint64_t hwm = 500000;
  // zmq_setsockopt(sender, ZMQ_SNDHWM, &hwm, sizeof(hwm));

  // connect
  string addr = "tcp://" + node.hostname() + ":" + std::to_string(node.port());
  if (FLAGS_local) {
    addr = "ipc:///tmp/" + node.id();
  }
  if (zmq_connect(sender, addr.c_str()) != 0) {
    LOG(WARNING) << "connect to " + addr + " failed: " + zmq_strerror(errno);
    return false;
  }

  senders_[id] = sender;

  VLOG(1) << "CONNECT to " << id << " [" << addr << "]";
  return true;
}

bool Van::Send(Message* msg, size_t* send_bytes) {
  // find the socket
  NodeID id = msg->recver;
  auto it = senders_.find(id);
  if (it == senders_.end()) {
    LOG(WARNING) << "there is no socket to node " + id;
    return false;
  }
  void *socket = it->second;

  // double check
  bool has_key = !msg->key.empty();
  if (has_key) {
    msg->task.set_has_key(has_key);
  } else {
    msg->task.clear_has_key();
  }
  int n = has_key + msg->value.size();

  // send task
  int task_size = msg->task.ByteSize();
  char* task_buf = new char[task_size+5];
  CHECK(msg->task.SerializeToArray(task_buf, task_size))
      << "failed to serialize " << msg->task.ShortDebugString();

  int tag = ZMQ_SNDMORE;
  if (n == 0) tag = 0; // ZMQ_DONTWAIT;
  zmq_msg_t task_msg;
  zmq_msg_init_data(&task_msg, task_buf, task_size, FreeData, NULL);

  while (true) {
    if (zmq_msg_send(&task_msg, socket, tag) == task_size) break;
    if (errno == EINTR) continue;
    LOG(WARNING) << "failed to send message to node [" << id
                 << "] errno: " << errno << " " << zmq_strerror(errno);
    return false;
  }
  *send_bytes += task_size;

  // send data
  for (int i = 0; i < n; ++i) {
    SArray<char>* data = new SArray<char>(
        (has_key && i == 0) ? msg->key : msg->value[i-has_key]);
    zmq_msg_t data_msg;
    int data_size = data->size();
    zmq_msg_init_data(&data_msg, data->data(), data->size(), FreeData, data);
    if (i == n - 1) tag = 0; // ZMQ_DONTWAIT;
    while (true) {
      if (zmq_msg_send(&data_msg, socket, tag) == data_size) break;
      if (errno == EINTR) continue;
      // if (errno == EINTR || errno == EAGAIN) {
      //   usleep(1000);
      //   continue;
      // }
      LOG(WARNING) << "failed to send message to node [" << id
                   << "] errno: " << errno << " " << zmq_strerror(errno)
                   << ". " << i << "/" << n << ": " << msg->ShortDebugString();
      return false;
    }
    *send_bytes += data_size;
  }

  VLOG(1) << "TO " << msg->recver << " " << msg->ShortDebugString();
  return true;
}

bool Van::Recv(Message* msg, size_t* recv_bytes) {
  msg->clear_data();
  for (int i = 0; ; ++i) {
    // zmq_msg_t zmsg;
    zmq_msg_t* zmsg = new zmq_msg_t;
    CHECK(zmq_msg_init(zmsg) == 0) << zmq_strerror(errno);
    while (true) {
      if (zmq_msg_recv(zmsg, receiver_, 0) != -1) break;
      if (errno == EINTR) continue;
      LOG(WARNING) << "failed to receive message. errno: "
                   << errno << " " << zmq_strerror(errno);
      return false;
    }
    char* buf = CHECK_NOTNULL((char *)zmq_msg_data(zmsg));
    size_t size = zmq_msg_size(zmsg);
    *recv_bytes += size;

    if (i == 0) {
      // identify
      msg->sender = std::string(buf, size);
      msg->recver = my_node_.id();
      CHECK(zmq_msg_more(zmsg));
      zmq_msg_close(zmsg);
      delete zmsg;
    } else if (i == 1) {
      // task
      CHECK(msg->task.ParseFromArray(buf, size))
          << "failed to parse string from " << msg->sender
          << ". this is " << my_node_.id() << " " << size;
      if (IsScheduler() && msg->task.control() &&
          msg->task.ctrl().cmd() == Control::REGISTER_NODE) {
        // it is the first time the scheduler receive message from the
        // sender. store the file desciptor of the sender for the monitor
        int val[64]; size_t val_len = msg->sender.size();
        CHECK_LT(val_len, 64*sizeof(int));
        memcpy(val, msg->sender.data(), val_len);
        CHECK(!zmq_getsockopt(
            receiver_,  ZMQ_IDENTITY_FD, (char*)val, &val_len))
            << "failed to get the file descriptor of " << msg->sender;
        CHECK_EQ(val_len, (size_t)4);
        int fd = val[0];
        VLOG(1) << "node [" << msg->sender << "] is on file descriptor " << fd;
        Lock l(fd_to_nodeid_mu_);
        fd_to_nodeid_[fd] = msg->sender;
      }
      zmq_msg_close(zmsg);
      if (!zmq_msg_more(zmsg)) break;
      delete zmsg;
    } else {
      // copy data
      // SArray<char> data; data.CopyFrom(buf, size);

      // ugly zero-copy
      SArray<char> data;
      data.reset(buf, size, [zmsg](char*) {
          zmq_msg_close(zmsg);
          delete zmsg;
        });

      if (i == 2 && msg->task.has_key()) {
        msg->key = data;
      } else {
        msg->value.push_back(data);
      }
      if (!zmq_msg_more(zmsg)) { break; }
    }
  }

  VLOG(1) << "FROM: " << msg->sender << " " << msg->ShortDebugString();
  return true;
}

Node Van::ParseNode(const string& node_str) {
  Node node;
  CHECK(google::protobuf::TextFormat::ParseFromString(node_str, &node));
  if (!node.has_id()) {
    string str = node.hostname() + "_" + std::to_string(node.port());
    if (node.role() == Node::SCHEDULER) {
      str = "H";
    } else if (node.role() == Node::WORKER) {
      str = "W_" + str;
    } else if (node.role() == Node::SERVER) {
      str = "S_" + str;
    }
    node.set_id(str);
  }
  return node;
}

void Van::Monitor() {
  VLOG(1) << "starting monitor...";
  void *s = CHECK_NOTNULL(zmq_socket (context_, ZMQ_PAIR));
  CHECK(!zmq_connect (s, "inproc://monitor"));
  while (true) {
    zmq_msg_t msg;
    zmq_msg_init(&msg);
    if (zmq_msg_recv(&msg, s, 0) == -1) {
      if (errno == EINTR) continue;
      break;
    }
    uint8_t *data = (uint8_t *)zmq_msg_data (&msg);
    int event = *(uint16_t *)(data);
    int value = *(uint32_t *)(data + 2);

    if (event == ZMQ_EVENT_DISCONNECTED) {
      auto& manager = Postoffice::instance().manager();
      if (IsScheduler()) {
        Lock l(fd_to_nodeid_mu_);
        if (fd_to_nodeid_.find(value) == fd_to_nodeid_.end()) {
          LOG(WARNING) << "cannot find the node id for FD = " << value;
          continue;
        }
        manager.NodeDisconnected(fd_to_nodeid_[value]);
      } else {
        manager.NodeDisconnected(scheduler_.id());
      }
    }
    if (event == ZMQ_EVENT_MONITOR_STOPPED) break;
  }
  zmq_close (s);
  VLOG(1) << "monitor stopped.";
}

} // namespace ps

// check whether I could connect to a specified node
// bool connected(const Node& node);
// bool Van::connected(const Node& node) {
//   auto it = senders_.find(node.id());
//   return it != senders_.end();
// }
<commit_msg>fix a bug when van process control msg<commit_after>#include "system/van.h"
#include <string.h>
#include <zmq.h>
#include <libgen.h>
#include <stdlib.h>
#include <time.h>
#include <functional>
#include "ps/shared_array.h"
#include "system/manager.h"
#include "system/postoffice.h"
namespace ps {
DEFINE_int32(bind_to, 0, "binding port");
DEFINE_bool(local, false, "run in local");

DECLARE_string(my_node);
DECLARE_string(scheduler);
DECLARE_int32(num_workers);
DECLARE_int32(num_servers);

Van::~Van() {
  for (auto& it : senders_) zmq_close(it.second);
  zmq_close(receiver_);
  zmq_ctx_destroy(context_);
}

void Van::Init() {
  scheduler_ = ParseNode(FLAGS_scheduler);
  my_node_ = ParseNode(FLAGS_my_node);
  LOG(INFO) << "I'm [" << my_node_.ShortDebugString() << "]";

  context_ = zmq_ctx_new();
  CHECK(context_ != NULL) << "create 0mq context failed";

  // one need to "sudo ulimit -n 65536" or edit /etc/security/limits.conf
  zmq_ctx_set(context_, ZMQ_MAX_SOCKETS, 65536);
  // zmq_ctx_set(context_, ZMQ_IO_THREADS, 4);

  Bind();
  // connect(my_node_);
  Connect(scheduler_);

  // setup monitor
  if (IsScheduler()) {
    CHECK(!zmq_socket_monitor(receiver_, "inproc://monitor", ZMQ_EVENT_ALL));
  } else {
    CHECK(!zmq_socket_monitor(
        senders_[scheduler_.id()], "inproc://monitor", ZMQ_EVENT_ALL));
  }
  monitor_thread_ = new std::thread(&Van::Monitor, this);
  monitor_thread_->detach();
}

void Van::Bind() {
  receiver_ = zmq_socket(context_, ZMQ_ROUTER);
  CHECK(receiver_ != NULL)
      << "create receiver socket failed: " << zmq_strerror(errno);
  string addr = "tcp://*:";
  bool retry = false;
  if (FLAGS_bind_to) {
    addr += std::to_string(FLAGS_bind_to);
  } else {
    CHECK(my_node_.has_port()) << my_node_.ShortDebugString();
    if (!IsScheduler()) retry = true;
    addr += std::to_string(my_node_.port());
  }
  if (FLAGS_local) {
    addr = "ipc:///tmp/" + my_node_.id();
  }
  std::hash<std::string> hash;
  srand((int)time(NULL) + hash(my_node_.id()));
  int max_retry = retry ? 40 : 1;
  for (int i = 0; i < max_retry; ++i) {
    if (zmq_bind(receiver_, addr.c_str()) == 0) break;
    CHECK_NE(i, max_retry - 1)
        << "bind to " << addr << " failed: " << " " << zmq_strerror(errno);

    my_node_.set_port(10000 + rand() % 40000);
    addr = "tcp://*:" + std::to_string(my_node_.port());
  }

  VLOG(1) << "BIND address " << addr;
}

void Van::Disconnect(const Node& node) {
  CHECK(node.has_id()) << node.ShortDebugString();
  NodeID id = node.id();
  if (senders_.find(id) != senders_.end()) {
    zmq_close (senders_[id]);
  }
  senders_.erase(id);
  VLOG(1) << "DISCONNECT from " << node.id();
}

bool Van::Connect(const Node& node) {
  CHECK(node.has_id()) << node.ShortDebugString();
  CHECK(node.has_port()) << node.ShortDebugString();
  CHECK(node.has_hostname()) << node.ShortDebugString();
  NodeID id = node.id();
  if (id == my_node_.id()) {
    // update my node info
    my_node_ = node;
  }
  if (senders_.find(id) != senders_.end()) {
    return true;
  }
  void *sender = zmq_socket(context_, ZMQ_DEALER);
  CHECK(sender != NULL) << zmq_strerror(errno);
  string my_id = my_node_.id(); // address(my_node_);
  zmq_setsockopt (sender, ZMQ_IDENTITY, my_id.data(), my_id.size());

  // uint64_t hwm = 500000;
  // zmq_setsockopt(sender, ZMQ_SNDHWM, &hwm, sizeof(hwm));

  // connect
  string addr = "tcp://" + node.hostname() + ":" + std::to_string(node.port());
  if (FLAGS_local) {
    addr = "ipc:///tmp/" + node.id();
  }
  if (zmq_connect(sender, addr.c_str()) != 0) {
    LOG(WARNING) << "connect to " + addr + " failed: " + zmq_strerror(errno);
    return false;
  }

  senders_[id] = sender;

  VLOG(1) << "CONNECT to " << id << " [" << addr << "]";
  return true;
}

bool Van::Send(Message* msg, size_t* send_bytes) {
  // find the socket
  NodeID id = msg->recver;
  auto it = senders_.find(id);
  if (it == senders_.end()) {
    LOG(WARNING) << "there is no socket to node " + id;
    return false;
  }
  void *socket = it->second;

  // double check
  bool has_key = !msg->key.empty();
  if (has_key) {
    msg->task.set_has_key(has_key);
  } else {
    msg->task.clear_has_key();
  }
  int n = has_key + msg->value.size();

  // send task
  int task_size = msg->task.ByteSize();
  char* task_buf = new char[task_size+5];
  CHECK(msg->task.SerializeToArray(task_buf, task_size))
      << "failed to serialize " << msg->task.ShortDebugString();

  int tag = ZMQ_SNDMORE;
  if (n == 0) tag = 0; // ZMQ_DONTWAIT;
  zmq_msg_t task_msg;
  zmq_msg_init_data(&task_msg, task_buf, task_size, FreeData, NULL);

  while (true) {
    if (zmq_msg_send(&task_msg, socket, tag) == task_size) break;
    if (errno == EINTR) continue;
    LOG(WARNING) << "failed to send message to node [" << id
                 << "] errno: " << errno << " " << zmq_strerror(errno);
    return false;
  }
  *send_bytes += task_size;

  // send data
  for (int i = 0; i < n; ++i) {
    SArray<char>* data = new SArray<char>(
        (has_key && i == 0) ? msg->key : msg->value[i-has_key]);
    zmq_msg_t data_msg;
    int data_size = data->size();
    zmq_msg_init_data(&data_msg, data->data(), data->size(), FreeData, data);
    if (i == n - 1) tag = 0; // ZMQ_DONTWAIT;
    while (true) {
      if (zmq_msg_send(&data_msg, socket, tag) == data_size) break;
      if (errno == EINTR) continue;
      // if (errno == EINTR || errno == EAGAIN) {
      //   usleep(1000);
      //   continue;
      // }
      LOG(WARNING) << "failed to send message to node [" << id
                   << "] errno: " << errno << " " << zmq_strerror(errno)
                   << ". " << i << "/" << n << ": " << msg->ShortDebugString();
      return false;
    }
    *send_bytes += data_size;
  }

  VLOG(1) << "TO " << msg->recver << " " << msg->ShortDebugString();
  return true;
}

bool Van::Recv(Message* msg, size_t* recv_bytes) {
  msg->clear_data();
  for (int i = 0; ; ++i) {
    // zmq_msg_t zmsg;
    zmq_msg_t* zmsg = new zmq_msg_t;
    CHECK(zmq_msg_init(zmsg) == 0) << zmq_strerror(errno);
    while (true) {
      if (zmq_msg_recv(zmsg, receiver_, 0) != -1) break;
      if (errno == EINTR) continue;
      LOG(WARNING) << "failed to receive message. errno: "
                   << errno << " " << zmq_strerror(errno);
      return false;
    }
    char* buf = CHECK_NOTNULL((char *)zmq_msg_data(zmsg));
    size_t size = zmq_msg_size(zmsg);
    *recv_bytes += size;

    if (i == 0) {
      // identify
      msg->sender = std::string(buf, size);
      msg->recver = my_node_.id();
      CHECK(zmq_msg_more(zmsg));
      zmq_msg_close(zmsg);
      delete zmsg;
    } else if (i == 1) {
      // task
      CHECK(msg->task.ParseFromArray(buf, size))
          << "failed to parse string from " << msg->sender
          << ". this is " << my_node_.id() << " " << size;
      if (IsScheduler() && msg->task.has_control() &&
          msg->task.ctrl().cmd() == Control::REGISTER_NODE) {
        // it is the first time the scheduler receive message from the
        // sender. store the file desciptor of the sender for the monitor
        int val[64]; size_t val_len = msg->sender.size();
        CHECK_LT(val_len, 64*sizeof(int));
        memcpy(val, msg->sender.data(), val_len);
        CHECK(!zmq_getsockopt(
            receiver_,  ZMQ_IDENTITY_FD, (char*)val, &val_len))
            << "failed to get the file descriptor of " << msg->sender;
        CHECK_EQ(val_len, (size_t)4);
        int fd = val[0];
        VLOG(1) << "node [" << msg->sender << "] is on file descriptor " << fd;
        Lock l(fd_to_nodeid_mu_);
        fd_to_nodeid_[fd] = msg->sender;
      }
      zmq_msg_close(zmsg);
      if (!zmq_msg_more(zmsg)) break;
      delete zmsg;
    } else {
      // copy data
      // SArray<char> data; data.CopyFrom(buf, size);

      // ugly zero-copy
      SArray<char> data;
      data.reset(buf, size, [zmsg](char*) {
          zmq_msg_close(zmsg);
          delete zmsg;
        });

      if (i == 2 && msg->task.has_key()) {
        msg->key = data;
      } else {
        msg->value.push_back(data);
      }
      if (!zmq_msg_more(zmsg)) { break; }
    }
  }

  VLOG(1) << "FROM: " << msg->sender << " " << msg->ShortDebugString();
  return true;
}

Node Van::ParseNode(const string& node_str) {
  Node node;
  CHECK(google::protobuf::TextFormat::ParseFromString(node_str, &node));
  if (!node.has_id()) {
    string str = node.hostname() + "_" + std::to_string(node.port());
    if (node.role() == Node::SCHEDULER) {
      str = "H";
    } else if (node.role() == Node::WORKER) {
      str = "W_" + str;
    } else if (node.role() == Node::SERVER) {
      str = "S_" + str;
    }
    node.set_id(str);
  }
  return node;
}

void Van::Monitor() {
  VLOG(1) << "starting monitor...";
  void *s = CHECK_NOTNULL(zmq_socket (context_, ZMQ_PAIR));
  CHECK(!zmq_connect (s, "inproc://monitor"));
  while (true) {
    zmq_msg_t msg;
    zmq_msg_init(&msg);
    if (zmq_msg_recv(&msg, s, 0) == -1) {
      if (errno == EINTR) continue;
      break;
    }
    uint8_t *data = (uint8_t *)zmq_msg_data (&msg);
    int event = *(uint16_t *)(data);
    int value = *(uint32_t *)(data + 2);

    if (event == ZMQ_EVENT_DISCONNECTED) {
      auto& manager = Postoffice::instance().manager();
      if (IsScheduler()) {
        Lock l(fd_to_nodeid_mu_);
        if (fd_to_nodeid_.find(value) == fd_to_nodeid_.end()) {
          LOG(WARNING) << "cannot find the node id for FD = " << value;
          continue;
        }
        manager.NodeDisconnected(fd_to_nodeid_[value]);
      } else {
        manager.NodeDisconnected(scheduler_.id());
      }
    }
    if (event == ZMQ_EVENT_MONITOR_STOPPED) break;
  }
  zmq_close (s);
  VLOG(1) << "monitor stopped.";
}

} // namespace ps

// check whether I could connect to a specified node
// bool connected(const Node& node);
// bool Van::connected(const Node& node) {
//   auto it = senders_.find(node.id());
//   return it != senders_.end();
// }
<|endoftext|>


<commit_before>#include "ParseTree.h"

#include <assert.h>
#include <setjmp.h>
#include <stdarg.h>

#include "Lexer.h"

namespace
{
	jmp_buf errorHandler;

	void Stop(ParseContext &ctx, const char *pos, const char *msg, va_list args)
	{
		ctx.errorPos = pos;

		char errorText[4096];

		vsnprintf(errorText, 4096, msg, args);
		errorText[4096 - 1] = '\0';

		ctx.errorMsg = InplaceStr(errorText);

		longjmp(errorHandler, 1);
	}

	void Stop(ParseContext &ctx, const char *pos, const char *msg, ...)
	{
		va_list args;
		va_start(args, msg);

		Stop(ctx, pos, msg, args);
	}

	void AssertAt(ParseContext &ctx, LexemeType type, const char *msg, ...)
	{
		if(!ctx.At(type))
		{
			va_list args;
			va_start(args, msg);

			Stop(ctx, ctx.Position(), msg, args);
		}
	}

	void AssertConsume(ParseContext &ctx, LexemeType type, const char *msg, ...)
	{
		if(!ctx.Consume(type))
		{
			va_list args;
			va_start(args, msg);

			Stop(ctx, ctx.Position(), msg, args);
		}
	}
}

SynBinaryOpType GetBinaryOpType(LexemeType type)
{
	switch(type)
	{
	case lex_add:
		return SYN_BINARY_OP_ADD;
	case lex_sub:
		return SYN_BINARY_OP_SUB;
	case lex_mul:
		return SYN_BINARY_OP_MUL;
	case lex_div:
		return SYN_BINARY_OP_DIV;
	case lex_mod:
		return SYN_BINARY_OP_MOD;
	case lex_pow:
		return SYN_BINARY_OP_POW;
	case lex_less:
		return SYN_BINARY_OP_LESS;
	case lex_lequal:
		return SYN_BINARY_OP_LESS_EQUAL;
	case lex_shl:
		return SYN_BINARY_OP_SHL;
	case lex_greater:
		return SYN_BINARY_OP_GREATER;
	case lex_gequal:
		return SYN_BINARY_OP_GREATER_EQUAL;
	case lex_shr:
		return SYN_BINARY_OP_SHR;
	case lex_equal:
		return SYN_BINARY_OP_EQUAL;
	case lex_nequal:
		return SYN_BINARY_OP_NOT_EQUAL;
	case lex_bitand:
		return SYN_BINARY_OP_BIT_AND;
	case lex_bitor:
		return SYN_BINARY_OP_BIT_OR;
	case lex_bitxor:
		return SYN_BINARY_OP_BIT_XOR;
	case lex_logand:
		return SYN_BINARY_OP_LOGICAL_AND;
	case lex_logor:
		return SYN_BINARY_OP_LOGICAL_OR;
	case lex_logxor:
		return SYN_BINARY_OP_LOGICAL_XOR;
	case lex_in:
		return SYN_BINARY_OP_IN;
	}

	return SYN_BINARY_OP_UNKNOWN;
}

unsigned GetBinaryOpPrecedence(SynBinaryOpType op)
{
	switch(op)
	{
	case SYN_BINARY_OP_ADD:
		return 2;
	case SYN_BINARY_OP_SUB:
		return 2;
	case SYN_BINARY_OP_MUL:
		return 1;
	case SYN_BINARY_OP_DIV:
		return 1;
	case SYN_BINARY_OP_MOD:
		return 1;
	case SYN_BINARY_OP_POW:
		return 0;
	case SYN_BINARY_OP_LESS:
		return 4;
	case SYN_BINARY_OP_LESS_EQUAL:
		return 4;
	case SYN_BINARY_OP_SHL:
		return 3;
	case SYN_BINARY_OP_GREATER:
		return 4;
	case SYN_BINARY_OP_GREATER_EQUAL:
		return 4;
	case SYN_BINARY_OP_SHR:
		return 3;
	case SYN_BINARY_OP_EQUAL:
		return 5;
	case SYN_BINARY_OP_NOT_EQUAL:
		return 5;
	case SYN_BINARY_OP_BIT_AND:
		return 6;
	case SYN_BINARY_OP_BIT_OR:
		return 8;
	case SYN_BINARY_OP_BIT_XOR:
		return 7;
	case SYN_BINARY_OP_LOGICAL_AND:
		return 9;
	case SYN_BINARY_OP_LOGICAL_OR:
		return 11;
	case SYN_BINARY_OP_LOGICAL_XOR:
		return 10;
	case SYN_BINARY_OP_IN:
		return 12;
	}

	return 0;
}

ParseContext::ParseContext()
{
	errorPos = 0;
}

LexemeType ParseContext::Peek()
{
	return currentLexeme->type;
}

bool ParseContext::At(LexemeType type)
{
	return currentLexeme->type == type;
}

bool ParseContext::Consume(LexemeType type)
{
	if(currentLexeme->type == type)
	{
		Skip();
		return true;
	}

	return false;
}

InplaceStr ParseContext::Consume()
{
	InplaceStr str(currentLexeme->pos, currentLexeme->length);

	Skip();

	return str;
}

void ParseContext::Skip()
{
	if(currentLexeme->type != lex_none)
		currentLexeme++;
}

const char* ParseContext::Position()
{
	return currentLexeme->pos;
}

SynBase* ParseTernaryExpr(ParseContext &ctx);

SynType* ParseTerminalType(ParseContext &ctx)
{
	if(ctx.At(lex_string))
	{
		InplaceStr name = ctx.Consume();

		return new SynTypeSimple(name);
	}

	if(ctx.Consume(lex_auto))
		return new SynTypeAuto();

	if(ctx.Consume(lex_generic))
		return new SynTypeGeneric();

	return NULL;
}

SynType* ParseType(ParseContext &ctx)
{
	SynType *base = ParseTerminalType(ctx);

	if(!base)
		return NULL;

	while(ctx.At(lex_obracket) || ctx.At(lex_ref))
	{
		if(ctx.Consume(lex_obracket))
		{
			SynBase *size = ParseTernaryExpr(ctx);

			AssertConsume(ctx, lex_cbracket, "ERROR: matching ']' not found");

			base = new SynTypeArray(base, size);
		}
		else if(ctx.Consume(lex_ref))
		{
			if(ctx.Consume(lex_oparen))
			{
				IntrusiveList<SynType> arguments;

				if(SynType *argument = ParseType(ctx))
				{
					arguments.push_back(argument);

					while(ctx.Consume(lex_comma))
					{
						argument = ParseType(ctx);

						if(!argument)
							Stop(ctx, ctx.Position(), "ERROR: type is expected after ','");

						arguments.push_back(argument);
					}
				}

				base = new SynTypeFunction(base, arguments);
			}
			else
			{
				base = new SynTypeReference(base);
			}
		}
	}

	return base;
}

SynBase* ParseTerminal(ParseContext &ctx)
{
	const char *start = ctx.Position();

	if(ctx.Consume(lex_true))
		return new SynBool(start, true);

	if(ctx.Consume(lex_false))
		return new SynBool(start, false);

	if(ctx.At(lex_number))
		return new SynNumber(start, ctx.Consume());

	if(ctx.Consume(lex_nullptr))
		return new SynNullptr(start);

	return NULL;
}

SynBase* ParseArithmetic(ParseContext &ctx)
{
	SynBase *lhs = ParseTerminal(ctx);

	if(!lhs)
		return NULL;

	unsigned startSize = ctx.binaryOpStack.size();

	while(SynBinaryOpType binaryOp = GetBinaryOpType(ctx.Peek()))
	{
		const char *start = ctx.Position();

		ctx.Skip();

		while(ctx.binaryOpStack.size() > startSize && GetBinaryOpPrecedence(ctx.binaryOpStack.back().type) <= GetBinaryOpPrecedence(binaryOp))
		{
			lhs = new SynBinaryOp(ctx.binaryOpStack.back().pos, ctx.binaryOpStack.back().type, lhs, ctx.binaryOpStack.back().value);

			ctx.binaryOpStack.pop_back();
		}

		SynBase *value = ParseTerminal(ctx);

		if(!value)
			Stop(ctx, ctx.Position(), "ERROR: terminal expression not found after binary operation");

		ctx.binaryOpStack.push_back(SynBinaryOpElement(start, binaryOp, value));
	}

	while(ctx.binaryOpStack.size() > startSize)
	{
		lhs = new SynBinaryOp(ctx.binaryOpStack.back().pos, ctx.binaryOpStack.back().type, lhs, ctx.binaryOpStack.back().value);

		ctx.binaryOpStack.pop_back();
	}

	return lhs;
}

SynBase* ParseTernaryExpr(ParseContext &ctx)
{
	SynBase *condition = ParseArithmetic(ctx);

	if(!condition)
		return NULL;

	return condition;
}

SynBase* ParseAssignment(ParseContext &ctx)
{
	SynBase *lhs = ParseTernaryExpr(ctx);

	if(!lhs)
		return NULL;

	return lhs;
}

SynReturn* ParseReturn(ParseContext &ctx)
{
	const char *start = ctx.Position();

	if(ctx.Consume(lex_return))
	{
		// Optional
		SynBase *value = ParseAssignment(ctx);

		AssertConsume(ctx, lex_semicolon, "ERROR: return statement must be followed by ';'");

		return new SynReturn(start, value);
	}

	return NULL;
}

SynTypedef* ParseTypedef(ParseContext &ctx)
{
	const char *start = ctx.Position();

	if(ctx.Consume(lex_typedef))
	{
		SynType *type = ParseType(ctx);

		if(!type)
			Stop(ctx, ctx.Position(), "ERROR: typename expected after typedef");

		AssertAt(ctx, lex_string, "ERROR: alias name expected after typename in typedef expression");

		InplaceStr alias = ctx.Consume();

		AssertConsume(ctx, lex_semicolon, "ERROR: ';' not found after typedef");

		return new SynTypedef(start, type, alias);
	}

	return NULL;
}

SynVariableDefinition* ParseVariableDefinition(ParseContext &ctx)
{
	const char *start = ctx.Position();

	if(ctx.At(lex_string))
	{
		InplaceStr name = ctx.Consume();
		SynBase *initializer = NULL;

		if(ctx.Consume(lex_set))
		{
			initializer = ParseAssignment(ctx);

			if(!initializer)
				Stop(ctx, ctx.Position(), "ERROR: expression not found after '='");
		}

		return new SynVariableDefinition(start, name, initializer);
	}

	return NULL;
}

SynVariableDefinitions* ParseVariableDefinitions(ParseContext &ctx)
{
	const char *start = ctx.Position();

	if(SynType *type = ParseType(ctx))
	{
		IntrusiveList<SynVariableDefinition> definitions;

		SynVariableDefinition *definition = ParseVariableDefinition(ctx);

		if(!definition)
			return NULL;

		definitions.push_back(definition);

		while(ctx.Consume(lex_comma))
		{
			definition = ParseVariableDefinition(ctx);

			if(!definition)
				Stop(ctx, ctx.Position(), "ERROR: next variable definition excepted after ','");

			definitions.push_back(definition);
		}

		AssertConsume(ctx, lex_semicolon, "ERROR: ';' not found after variable definition");

		return new SynVariableDefinitions(start, type, definitions);
	}

	return NULL;
}

SynBase* ParseExpression(ParseContext &ctx)
{
	//const char *start = ctx.Position();

	if(ctx.At(lex_return))
		return ParseReturn(ctx);

	if(ctx.At(lex_typedef))
		return ParseTypedef(ctx);

	if(SynBase *node = ParseVariableDefinitions(ctx))
		return node;

	return NULL;
}

SynModuleImport* ParseImport(ParseContext &ctx)
{
	const char *start = ctx.Position();

	if(ctx.Consume(lex_import))
	{
		AssertAt(ctx, lex_string, "ERROR: string expected after import");

		Stop(ctx, start, "ERROR: not implemented");

		return NULL;
	}

	return NULL;
}

SynBase* ParseModule(ParseContext &ctx)
{
	const char *start = ctx.Position();

	IntrusiveList<SynModuleImport> imports;

	while(SynModuleImport *import = ParseImport(ctx))
		imports.push_back(import);

	IntrusiveList<SynBase> expressions;

	while(SynBase* expression = ParseExpression(ctx))
		expressions.push_back(expression);

	return new SynModule(start, imports, expressions);
}

SynBase* Parse(ParseContext &ctx)
{
	SynBase *tree = ParseModule(ctx);

	if(!ctx.Consume(lex_none))
		Stop(ctx, ctx.Position(), "ERROR: unexpected symbol");

	return tree;
}

SynBase* Parse(ParseContext &ctx, const char *code)
{
	Lexer lexer;

	lexer.Lexify(code);

	if(!setjmp(errorHandler))
	{
		ctx.currentLexeme = lexer.GetStreamStart();

		return Parse(ctx);
	}

	return NULL;
}
<commit_msg>Added back-tracking if variable definition parsing aborts<commit_after>#include "ParseTree.h"

#include <assert.h>
#include <setjmp.h>
#include <stdarg.h>

#include "Lexer.h"

namespace
{
	jmp_buf errorHandler;

	void Stop(ParseContext &ctx, const char *pos, const char *msg, va_list args)
	{
		ctx.errorPos = pos;

		char errorText[4096];

		vsnprintf(errorText, 4096, msg, args);
		errorText[4096 - 1] = '\0';

		ctx.errorMsg = InplaceStr(errorText);

		longjmp(errorHandler, 1);
	}

	void Stop(ParseContext &ctx, const char *pos, const char *msg, ...)
	{
		va_list args;
		va_start(args, msg);

		Stop(ctx, pos, msg, args);
	}

	void AssertAt(ParseContext &ctx, LexemeType type, const char *msg, ...)
	{
		if(!ctx.At(type))
		{
			va_list args;
			va_start(args, msg);

			Stop(ctx, ctx.Position(), msg, args);
		}
	}

	void AssertConsume(ParseContext &ctx, LexemeType type, const char *msg, ...)
	{
		if(!ctx.Consume(type))
		{
			va_list args;
			va_start(args, msg);

			Stop(ctx, ctx.Position(), msg, args);
		}
	}
}

SynBinaryOpType GetBinaryOpType(LexemeType type)
{
	switch(type)
	{
	case lex_add:
		return SYN_BINARY_OP_ADD;
	case lex_sub:
		return SYN_BINARY_OP_SUB;
	case lex_mul:
		return SYN_BINARY_OP_MUL;
	case lex_div:
		return SYN_BINARY_OP_DIV;
	case lex_mod:
		return SYN_BINARY_OP_MOD;
	case lex_pow:
		return SYN_BINARY_OP_POW;
	case lex_less:
		return SYN_BINARY_OP_LESS;
	case lex_lequal:
		return SYN_BINARY_OP_LESS_EQUAL;
	case lex_shl:
		return SYN_BINARY_OP_SHL;
	case lex_greater:
		return SYN_BINARY_OP_GREATER;
	case lex_gequal:
		return SYN_BINARY_OP_GREATER_EQUAL;
	case lex_shr:
		return SYN_BINARY_OP_SHR;
	case lex_equal:
		return SYN_BINARY_OP_EQUAL;
	case lex_nequal:
		return SYN_BINARY_OP_NOT_EQUAL;
	case lex_bitand:
		return SYN_BINARY_OP_BIT_AND;
	case lex_bitor:
		return SYN_BINARY_OP_BIT_OR;
	case lex_bitxor:
		return SYN_BINARY_OP_BIT_XOR;
	case lex_logand:
		return SYN_BINARY_OP_LOGICAL_AND;
	case lex_logor:
		return SYN_BINARY_OP_LOGICAL_OR;
	case lex_logxor:
		return SYN_BINARY_OP_LOGICAL_XOR;
	case lex_in:
		return SYN_BINARY_OP_IN;
	}

	return SYN_BINARY_OP_UNKNOWN;
}

unsigned GetBinaryOpPrecedence(SynBinaryOpType op)
{
	switch(op)
	{
	case SYN_BINARY_OP_ADD:
		return 2;
	case SYN_BINARY_OP_SUB:
		return 2;
	case SYN_BINARY_OP_MUL:
		return 1;
	case SYN_BINARY_OP_DIV:
		return 1;
	case SYN_BINARY_OP_MOD:
		return 1;
	case SYN_BINARY_OP_POW:
		return 0;
	case SYN_BINARY_OP_LESS:
		return 4;
	case SYN_BINARY_OP_LESS_EQUAL:
		return 4;
	case SYN_BINARY_OP_SHL:
		return 3;
	case SYN_BINARY_OP_GREATER:
		return 4;
	case SYN_BINARY_OP_GREATER_EQUAL:
		return 4;
	case SYN_BINARY_OP_SHR:
		return 3;
	case SYN_BINARY_OP_EQUAL:
		return 5;
	case SYN_BINARY_OP_NOT_EQUAL:
		return 5;
	case SYN_BINARY_OP_BIT_AND:
		return 6;
	case SYN_BINARY_OP_BIT_OR:
		return 8;
	case SYN_BINARY_OP_BIT_XOR:
		return 7;
	case SYN_BINARY_OP_LOGICAL_AND:
		return 9;
	case SYN_BINARY_OP_LOGICAL_OR:
		return 11;
	case SYN_BINARY_OP_LOGICAL_XOR:
		return 10;
	case SYN_BINARY_OP_IN:
		return 12;
	}

	return 0;
}

ParseContext::ParseContext()
{
	errorPos = 0;
}

LexemeType ParseContext::Peek()
{
	return currentLexeme->type;
}

bool ParseContext::At(LexemeType type)
{
	return currentLexeme->type == type;
}

bool ParseContext::Consume(LexemeType type)
{
	if(currentLexeme->type == type)
	{
		Skip();
		return true;
	}

	return false;
}

InplaceStr ParseContext::Consume()
{
	InplaceStr str(currentLexeme->pos, currentLexeme->length);

	Skip();

	return str;
}

void ParseContext::Skip()
{
	if(currentLexeme->type != lex_none)
		currentLexeme++;
}

const char* ParseContext::Position()
{
	return currentLexeme->pos;
}

SynBase* ParseTernaryExpr(ParseContext &ctx);

SynType* ParseTerminalType(ParseContext &ctx)
{
	if(ctx.At(lex_string))
	{
		InplaceStr name = ctx.Consume();

		return new SynTypeSimple(name);
	}

	if(ctx.Consume(lex_auto))
		return new SynTypeAuto();

	if(ctx.Consume(lex_generic))
		return new SynTypeGeneric();

	return NULL;
}

SynType* ParseType(ParseContext &ctx)
{
	SynType *base = ParseTerminalType(ctx);

	if(!base)
		return NULL;

	while(ctx.At(lex_obracket) || ctx.At(lex_ref))
	{
		if(ctx.Consume(lex_obracket))
		{
			SynBase *size = ParseTernaryExpr(ctx);

			AssertConsume(ctx, lex_cbracket, "ERROR: matching ']' not found");

			base = new SynTypeArray(base, size);
		}
		else if(ctx.Consume(lex_ref))
		{
			if(ctx.Consume(lex_oparen))
			{
				IntrusiveList<SynType> arguments;

				if(SynType *argument = ParseType(ctx))
				{
					arguments.push_back(argument);

					while(ctx.Consume(lex_comma))
					{
						argument = ParseType(ctx);

						if(!argument)
							Stop(ctx, ctx.Position(), "ERROR: type is expected after ','");

						arguments.push_back(argument);
					}
				}

				base = new SynTypeFunction(base, arguments);
			}
			else
			{
				base = new SynTypeReference(base);
			}
		}
	}

	return base;
}

SynBase* ParseTerminal(ParseContext &ctx)
{
	const char *start = ctx.Position();

	if(ctx.Consume(lex_true))
		return new SynBool(start, true);

	if(ctx.Consume(lex_false))
		return new SynBool(start, false);

	if(ctx.At(lex_number))
		return new SynNumber(start, ctx.Consume());

	if(ctx.Consume(lex_nullptr))
		return new SynNullptr(start);

	return NULL;
}

SynBase* ParseArithmetic(ParseContext &ctx)
{
	SynBase *lhs = ParseTerminal(ctx);

	if(!lhs)
		return NULL;

	unsigned startSize = ctx.binaryOpStack.size();

	while(SynBinaryOpType binaryOp = GetBinaryOpType(ctx.Peek()))
	{
		const char *start = ctx.Position();

		ctx.Skip();

		while(ctx.binaryOpStack.size() > startSize && GetBinaryOpPrecedence(ctx.binaryOpStack.back().type) <= GetBinaryOpPrecedence(binaryOp))
		{
			lhs = new SynBinaryOp(ctx.binaryOpStack.back().pos, ctx.binaryOpStack.back().type, lhs, ctx.binaryOpStack.back().value);

			ctx.binaryOpStack.pop_back();
		}

		SynBase *value = ParseTerminal(ctx);

		if(!value)
			Stop(ctx, ctx.Position(), "ERROR: terminal expression not found after binary operation");

		ctx.binaryOpStack.push_back(SynBinaryOpElement(start, binaryOp, value));
	}

	while(ctx.binaryOpStack.size() > startSize)
	{
		lhs = new SynBinaryOp(ctx.binaryOpStack.back().pos, ctx.binaryOpStack.back().type, lhs, ctx.binaryOpStack.back().value);

		ctx.binaryOpStack.pop_back();
	}

	return lhs;
}

SynBase* ParseTernaryExpr(ParseContext &ctx)
{
	SynBase *condition = ParseArithmetic(ctx);

	if(!condition)
		return NULL;

	return condition;
}

SynBase* ParseAssignment(ParseContext &ctx)
{
	SynBase *lhs = ParseTernaryExpr(ctx);

	if(!lhs)
		return NULL;

	return lhs;
}

SynReturn* ParseReturn(ParseContext &ctx)
{
	const char *start = ctx.Position();

	if(ctx.Consume(lex_return))
	{
		// Optional
		SynBase *value = ParseAssignment(ctx);

		AssertConsume(ctx, lex_semicolon, "ERROR: return statement must be followed by ';'");

		return new SynReturn(start, value);
	}

	return NULL;
}

SynTypedef* ParseTypedef(ParseContext &ctx)
{
	const char *start = ctx.Position();

	if(ctx.Consume(lex_typedef))
	{
		SynType *type = ParseType(ctx);

		if(!type)
			Stop(ctx, ctx.Position(), "ERROR: typename expected after typedef");

		AssertAt(ctx, lex_string, "ERROR: alias name expected after typename in typedef expression");

		InplaceStr alias = ctx.Consume();

		AssertConsume(ctx, lex_semicolon, "ERROR: ';' not found after typedef");

		return new SynTypedef(start, type, alias);
	}

	return NULL;
}

SynVariableDefinition* ParseVariableDefinition(ParseContext &ctx)
{
	const char *start = ctx.Position();

	if(ctx.At(lex_string))
	{
		InplaceStr name = ctx.Consume();
		SynBase *initializer = NULL;

		if(ctx.Consume(lex_set))
		{
			initializer = ParseAssignment(ctx);

			if(!initializer)
				Stop(ctx, ctx.Position(), "ERROR: expression not found after '='");
		}

		return new SynVariableDefinition(start, name, initializer);
	}

	return NULL;
}

SynVariableDefinitions* ParseVariableDefinitions(ParseContext &ctx)
{
	const char *start = ctx.Position();
	Lexeme *lexeme = ctx.currentLexeme;

	if(SynType *type = ParseType(ctx))
	{
		IntrusiveList<SynVariableDefinition> definitions;

		SynVariableDefinition *definition = ParseVariableDefinition(ctx);

		if(!definition)
		{
			// Backtrack
			ctx.currentLexeme = lexeme;

			return NULL;
		}

		definitions.push_back(definition);

		while(ctx.Consume(lex_comma))
		{
			definition = ParseVariableDefinition(ctx);

			if(!definition)
				Stop(ctx, ctx.Position(), "ERROR: next variable definition excepted after ','");

			definitions.push_back(definition);
		}

		AssertConsume(ctx, lex_semicolon, "ERROR: ';' not found after variable definition");

		return new SynVariableDefinitions(start, type, definitions);
	}

	return NULL;
}

SynBase* ParseExpression(ParseContext &ctx)
{
	//const char *start = ctx.Position();

	if(ctx.At(lex_return))
		return ParseReturn(ctx);

	if(ctx.At(lex_typedef))
		return ParseTypedef(ctx);

	if(SynBase *node = ParseVariableDefinitions(ctx))
		return node;

	return NULL;
}

SynModuleImport* ParseImport(ParseContext &ctx)
{
	const char *start = ctx.Position();

	if(ctx.Consume(lex_import))
	{
		AssertAt(ctx, lex_string, "ERROR: string expected after import");

		Stop(ctx, start, "ERROR: not implemented");

		return NULL;
	}

	return NULL;
}

SynBase* ParseModule(ParseContext &ctx)
{
	const char *start = ctx.Position();

	IntrusiveList<SynModuleImport> imports;

	while(SynModuleImport *import = ParseImport(ctx))
		imports.push_back(import);

	IntrusiveList<SynBase> expressions;

	while(SynBase* expression = ParseExpression(ctx))
		expressions.push_back(expression);

	return new SynModule(start, imports, expressions);
}

SynBase* Parse(ParseContext &ctx)
{
	SynBase *tree = ParseModule(ctx);

	if(!ctx.Consume(lex_none))
		Stop(ctx, ctx.Position(), "ERROR: unexpected symbol");

	return tree;
}

SynBase* Parse(ParseContext &ctx, const char *code)
{
	Lexer lexer;

	lexer.Lexify(code);

	if(!setjmp(errorHandler))
	{
		ctx.currentLexeme = lexer.GetStreamStart();

		return Parse(ctx);
	}

	return NULL;
}
<|endoftext|>


<commit_before>/************************************************************************
*
* Flood Project  (2008-201x)
* Licensed under the simplified BSD license. All rights reserved.
*
************************************************************************/

#include "Core/API.h"
#include "Core/Concurrency.h"
#include "Core/Memory.h"

#ifdef PLATFORM_WINDOWS

#define WIN32_LEAN_AND_MEAN
#define NOMINMAX

#include <Windows.h>
#include <process.h>

NAMESPACE_CORE_BEGIN

//-----------------------------------//

#pragma region Threads

static bool ThreadIsValid(Thread* thread)
{
	return thread && thread->Handle;
}

bool ThreadJoin(Thread* thread)
{
	if( !ThreadIsValid(thread) || !thread->IsRunning )
		return false;

	thread->IsRunning = false;
	assert(thread->Handle);

	return ::WaitForSingleObject(thread->Handle, INFINITE) != WAIT_FAILED;
}

//-----------------------------------//

bool ThreadPause(Thread* thread)
{
	if( !ThreadIsValid(thread) )
		return false;

	return ::SuspendThread((HANDLE) thread->Handle) != -1;
}

//-----------------------------------//

bool ThreadResume(Thread* thread)
{
	if( !ThreadIsValid(thread) )
		return false;

	return ::ResumeThread((HANDLE) thread->Handle) != -1;
}

//-----------------------------------//

bool ThreadSetPriority(Thread* thread, ThreadPriority threadPriority)
{
	thread->Priority = threadPriority;

	int priority = 0;

	switch(threadPriority)
	{
	case ThreadPriority::Low:
		priority = THREAD_PRIORITY_BELOW_NORMAL;
		break;
	case ThreadPriority::Normal:
		priority = THREAD_PRIORITY_NORMAL;
		break;
	case ThreadPriority::High:
		priority = THREAD_PRIORITY_ABOVE_NORMAL;
		break;
	};

	return ::SetThreadPriority((HANDLE) thread->Handle, priority) != 0;
}

//-----------------------------------//

unsigned int WINAPI _ThreadMain(void* ptr)
{
	Thread* thread = (Thread*) ptr;
	thread->Function(thread, thread->Userdata);

	// _endthread automatically closes the thread handle.
	// ::CloseHandle((HANDLE) thread->Handle);

	thread->Handle = nullptr;
	thread->IsRunning = false;

	::_endthreadex(0);

	// Should not be reached.
	return 0;
}

//-----------------------------------//

bool ThreadStart(Thread* thread, ThreadFunction function, void* data)
{
	if (!thread || !function || thread->IsRunning)
		return false;

	thread->Function = function;
	thread->Userdata = data;

	// Create the thread suspended.
	uintptr_t handle = ::_beginthreadex(nullptr,
		0, _ThreadMain, thread, CREATE_SUSPENDED, nullptr);

	thread->Handle = (void*) handle;

	// State is set up, resume the thread.
	if( thread->Handle > 0 )
	{
		thread->IsRunning = true;
		ThreadSetPriority(thread, ThreadPriority::Normal);
		ThreadResume(thread);
	}

	return thread->Handle != 0;
}

//-----------------------------------//

static void SetThreadName( DWORD dwThreadID, LPCSTR szThreadName )
{
	struct THREADNAME_INFO
	{
		DWORD dwType;
		LPCSTR szName;
		DWORD dwThreadID;
		DWORD dwFlags;
	};

	THREADNAME_INFO info;
	info.dwType = 0x1000;
	info.szName = szThreadName;
	info.dwThreadID = dwThreadID;
	info.dwFlags = 0;

	__try
	{
        RaiseException( 0x406D1388, 0, sizeof(info)/sizeof(DWORD), (const ULONG_PTR*)&info );
	}
	__except(EXCEPTION_CONTINUE_EXECUTION) { }
}

//-----------------------------------//

typedef DWORD (WINAPI *GetThreadIdFn)(HANDLE);

void ThreadSetName( Thread* thread, const char* name )
{
	if( !thread ) return;

	HMODULE module = GetModuleHandleA("Kernel32.dll");
	if( module == NULL ) return;

	// GetThreadId only exists on Vista or later versions, test if it exists
	// at runtime or the program will not run due to dynamic linking errors.

	GetThreadIdFn pGetThreadId = (GetThreadIdFn) GetProcAddress(module, "GetThreadId");
	if( pGetThreadId == NULL ) return;

	SetThreadName( pGetThreadId(thread->Handle), name );
}

#pragma endregion

//-----------------------------------//

#pragma region Mutex

struct Mutex
{
	CRITICAL_SECTION Handle;
};

#define CS_CREATE_IMMEDIATELY_ON_WIN2000 0x080000000
#define CS_SPIN_COUNT 1500

//-----------------------------------//

Mutex* MutexCreate(Allocator* alloc)
{
	Mutex* mutex = Allocate(alloc, Mutex);
	MutexInit(mutex);

	return mutex;
}

//-----------------------------------//

API_CORE void MutexInit(Mutex* mutex)
{
	if (!mutex) return;

	LPCRITICAL_SECTION cs = (LPCRITICAL_SECTION) &mutex->Handle;

	BOOL result = ::InitializeCriticalSectionAndSpinCount(
		cs, CS_SPIN_COUNT | CS_CREATE_IMMEDIATELY_ON_WIN2000);

	assert(result && "Could not initialize critical section");
}

//-----------------------------------//

void MutexDestroy(Mutex* mutex)
{
	if (!mutex) return;

	LPCRITICAL_SECTION cs = (LPCRITICAL_SECTION) &mutex->Handle;
	DeleteCriticalSection(cs);

	Deallocate(mutex);
}

//-----------------------------------//

void MutexLock(Mutex* mutex)
{
	LPCRITICAL_SECTION cs = (LPCRITICAL_SECTION) &mutex->Handle;
	::EnterCriticalSection(cs);
}

//-----------------------------------//

void MutexUnlock(Mutex* mutex)
{
	LPCRITICAL_SECTION cs = (LPCRITICAL_SECTION) &mutex->Handle;
	::LeaveCriticalSection(cs);
}

#pragma endregion

//-----------------------------------//

#pragma region Condition Variables

/**
 * Windows only provides condition variables since Windows Vista,
 * so we must query those functions explicitly to be able to function
 * under Windows XP, though right now we provide no emulation.
 */

typedef VOID (WINAPI *InitializeConditionVariableFn)(PCONDITION_VARIABLE);
typedef BOOL (WINAPI *SleepConditionVariableFn)(PCONDITION_VARIABLE,
												PCRITICAL_SECTION, DWORD);
typedef VOID (WINAPI *WakeConditionVariableFn)(PCONDITION_VARIABLE);
typedef VOID (WINAPI *WakeAllConditionVariableFn)(PCONDITION_VARIABLE);

struct ConditionFunctions
{
	InitializeConditionVariableFn Init;
	SleepConditionVariableFn Sleep;
	WakeConditionVariableFn Wake;
	WakeAllConditionVariableFn WakeAll;
};

static ConditionFunctions* IntializeConditionFunctions()
{
	HMODULE module = GetModuleHandleA("Kernel32.dll");

	if( module == NULL )
		return 0;

	static ConditionFunctions functions;
	functions.Init = (InitializeConditionVariableFn)
		GetProcAddress(module, "InitializeConditionVariable");

	functions.Sleep = (SleepConditionVariableFn)
		GetProcAddress(module, "SleepConditionVariableCS");

	functions.Wake = (WakeConditionVariableFn)
		GetProcAddress(module, "WakeConditionVariable");

	functions.WakeAll = (WakeAllConditionVariableFn)
		GetProcAddress(module, "WakeAllConditionVariable");

	return &functions;
}

static ConditionFunctions* g_ConditionFunctions = IntializeConditionFunctions();

struct Condition
{
	CONDITION_VARIABLE Handle;
};

//-----------------------------------//

Condition* ConditionCreate(Allocator* alloc)
{
	Condition* cond = Allocate(alloc, Condition);
	ConditionInit(cond);

	return cond;
}

//-----------------------------------//

void ConditionDestroy(Condition* cond)
{
	Deallocate(cond);
}

//-----------------------------------//

void ConditionInit(Condition* cond)
{
	if( !cond ) return;

	if( !g_ConditionFunctions )
		return;

	CONDITION_VARIABLE* cvar = (CONDITION_VARIABLE*) &cond->Handle;

	if(g_ConditionFunctions->Init)
		g_ConditionFunctions->Init(cvar);
}

//-----------------------------------//

void ConditionWait(Condition* cond, Mutex* mutex)
{
	if( !cond || !mutex ) return;
	CONDITION_VARIABLE* cvar = (CONDITION_VARIABLE*) &cond->Handle;
	LPCRITICAL_SECTION cs = (LPCRITICAL_SECTION) &mutex->Handle;

	BOOL ret = FALSE;

	if(g_ConditionFunctions->Sleep)
	{
		ret = g_ConditionFunctions->Sleep(cvar, cs, INFINITE);
		assert( ret != FALSE );
	}
}

//-----------------------------------//

void ConditionWakeOne(Condition* cond)
{
	if( !cond ) return;
	CONDITION_VARIABLE* cvar = (CONDITION_VARIABLE*) &cond->Handle;

	if(g_ConditionFunctions->Wake)
		g_ConditionFunctions->Wake(cvar);
}

//-----------------------------------//

void ConditionWakeAll(Condition* cond)
{
	if( !cond ) return;
	CONDITION_VARIABLE* cvar = (CONDITION_VARIABLE*) &cond->Handle;

	if(g_ConditionFunctions->WakeAll)
		g_ConditionFunctions->WakeAll(cvar);
}

#pragma endregion

//-----------------------------------//

#pragma region Atomics

int32 AtomicRead(volatile Atomic* atomic)
{
	return ::InterlockedExchangeAdd(atomic, 0);
}

int32 AtomicWrite(volatile Atomic* atomic, int32 value)
{
	return ::InterlockedExchange(atomic, value);
}

int32 AtomicAdd(volatile Atomic* atomic, int32 value)
{
	return ::InterlockedExchangeAdd(atomic, value);
}

int32 AtomicIncrement(volatile Atomic* atomic)
{
	return ::InterlockedIncrement(atomic);
}

int32 AtomicDecrement(volatile Atomic* atomic)
{
	return ::InterlockedDecrement(atomic);
}

#pragma endregion

//-----------------------------------//

NAMESPACE_CORE_END

#endif
<commit_msg>Removed useless API macro.<commit_after>/************************************************************************
*
* Flood Project  (2008-201x)
* Licensed under the simplified BSD license. All rights reserved.
*
************************************************************************/

#include "Core/API.h"
#include "Core/Concurrency.h"
#include "Core/Memory.h"

#ifdef PLATFORM_WINDOWS

#define WIN32_LEAN_AND_MEAN
#define NOMINMAX

#include <Windows.h>
#include <process.h>

NAMESPACE_CORE_BEGIN

//-----------------------------------//

#pragma region Threads

static bool ThreadIsValid(Thread* thread)
{
	return thread && thread->Handle;
}

bool ThreadJoin(Thread* thread)
{
	if( !ThreadIsValid(thread) || !thread->IsRunning )
		return false;

	thread->IsRunning = false;
	assert(thread->Handle);

	return ::WaitForSingleObject(thread->Handle, INFINITE) != WAIT_FAILED;
}

//-----------------------------------//

bool ThreadPause(Thread* thread)
{
	if( !ThreadIsValid(thread) )
		return false;

	return ::SuspendThread((HANDLE) thread->Handle) != -1;
}

//-----------------------------------//

bool ThreadResume(Thread* thread)
{
	if( !ThreadIsValid(thread) )
		return false;

	return ::ResumeThread((HANDLE) thread->Handle) != -1;
}

//-----------------------------------//

bool ThreadSetPriority(Thread* thread, ThreadPriority threadPriority)
{
	thread->Priority = threadPriority;

	int priority = 0;

	switch(threadPriority)
	{
	case ThreadPriority::Low:
		priority = THREAD_PRIORITY_BELOW_NORMAL;
		break;
	case ThreadPriority::Normal:
		priority = THREAD_PRIORITY_NORMAL;
		break;
	case ThreadPriority::High:
		priority = THREAD_PRIORITY_ABOVE_NORMAL;
		break;
	};

	return ::SetThreadPriority((HANDLE) thread->Handle, priority) != 0;
}

//-----------------------------------//

unsigned int WINAPI _ThreadMain(void* ptr)
{
	Thread* thread = (Thread*) ptr;
	thread->Function(thread, thread->Userdata);

	// _endthread automatically closes the thread handle.
	// ::CloseHandle((HANDLE) thread->Handle);

	thread->Handle = nullptr;
	thread->IsRunning = false;

	::_endthreadex(0);

	// Should not be reached.
	return 0;
}

//-----------------------------------//

bool ThreadStart(Thread* thread, ThreadFunction function, void* data)
{
	if (!thread || !function || thread->IsRunning)
		return false;

	thread->Function = function;
	thread->Userdata = data;

	// Create the thread suspended.
	uintptr_t handle = ::_beginthreadex(nullptr,
		0, _ThreadMain, thread, CREATE_SUSPENDED, nullptr);

	thread->Handle = (void*) handle;

	// State is set up, resume the thread.
	if( thread->Handle > 0 )
	{
		thread->IsRunning = true;
		ThreadSetPriority(thread, ThreadPriority::Normal);
		ThreadResume(thread);
	}

	return thread->Handle != 0;
}

//-----------------------------------//

static void SetThreadName( DWORD dwThreadID, LPCSTR szThreadName )
{
	struct THREADNAME_INFO
	{
		DWORD dwType;
		LPCSTR szName;
		DWORD dwThreadID;
		DWORD dwFlags;
	};

	THREADNAME_INFO info;
	info.dwType = 0x1000;
	info.szName = szThreadName;
	info.dwThreadID = dwThreadID;
	info.dwFlags = 0;

	__try
	{
        RaiseException( 0x406D1388, 0, sizeof(info)/sizeof(DWORD), (const ULONG_PTR*)&info );
	}
	__except(EXCEPTION_CONTINUE_EXECUTION) { }
}

//-----------------------------------//

typedef DWORD (WINAPI *GetThreadIdFn)(HANDLE);

void ThreadSetName( Thread* thread, const char* name )
{
	if( !thread ) return;

	HMODULE module = GetModuleHandleA("Kernel32.dll");
	if( module == NULL ) return;

	// GetThreadId only exists on Vista or later versions, test if it exists
	// at runtime or the program will not run due to dynamic linking errors.

	GetThreadIdFn pGetThreadId = (GetThreadIdFn) GetProcAddress(module, "GetThreadId");
	if( pGetThreadId == NULL ) return;

	SetThreadName( pGetThreadId(thread->Handle), name );
}

#pragma endregion

//-----------------------------------//

#pragma region Mutex

struct Mutex
{
	CRITICAL_SECTION Handle;
};

#define CS_CREATE_IMMEDIATELY_ON_WIN2000 0x080000000
#define CS_SPIN_COUNT 1500

//-----------------------------------//

Mutex* MutexCreate(Allocator* alloc)
{
	Mutex* mutex = Allocate(alloc, Mutex);
	MutexInit(mutex);

	return mutex;
}

//-----------------------------------//

void MutexInit(Mutex* mutex)
{
	if (!mutex) return;

	LPCRITICAL_SECTION cs = (LPCRITICAL_SECTION) &mutex->Handle;

	BOOL result = ::InitializeCriticalSectionAndSpinCount(
		cs, CS_SPIN_COUNT | CS_CREATE_IMMEDIATELY_ON_WIN2000);

	assert(result && "Could not initialize critical section");
}

//-----------------------------------//

void MutexDestroy(Mutex* mutex)
{
	if (!mutex) return;

	LPCRITICAL_SECTION cs = (LPCRITICAL_SECTION) &mutex->Handle;
	DeleteCriticalSection(cs);

	Deallocate(mutex);
}

//-----------------------------------//

void MutexLock(Mutex* mutex)
{
	LPCRITICAL_SECTION cs = (LPCRITICAL_SECTION) &mutex->Handle;
	::EnterCriticalSection(cs);
}

//-----------------------------------//

void MutexUnlock(Mutex* mutex)
{
	LPCRITICAL_SECTION cs = (LPCRITICAL_SECTION) &mutex->Handle;
	::LeaveCriticalSection(cs);
}

#pragma endregion

//-----------------------------------//

#pragma region Condition Variables

/**
 * Windows only provides condition variables since Windows Vista,
 * so we must query those functions explicitly to be able to function
 * under Windows XP, though right now we provide no emulation.
 */

typedef VOID (WINAPI *InitializeConditionVariableFn)(PCONDITION_VARIABLE);
typedef BOOL (WINAPI *SleepConditionVariableFn)(PCONDITION_VARIABLE,
												PCRITICAL_SECTION, DWORD);
typedef VOID (WINAPI *WakeConditionVariableFn)(PCONDITION_VARIABLE);
typedef VOID (WINAPI *WakeAllConditionVariableFn)(PCONDITION_VARIABLE);

struct ConditionFunctions
{
	InitializeConditionVariableFn Init;
	SleepConditionVariableFn Sleep;
	WakeConditionVariableFn Wake;
	WakeAllConditionVariableFn WakeAll;
};

static ConditionFunctions* IntializeConditionFunctions()
{
	HMODULE module = GetModuleHandleA("Kernel32.dll");

	if( module == NULL )
		return 0;

	static ConditionFunctions functions;
	functions.Init = (InitializeConditionVariableFn)
		GetProcAddress(module, "InitializeConditionVariable");

	functions.Sleep = (SleepConditionVariableFn)
		GetProcAddress(module, "SleepConditionVariableCS");

	functions.Wake = (WakeConditionVariableFn)
		GetProcAddress(module, "WakeConditionVariable");

	functions.WakeAll = (WakeAllConditionVariableFn)
		GetProcAddress(module, "WakeAllConditionVariable");

	return &functions;
}

static ConditionFunctions* g_ConditionFunctions = IntializeConditionFunctions();

struct Condition
{
	CONDITION_VARIABLE Handle;
};

//-----------------------------------//

Condition* ConditionCreate(Allocator* alloc)
{
	Condition* cond = Allocate(alloc, Condition);
	ConditionInit(cond);

	return cond;
}

//-----------------------------------//

void ConditionDestroy(Condition* cond)
{
	Deallocate(cond);
}

//-----------------------------------//

void ConditionInit(Condition* cond)
{
	if( !cond ) return;

	if( !g_ConditionFunctions )
		return;

	CONDITION_VARIABLE* cvar = (CONDITION_VARIABLE*) &cond->Handle;

	if(g_ConditionFunctions->Init)
		g_ConditionFunctions->Init(cvar);
}

//-----------------------------------//

void ConditionWait(Condition* cond, Mutex* mutex)
{
	if( !cond || !mutex ) return;
	CONDITION_VARIABLE* cvar = (CONDITION_VARIABLE*) &cond->Handle;
	LPCRITICAL_SECTION cs = (LPCRITICAL_SECTION) &mutex->Handle;

	BOOL ret = FALSE;

	if(g_ConditionFunctions->Sleep)
	{
		ret = g_ConditionFunctions->Sleep(cvar, cs, INFINITE);
		assert( ret != FALSE );
	}
}

//-----------------------------------//

void ConditionWakeOne(Condition* cond)
{
	if( !cond ) return;
	CONDITION_VARIABLE* cvar = (CONDITION_VARIABLE*) &cond->Handle;

	if(g_ConditionFunctions->Wake)
		g_ConditionFunctions->Wake(cvar);
}

//-----------------------------------//

void ConditionWakeAll(Condition* cond)
{
	if( !cond ) return;
	CONDITION_VARIABLE* cvar = (CONDITION_VARIABLE*) &cond->Handle;

	if(g_ConditionFunctions->WakeAll)
		g_ConditionFunctions->WakeAll(cvar);
}

#pragma endregion

//-----------------------------------//

#pragma region Atomics

int32 AtomicRead(volatile Atomic* atomic)
{
	return ::InterlockedExchangeAdd(atomic, 0);
}

int32 AtomicWrite(volatile Atomic* atomic, int32 value)
{
	return ::InterlockedExchange(atomic, value);
}

int32 AtomicAdd(volatile Atomic* atomic, int32 value)
{
	return ::InterlockedExchangeAdd(atomic, value);
}

int32 AtomicIncrement(volatile Atomic* atomic)
{
	return ::InterlockedIncrement(atomic);
}

int32 AtomicDecrement(volatile Atomic* atomic)
{
	return ::InterlockedDecrement(atomic);
}

#pragma endregion

//-----------------------------------//

NAMESPACE_CORE_END

#endif
<|endoftext|>


<commit_before>#include <string>
#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <iterator>
#include <tuple>
#include <regex>
#include <array>
#include <valarray>
#define all(v)begin(v),end(v)
#define dump(v)copy(all(v),ostream_iterator<decltype(*v.begin())>(cout,"\n"))
#define rg(i,a,b)for(int i=a,i##e=b;i<i##e;++i)
#define fr(i,n)for(int i=0,i##e=n;i<i##e;++i)
#define rf(i,n)for(int i=n-1;i>=0;--i)
#define ei(a,m)for(auto&a:m)if(int a##i=&a-&*begin(m)+1)if(--a##i,1)
#define sz(v)int(v.size())
#define sr(v)sort(all(v))
#define rs(v)sort(all(v),greater<int>())
#define rev(v)reverse(all(v))
#define eb emplace_back
#define stst stringstream
#define big numeric_limits<int>::max()
#define g(t,i)get<i>(t)
#define cb(v,w)copy(all(v),back_inserter(w))
#define uni(v)sort(all(v));v.erase(unique(all(v)),end(v))
#define vt(...)vector<tuple<__VA_ARGS__>>
#define smx(a,b)a=max(a,b)
#define smn(a,b)a=min(a,b)
#define words(w,q)vector<string>w;[&w](string&&s){stringstream u(s);string r;while(u>>r)w.eb(r);}(q);
#define digits(d,n,s)vector<int>d;[&d](int m){while(m)d.eb(m%s),m/=s;}(n);
typedef long long ll;
using namespace std;

struct VerifyCreditCard {
	string checkDigits(string cn) {
		int s = 0;
		ei(a, cn) {
			int d = (a - '0') * ((ai % 2 == 0) ^ sz(cn) ? 2 : 1);
			cout << d << endl;
			s += d % 10 + d / 10;
		}
		return s % 10 ? "INVALID" : "VALID";
	}
};

// BEGIN KAWIGIEDIT TESTING
// Generated by KawigiEdit-pf 2.3.0
#include <iostream>
#include <string>
#include <vector>
#include <ctime>
#include <cmath>
using namespace std;
bool KawigiEdit_RunTest(int testNum, string p0, bool hasAnswer, string p1) {
	cout << "Test " << testNum << ": [" << "\"" << p0 << "\"";
	cout << "]" << endl;
	VerifyCreditCard *obj;
	string answer;
	obj = new VerifyCreditCard();
	clock_t startTime = clock();
	answer = obj->checkDigits(p0);
	clock_t endTime = clock();
	delete obj;
	bool res;
	res = true;
	cout << "Time: " << double(endTime - startTime) / CLOCKS_PER_SEC << " seconds" << endl;
	if (hasAnswer) {
		cout << "Desired answer:" << endl;
		cout << "\t" << "\"" << p1 << "\"" << endl;
	}
	cout << "Your answer:" << endl;
	cout << "\t" << "\"" << answer << "\"" << endl;
	if (hasAnswer) {
		res = answer == p1;
	}
	if (!res) {
		cout << "DOESN'T MATCH!!!!" << endl;
	} else if (double(endTime - startTime) / CLOCKS_PER_SEC >= 2) {
		cout << "FAIL the timeout" << endl;
		res = false;
	} else if (hasAnswer) {
		cout << "Match :-)" << endl;
	} else {
		cout << "OK, but is it right?" << endl;
	}
	cout << "" << endl;
	return res;
}
int main() {
	bool all_right;
	bool disabled;
	bool tests_disabled;
	all_right = true;
	tests_disabled = false;

	string p0;
	string p1;

	// ----- test 0 -----
	disabled = false;
	p0 = "21378";
	p1 = "VALID";
	all_right = (disabled || KawigiEdit_RunTest(0, p0, true, p1) ) && all_right;
	tests_disabled = tests_disabled || disabled;
	// ------------------

	// ----- test 1 -----
	disabled = false;
	p0 = "31378";
	p1 = "INVALID";
	all_right = (disabled || KawigiEdit_RunTest(1, p0, true, p1) ) && all_right;
	tests_disabled = tests_disabled || disabled;
	// ------------------

	// ----- test 2 -----
	disabled = false;
	p0 = "11111101";
	p1 = "VALID";
	all_right = (disabled || KawigiEdit_RunTest(2, p0, true, p1) ) && all_right;
	tests_disabled = tests_disabled || disabled;
	// ------------------

	// ----- test 3 -----
	disabled = false;
	p0 = "50005";
	p1 = "VALID";
	all_right = (disabled || KawigiEdit_RunTest(3, p0, true, p1) ) && all_right;
	tests_disabled = tests_disabled || disabled;
	// ------------------

	// ----- test 4 -----
	disabled = false;
	p0 = "542987223412";
	p1 = "INVALID";
	all_right = (disabled || KawigiEdit_RunTest(4, p0, true, p1) ) && all_right;
	tests_disabled = tests_disabled || disabled;
	// ------------------

	if (all_right) {
		if (tests_disabled) {
			cout << "You're a stud (but some test cases were disabled)!" << endl;
		} else {
			cout << "You're a stud (at least on given cases)!" << endl;
		}
	} else {
		cout << "Some of the test cases had errors." << endl;
	}
	return 0;
}
// END KAWIGIEDIT TESTING
<commit_msg>VerifyCreditCard<commit_after>#include <string>
#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <iterator>
#include <tuple>
#include <regex>
#include <array>
#include <valarray>
#define all(v)begin(v),end(v)
#define dump(v)copy(all(v),ostream_iterator<decltype(*v.begin())>(cout,"\n"))
#define rg(i,a,b)for(int i=a,i##e=b;i<i##e;++i)
#define fr(i,n)for(int i=0,i##e=n;i<i##e;++i)
#define rf(i,n)for(int i=n-1;i>=0;--i)
#define ei(a,m)for(auto&a:m)if(int a##i=&a-&*begin(m)+1)if(--a##i,1)
#define sz(v)int(v.size())
#define sr(v)sort(all(v))
#define rs(v)sort(all(v),greater<int>())
#define rev(v)reverse(all(v))
#define eb emplace_back
#define stst stringstream
#define big numeric_limits<int>::max()
#define g(t,i)get<i>(t)
#define cb(v,w)copy(all(v),back_inserter(w))
#define uni(v)sort(all(v));v.erase(unique(all(v)),end(v))
#define vt(...)vector<tuple<__VA_ARGS__>>
#define smx(a,b)a=max(a,b)
#define smn(a,b)a=min(a,b)
#define words(w,q)vector<string>w;[&w](string&&s){stringstream u(s);string r;while(u>>r)w.eb(r);}(q);
#define digits(d,n,s)vector<int>d;[&d](int m){while(m)d.eb(m%s),m/=s;}(n);
typedef long long ll;
using namespace std;

struct VerifyCreditCard {
	string checkDigits(string cn) {
		int s = 0;
		ei(a, cn) {
			int d = (a - '0') * ((ai % 2) ^ (sz(cn) % 2) ? 2 : 1);
			cout << d << endl;
			s += d % 10 + d / 10;
		}
		return s % 10 ? "INVALID" : "VALID";
	}
};

// BEGIN KAWIGIEDIT TESTING
// Generated by KawigiEdit-pf 2.3.0
#include <iostream>
#include <string>
#include <vector>
#include <ctime>
#include <cmath>
using namespace std;
bool KawigiEdit_RunTest(int testNum, string p0, bool hasAnswer, string p1) {
	cout << "Test " << testNum << ": [" << "\"" << p0 << "\"";
	cout << "]" << endl;
	VerifyCreditCard *obj;
	string answer;
	obj = new VerifyCreditCard();
	clock_t startTime = clock();
	answer = obj->checkDigits(p0);
	clock_t endTime = clock();
	delete obj;
	bool res;
	res = true;
	cout << "Time: " << double(endTime - startTime) / CLOCKS_PER_SEC << " seconds" << endl;
	if (hasAnswer) {
		cout << "Desired answer:" << endl;
		cout << "\t" << "\"" << p1 << "\"" << endl;
	}
	cout << "Your answer:" << endl;
	cout << "\t" << "\"" << answer << "\"" << endl;
	if (hasAnswer) {
		res = answer == p1;
	}
	if (!res) {
		cout << "DOESN'T MATCH!!!!" << endl;
	} else if (double(endTime - startTime) / CLOCKS_PER_SEC >= 2) {
		cout << "FAIL the timeout" << endl;
		res = false;
	} else if (hasAnswer) {
		cout << "Match :-)" << endl;
	} else {
		cout << "OK, but is it right?" << endl;
	}
	cout << "" << endl;
	return res;
}
int main() {
	bool all_right;
	bool disabled;
	bool tests_disabled;
	all_right = true;
	tests_disabled = false;

	string p0;
	string p1;

	// ----- test 0 -----
	disabled = false;
	p0 = "21378";
	p1 = "VALID";
	all_right = (disabled || KawigiEdit_RunTest(0, p0, true, p1) ) && all_right;
	tests_disabled = tests_disabled || disabled;
	// ------------------

	// ----- test 1 -----
	disabled = false;
	p0 = "31378";
	p1 = "INVALID";
	all_right = (disabled || KawigiEdit_RunTest(1, p0, true, p1) ) && all_right;
	tests_disabled = tests_disabled || disabled;
	// ------------------

	// ----- test 2 -----
	disabled = false;
	p0 = "11111101";
	p1 = "VALID";
	all_right = (disabled || KawigiEdit_RunTest(2, p0, true, p1) ) && all_right;
	tests_disabled = tests_disabled || disabled;
	// ------------------

	// ----- test 3 -----
	disabled = false;
	p0 = "50005";
	p1 = "VALID";
	all_right = (disabled || KawigiEdit_RunTest(3, p0, true, p1) ) && all_right;
	tests_disabled = tests_disabled || disabled;
	// ------------------

	// ----- test 4 -----
	disabled = false;
	p0 = "542987223412";
	p1 = "INVALID";
	all_right = (disabled || KawigiEdit_RunTest(4, p0, true, p1) ) && all_right;
	tests_disabled = tests_disabled || disabled;
	// ------------------

	if (all_right) {
		if (tests_disabled) {
			cout << "You're a stud (but some test cases were disabled)!" << endl;
		} else {
			cout << "You're a stud (at least on given cases)!" << endl;
		}
	} else {
		cout << "Some of the test cases had errors." << endl;
	}
	return 0;
}
// END KAWIGIEDIT TESTING
<|endoftext|>


<commit_before>/*=========================================================================

  Program:   Insight Segmentation & Registration Toolkit
  Module:    ImageHistogram4.cxx
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

  Copyright (c) Insight Software Consortium. All rights reserved.
  See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notices for more information.

=========================================================================*/
#if defined(_MSC_VER)
#pragma warning ( disable : 4786 )
#endif

// Software Guide : BeginLatex
//
// This example shows how to compute and save the histogram of an RGB image.
// using the helper class \doxygen{ImageToHistogramGenerator}.
//
// In this first example we compute the joint histogram of the thre channels.
//
// Software Guide : EndLatex

// Software Guide : BeginCodeSnippet
#include "itkImageToHistogramGenerator.h"
#include "itkImage.h"
#include "itkRGBPixel.h"
// Software Guide : EndCodeSnippet

#include "itkImageFileReader.h"

int main( int argc, char * argv [] )
{

  if( argc < 3 )
    {
    std::cerr << "Missing command line arguments" << std::endl;
    std::cerr << "Usage :  ImageHistogram1  inputRGBImageFileName histogramFilename.raw" << std::endl;
    return -1;
    }

// Software Guide : BeginCodeSnippet
  typedef unsigned char                         PixelComponentType;

  typedef itk::RGBPixel< PixelComponentType >   RGBPixelType;

  const unsigned int                            Dimension = 2;

  typedef itk::Image< RGBPixelType, Dimension > RGBImageType;

  typedef itk::ImageFileReader< RGBImageType >  ReaderType;

  ReaderType::Pointer reader = ReaderType::New();

  reader->SetFileName( argv[1] );

  try
    {
    reader->Update();
    }
  catch( itk::ExceptionObject & excp )
    {
    std::cerr << "Problem encoutered while reading image file : " << argv[1] << std::endl;
    std::cerr << excp << std::endl;
    return -1;
    }

  typedef itk::Statistics::ImageToHistogramGenerator<
                                              RGBImageType
                                                          >   HistogramGeneratorType;
  typedef HistogramGeneratorType::SizeType   SizeType;

  SizeType size;

  size[0] = 255;  // number of bins for the Red   channel
  size[1] = 255;  // number of bins for the Green channel
  size[2] = 255;  // number of bins for the Blue  channel

  HistogramGeneratorType::Pointer histogramGenerator = HistogramGeneratorType::New();

  histogramGenerator->SetInput(  reader->GetOutput()  );

  histogramGenerator->SetNumberOfBins( size );
  histogramGenerator->SetMarginalScale( 10.0 );
  histogramGenerator->Compute();

  typedef HistogramGeneratorType::HistogramType  HistogramType;

  const HistogramType * histogram = histogramGenerator->GetOutput();

  const unsigned int histogramSize = histogram->Size();

  std::cout << "Histogram size " << histogramSize << std::endl;

  std::ofstream histogramFile;
  histogramFile.open( argv[2] );

  HistogramType::ConstIterator itr = histogram->Begin();
  HistogramType::ConstIterator end = histogram->End();

  typedef HistogramType::FrequencyType FrequencyType;

  while( itr != end )
    {
    const FrequencyType frequency = itr.GetFrequency();
    histogramFile.write( (const char *)(&frequency), sizeof(frequency) );
    ++itr;
    }

  histogramFile.close();

// Software Guide : EndCodeSnippet

  return 0;

}

<commit_msg>STYLE: Added comments for the Software Guide.<commit_after>/*=========================================================================

  Program:   Insight Segmentation & Registration Toolkit
  Module:    ImageHistogram4.cxx
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

  Copyright (c) Insight Software Consortium. All rights reserved.
  See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notices for more information.

=========================================================================*/
#if defined(_MSC_VER)
#pragma warning ( disable : 4786 )
#endif

// Software Guide : BeginLatex
//
// The statistics framework in ITK has been designed for managing multi-variate
// statistics in a natural way. The \subdoxygen{Statistics}{Histogram} class
// reflects this concept clearly since it is a N-variable joint histogram. This
// nature of the Histogram class is exploited in the following example in order
// to build the joint histogram of a color image encoded in RGB values.
//
// Note that the same treatement could be applied further to any vector image
// thanks to the generic programming approach used in the implementation of the
// statistical framework.
//
// The most relevant class in this example is the
// \subdoxygen{Statistics}{ImageToHistogramGenerator}. This class will take
// care of adapting the \doxygen{Image} to a list of samples and then to a
// histogram generator. The user is only bound to provide the desired
// resolution on the histogram bins for each one of the image components.
//
// In this example we compute the joint histogram of the thre channels of an
// RGB image. Our output histogram will be equivalent to a 3D array of bins.
// This histogram could be used further for feeding a segmentation method based
// on statistical pattern recognition. Such method was actually used during the
// generation of the image in the cover of the Software Guide.
//
// The first step is to include the header files for the histogram generator,
// the RGB pixel type and the Image.
//
// \index{itk::Statistics::ImageToHistogramGenerator!header}
// \index{itk::RGBPixel!header}
// \index{itk::RGBPixel!Statistics}
//
// Software Guide : EndLatex

// Software Guide : BeginCodeSnippet
#include "itkImageToHistogramGenerator.h"
#include "itkImage.h"
#include "itkRGBPixel.h"
// Software Guide : EndCodeSnippet

#include "itkImageFileReader.h"

int main( int argc, char * argv [] )
{

  if( argc < 3 )
    {
    std::cerr << "Missing command line arguments" << std::endl;
    std::cerr << "Usage :  ImageHistogram1  inputRGBImageFileName ";
    std::cerr << " histogramFilename.raw" << std::endl;
    return -1;
    }

// Software Guide : BeginLatex
//
// We declare now the type used for the components of the RGB pixel,
// instantiate the type of the RGBPixel and instantiate the image type.
//
// Software Guide : EndLatex

// Software Guide : BeginCodeSnippet
  typedef unsigned char                         PixelComponentType;

  typedef itk::RGBPixel< PixelComponentType >   RGBPixelType;

  const unsigned int                            Dimension = 2;

  typedef itk::Image< RGBPixelType, Dimension > RGBImageType;
// Software Guide : EndCodeSnippet

  typedef itk::ImageFileReader< RGBImageType >  ReaderType;

  ReaderType::Pointer reader = ReaderType::New();

  reader->SetFileName( argv[1] );

  try
    {
    reader->Update();
    }
  catch( itk::ExceptionObject & excp )
    {
    std::cerr << "Problem reading image file : " << argv[1] << std::endl;
    std::cerr << excp << std::endl;
    return -1;
    }

// Software Guide : BeginLatex
//
// Using the type of the color image, and in general of any vector image, we
// can now instantiate the type of the histogram generator class. We then use
// that type for constructing an instance of the generator by invoking its
// \code{New()} method and assigning the result to a smart pointer.
//
// Software Guide : EndLatex

// Software Guide : BeginCodeSnippet
  typedef itk::Statistics::ImageToHistogramGenerator<
                                 RGBImageType >   HistogramGeneratorType;

  HistogramGeneratorType::Pointer histogramGenerator =
                                           HistogramGeneratorType::New();
// Software Guide : EndCodeSnippet

// Software Guide : BeginLatex
//
// The resolution at which the statistics of each one of the color component
// will be evaluated is defined by setting the number of bins along every
// component in the joint histogram. For this purpose we take the
// \code{SizeType} trait from the generator and use it to instantiate a
// \code{size} variable. We set in this variable the number of bins to use for
// each component of the color image.
//
// Software Guide : EndLatex

// Software Guide : BeginCodeSnippet
  typedef HistogramGeneratorType::SizeType   SizeType;

  SizeType size;

  size[0] = 255;  // number of bins for the Red   channel
  size[1] = 255;  // number of bins for the Green channel
  size[2] = 255;  // number of bins for the Blue  channel

  histogramGenerator->SetNumberOfBins( size );
// Software Guide : EndCodeSnippet

// Software Guide : BeginLatex
//
// The input to the histogram generator is taken from the output of an image
// reader. Of course, the output of any filter producing an RGB image could
// have been used instead of a reader.
//
// Software Guide : EndLatex

// Software Guide : BeginCodeSnippet
  histogramGenerator->SetInput(  reader->GetOutput()  );
// Software Guide : EndCodeSnippet

// Software Guide : BeginLatex
//
// The marginal scale is defined in the histogram generator. This value will
// define the precision in the assignment of values to the histogram bins.
//
// Software Guide : EndLatex

// Software Guide : BeginCodeSnippet
  histogramGenerator->SetMarginalScale( 10.0 );
// Software Guide : EndCodeSnippet

// Software Guide : BeginLatex
//
// Finally, the computation of the histogram is triggered by invoking the
// \code{Compute()} method of the generator. Note that generators are not
// pipeline objects. It is therefore your responsibility to make sure that you
// update the filter that provides the input image to the generator.
//
// Software Guide : EndLatex

// Software Guide : BeginCodeSnippet
  histogramGenerator->Compute();
// Software Guide : EndCodeSnippet

// Software Guide : BeginLatex
//
// At this point, we can recover the histogram by calling the
// \code{GetOutput()} method of the generator. The result is assigned to a
// variable that is instantiated using the \code{HistogramType} trait of the
// generator type.
//
// Software Guide : EndLatex

// Software Guide : BeginCodeSnippet
  typedef HistogramGeneratorType::HistogramType  HistogramType;

  const HistogramType * histogram = histogramGenerator->GetOutput();
// Software Guide : EndCodeSnippet

// Software Guide : BeginLatex
//
// We can verify that the computed histogram has the requested size by invoking
// its \code{Size()} method.
//
// Software Guide : EndLatex

// Software Guide : BeginCodeSnippet
  const unsigned int histogramSize = histogram->Size();

  std::cout << "Histogram size " << histogramSize << std::endl;
// Software Guide : EndCodeSnippet

// Software Guide : BeginLatex
//
// The values of the histogram can now be saved into a file by walking through
// all of the histogram bins and pushing them into a std::ofstream.
//
// Software Guide : EndLatex

// Software Guide : BeginCodeSnippet
  std::ofstream histogramFile;
  histogramFile.open( argv[2] );

  HistogramType::ConstIterator itr = histogram->Begin();
  HistogramType::ConstIterator end = histogram->End();

  typedef HistogramType::FrequencyType FrequencyType;

  while( itr != end )
    {
    const FrequencyType frequency = itr.GetFrequency();
    histogramFile.write( (const char *)(&frequency), sizeof(frequency) );
    ++itr;
    }

  histogramFile.close();
// Software Guide : EndCodeSnippet

// Software Guide : BeginLatex
//
// Note that here the histogram is saved as a block of memory in a raw file. At
// this point you can use visualization software in order to explore the
// histogram in a display that would be equivalen to a scatter plot of the RGB
// components of the input color image.
//
// Software Guide : EndLatex

  return 0;

}

<|endoftext|>


<commit_before>#include "../tlang.h"
#include <taichi/util.h>
#include <taichi/visual/gui.h>
#include <taichi/system/profiler.h>
#include <taichi/visualization/particle_visualization.h>

TC_NAMESPACE_BEGIN

using namespace Tlang;

auto cnn = [](std::vector<std::string> cli_param) {
  CoreState::set_trigger_gdb_when_crash(true);
  auto param = parse_param(cli_param);
  auto path = param.get("grid_path", "");
  auto use_dense = param.get("use_dense", false);
  TC_P(path);
  TC_P(use_dense);

  auto f = fopen(path.c_str(), "rb");
  TC_ASSERT_INFO(f, "grid not found");
  int magic_number = -1;
  if (fread(&magic_number, sizeof(int), 1, f)) {
  }
  int n_dim = -1;
  if (fread(&n_dim, sizeof(int), 1, f)) {
  }
  int size = 1;
  for (int i = 0; i < n_dim; i++) {
    int d = -1;
    if (fread(&d, sizeof(int), 1, f)) {
    }
    size *= d;
  }
  float *data = new float[size];
  trash(fread(data, sizeof(float), size, f));
  fclose(f);

  Program prog(Arch::gpu);
  prog.config.lower_access = true;
  prog.config.lazy_compilation = false;
  prog.config.simplify_after_lower_access = true;

  // constexpr int dim = 3;
  constexpr int n = 256;

  constexpr int num_ch1 = 16, num_ch2 = 16;

  Global(layer1, f32);
  Global(layer2, f32);
  Global(weights, f32);

  layout([&]() {
    auto ijkl = Indices(0, 1, 2, 3);
    if (use_dense) {
      root.dense(ijkl, {n, n, n, num_ch1}).place(layer1);
      root.dense(ijkl, {n, n, n, num_ch2}).place(layer2);
    } else {
      root.dense(ijkl, {n / 8, n / 8, n / 8, 1})
          .bitmasked()
          .dense(ijkl, {8, 8, 8, num_ch1})
          .place(layer1);
      root.dense(ijkl, {n / 8, n / 8, n / 8, 1})
          .bitmasked()
          .dense(ijkl, {8, 8, 8, num_ch2})
          .place(layer2);
    }
    root.dense(ijkl, {4, 4, 4, num_ch1 * num_ch2}).place(weights);
  });

  for (int c_in = 0; c_in < num_ch1; c_in++) {
    for (int i = 0; i < n; i++) {
      for (int j = 0; j < n; j++) {
        for (int k = 0; k < n; k++) {
          float v = data[((c_in * n + k) * n + j) * n + i];
          if (v != 0) {
            layer1.val<float32>(i, j, k, c_in) = v;
          }
        }
      }
    }
  }
  delete[] data;

  prog.config.print_ir = true;
  Kernel(forward).def([&] {
    // Cache(0, layer2);
    BlockDim(128);
    For(layer2, [&](Expr i, Expr j, Expr k, Expr c_out) {
      auto sum = Var(0.0f);
      auto weight = weights[Expr(1), Expr(1), Expr(1), c_out];
      Assert(weight == 1.0f / 16.0f);
    });
  });
  prog.config.print_ir = false;

  // expand blocks
  kernel([&] {
    kernel_name("dilate");
    auto block_size = 8;
    For(layer1, [&](Expr i, Expr j, Expr k) {
      for (int x = -1; x < 2; x++) {
        for (int y = -1; y < 2; y++) {
          for (int z = -1; z < 2; z++) {
            layer2[i + x * block_size, j + y * block_size,
                   k + z * block_size] += 0.0f;  // simply activate the block
          }
        }
      }
    });
  })();

  for (int c_out = 0; c_out < num_ch2; c_out++) {
    for (int c_in = 0; c_in < num_ch1; c_in++) {
      float inc = 0.1f;
      for (int dx = -1; dx < 2; dx++) {
        for (int dy = -1; dy < 2; dy++) {
          for (int dz = -1; dz < 2; dz++) {
            if (dx == 0 && dy == 0 && dz == 0) {
              weights.val<float32>(dx + 1, dy + 1, dz + 1,
                                   c_in * num_ch2 + c_out) = 1.f / 16.f;
            } else {
              weights.val<float32>(dx + 1, dy + 1, dz + 1,
                                   c_in * num_ch2 + c_out) = 0.f;
            }
            inc += 0.1f;
          }
        }
      }
    }
  }

  // prog.config.print_ir = true;

  for (int i = 0; i < 1; i++) {
    forward();
  }
  prog.profiler_print();

  // Write the first layer of output
  data = new float[n * n * n];
  int non_zero = 0;
  int zero = 0;
  for (int i = 0; i < n; i++) {
    for (int j = 0; j < n; j++) {
      for (int k = 0; k < n; k++) {
        data[((0 * n + k) * n + j) * n + i] = layer2.val<float32>(i, j, k, 0);
        if (layer2.val<float32>(i, j, k, 0) != 0) {
          non_zero++;
        } else {
          zero++;
        }
      }
    }
  }
  std::cout << "Non zero:" << non_zero << ", zero:" << zero << std::endl;
  std::cerr << "Sparsity:" << (double)non_zero / (double)(non_zero + zero)
            << std::endl;
  auto f_out = fopen("our_output.bin", "wb");
  fwrite(data, sizeof(float), n * n * n, f_out);
  fclose(f_out);

#if 0
  int gui_res = 512;
  GUI gui("Sparse CNN", Vector2i(gui_res + 200, gui_res), false);
  int layer = 1;
  int k = 0;
  int channel = 0;
  gui.slider("z", k, 0, n - 1)
      .slider("Layer", layer, 1, 2)
      .slider("Channel", channel, 0, num_ch1);

  int scale = gui_res / n;
  auto &canvas = gui.get_canvas();
  for (int frame = 1;; frame++) {
    for (int i = 0; i < gui_res - scale; i++) {
      for (int j = 0; j < gui_res - scale; j++) {
        real dx;
        if (layer == 1) {
          dx = layer1.val<float32>(i / scale, j / scale, k, channel);
        } else {
          dx = layer2.val<float32>(i / scale, j / scale, k, channel);
        }
        canvas.img[i][j] = Vector4(0.5f) + Vector4(dx) * 0.5f;
      }
    }
    gui.update();
  }
#endif
};
TC_REGISTER_TASK(cnn);

TC_NAMESPACE_END
<commit_msg>revert cnn<commit_after>#include "../tlang.h"
#include <taichi/util.h>
#include <taichi/visual/gui.h>
#include <taichi/system/profiler.h>
#include <taichi/visualization/particle_visualization.h>

TC_NAMESPACE_BEGIN

using namespace Tlang;

auto cnn = [](std::vector<std::string> cli_param) {
  CoreState::set_trigger_gdb_when_crash(true);
  auto param = parse_param(cli_param);
  auto path = param.get("grid_path", "");
  auto use_dense = param.get("use_dense", false);
  TC_P(path);
  TC_P(use_dense);

  auto f = fopen(path.c_str(), "rb");
  TC_ASSERT_INFO(f, "grid not found");
  int magic_number = -1;
  if (fread(&magic_number, sizeof(int), 1, f)) {
  }
  int n_dim = -1;
  if (fread(&n_dim, sizeof(int), 1, f)) {
  }
  int size = 1;
  for (int i = 0; i < n_dim; i++) {
    int d = -1;
    if (fread(&d, sizeof(int), 1, f)) {
    }
    size *= d;
  }
  float *data = new float[size];
  trash(fread(data, sizeof(float), size, f));
  fclose(f);

  Program prog(Arch::gpu);

  // constexpr int dim = 3;
  constexpr int n = 256;

  constexpr int num_ch1 = 16, num_ch2 = 16;

  Global(layer1, f32);
  Global(layer2, f32);
  Global(weights, f32);

  layout([&]() {
    auto ijkl = Indices(0, 1, 2, 3);
    if (use_dense) {
      root.dense(ijkl, {n, n, n, num_ch1}).place(layer1);
      root.dense(ijkl, {n, n, n, num_ch2}).place(layer2);
    } else {
      root.dense(ijkl, {n / 8, n / 8, n / 8, 1})
          .bitmasked()
          .dense(ijkl, {8, 8, 8, num_ch1})
          .place(layer1);
      root.dense(ijkl, {n / 8, n / 8, n / 8, 1})
          .bitmasked()
          .dense(ijkl, {8, 8, 8, num_ch2})
          .place(layer2);
    }
    root.dense(ijkl, {4, 4, 4, num_ch1 * num_ch2}).place(weights);
  });

  for (int c_in = 0; c_in < num_ch1; c_in++) {
    for (int i = 0; i < n; i++) {
      for (int j = 0; j < n; j++) {
        for (int k = 0; k < n; k++) {
          float v = data[((c_in * n + k) * n + j) * n + i];
          if (v != 0) {
            layer1.val<float32>(i, j, k, c_in) = v;
          }
        }
      }
    }
  }
  delete[] data;

  Kernel(forward).def([&] {
    // Cache(0, layer2);
    BlockDim(128);
    For(layer2, [&](Expr i, Expr j, Expr k, Expr c_out) {
      auto sum = Var(0.0f);
      auto weight = weights[Expr(1), Expr(1), Expr(1), c_out];
      Assert(weight == 1.0f / 16.0f);
    });
  });

  // expand blocks
  kernel([&] {
    kernel_name("dilate");
    auto block_size = 8;
    For(layer1, [&](Expr i, Expr j, Expr k) {
      for (int x = -1; x < 2; x++) {
        for (int y = -1; y < 2; y++) {
          for (int z = -1; z < 2; z++) {
            layer2[i + x * block_size, j + y * block_size,
                   k + z * block_size] += 0.0f;  // simply activate the block
          }
        }
      }
    });
  })();

  for (int c_out = 0; c_out < num_ch2; c_out++) {
    for (int c_in = 0; c_in < num_ch1; c_in++) {
      float inc = 0.1f;
      for (int dx = -1; dx < 2; dx++) {
        for (int dy = -1; dy < 2; dy++) {
          for (int dz = -1; dz < 2; dz++) {
            if (dx == 0 && dy == 0 && dz == 0) {
              weights.val<float32>(dx + 1, dy + 1, dz + 1,
                                   c_in * num_ch2 + c_out) = 1.f / 16.f;
            } else {
              weights.val<float32>(dx + 1, dy + 1, dz + 1,
                                   c_in * num_ch2 + c_out) = 0.f;
            }
            inc += 0.1f;
          }
        }
      }
    }
  }

  // prog.config.print_ir = true;

  for (int i = 0; i < 20; i++) {
    forward();
  }
  prog.profiler_print();

  // Write the first layer of output
  data = new float[n * n * n];
  int non_zero = 0;
  int zero = 0;
  for (int i = 0; i < n; i++) {
    for (int j = 0; j < n; j++) {
      for (int k = 0; k < n; k++) {
        data[((0 * n + k) * n + j) * n + i] = layer2.val<float32>(i, j, k, 0);
        if (layer2.val<float32>(i, j, k, 0) != 0) {
          non_zero++;
        } else {
          zero++;
        }
      }
    }
  }
  std::cout << "Non zero:" << non_zero << ", zero:" << zero << std::endl;
  std::cerr << "Sparsity:" << (double)non_zero / (double)(non_zero + zero)
            << std::endl;
  auto f_out = fopen("our_output.bin", "wb");
  fwrite(data, sizeof(float), n * n * n, f_out);
  fclose(f_out);

#if 0
  int gui_res = 512;
  GUI gui("Sparse CNN", Vector2i(gui_res + 200, gui_res), false);
  int layer = 1;
  int k = 0;
  int channel = 0;
  gui.slider("z", k, 0, n - 1)
      .slider("Layer", layer, 1, 2)
      .slider("Channel", channel, 0, num_ch1);

  int scale = gui_res / n;
  auto &canvas = gui.get_canvas();
  for (int frame = 1;; frame++) {
    for (int i = 0; i < gui_res - scale; i++) {
      for (int j = 0; j < gui_res - scale; j++) {
        real dx;
        if (layer == 1) {
          dx = layer1.val<float32>(i / scale, j / scale, k, channel);
        } else {
          dx = layer2.val<float32>(i / scale, j / scale, k, channel);
        }
        canvas.img[i][j] = Vector4(0.5f) + Vector4(dx) * 0.5f;
      }
    }
    gui.update();
  }
#endif
};
TC_REGISTER_TASK(cnn);

TC_NAMESPACE_END
<|endoftext|>


<commit_before>#ifndef DUNE_STUFF_ALIASES_HH
#define DUNE_STUFF_ALIASES_HH

namespace DSC = Dune::Stuff::Common;
namespace DSG = Dune::Stuff::Grid;
namespace DSFe = Dune::Stuff::Fem;
namespace DSFu = Dune::Stuff::Function;

#endif // DUNE_STUFF_ALIASES_HH

/** Copyright (c) 2012, Rene Milk
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions are met:
   *
   * 1. Redistributions of source code must retain the above copyright notice, this
   *    list of conditions and the following disclaimer.
   * 2. Redistributions in binary form must reproduce the above copyright notice,
   *    this list of conditions and the following disclaimer in the documentation
   *    and/or other materials provided with the distribution.
   *
   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
   * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   *
   * The views and conclusions contained in the software and documentation are those
   * of the authors and should not be interpreted as representing official policies,
   * either expressed or implied, of the FreeBSD Project.
   **/
<commit_msg>[common] adds ns "forwards" to aliases.hh<commit_after>#ifndef DUNE_STUFF_ALIASES_HH
#define DUNE_STUFF_ALIASES_HH

namespace Dune {
namespace Stuff {

namespace Common {}
namespace Grid {}
namespace Fem {}
namespace Function {}

}
}

namespace DSC = Dune::Stuff::Common;
namespace DSG = Dune::Stuff::Grid;
namespace DSFe = Dune::Stuff::Fem;
namespace DSFu = Dune::Stuff::Function;

#endif // DUNE_STUFF_ALIASES_HH

/** Copyright (c) 2012, Rene Milk
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions are met:
   *
   * 1. Redistributions of source code must retain the above copyright notice, this
   *    list of conditions and the following disclaimer.
   * 2. Redistributions in binary form must reproduce the above copyright notice,
   *    this list of conditions and the following disclaimer in the documentation
   *    and/or other materials provided with the distribution.
   *
   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
   * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   *
   * The views and conclusions contained in the software and documentation are those
   * of the authors and should not be interpreted as representing official policies,
   * either expressed or implied, of the FreeBSD Project.
   **/
<|endoftext|>