<commit_before>/*
 * Copyright 2016, Simula Research Laboratory
 *
 * 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 <algorithm>
#include "Regression.h"

static void RemoveAllFiles(const boost::filesystem::path& dirPath);
static std::vector<boost::filesystem::path> CollectFiles(const boost::filesystem::path& dirPath);
static bool SortTags(FrameLog& log);

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

TestRunner::TestRunner(const std::string& inputDir, const std::string& outputDir, boost::optional<bool> useCuda) :
  _inputDirPath(inputDir), _outputDirPath(outputDir), _useCuda(useCuda)
{
  if (!exists(_inputDirPath) || !is_directory(_inputDirPath))
    throw std::runtime_error("TestRunner: inputDir is not a directory");
  if (!exists(_outputDirPath) || !is_directory(_outputDirPath))
    throw std::runtime_error("TestRunner: outputDir is not a directory");
  RemoveAllFiles(_outputDirPath);
  _inputFilePaths = CollectFiles(_inputDirPath);
}

void TestRunner::adjustParameters(cctag::Parameters& parameters)
{
  if (_useCuda)
    parameters._useCuda = *_useCuda;
}

// Input directory must contain images.
// NB! parameters is by-val since we may need to adjust them.
void TestRunner::generateReferenceResults(cctag::Parameters parameters)
{
  adjustParameters(parameters);
  size_t i = 1, count = _inputFilePaths.size();
  for (const auto& inputFilePath: _inputFilePaths) {
    std::clog << "Processing file " << i++ << "/" << count << ": " << inputFilePath << std::endl;
    FileLog fileLog = FileLog::detect(inputFilePath.native(), parameters);
    auto outputPath = _outputDirPath / inputFilePath.filename().replace_extension(".xml");
    fileLog.save(outputPath.native());
  }
}

// Input directory must contain XML files; parameters and input file will be read from those.
void TestRunner::generateTestResults()
{
  size_t i = 1, count = _inputFilePaths.size();
  for (const auto& inputFilePath: _inputFilePaths)
  if (inputFilePath.extension() == ".xml") {
    std::clog << "Processing file " << i++ << "/" << count << ": " << inputFilePath << std::endl;
    FileLog fileLog;
    fileLog.load(inputFilePath.native());
    adjustParameters(fileLog.parameters);
    fileLog = FileLog::detect(fileLog.filename, fileLog.parameters);
    auto outputPath = _outputDirPath / inputFilePath.filename();
    fileLog.save(outputPath.native());
  }
}

/////////////////////////////////////////////////////////////////////////////
// TestChecker assumption: all IDs in the frame are different.

TestChecker::TestChecker(const std::string& referenceDir, const std::string& testDir, float epsilon) :
  _referenceDirPath(referenceDir), _testDirPath(testDir), _epsilon(epsilon), _failed(false)
{
  if (!exists(_referenceDirPath) || !is_directory(_referenceDirPath))
    throw std::runtime_error("TestChecker: referenceDir is not a directory");
  if (!exists(_testDirPath) || !is_directory(_testDirPath))
    throw std::runtime_error("TestChecker: testDir is not a directory");
  _referenceFilePaths = CollectFiles(_referenceDirPath);
  _testFilePaths = CollectFiles(_testDirPath);
}

boost::filesystem::path TestChecker::testToReferencePath(const boost::filesystem::path& testPath)
{
  const auto testFilename = testPath.filename();
  auto it = std::find_if(_referenceFilePaths.begin(), _referenceFilePaths.end(),
    [testFilename](const boost::filesystem::path& p) { return p.filename() == testFilename; });
  return it != _referenceFilePaths.end() ? *it : boost::filesystem::path();
}

bool TestChecker::check()
{
  size_t i = 1, count = _testFilePaths.size();
  for (const auto& testFilePath: _testFilePaths) {
    std::clog << "Processing file " << i++ << "/" << count << ": " << testFilePath << std::endl;
    try {
      check(testFilePath);
    }
    catch (check_error& e) {
      std::clog << "  FAILED: " << e.what() << std::endl;
      _failed = true;
    }
  }
  return !_failed;
}

void TestChecker::check(const boost::filesystem::path& testFilePath)
{
  auto referenceFilePath = testToReferencePath(testFilePath);
  if (referenceFilePath.empty())
    throw check_error("reference file not found");

  FileLog referenceLog, testLog;
  referenceLog.load(referenceFilePath.native());
  testLog.load(testFilePath.native());
  compare(referenceLog, testLog);
}

void TestChecker::compare(FileLog& referenceLog, FileLog& testLog)
{
  const auto frameOrdCmp = [](const FrameLog& f1, const FrameLog& f2) { return f1.frame < f2.frame; };

  if (referenceLog.filename != testLog.filename)
    throw check_error("mismatching filenames");
  if (referenceLog.parameters._nCrowns != testLog.parameters._nCrowns)  // XXX: should check parameters better
    throw check_error("mismatching parameters");
  if (referenceLog.frameLogs.size() != testLog.frameLogs.size())
    throw check_error("mismatching frame counts");
  if (!std::is_sorted(referenceLog.frameLogs.begin(), referenceLog.frameLogs.end(), frameOrdCmp))
    throw check_error("reference log frames not monotonic");
  if (!std::is_sorted(testLog.frameLogs.begin(), testLog.frameLogs.end(), frameOrdCmp))
    throw check_error("test log frames not monotonic");

  const size_t frameCount = referenceLog.frameLogs.size();
  for (size_t i = 0; i < frameCount; ++i)
    compare(referenceLog.frameLogs[i], testLog.frameLogs[i], i);
}

void TestChecker::compare(FrameLog& referenceLog, FrameLog& testLog, size_t frame)
{
  if (referenceLog.tags.size() != testLog.tags.size())
    throw check_error(std::string("different # of tags in frame ") + std::to_string(frame));
  if (!SortTags(referenceLog))
    throw check_error("reference log contains duplicate IDs");
  if (!SortTags(testLog))
    throw check_error("test log contains duplicate IDs");

  _elapsedDiffAcc(testLog.elapsedTime - referenceLog.elapsedTime);

  const size_t tagCount = referenceLog.tags.size();
  for (size_t i = 0; i < tagCount; ++i) {
    _qualityDiffAcc(testLog.tags[i].quality - referenceLog.tags[i].quality);
    compare(referenceLog.tags[i], testLog.tags[i], frame);
  }
}

void TestChecker::compare(const DetectedTag& referenceTag, const DetectedTag& testTag, size_t frame)
{
  const bool ref_reliable = referenceTag.status == cctag::status::id_reliable;
  const bool test_reliable = testTag.status == cctag::status::id_reliable;

  if (ref_reliable ^ test_reliable)
    throw check_error(std::string("tags of different status in frame ") + std::to_string(frame));

  if (ref_reliable && test_reliable) {
    if (referenceTag.id != testTag.id)
      throw check_error(std::string("tags with different IDs frame ") + std::to_string(frame));

    float dx = fabs(referenceTag.x - testTag.x);
    float dy = fabs(referenceTag.y - testTag.y);
    if (dx > _epsilon || dy > _epsilon)
      throw check_error(std::string("tags at different positions in frame ") + std::to_string(frame));
  }
}

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

static void RemoveAllFiles(const boost::filesystem::path& dirPath)
{
  using namespace boost::filesystem;
  remove_all(dirPath);
  create_directories(dirPath);
}

static std::vector<boost::filesystem::path> CollectFiles(const boost::filesystem::path& dirPath)
{
  using namespace boost::filesystem;
  std::vector<path> filePaths;

  directory_iterator it(dirPath), end;
  while (it != end) {
    auto de = *it++;
    if (de.status().type() == regular_file)
      filePaths.push_back(canonical(de.path()));
  }

  return filePaths;
}

// Removes all tags with status != 1, then sorts them by id. Returns true if there are no duplicate tags.
static bool SortTags(FrameLog& log)
{
  {
    auto it = std::remove_if(log.tags.begin(), log.tags.end(),
      [](const DetectedTag& t) { return t.status < 1; });
    log.tags.erase(it, log.tags.end());
  }

  std::sort(log.tags.begin(), log.tags.end(),
    [](const DetectedTag& t1, const DetectedTag& t2) { return t1.id < t2.id; });

  return std::adjacent_find(log.tags.begin(), log.tags.end(),
    [](const DetectedTag& t1, const DetectedTag& t2) { return t1.id == t2.id; }) == log.tags.end();
}
<commit_msg>[applications] using string() instead of native()<commit_after>/*
 * Copyright 2016, Simula Research Laboratory
 *
 * 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 <algorithm>
#include "Regression.h"

static void RemoveAllFiles(const boost::filesystem::path& dirPath);
static std::vector<boost::filesystem::path> CollectFiles(const boost::filesystem::path& dirPath);
static bool SortTags(FrameLog& log);

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

TestRunner::TestRunner(const std::string& inputDir, const std::string& outputDir, boost::optional<bool> useCuda) :
  _inputDirPath(inputDir), _outputDirPath(outputDir), _useCuda(useCuda)
{
  if (!exists(_inputDirPath) || !is_directory(_inputDirPath))
    throw std::runtime_error("TestRunner: inputDir is not a directory");
  if (!exists(_outputDirPath) || !is_directory(_outputDirPath))
    throw std::runtime_error("TestRunner: outputDir is not a directory");
  RemoveAllFiles(_outputDirPath);
  _inputFilePaths = CollectFiles(_inputDirPath);
}

void TestRunner::adjustParameters(cctag::Parameters& parameters)
{
  if (_useCuda)
    parameters._useCuda = *_useCuda;
}

// Input directory must contain images.
// NB! parameters is by-val since we may need to adjust them.
void TestRunner::generateReferenceResults(cctag::Parameters parameters)
{
  adjustParameters(parameters);
  size_t i = 1, count = _inputFilePaths.size();
  for (const auto& inputFilePath: _inputFilePaths) {
    std::clog << "Processing file " << i++ << "/" << count << ": " << inputFilePath << std::endl;
    FileLog fileLog = FileLog::detect(inputFilePath.string(), parameters);
    const auto outputPath = _outputDirPath / inputFilePath.filename().replace_extension(".xml");
    fileLog.save(outputPath.string());
  }
}

// Input directory must contain XML files; parameters and input file will be read from those.
void TestRunner::generateTestResults()
{
  size_t i = 1, count = _inputFilePaths.size();
  for (const auto& inputFilePath: _inputFilePaths)
  if (inputFilePath.extension() == ".xml") {
    std::clog << "Processing file " << i++ << "/" << count << ": " << inputFilePath << std::endl;
    FileLog fileLog;
    fileLog.load(inputFilePath.string());
    adjustParameters(fileLog.parameters);
    fileLog = FileLog::detect(fileLog.filename, fileLog.parameters);
    auto outputPath = _outputDirPath / inputFilePath.filename();
    fileLog.save(outputPath.string());
  }
}

/////////////////////////////////////////////////////////////////////////////
// TestChecker assumption: all IDs in the frame are different.

TestChecker::TestChecker(const std::string& referenceDir, const std::string& testDir, float epsilon) :
  _referenceDirPath(referenceDir), _testDirPath(testDir), _epsilon(epsilon), _failed(false)
{
  if (!exists(_referenceDirPath) || !is_directory(_referenceDirPath))
    throw std::runtime_error("TestChecker: referenceDir is not a directory");
  if (!exists(_testDirPath) || !is_directory(_testDirPath))
    throw std::runtime_error("TestChecker: testDir is not a directory");
  _referenceFilePaths = CollectFiles(_referenceDirPath);
  _testFilePaths = CollectFiles(_testDirPath);
}

boost::filesystem::path TestChecker::testToReferencePath(const boost::filesystem::path& testPath)
{
  const auto testFilename = testPath.filename();
  auto it = std::find_if(_referenceFilePaths.begin(), _referenceFilePaths.end(),
    [testFilename](const boost::filesystem::path& p) { return p.filename() == testFilename; });
  return it != _referenceFilePaths.end() ? *it : boost::filesystem::path();
}

bool TestChecker::check()
{
  size_t i = 1, count = _testFilePaths.size();
  for (const auto& testFilePath: _testFilePaths) {
    std::clog << "Processing file " << i++ << "/" << count << ": " << testFilePath << std::endl;
    try {
      check(testFilePath);
    }
    catch (check_error& e) {
      std::clog << "  FAILED: " << e.what() << std::endl;
      _failed = true;
    }
  }
  return !_failed;
}

void TestChecker::check(const boost::filesystem::path& testFilePath)
{
  auto referenceFilePath = testToReferencePath(testFilePath);
  if (referenceFilePath.empty())
    throw check_error("reference file not found");

  FileLog referenceLog, testLog;
  referenceLog.load(referenceFilePath.string());
  testLog.load(testFilePath.string());
  compare(referenceLog, testLog);
}

void TestChecker::compare(FileLog& referenceLog, FileLog& testLog)
{
  const auto frameOrdCmp = [](const FrameLog& f1, const FrameLog& f2) { return f1.frame < f2.frame; };

  if (referenceLog.filename != testLog.filename)
    throw check_error("mismatching filenames");
  if (referenceLog.parameters._nCrowns != testLog.parameters._nCrowns)  // XXX: should check parameters better
    throw check_error("mismatching parameters");
  if (referenceLog.frameLogs.size() != testLog.frameLogs.size())
    throw check_error("mismatching frame counts");
  if (!std::is_sorted(referenceLog.frameLogs.begin(), referenceLog.frameLogs.end(), frameOrdCmp))
    throw check_error("reference log frames not monotonic");
  if (!std::is_sorted(testLog.frameLogs.begin(), testLog.frameLogs.end(), frameOrdCmp))
    throw check_error("test log frames not monotonic");

  const size_t frameCount = referenceLog.frameLogs.size();
  for (size_t i = 0; i < frameCount; ++i)
    compare(referenceLog.frameLogs[i], testLog.frameLogs[i], i);
}

void TestChecker::compare(FrameLog& referenceLog, FrameLog& testLog, size_t frame)
{
  if (referenceLog.tags.size() != testLog.tags.size())
    throw check_error(std::string("different # of tags in frame ") + std::to_string(frame));
  if (!SortTags(referenceLog))
    throw check_error("reference log contains duplicate IDs");
  if (!SortTags(testLog))
    throw check_error("test log contains duplicate IDs");

  _elapsedDiffAcc(testLog.elapsedTime - referenceLog.elapsedTime);

  const size_t tagCount = referenceLog.tags.size();
  for (size_t i = 0; i < tagCount; ++i) {
    _qualityDiffAcc(testLog.tags[i].quality - referenceLog.tags[i].quality);
    compare(referenceLog.tags[i], testLog.tags[i], frame);
  }
}

void TestChecker::compare(const DetectedTag& referenceTag, const DetectedTag& testTag, size_t frame)
{
  const bool ref_reliable = referenceTag.status == cctag::status::id_reliable;
  const bool test_reliable = testTag.status == cctag::status::id_reliable;

  if (ref_reliable ^ test_reliable)
    throw check_error(std::string("tags of different status in frame ") + std::to_string(frame));

  if (ref_reliable && test_reliable) {
    if (referenceTag.id != testTag.id)
      throw check_error(std::string("tags with different IDs frame ") + std::to_string(frame));

    float dx = fabs(referenceTag.x - testTag.x);
    float dy = fabs(referenceTag.y - testTag.y);
    if (dx > _epsilon || dy > _epsilon)
      throw check_error(std::string("tags at different positions in frame ") + std::to_string(frame));
  }
}

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

static void RemoveAllFiles(const boost::filesystem::path& dirPath)
{
  using namespace boost::filesystem;
  remove_all(dirPath);
  create_directories(dirPath);
}

static std::vector<boost::filesystem::path> CollectFiles(const boost::filesystem::path& dirPath)
{
  using namespace boost::filesystem;
  std::vector<path> filePaths;

  directory_iterator it(dirPath), end;
  while (it != end) {
    auto de = *it++;
    if (de.status().type() == regular_file)
      filePaths.push_back(canonical(de.path()));
  }

  return filePaths;
}

// Removes all tags with status != 1, then sorts them by id. Returns true if there are no duplicate tags.
static bool SortTags(FrameLog& log)
{
  {
    auto it = std::remove_if(log.tags.begin(), log.tags.end(),
      [](const DetectedTag& t) { return t.status < 1; });
    log.tags.erase(it, log.tags.end());
  }

  std::sort(log.tags.begin(), log.tags.end(),
    [](const DetectedTag& t1, const DetectedTag& t2) { return t1.id < t2.id; });

  return std::adjacent_find(log.tags.begin(), log.tags.end(),
    [](const DetectedTag& t1, const DetectedTag& t2) { return t1.id == t2.id; }) == log.tags.end();
}
<|endoftext|>


<commit_before>/*
   Copyright 2014 John Bailey

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

#include "MetricUnit.hpp"

const std::string MetricUnit::m_namePrefix[ METRIC_UNIT_MAX ] = {
	"",
	"File: ",
	"\tFunction: ",
	"\tMethod: "
};
const std::string MetricUnit::m_dumpPrefix[ METRIC_UNIT_MAX ] = {
	"",
	"\t",
	"\t\t",
	"\t\t"
};
const std::string MetricUnit::m_subPrefix[ METRIC_UNIT_MAX ] = {
	"Files: ",
	"Functions/Methods: ",
	"",
	""
};

const std::string MetricUnit::m_metricNames[ METRIC_TYPE_MAX ] = {
	"IF statements",
	"ELSE statements",
	"FOR loops",
	"RETURN statements",
	"WHILE loops",
	"SWITCH statements",
	"CASE statements",
	"DEFAULT statements",
	"LOGICAL AND operators",
	"LOGICAL OR operators",
	"TERNARY operators",
	"GOTO statements",
	"LABEL statements",
	"Variables",
	"Return points",
	"Statements",
	"McCabe",
	"Modified McCabe"
};

MetricUnit::MetricUnit( const std::string& p_name, const MetricUnitType_e p_type ) : m_name( p_name ), m_type( p_type )
{
	uint16_t loop;

	for( loop = 0;
		 loop < METRIC_TYPE_MAX;
		 loop++ )
	{
		m_counters[ loop ] = 0;
	}
}

void MetricUnit::increment( const MetricType_e p_metricType )
{
	m_counters[ p_metricType ]++;
}

MetricUnitType_e MetricUnit::GetType( void ) const
{
	return m_type;
}

MetricUnit::counter_t MetricUnit::getSubUnitCount( const MetricUnitType_e p_type ) const
{
	counter_t ret_val = 0;

	for( SubUnitMap_t::const_iterator unitIt = m_subUnits.begin();
		 unitIt != m_subUnits.end();
		 ++unitIt )
	{
		if( (*unitIt).second->GetType() == p_type )
		{
			ret_val++;
		}
		ret_val += (*unitIt).second->getSubUnitCount( p_type );
	}

	return ret_val;
}

MetricUnit::counter_t MetricUnit::getCounter( const MetricType_e p_metricType ) const
{
	counter_t ret_val = 0;

	switch( p_metricType )
	{
		case METRIC_TYPE_MODIFIED_CYCLOMATIC:
			if(( m_type == METRIC_UNIT_FUNCTION ) ||
			   ( m_type == METRIC_UNIT_METHOD ))
			{
				ret_val = getCounter( METRIC_TYPE_FORLOOP ) +
						  getCounter( METRIC_TYPE_IF ) +
						  getCounter( METRIC_TYPE_WHILELOOP ) +
						  getCounter( METRIC_TYPE_SWITCH ) +
						  getCounter( METRIC_TYPE_LOGICAL_AND ) +
						  getCounter( METRIC_TYPE_LOGICAL_OR ) +
						  getCounter( METRIC_TYPE_TERNARY ) + 1;
			}
			break;
		case METRIC_TYPE_CYCLOMATIC:
			if(( m_type == METRIC_UNIT_FUNCTION ) ||
			   ( m_type == METRIC_UNIT_METHOD ))
			{
				// TODO: What about the 'DEFAULT'?
				ret_val = getCounter( METRIC_TYPE_FORLOOP ) +
						  getCounter( METRIC_TYPE_IF ) +
						  getCounter( METRIC_TYPE_WHILELOOP ) +
						  getCounter( METRIC_TYPE_CASE ) +
						  getCounter( METRIC_TYPE_LOGICAL_AND ) +
						  getCounter( METRIC_TYPE_LOGICAL_OR ) +
						  getCounter( METRIC_TYPE_TERNARY ) + 1;
			}
			break;
		default:
			ret_val  = m_counters[ p_metricType ];
			for( SubUnitMap_t::const_iterator unitIt = m_subUnits.begin();
				 unitIt != m_subUnits.end();
				 ++unitIt )
			{
				ret_val += (*unitIt).second->getCounter( p_metricType );
			}
			break;
	}

	return ret_val;
}

void MetricUnit::dump( std::ostream& out, const bool p_output[ METRIC_UNIT_MAX ], const MetricDumpFormat_e p_fmt ) const
{
	std::string sep;

	if( p_output[ m_type ] )
	{
		switch( p_fmt ) {
		case METRIC_DUMP_FORMAT_TREE:
			// TODO: Should be endl
			sep = "\r\n";
			break;
		case METRIC_DUMP_FORMAT_TSV:
			sep = "\t";
			break;
		case METRIC_DUMP_FORMAT_CSV:
			sep = ",";
			break;
		}

		if(( p_fmt == METRIC_DUMP_FORMAT_TSV ) ||
		   ( p_fmt == METRIC_DUMP_FORMAT_CSV )) {

			if( m_type == METRIC_UNIT_GLOBAL )
			{
				out << "Name" << sep
					<< m_subPrefix[ m_type ] << sep
					<< "Functions" << sep;
				unsigned loop;
				for( loop = 0;
					 loop < METRIC_TYPE_MAX;
					 loop++ )
				{
					out << m_metricNames[loop] << sep;
				}
				out << std::endl;
			}
		}

		if( p_fmt == METRIC_DUMP_FORMAT_TREE ) {
			out << m_namePrefix[ m_type ];
		}
		out << m_name << sep;
		if( p_fmt == METRIC_DUMP_FORMAT_TREE ) {
			if( m_subPrefix[ m_type ].length() ) {
				out << m_dumpPrefix[ m_type ] << m_subPrefix[ m_type ] << m_subUnits.size() << sep;
			}
		} else {
			out << m_subUnits.size() << sep;
		}
		if( p_fmt == METRIC_DUMP_FORMAT_TREE ) {
			out << m_dumpPrefix[ m_type ] << "Functions: ";
		}
		out << getSubUnitCount( METRIC_UNIT_FUNCTION ) << sep;

		unsigned loop;
		for( loop = 0;
			 loop < METRIC_TYPE_MAX;
			 loop++ )
		{
			if( p_fmt == METRIC_DUMP_FORMAT_TREE ) {
				out << m_dumpPrefix[ m_type ] << m_metricNames[loop] << ": ";
			}
			out << getCounter((MetricType_e) loop ) << sep;
		}

		if(( p_fmt == METRIC_DUMP_FORMAT_TSV ) ||
		   ( p_fmt == METRIC_DUMP_FORMAT_CSV )) {
			out << std::endl;
		}
	}

	for( SubUnitMap_t::const_iterator unitIt = m_subUnits.begin();
		 unitIt != m_subUnits.end();
		 ++unitIt )
	{
		(*unitIt).second->dump( out, p_output, p_fmt );
	}
}

MetricUnit* MetricUnit::getSubUnit( const std::string& p_name, const MetricUnitType_e p_type )
{
	MetricUnit* ret_val = NULL;
	SubUnitMap_t::iterator name_it = m_subUnits.find( p_name );
	if( name_it == m_subUnits.end() )
	{
		ret_val = new MetricUnit( p_name, p_type );

		if(( p_type == METRIC_UNIT_FUNCTION ) ||
		   ( p_type == METRIC_UNIT_METHOD ))
		{
			/* By default, every function/method has 1 return point */
			ret_val->increment( METRIC_TYPE_RETURNPOINTS );
		}
		m_subUnits[ p_name ] = ret_val;
	} else {
		ret_val = (*name_it).second;
	}
	return ret_val;
}
<commit_msg>Closes #25 by preventing function count when outputing function metrics and supressing McCabe at anything other than the function/method level<commit_after>/*
   Copyright 2014 John Bailey

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

#include "MetricUnit.hpp"

const std::string MetricUnit::m_namePrefix[ METRIC_UNIT_MAX ] = {
	"",
	"File: ",
	"\tFunction: ",
	"\tMethod: "
};
const std::string MetricUnit::m_dumpPrefix[ METRIC_UNIT_MAX ] = {
	"",
	"\t",
	"\t\t",
	"\t\t"
};
const std::string MetricUnit::m_subPrefix[ METRIC_UNIT_MAX ] = {
	"Files: ",
	"Functions/Methods: ",
	"",
	""
};

const std::string MetricUnit::m_metricNames[ METRIC_TYPE_MAX ] = {
	"IF statements",
	"ELSE statements",
	"FOR loops",
	"RETURN statements",
	"WHILE loops",
	"SWITCH statements",
	"CASE statements",
	"DEFAULT statements",
	"LOGICAL AND operators",
	"LOGICAL OR operators",
	"TERNARY operators",
	"GOTO statements",
	"LABEL statements",
	"Variables",
	"Return points",
	"Statements",
	"McCabe",
	"Modified McCabe"
};

MetricUnit::MetricUnit( const std::string& p_name, const MetricUnitType_e p_type ) : m_name( p_name ), m_type( p_type )
{
	uint16_t loop;

	for( loop = 0;
		 loop < METRIC_TYPE_MAX;
		 loop++ )
	{
		m_counters[ loop ] = 0;
	}
}

void MetricUnit::increment( const MetricType_e p_metricType )
{
	m_counters[ p_metricType ]++;
}

MetricUnitType_e MetricUnit::GetType( void ) const
{
	return m_type;
}

MetricUnit::counter_t MetricUnit::getSubUnitCount( const MetricUnitType_e p_type ) const
{
	counter_t ret_val = 0;

	for( SubUnitMap_t::const_iterator unitIt = m_subUnits.begin();
		 unitIt != m_subUnits.end();
		 ++unitIt )
	{
		if( (*unitIt).second->GetType() == p_type )
		{
			ret_val++;
		}
		ret_val += (*unitIt).second->getSubUnitCount( p_type );
	}

	return ret_val;
}

MetricUnit::counter_t MetricUnit::getCounter( const MetricType_e p_metricType ) const
{
	counter_t ret_val = 0;

	switch( p_metricType )
	{
		case METRIC_TYPE_MODIFIED_CYCLOMATIC:
			if(( m_type == METRIC_UNIT_FUNCTION ) ||
			   ( m_type == METRIC_UNIT_METHOD ))
			{
				ret_val = getCounter( METRIC_TYPE_FORLOOP ) +
						  getCounter( METRIC_TYPE_IF ) +
						  getCounter( METRIC_TYPE_WHILELOOP ) +
						  getCounter( METRIC_TYPE_SWITCH ) +
						  getCounter( METRIC_TYPE_LOGICAL_AND ) +
						  getCounter( METRIC_TYPE_LOGICAL_OR ) +
						  getCounter( METRIC_TYPE_TERNARY ) + 1;
			}
			break;
		case METRIC_TYPE_CYCLOMATIC:
			if(( m_type == METRIC_UNIT_FUNCTION ) ||
			   ( m_type == METRIC_UNIT_METHOD ))
			{
				// TODO: What about the 'DEFAULT'?
				ret_val = getCounter( METRIC_TYPE_FORLOOP ) +
						  getCounter( METRIC_TYPE_IF ) +
						  getCounter( METRIC_TYPE_WHILELOOP ) +
						  getCounter( METRIC_TYPE_CASE ) +
						  getCounter( METRIC_TYPE_LOGICAL_AND ) +
						  getCounter( METRIC_TYPE_LOGICAL_OR ) +
						  getCounter( METRIC_TYPE_TERNARY ) + 1;
			}
			break;
		default:
			ret_val  = m_counters[ p_metricType ];
			for( SubUnitMap_t::const_iterator unitIt = m_subUnits.begin();
				 unitIt != m_subUnits.end();
				 ++unitIt )
			{
				ret_val += (*unitIt).second->getCounter( p_metricType );
			}
			break;
	}

	return ret_val;
}

void MetricUnit::dump( std::ostream& out, const bool p_output[ METRIC_UNIT_MAX ], const MetricDumpFormat_e p_fmt ) const
{
	std::string sep;

	if( p_output[ m_type ] )
	{
		switch( p_fmt ) {
		case METRIC_DUMP_FORMAT_TREE:
			// TODO: Should be endl
			sep = "\r\n";
			break;
		case METRIC_DUMP_FORMAT_TSV:
			sep = "\t";
			break;
		case METRIC_DUMP_FORMAT_CSV:
			sep = ",";
			break;
		}

		if(( p_fmt == METRIC_DUMP_FORMAT_TSV ) ||
		   ( p_fmt == METRIC_DUMP_FORMAT_CSV )) {

			if( m_type == METRIC_UNIT_GLOBAL )
			{
				out << "Name" << sep
					<< m_subPrefix[ m_type ] << sep
					<< "Functions" << sep;
				unsigned loop;
				for( loop = 0;
					 loop < METRIC_TYPE_MAX;
					 loop++ )
				{
					out << m_metricNames[loop] << sep;
				}
				out << std::endl;
			}
		}

		if( p_fmt == METRIC_DUMP_FORMAT_TREE ) {
			out << m_namePrefix[ m_type ];
		}
		out << m_name << sep;
		if( p_fmt == METRIC_DUMP_FORMAT_TREE ) {
			if( m_subPrefix[ m_type ].length() ) {
				out << m_dumpPrefix[ m_type ] << m_subPrefix[ m_type ] << m_subUnits.size() << sep;
			}
		} else {
			out << m_subUnits.size() << sep;
		}
		/* Don't output function count at the function/method level */
		if(( m_type != METRIC_UNIT_FUNCTION ) &&
		   ( m_type != METRIC_UNIT_METHOD ))
		{
			if( p_fmt == METRIC_DUMP_FORMAT_TREE ) {
				out << m_dumpPrefix[ m_type ] << "Functions: ";
			}
			out << getSubUnitCount( METRIC_UNIT_FUNCTION ) << sep;
		} else if( p_fmt != METRIC_DUMP_FORMAT_TREE )
		{
			out << sep;
		}
		unsigned loop;
		for( loop = 0;
			 loop < METRIC_TYPE_MAX;
			 loop++ )
		{
			/* Filter out metrics which only apply at file/method level */
			if((( loop != METRIC_TYPE_CYCLOMATIC ) &&
			    ( loop != METRIC_TYPE_MODIFIED_CYCLOMATIC )) ||
			   (( m_type == METRIC_UNIT_FUNCTION ) ||
			    ( m_type == METRIC_UNIT_METHOD )))
			{
				if( p_fmt == METRIC_DUMP_FORMAT_TREE ) {
					out << m_dumpPrefix[ m_type ] << m_metricNames[loop] << ": ";
				}
				out << getCounter((MetricType_e) loop ) << sep;
			} else if( p_fmt != METRIC_DUMP_FORMAT_TREE )
			{
				out << sep;
			}
		}

		if(( p_fmt == METRIC_DUMP_FORMAT_TSV ) ||
		   ( p_fmt == METRIC_DUMP_FORMAT_CSV )) {
			out << std::endl;
		}
	}

	for( SubUnitMap_t::const_iterator unitIt = m_subUnits.begin();
		 unitIt != m_subUnits.end();
		 ++unitIt )
	{
		(*unitIt).second->dump( out, p_output, p_fmt );
	}
}

MetricUnit* MetricUnit::getSubUnit( const std::string& p_name, const MetricUnitType_e p_type )
{
	MetricUnit* ret_val = NULL;
	SubUnitMap_t::iterator name_it = m_subUnits.find( p_name );
	if( name_it == m_subUnits.end() )
	{
		ret_val = new MetricUnit( p_name, p_type );

		if(( p_type == METRIC_UNIT_FUNCTION ) ||
		   ( p_type == METRIC_UNIT_METHOD ))
		{
			/* By default, every function/method has 1 return point */
			ret_val->increment( METRIC_TYPE_RETURNPOINTS );
		}
		m_subUnits[ p_name ] = ret_val;
	} else {
		ret_val = (*name_it).second;
	}
	return ret_val;
}
<|endoftext|>


<commit_before>
#include "Globals.h"  // NOTE: MSVC stupidness requires this to be the same across all modules

#include "MobSpawner.h"
#include "Mobs/IncludeAllMonsters.h"

cMobSpawner::cMobSpawner(cMonster::eFamily a_MonsterFamily, const std::set<eMonsterType>& a_AllowedTypes) :
	m_MonsterFamily(a_MonsterFamily),
	m_NewPack(true),
	m_MobType(mtInvalidType)
{
	for (std::set<eMonsterType>::const_iterator itr = a_AllowedTypes.begin(); itr != a_AllowedTypes.end(); ++itr)
	{
		if (cMonster::FamilyFromType(*itr) == a_MonsterFamily)
		{
			m_AllowedTypes.insert(*itr);
		}
	}
}

bool cMobSpawner::CheckPackCenter(BLOCKTYPE a_BlockType)
{
	// Packs of non-water mobs can only be centered on an air block
	// Packs of water mobs can only be centered on a water block
	if (m_MonsterFamily == cMonster::mfWater)
	{
		return IsBlockWater(a_BlockType);
	}
	else
	{
		return a_BlockType == E_BLOCK_AIR;
	}
}

void cMobSpawner::addIfAllowed(eMonsterType toAdd, std::set<eMonsterType>& toAddIn)
{
	std::set<eMonsterType>::iterator itr = m_AllowedTypes.find(toAdd);
	if (itr != m_AllowedTypes.end())
	{
		toAddIn.insert(toAdd);
	}
}

eMonsterType cMobSpawner::ChooseMobType(EMCSBiome a_Biome)
{
	std::set<eMonsterType> allowedMobs;

	if (a_Biome == biMushroomIsland || a_Biome == biMushroomShore)
	{
		addIfAllowed(mtMooshroom, allowedMobs);
	}
	else if (a_Biome == biNether)
	{
		addIfAllowed(mtGhast, allowedMobs);
		addIfAllowed(mtZombiePigman, allowedMobs);
		addIfAllowed(mtMagmaCube, allowedMobs);
	}
	else if (a_Biome == biEnd)
	{
		addIfAllowed(mtEnderman, allowedMobs);
	}
	else
	{
		addIfAllowed(mtBat, allowedMobs);
		addIfAllowed(mtSpider, allowedMobs);
		addIfAllowed(mtZombie, allowedMobs);
		addIfAllowed(mtSkeleton, allowedMobs);
		addIfAllowed(mtCreeper, allowedMobs);
		addIfAllowed(mtSquid, allowedMobs);

		if (a_Biome != biDesert && a_Biome != biBeach && a_Biome != biOcean)
		{
			addIfAllowed(mtSheep, allowedMobs);
			addIfAllowed(mtPig, allowedMobs);
			addIfAllowed(mtCow, allowedMobs);
			addIfAllowed(mtChicken, allowedMobs);
			addIfAllowed(mtEnderman, allowedMobs);
			addIfAllowed(mtSlime, allowedMobs);  // MG TODO : much more complicated rule

			if (a_Biome == biForest || a_Biome == biForestHills || a_Biome == biTaiga  || a_Biome == biTaigaHills)
			{
				addIfAllowed(mtWolf, allowedMobs);
			}
			else if (a_Biome == biJungle || a_Biome == biJungleHills)
			{
				addIfAllowed(mtOcelot, allowedMobs);
			}
		}
	}

	size_t allowedMobsSize = allowedMobs.size();
	if (allowedMobsSize > 0)
	{
		std::set<eMonsterType>::iterator itr = allowedMobs.begin();
		int iRandom = m_Random.NextInt((int)allowedMobsSize, a_Biome);

		for (int i = 0; i < iRandom; i++)
		{
			++itr;
		}

		return *itr;
	}
	return mtInvalidType;
}

bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, eMonsterType a_MobType, EMCSBiome a_Biome)
{
	cFastRandom Random;
	BLOCKTYPE TargetBlock = E_BLOCK_AIR;
	if (a_Chunk->UnboundedRelGetBlockType(a_RelX, a_RelY, a_RelZ, TargetBlock))
	{
		if ((a_RelY + 1 > cChunkDef::Height) || (a_RelY - 1 < 0))
		{
			return false;
		}

		NIBBLETYPE BlockLight = a_Chunk->GetBlockLight(a_RelX, a_RelY, a_RelZ);
		NIBBLETYPE SkyLight = a_Chunk->GetSkyLight(a_RelX, a_RelY, a_RelZ);
		BLOCKTYPE BlockAbove = a_Chunk->GetBlock(a_RelX, a_RelY + 1, a_RelZ);
		BLOCKTYPE BlockBelow = a_Chunk->GetBlock(a_RelX, a_RelY - 1, a_RelZ);

		SkyLight = a_Chunk->GetTimeAlteredLight(SkyLight);

		switch (a_MobType)
		{
			case mtSquid:
			{
				return IsBlockWater(TargetBlock) && (a_RelY >= 45) && (a_RelY <= 62);
			}

			case mtBat:
			{
				return (a_RelY <= 63) && (BlockLight <= 4) && (SkyLight <= 4) && (TargetBlock == E_BLOCK_AIR) && !cBlockInfo::IsTransparent(BlockAbove);
			}

			case mtChicken:
			case mtCow:
			case mtPig:
			case mtHorse:
			case mtSheep:
			{
				return (
					(TargetBlock == E_BLOCK_AIR) &&
					(BlockAbove == E_BLOCK_AIR) &&
					(!cBlockInfo::IsTransparent(BlockBelow)) &&
					(BlockBelow == E_BLOCK_GRASS) &&
					(SkyLight >= 9)
				);
			}

			case mtOcelot:
			{
				return (
					(TargetBlock == E_BLOCK_AIR) &&
					(BlockAbove == E_BLOCK_AIR) &&
					(
						(BlockBelow == E_BLOCK_GRASS) || (BlockBelow == E_BLOCK_LEAVES) || (BlockBelow == E_BLOCK_NEW_LEAVES)
					) &&
					(a_RelY >= 62) &&
					(Random.NextInt(3, a_Biome) != 0)
				);
			}

			case mtEnderman:
			{
				if (a_RelY < 250)
				{
					BLOCKTYPE BlockTop = a_Chunk->GetBlock(a_RelX, a_RelY + 2, a_RelZ);
					if (BlockTop == E_BLOCK_AIR)
					{
						BlockTop = a_Chunk->GetBlock(a_RelX, a_RelY + 3, a_RelZ);
						return (
							(TargetBlock == E_BLOCK_AIR) &&
							(BlockAbove == E_BLOCK_AIR) &&
							(BlockTop == E_BLOCK_AIR) &&
							(!cBlockInfo::IsTransparent(BlockBelow)) &&
							(SkyLight <= 7) &&
							(BlockLight <= 7)
						);
					}
				}
				break;
			}

			case mtSpider:
			{
				bool CanSpawn = true;
				bool HasFloor = false;
				for (int x = 0; x < 2; ++x)
				{
					for (int z = 0; z < 2; ++z)
					{
						CanSpawn = a_Chunk->UnboundedRelGetBlockType(a_RelX + x, a_RelY, a_RelZ + z, TargetBlock);
						CanSpawn = CanSpawn && (TargetBlock == E_BLOCK_AIR);
						if (!CanSpawn)
						{
							return false;
						}
						HasFloor = (
							HasFloor ||
							(
								a_Chunk->UnboundedRelGetBlockType(a_RelX + x, a_RelY - 1, a_RelZ + z, TargetBlock) &&
								!cBlockInfo::IsTransparent(TargetBlock)
							)
						);
					}
				}
				return CanSpawn && HasFloor && (SkyLight <= 7) && (BlockLight <= 7);
			}

			case mtCreeper:
			case mtSkeleton:
			case mtZombie:
			{
				return (
					(TargetBlock == E_BLOCK_AIR) &&
					(BlockAbove == E_BLOCK_AIR) &&
					(!cBlockInfo::IsTransparent(BlockBelow)) &&
					(SkyLight <= 7) &&
					(BlockLight <= 7) &&
					(Random.NextInt(2, a_Biome) == 0)
				);
			}

			case mtMagmaCube:
			case mtSlime:
			{
				return (
					(TargetBlock == E_BLOCK_AIR) &&
					(BlockAbove == E_BLOCK_AIR) &&
					(!cBlockInfo::IsTransparent(BlockBelow)) &&
					(
						(a_RelY <= 40) || (a_Biome == biSwampland)
					)
				);
			}

			case mtGhast:
			case mtZombiePigman:
			{
				return (
					(TargetBlock == E_BLOCK_AIR) &&
					(BlockAbove == E_BLOCK_AIR) &&
					(!cBlockInfo::IsTransparent(BlockBelow)) &&
					(Random.NextInt(20, a_Biome) == 0)
				);
			}

			case mtWolf:
			{
				return (
					(TargetBlock == E_BLOCK_GRASS) &&
					(BlockAbove == E_BLOCK_AIR) &&
					(
						(a_Biome == biTaiga) ||
						(a_Biome == biTaigaHills) ||
						(a_Biome == biForest) ||
						(a_Biome == biForestHills) ||
						(a_Biome == biColdTaiga) ||
						(a_Biome == biColdTaigaHills) ||
						(a_Biome == biTaigaM) ||
						(a_Biome == biMegaTaiga) ||
						(a_Biome == biMegaTaigaHills)
					)
				);
			}

			default:
			{
				LOGD("MG TODO: Write spawning rule for mob type %d", a_MobType);
				return false;
			}
		}
	}
	return false;
}

cMonster* cMobSpawner::TryToSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, EMCSBiome a_Biome, int& a_MaxPackSize)
{
	cMonster* toReturn = nullptr;
	if (m_NewPack)
	{
		m_MobType = ChooseMobType(a_Biome);
		if (m_MobType == mtInvalidType)
		{
			return toReturn;
		}
		if (m_MobType == mtWolf)
		{
			a_MaxPackSize = 8;
		}
		else if (m_MobType == mtGhast)
		{
			a_MaxPackSize = 1;
		}
		m_NewPack = false;
	}

	// Make sure we are looking at the right chunk to spawn in
	a_Chunk = a_Chunk->GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ);

	if ((m_AllowedTypes.find(m_MobType) != m_AllowedTypes.end()) && CanSpawnHere(a_Chunk, a_RelX, a_RelY, a_RelZ, m_MobType, a_Biome))
	{
		cMonster * newMob = cMonster::NewMonsterFromType(m_MobType);
		if (newMob)
		{
			m_Spawned.insert(newMob);
		}
		toReturn = newMob;
	}
	return toReturn;
}

void cMobSpawner::NewPack()
{
	m_NewPack = true;
}

cMobSpawner::tSpawnedContainer & cMobSpawner::getSpawned(void)
{
	return m_Spawned;
}

bool cMobSpawner::CanSpawnAnything(void)
{
	return !m_AllowedTypes.empty();
}

<commit_msg>added spawning rule to mooshroom<commit_after>
#include "Globals.h"  // NOTE: MSVC stupidness requires this to be the same across all modules

#include "MobSpawner.h"
#include "Mobs/IncludeAllMonsters.h"

cMobSpawner::cMobSpawner(cMonster::eFamily a_MonsterFamily, const std::set<eMonsterType>& a_AllowedTypes) :
	m_MonsterFamily(a_MonsterFamily),
	m_NewPack(true),
	m_MobType(mtInvalidType)
{
	for (std::set<eMonsterType>::const_iterator itr = a_AllowedTypes.begin(); itr != a_AllowedTypes.end(); ++itr)
	{
		if (cMonster::FamilyFromType(*itr) == a_MonsterFamily)
		{
			m_AllowedTypes.insert(*itr);
		}
	}
}

bool cMobSpawner::CheckPackCenter(BLOCKTYPE a_BlockType)
{
	// Packs of non-water mobs can only be centered on an air block
	// Packs of water mobs can only be centered on a water block
	if (m_MonsterFamily == cMonster::mfWater)
	{
		return IsBlockWater(a_BlockType);
	}
	else
	{
		return a_BlockType == E_BLOCK_AIR;
	}
}

void cMobSpawner::addIfAllowed(eMonsterType toAdd, std::set<eMonsterType>& toAddIn)
{
	std::set<eMonsterType>::iterator itr = m_AllowedTypes.find(toAdd);
	if (itr != m_AllowedTypes.end())
	{
		toAddIn.insert(toAdd);
	}
}

eMonsterType cMobSpawner::ChooseMobType(EMCSBiome a_Biome)
{
	std::set<eMonsterType> allowedMobs;

	if (a_Biome == biMushroomIsland || a_Biome == biMushroomShore)
	{
		addIfAllowed(mtMooshroom, allowedMobs);
	}
	else if (a_Biome == biNether)
	{
		addIfAllowed(mtGhast, allowedMobs);
		addIfAllowed(mtZombiePigman, allowedMobs);
		addIfAllowed(mtMagmaCube, allowedMobs);
	}
	else if (a_Biome == biEnd)
	{
		addIfAllowed(mtEnderman, allowedMobs);
	}
	else
	{
		addIfAllowed(mtBat, allowedMobs);
		addIfAllowed(mtSpider, allowedMobs);
		addIfAllowed(mtZombie, allowedMobs);
		addIfAllowed(mtSkeleton, allowedMobs);
		addIfAllowed(mtCreeper, allowedMobs);
		addIfAllowed(mtSquid, allowedMobs);

		if (a_Biome != biDesert && a_Biome != biBeach && a_Biome != biOcean)
		{
			addIfAllowed(mtSheep, allowedMobs);
			addIfAllowed(mtPig, allowedMobs);
			addIfAllowed(mtCow, allowedMobs);
			addIfAllowed(mtChicken, allowedMobs);
			addIfAllowed(mtEnderman, allowedMobs);
			addIfAllowed(mtSlime, allowedMobs);  // MG TODO : much more complicated rule

			if (a_Biome == biForest || a_Biome == biForestHills || a_Biome == biTaiga  || a_Biome == biTaigaHills)
			{
				addIfAllowed(mtWolf, allowedMobs);
			}
			else if (a_Biome == biJungle || a_Biome == biJungleHills)
			{
				addIfAllowed(mtOcelot, allowedMobs);
			}
		}
	}

	size_t allowedMobsSize = allowedMobs.size();
	if (allowedMobsSize > 0)
	{
		std::set<eMonsterType>::iterator itr = allowedMobs.begin();
		int iRandom = m_Random.NextInt((int)allowedMobsSize, a_Biome);

		for (int i = 0; i < iRandom; i++)
		{
			++itr;
		}

		return *itr;
	}
	return mtInvalidType;
}

bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, eMonsterType a_MobType, EMCSBiome a_Biome)
{
	cFastRandom Random;
	BLOCKTYPE TargetBlock = E_BLOCK_AIR;
	if (a_Chunk->UnboundedRelGetBlockType(a_RelX, a_RelY, a_RelZ, TargetBlock))
	{
		if ((a_RelY + 1 > cChunkDef::Height) || (a_RelY - 1 < 0))
		{
			return false;
		}

		NIBBLETYPE BlockLight = a_Chunk->GetBlockLight(a_RelX, a_RelY, a_RelZ);
		NIBBLETYPE SkyLight = a_Chunk->GetSkyLight(a_RelX, a_RelY, a_RelZ);
		BLOCKTYPE BlockAbove = a_Chunk->GetBlock(a_RelX, a_RelY + 1, a_RelZ);
		BLOCKTYPE BlockBelow = a_Chunk->GetBlock(a_RelX, a_RelY - 1, a_RelZ);

		SkyLight = a_Chunk->GetTimeAlteredLight(SkyLight);

		switch (a_MobType)
		{
			case mtSquid:
			{
				return IsBlockWater(TargetBlock) && (a_RelY >= 45) && (a_RelY <= 62);
			}

			case mtBat:
			{
				return (a_RelY <= 63) && (BlockLight <= 4) && (SkyLight <= 4) && (TargetBlock == E_BLOCK_AIR) && !cBlockInfo::IsTransparent(BlockAbove);
			}

			case mtChicken:
			case mtCow:
			case mtPig:
			case mtHorse:
			case mtSheep:
			{
				return (
					(TargetBlock == E_BLOCK_AIR) &&
					(BlockAbove == E_BLOCK_AIR) &&
					(!cBlockInfo::IsTransparent(BlockBelow)) &&
					(BlockBelow == E_BLOCK_GRASS) &&
					(SkyLight >= 9)
				);
			}

			case mtOcelot:
			{
				return (
					(TargetBlock == E_BLOCK_AIR) &&
					(BlockAbove == E_BLOCK_AIR) &&
					(
						(BlockBelow == E_BLOCK_GRASS) || (BlockBelow == E_BLOCK_LEAVES) || (BlockBelow == E_BLOCK_NEW_LEAVES)
					) &&
					(a_RelY >= 62) &&
					(Random.NextInt(3, a_Biome) != 0)
				);
			}

			case mtEnderman:
			{
				if (a_RelY < 250)
				{
					BLOCKTYPE BlockTop = a_Chunk->GetBlock(a_RelX, a_RelY + 2, a_RelZ);
					if (BlockTop == E_BLOCK_AIR)
					{
						BlockTop = a_Chunk->GetBlock(a_RelX, a_RelY + 3, a_RelZ);
						return (
							(TargetBlock == E_BLOCK_AIR) &&
							(BlockAbove == E_BLOCK_AIR) &&
							(BlockTop == E_BLOCK_AIR) &&
							(!cBlockInfo::IsTransparent(BlockBelow)) &&
							(SkyLight <= 7) &&
							(BlockLight <= 7)
						);
					}
				}
				break;
			}

			case mtSpider:
			{
				bool CanSpawn = true;
				bool HasFloor = false;
				for (int x = 0; x < 2; ++x)
				{
					for (int z = 0; z < 2; ++z)
					{
						CanSpawn = a_Chunk->UnboundedRelGetBlockType(a_RelX + x, a_RelY, a_RelZ + z, TargetBlock);
						CanSpawn = CanSpawn && (TargetBlock == E_BLOCK_AIR);
						if (!CanSpawn)
						{
							return false;
						}
						HasFloor = (
							HasFloor ||
							(
								a_Chunk->UnboundedRelGetBlockType(a_RelX + x, a_RelY - 1, a_RelZ + z, TargetBlock) &&
								!cBlockInfo::IsTransparent(TargetBlock)
							)
						);
					}
				}
				return CanSpawn && HasFloor && (SkyLight <= 7) && (BlockLight <= 7);
			}

			case mtCreeper:
			case mtSkeleton:
			case mtZombie:
			{
				return (
					(TargetBlock == E_BLOCK_AIR) &&
					(BlockAbove == E_BLOCK_AIR) &&
					(!cBlockInfo::IsTransparent(BlockBelow)) &&
					(SkyLight <= 7) &&
					(BlockLight <= 7) &&
					(Random.NextInt(2, a_Biome) == 0)
				);
			}

			case mtMagmaCube:
			case mtSlime:
			{
				return (
					(TargetBlock == E_BLOCK_AIR) &&
					(BlockAbove == E_BLOCK_AIR) &&
					(!cBlockInfo::IsTransparent(BlockBelow)) &&
					(
						(a_RelY <= 40) || (a_Biome == biSwampland)
					)
				);
			}

			case mtGhast:
			case mtZombiePigman:
			{
				return (
					(TargetBlock == E_BLOCK_AIR) &&
					(BlockAbove == E_BLOCK_AIR) &&
					(!cBlockInfo::IsTransparent(BlockBelow)) &&
					(Random.NextInt(20, a_Biome) == 0)
				);
			}

			case mtWolf:
			{
				return (
					(TargetBlock == E_BLOCK_GRASS) &&
					(BlockAbove == E_BLOCK_AIR) &&
					(
						(a_Biome == biTaiga) ||
						(a_Biome == biTaigaHills) ||
						(a_Biome == biForest) ||
						(a_Biome == biForestHills) ||
						(a_Biome == biColdTaiga) ||
						(a_Biome == biColdTaigaHills) ||
						(a_Biome == biTaigaM) ||
						(a_Biome == biMegaTaiga) ||
						(a_Biome == biMegaTaigaHills)
					)
				);
			}

			case mtMooshroom:
			{
				return (
					(TargetBlock == E_BLOCK_AIR) &&
					(BlockAbove == E_BLOCK_AIR) &&
					(!cBlockInfo::IsTransparent(BlockBelow)) &&
					(
						a_Biome == biMushroomShore ||
						a_Biome == biMushroomIsland
					)
				);
			}

			default:
			{
				LOGD("MG TODO: Write spawning rule for mob type %d", a_MobType);
				return false;
			}
		}
	}
	return false;
}

cMonster* cMobSpawner::TryToSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, EMCSBiome a_Biome, int& a_MaxPackSize)
{
	cMonster* toReturn = nullptr;
	if (m_NewPack)
	{
		m_MobType = ChooseMobType(a_Biome);
		if (m_MobType == mtInvalidType)
		{
			return toReturn;
		}
		if (m_MobType == mtWolf)
		{
			a_MaxPackSize = 8;
		}
		else if (m_MobType == mtGhast)
		{
			a_MaxPackSize = 1;
		}
		m_NewPack = false;
	}

	// Make sure we are looking at the right chunk to spawn in
	a_Chunk = a_Chunk->GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ);

	if ((m_AllowedTypes.find(m_MobType) != m_AllowedTypes.end()) && CanSpawnHere(a_Chunk, a_RelX, a_RelY, a_RelZ, m_MobType, a_Biome))
	{
		cMonster * newMob = cMonster::NewMonsterFromType(m_MobType);
		if (newMob)
		{
			m_Spawned.insert(newMob);
		}
		toReturn = newMob;
	}
	return toReturn;
}

void cMobSpawner::NewPack()
{
	m_NewPack = true;
}

cMobSpawner::tSpawnedContainer & cMobSpawner::getSpawned(void)
{
	return m_Spawned;
}

bool cMobSpawner::CanSpawnAnything(void)
{
	return !m_AllowedTypes.empty();
}

<|endoftext|>


<commit_before>//===----------------------------------------------------------------------===//
//
//                         PelotonDB
//
// peloton_service.cpp
//
// Identification: src/backend/message/peloton_service.cpp
//
// Copyright (c) 2015, Carnegie Mellon University Database Group
//
//===----------------------------------------------------------------------===//

#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <iostream>

#include "backend/networking/peloton_service.h"
#include "backend/networking/peloton_endpoint.h"
#include "backend/networking/rpc_server.h"

namespace peloton {
namespace networking {

void PelotonService::TransactionInit(::google::protobuf::RpcController* controller,
        const TransactionInitRequest* request,
        TransactionInitResponse* response,
        ::google::protobuf::Closure* done) {

    //TODO: use controller here
    std::cout << controller << request << response << done << std::endl;
}

void PelotonService::TransactionWork(::google::protobuf::RpcController* controller,
        const TransactionWorkRequest* request,
        TransactionWorkResponse* response,
        ::google::protobuf::Closure* done) {

    //TODO: use controller here
    std::cout << controller << request << response << done << std::endl;
}

void PelotonService::TransactionPrefetch(::google::protobuf::RpcController* controller,
        const TransactionPrefetchResult* request,
        TransactionPrefetchAcknowledgement* response,
        ::google::protobuf::Closure* done) {

    //TODO: use controller here
    std::cout << controller << request << response << done << std::endl;
}

void PelotonService::TransactionMap(::google::protobuf::RpcController* controller,
        const TransactionMapRequest* request,
        TransactionMapResponse* response,
        ::google::protobuf::Closure* done) {

    //TODO: use controller here
    std::cout << controller << request << response << done << std::endl;
}

void PelotonService::TransactionReduce(::google::protobuf::RpcController* controller,
        const TransactionReduceRequest* request,
        TransactionReduceResponse* response,
        ::google::protobuf::Closure* done) {

    //TODO: use controller here
    std::cout << controller << request << response << done << std::endl;
}

void PelotonService::TransactionPrepare(::google::protobuf::RpcController* controller,
        const TransactionPrepareRequest* request,
        TransactionPrepareResponse* response,
        ::google::protobuf::Closure* done) {

    //TODO: use controller here
    std::cout << controller << request << response << done << std::endl;
}

void PelotonService::TransactionFinish(::google::protobuf::RpcController* controller,
        const TransactionFinishRequest* request,
        TransactionFinishResponse* response,
        ::google::protobuf::Closure* done) {

    //TODO: use controller here
    std::cout << controller << request << response << done << std::endl;
}

void PelotonService::TransactionRedirect(::google::protobuf::RpcController* controller,
        const TransactionRedirectRequest* request,
        TransactionRedirectResponse* response,
        ::google::protobuf::Closure* done) {

    //TODO: use controller here
    std::cout << controller << request << response << done << std::endl;
}

void PelotonService::TransactionDebug(::google::protobuf::RpcController* controller,
        const TransactionDebugRequest* request,
        TransactionDebugResponse* response,
        ::google::protobuf::Closure* done) {

    //TODO: use controller here
    std::cout << controller << request << response << done << std::endl;
}

void PelotonService::SendData(::google::protobuf::RpcController* controller,
        const SendDataRequest* request,
        SendDataResponse* response,
        ::google::protobuf::Closure* done) {

    //TODO: use controller here
    std::cout << controller << request << response << done << std::endl;
}

void PelotonService::Initialize(::google::protobuf::RpcController* controller,
        const InitializeRequest* request,
        InitializeResponse* response,
        ::google::protobuf::Closure* done) {

    //TODO: use controller here
    std::cout << controller << request << response << done << std::endl;
}

void PelotonService::ShutdownPrepare(::google::protobuf::RpcController* controller,
        const ShutdownPrepareRequest* request,
        ShutdownPrepareResponse* response,
        ::google::protobuf::Closure* done) {

    //TODO: use controller here
    std::cout << controller << request << response << done << std::endl;
}

void PelotonService::Shutdown(::google::protobuf::RpcController* controller,
        const ShutdownRequest* request,
        ShutdownResponse* response,
        ::google::protobuf::Closure* done) {

    //TODO: use controller here
    std::cout << controller << request << response << done << std::endl;
}

void PelotonService::Heartbeat(::google::protobuf::RpcController* controller,
        const HeartbeatRequest* request,
        HeartbeatResponse* response,
        ::google::protobuf::Closure* done) {

    std::cerr << "Received from client: " <<
        "sender site: " << request->sender_site() <<
        "last_txn_id: " << request->last_transaction_id() <<
         std::endl;

    response->set_sender_site(9876);
    Status status = ABORT_SPECULATIVE;
    response->set_status(status);

    if (done) {
        done->Run();
    }

    //TODO: use controller here
    std::cout << "controller: " << controller << std::endl;
}

void PelotonService::UnevictData(::google::protobuf::RpcController* controller,
        const UnevictDataRequest* request,
        UnevictDataResponse* response,
        ::google::protobuf::Closure* done) {

    //TODO: use controller here
    std::cout << controller << request << response << done << std::endl;
}

void PelotonService::TimeSync(::google::protobuf::RpcController* controller,
        const TimeSyncRequest* request,
        TimeSyncResponse* response,
        ::google::protobuf::Closure* done) {

    //TODO: use controller here
    std::cout << controller << request << response << done << std::endl;
}
/*
void StartPelotonService() {

	::google::protobuf::Service* service = NULL;

	try {
		RpcServer rpc_server(PELOTON_ENDPOINT_ADDR);
		service = new PelotonService();
		rpc_server.RegisterService(service);
		rpc_server.Start();
	} catch (peloton::message::exception& e) {
		std::cerr << "NN EXCEPTION : " << e.what() << std::endl;
		delete service;
	} catch (std::exception& e) {
		std::cerr << "STD EXCEPTION : " << e.what() << std::endl;
		delete service;
	} catch (...) {
		std::cerr << "UNTRAPPED EXCEPTION " << std::endl;
		delete service;
	}
}
*/
}  // namespace networking
}  // namespace peloton
<commit_msg>refactor peloton_service.cpp<commit_after>//===----------------------------------------------------------------------===//
//
//                         PelotonDB
//
// peloton_service.cpp
//
// Identification: src/backend/networking/peloton_service.cpp
//
// Copyright (c) 2015, Carnegie Mellon University Database Group
//
//===----------------------------------------------------------------------===//

#include "backend/networking/peloton_service.h"
#include "backend/networking/peloton_endpoint.h"
#include "backend/networking/rpc_server.h"
#include "backend/common/logger.h"

#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <iostream>

namespace peloton {
namespace networking {

void PelotonService::TransactionInit(::google::protobuf::RpcController* controller,
        const TransactionInitRequest* request,
        TransactionInitResponse* response,
        ::google::protobuf::Closure* done) {

    // TODO: controller should be set, we probably use it in the future
    if (controller->Failed()) {
        std::string error = controller->ErrorText();
        LOG_TRACE( "PelotonService with controller failed:%s ", error.c_str() );
    }

    // TODO: process request and set response
    std::cout << request << response;

    // if callback exist, run it
    if (done) {
        done->Run();
    }
}

void PelotonService::TransactionWork(::google::protobuf::RpcController* controller,
        const TransactionWorkRequest* request,
        TransactionWorkResponse* response,
        ::google::protobuf::Closure* done) {
    // TODO: controller should be set, we probably use it in the future
    if (controller->Failed()) {
        std::string error = controller->ErrorText();
        LOG_TRACE( "PelotonService with controller failed:%s ", error.c_str() );
    }

    // TODO: process request and set response
    std::cout << request << response;

    // if callback exist, run it
    if (done) {
        done->Run();
    }
}

void PelotonService::TransactionPrefetch(::google::protobuf::RpcController* controller,
        const TransactionPrefetchResult* request,
        TransactionPrefetchAcknowledgement* response,
        ::google::protobuf::Closure* done) {

    // TODO: controller should be set, we probably use it in the future
    if (controller->Failed()) {
        std::string error = controller->ErrorText();
        LOG_TRACE( "PelotonService with controller failed:%s ", error.c_str() );
    }

    // TODO: process request and set response
    std::cout << request << response;

    // if callback exist, run it
    if (done) {
        done->Run();
    }
}

void PelotonService::TransactionMap(::google::protobuf::RpcController* controller,
        const TransactionMapRequest* request,
        TransactionMapResponse* response,
        ::google::protobuf::Closure* done) {

    // TODO: controller should be set, we probably use it in the future
    if (controller->Failed()) {
        std::string error = controller->ErrorText();
        LOG_TRACE( "PelotonService with controller failed:%s ", error.c_str() );
    }

    // TODO: process request and set response
    std::cout << request << response;

    // if callback exist, run it
    if (done) {
        done->Run();
    }
}

void PelotonService::TransactionReduce(::google::protobuf::RpcController* controller,
        const TransactionReduceRequest* request,
        TransactionReduceResponse* response,
        ::google::protobuf::Closure* done) {

    // TODO: controller should be set, we probably use it in the future
    if (controller->Failed()) {
        std::string error = controller->ErrorText();
        LOG_TRACE( "PelotonService with controller failed:%s ", error.c_str() );
    }

    // TODO: process request and set response
    std::cout << request << response;

    // if callback exist, run it
    if (done) {
        done->Run();
    }
}

void PelotonService::TransactionPrepare(::google::protobuf::RpcController* controller,
        const TransactionPrepareRequest* request,
        TransactionPrepareResponse* response,
        ::google::protobuf::Closure* done) {

    // TODO: controller should be set, we probably use it in the future
    if (controller->Failed()) {
        std::string error = controller->ErrorText();
        LOG_TRACE( "PelotonService with controller failed:%s ", error.c_str() );
    }

    // TODO: process request and set response
    std::cout << request << response;

    // if callback exist, run it
    if (done) {
        done->Run();
    }
}

void PelotonService::TransactionFinish(::google::protobuf::RpcController* controller,
        const TransactionFinishRequest* request,
        TransactionFinishResponse* response,
        ::google::protobuf::Closure* done) {

    // TODO: controller should be set, we probably use it in the future
    if (controller->Failed()) {
        std::string error = controller->ErrorText();
        LOG_TRACE( "PelotonService with controller failed:%s ", error.c_str() );
    }

    // TODO: process request and set response
    std::cout << request << response;

    // if callback exist, run it
    if (done) {
        done->Run();
    }
}

void PelotonService::TransactionRedirect(::google::protobuf::RpcController* controller,
        const TransactionRedirectRequest* request,
        TransactionRedirectResponse* response,
        ::google::protobuf::Closure* done) {

    // TODO: controller should be set, we probably use it in the future
    if (controller->Failed()) {
        std::string error = controller->ErrorText();
        LOG_TRACE( "PelotonService with controller failed:%s ", error.c_str() );
    }

    // TODO: process request and set response
    std::cout << request << response;

    // if callback exist, run it
    if (done) {
        done->Run();
    }
}

void PelotonService::TransactionDebug(::google::protobuf::RpcController* controller,
        const TransactionDebugRequest* request,
        TransactionDebugResponse* response,
        ::google::protobuf::Closure* done) {

    // TODO: controller should be set, we probably use it in the future
    if (controller->Failed()) {
        std::string error = controller->ErrorText();
        LOG_TRACE( "PelotonService with controller failed:%s ", error.c_str() );
    }

    // TODO: process request and set response
    std::cout << request << response;

    // if callback exist, run it
    if (done) {
        done->Run();
    }
}

void PelotonService::SendData(::google::protobuf::RpcController* controller,
        const SendDataRequest* request,
        SendDataResponse* response,
        ::google::protobuf::Closure* done) {

    // TODO: controller should be set, we probably use it in the future
    if (controller->Failed()) {
        std::string error = controller->ErrorText();
        LOG_TRACE( "PelotonService with controller failed:%s ", error.c_str() );
    }

    // TODO: process request and set response
    std::cout << request << response;

    // if callback exist, run it
    if (done) {
        done->Run();
    }
}

void PelotonService::Initialize(::google::protobuf::RpcController* controller,
        const InitializeRequest* request,
        InitializeResponse* response,
        ::google::protobuf::Closure* done) {

    // TODO: controller should be set, we probably use it in the future
    if (controller->Failed()) {
        std::string error = controller->ErrorText();
        LOG_TRACE( "PelotonService with controller failed:%s ", error.c_str() );
    }

    // TODO: process request and set response
    std::cout << request << response;

    // if callback exist, run it
    if (done) {
        done->Run();
    }
}

void PelotonService::ShutdownPrepare(::google::protobuf::RpcController* controller,
        const ShutdownPrepareRequest* request,
        ShutdownPrepareResponse* response,
        ::google::protobuf::Closure* done) {

    // TODO: controller should be set, we probably use it in the future
    if (controller->Failed()) {
        std::string error = controller->ErrorText();
        LOG_TRACE( "PelotonService with controller failed:%s ", error.c_str() );
    }

    // TODO: process request and set response
    std::cout << request << response;

    // if callback exist, run it
    if (done) {
        done->Run();
    }
}

void PelotonService::Shutdown(::google::protobuf::RpcController* controller,
        const ShutdownRequest* request,
        ShutdownResponse* response,
        ::google::protobuf::Closure* done) {

    // TODO: controller should be set, we probably use it in the future
    if (controller->Failed()) {
        std::string error = controller->ErrorText();
        LOG_TRACE( "PelotonService with controller failed:%s ", error.c_str() );
    }

    // TODO: process request and set response
    std::cout << request << response;

    // if callback exist, run it
    if (done) {
        done->Run();
    }
}

void PelotonService::Heartbeat(::google::protobuf::RpcController* controller,
        const HeartbeatRequest* request,
        HeartbeatResponse* response,
        ::google::protobuf::Closure* done) {

    // TODO: controller should be set, we probably use it in the future
    if (controller->Failed()) {
        std::string error = controller->ErrorText();
        LOG_TRACE( "PelotonService with controller failed:%s ", error.c_str() );
    }

    /*
     * If request is not null, this is a rpc  call, server should handle the reqeust
     */
    if (request != NULL) {

        LOG_TRACE("Received from client, sender site: %d, last_txn_id: %lld",
                request->sender_site(),
                request->last_transaction_id());

        response->set_sender_site(9876);
        Status status = ABORT_SPECULATIVE;
        response->set_status(status);

        // if callback exist, run it
        if (done) {
            done->Run();
        }
    }
    /*
     * Here is for the client callback for Heartbeat
     */
    else {
        // proecess the response
        LOG_TRACE("proecess the Heartbeat response");

        if (response->has_sender_site() == true) {
            std::cout << "sender site: " << response->sender_site() << std::endl;
        } else {
            std::cout << "No response: site is null" << std::endl;
        }

        if (response->has_status() == true) {
            std::cout << "Status: " << response->status() << std::endl;
        } else {
            std::cout << "No response: status is null" << std::endl;
        }
    }

}

void PelotonService::UnevictData(::google::protobuf::RpcController* controller,
        const UnevictDataRequest* request,
        UnevictDataResponse* response,
        ::google::protobuf::Closure* done) {

    // TODO: controller should be set, we probably use it in the future
    if (controller->Failed()) {
        std::string error = controller->ErrorText();
        LOG_TRACE( "PelotonService with controller failed:%s ", error.c_str() );
    }

    // TODO: process request and set response
    std::cout << request << response;

    // if callback exist, run it
    if (done) {
        done->Run();
    }
}

void PelotonService::TimeSync(::google::protobuf::RpcController* controller,
        const TimeSyncRequest* request,
        TimeSyncResponse* response,
        ::google::protobuf::Closure* done) {

    // TODO: controller should be set, we probably use it in the future
    if (controller->Failed()) {
        std::string error = controller->ErrorText();
        LOG_TRACE( "PelotonService with controller failed:%s ", error.c_str() );
    }

    // TODO: process request and set response
    std::cout << request << response;

    // if callback exist, run it
    if (done) {
        done->Run();
    }
}
/*
void StartPelotonService() {
    ::google::protobuf::Service* service = NULL;
    try {
        RpcServer rpc_server(PELOTON_ENDPOINT_ADDR);
        service = new PelotonService();
        rpc_server.RegisterService(service);
        rpc_server.Start();
    } catch (peloton::message::exception& e) {
        std::cerr << "NN EXCEPTION : " << e.what() << std::endl;
        delete service;
    } catch (std::exception& e) {
        std::cerr << "STD EXCEPTION : " << e.what() << std::endl;
        delete service;
    } catch (...) {
        std::cerr << "UNTRAPPED EXCEPTION " << std::endl;
        delete service;
    }
}
*/
}  // namespace networking
}  // namespace peloton
<|endoftext|>


<commit_before>#include "fft.hpp"

using number = std::complex<double>;

inline uint32_t reverse_bits(uint32_t n, int bits)
{
	uint32_t rn = 0;
	for(int i = 0; i < bits; ++i)
		if((n >> i) & 1)
			rn |= 1 << (bits - 1 - i);
	return rn;
}

inline void swap_reverse(std::vector<number>& data, int bits)
{
	for(uint32_t i = 0; i < data.size(); ++i)
	{
		auto j = reverse_bits(i, bits);
		if(i < j) std::swap(data[i], data[j]);
	}
}

void fft(std::vector<number>& data)
{
	int bits = __builtin_ctz(data.size());
	swap_reverse(data, bits);

	for(int k = 0; k < bits; ++k)
	{
		double angle = M_PI / (1 << k);
		number primitive_root = {cos(angle), sin(angle)};

		for(unsigned i = 0; i < data.size(); i += (2u << k))
		{
			number w = {1, 0};
			for(unsigned j = i; j < i + (1u << k); ++j)
			{
				data[j + (1u << k)] *= w;
				auto diff = data[j] - data[j + (1u << k)];
				data[j] += data[j + (1u << k)];
				data[j + (1u << k)] = diff;

				w *= primitive_root;
			}
		}
	}
}
<commit_msg>Rename alias<commit_after>#include "fft.hpp"

using ComplexNumber = std::complex<double>;

inline uint32_t reverse_bits(uint32_t n, int bits)
{
	uint32_t rn = 0;
	for(int i = 0; i < bits; ++i)
		if((n >> i) & 1)
			rn |= 1 << (bits - 1 - i);
	return rn;
}

inline void swap_reverse(std::vector<ComplexNumber>& data, int bits)
{
	for(uint32_t i = 0; i < data.size(); ++i)
	{
		auto j = reverse_bits(i, bits);
		if(i < j) std::swap(data[i], data[j]);
	}
}

void fft(std::vector<ComplexNumber>& data)
{
	int bits = __builtin_ctz(data.size());
	swap_reverse(data, bits);

	for(int k = 0; k < bits; ++k)
	{
		double angle = M_PI / (1 << k);
		ComplexNumber primitive_root = {cos(angle), sin(angle)};

		for(unsigned i = 0; i < data.size(); i += (2u << k))
		{
			ComplexNumber w = {1, 0};
			for(unsigned j = i; j < i + (1u << k); ++j)
			{
				data[j + (1u << k)] *= w;
				auto diff = data[j] - data[j + (1u << k)];
				data[j] += data[j + (1u << k)];
				data[j + (1u << k)] = diff;

				w *= primitive_root;
			}
		}
	}
}
<|endoftext|>


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

#include "base/random.hh"
#include "mem/simple_mem.hh"

using namespace std;

SimpleMemory::SimpleMemory(const SimpleMemoryParams* p) :
    AbstractMemory(p),
    port(name() + ".port", *this), latency(p->latency),
    latency_var(p->latency_var), bandwidth(p->bandwidth), isBusy(false),
    retryReq(false), retryResp(false),
    releaseEvent(this), dequeueEvent(this), drainManager(NULL)
{
}

void
SimpleMemory::init()
{
    // allow unconnected memories as this is used in several ruby
    // systems at the moment
    if (port.isConnected()) {
        port.sendRangeChange();
    }
}

Tick
SimpleMemory::recvAtomic(PacketPtr pkt)
{
    access(pkt);
    return pkt->memInhibitAsserted() ? 0 : getLatency();
}

void
SimpleMemory::recvFunctional(PacketPtr pkt)
{
    pkt->pushLabel(name());

    functionalAccess(pkt);

    // potentially update the packets in our packet queue as well
    for (auto i = packetQueue.begin(); i != packetQueue.end(); ++i)
        pkt->checkFunctional(i->pkt);

    pkt->popLabel();
}

bool
SimpleMemory::recvTimingReq(PacketPtr pkt)
{
    /// @todo temporary hack to deal with memory corruption issues until
    /// 4-phase transactions are complete
    for (int x = 0; x < pendingDelete.size(); x++)
        delete pendingDelete[x];
    pendingDelete.clear();

    if (pkt->memInhibitAsserted()) {
        // snooper will supply based on copy of packet
        // still target's responsibility to delete packet
        pendingDelete.push_back(pkt);
        return true;
    }

    // we should never get a new request after committing to retry the
    // current one, the bus violates the rule as it simply sends a
    // retry to the next one waiting on the retry list, so simply
    // ignore it
    if (retryReq)
        return false;

    // if we are busy with a read or write, remember that we have to
    // retry
    if (isBusy) {
        retryReq = true;
        return false;
    }

    // @todo someone should pay for this
    pkt->busFirstWordDelay = pkt->busLastWordDelay = 0;

    // update the release time according to the bandwidth limit, and
    // do so with respect to the time it takes to finish this request
    // rather than long term as it is the short term data rate that is
    // limited for any real memory

    // only look at reads and writes when determining if we are busy,
    // and for how long, as it is not clear what to regulate for the
    // other types of commands
    if (pkt->isRead() || pkt->isWrite()) {
        // calculate an appropriate tick to release to not exceed
        // the bandwidth limit
        Tick duration = pkt->getSize() * bandwidth;

        // only consider ourselves busy if there is any need to wait
        // to avoid extra events being scheduled for (infinitely) fast
        // memories
        if (duration != 0) {
            schedule(releaseEvent, curTick() + duration);
            isBusy = true;
        }
    }

    // go ahead and deal with the packet and put the response in the
    // queue if there is one
    bool needsResponse = pkt->needsResponse();
    recvAtomic(pkt);
    // turn packet around to go back to requester if response expected
    if (needsResponse) {
        // recvAtomic() should already have turned packet into
        // atomic response
        assert(pkt->isResponse());
        // to keep things simple (and in order), we put the packet at
        // the end even if the latency suggests it should be sent
        // before the packet(s) before it
        packetQueue.push_back(DeferredPacket(pkt, curTick() + getLatency()));
        if (!dequeueEvent.scheduled())
            schedule(dequeueEvent, packetQueue.back().tick);
    } else {
        pendingDelete.push_back(pkt);
    }

    return true;
}

void
SimpleMemory::release()
{
    assert(isBusy);
    isBusy = false;
    if (retryReq) {
        retryReq = false;
        port.sendRetry();
    }
}

void
SimpleMemory::dequeue()
{
    assert(!packetQueue.empty());
    DeferredPacket deferred_pkt = packetQueue.front();

    retryResp = !port.sendTimingResp(deferred_pkt.pkt);

    if (!retryResp) {
        packetQueue.pop_front();

        // if the queue is not empty, schedule the next dequeue event,
        // otherwise signal that we are drained if we were asked to do so
        if (!packetQueue.empty()) {
            // if there were packets that got in-between then we
            // already have an event scheduled, so use re-schedule
            reschedule(dequeueEvent,
                       std::max(packetQueue.front().tick, curTick()), true);
        } else if (drainManager) {
            drainManager->signalDrainDone();
            drainManager = NULL;
        }
    }
}

Tick
SimpleMemory::getLatency() const
{
    return latency +
        (latency_var ? random_mt.random<Tick>(0, latency_var) : 0);
}

void
SimpleMemory::recvRetry()
{
    assert(retryResp);

    dequeue();
}

BaseSlavePort &
SimpleMemory::getSlavePort(const std::string &if_name, PortID idx)
{
    if (if_name != "port") {
        return MemObject::getSlavePort(if_name, idx);
    } else {
        return port;
    }
}

unsigned int
SimpleMemory::drain(DrainManager *dm)
{
    int count = 0;

    // also track our internal queue
    if (!packetQueue.empty()) {
        count += 1;
        drainManager = dm;
    }

    if (count)
        setDrainState(Drainable::Draining);
    else
        setDrainState(Drainable::Drained);
    return count;
}

SimpleMemory::MemoryPort::MemoryPort(const std::string& _name,
                                     SimpleMemory& _memory)
    : SlavePort(_name, &_memory), memory(_memory)
{ }

AddrRangeList
SimpleMemory::MemoryPort::getAddrRanges() const
{
    AddrRangeList ranges;
    ranges.push_back(memory.getAddrRange());
    return ranges;
}

Tick
SimpleMemory::MemoryPort::recvAtomic(PacketPtr pkt)
{
    return memory.recvAtomic(pkt);
}

void
SimpleMemory::MemoryPort::recvFunctional(PacketPtr pkt)
{
    memory.recvFunctional(pkt);
}

bool
SimpleMemory::MemoryPort::recvTimingReq(PacketPtr pkt)
{
    return memory.recvTimingReq(pkt);
}

void
SimpleMemory::MemoryPort::recvRetry()
{
    memory.recvRetry();
}

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

#include "base/random.hh"
#include "mem/simple_mem.hh"

using namespace std;

SimpleMemory::SimpleMemory(const SimpleMemoryParams* p) :
    AbstractMemory(p),
    port(name() + ".port", *this), latency(p->latency),
    latency_var(p->latency_var), bandwidth(p->bandwidth), isBusy(false),
    retryReq(false), retryResp(false),
    releaseEvent(this), dequeueEvent(this), drainManager(NULL)
{
}

void
SimpleMemory::init()
{
    // allow unconnected memories as this is used in several ruby
    // systems at the moment
    if (port.isConnected()) {
        port.sendRangeChange();
    }
}

Tick
SimpleMemory::recvAtomic(PacketPtr pkt)
{
    access(pkt);
    return pkt->memInhibitAsserted() ? 0 : getLatency();
}

void
SimpleMemory::recvFunctional(PacketPtr pkt)
{
    pkt->pushLabel(name());

    functionalAccess(pkt);

    // potentially update the packets in our packet queue as well
    for (auto i = packetQueue.begin(); i != packetQueue.end(); ++i)
        pkt->checkFunctional(i->pkt);

    pkt->popLabel();
}

bool
SimpleMemory::recvTimingReq(PacketPtr pkt)
{
    /// @todo temporary hack to deal with memory corruption issues until
    /// 4-phase transactions are complete
    for (int x = 0; x < pendingDelete.size(); x++)
        delete pendingDelete[x];
    pendingDelete.clear();

    if (pkt->memInhibitAsserted()) {
        // snooper will supply based on copy of packet
        // still target's responsibility to delete packet
        pendingDelete.push_back(pkt);
        return true;
    }

    // we should never get a new request after committing to retry the
    // current one, the bus violates the rule as it simply sends a
    // retry to the next one waiting on the retry list, so simply
    // ignore it
    if (retryReq)
        return false;

    // if we are busy with a read or write, remember that we have to
    // retry
    if (isBusy) {
        retryReq = true;
        return false;
    }

    // @todo someone should pay for this
    pkt->busFirstWordDelay = pkt->busLastWordDelay = 0;

    // update the release time according to the bandwidth limit, and
    // do so with respect to the time it takes to finish this request
    // rather than long term as it is the short term data rate that is
    // limited for any real memory

    // only look at reads and writes when determining if we are busy,
    // and for how long, as it is not clear what to regulate for the
    // other types of commands
    if (pkt->isRead() || pkt->isWrite()) {
        // calculate an appropriate tick to release to not exceed
        // the bandwidth limit
        Tick duration = pkt->getSize() * bandwidth;

        // only consider ourselves busy if there is any need to wait
        // to avoid extra events being scheduled for (infinitely) fast
        // memories
        if (duration != 0) {
            schedule(releaseEvent, curTick() + duration);
            isBusy = true;
        }
    }

    // go ahead and deal with the packet and put the response in the
    // queue if there is one
    bool needsResponse = pkt->needsResponse();
    recvAtomic(pkt);
    // turn packet around to go back to requester if response expected
    if (needsResponse) {
        // recvAtomic() should already have turned packet into
        // atomic response
        assert(pkt->isResponse());
        // to keep things simple (and in order), we put the packet at
        // the end even if the latency suggests it should be sent
        // before the packet(s) before it
        packetQueue.push_back(DeferredPacket(pkt, curTick() + getLatency()));
        if (!retryResp && !dequeueEvent.scheduled())
            schedule(dequeueEvent, packetQueue.back().tick);
    } else {
        pendingDelete.push_back(pkt);
    }

    return true;
}

void
SimpleMemory::release()
{
    assert(isBusy);
    isBusy = false;
    if (retryReq) {
        retryReq = false;
        port.sendRetry();
    }
}

void
SimpleMemory::dequeue()
{
    assert(!packetQueue.empty());
    DeferredPacket deferred_pkt = packetQueue.front();

    retryResp = !port.sendTimingResp(deferred_pkt.pkt);

    if (!retryResp) {
        packetQueue.pop_front();

        // if the queue is not empty, schedule the next dequeue event,
        // otherwise signal that we are drained if we were asked to do so
        if (!packetQueue.empty()) {
            // if there were packets that got in-between then we
            // already have an event scheduled, so use re-schedule
            reschedule(dequeueEvent,
                       std::max(packetQueue.front().tick, curTick()), true);
        } else if (drainManager) {
            drainManager->signalDrainDone();
            drainManager = NULL;
        }
    }
}

Tick
SimpleMemory::getLatency() const
{
    return latency +
        (latency_var ? random_mt.random<Tick>(0, latency_var) : 0);
}

void
SimpleMemory::recvRetry()
{
    assert(retryResp);

    dequeue();
}

BaseSlavePort &
SimpleMemory::getSlavePort(const std::string &if_name, PortID idx)
{
    if (if_name != "port") {
        return MemObject::getSlavePort(if_name, idx);
    } else {
        return port;
    }
}

unsigned int
SimpleMemory::drain(DrainManager *dm)
{
    int count = 0;

    // also track our internal queue
    if (!packetQueue.empty()) {
        count += 1;
        drainManager = dm;
    }

    if (count)
        setDrainState(Drainable::Draining);
    else
        setDrainState(Drainable::Drained);
    return count;
}

SimpleMemory::MemoryPort::MemoryPort(const std::string& _name,
                                     SimpleMemory& _memory)
    : SlavePort(_name, &_memory), memory(_memory)
{ }

AddrRangeList
SimpleMemory::MemoryPort::getAddrRanges() const
{
    AddrRangeList ranges;
    ranges.push_back(memory.getAddrRange());
    return ranges;
}

Tick
SimpleMemory::MemoryPort::recvAtomic(PacketPtr pkt)
{
    return memory.recvAtomic(pkt);
}

void
SimpleMemory::MemoryPort::recvFunctional(PacketPtr pkt)
{
    memory.recvFunctional(pkt);
}

bool
SimpleMemory::MemoryPort::recvTimingReq(PacketPtr pkt)
{
    return memory.recvTimingReq(pkt);
}

void
SimpleMemory::MemoryPort::recvRetry()
{
    memory.recvRetry();
}

SimpleMemory*
SimpleMemoryParams::create()
{
    return new SimpleMemory(this);
}
<|endoftext|>


<commit_before>/*
 * The MIT License
 *
 * Copyright 2017-2018 Norwegian University of Technology
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING  FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#ifndef FMI4CPP_SCALARVARIABLE_HPP
#define FMI4CPP_SCALARVARIABLE_HPP

#include <memory>
#include <optional>
#include <boost/optional.hpp>
#include <boost/property_tree/ptree.hpp>
#include <ostream>

#include "enums.hpp"
#include "../fmi2Functions.h"
#include "ScalarVariableAttributes.hpp"

using std::string;
using boost::property_tree::ptree;

namespace fmi4cpp::fmi2::xml {

    //forward declarations
    class IntegerVariable;

    class RealVariable;

    class StringVariable;

    class BooleanVariable;

    class EnumerationVariable;

    class ScalarVariable {

    private:
        string name_;
        string description_;
        fmi2ValueReference valueReference_;
        bool canHandleMultipleSetPerTimelnstant_;

        fmi2Causality causality_;
        fmi2Variability variability_;
        fmi2Initial initial_;

        std::shared_ptr<IntegerAttribute> integerAttribute_;
        std::shared_ptr<RealAttribute> realAttribute_;
        std::shared_ptr<StringAttribute> stringAttribute_;
        std::shared_ptr<BooleanAttribute> booleanAttribute_;
        std::shared_ptr<EnumerationAttribute> enumerationAttribute_;

    public:

        string getName() const;

        string getDescription() const;

        fmi2ValueReference getValueReference() const;

        bool canHandleMultipleSetPerTimelnstant() const;

        fmi2Causality getCausality() const;

        fmi2Variability getVariability() const;

        fmi2Initial getInitial() const;

        IntegerVariable asIntegerVariable() const;

        RealVariable asRealVariable() const;

        StringVariable asStringVariable() const;

        BooleanVariable asBooleanVariable() const;

        EnumerationVariable asEnumerationVariable() const;

        bool isIntegerVariable() const;

        bool isRealVariable() const;

        bool isStringVariable() const;

        bool isBooleanVariable() const;

        bool isEnumerationVariable() const;

        void load(const ptree &node);

        friend std::ostream &operator<<(std::ostream &os, const ScalarVariable &variable);

    };

    template <class T, class U>
    class TypedVariable {

    private:

        bool startChanged;
        std::optional<T> originalStart;

    protected:
        U &attribute_;

    public:

        TypedVariable(U &attribute_) : attribute_(attribute_) {
            originalStart = attribute_.start;
        }

        bool hasStartChanged() const {
            return startChanged;
        }

        std::optional<T> getStart() const {
            return attribute_.start;
        }

        bool setStart(const T value) {
            if (attribute_.start) {
                attribute_.start = value;
                startChanged = true;
                return true;
            } else if (attribute_.start.value() != value) {
                attribute_.start = value;
                startChanged = true;
                return true;
            }
            return false;
        }

        void resetStart() {
            if (startChanged) {
                attribute_.start = originalStart;
                startChanged = false;
            }
        }

    };

    class IntegerVariable : public ScalarVariable, public TypedVariable<int, IntegerAttribute> {

    public:
        IntegerVariable(const ScalarVariable &var, IntegerAttribute &attribute);

        std::optional<int> getMin() const;

        std::optional<int> getMax() const;

        void setStart(const int start);

        std::optional<string> getQuantity() const;

    };

    class RealVariable : public ScalarVariable, public TypedVariable<double, RealAttribute> {

    public:
        RealVariable(const ScalarVariable &var, RealAttribute &attribute);

        std::optional<double> getMin() const;

        std::optional<double> getMax() const;

        std::optional<double> getNominal() const;

        bool getReinit() const;

        bool getUnbounded() const;

        bool getRelativeQuantity() const;

        std::optional<string> getQuantity() const;

        std::optional<string> getUnit() const;

        std::optional<string> getDisplayUnit() const;

        std::optional<unsigned int> getDerivative() const;

    };

    class StringVariable : public ScalarVariable, public TypedVariable<string, StringAttribute> {

    public:
        StringVariable(const ScalarVariable &var, StringAttribute &attribute);
    };

    class BooleanVariable : public ScalarVariable, public TypedVariable<bool, BooleanAttribute> {

    public:
        BooleanVariable(const ScalarVariable &var, BooleanAttribute &attribute);
    };

    class EnumerationVariable : public ScalarVariable, public TypedVariable<int, EnumerationAttribute> {

    public:
        EnumerationVariable(const ScalarVariable &var, EnumerationAttribute &attribute);

        std::optional<int> getMin() const;

        std::optional<int> getMax() const;

        std::optional<string> getQuantity() const;

    };

}

#endif //FMI4CPP_SCALARVARIABLE_HPP
<commit_msg>refactor<commit_after>/*
 * The MIT License
 *
 * Copyright 2017-2018 Norwegian University of Technology
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING  FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#ifndef FMI4CPP_SCALARVARIABLE_HPP
#define FMI4CPP_SCALARVARIABLE_HPP

#include <memory>
#include <optional>
#include <boost/optional.hpp>
#include <boost/property_tree/ptree.hpp>
#include <ostream>

#include "enums.hpp"
#include "../fmi2Functions.h"
#include "ScalarVariableAttributes.hpp"

using boost::property_tree::ptree;

namespace fmi4cpp::fmi2::xml {

    //forward declarations
    class IntegerVariable;

    class RealVariable;

    class StringVariable;

    class BooleanVariable;

    class EnumerationVariable;

    class ScalarVariable {

    private:
        std::string name_;
        std::string description_;
        fmi2ValueReference valueReference_;
        bool canHandleMultipleSetPerTimelnstant_;

        fmi2Causality causality_;
        fmi2Variability variability_;
        fmi2Initial initial_;

        std::shared_ptr<IntegerAttribute> integerAttribute_;
        std::shared_ptr<RealAttribute> realAttribute_;
        std::shared_ptr<StringAttribute> stringAttribute_;
        std::shared_ptr<BooleanAttribute> booleanAttribute_;
        std::shared_ptr<EnumerationAttribute> enumerationAttribute_;

    public:

        std::string getName() const;

        std::string getDescription() const;

        fmi2ValueReference getValueReference() const;

        bool canHandleMultipleSetPerTimelnstant() const;

        fmi2Causality getCausality() const;

        fmi2Variability getVariability() const;

        fmi2Initial getInitial() const;

        IntegerVariable asIntegerVariable() const;

        RealVariable asRealVariable() const;

        StringVariable asStringVariable() const;

        BooleanVariable asBooleanVariable() const;

        EnumerationVariable asEnumerationVariable() const;

        bool isIntegerVariable() const;

        bool isRealVariable() const;

        bool isStringVariable() const;

        bool isBooleanVariable() const;

        bool isEnumerationVariable() const;

        void load(const ptree &node);

        friend std::ostream &operator<<(std::ostream &os, const ScalarVariable &variable);

    };

    template <class T, class U>
    class TypedVariable {

    private:

        bool startChanged;
        std::optional<T> originalStart;

    protected:
        U &attribute_;

    public:

        TypedVariable(U &attribute_) : attribute_(attribute_) {
            originalStart = attribute_.start;
        }

        bool hasStartChanged() const {
            return startChanged;
        }

        std::optional<T> getStart() const {
            return attribute_.start;
        }

        bool setStart(const T value) {
            if (attribute_.start) {
                attribute_.start = value;
                startChanged = true;
                return true;
            } else if (attribute_.start.value() != value) {
                attribute_.start = value;
                startChanged = true;
                return true;
            }
            return false;
        }

        void resetStart() {
            if (startChanged) {
                attribute_.start = originalStart;
                startChanged = false;
            }
        }

    };

    class IntegerVariable : public ScalarVariable, public TypedVariable<int, IntegerAttribute> {

    public:
        IntegerVariable(const ScalarVariable &var, IntegerAttribute &attribute);

        std::optional<int> getMin() const;

        std::optional<int> getMax() const;

        void setStart(const int start);

        std::optional<std::string> getQuantity() const;

    };

    class RealVariable : public ScalarVariable, public TypedVariable<double, RealAttribute> {

    public:
        RealVariable(const ScalarVariable &var, RealAttribute &attribute);

        std::optional<double> getMin() const;

        std::optional<double> getMax() const;

        std::optional<double> getNominal() const;

        bool getReinit() const;

        bool getUnbounded() const;

        bool getRelativeQuantity() const;

        std::optional<std::string> getQuantity() const;

        std::optional<std::string> getUnit() const;

        std::optional<std::string> getDisplayUnit() const;

        std::optional<unsigned int> getDerivative() const;

    };

    class StringVariable : public ScalarVariable, public TypedVariable<std::string, StringAttribute> {

    public:
        StringVariable(const ScalarVariable &var, StringAttribute &attribute);
    };

    class BooleanVariable : public ScalarVariable, public TypedVariable<bool, BooleanAttribute> {

    public:
        BooleanVariable(const ScalarVariable &var, BooleanAttribute &attribute);
    };

    class EnumerationVariable : public ScalarVariable, public TypedVariable<int, EnumerationAttribute> {

    public:
        EnumerationVariable(const ScalarVariable &var, EnumerationAttribute &attribute);

        std::optional<int> getMin() const;

        std::optional<int> getMax() const;

        std::optional<std::string> getQuantity() const;

    };

}

#endif //FMI4CPP_SCALARVARIABLE_HPP
<|endoftext|>