<commit_before>/*
 *  niicreate.cpp
 *
 *  Created by Tobias Wood on 30/05/2014.
 *  Copyright (c) 2014 Tobias Wood.
 *
 *  This Source Code Form is subject to the terms of the Mozilla Public
 *  License, v. 2.0. If a copy of the MPL was not distributed with this
 *  file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 */

#include <getopt.h>
#include <iostream>
#include <random>
#include <functional>

#include "Nifti/Nifti.h"
#include "QUIT/MultiArray.h"
#include "QUIT/ThreadPool.h"
using namespace std;

//******************************************************************************
// Arguments / Usage
//******************************************************************************
const string usage {
"Usage is: niicreate [options] filename dims voxdims [extra arguments]\n\
\n\
This is a tool to create Nifti files, either blank headers with orientation\n\
information, e.g. for registration, or files filled with simple patterns of\n\
data e.g. solid values, gradients, or blocks. The default is to create a\n\
3D file filled with zeros. Choose from the options below to create something\n\
else.\n\
\n\
Main Options:\n\
	--help, -h      : Print this message\n\
	--dtype, -t F   : Set the datatype to 64 bit float (default)\n\
	            I   : Set the datatype to 16 bit int\n\
	            C   : Set the datatype to 128 bit complex\n\
	            NNN : Set the datatype to the given valid Nifti datatype\n\
File Content Options:\n\
	--blank, -b     : Create the header information only\n\
	--value, -v D   : Fill dimension D with constant value v (val)\n\
	--grad, -g D    : Fill dimension D with a smooth gradient (low, high)\n\
	--step, -s D    : Fill dimension D with stepped data (low, high, steps)\n\
	--uniform, -U D : Fill dimension D with uniform noise (mid, width)\n\
	--gauss, -G D   : Fill dimension D with gaussian noise (mean, std dev)\n"
};

enum class FillType { Zero, Value, Gradient, Steps, Uniform, Gaussian };
static bool verbose = false, isBlank = false;
static vector<FillType> fillTypes(4, FillType::Zero);
static Eigen::Array4f startVal = Eigen::Array4f::Zero(), deltaVal = Eigen::Array4f::Zero();
static Eigen::Array4i stepLength = Eigen::Array4i::Ones();
static vector<uniform_real_distribution<float>> uniforms(4);
static vector<normal_distribution<float>> gauss(4);
static Nifti::DataType dType = Nifti::DataType::FLOAT64;
static struct option long_options[] = {
	{"help", no_argument, 0, 'h'},
	{"dtype", required_argument, 0, 't'},
	{"blank", no_argument, 0, 'b'},
	{"zero", no_argument, 0, 'z'},
	{"value", required_argument, 0, 'v'},
	{"grad", required_argument, 0, 'g'},
	{"step", required_argument, 0, 's'},
	{"uniform", required_argument, 0,'U'},
	{"gauss", required_argument, 0, 'G'},
	{0, 0, 0, 0}
};
//******************************************************************************
// Main
//******************************************************************************
int main(int argc, char **argv)
{
	//**************************************************************************
	// Argument Processing
	//**************************************************************************
	size_t expected_extra_args = 9;
	int indexptr = 0, c;
	while ((c = getopt_long(argc, argv, "d:t:bzv:g:s:U:G:h", long_options, &indexptr)) != -1) {
		switch (c) {
			case 't':
				switch (*optarg) {
					case 'F': dType = Nifti::DataType::FLOAT32; break;
					case 'I': dType = Nifti::DataType::INT16; break;
					case 'C': dType = Nifti::DataType::COMPLEX64; break;
					default: dType = Nifti::DataTypeForCode(atoi(optarg)); break;
				} break;
			case 'b': isBlank = true; break;
			case 'v': fillTypes.at(atoi(optarg)) = FillType::Value; expected_extra_args += 1; break;
			case 'g': fillTypes.at(atoi(optarg)) = FillType::Gradient; expected_extra_args += 2; break;
			case 's': fillTypes.at(atoi(optarg)) = FillType::Steps; expected_extra_args += 3; break;
			case 'U': fillTypes.at(atoi(optarg)) = FillType::Uniform; expected_extra_args += 2; break;
			case 'G': fillTypes.at(atoi(optarg)) = FillType::Gaussian; expected_extra_args += 2; break;
			case 'h':
			case '?': // getopt will print an error message
				exit(EXIT_FAILURE);
		}
	}
	if ((argc - optind) != expected_extra_args) {
		cerr << "Wrong number of arguments." << endl;
		cout << usage << endl;
		exit(EXIT_FAILURE);
	}

	string fName(argv[optind++]);
	MultiArray<float, 4>::Index dims;
	Eigen::ArrayXf vdims(4);
	for (size_t i = 0; i < 4; i++) { dims[i] = atoi(argv[optind++]); }
	for (size_t i = 0; i < 4; i++) { vdims[i] = atof(argv[optind++]); }
	Nifti file(dims, vdims, dType);
	file.open(fName, Nifti::Mode::Write);

	if (!isBlank) {
		MultiArray<float, 4> data(dims);
		for (size_t d = 0; d < 4; d++) {
			switch (fillTypes.at(d)) {
				case FillType::Zero:
					stepLength[d] = 1;
					break;
				case FillType::Value:
					startVal[d] = atof(argv[optind++]);
					deltaVal[d] = 0;
					stepLength[d] = 1;
					break;
				case FillType::Gradient:
					startVal[d] = atof(argv[optind++]);
					deltaVal[d] = (atof(argv[optind++]) - startVal[d]) / (dims[d] - 1);
					stepLength[d] = 1;
					break;
				case FillType::Steps: {
					startVal[d] = atof(argv[optind++]);
					float endVal = atof(argv[optind++]);
					int steps = atoi(argv[optind++]);
					stepLength[d] = dims[d] / steps;
					deltaVal[d] = (endVal - startVal[d]) / (steps - 1);
				} break;
				case FillType::Uniform: {
					float lo = atof(argv[optind++]);
					float hi = atof(argv[optind++]);
					uniforms[d] = uniform_real_distribution<float>(lo, hi);
				} break;
				case FillType::Gaussian: {
					float mean = atof(argv[optind++]);
					float std  = atof(argv[optind++]);
					gauss[d] = normal_distribution<float>(mean, std);
				} break;
			}
		}
		random_device seed;
		mt19937_64 twist(seed());

		MultiArray<float, 4>::Index index;
		function<void (const int&, const float&)> processDim = [&] (const int &d, const float& outVal) {
			float inVal = outVal + startVal[d];
			for (index[d] = 0;index[d] < dims[d]; index[d]++) {
				if (fillTypes[d] == FillType::Uniform)
					inVal = outVal + uniforms[d].operator()(twist);
				else if (fillTypes[d] == FillType::Gaussian)
					inVal = outVal + gauss[d].operator()(twist);
				if (d > 0) {
					processDim(d - 1, inVal);
				} else {
					data[index] = inVal;
				}
				if ((index[d] % stepLength[d]) == (stepLength[d] - 1)) inVal += deltaVal[d];
			}
		};
		processDim(3, 0.);
		file.writeAll(data.begin(), data.end());
	}
	file.close();
	exit(EXIT_SUCCESS);
}

<commit_msg>Added options to only fill a slab along a dimension and to copy the transform from another file.<commit_after>/*
 *  niicreate.cpp
 *
 *  Created by Tobias Wood on 30/05/2014.
 *  Copyright (c) 2014 Tobias Wood.
 *
 *  This Source Code Form is subject to the terms of the Mozilla Public
 *  License, v. 2.0. If a copy of the MPL was not distributed with this
 *  file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 */

#include <getopt.h>
#include <iostream>
#include <random>
#include <functional>

#include "Nifti/Nifti.h"
#include "QUIT/MultiArray.h"
#include "QUIT/ThreadPool.h"
using namespace std;

//******************************************************************************
// Arguments / Usage
//******************************************************************************
const string usage {
"Usage is: niicreate [options] filename dims voxdims [extra arguments]\n\
\n\
This is a tool to create Nifti files, either blank headers with orientation\n\
information, e.g. for registration, or files filled with simple patterns of\n\
data e.g. solid values, gradients, or blocks. The default is to create a\n\
3D file filled with zeros. Choose from the options below to create something\n\
else.\n\
\n\
Main Options:\n\
	--help, -h       : Print this message\n\
	--dtype, -t F    : Set the datatype to 64 bit float (default)\n\
	            I    : Set the datatype to 16 bit int\n\
	            C    : Set the datatype to 128 bit complex\n\
	            NNN  : Set the datatype to the given valid Nifti datatype\n\
	--xform, -x FILE : Copy header transform from another Nifti\n\
File Content Options:\n\
	--blank, -b     : Create the header information only\n\
	--value, -v D   : Fill dimension D with constant value v (val)\n\
	--slab, -l D    : Fill dimension D with a slab (val, start, end)\n\
	--grad, -g D    : Fill dimension D with a smooth gradient (low, high)\n\
	--step, -s D    : Fill dimension D with stepped data (low, high, steps)\n\
	--uniform, -U D : Fill dimension D with uniform noise (mid, width)\n\
	--gauss, -G D   : Fill dimension D with gaussian noise (mean, std dev)\n"
};

enum class FillType { Zero, Value, Slab, Gradient, Steps, Uniform, Gaussian };
static bool verbose = false, isBlank = false;
static vector<FillType> fillTypes(4, FillType::Zero);
static Eigen::Array4f startVal = Eigen::Array4f::Zero(), deltaVal = Eigen::Array4f::Zero();
static Eigen::Array4i stepLength = Eigen::Array4i::Ones();
static vector<uniform_real_distribution<float>> uniforms(4);
static vector<normal_distribution<float>> gauss(4);
static Nifti::DataType dType = Nifti::DataType::FLOAT64;
static Eigen::Affine3f xform = Eigen::Affine3f::Identity();
static struct option long_options[] = {
	{"help", no_argument, 0, 'h'},
	{"dtype", required_argument, 0, 't'},
	{"xform", required_argument, 0, 'x'},
	{"blank", no_argument, 0, 'b'},
	{"zero", no_argument, 0, 'z'},
	{"value", required_argument, 0, 'v'},
	{"slab", required_argument, 0, 'l'},
	{"grad", required_argument, 0, 'g'},
	{"step", required_argument, 0, 's'},
	{"uniform", required_argument, 0,'U'},
	{"gauss", required_argument, 0, 'G'},
	{0, 0, 0, 0}
};
//******************************************************************************
// Main
//******************************************************************************
int main(int argc, char **argv)
{
	//**************************************************************************
	// Argument Processing
	//**************************************************************************
	size_t expected_extra_args = 9;
	int indexptr = 0, c;
	while ((c = getopt_long(argc, argv, "d:t:x:bzv:l:g:s:U:G:h", long_options, &indexptr)) != -1) {
		switch (c) {
			case 't':
				switch (*optarg) {
					case 'F': dType = Nifti::DataType::FLOAT32; break;
					case 'I': dType = Nifti::DataType::INT16; break;
					case 'C': dType = Nifti::DataType::COMPLEX64; break;
					default: dType = Nifti::DataTypeForCode(atoi(optarg)); break;
				} break;
			case 'x': {
				Nifti other(optarg, Nifti::Mode::ReadHeader);
				xform = other.transform();
			} break;
			case 'b': isBlank = true; break;
			case 'v': fillTypes.at(atoi(optarg)) = FillType::Value; expected_extra_args += 1; break;
			case 'l': fillTypes.at(atoi(optarg)) = FillType::Slab; expected_extra_args += 3; break;
			case 'g': fillTypes.at(atoi(optarg)) = FillType::Gradient; expected_extra_args += 2; break;
			case 's': fillTypes.at(atoi(optarg)) = FillType::Steps; expected_extra_args += 3; break;
			case 'U': fillTypes.at(atoi(optarg)) = FillType::Uniform; expected_extra_args += 2; break;
			case 'G': fillTypes.at(atoi(optarg)) = FillType::Gaussian; expected_extra_args += 2; break;
			case 'h':
			case '?': // getopt will print an error message
				exit(EXIT_FAILURE);
		}
	}
	if ((argc - optind) != expected_extra_args) {
		cerr << "Wrong number of arguments." << endl;
		cout << usage << endl;
		exit(EXIT_FAILURE);
	}

	string fName(argv[optind++]);
	MultiArray<float, 4>::Index dims;
	Eigen::ArrayXf vdims(4);
	for (size_t i = 0; i < 4; i++) { dims[i] = atoi(argv[optind++]); }
	for (size_t i = 0; i < 4; i++) { vdims[i] = atof(argv[optind++]); }
	Nifti file(dims, vdims, dType);
	file.setTransform(xform);
	file.open(fName, Nifti::Mode::Write);

	if (!isBlank) {
		MultiArray<float, 4> data(dims);
		MultiArray<float, 4>::Index starts; starts.setZero();
		MultiArray<float, 4>::Index ends = data.dims();
		for (size_t d = 0; d < 4; d++) {
			switch (fillTypes.at(d)) {
				case FillType::Zero:
					stepLength[d] = 1;
					break;
				case FillType::Value:
					startVal[d] = atof(argv[optind++]);
					deltaVal[d] = 0;
					stepLength[d] = 1;
					break;
				case FillType::Slab:
					startVal[d] = atof(argv[optind++]);
					deltaVal[d] = 0;
					stepLength[d] = 1;
					starts[d] = atoi(argv[optind++]);
					ends[d] = atoi(argv[optind++]);
					if (starts[d] >= data.dims()[d]) {
						cerr << "Invalid slab start slice." << endl;
						exit(EXIT_FAILURE);
					}
					if (ends[d] > data.dims()[d]) {
						cerr << "Invalid slab end slice." << endl;
						exit(EXIT_FAILURE);
					}
					break;
				case FillType::Gradient:
					startVal[d] = atof(argv[optind++]);
					deltaVal[d] = (atof(argv[optind++]) - startVal[d]) / (dims[d] - 1);
					stepLength[d] = 1;
					break;
				case FillType::Steps: {
					startVal[d] = atof(argv[optind++]);
					float endVal = atof(argv[optind++]);
					int steps = atoi(argv[optind++]);
					stepLength[d] = dims[d] / steps;
					deltaVal[d] = (endVal - startVal[d]) / (steps - 1);
				} break;
				case FillType::Uniform: {
					float lo = atof(argv[optind++]);
					float hi = atof(argv[optind++]);
					uniforms[d] = uniform_real_distribution<float>(lo, hi);
				} break;
				case FillType::Gaussian: {
					float mean = atof(argv[optind++]);
					float std  = atof(argv[optind++]);
					gauss[d] = normal_distribution<float>(mean, std);
				} break;
			}
		}
		random_device seed;
		mt19937_64 twist(seed());

		MultiArray<float, 4>::Index index;
		function<void (const int&, const float&)> processDim = [&] (const int &d, const float& outVal) {
			float inVal = outVal + startVal[d];
			for (index[d] = starts[d];index[d] < ends[d]; index[d]++) {
				if (fillTypes[d] == FillType::Uniform)
					inVal = outVal + uniforms[d].operator()(twist);
				else if (fillTypes[d] == FillType::Gaussian)
					inVal = outVal + gauss[d].operator()(twist);
				if (d > 0) {
					processDim(d - 1, inVal);
				} else {
					data[index] = inVal;
				}
				if ((index[d] % stepLength[d]) == (stepLength[d] - 1)) inVal += deltaVal[d];
			}
		};
		processDim(3, 0.);
		file.writeAll(data.begin(), data.end());
	}
	file.close();
	exit(EXIT_SUCCESS);
}

<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "ConnectionMock.h"
#include "Messaging.h"
#include "ServerImpl.h"
#include "Path.h"

#include "gtest/gtest.h"

//    Client            Server
//      |       NON       |   NonConfirmable request
//      |---------------->|
//      |                 |
//      |       NON       |   NonConfirmable answer for request
//      |<----------------|
TEST(ServerImpl_onMessage, NoAcknowledgeForNonConfirmableMessage) {
  // GIVEN a NonConfirmable message
  auto conn = std::make_shared<ConnectionMock>();
  CoAP::Messaging srv(conn);
  srv.requestHandler()
      .onUri("/")
          .onGet([](const Path& path){
            return CoAP::RestResponse().withCode(CoAP::Code::Content);
          });
  auto msg = CoAP::Message(CoAP::Type::NonConfirmable, 0, CoAP::Code::GET, 0, "/");

  // WHEN the NonConfirmable message is sent to the server
  srv.onMessage(msg, 0, 0);

  // THEN we only get the answer back, but no acknowledge for reception of the message
  ASSERT_EQ(1, conn->sentMessages_.size());
  EXPECT_EQ(CoAP::Type::NonConfirmable, conn->sentMessages_[0].type());
  EXPECT_EQ(CoAP::Code::Content, conn->sentMessages_[0].code());
}

//    Client            Server
//      |       CON       |   Confirmable request
//      |---------------->|
//      |                 |
//      |       ACK       |   Acknowledge for reception of request
//      |<----------------|
//      |                 |
//      |       CON       |   Confirmable answer for request
//      |<----------------|
//      :                 :
//      :       ACK       :   Client acknowledge is not tested here
//      :- - - - - - - - >:
TEST(ServerImpl_onMessage, AcknowledgeForConfirmableMessage) {
  // GIVEN
  auto conn = std::make_shared<ConnectionMock>();
  CoAP::Messaging srv(conn);
  srv.requestHandler()
      .onUri("/")
          .onGet([](const Path& path){
            return CoAP::RestResponse().withCode(CoAP::Code::Content);
          }, true);
  auto msg = CoAP::Message(CoAP::Type::Confirmable, 0, CoAP::Code::GET, 0, "/");

  // WHEN
  srv.onMessage(msg, 0, 0);

  // THEN
  ASSERT_EQ(2, conn->sentMessages_.size());
  EXPECT_EQ(CoAP::Type::Acknowledgement, conn->sentMessages_[0].type());
  EXPECT_EQ(CoAP::Type::Confirmable, conn->sentMessages_[1].type());
  EXPECT_EQ(CoAP::Code::Content, conn->sentMessages_[1].code());
}

TEST(ServerImpl_onMessage, NoAcknowledgeForConfirmableMessageOnInvalidRessource) {
  // GIVEN
  auto conn = std::make_shared<ConnectionMock>();
  CoAP::Messaging srv(conn);
  srv.requestHandler()
      .onUri("/abc")
      .onGet([](const Path& path){
        return CoAP::RestResponse().withCode(CoAP::Code::Content);
      }, true);
  auto msg = CoAP::Message(CoAP::Type::Confirmable, 0, CoAP::Code::GET, 0, "/xyz");

  // WHEN
  srv.onMessage(msg, 0, 0);

  // THEN
  ASSERT_EQ(1, conn->sentMessages_.size());
  EXPECT_EQ(CoAP::Type::Confirmable, conn->sentMessages_[0].type());
  EXPECT_EQ(CoAP::Code::NotFound, conn->sentMessages_[0].code());
}

TEST(ServerImpl_onRequest, PiggybackedResponseForGET) {
  // GIVEN a Server with a response handler
  auto conn = std::make_shared<ConnectionMock>();
  CoAP::Messaging srv(conn);
  srv.requestHandler()
      .onUri("/")
      .onGet([](const Path& path){
        return CoAP::RestResponse().withCode(CoAP::Code::Content).withPayload("Hello");
      });

  // WHEN it receives a confirmable message with a GET request
  auto msg = CoAP::Message(CoAP::Type::Confirmable, 0, CoAP::Code::GET, 0, "/");
  srv.onMessage(msg, 0, 0);

  // THEN it sends an ACK message with a piggybacked response
  ASSERT_EQ(1, conn->sentMessages_.size());
  EXPECT_EQ(CoAP::Type::Confirmable, conn->sentMessages_[0].type());
  EXPECT_EQ(CoAP::Code::Content, conn->sentMessages_[0].code());
  EXPECT_EQ("Hello", conn->sentMessages_[0].payload());
}

TEST(ServerImpl_onRequest, PiggybackedResponseForPUT) {
  // GIVEN a Server with a response handler
  auto conn = std::make_shared<ConnectionMock>();
  CoAP::Messaging srv(conn);
  srv.requestHandler()
      .onUri("/")
      .onPut([](const Path& path, const std::string& payload){
        return CoAP::RestResponse().withCode(CoAP::Code::Created);
      });

  // WHEN it receives a confirmable message with a GET request
  auto msg = CoAP::Message(CoAP::Type::Confirmable, 0, CoAP::Code::PUT, 0, "/");
  srv.onMessage(msg, 0, 0);

  // THEN it sends an ACK message with a piggybacked response
  ASSERT_EQ(1, conn->sentMessages_.size());
  EXPECT_EQ(CoAP::Type::Confirmable, conn->sentMessages_[0].type());
  EXPECT_EQ(CoAP::Code::Created, conn->sentMessages_[0].code());
}

TEST(ServerImpl_onRequest, PiggybackedResponseForPOST) {
  // GIVEN a Server with a response handler
  auto conn = std::make_shared<ConnectionMock>();
  CoAP::Messaging srv(conn);
  srv.requestHandler()
      .onUri("/")
      .onPost([](const Path& path, const std::string& payload){
        return CoAP::RestResponse().withCode(CoAP::Code::Changed);
      });

  // WHEN it receives a confirmable message with a GET request
  auto msg = CoAP::Message(CoAP::Type::Confirmable, 0, CoAP::Code::POST, 0, "/");
  srv.onMessage(msg, 0, 0);

  // THEN it sends an ACK message with a piggybacked response
  ASSERT_EQ(1, conn->sentMessages_.size());
  EXPECT_EQ(CoAP::Type::Confirmable, conn->sentMessages_[0].type());
  EXPECT_EQ(CoAP::Code::Changed, conn->sentMessages_[0].code());
}

TEST(ServerImpl_onRequest, PiggybackedResponseForDELETE) {
  // GIVEN a Server with a response handler
  auto conn = std::make_shared<ConnectionMock>();
  CoAP::Messaging srv(conn);
  srv.requestHandler()
      .onUri("/")
      .onDelete([](const Path& path){
        return CoAP::RestResponse().withCode(CoAP::Code::Deleted);
      });

  // WHEN it receives a confirmable message with a GET request
  auto msg = CoAP::Message(CoAP::Type::Confirmable, 0, CoAP::Code::DELETE, 0, "/");
  srv.onMessage(msg, 0, 0);

  // THEN it sends an ACK message with a piggybacked response
  ASSERT_EQ(1, conn->sentMessages_.size());
  EXPECT_EQ(CoAP::Type::Confirmable, conn->sentMessages_[0].type());
  EXPECT_EQ(CoAP::Code::Deleted, conn->sentMessages_[0].code());
}

TEST(ServerImpl_onRequest, DelegatesGetRequestAndReceivesResponse) {
  // GIVEN
  auto conn = std::make_shared<ConnectionMock>();
  CoAP::Messaging srv(conn);
  auto getCalled = 0;
  srv.requestHandler()
      .onUri("/*")
          .onGet([&getCalled](const Path& path){
            ++getCalled;
            return CoAP::RestResponse().withCode(CoAP::Code::Content);
          });
  auto msg = CoAP::Message(CoAP::Type::NonConfirmable, 0, CoAP::Code::GET, 0, "/some/where");

  // WHEN NonConfirmable message with GET request is sent to the Server
  auto reply = srv.getServer().onRequest(msg, 0, 0);

  // THEN RequestHandler GET is called with the message and an reply is sent
  EXPECT_EQ(1, getCalled);

  EXPECT_EQ(CoAP::Code::Content, reply.code());
}

TEST(ServerImpl_onRequest, DelegatesPutRequestAndReceivesResponse) {
  // GIVEN
  auto conn = std::make_shared<ConnectionMock>();
  CoAP::Messaging srv(conn);
  auto putCalled = 0;
  srv.requestHandler()
      .onUri("/*")
          .onPut([&putCalled](const Path& path, std::string payload){
            ++putCalled;
            return CoAP::RestResponse().withCode(CoAP::Code::Changed);
          });
  auto msg = CoAP::Message(CoAP::Type::NonConfirmable, 0, CoAP::Code::PUT, 0, "/some/where");

  // WHEN NonConfirmable message with PUT request is sent to the Server
  auto reply = srv.getServer().onRequest(msg, 0, 0);

  // THEN RequestHandler PUT is called with the message and an reply is sent
  EXPECT_EQ(1, putCalled);

  EXPECT_EQ(CoAP::Code::Changed, reply.code());
}

TEST(ServerImpl_onRequest, DelegatesPostRequestAndReceivesResponse) {
  // GIVEN
  auto conn = std::make_shared<ConnectionMock>();
  CoAP::Messaging srv(conn);
  auto postCalled = 0;
  srv.requestHandler()
      .onUri("/*")
          .onPost([&postCalled](const Path& path, std::string payload){
            ++postCalled;
            return CoAP::RestResponse().withCode(CoAP::Code::Created);
          });
  auto msg = CoAP::Message(CoAP::Type::NonConfirmable, 0, CoAP::Code::POST, 0, "/some/where");

  // WHEN NonConfirmable message with POST request is sent to the Server
  auto reply = srv.getServer().onRequest(msg, 0, 0);

  // THEN RequestHandler POST is called with the message and an reply is sent
  EXPECT_EQ(1, postCalled);

  EXPECT_EQ(CoAP::Code::Created, reply.code());
}

TEST(ServerImpl_onRequest, DelegatesDeleteRequestAndReceivesResponse) {
  // GIVEN
  auto conn = std::make_shared<ConnectionMock>();
  CoAP::Messaging srv(conn);
  auto deleteCalled = 0;
  srv.requestHandler()
      .onUri("/*")
        .onDelete([&deleteCalled](const Path& path){
          ++deleteCalled;
          return CoAP::RestResponse().withCode(CoAP::Code::Deleted);
        });
  auto msg = CoAP::Message(CoAP::Type::NonConfirmable, 0, CoAP::Code::DELETE, 0, "/some/where");

  // WHEN NonConfirmable message with DELETE request is sent to the Server
  auto reply = srv.getServer().onRequest(msg, 0, 0);

  // THEN RequestHandler DELETE is called with the message and an reply is sent
  EXPECT_EQ(1, deleteCalled);

  EXPECT_EQ(CoAP::Code::Deleted, reply.code());
}

TEST(ServerImpl_onRequest, DelegatesObserveRequestAndReceivesNotification) {
  // GIVEN
  auto conn = std::make_shared<ConnectionMock>();
  CoAP::Messaging srv(conn);
  auto observeCalled = 0;
  srv.requestHandler()
      .onUri("/*")
        .onGet([&observeCalled](const Path& path){
          return CoAP::RestResponse().withCode(CoAP::Code::Content).withPayload("Get");
        })
        .onObserve([&observeCalled](const Path& path, std::weak_ptr<CoAP::Notifications> observer){
          ++observeCalled;
          observer.lock()->onNext(CoAP::RestResponse().withCode(CoAP::Code::Content).withPayload("Notification"));
          return CoAP::RestResponse().withCode(CoAP::Code::Content).withPayload("Observe");
        });
  auto msg =
      CoAP::Message(CoAP::Type::NonConfirmable, 0, CoAP::Code::GET, 0, "/some/where")
      .withObserveValue(0);

  // WHEN NonConfirmable message with GET request is sent to the Server
  auto reply = srv.getServer().onRequest(msg, 0, 0);

  // THEN RequestHandler OBSERVE is called with the message and an reply is sent
  EXPECT_EQ(1, observeCalled);

  EXPECT_EQ(CoAP::Code::Content, reply.code());
  EXPECT_EQ("Observe", reply.payload());

  // AND in this example the OBSERVE handler sends also a Notification.
  EXPECT_EQ(1u, conn->sentMessages_.size());
  auto firstMessage = conn->sentMessages_.front();
  EXPECT_EQ("Notification", firstMessage.payload());
}

//TODO: Test the retransmit of notifications (confirmable without ack)

//TODO: Test rejection of notification (confirmable with reset)

TEST(ServerImpl_onRequest, DelegatesObserveRequestAndCancelsNotifications) {
  // GIVEN
  auto conn = std::make_shared<ConnectionMock>();
  CoAP::Messaging srv(conn);
  auto observeCalled = 0;
  std::weak_ptr<CoAP::Notifications> notifications;
  srv.requestHandler()
      .onUri("/*")
        .onGet([&observeCalled](const Path& path){
          return CoAP::RestResponse().withCode(CoAP::Code::Content).withPayload("Get");
        })
        .onObserve([&observeCalled, &notifications](const Path& path, std::weak_ptr<CoAP::Notifications> observer){
          ++observeCalled;
          notifications = observer;
          return CoAP::RestResponse().withCode(CoAP::Code::Content).withPayload("Observe");
        });
  auto observeMsg =
      CoAP::Message(CoAP::Type::NonConfirmable, 0, CoAP::Code::GET, 0, "/some/where")
          .withObserveValue(0);
  auto unobserveMsg =
      CoAP::Message(CoAP::Type::NonConfirmable, 0, CoAP::Code::GET, 0, "/some/where")
          .withObserveValue(1);

  // WHEN NonConfirmable message with GET request is sent to the Server
  auto observeReply = srv.getServer().onRequest(observeMsg, 0, 0);
  notifications.lock()->onNext(CoAP::RestResponse().withCode(CoAP::Code::Content).withPayload("Notification"));
  auto unobserveReply = srv.getServer().onRequest(unobserveMsg, 0, 0);
  EXPECT_EQ(true, notifications.expired());

  // THEN RequestHandler OBSERVE is called with the message and an reply is sent
  EXPECT_EQ(1, observeCalled);

  EXPECT_EQ(CoAP::Code::Content, observeReply.code());
  EXPECT_EQ("Observe", observeReply.payload());

  // AND in this example the OBSERVE handler sends also a Notification.
  EXPECT_EQ(1u, conn->sentMessages_.size());
  auto firstMessage = conn->sentMessages_.front();
  EXPECT_EQ("Notification", firstMessage.payload());
}

TEST(ServerImpl_onMessage, EmptyRequestCausesPingResponse) {
  // GIVEN
  auto conn = std::make_shared<ConnectionMock>();
  CoAP::Messaging srv(conn);

  // WHEN
  auto msg = CoAP::Message(CoAP::Type::Confirmable, 0, CoAP::Code::Empty, 0, "");
  srv.onMessage(msg, 0, 0);

  // THEN
  ASSERT_EQ(1, conn->sentMessages_.size());
  EXPECT_EQ(CoAP::Type::Reset, conn->sentMessages_[0].type());
  EXPECT_EQ(CoAP::Code::Empty, conn->sentMessages_[0].code());
}<commit_msg>Added a unittest for rejecting a Notification<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "ConnectionMock.h"
#include "Messaging.h"
#include "ServerImpl.h"
#include "Path.h"

#include "gtest/gtest.h"

//    Client            Server
//      |       NON       |   NonConfirmable request
//      |---------------->|
//      |                 |
//      |       NON       |   NonConfirmable answer for request
//      |<----------------|
TEST(ServerImpl_onMessage, NoAcknowledgeForNonConfirmableMessage) {
  // GIVEN a NonConfirmable message
  auto conn = std::make_shared<ConnectionMock>();
  CoAP::Messaging srv(conn);
  srv.requestHandler()
      .onUri("/")
          .onGet([](const Path& path){
            return CoAP::RestResponse().withCode(CoAP::Code::Content);
          });
  auto msg = CoAP::Message(CoAP::Type::NonConfirmable, 0, CoAP::Code::GET, 0, "/");

  // WHEN the NonConfirmable message is sent to the server
  srv.onMessage(msg, 0, 0);

  // THEN we only get the answer back, but no acknowledge for reception of the message
  ASSERT_EQ(1, conn->sentMessages_.size());
  EXPECT_EQ(CoAP::Type::NonConfirmable, conn->sentMessages_[0].type());
  EXPECT_EQ(CoAP::Code::Content, conn->sentMessages_[0].code());
}

//    Client            Server
//      |       CON       |   Confirmable request
//      |---------------->|
//      |                 |
//      |       ACK       |   Acknowledge for reception of request
//      |<----------------|
//      |                 |
//      |       CON       |   Confirmable answer for request
//      |<----------------|
//      :                 :
//      :       ACK       :   Client acknowledge is not tested here
//      :- - - - - - - - >:
TEST(ServerImpl_onMessage, AcknowledgeForConfirmableMessage) {
  // GIVEN
  auto conn = std::make_shared<ConnectionMock>();
  CoAP::Messaging srv(conn);
  srv.requestHandler()
      .onUri("/")
          .onGet([](const Path& path){
            return CoAP::RestResponse().withCode(CoAP::Code::Content);
          }, true);
  auto msg = CoAP::Message(CoAP::Type::Confirmable, 0, CoAP::Code::GET, 0, "/");

  // WHEN
  srv.onMessage(msg, 0, 0);

  // THEN
  ASSERT_EQ(2, conn->sentMessages_.size());
  EXPECT_EQ(CoAP::Type::Acknowledgement, conn->sentMessages_[0].type());
  EXPECT_EQ(CoAP::Type::Confirmable, conn->sentMessages_[1].type());
  EXPECT_EQ(CoAP::Code::Content, conn->sentMessages_[1].code());
}

TEST(ServerImpl_onMessage, NoAcknowledgeForConfirmableMessageOnInvalidRessource) {
  // GIVEN
  auto conn = std::make_shared<ConnectionMock>();
  CoAP::Messaging srv(conn);
  srv.requestHandler()
      .onUri("/abc")
      .onGet([](const Path& path){
        return CoAP::RestResponse().withCode(CoAP::Code::Content);
      }, true);
  auto msg = CoAP::Message(CoAP::Type::Confirmable, 0, CoAP::Code::GET, 0, "/xyz");

  // WHEN
  srv.onMessage(msg, 0, 0);

  // THEN
  ASSERT_EQ(1, conn->sentMessages_.size());
  EXPECT_EQ(CoAP::Type::Confirmable, conn->sentMessages_[0].type());
  EXPECT_EQ(CoAP::Code::NotFound, conn->sentMessages_[0].code());
}

TEST(ServerImpl_onRequest, PiggybackedResponseForGET) {
  // GIVEN a Server with a response handler
  auto conn = std::make_shared<ConnectionMock>();
  CoAP::Messaging srv(conn);
  srv.requestHandler()
      .onUri("/")
      .onGet([](const Path& path){
        return CoAP::RestResponse().withCode(CoAP::Code::Content).withPayload("Hello");
      });

  // WHEN it receives a confirmable message with a GET request
  auto msg = CoAP::Message(CoAP::Type::Confirmable, 0, CoAP::Code::GET, 0, "/");
  srv.onMessage(msg, 0, 0);

  // THEN it sends an ACK message with a piggybacked response
  ASSERT_EQ(1, conn->sentMessages_.size());
  EXPECT_EQ(CoAP::Type::Confirmable, conn->sentMessages_[0].type());
  EXPECT_EQ(CoAP::Code::Content, conn->sentMessages_[0].code());
  EXPECT_EQ("Hello", conn->sentMessages_[0].payload());
}

TEST(ServerImpl_onRequest, PiggybackedResponseForPUT) {
  // GIVEN a Server with a response handler
  auto conn = std::make_shared<ConnectionMock>();
  CoAP::Messaging srv(conn);
  srv.requestHandler()
      .onUri("/")
      .onPut([](const Path& path, const std::string& payload){
        return CoAP::RestResponse().withCode(CoAP::Code::Created);
      });

  // WHEN it receives a confirmable message with a GET request
  auto msg = CoAP::Message(CoAP::Type::Confirmable, 0, CoAP::Code::PUT, 0, "/");
  srv.onMessage(msg, 0, 0);

  // THEN it sends an ACK message with a piggybacked response
  ASSERT_EQ(1, conn->sentMessages_.size());
  EXPECT_EQ(CoAP::Type::Confirmable, conn->sentMessages_[0].type());
  EXPECT_EQ(CoAP::Code::Created, conn->sentMessages_[0].code());
}

TEST(ServerImpl_onRequest, PiggybackedResponseForPOST) {
  // GIVEN a Server with a response handler
  auto conn = std::make_shared<ConnectionMock>();
  CoAP::Messaging srv(conn);
  srv.requestHandler()
      .onUri("/")
      .onPost([](const Path& path, const std::string& payload){
        return CoAP::RestResponse().withCode(CoAP::Code::Changed);
      });

  // WHEN it receives a confirmable message with a GET request
  auto msg = CoAP::Message(CoAP::Type::Confirmable, 0, CoAP::Code::POST, 0, "/");
  srv.onMessage(msg, 0, 0);

  // THEN it sends an ACK message with a piggybacked response
  ASSERT_EQ(1, conn->sentMessages_.size());
  EXPECT_EQ(CoAP::Type::Confirmable, conn->sentMessages_[0].type());
  EXPECT_EQ(CoAP::Code::Changed, conn->sentMessages_[0].code());
}

TEST(ServerImpl_onRequest, PiggybackedResponseForDELETE) {
  // GIVEN a Server with a response handler
  auto conn = std::make_shared<ConnectionMock>();
  CoAP::Messaging srv(conn);
  srv.requestHandler()
      .onUri("/")
      .onDelete([](const Path& path){
        return CoAP::RestResponse().withCode(CoAP::Code::Deleted);
      });

  // WHEN it receives a confirmable message with a GET request
  auto msg = CoAP::Message(CoAP::Type::Confirmable, 0, CoAP::Code::DELETE, 0, "/");
  srv.onMessage(msg, 0, 0);

  // THEN it sends an ACK message with a piggybacked response
  ASSERT_EQ(1, conn->sentMessages_.size());
  EXPECT_EQ(CoAP::Type::Confirmable, conn->sentMessages_[0].type());
  EXPECT_EQ(CoAP::Code::Deleted, conn->sentMessages_[0].code());
}

TEST(ServerImpl_onRequest, DelegatesGetRequestAndReceivesResponse) {
  // GIVEN
  auto conn = std::make_shared<ConnectionMock>();
  CoAP::Messaging srv(conn);
  auto getCalled = 0;
  srv.requestHandler()
      .onUri("/*")
          .onGet([&getCalled](const Path& path){
            ++getCalled;
            return CoAP::RestResponse().withCode(CoAP::Code::Content);
          });
  auto msg = CoAP::Message(CoAP::Type::NonConfirmable, 0, CoAP::Code::GET, 0, "/some/where");

  // WHEN NonConfirmable message with GET request is sent to the Server
  auto reply = srv.getServer().onRequest(msg, 0, 0);

  // THEN RequestHandler GET is called with the message and an reply is sent
  EXPECT_EQ(1, getCalled);

  EXPECT_EQ(CoAP::Code::Content, reply.code());
}

TEST(ServerImpl_onRequest, DelegatesPutRequestAndReceivesResponse) {
  // GIVEN
  auto conn = std::make_shared<ConnectionMock>();
  CoAP::Messaging srv(conn);
  auto putCalled = 0;
  srv.requestHandler()
      .onUri("/*")
          .onPut([&putCalled](const Path& path, std::string payload){
            ++putCalled;
            return CoAP::RestResponse().withCode(CoAP::Code::Changed);
          });
  auto msg = CoAP::Message(CoAP::Type::NonConfirmable, 0, CoAP::Code::PUT, 0, "/some/where");

  // WHEN NonConfirmable message with PUT request is sent to the Server
  auto reply = srv.getServer().onRequest(msg, 0, 0);

  // THEN RequestHandler PUT is called with the message and an reply is sent
  EXPECT_EQ(1, putCalled);

  EXPECT_EQ(CoAP::Code::Changed, reply.code());
}

TEST(ServerImpl_onRequest, DelegatesPostRequestAndReceivesResponse) {
  // GIVEN
  auto conn = std::make_shared<ConnectionMock>();
  CoAP::Messaging srv(conn);
  auto postCalled = 0;
  srv.requestHandler()
      .onUri("/*")
          .onPost([&postCalled](const Path& path, std::string payload){
            ++postCalled;
            return CoAP::RestResponse().withCode(CoAP::Code::Created);
          });
  auto msg = CoAP::Message(CoAP::Type::NonConfirmable, 0, CoAP::Code::POST, 0, "/some/where");

  // WHEN NonConfirmable message with POST request is sent to the Server
  auto reply = srv.getServer().onRequest(msg, 0, 0);

  // THEN RequestHandler POST is called with the message and an reply is sent
  EXPECT_EQ(1, postCalled);

  EXPECT_EQ(CoAP::Code::Created, reply.code());
}

TEST(ServerImpl_onRequest, DelegatesDeleteRequestAndReceivesResponse) {
  // GIVEN
  auto conn = std::make_shared<ConnectionMock>();
  CoAP::Messaging srv(conn);
  auto deleteCalled = 0;
  srv.requestHandler()
      .onUri("/*")
        .onDelete([&deleteCalled](const Path& path){
          ++deleteCalled;
          return CoAP::RestResponse().withCode(CoAP::Code::Deleted);
        });
  auto msg = CoAP::Message(CoAP::Type::NonConfirmable, 0, CoAP::Code::DELETE, 0, "/some/where");

  // WHEN NonConfirmable message with DELETE request is sent to the Server
  auto reply = srv.getServer().onRequest(msg, 0, 0);

  // THEN RequestHandler DELETE is called with the message and an reply is sent
  EXPECT_EQ(1, deleteCalled);

  EXPECT_EQ(CoAP::Code::Deleted, reply.code());
}

TEST(ServerImpl_onRequest, DelegatesObserveRequestAndReceivesNotification) {
  // GIVEN
  auto conn = std::make_shared<ConnectionMock>();
  CoAP::Messaging srv(conn);
  auto observeCalled = 0;
  srv.requestHandler()
      .onUri("/*")
        .onGet([&observeCalled](const Path& path){
          return CoAP::RestResponse().withCode(CoAP::Code::Content).withPayload("Get");
        })
        .onObserve([&observeCalled](const Path& path, std::weak_ptr<CoAP::Notifications> observer){
          ++observeCalled;
          observer.lock()->onNext(CoAP::RestResponse().withCode(CoAP::Code::Content).withPayload("Notification"));
          return CoAP::RestResponse().withCode(CoAP::Code::Content).withPayload("Observe");
        });
  auto msg =
      CoAP::Message(CoAP::Type::NonConfirmable, 0, CoAP::Code::GET, 0, "/some/where")
      .withObserveValue(0);

  // WHEN NonConfirmable message with GET request is sent with OBSERVE=0 to the Server
  auto reply = srv.getServer().onRequest(msg, 0, 0);

  // THEN RequestHandler OBSERVE is called with the message and an reply is sent
  EXPECT_EQ(1, observeCalled);

  EXPECT_EQ(CoAP::Code::Content, reply.code());
  EXPECT_EQ("Observe", reply.payload());

  // AND in this example the OBSERVE handler sends also a Notification.
  EXPECT_EQ(1u, conn->sentMessages_.size());
  auto firstMessage = conn->sentMessages_.front();
  EXPECT_EQ("Notification", firstMessage.payload());
}

TEST(ServerImpl_onRequest, DelegatesObserveRequestAndCancelsNotifications) {
  // GIVEN
  auto conn = std::make_shared<ConnectionMock>();
  CoAP::Messaging srv(conn);
  auto observeCalled = 0;
  std::weak_ptr<CoAP::Notifications> notifications;
  srv.requestHandler()
      .onUri("/*")
        .onGet([&observeCalled](const Path& path){
          return CoAP::RestResponse().withCode(CoAP::Code::Content).withPayload("Get");
        })
        .onObserve([&observeCalled, &notifications](const Path& path, std::weak_ptr<CoAP::Notifications> observer){
          ++observeCalled;
          notifications = observer;
          return CoAP::RestResponse().withCode(CoAP::Code::Content).withPayload("Observe");
        });
  auto observeMsg =
      CoAP::Message(CoAP::Type::NonConfirmable, 0, CoAP::Code::GET, 0, "/some/where")
          .withObserveValue(0);
  auto unobserveMsg =
      CoAP::Message(CoAP::Type::NonConfirmable, 0, CoAP::Code::GET, 0, "/some/where")
          .withObserveValue(1);

  // WHEN NonConfirmable message with GET request is sent with OBSERVE=1 to the Server
  auto observeReply = srv.getServer().onRequest(observeMsg, 0, 0);
  notifications.lock()->onNext(CoAP::RestResponse().withCode(CoAP::Code::Content).withPayload("Notification"));
  auto unobserveReply = srv.getServer().onRequest(unobserveMsg, 0, 0);

  // THEN the notification observer is canceled
  EXPECT_EQ(true, notifications.expired());
}

TEST(ServerImpl_onRequest, RejectedNotificationCancelsObservation) {
  // GIVEN
  auto conn = std::make_shared<ConnectionMock>();
  CoAP::Messaging srv(conn);
  auto observeCalled = 0;
  std::weak_ptr<CoAP::Notifications> notifications;
  srv.requestHandler()
      .onUri("/*")
        .onGet([&observeCalled](const Path& path){
          return CoAP::RestResponse().withCode(CoAP::Code::Content).withPayload("Get");
        })
        .onObserve([&observeCalled, &notifications](const Path& path, std::weak_ptr<CoAP::Notifications> observer){
          ++observeCalled;
          notifications = observer;
          return CoAP::RestResponse().withCode(CoAP::Code::Content).withPayload("Observe");
        });
  auto observeMsg =
      CoAP::Message(CoAP::Type::Confirmable, 0, CoAP::Code::GET, 0, "/some/where")
          .withObserveValue(0);

  // WHEN NonConfirmable message with GET request is sent to the Server and the first
  //      notification is answered with Reset
  auto observeReply = srv.getServer().onRequest(observeMsg, 0, 0);
  notifications.lock()->onNext(CoAP::RestResponse().withCode(CoAP::Code::Content).withPayload("Notification"));
  srv.getServer().onMessage(CoAP::Message(CoAP::Type::Reset, 0, CoAP::Code::GET, 0, "/some/where"), 0, 0);

  // THEN the notification observer is canceled
  EXPECT_EQ(true, notifications.expired());
}

TEST(ServerImpl_onMessage, EmptyRequestCausesPingResponse) {
  // GIVEN
  auto conn = std::make_shared<ConnectionMock>();
  CoAP::Messaging srv(conn);

  // WHEN
  auto msg = CoAP::Message(CoAP::Type::Confirmable, 0, CoAP::Code::Empty, 0, "");
  srv.onMessage(msg, 0, 0);

  // THEN
  ASSERT_EQ(1, conn->sentMessages_.size());
  EXPECT_EQ(CoAP::Type::Reset, conn->sentMessages_[0].type());
  EXPECT_EQ(CoAP::Code::Empty, conn->sentMessages_[0].code());
}<|endoftext|>


<commit_before>#include <sys/time.h>
#include <cstdlib>
#include "trainable.h"
#include "exception.h"

#include <limits>
using std::numeric_limits;

#include <iostream>
using std::cout;
using std::endl;

#include <iomanip>
using std::setw;
using std::setprecision;

CMT::Trainable::Callback::~Callback() {
}

CMT::Trainable::Parameters::Parameters() {
	verbosity = 0;
	maxIter = 1000;
	threshold = 1e-9;
	numGrad = 20;
	batchSize = 2000;
	callback = 0;
	cbIter = 25;
	valIter = 5;
	valLookAhead = 20;
}

CMT::Trainable::Parameters::~Parameters() {
	if(callback)
		delete callback;
}

CMT::Trainable::Parameters::Parameters(const Parameters& params) :
	verbosity(params.verbosity),
	maxIter(params.maxIter),
	threshold(params.threshold),
	numGrad(params.numGrad),
	batchSize(params.batchSize),
	callback(0),
	cbIter(params.cbIter),
	valIter(params.valIter),
	valLookAhead(params.valLookAhead)
{
	if(params.callback)
		callback = params.callback->copy();
}

CMT::Trainable::Parameters& CMT::Trainable::Parameters::operator=(
	const Parameters& params)
{
	verbosity = params.verbosity;
	maxIter = params.maxIter;
	threshold = params.threshold;
	numGrad = params.numGrad;
	batchSize = params.batchSize;
	callback = params.callback ? params.callback->copy() : 0;
	cbIter = params.cbIter;
	valIter = params.valIter;
	valLookAhead = params.valLookAhead;

	return *this;
}

CMT::Trainable::InstanceLBFGS::InstanceLBFGS(
	CMT::Trainable* cd,
	const CMT::Trainable::Parameters* params,
	const MatrixXd* input,
	const MatrixXd* output) :
	cd(cd),
	params(params),
	input(input),
	output(output),
	inputVal(0),
	outputVal(0),
	logLoss(numeric_limits<double>::max()),
	counter(0),
	parameters(0),
	fx(numeric_limits<double>::max())
{
}

CMT::Trainable::InstanceLBFGS::InstanceLBFGS(
	CMT::Trainable* cd,
	const CMT::Trainable::Parameters* params,
	const MatrixXd* input,
	const MatrixXd* output,
	const MatrixXd* inputVal,
	const MatrixXd* outputVal) :
	cd(cd),
	params(params),
	input(input),
	output(output),
	inputVal(inputVal),
	outputVal(outputVal),
	logLoss(numeric_limits<double>::max()),
	counter(0),
	parameters(cd->parameters(*params)),
	fx(numeric_limits<double>::max())
{
}

CMT::Trainable::InstanceLBFGS::~InstanceLBFGS() {
	if(parameters)
		lbfgs_free(parameters);
}

CMT::Trainable::~Trainable() {
}

int CMT::Trainable::callbackLBFGS(
	void* instance,
	const lbfgsfloatval_t* x,
	const lbfgsfloatval_t* g,
	const lbfgsfloatval_t fx,
	const lbfgsfloatval_t xnorm,
	const lbfgsfloatval_t gnorm,
	const lbfgsfloatval_t, int,
	int iteration,
	int)
{
	// unpack user data
	InstanceLBFGS* inst = static_cast<InstanceLBFGS*>(instance);

	const CMT::Trainable::Parameters& params = *inst->params;

	// check whether to evaluate validation set
	if(inst->inputVal && inst->outputVal && iteration % params.valIter == 0) {
		inst->cd->setParameters(x, params);

		double logLoss = inst->cd->evaluate(*inst->inputVal, *inst->outputVal);

		if(params.verbosity > 0) {
			cout << setw(6) << iteration;
			cout << setw(11) << setprecision(5) << fx;
			cout << setw(11) << setprecision(5) << logLoss << endl;
		}

		if(logLoss < inst->logLoss) {
			// store current parameters for later
			for(int i = 0, N = inst->cd->numParameters(params); i < N; ++i)
				inst->parameters[i] = x[i];

			inst->counter = 0;
			inst->logLoss = logLoss;
		} else {
			inst->counter += 1;

			if(params.valLookAhead > 0 && inst->counter >= params.valLookAhead)
				// performance did not improve for valLookAhead times
				return 1;
		}
	} else {
		if(params.verbosity > 0)
			cout << setw(6) << iteration << setw(11) << setprecision(5) << fx << endl;
	}

	if(params.callback && iteration % params.cbIter == 0) {
		inst->cd->setParameters(x, params);

		if(!(*params.callback)(iteration, *inst->cd))
			return 1;
	}

	// check for convergence
	if(inst->fx - fx < params.threshold)
		return 1;

	inst->fx = fx;

	return 0;
}

lbfgsfloatval_t CMT::Trainable::evaluateLBFGS(
	void* instance,
	const lbfgsfloatval_t* x,
	lbfgsfloatval_t* g,
	int, double)
{
	// unpack user data
	const InstanceLBFGS& inst = *static_cast<InstanceLBFGS*>(instance);
	const CMT::Trainable& cd = *inst.cd;
	const CMT::Trainable::Parameters& params = *inst.params;
	const MatrixXd& input = *inst.input;
	const MatrixXd& output = *inst.output;

	return cd.parameterGradient(input, output, x, g, params);
}

void CMT::Trainable::initialize(const MatrixXd& input, const MatrixXd& output) {
}

void CMT::Trainable::initialize(const pair<ArrayXXd, ArrayXXd>& data) {
	initialize(data.first, data.second);
}

bool CMT::Trainable::train(
	const MatrixXd& input,
	const MatrixXd& output,
	const Parameters& params)
{
	return train(input, output, 0, 0, params);
}

bool CMT::Trainable::train(
	const MatrixXd& input,
	const MatrixXd& output,
	const MatrixXd& inputVal,
	const MatrixXd& outputVal,
	const Parameters& params)
{
	return train(input, output, &inputVal, &outputVal, params);
}

bool CMT::Trainable::train(
	const pair<ArrayXXd, ArrayXXd>& data,
	const Parameters& params)
{
	return train(data.first, data.second, 0, 0, params);
}

bool CMT::Trainable::train(
	const pair<ArrayXXd, ArrayXXd>& data,
	const pair<ArrayXXd, ArrayXXd>& dataVal,
	const Parameters& params)
{
	return train(
		data.first,
		data.second,
		dataVal.first,
		dataVal.second,
		params);
}

bool CMT::Trainable::train(
	const MatrixXd& input,
	const MatrixXd& output,
	const MatrixXd* inputVal,
	const MatrixXd* outputVal,
	const Parameters& params)
{
	if(input.rows() != dimIn() || output.rows() != dimOut())
		throw Exception("Data has wrong dimensionality.");

	if(input.cols() != output.cols())
		throw Exception("The number of inputs and outputs should be the same.");

	if(inputVal && outputVal) {
		if(inputVal->rows() != dimIn() || outputVal->rows() != dimOut())
			throw Exception("Data has wrong dimensionality.");

		if(inputVal->cols() != outputVal->cols())
			throw Exception("The number of validation inputs and outputs should be the same.");

	} else if(inputVal || outputVal) {
		throw Exception("Inputs or outputs of the validation set are missing.");
	}

	if(input.cols() < 1)
		return true;

	// create copy of model parameters for L-BFGS
	lbfgsfloatval_t* x = parameters(params);

	// optimization hyperparameters of L-BFGS
	lbfgs_parameter_t hyperparams;
	lbfgs_parameter_init(&hyperparams);
	hyperparams.max_iterations = params.maxIter;
	hyperparams.m = params.numGrad;
	hyperparams.epsilon = 1e-7;
	hyperparams.linesearch = LBFGS_LINESEARCH_MORETHUENTE;
	hyperparams.max_linesearch = 100;
	hyperparams.ftol = 1e-4;

	// wrap all additional arguments to optimization routine
	InstanceLBFGS instance(this, &params, &input, &output, inputVal, outputVal);

	if(params.verbosity > 0)
		if(inputVal && outputVal) {
			cout << setw(6) << 0;
			cout << setw(11) << setprecision(5) << evaluateLBFGS(&instance, x, 0, 0, 0.);
			cout << setw(11) << setprecision(5) << evaluate(*inputVal, *outputVal) << endl;
		} else {
			cout << setw(6) << 0;
			cout << setw(11) << setprecision(5) << evaluateLBFGS(&instance, x, 0, 0, 0.) << endl;
		}

	// start LBFGS optimization
	int status = LBFGSERR_MAXIMUMITERATION;
	if(params.maxIter > 0)
		status = lbfgs(numParameters(params), x, 0,
			&evaluateLBFGS,
			&callbackLBFGS,
			&instance,
			&hyperparams);

	// copy parameters back
	setParameters(x, params);

	if(inputVal && outputVal && instance.parameters) {
		double logLoss = evaluate(*inputVal, *outputVal);

		// use parameters which minimize the validation error
		setParameters(instance.parameters, params);

		// check that they really give a smaller validation error
		if(evaluate(*inputVal, *outputVal) > logLoss)
			// otherwise, use other parameters after all
			setParameters(x, params);
	}

	// free memory used by LBFGS
	lbfgs_free(x);

	if(status >= 0 || status == LBFGSERR_ROUNDING_ERROR) {
		return true;
	} else {
		if(status != LBFGSERR_MAXIMUMITERATION)
			cout << "There might be a problem with the optimization (" << status << ")." << endl;
		return false;
	}
}

double CMT::Trainable::checkGradient(
	const MatrixXd& input,
	const MatrixXd& output,
	double epsilon,
	const Parameters& params)
{
	if(input.rows() != dimIn() || output.rows() != dimOut())
		throw Exception("Data has wrong dimensionality.");
	if(input.cols() != output.cols())
		throw Exception("The number of inputs and outputs should be the same.");

	// request memory for LBFGS and copy parameters
	lbfgsfloatval_t* x = parameters(params);

	int numParams = numParameters(params);

	lbfgsfloatval_t y[numParams];
	lbfgsfloatval_t g[numParams];
	lbfgsfloatval_t n[numParams];
	lbfgsfloatval_t val1;
	lbfgsfloatval_t val2;

	// make another copy
	for(int i = 0; i < numParams; ++i)
		y[i] = x[i];

	// arguments to LBFGS function
	InstanceLBFGS instance(this, &params, &input, &output);

	// compute numerical gradient using central differences
	for(int i = 0; i < numParams; ++i) {
		y[i] = x[i] + epsilon;
		val1 = evaluateLBFGS(&instance, y, 0, 0, 0.);
		y[i] = x[i] - epsilon;
		val2 = evaluateLBFGS(&instance, y, 0, 0, 0.);
		y[i] = x[i];
		n[i] = (val1 - val2) / (2. * epsilon);
	}

	// compute analytical gradient
	evaluateLBFGS(&instance, x, g, 0, 0.);

	// squared error
	double err = 0.;
	for(int i = 0; i < numParams; ++i)
		err += (g[i] - n[i]) * (g[i] - n[i]);

	return sqrt(err);
}

double CMT::Trainable::checkPerformance(
	const MatrixXd& input,
	const MatrixXd& output,
	int repetitions,
	const Parameters& params)
{
	if(input.rows() != dimIn() || output.rows() != dimOut())
		throw Exception("Data has wrong dimensionality.");
	if(input.cols() != output.cols())
		throw Exception("The number of inputs and outputs should be the same.");

	// request memory for LBFGS and copy parameters
	lbfgsfloatval_t* x = parameters(params);

	// optimization hyperparameters
	lbfgs_parameter_t paramsLBFGS;
	lbfgs_parameter_init(&paramsLBFGS);
	paramsLBFGS.max_iterations = params.maxIter;
	paramsLBFGS.m = params.numGrad;

	// wrap additional arguments
	InstanceLBFGS instance(this, &params, &input, &output);

	// measure time it takes to evaluate gradient in seconds
	lbfgsfloatval_t* g = lbfgs_malloc(numParameters(params));
	timeval from, to;

	gettimeofday(&from, 0);
	for(int i = 0; i < repetitions; ++i)
		evaluateLBFGS(&instance, x, g, 0, 0.);
	gettimeofday(&to, 0);

	// free memory used by LBFGS
	lbfgs_free(x);

	return (to.tv_sec + to.tv_usec / 1E6 - from.tv_sec - from.tv_usec / 1E6) / repetitions;
}
<commit_msg>Minor changes.<commit_after>#include <sys/time.h>
#include <cstdlib>
#include "trainable.h"
#include "exception.h"

#include <limits>
using std::numeric_limits;

#include <iostream>
using std::cout;
using std::endl;

#include <iomanip>
using std::setw;
using std::setprecision;

CMT::Trainable::Callback::~Callback() {
}

CMT::Trainable::Parameters::Parameters() {
	verbosity = 0;
	maxIter = 1000;
	threshold = 1e-9;
	numGrad = 20;
	batchSize = 2000;
	callback = 0;
	cbIter = 25;
	valIter = 5;
	valLookAhead = 20;
}

CMT::Trainable::Parameters::~Parameters() {
	if(callback)
		delete callback;
}

CMT::Trainable::Parameters::Parameters(const Parameters& params) :
	verbosity(params.verbosity),
	maxIter(params.maxIter),
	threshold(params.threshold),
	numGrad(params.numGrad),
	batchSize(params.batchSize),
	callback(0),
	cbIter(params.cbIter),
	valIter(params.valIter),
	valLookAhead(params.valLookAhead)
{
	if(params.callback)
		callback = params.callback->copy();
}

CMT::Trainable::Parameters& CMT::Trainable::Parameters::operator=(
	const Parameters& params)
{
	verbosity = params.verbosity;
	maxIter = params.maxIter;
	threshold = params.threshold;
	numGrad = params.numGrad;
	batchSize = params.batchSize;
	callback = params.callback ? params.callback->copy() : 0;
	cbIter = params.cbIter;
	valIter = params.valIter;
	valLookAhead = params.valLookAhead;

	return *this;
}

CMT::Trainable::InstanceLBFGS::InstanceLBFGS(
	CMT::Trainable* cd,
	const CMT::Trainable::Parameters* params,
	const MatrixXd* input,
	const MatrixXd* output) :
	cd(cd),
	params(params),
	input(input),
	output(output),
	inputVal(0),
	outputVal(0),
	logLoss(numeric_limits<double>::max()),
	counter(0),
	parameters(0),
	fx(numeric_limits<double>::max())
{
}

CMT::Trainable::InstanceLBFGS::InstanceLBFGS(
	CMT::Trainable* cd,
	const CMT::Trainable::Parameters* params,
	const MatrixXd* input,
	const MatrixXd* output,
	const MatrixXd* inputVal,
	const MatrixXd* outputVal) :
	cd(cd),
	params(params),
	input(input),
	output(output),
	inputVal(inputVal),
	outputVal(outputVal),
	logLoss(numeric_limits<double>::max()),
	counter(0),
	parameters(cd->parameters(*params)),
	fx(numeric_limits<double>::max())
{
}

CMT::Trainable::InstanceLBFGS::~InstanceLBFGS() {
	if(parameters)
		lbfgs_free(parameters);
}

CMT::Trainable::~Trainable() {
}

int CMT::Trainable::callbackLBFGS(
	void* instance,
	const lbfgsfloatval_t* x,
	const lbfgsfloatval_t* g,
	const lbfgsfloatval_t fx,
	const lbfgsfloatval_t xnorm,
	const lbfgsfloatval_t gnorm,
	const lbfgsfloatval_t, int,
	int iteration,
	int)
{
	// unpack user data
	InstanceLBFGS* inst = static_cast<InstanceLBFGS*>(instance);

	const CMT::Trainable::Parameters& params = *inst->params;

	// check whether to evaluate validation set
	if(inst->inputVal && inst->outputVal && iteration % params.valIter == 0) {
		inst->cd->setParameters(x, params);

		double logLoss = inst->cd->evaluate(*inst->inputVal, *inst->outputVal);

		if(params.verbosity > 0) {
			cout << setw(6) << iteration;
			cout << setw(11) << setprecision(5) << fx;
			cout << setw(11) << setprecision(5) << logLoss << endl;
		}

		if(logLoss < inst->logLoss) {
			// store current parameters for later
			for(int i = 0, N = inst->cd->numParameters(params); i < N; ++i)
				inst->parameters[i] = x[i];

			inst->counter = 0;
			inst->logLoss = logLoss;
		} else {
			inst->counter += 1;

			if(params.valLookAhead > 0 && inst->counter >= params.valLookAhead)
				// performance did not improve for valLookAhead times
				return 1;
		}
	} else {
		if(params.verbosity > 0)
			cout << setw(6) << iteration << setw(11) << setprecision(5) << fx << endl;
	}

	if(params.callback && iteration % params.cbIter == 0) {
		inst->cd->setParameters(x, params);

		if(!(*params.callback)(iteration, *inst->cd))
			return 1;
	}

	// check for convergence
	if(inst->fx - fx < params.threshold)
		return 1;

	inst->fx = fx;

	return 0;
}

lbfgsfloatval_t CMT::Trainable::evaluateLBFGS(
	void* instance,
	const lbfgsfloatval_t* x,
	lbfgsfloatval_t* g,
	int, double)
{
	// unpack user data
	const InstanceLBFGS& inst = *static_cast<InstanceLBFGS*>(instance);
	const CMT::Trainable& cd = *inst.cd;
	const CMT::Trainable::Parameters& params = *inst.params;
	const MatrixXd& input = *inst.input;
	const MatrixXd& output = *inst.output;

	return cd.parameterGradient(input, output, x, g, params);
}

void CMT::Trainable::initialize(const MatrixXd& input, const MatrixXd& output) {
}

void CMT::Trainable::initialize(const pair<ArrayXXd, ArrayXXd>& data) {
	initialize(data.first, data.second);
}

bool CMT::Trainable::train(
	const MatrixXd& input,
	const MatrixXd& output,
	const Parameters& params)
{
	return train(input, output, 0, 0, params);
}

bool CMT::Trainable::train(
	const MatrixXd& input,
	const MatrixXd& output,
	const MatrixXd& inputVal,
	const MatrixXd& outputVal,
	const Parameters& params)
{
	return train(input, output, &inputVal, &outputVal, params);
}

bool CMT::Trainable::train(
	const pair<ArrayXXd, ArrayXXd>& data,
	const Parameters& params)
{
	return train(data.first, data.second, 0, 0, params);
}

bool CMT::Trainable::train(
	const pair<ArrayXXd, ArrayXXd>& data,
	const pair<ArrayXXd, ArrayXXd>& dataVal,
	const Parameters& params)
{
	return train(
		data.first,
		data.second,
		dataVal.first,
		dataVal.second,
		params);
}

bool CMT::Trainable::train(
	const MatrixXd& input,
	const MatrixXd& output,
	const MatrixXd* inputVal,
	const MatrixXd* outputVal,
	const Parameters& params)
{
	if(input.rows() != dimIn() || output.rows() != dimOut())
		throw Exception("Data has wrong dimensionality.");

	if(input.cols() != output.cols())
		throw Exception("The number of inputs and outputs should be the same.");

	if(inputVal && outputVal) {
		if(inputVal->rows() != dimIn() || outputVal->rows() != dimOut())
			throw Exception("Data has wrong dimensionality.");

		if(inputVal->cols() != outputVal->cols())
			throw Exception("The number of validation inputs and outputs should be the same.");

	} else if(inputVal || outputVal) {
		throw Exception("Inputs or outputs of the validation set are missing.");
	}

	if(input.cols() < 1)
		return true;

	// create copy of model parameters for L-BFGS
	lbfgsfloatval_t* x = parameters(params);

	// optimization hyperparameters of L-BFGS
	lbfgs_parameter_t hyperparams;
	lbfgs_parameter_init(&hyperparams);
	hyperparams.max_iterations = params.maxIter;
	hyperparams.m = params.numGrad;
	hyperparams.epsilon = 1e-7;
	hyperparams.linesearch = LBFGS_LINESEARCH_MORETHUENTE;
	hyperparams.max_linesearch = 100;
	hyperparams.ftol = 1e-4;

	// wrap all additional arguments to optimization routine
	InstanceLBFGS instance(this, &params, &input, &output, inputVal, outputVal);

	if(params.verbosity > 0)
		if(inputVal && outputVal) {
			cout << setw(6) << 0;
			cout << setw(11) << setprecision(5) << evaluateLBFGS(&instance, x, 0, 0, 0.);
			cout << setw(11) << setprecision(5) << evaluate(*inputVal, *outputVal) << endl;
		} else {
			cout << setw(6) << 0;
			cout << setw(11) << setprecision(5) << evaluateLBFGS(&instance, x, 0, 0, 0.) << endl;
		}

	// start LBFGS optimization
	int status = LBFGSERR_MAXIMUMITERATION;
	if(params.maxIter > 0)
		status = lbfgs(numParameters(params), x, 0,
			&evaluateLBFGS,
			&callbackLBFGS,
			&instance,
			&hyperparams);

	// copy parameters back
	setParameters(x, params);

	if(inputVal && outputVal && instance.parameters) {
		// negative log-likelihood using current parameters
		double logLoss = evaluate(*inputVal, *outputVal);

		// switch to parameters which minimize the validation error
		setParameters(instance.parameters, params);

		// check that they really give a smaller validation error
		if(logLoss < evaluate(*inputVal, *outputVal))
			// otherwise, use other set of parameters after all
			setParameters(x, params);
	}

	// free memory used by LBFGS
	lbfgs_free(x);

	if(status >= 0 || status == LBFGSERR_ROUNDING_ERROR) {
		return true;
	} else {
		if(status != LBFGSERR_MAXIMUMITERATION)
			cout << "There might be a problem with the optimization (" << status << ")." << endl;
		return false;
	}
}

double CMT::Trainable::checkGradient(
	const MatrixXd& input,
	const MatrixXd& output,
	double epsilon,
	const Parameters& params)
{
	if(input.rows() != dimIn() || output.rows() != dimOut())
		throw Exception("Data has wrong dimensionality.");
	if(input.cols() != output.cols())
		throw Exception("The number of inputs and outputs should be the same.");

	// request memory for LBFGS and copy parameters
	lbfgsfloatval_t* x = parameters(params);

	int numParams = numParameters(params);

	lbfgsfloatval_t y[numParams];
	lbfgsfloatval_t g[numParams];
	lbfgsfloatval_t n[numParams];
	lbfgsfloatval_t val1;
	lbfgsfloatval_t val2;

	// make another copy
	for(int i = 0; i < numParams; ++i)
		y[i] = x[i];

	// arguments to LBFGS function
	InstanceLBFGS instance(this, &params, &input, &output);

	// compute numerical gradient using central differences
	for(int i = 0; i < numParams; ++i) {
		y[i] = x[i] + epsilon;
		val1 = evaluateLBFGS(&instance, y, 0, 0, 0.);
		y[i] = x[i] - epsilon;
		val2 = evaluateLBFGS(&instance, y, 0, 0, 0.);
		y[i] = x[i];
		n[i] = (val1 - val2) / (2. * epsilon);
	}

	// compute analytical gradient
	evaluateLBFGS(&instance, x, g, 0, 0.);

	// squared error
	double err = 0.;
	for(int i = 0; i < numParams; ++i)
		err += (g[i] - n[i]) * (g[i] - n[i]);

	return sqrt(err);
}

double CMT::Trainable::checkPerformance(
	const MatrixXd& input,
	const MatrixXd& output,
	int repetitions,
	const Parameters& params)
{
	if(input.rows() != dimIn() || output.rows() != dimOut())
		throw Exception("Data has wrong dimensionality.");
	if(input.cols() != output.cols())
		throw Exception("The number of inputs and outputs should be the same.");

	// request memory for LBFGS and copy parameters
	lbfgsfloatval_t* x = parameters(params);

	// optimization hyperparameters
	lbfgs_parameter_t paramsLBFGS;
	lbfgs_parameter_init(&paramsLBFGS);
	paramsLBFGS.max_iterations = params.maxIter;
	paramsLBFGS.m = params.numGrad;

	// wrap additional arguments
	InstanceLBFGS instance(this, &params, &input, &output);

	// measure time it takes to evaluate gradient in seconds
	lbfgsfloatval_t* g = lbfgs_malloc(numParameters(params));
	timeval from, to;

	gettimeofday(&from, 0);
	for(int i = 0; i < repetitions; ++i)
		evaluateLBFGS(&instance, x, g, 0, 0.);
	gettimeofday(&to, 0);

	// free memory used by LBFGS
	lbfgs_free(x);

	return (to.tv_sec + to.tv_usec / 1E6 - from.tv_sec - from.tv_usec / 1E6) / repetitions;
}
<|endoftext|>


<commit_before>//////////////////////////////////////////////////////////////////////////
//
// SneezyMUD - All rights reserved, SneezyMUD Coding Team
//
//////////////////////////////////////////////////////////////////////////

#include "comm.h"
#include "room.h"
#include "obj_plant.h"
#include "thing.h"
#include "being.h"

TPlant::TPlant() :
  TExpandableContainer()
{
  setType(0);
  setAge(0);
  setYield(0);
  setCarryWeightLimit(100.0);
  setCarryVolumeLimit(10000);
}

TPlant::TPlant(const TPlant &a) :
  TExpandableContainer(a)
{
}

void TPlant::assignFourValues(int x1, int x2, int x3, int x4)
{
  setType(x1);
  setAge(x2);
  setYield(x3);
  setVerminated(x4);
}

void TPlant::getFourValues(int *x1, int *x2, int *x3, int *x4) const
{
  *x1 = getType();
  *x2 = getAge();
  *x3 = getYield();
  *x4 = getVerminated();
}

TPlant & TPlant::operator=(const TPlant &a)
{
  if (this == &a) return *this;
  TObj::operator=(a);
  return *this;
}

TPlant::~TPlant()
{
  if (this->getVerminated())
	  vlogf(LOG_MISC, format("%s is dying: lifetime yield of %i, %i lost to vermin.") % this->getName() % this->getYield() % this->getVerminated());
  else
	  vlogf(LOG_MISC, format("%s is dying: lifetime yield of %i.") % this->getName() % this->getYield());
}

int TPlant::putSomethingInto(TBeing *ch, TThing *)
{
  // technically, would be OK since is a container, but prevent them anyhow
  act("Unfortunately, you can't put things into $p.",
             FALSE, ch, this, 0, TO_CHAR);
  return 2;
}

sstring TPlant::statObjInfo() const
{
  char buf[256];
  if (this->getVerminated())
	  sprintf(buf, "Type: %i Age: %i Total Yield: %i To Vermin: %i", getType(), getAge(), getYield(), getVerminated());
  else
	  sprintf(buf, "Type: %i Age: %i Total Yield: %i", getType(), getAge(), getYield());
  sstring a(buf);
  return a;
}

// the case numbers are the vnum of the seed tool object
// the return value is the plant type index
int seed_to_plant(int vnum)
{
  switch(vnum){
    case 13880:
      return 0;
    case 13881:
      return 1;
    case 13882:
      return 2;
    case 13883:
      return 3;
    case 13884:
      return 4;
    case 13885:
      return 5;
    case 13886:
      return 6;
    case 34213:
      return 7;
    case 33526:
      return 8;
    case 35485:
      return 9;
    case 35499:
      return 10;
    case 33600:
      return 11;
    case 34738:
      return 12;
    case 29410:  // candy heart seed
      return 13; // candy heart tree
    case 34216:  // grape seeds
      return 14; // gray grapes
  }

  return 0;
}

void TPlant::updateDesc()
{
  int plantindex=std::min(3, (getAge()/10));
  char buf[256];

  const char *plantkeywords [] =
  {
    "mound dirt",
    "sprout tiny",
    "plant small %s",
    "plant %s",
    "plant withered %s"
  };

  const char *plantname [] =
  {
    "a small mound of <o>dirt<1>",
    "a tiny sprout",
    "a small %s",
    "a %s",
    "an old, withered %s"
  };

  const char *plantdesc [] =
  {
    "A small mound of <o>dirt<1> is here.",
    "A tiny sprout is growing here.",
    "A small %s is here.",
    "A %s is here.",
    "An old, withered %s is here."
  };

  const char *planttypes [] =
  {
    "<r>tomato<1> <g>plant<1>",
    "<r>red<1> rose bush",
    "<r>apple<1> tree",
    "<W>white<1> rose bush",
    "<Y>yellow<1> rose bush",
    "<o>orange<1> tree",
    "<g>money<1> tree",
    "<w>pipe<g>weed<1><o> bush<1>",
    "<o>pumpkin<1> vine",
    "<g>turnip<1> plant",
    "<G>lettuce<1> plant",
    "<g>marijuana<1> plant",
    "
catnip<1> <g>plant<1>",
    "
candy heart<1> tree",
    "vine of <k>gray grapes<1>"
  };
  const char *planttypeskeywords [] =
  {
    "tomato",
    "red rose bush",
    "apple tree",
    "white rose bush",
    "yellow rose bush",
    "orange tree",
    "money tree",
    "pipeweed bush",
    "pumpkin vine",
    "turnip plant",
    "lettuce plant",
    "marijuana plant",
    "catnip plant",
    "candy heart tree",
    "gray grape vine"
  };
  int plantfruits [] =
  {
    14348,
    28917,
    8936,
    28918,
    28919,
    432,
    13,
    34212,
    33507,
    33508,
    33525,
    33601,
    34737,
    29405,
    34215
  };

  // considering a plant ages an average of 2 'age' points each mud hour,
  // a plant will age to 17520 in a mud year
  int plantlifeexpectancy [] =
  {
    175200, // 10 years
    262800, // 15 years
    219000,  // 12.5 years
    262800,  // 15 years
    262800,  // 15 years
    219000,  // 12.5 years
    35040,  // 2 years
    17520,  // 1 year
    8760, // 1/2 year
    8760, // 1/2 year
    8760, // bah
    17520, // 1 year
    17520, // 1 year
    2350, // 2 real days
    262800 // 15 years
  };

  // really old plants should wither and die
  if(getAge() > plantlifeexpectancy[getType()]){
    plantindex=4;
    if(obj_flags.decay_time <= -1)
      obj_flags.decay_time=300;
  }

  if (isObjStat(ITEM_STRUNG)) {
    delete [] shortDescr;
    delete [] descr;

    extraDescription *exd;
    while ((exd = ex_description)) {
      ex_description = exd->next;
      delete exd;
    }
    ex_description = NULL;
    delete [] action_description;
    action_description = NULL;
  } else {
    addObjStat(ITEM_STRUNG);
    name = mud_str_dup(obj_index[getItemIndex()].name);
    ex_description = NULL;
    action_description = NULL;
  }

  if(plantindex>=2){
    sprintf(buf, plantkeywords[plantindex], planttypeskeywords[getType()]);
    delete [] name;
    name = mud_str_dup(buf);

    sprintf(buf, plantname[plantindex], planttypes[getType()]);
    shortDescr = mud_str_dup(buf);

    sprintf(buf, plantdesc[plantindex], planttypes[getType()]);
    setDescr(mud_str_dup(buf));
  } else {
    sprintf(buf, plantkeywords[plantindex]);
    delete [] name;
    name = mud_str_dup(buf);

    sprintf(buf, plantname[plantindex]);
    shortDescr = mud_str_dup(buf);

    sprintf(buf, plantdesc[plantindex]);
    setDescr(mud_str_dup(buf));
  }

  if(plantindex == 3 && !::number(0,3)){
    TThing *t;
    int count=0;

    for(StuffIter it=stuff.begin();it!=stuff.end() && (t=*it);++it){
      ++count;
    }

    if(count<=4 &&
       (obj_index[real_object(plantfruits[getType()])].getNumber() <
	obj_index[real_object(plantfruits[getType()])].max_exist)){
      t=read_object(plantfruits[getType()], VIRTUAL);
      *this += *t;
      setYield(getYield() + 1);
    }
  }

  if(plantindex == 4 && !::number(0,11)){
    TThing *t;
    int count=0;

    for(StuffIter it=stuff.begin();it!=stuff.end() && (t=*it);++it){
      ++count;
    }

    if(count<=2){
      t=read_object(plantfruits[getType()], VIRTUAL);
      *this += *t;
      setYield(getYield() + 1);
    }
  }

  if(roomp){
    TObj *o;
    for(StuffIter it=stuff.begin();it!=stuff.end();++it){
      if((o=dynamic_cast<TObj *>(*it)) &&
	 (o->obj_flags.decay_time >= 0) && // only drop if it will decay
	 !::number(0,40)){
	--(*o);
	*roomp+=*o;
	sendrpf(roomp, "%s falls to the ground.\n\r",
		sstring(o->getName()).cap().c_str());
	break; // iterator is invalidated
      }
    }
  }

  /*
  if (roomp && getType() == 13 && plantindex > 1) {
	  // candy trees spawn rainbow-rats, which eat the candy
	  if (plantindex > 2) {
		  TThing *tcount;
		  TPlant *tree;
		  double tree_count = 0.0;
		  for(StuffIter it=roomp->stuff.begin();it!=roomp->stuff.end() && (tcount=*it);++it) {
			tree = dynamic_cast<TPlant *>(tcount);
		    if(tree && tree->getType() == getType()) {
		      // looking for same type of trees
		      // let's only count trees with stuff in them, to avoid over-population of vermin
		      if (tree->getStuff())
		    	  ++tree_count;
		    }
		  }
		  if (tree_count > 1 && ::number(1, 100000) < (int) ((tree_count / 16) * 200)) {
			// this is about a 17% chance per MUD day for a room of 8 laden trees
			// 5% for a room of 2 trees
			int mob_vnum = MOB_CANDY_HEART_EATER; // sugar-toothed weasel
		    TBeing *mob = read_mobile(mob_vnum, VIRTUAL);
		    if (!mob) {
		      // vlogf(LOG_PROC, format("Failed attempt to spawn a fruit scavenger (%i) in room %i.") % mob_vnum % in_room);
		      return;
		    }
		    *roomp += *mob;
		    colorAct(COLOR_MOBS,
		            ((mob->ex_description && mob->ex_description->findExtraDesc("repop")) ?
		            mob->ex_description->findExtraDesc("repop") :
		            "$n appears suddenly in the room."), TRUE, mob, 0, 0, TO_ROOM);
		  }
	  }
  }
  */
}

void TPlant::updateAge(){
  setAge(getAge()+::number(1,3));
  updateDesc();
  if(roomp)

    roomp->saveItems("");
}

<commit_msg>slowed down dropping of plant products<commit_after>//////////////////////////////////////////////////////////////////////////
//
// SneezyMUD - All rights reserved, SneezyMUD Coding Team
//
//////////////////////////////////////////////////////////////////////////

#include "comm.h"
#include "room.h"
#include "obj_plant.h"
#include "thing.h"
#include "being.h"

TPlant::TPlant() :
  TExpandableContainer()
{
  setType(0);
  setAge(0);
  setYield(0);
  setCarryWeightLimit(100.0);
  setCarryVolumeLimit(10000);
}

TPlant::TPlant(const TPlant &a) :
  TExpandableContainer(a)
{
}

void TPlant::assignFourValues(int x1, int x2, int x3, int x4)
{
  setType(x1);
  setAge(x2);
  setYield(x3);
  setVerminated(x4);
}

void TPlant::getFourValues(int *x1, int *x2, int *x3, int *x4) const
{
  *x1 = getType();
  *x2 = getAge();
  *x3 = getYield();
  *x4 = getVerminated();
}

TPlant & TPlant::operator=(const TPlant &a)
{
  if (this == &a) return *this;
  TObj::operator=(a);
  return *this;
}

TPlant::~TPlant()
{
  if (this->getVerminated())
	  vlogf(LOG_MISC, format("%s is dying: lifetime yield of %i, %i lost to vermin.") % this->getName() % this->getYield() % this->getVerminated());
  else
	  vlogf(LOG_MISC, format("%s is dying: lifetime yield of %i.") % this->getName() % this->getYield());
}

int TPlant::putSomethingInto(TBeing *ch, TThing *)
{
  // technically, would be OK since is a container, but prevent them anyhow
  act("Unfortunately, you can't put things into $p.",
             FALSE, ch, this, 0, TO_CHAR);
  return 2;
}

sstring TPlant::statObjInfo() const
{
  char buf[256];
  if (this->getVerminated())
	  sprintf(buf, "Type: %i Age: %i Total Yield: %i To Vermin: %i", getType(), getAge(), getYield(), getVerminated());
  else
	  sprintf(buf, "Type: %i Age: %i Total Yield: %i", getType(), getAge(), getYield());
  sstring a(buf);
  return a;
}

// the case numbers are the vnum of the seed tool object
// the return value is the plant type index
int seed_to_plant(int vnum)
{
  switch(vnum){
    case 13880:
      return 0;
    case 13881:
      return 1;
    case 13882:
      return 2;
    case 13883:
      return 3;
    case 13884:
      return 4;
    case 13885:
      return 5;
    case 13886:
      return 6;
    case 34213:
      return 7;
    case 33526:
      return 8;
    case 35485:
      return 9;
    case 35499:
      return 10;
    case 33600:
      return 11;
    case 34738:
      return 12;
    case 29410:  // candy heart seed
      return 13; // candy heart tree
    case 34216:  // grape seeds
      return 14; // gray grapes
  }

  return 0;
}

void TPlant::updateDesc()
{
  int plantindex=std::min(3, (getAge()/10));
  char buf[256];

  const char *plantkeywords [] =
  {
    "mound dirt",
    "sprout tiny",
    "plant small %s",
    "plant %s",
    "plant withered %s"
  };

  const char *plantname [] =
  {
    "a small mound of <o>dirt<1>",
    "a tiny sprout",
    "a small %s",
    "a %s",
    "an old, withered %s"
  };

  const char *plantdesc [] =
  {
    "A small mound of <o>dirt<1> is here.",
    "A tiny sprout is growing here.",
    "A small %s is here.",
    "A %s is here.",
    "An old, withered %s is here."
  };

  const char *planttypes [] =
  {
    "<r>tomato<1> <g>plant<1>",
    "<r>red<1> rose bush",
    "<r>apple<1> tree",
    "<W>white<1> rose bush",
    "<Y>yellow<1> rose bush",
    "<o>orange<1> tree",
    "<g>money<1> tree",
    "<w>pipe<g>weed<1><o> bush<1>",
    "<o>pumpkin<1> vine",
    "<g>turnip<1> plant",
    "<G>lettuce<1> plant",
    "<g>marijuana<1> plant",
    "
catnip<1> <g>plant<1>",
    "
candy heart<1> tree",
    "vine of <k>gray grapes<1>"
  };
  const char *planttypeskeywords [] =
  {
    "tomato",
    "red rose bush",
    "apple tree",
    "white rose bush",
    "yellow rose bush",
    "orange tree",
    "money tree",
    "pipeweed bush",
    "pumpkin vine",
    "turnip plant",
    "lettuce plant",
    "marijuana plant",
    "catnip plant",
    "candy heart tree",
    "gray grape vine"
  };
  int plantfruits [] =
  {
    14348,
    28917,
    8936,
    28918,
    28919,
    432,
    13,
    34212,
    33507,
    33508,
    33525,
    33601,
    34737,
    29405,
    34215
  };

  // considering a plant ages an average of 2 'age' points each mud hour,
  // a plant will age to 17520 in a mud year
  int plantlifeexpectancy [] =
  {
    175200, // 10 years
    262800, // 15 years
    219000,  // 12.5 years
    262800,  // 15 years
    262800,  // 15 years
    219000,  // 12.5 years
    35040,  // 2 years
    17520,  // 1 year
    8760, // 1/2 year
    8760, // 1/2 year
    8760, // bah
    17520, // 1 year
    17520, // 1 year
    2350, // 2 real days
    262800 // 15 years
  };

  // really old plants should wither and die
  if(getAge() > plantlifeexpectancy[getType()]){
    plantindex=4;
    if(obj_flags.decay_time <= -1)
      obj_flags.decay_time=300;
  }

  if (isObjStat(ITEM_STRUNG)) {
    delete [] shortDescr;
    delete [] descr;

    extraDescription *exd;
    while ((exd = ex_description)) {
      ex_description = exd->next;
      delete exd;
    }
    ex_description = NULL;
    delete [] action_description;
    action_description = NULL;
  } else {
    addObjStat(ITEM_STRUNG);
    name = mud_str_dup(obj_index[getItemIndex()].name);
    ex_description = NULL;
    action_description = NULL;
  }

  if(plantindex>=2){
    sprintf(buf, plantkeywords[plantindex], planttypeskeywords[getType()]);
    delete [] name;
    name = mud_str_dup(buf);

    sprintf(buf, plantname[plantindex], planttypes[getType()]);
    shortDescr = mud_str_dup(buf);

    sprintf(buf, plantdesc[plantindex], planttypes[getType()]);
    setDescr(mud_str_dup(buf));
  } else {
    sprintf(buf, plantkeywords[plantindex]);
    delete [] name;
    name = mud_str_dup(buf);

    sprintf(buf, plantname[plantindex]);
    shortDescr = mud_str_dup(buf);

    sprintf(buf, plantdesc[plantindex]);
    setDescr(mud_str_dup(buf));
  }

  if(plantindex == 3 && !::number(0,3)){
    TThing *t;
    int count=0;

    for(StuffIter it=stuff.begin();it!=stuff.end() && (t=*it);++it){
      ++count;
    }

    if(count<=4 &&
       (obj_index[real_object(plantfruits[getType()])].getNumber() <
	obj_index[real_object(plantfruits[getType()])].max_exist)){
      t=read_object(plantfruits[getType()], VIRTUAL);
      *this += *t;
      setYield(getYield() + 1);
    }
  }

  if(plantindex == 4 && !::number(0,11)){
    TThing *t;
    int count=0;

    for(StuffIter it=stuff.begin();it!=stuff.end() && (t=*it);++it){
      ++count;
    }

    if(count<=2){
      t=read_object(plantfruits[getType()], VIRTUAL);
      *this += *t;
      setYield(getYield() + 1);
    }
  }

  if(roomp){
    TObj *o;
    for(StuffIter it=stuff.begin();it!=stuff.end();++it){
      if((o=dynamic_cast<TObj *>(*it)) &&
	 (o->obj_flags.decay_time >= 0) && // only drop if it will decay
	 !::number(0,150)){
	--(*o);
	*roomp+=*o;
	sendrpf(roomp, "%s falls to the ground.\n\r",
		sstring(o->getName()).cap().c_str());
	break; // iterator is invalidated
      }
    }
  }

  /*
  if (roomp && getType() == 13 && plantindex > 1) {
	  // candy trees spawn rainbow-rats, which eat the candy
	  if (plantindex > 2) {
		  TThing *tcount;
		  TPlant *tree;
		  double tree_count = 0.0;
		  for(StuffIter it=roomp->stuff.begin();it!=roomp->stuff.end() && (tcount=*it);++it) {
			tree = dynamic_cast<TPlant *>(tcount);
		    if(tree && tree->getType() == getType()) {
		      // looking for same type of trees
		      // let's only count trees with stuff in them, to avoid over-population of vermin
		      if (tree->getStuff())
		    	  ++tree_count;
		    }
		  }
		  if (tree_count > 1 && ::number(1, 100000) < (int) ((tree_count / 16) * 200)) {
			// this is about a 17% chance per MUD day for a room of 8 laden trees
			// 5% for a room of 2 trees
			int mob_vnum = MOB_CANDY_HEART_EATER; // sugar-toothed weasel
		    TBeing *mob = read_mobile(mob_vnum, VIRTUAL);
		    if (!mob) {
		      // vlogf(LOG_PROC, format("Failed attempt to spawn a fruit scavenger (%i) in room %i.") % mob_vnum % in_room);
		      return;
		    }
		    *roomp += *mob;
		    colorAct(COLOR_MOBS,
		            ((mob->ex_description && mob->ex_description->findExtraDesc("repop")) ?
		            mob->ex_description->findExtraDesc("repop") :
		            "$n appears suddenly in the room."), TRUE, mob, 0, 0, TO_ROOM);
		  }
	  }
  }
  */
}

void TPlant::updateAge(){
  setAge(getAge()+::number(1,3));
  updateDesc();
  if(roomp)

    roomp->saveItems("");
}

<|endoftext|>


<commit_before>#pragma once

#include "../StandardInclude.hpp"
#include "../EntityInclude.hpp"

#include "../StateInterface.hpp"

class GameTitle : public StateInterface
{
public: static GameTitle &GetInstance(void) { static auto instance = GameTitle(); return instance; }
private: GameTitle() { }

private:
	bool IsInitial = true;
	int logoHandle;
	int fontHandle;

public:
	// 場面名を取得します
	string StateName()
	{
		return "Title";
	}

	// 更新(ターゲット時のみ)
	void Update()
	{
		if (IsInitial)
		{
			IsInitial = false;

			logoHandle = LoadGraph("Image/logo.png", 1);
			fontHandle = CreateFontToHandle("メイリオ", 25, 5, DX_FONTTYPE_ANTIALIASING_8X8);
		}
	}

	// 描画(常時)
	void Draw(StateEventArgs e)
	{
		if (e.IsActive())
		{
			auto screenRightBottom = Core::GetInstance().ScreenSize.GetWidthHeightAsPoint();
			auto screenCenter = screenRightBottom / 2;

			int imageSize[2];
			GetGraphSize(logoHandle, &imageSize[0], &imageSize[1]);
			auto logoRightBottom = Point(imageSize[0], imageSize[1]);
			auto logoCenter = logoRightBottom / 2;

			// logo
			auto logoLocation = screenCenter - logoCenter;
			logoLocation.AddY(-150);
			DrawGraph(logoLocation.GetX(), logoLocation.GetY(), logoHandle, 1);

			// menu
			auto itemCenter = screenCenter;
			Size boxSize(screenRightBottom.GetX() / 3, 40);

			itemCenter.AddY(100);
			DrawMenuItem(itemCenter, boxSize, "Start");

			itemCenter.AddY(60);
			DrawMenuItem(itemCenter, boxSize, "Setting");

			itemCenter.AddY(60);
			DrawMenuItem(itemCenter, boxSize, "End");
		}
	}

	void DrawMenuItem(Point centerPosition, Size boxSize, const char *text)
	{
		SetDrawBlendMode(DX_BLENDMODE_ALPHA, (int)(255 * 0.7));
		DrawBox(centerPosition.GetX() - boxSize.GetWidth() / 2, centerPosition.GetY() - boxSize.GetHeight() / 2, centerPosition.GetX() + boxSize.GetWidth() / 2, centerPosition.GetY() + boxSize.GetHeight() / 2, 0xffffff, 0);
		SetDrawBlendMode(DX_BLENDMODE_NOBLEND, 0);
		DrawStringToHandle(centerPosition.GetX() - GetDrawStringWidthToHandle(text, strlen(text), fontHandle) / 2, centerPosition.GetY() - 25 / 2, text, 0xffffff, fontHandle);
	}
};<commit_msg>Refactoring<commit_after>#pragma once

#include "../StandardInclude.hpp"
#include "../EntityInclude.hpp"

#include "../StateInterface.hpp"

class GameTitle : public StateInterface
{
public: static GameTitle &GetInstance(void) { static auto instance = GameTitle(); return instance; }
private: GameTitle() { }

private:
	bool IsInitial = true;
	int logoHandle;
	int fontHandle;

public:
	// 場面名を取得します
	string StateName()
	{
		return "Title";
	}

	// 更新(ターゲット時のみ)
	void Update()
	{
		if (IsInitial)
		{
			IsInitial = false;

			logoHandle = LoadGraph("Image/logo.png", 1);
			fontHandle = CreateFontToHandle("メイリオ", 25, 5, DX_FONTTYPE_ANTIALIASING_8X8);
		}
	}

	// 描画(常時)
	void Draw(StateEventArgs e)
	{
		if (e.IsActive())
		{
			auto screenRightBottom = Core::GetInstance().ScreenSize.GetWidthHeightAsPoint();
			auto screenCenter = screenRightBottom / 2;

			int imageSize[2];
			GetGraphSize(logoHandle, &imageSize[0], &imageSize[1]);
			auto logoRightBottom = Point(imageSize[0], imageSize[1]);
			auto logoCenter = logoRightBottom / 2;

			// logo
			auto logoLocation = screenCenter - logoCenter;
			logoLocation.AddY(-150);
			DrawGraph(logoLocation.GetX(), logoLocation.GetY(), logoHandle, 1);

			// menu
			auto itemCenter = screenCenter;
			Size boxSize(screenRightBottom.GetX() / 3, 40);

			itemCenter.AddY(100);
			DrawMenuItem(itemCenter, boxSize, "Start");

			itemCenter.AddY(60);
			DrawMenuItem(itemCenter, boxSize, "Setting");

			itemCenter.AddY(60);
			DrawMenuItem(itemCenter, boxSize, "End");
		}
	}

	void DrawMenuItem(Point centerPosition, Size boxSize, const char *text)
	{
		SetDrawBlendMode(DX_BLENDMODE_ALPHA, (int)(255 * 0.7));
		boxSize = boxSize / 2;
		DrawBox(centerPosition.GetX() - boxSize.GetWidth(), centerPosition.GetY() - boxSize.GetHeight(), centerPosition.GetX() + boxSize.GetWidth(), centerPosition.GetY() + boxSize.GetHeight(), 0xffffff, 0);
		SetDrawBlendMode(DX_BLENDMODE_NOBLEND, 0);
		DrawStringToHandle(centerPosition.GetX() - GetDrawStringWidthToHandle(text, strlen(text), fontHandle) / 2, centerPosition.GetY() - 25 / 2, text, 0xffffff, fontHandle);
	}
};<|endoftext|>


<commit_before>// Copyright 2022 The IREE Authors
//
// Licensed under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

//===- AMDConfig.h - AMD CodeGen Configurations ---------------------------===//
//
// This file contains CodeGen configurations for AMD GPUs.
//
//===----------------------------------------------------------------------===//

#include "iree/compiler/Codegen/Dialect/LoweringConfig.h"
#include "iree/compiler/Codegen/SPIRV/KernelConfig.h"
#include "iree/compiler/Codegen/Utils/Utils.h"
#include "llvm/Support/Debug.h"
#include "mlir/Dialect/Linalg/IR/Linalg.h"
#include "mlir/IR/BuiltinOps.h"

#define DEBUG_TYPE "iree-spirv-amd-config"

namespace mlir {
namespace iree_compiler {
namespace detail {

static LogicalResult setAMDMatmulConfig(linalg::LinalgOp op, int subgroupSize) {
  const std::array<int64_t, 2> workgroupXY = {subgroupSize / 2, 8};
  const std::array<int64_t, 3> threadMNK = {8, 4, 32};
  return setMatmulOpConfig(op, subgroupSize, workgroupXY, threadMNK,
                           /*useWorkgroupMemory=*/true);
}

// RDNA architecture:
// https://gpuopen.com/wp-content/uploads/2019/08/RDNA_Architecture_public.pdf
//
// Workgroup Processor (WGP) is the block for workgroups in RDNA; it has its own
// instruction/constant cache, L0 cache x2, Local Data Share (LDS, a.k.a. shared
// memory), SALU x4, SIMD32 x4.
//
// * 1024 registers per SIMD32
// * 128KB LDS per WGP
// * Max 20 waves per SIMD32
// * Max 64KB LDS per workgroup

LogicalResult setAMDCodeGenConfig(const spirv::TargetEnv &targetEnv,
                                  Operation *rootOp) {
  int subgroupSize = targetEnv.getResourceLimits().getSubgroupSize();

  if (auto linalgOp = dyn_cast<linalg::LinalgOp>(rootOp)) {
    if (isMatmulOrBatchMatmul(linalgOp))
      return setAMDMatmulConfig(linalgOp, subgroupSize);
  }

  return TypeSwitch<Operation *, LogicalResult>(rootOp)
      .Case<linalg::BatchMatmulOp, linalg::MatmulOp>([subgroupSize](auto op) {
        return setAMDMatmulConfig(op, subgroupSize);
      })
      .Default([](Operation *) { return success(); });
}

}  // namespace detail
}  // namespace iree_compiler
}  // namespace mlir
<commit_msg>[spirv] Update default AMD convolution and matmul configuration (#10467)<commit_after>// Copyright 2022 The IREE Authors
//
// Licensed under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

//===- AMDConfig.h - AMD CodeGen Configurations ---------------------------===//
//
// This file contains CodeGen configurations for AMD GPUs.
//
//===----------------------------------------------------------------------===//

#include "iree/compiler/Codegen/Dialect/LoweringConfig.h"
#include "iree/compiler/Codegen/SPIRV/KernelConfig.h"
#include "iree/compiler/Codegen/Utils/Utils.h"
#include "llvm/Support/Debug.h"
#include "mlir/Dialect/Linalg/IR/Linalg.h"
#include "mlir/Dialect/Tensor/IR/Tensor.h"
#include "mlir/IR/BuiltinOps.h"

#define DEBUG_TYPE "iree-spirv-amd-config"

namespace mlir {
namespace iree_compiler {
namespace detail {

static LogicalResult setAMDMatmulConfig(linalg::LinalgOp op, int subgroupSize) {
  const std::array<int64_t, 2> workgroupXY = {subgroupSize / 2, 8};
  const std::array<int64_t, 3> threadMNK = {8, 4, 16};
  return setMatmulOpConfig(op, subgroupSize, workgroupXY, threadMNK,
                           /*useWorkgroupMemory=*/true);
}

// RDNA architecture:
// https://gpuopen.com/wp-content/uploads/2019/08/RDNA_Architecture_public.pdf
//
// Workgroup Processor (WGP) is the block for workgroups in RDNA; it has its own
// instruction/constant cache, L0 cache x2, Local Data Share (LDS, a.k.a. shared
// memory), SALU x4, SIMD32 x4.
//
// * 1024 registers per SIMD32
// * 128KB LDS per WGP
// * Max 20 waves per SIMD32
// * Max 64KB LDS per workgroup

LogicalResult setAMDCodeGenConfig(const spirv::TargetEnv &targetEnv,
                                  Operation *rootOp) {
  int subgroupSize = targetEnv.getResourceLimits().getSubgroupSize();

  if (auto linalgOp = dyn_cast<linalg::LinalgOp>(rootOp)) {
    if (isMatmulOrBatchMatmul(linalgOp))
      return setAMDMatmulConfig(linalgOp, subgroupSize);
  }

  return TypeSwitch<Operation *, LogicalResult>(rootOp)
      .Case<linalg::BatchMatmulOp, linalg::MatmulOp>([subgroupSize](auto op) {
        return setAMDMatmulConfig(op, subgroupSize);
      })
      .Case<linalg::Conv2DNhwcHwcfOp>([subgroupSize](auto op) {
        bool hasPaddedInput =
            op.image().template getDefiningOp<tensor::PadOp>();
        int bestTilingFactor = hasPaddedInput ? 16 : 32;
        return setConvOpConfig(op, subgroupSize, bestTilingFactor);
      })
      .Case<linalg::DepthwiseConv2DNhwcHwcOp>([subgroupSize](auto op) {
        bool hasPaddedInput =
            op.image().template getDefiningOp<tensor::PadOp>();
        int bestTilingFactor = hasPaddedInput ? 16 : 32;
        return setConvOpConfig(op, subgroupSize, bestTilingFactor);
      })
      .Default([](Operation *) { return success(); });
}

}  // namespace detail
}  // namespace iree_compiler
}  // namespace mlir
<|endoftext|>