<commit_before>// Copyright (C) 2015 Jérôme Leclercq
// This file is part of the "Nazara Engine - Graphics module"
// For conditions of distribution and use, see copyright notice in Config.hpp

#include <Nazara/Graphics/ParticleSystem.hpp>
#include <Nazara/Core/CallOnExit.hpp>
#include <Nazara/Core/ErrorFlags.hpp>
#include <Nazara/Core/StringStream.hpp>
#include <Nazara/Graphics/ParticleMapper.hpp>
#include <cstdlib>
#include <memory>
#include <Nazara/Graphics/Debug.hpp>

NzParticleSystem::NzParticleSystem(unsigned int maxParticleCount, nzParticleLayout layout) :
NzParticleSystem(maxParticleCount, NzParticleDeclaration::Get(layout))
{
}

NzParticleSystem::NzParticleSystem(unsigned int maxParticleCount, const NzParticleDeclaration* declaration) :
m_declaration(declaration),
m_processing(false),
m_maxParticleCount(maxParticleCount),
m_particleCount(0)
{
	// En cas d'erreur, un constructeur ne peut que lancer une exception
	NzErrorFlags flags(nzErrorFlag_ThrowException, true);

	m_particleSize = m_declaration->GetStride(); // La taille de chaque particule

	ResizeBuffer();
}

NzParticleSystem::NzParticleSystem(const NzParticleSystem& system) :
NzRenderable(system),
m_controllers(system.m_controllers),
m_generators(system.m_generators),
m_declaration(system.m_declaration),
m_renderer(system.m_renderer),
m_processing(false),
m_maxParticleCount(system.m_maxParticleCount),
m_particleCount(system.m_particleCount),
m_particleSize(system.m_particleSize)
{
	NzErrorFlags flags(nzErrorFlag_ThrowException, true);

	ResizeBuffer();

	// On ne copie que les particules vivantes
	std::memcpy(m_buffer.data(), system.m_buffer.data(), system.m_particleCount*m_particleSize);
}

NzParticleSystem::~NzParticleSystem() = default;

void NzParticleSystem::AddController(NzParticleController* controller)
{
	NazaraAssert(controller, "Invalid particle controller");

	m_controllers.emplace_back(controller);
}

void NzParticleSystem::AddEmitter(NzParticleEmitter* emitter)
{
	NazaraAssert(controller, "Invalid particle emitter");

	m_emitters.emplace_back(emitter);
}

void NzParticleSystem::AddGenerator(NzParticleGenerator* generator)
{
	NazaraAssert(controller, "Invalid particle generator");

	m_generators.emplace_back(generator);
}

void NzParticleSystem::AddToRenderQueue(NzAbstractRenderQueue* renderQueue, const NzMatrix4f& transformMatrix) const
{
	NazaraAssert(m_renderer, "Invalid particle renderer");
	NazaraAssert(renderQueue, "Invalid renderqueue");
	NazaraUnused(transformMatrix);

	if (m_particleCount > 0)
	{
		NzParticleMapper mapper(m_buffer.data(), m_declaration);
		m_renderer->Render(*this, mapper, 0, m_particleCount-1, renderQueue);
	}
}

void NzParticleSystem::ApplyControllers(NzParticleMapper& mapper, unsigned int particleCount, float elapsedTime)
{
	m_processing = true;

	// Pour éviter un verrouillage en cas d'exception
	NzCallOnExit onExit([this]()
	{
		m_processing = false;
	});

	for (NzParticleController* controller : m_controllers)
		controller->Apply(*this, mapper, 0, particleCount-1, elapsedTime);

	onExit.CallAndReset();

	// On tue maintenant les particules mortes durant la mise à jour
	if (m_dyingParticles.size() < m_particleCount)
	{
		// On tue les particules depuis la dernière vers la première (en terme de place), le std::set étant trié via std::greater
		// La raison est simple, étant donné que la mort d'une particule signifie le déplacement de la dernière particule du buffer,
		// sans cette solution certaines particules pourraient échapper à la mort
		for (unsigned int index : m_dyingParticles)
			KillParticle(index);
	}
	else
		KillParticles(); // Toutes les particules sont mortes, ceci est beaucoup plus rapide

	m_dyingParticles.clear();
}

void* NzParticleSystem::CreateParticle()
{
	return CreateParticles(1);
}

void* NzParticleSystem::CreateParticles(unsigned int count)
{
	if (count == 0)
		return nullptr;

	if (m_particleCount + count > m_maxParticleCount)
		return nullptr;

	unsigned int particlesIndex = m_particleCount;
	m_particleCount += count;

	return &m_buffer[particlesIndex*m_particleSize];
}

void* NzParticleSystem::GenerateParticle()
{
	return GenerateParticles(1);
}

void* NzParticleSystem::GenerateParticles(unsigned int count)
{
	void* ptr = CreateParticles(count);
	if (!ptr)
		return nullptr;

	NzParticleMapper mapper(ptr, m_declaration);
	for (NzParticleGenerator* generator : m_generators)
		generator->Generate(*this, mapper, 0, count-1);

	return ptr;
}

const NzParticleDeclaration* NzParticleSystem::GetDeclaration() const
{
	return m_declaration;
}

float NzParticleSystem::GetFixedStepSize() const
{
	return m_stepSize;
}

unsigned int NzParticleSystem::GetMaxParticleCount() const
{
	return m_maxParticleCount;
}

unsigned int NzParticleSystem::GetParticleCount() const
{
	return m_particleCount;
}

unsigned int NzParticleSystem::GetParticleSize() const
{
	return m_particleSize;
}

bool NzParticleSystem::IsDrawable() const
{
	return true;
}

void NzParticleSystem::KillParticle(unsigned int index)
{
	///FIXME: Vérifier index

	if (m_processing)
	{
		// Le buffer est en train d'être modifié, nous ne pouvons pas réduire sa taille, on place alors la particule dans une liste d'attente
		m_dyingParticles.insert(index);
		return;
	}

	// On déplace la dernière particule vivante à la place de celle-ci
	if (--m_particleCount > 0)
		std::memcpy(&m_buffer[index*m_particleSize], &m_buffer[m_particleCount*m_particleSize], m_particleSize);
}

void NzParticleSystem::KillParticles()
{
	m_particleCount = 0;
}

void NzParticleSystem::RemoveController(NzParticleController* controller)
{
	auto it = std::find(m_controllers.begin(), m_controllers.end(), controller);
	if (it != m_controllers.end())
		m_controllers.erase(it);
}

void NzParticleSystem::RemoveEmitter(NzParticleEmitter* emitter)
{
	auto it = std::find(m_emitters.begin(), m_emitters.end(), emitter);
	if (it != m_emitters.end())
		m_emitters.erase(it);
}

void NzParticleSystem::RemoveGenerator(NzParticleGenerator* generator)
{
	auto it = std::find(m_generators.begin(), m_generators.end(), generator);
	if (it != m_generators.end())
		m_generators.erase(it);
}

void NzParticleSystem::SetFixedStepSize(float stepSize)
{
	m_stepSize = stepSize;
}

void NzParticleSystem::SetRenderer(NzParticleRenderer* renderer)
{
	m_renderer = renderer;
}

void NzParticleSystem::Update(float elapsedTime)
{
	// Émission
	for (NzParticleEmitter* emitter : m_emitters)
		emitter->Emit(*this, elapsedTime);

	// Mise à jour
	if (m_particleCount > 0)
	{
		///TODO: Mettre à jour en utilisant des threads
		NzParticleMapper mapper(m_buffer.data(), m_declaration);
		ApplyControllers(mapper, m_particleCount, elapsedTime);
	}
}

void NzParticleSystem::UpdateBoundingVolume(const NzMatrix4f& transformMatrix)
{
	NazaraUnused(transformMatrix);

	// Nothing to do here (our bounding volume is global)
}

NzParticleSystem& NzParticleSystem::operator=(const NzParticleSystem& system)
{
	NzErrorFlags flags(nzErrorFlag_ThrowException, true);

	NzRenderable::operator=(system);

	m_controllers = system.m_controllers;
	m_declaration = system.m_declaration;
	m_generators = system.m_generators;
	m_maxParticleCount = system.m_maxParticleCount;
	m_particleCount = system.m_particleCount;
	m_particleSize = system.m_particleSize;
	m_renderer = system.m_renderer;
	m_stepSize = system.m_stepSize;

	// La copie ne peut pas (ou plutôt ne devrait pas) avoir lieu pendant une mise à jour, inutile de copier
	m_dyingParticles.clear();
	m_processing = false;
	m_stepAccumulator = 0.f;

	m_buffer.clear(); // Pour éviter une recopie lors du resize() qui ne servira pas à grand chose
	ResizeBuffer();

	// On ne copie que les particules vivantes
	std::memcpy(m_buffer.data(), system.m_buffer.data(), system.m_particleCount*m_particleSize);

	return *this;
}

void NzParticleSystem::MakeBoundingVolume() const
{
	///TODO: Calculer l'AABB (prendre la taille des particules en compte s'il y a)
	m_boundingVolume.MakeInfinite();
}

void NzParticleSystem::ResizeBuffer()
{
	// Histoire de décrire un peu mieux l'erreur en cas d'échec
	try
	{
		m_buffer.resize(m_maxParticleCount*m_particleSize);
	}
	catch (const std::exception& e)
	{
		NzStringStream stream;
		stream << "Failed to allocate particle buffer (" << e.what() << ") for " << m_maxParticleCount << " particles of size " << m_particleSize;

		NazaraError(stream.ToString());
	}
}

<commit_msg>Graphics/ParticleSystem: Fix debug build<commit_after>// Copyright (C) 2015 Jérôme Leclercq
// This file is part of the "Nazara Engine - Graphics module"
// For conditions of distribution and use, see copyright notice in Config.hpp

#include <Nazara/Graphics/ParticleSystem.hpp>
#include <Nazara/Core/CallOnExit.hpp>
#include <Nazara/Core/ErrorFlags.hpp>
#include <Nazara/Core/StringStream.hpp>
#include <Nazara/Graphics/ParticleMapper.hpp>
#include <cstdlib>
#include <memory>
#include <Nazara/Graphics/Debug.hpp>

NzParticleSystem::NzParticleSystem(unsigned int maxParticleCount, nzParticleLayout layout) :
NzParticleSystem(maxParticleCount, NzParticleDeclaration::Get(layout))
{
}

NzParticleSystem::NzParticleSystem(unsigned int maxParticleCount, const NzParticleDeclaration* declaration) :
m_declaration(declaration),
m_processing(false),
m_maxParticleCount(maxParticleCount),
m_particleCount(0)
{
	// En cas d'erreur, un constructeur ne peut que lancer une exception
	NzErrorFlags flags(nzErrorFlag_ThrowException, true);

	m_particleSize = m_declaration->GetStride(); // La taille de chaque particule

	ResizeBuffer();
}

NzParticleSystem::NzParticleSystem(const NzParticleSystem& system) :
NzRenderable(system),
m_controllers(system.m_controllers),
m_generators(system.m_generators),
m_declaration(system.m_declaration),
m_renderer(system.m_renderer),
m_processing(false),
m_maxParticleCount(system.m_maxParticleCount),
m_particleCount(system.m_particleCount),
m_particleSize(system.m_particleSize)
{
	NzErrorFlags flags(nzErrorFlag_ThrowException, true);

	ResizeBuffer();

	// On ne copie que les particules vivantes
	std::memcpy(m_buffer.data(), system.m_buffer.data(), system.m_particleCount*m_particleSize);
}

NzParticleSystem::~NzParticleSystem() = default;

void NzParticleSystem::AddController(NzParticleController* controller)
{
	NazaraAssert(controller, "Invalid particle controller");

	m_controllers.emplace_back(controller);
}

void NzParticleSystem::AddEmitter(NzParticleEmitter* emitter)
{
	NazaraAssert(emitter, "Invalid particle emitter");

	m_emitters.emplace_back(emitter);
}

void NzParticleSystem::AddGenerator(NzParticleGenerator* generator)
{
	NazaraAssert(generator, "Invalid particle generator");

	m_generators.emplace_back(generator);
}

void NzParticleSystem::AddToRenderQueue(NzAbstractRenderQueue* renderQueue, const NzMatrix4f& transformMatrix) const
{
	NazaraAssert(m_renderer, "Invalid particle renderer");
	NazaraAssert(renderQueue, "Invalid renderqueue");
	NazaraUnused(transformMatrix);

	if (m_particleCount > 0)
	{
		NzParticleMapper mapper(m_buffer.data(), m_declaration);
		m_renderer->Render(*this, mapper, 0, m_particleCount-1, renderQueue);
	}
}

void NzParticleSystem::ApplyControllers(NzParticleMapper& mapper, unsigned int particleCount, float elapsedTime)
{
	m_processing = true;

	// Pour éviter un verrouillage en cas d'exception
	NzCallOnExit onExit([this]()
	{
		m_processing = false;
	});

	for (NzParticleController* controller : m_controllers)
		controller->Apply(*this, mapper, 0, particleCount-1, elapsedTime);

	onExit.CallAndReset();

	// On tue maintenant les particules mortes durant la mise à jour
	if (m_dyingParticles.size() < m_particleCount)
	{
		// On tue les particules depuis la dernière vers la première (en terme de place), le std::set étant trié via std::greater
		// La raison est simple, étant donné que la mort d'une particule signifie le déplacement de la dernière particule du buffer,
		// sans cette solution certaines particules pourraient échapper à la mort
		for (unsigned int index : m_dyingParticles)
			KillParticle(index);
	}
	else
		KillParticles(); // Toutes les particules sont mortes, ceci est beaucoup plus rapide

	m_dyingParticles.clear();
}

void* NzParticleSystem::CreateParticle()
{
	return CreateParticles(1);
}

void* NzParticleSystem::CreateParticles(unsigned int count)
{
	if (count == 0)
		return nullptr;

	if (m_particleCount + count > m_maxParticleCount)
		return nullptr;

	unsigned int particlesIndex = m_particleCount;
	m_particleCount += count;

	return &m_buffer[particlesIndex*m_particleSize];
}

void* NzParticleSystem::GenerateParticle()
{
	return GenerateParticles(1);
}

void* NzParticleSystem::GenerateParticles(unsigned int count)
{
	void* ptr = CreateParticles(count);
	if (!ptr)
		return nullptr;

	NzParticleMapper mapper(ptr, m_declaration);
	for (NzParticleGenerator* generator : m_generators)
		generator->Generate(*this, mapper, 0, count-1);

	return ptr;
}

const NzParticleDeclaration* NzParticleSystem::GetDeclaration() const
{
	return m_declaration;
}

float NzParticleSystem::GetFixedStepSize() const
{
	return m_stepSize;
}

unsigned int NzParticleSystem::GetMaxParticleCount() const
{
	return m_maxParticleCount;
}

unsigned int NzParticleSystem::GetParticleCount() const
{
	return m_particleCount;
}

unsigned int NzParticleSystem::GetParticleSize() const
{
	return m_particleSize;
}

bool NzParticleSystem::IsDrawable() const
{
	return true;
}

void NzParticleSystem::KillParticle(unsigned int index)
{
	///FIXME: Vérifier index

	if (m_processing)
	{
		// Le buffer est en train d'être modifié, nous ne pouvons pas réduire sa taille, on place alors la particule dans une liste d'attente
		m_dyingParticles.insert(index);
		return;
	}

	// On déplace la dernière particule vivante à la place de celle-ci
	if (--m_particleCount > 0)
		std::memcpy(&m_buffer[index*m_particleSize], &m_buffer[m_particleCount*m_particleSize], m_particleSize);
}

void NzParticleSystem::KillParticles()
{
	m_particleCount = 0;
}

void NzParticleSystem::RemoveController(NzParticleController* controller)
{
	auto it = std::find(m_controllers.begin(), m_controllers.end(), controller);
	if (it != m_controllers.end())
		m_controllers.erase(it);
}

void NzParticleSystem::RemoveEmitter(NzParticleEmitter* emitter)
{
	auto it = std::find(m_emitters.begin(), m_emitters.end(), emitter);
	if (it != m_emitters.end())
		m_emitters.erase(it);
}

void NzParticleSystem::RemoveGenerator(NzParticleGenerator* generator)
{
	auto it = std::find(m_generators.begin(), m_generators.end(), generator);
	if (it != m_generators.end())
		m_generators.erase(it);
}

void NzParticleSystem::SetFixedStepSize(float stepSize)
{
	m_stepSize = stepSize;
}

void NzParticleSystem::SetRenderer(NzParticleRenderer* renderer)
{
	m_renderer = renderer;
}

void NzParticleSystem::Update(float elapsedTime)
{
	// Émission
	for (NzParticleEmitter* emitter : m_emitters)
		emitter->Emit(*this, elapsedTime);

	// Mise à jour
	if (m_particleCount > 0)
	{
		///TODO: Mettre à jour en utilisant des threads
		NzParticleMapper mapper(m_buffer.data(), m_declaration);
		ApplyControllers(mapper, m_particleCount, elapsedTime);
	}
}

void NzParticleSystem::UpdateBoundingVolume(const NzMatrix4f& transformMatrix)
{
	NazaraUnused(transformMatrix);

	// Nothing to do here (our bounding volume is global)
}

NzParticleSystem& NzParticleSystem::operator=(const NzParticleSystem& system)
{
	NzErrorFlags flags(nzErrorFlag_ThrowException, true);

	NzRenderable::operator=(system);

	m_controllers = system.m_controllers;
	m_declaration = system.m_declaration;
	m_generators = system.m_generators;
	m_maxParticleCount = system.m_maxParticleCount;
	m_particleCount = system.m_particleCount;
	m_particleSize = system.m_particleSize;
	m_renderer = system.m_renderer;
	m_stepSize = system.m_stepSize;

	// La copie ne peut pas (ou plutôt ne devrait pas) avoir lieu pendant une mise à jour, inutile de copier
	m_dyingParticles.clear();
	m_processing = false;
	m_stepAccumulator = 0.f;

	m_buffer.clear(); // Pour éviter une recopie lors du resize() qui ne servira pas à grand chose
	ResizeBuffer();

	// On ne copie que les particules vivantes
	std::memcpy(m_buffer.data(), system.m_buffer.data(), system.m_particleCount*m_particleSize);

	return *this;
}

void NzParticleSystem::MakeBoundingVolume() const
{
	///TODO: Calculer l'AABB (prendre la taille des particules en compte s'il y a)
	m_boundingVolume.MakeInfinite();
}

void NzParticleSystem::ResizeBuffer()
{
	// Histoire de décrire un peu mieux l'erreur en cas d'échec
	try
	{
		m_buffer.resize(m_maxParticleCount*m_particleSize);
	}
	catch (const std::exception& e)
	{
		NzStringStream stream;
		stream << "Failed to allocate particle buffer (" << e.what() << ") for " << m_maxParticleCount << " particles of size " << m_particleSize;

		NazaraError(stream.ToString());
	}
}

<|endoftext|>


<commit_before>#include "BiLM_NPLM.h"
#include "neuralLM.h"
#include "vocabulary.h"

namespace Moses {

BilingualLM_NPLM::BilingualLM_NPLM(const std::string &line)
      : BilingualLM(line),
        premultiply(true),
        factored(false),
        neuralLM_cache(1000000) {

          if (!NULL_overwrite) {
            NULL_string = "<null>"; //Default null value for nplm
          }
          FactorCollection& factorFactory = FactorCollection::Instance(); // To add null word.
          const Factor* NULL_factor = factorFactory.AddFactor(NULL_string);
          NULL_word.SetFactor(0, NULL_factor);
        }

float BilingualLM_NPLM::Score(std::vector<int>& source_words, std::vector<int>& target_words) const {
  source_words.reserve(source_ngrams+target_ngrams+1);
  source_words.insert( source_words.end(), target_words.begin(), target_words.end() );
  return FloorScore(m_neuralLM->lookup_ngram(source_words));
}

const Word& BilingualLM_NPLM::getNullWord() const {
  return NULL_word;
}

int BilingualLM_NPLM::getNeuralLMId(const Word& word, bool is_source_word) const {
  initSharedPointer();

  //Decide if we are doing source or target side first.
  boost::unordered_map<const Factor*, int> * neuralLMids;
  int unknown_word_id;
  if (is_source_word) {
    neuralLMids = &source_neuralLMids;
    unknown_word_id = source_unknown_word_id;
  } else {
    neuralLMids = &target_neuralLMids;
    unknown_word_id = target_unknown_word_id;
  }

  boost::unordered_map<const Factor*, int>::iterator it;
  const Factor* factor = word.GetFactor(word_factortype);

  it = neuralLMids->find(factor);
  //If we know the word return immediately
  if (it != neuralLMids->end()){
    return it->second;
  }
  //If we don't know the word and we aren't factored, return the word.
  if (!factored) {
      return unknown_word_id;
  }
  //Else try to get a pos_factor
  const Factor* pos_factor = word.GetFactor(pos_factortype);
  it = neuralLMids->find(pos_factor);
  if (it != neuralLMids->end()){
    return it->second;
  } else {
    return unknown_word_id;
  }
}

void BilingualLM_NPLM::initSharedPointer() const {
  if (!m_neuralLM.get()) {
    m_neuralLM.reset(new nplm::neuralLM(*m_neuralLM_shared));
  }
}

void BilingualLM_NPLM::SetParameter(const std::string& key, const std::string& value) {
  if (key == "target_ngrams") {
    target_ngrams = Scan<int>(value);
  } else if (key == "source_ngrams") {
    source_ngrams = Scan<int>(value);
  } else if (key == "factored") {
    factored = Scan<bool>(value);
  } else if (key == "pos_factor") {
    pos_factortype = Scan<FactorType>(value);
  } else if (key == "source_vocab") {
    source_vocab_path = value;
  } else if (key == "target_vocab") {
    target_vocab_path = value;
  } else if (key == "cache_size") {
    neuralLM_cache = atoi(value.c_str());
  } else if (key == "premultiply") {
    premultiply = Scan<bool>(value);
  } else if (key == "null_word") {
    NULL_string = value;
    NULL_overwrite = true;
  } else {
    BilingualLM::SetParameter(key, value);
  }
}

void BilingualLM_NPLM::loadModel() {
  m_neuralLM_shared = new nplm::neuralLM();
  m_neuralLM_shared->read(m_filePath);
  if (premultiply) {
    m_neuralLM_shared->premultiply();
  }

  int ngram_order = target_ngrams + source_ngrams + 1;
  UTIL_THROW_IF2(
      ngram_order != m_neuralLM_shared->get_order(),
      "Wrong order of neuralLM: LM has " << m_neuralLM_shared->get_order() <<
      ", but Moses expects " << ngram_order);

  m_neuralLM_shared->set_cache(neuralLM_cache); //Default 1000000

  //Setup factor -> NeuralLMId cache. First target words
  FactorCollection& factorFactory = FactorCollection::Instance(); //To do the conversion from string to vocabID
  int wordid_counter = 0;
  target_unknown_word_id = wordid_counter; //The first word is <unk>
  std::string raw_word;
  std::ifstream infile_target(target_vocab_path.c_str());
  while (infile_target >> raw_word) {
    const Factor * factor = factorFactory.AddFactor(raw_word);
    target_neuralLMids.insert(std::make_pair(factor, wordid_counter));
    wordid_counter++;
  }
  infile_target.close();
  source_unknown_word_id = wordid_counter; //The first word is <unk> from the next file

  //Source words now:
  std::ifstream infile_source(source_vocab_path.c_str());
  while (infile_source >> raw_word) {
    const Factor * factor = factorFactory.AddFactor(raw_word);
    source_neuralLMids.insert(std::make_pair(factor, wordid_counter));
    wordid_counter++;
  }
  infile_source.close();

}

} // namespace Moses
<commit_msg>null padding<commit_after>#include "BiLM_NPLM.h"
#include "neuralLM.h"
#include "vocabulary.h"

namespace Moses {

BilingualLM_NPLM::BilingualLM_NPLM(const std::string &line)
      : BilingualLM(line),
        premultiply(true),
        factored(false),
        neuralLM_cache(1000000) {

          NULL_string = "<null>"; //Default null value for nplm
          FactorCollection& factorFactory = FactorCollection::Instance(); // To add null word.
          const Factor* NULL_factor = factorFactory.AddFactor(NULL_string);
          NULL_word.SetFactor(0, NULL_factor);
        }

float BilingualLM_NPLM::Score(std::vector<int>& source_words, std::vector<int>& target_words) const {
  source_words.reserve(source_ngrams+target_ngrams+1);
  source_words.insert( source_words.end(), target_words.begin(), target_words.end() );
  return FloorScore(m_neuralLM->lookup_ngram(source_words));
}

const Word& BilingualLM_NPLM::getNullWord() const {
  return NULL_word;
}

int BilingualLM_NPLM::getNeuralLMId(const Word& word, bool is_source_word) const {
  initSharedPointer();

  //Decide if we are doing source or target side first.
  boost::unordered_map<const Factor*, int> * neuralLMids;
  int unknown_word_id;
  if (is_source_word) {
    neuralLMids = &source_neuralLMids;
    unknown_word_id = source_unknown_word_id;
  } else {
    neuralLMids = &target_neuralLMids;
    unknown_word_id = target_unknown_word_id;
  }

  boost::unordered_map<const Factor*, int>::iterator it;
  const Factor* factor = word.GetFactor(word_factortype);

  it = neuralLMids->find(factor);
  //If we know the word return immediately
  if (it != neuralLMids->end()){
    return it->second;
  }
  //If we don't know the word and we aren't factored, return the word.
  if (!factored) {
      return unknown_word_id;
  }
  //Else try to get a pos_factor
  const Factor* pos_factor = word.GetFactor(pos_factortype);
  it = neuralLMids->find(pos_factor);
  if (it != neuralLMids->end()){
    return it->second;
  } else {
    return unknown_word_id;
  }
}

void BilingualLM_NPLM::initSharedPointer() const {
  if (!m_neuralLM.get()) {
    m_neuralLM.reset(new nplm::neuralLM(*m_neuralLM_shared));
  }
}

void BilingualLM_NPLM::SetParameter(const std::string& key, const std::string& value) {
  if (key == "target_ngrams") {
    target_ngrams = Scan<int>(value);
  } else if (key == "source_ngrams") {
    source_ngrams = Scan<int>(value);
  } else if (key == "factored") {
    factored = Scan<bool>(value);
  } else if (key == "pos_factor") {
    pos_factortype = Scan<FactorType>(value);
  } else if (key == "source_vocab") {
    source_vocab_path = value;
  } else if (key == "target_vocab") {
    target_vocab_path = value;
  } else if (key == "cache_size") {
    neuralLM_cache = atoi(value.c_str());
  } else if (key == "premultiply") {
    premultiply = Scan<bool>(value);
    //TODO: doesn't currently do anything (constructor doesn't know about parameters)
  } else if (key == "null_word") {
    NULL_string = value;
    NULL_overwrite = true;
  } else {
    BilingualLM::SetParameter(key, value);
  }
}

void BilingualLM_NPLM::loadModel() {
  m_neuralLM_shared = new nplm::neuralLM();
  m_neuralLM_shared->read(m_filePath);
  if (premultiply) {
    m_neuralLM_shared->premultiply();
  }

  int ngram_order = target_ngrams + source_ngrams + 1;
  UTIL_THROW_IF2(
      ngram_order != m_neuralLM_shared->get_order(),
      "Wrong order of neuralLM: LM has " << m_neuralLM_shared->get_order() <<
      ", but Moses expects " << ngram_order);

  m_neuralLM_shared->set_cache(neuralLM_cache); //Default 1000000

  //Setup factor -> NeuralLMId cache. First target words
  FactorCollection& factorFactory = FactorCollection::Instance(); //To do the conversion from string to vocabID
  int wordid_counter = 0;
  target_unknown_word_id = wordid_counter; //The first word is <unk>
  std::string raw_word;
  std::ifstream infile_target(target_vocab_path.c_str());
  while (infile_target >> raw_word) {
    const Factor * factor = factorFactory.AddFactor(raw_word);
    target_neuralLMids.insert(std::make_pair(factor, wordid_counter));
    wordid_counter++;
  }
  infile_target.close();
  source_unknown_word_id = wordid_counter; //The first word is <unk> from the next file

  //Source words now:
  std::ifstream infile_source(source_vocab_path.c_str());
  while (infile_source >> raw_word) {
    const Factor * factor = factorFactory.AddFactor(raw_word);
    source_neuralLMids.insert(std::make_pair(factor, wordid_counter));
    wordid_counter++;
  }
  infile_source.close();

}

} // namespace Moses
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2008-2016 the MRtrix3 contributors
 *
 * 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/
 *
 * MRtrix is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *
 * For more details, see www.mrtrix.org
 *
 */

#include "command.h"
#include "image.h"
#include "algo/loop.h"
#include "progressbar.h"
#include "dwi/gradient.h"

using namespace MR;
using namespace App;

void usage () {
DESCRIPTION
  + "concatenate several images into one";

ARGUMENTS
  + Argument ("image1", "the first input image.")
  .type_image_in()

  + Argument ("image2", "additional input image(s).")
  .type_image_in()
  .allow_multiple()

  + Argument ("output", "the output image.")
  .type_image_out ();

OPTIONS
  + Option ("axis",
  "specify axis along which concatenation should be performed. By default, "
  "the program will use the last non-singleton, non-spatial axis of any of "
  "the input images - in other words axis 3 or whichever axis (greater than 3) "
  "of the input images has size greater than one.")
  + Argument ("axis").type_integer (0)

  + DataType::options();
}

typedef float value_type;

void run () {
  int axis = get_option_value ("axis", -1);

  int num_images = argument.size()-1;
  std::vector<Header> in (num_images);
  in[0] = Header::open (argument[0]);

  int ndims = 0;
  int last_dim;

  for (int i = 1; i < num_images; i++) {
    in[i] = Header::open (argument[i]);
    for (last_dim = in[i].ndim()-1; in[i].size (last_dim) <= 1 && last_dim >= 0; last_dim--);
    if (last_dim > ndims)
      ndims = last_dim;
  }

  if (axis < 0) axis = std::max (3, ndims);
  ++ndims;

  for (int i = 0; i < ndims; i++)
    if (i != axis)
      for (int n = 0; n < num_images; n++)
        if (in[0].size (i) != in[n].size (i))
          throw Exception ("dimensions of input images do not match");

  if (axis >= ndims) ndims = axis+1;

  Header header_out (in[0]);
  header_out.set_ndim (ndims);

  for (size_t i = 0; i < header_out.ndim(); i++) {
    if (header_out.size (i) <= 1) {
      for (int n = 0; n < num_images; n++) {
        if (in[n].ndim() > i) {
          header_out.size(i) = in[n].size (i);
          header_out.spacing(i) = in[n].spacing (i);
          break;
        }
      }
    }
  }

  {
    size_t axis_dim = 0;
    for (int n = 0; n < num_images; n++) {
      if (in[n].datatype().is_complex())
        header_out.datatype() = DataType::CFloat32;
      axis_dim += in[n].ndim() > size_t (axis) ? (in[n].size (axis) > 1 ? in[n].size (axis) : 1) : 1;
    }
    header_out.size (axis) = axis_dim;
  }

  header_out.datatype() = DataType::from_command_line (header_out.datatype());

  if (axis > 2) { // concatenate DW schemes
    size_t nrows = 0;
    std::vector<Eigen::MatrixXd> input_grads;
    for (int n = 0; n < num_images; ++n) {
      auto grad = DWI::get_DW_scheme (in[n]);
      input_grads.push_back (grad);
      if (grad.rows() == 0 || grad.cols() != 4) {
        nrows = 0;
        break;
      }
      nrows += grad.rows();
    }

    if (nrows) {
      Eigen::MatrixXd grad_out (nrows, 4);
      int row = 0;
      for (int n = 0; n < num_images; ++n)
        for (ssize_t i = 0; i < input_grads[n].rows(); ++i, ++row)
          for (size_t j = 0; j < 4; ++j)
            grad_out (row,j) = input_grads[n](i,j);
      header_out.set_DW_scheme (grad_out);
    }
  }

  auto image_out = Image<value_type>::create (argument[num_images], header_out);
  int axis_offset = 0;

  for (int i = 0; i < num_images; i++) {
    auto image_in = in[i].get_image<value_type>();

    auto copy_func = [&axis, &axis_offset](decltype(image_in)& in, decltype(image_out)& out)
    {
      out.index (axis) = axis < int(in.ndim()) ? in.index (axis) + axis_offset : axis_offset;
      out.value() = in.value();
    };

    ThreadedLoop ("concatenating \"" + image_in.name() + "\"...", image_in, 0, std::min (image_in.ndim(), image_out.ndim()))
      .run (copy_func, image_in, image_out);
    if (axis < int(image_in.ndim()))
      axis_offset += image_in.size (axis);
    else {
      ++axis_offset;
      image_out.index (axis) = axis_offset;
    }
  }
}

<commit_msg>mrcat: allow DW gradient tables wider than 4 columns<commit_after>/*
 * Copyright (c) 2008-2016 the MRtrix3 contributors
 *
 * 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/
 *
 * MRtrix is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *
 * For more details, see www.mrtrix.org
 *
 */

#include "command.h"
#include "image.h"
#include "algo/loop.h"
#include "progressbar.h"
#include "dwi/gradient.h"

using namespace MR;
using namespace App;

void usage () {
DESCRIPTION
  + "concatenate several images into one";

ARGUMENTS
  + Argument ("image1", "the first input image.")
  .type_image_in()

  + Argument ("image2", "additional input image(s).")
  .type_image_in()
  .allow_multiple()

  + Argument ("output", "the output image.")
  .type_image_out ();

OPTIONS
  + Option ("axis",
  "specify axis along which concatenation should be performed. By default, "
  "the program will use the last non-singleton, non-spatial axis of any of "
  "the input images - in other words axis 3 or whichever axis (greater than 3) "
  "of the input images has size greater than one.")
  + Argument ("axis").type_integer (0)

  + DataType::options();
}

typedef float value_type;

void run () {
  int axis = get_option_value ("axis", -1);

  int num_images = argument.size()-1;
  std::vector<Header> in (num_images);
  in[0] = Header::open (argument[0]);

  int ndims = 0;
  int last_dim;

  for (int i = 1; i < num_images; i++) {
    in[i] = Header::open (argument[i]);
    for (last_dim = in[i].ndim()-1; in[i].size (last_dim) <= 1 && last_dim >= 0; last_dim--);
    if (last_dim > ndims)
      ndims = last_dim;
  }

  if (axis < 0) axis = std::max (3, ndims);
  ++ndims;

  for (int i = 0; i < ndims; i++)
    if (i != axis)
      for (int n = 0; n < num_images; n++)
        if (in[0].size (i) != in[n].size (i))
          throw Exception ("dimensions of input images do not match");

  if (axis >= ndims) ndims = axis+1;

  Header header_out (in[0]);
  header_out.set_ndim (ndims);

  for (size_t i = 0; i < header_out.ndim(); i++) {
    if (header_out.size (i) <= 1) {
      for (int n = 0; n < num_images; n++) {
        if (in[n].ndim() > i) {
          header_out.size(i) = in[n].size (i);
          header_out.spacing(i) = in[n].spacing (i);
          break;
        }
      }
    }
  }

  {
    size_t axis_dim = 0;
    for (int n = 0; n < num_images; n++) {
      if (in[n].datatype().is_complex())
        header_out.datatype() = DataType::CFloat32;
      axis_dim += in[n].ndim() > size_t (axis) ? (in[n].size (axis) > 1 ? in[n].size (axis) : 1) : 1;
    }
    header_out.size (axis) = axis_dim;
  }

  header_out.datatype() = DataType::from_command_line (header_out.datatype());

  if (axis > 2) { // concatenate DW schemes
    size_t nrows = 0;
    std::vector<Eigen::MatrixXd> input_grads;
    for (int n = 0; n < num_images; ++n) {
      auto grad = DWI::get_DW_scheme (in[n]);
      input_grads.push_back (grad);
      if (grad.rows() == 0 || grad.cols() < 4) {
        nrows = 0;
        break;
      }
      nrows += grad.rows();
    }

    if (nrows) {
      Eigen::MatrixXd grad_out (nrows, 4);
      int row = 0;
      for (int n = 0; n < num_images; ++n)
        for (ssize_t i = 0; i < input_grads[n].rows(); ++i, ++row)
          for (size_t j = 0; j < 4; ++j)
            grad_out (row,j) = input_grads[n](i,j);
      header_out.set_DW_scheme (grad_out);
    }
  }

  auto image_out = Image<value_type>::create (argument[num_images], header_out);
  int axis_offset = 0;

  for (int i = 0; i < num_images; i++) {
    auto image_in = in[i].get_image<value_type>();

    auto copy_func = [&axis, &axis_offset](decltype(image_in)& in, decltype(image_out)& out)
    {
      out.index (axis) = axis < int(in.ndim()) ? in.index (axis) + axis_offset : axis_offset;
      out.value() = in.value();
    };

    ThreadedLoop ("concatenating \"" + image_in.name() + "\"...", image_in, 0, std::min (image_in.ndim(), image_out.ndim()))
      .run (copy_func, image_in, image_out);
    if (axis < int(image_in.ndim()))
      axis_offset += image_in.size (axis);
    else {
      ++axis_offset;
      image_out.index (axis) = axis_offset;
    }
  }
}

<|endoftext|>


<commit_before>// Copyright (c) 2009-2019 The Regents of the University of Michigan
// This file is part of the HOOMD-blue project, released under the BSD 3-Clause License.

#include "Variant.h"

//* Method to enable unit testing of C++ trigger calls from pytest
Scalar testVariantCall(std::shared_ptr<Variant> t, uint64_t step)
    {
    return (*t)(step);
    }

//* Trampoline for classes inherited in python
class VariantPy : public Variant
    {
    public:
        // Inherit the constructors
        using Variant::Variant;

        // trampoline method
        Scalar operator()(uint64_t timestep) override
            {
            PYBIND11_OVERLOAD_NAME(Scalar,       // Return type
                                   Variant,      // Parent class
                                   "__call__",   // name of function in python
                                   operator(),   // Name of function in C++
                                   timestep      // Argument(s)
                              );
            }

        Scalar min() override
            {
            PYBIND11_OVERLOAD_PURE(Scalar,       // Return type
                                   Variant,      // Parent class
                                   min           // name of function
                    );
            }

        Scalar max() override
            {
            PYBIND11_OVERLOAD_PURE(Scalar,       // Return type
                                   Variant,      // Parent class
                                   min           // name of function
                    );
            }
    };

void export_Variant(pybind11::module& m)
    {
    pybind11::class_<Variant, VariantPy, std::shared_ptr<Variant> >(m,"Variant")
        .def(pybind11::init<>())
        .def("__call__", &Variant::operator())
        .def("min", &Variant::min)
        .def("max", &Variant::max)
        .def_property_readonly("range", &Variant::range)
        ;

    pybind11::class_<VariantConstant, Variant, std::shared_ptr<VariantConstant> >(m, "VariantConstant")
        .def(pybind11::init< Scalar >(), pybind11::arg("value"))
        .def_property("value", &VariantConstant::getValue, &VariantConstant::setValue)
        ;

    pybind11::class_<VariantRamp, Variant, std::shared_ptr<VariantRamp> >(m, "VariantRamp")
        .def(pybind11::init< Scalar, Scalar, uint64_t, uint64_t >(), pybind11::arg("A"),
                                                                     pybind11::arg("B"),
                                                                     pybind11::arg("t_start"),
                                                                     pybind11::arg("t_ramp"))
        .def_property("A", &VariantRamp::getA, &VariantRamp::setA)
        .def_property("B", &VariantRamp::getB, &VariantRamp::setB)
        .def_property("t_start", &VariantRamp::getTStart, &VariantRamp::setTStart)
        .def_property("t_ramp", &VariantRamp::getTRamp, &VariantRamp::setTRamp)
        ;

    pybind11::class_<VariantCycle, Variant, std::shared_ptr<VariantCycle> >(m, "VariantCycle")
        .def(pybind11::init< Scalar,
                             Scalar,
                             uint64_t,
                             uint64_t,
                             uint64_t,
                             uint64_t,
                             uint64_t >(),
            pybind11::arg("A"),
            pybind11::arg("B"),
            pybind11::arg("t_start"),
            pybind11::arg("t_A"),
            pybind11::arg("t_AB"),
            pybind11::arg("t_B"),
            pybind11::arg("t_BA"))

        .def_property("A", &VariantCycle::getA, &VariantCycle::setA)
        .def_property("B", &VariantCycle::getB, &VariantCycle::setB)
        .def_property("t_start", &VariantCycle::getTStart, &VariantCycle::setTStart)
        .def_property("t_A", &VariantCycle::getTA, &VariantCycle::setTA)
        .def_property("t_AB", &VariantCycle::getTAB, &VariantCycle::setTAB)
        .def_property("t_B", &VariantCycle::getTB, &VariantCycle::setTB)
        .def_property("t_BA", &VariantCycle::getTBA, &VariantCycle::setTBA)
        ;

    m.def("_test_variant_call", &testVariantCall);
    }
<commit_msg>Fix max method in VariantPy<commit_after>// Copyright (c) 2009-2019 The Regents of the University of Michigan
// This file is part of the HOOMD-blue project, released under the BSD 3-Clause License.

#include "Variant.h"

//* Method to enable unit testing of C++ trigger calls from pytest
Scalar testVariantCall(std::shared_ptr<Variant> t, uint64_t step)
    {
    return (*t)(step);
    }

//* Trampoline for classes inherited in python
class VariantPy : public Variant
    {
    public:
        // Inherit the constructors
        using Variant::Variant;

        // trampoline method
        Scalar operator()(uint64_t timestep) override
            {
            PYBIND11_OVERLOAD_NAME(Scalar,       // Return type
                                   Variant,      // Parent class
                                   "__call__",   // name of function in python
                                   operator(),   // Name of function in C++
                                   timestep      // Argument(s)
                              );
            }

        Scalar min() override
            {
            PYBIND11_OVERLOAD_PURE(Scalar,       // Return type
                                   Variant,      // Parent class
                                   min           // name of function
                    );
            }

        Scalar max() override
            {
            PYBIND11_OVERLOAD_PURE(Scalar,       // Return type
                                   Variant,      // Parent class
                                   max           // name of function
                    );
            }
    };

void export_Variant(pybind11::module& m)
    {
    pybind11::class_<Variant, VariantPy, std::shared_ptr<Variant> >(m,"Variant")
        .def(pybind11::init<>())
        .def("__call__", &Variant::operator())
        .def("min", &Variant::min)
        .def("max", &Variant::max)
        .def_property_readonly("range", &Variant::range)
        ;

    pybind11::class_<VariantConstant, Variant, std::shared_ptr<VariantConstant> >(m, "VariantConstant")
        .def(pybind11::init< Scalar >(), pybind11::arg("value"))
        .def_property("value", &VariantConstant::getValue, &VariantConstant::setValue)
        ;

    pybind11::class_<VariantRamp, Variant, std::shared_ptr<VariantRamp> >(m, "VariantRamp")
        .def(pybind11::init< Scalar, Scalar, uint64_t, uint64_t >(), pybind11::arg("A"),
                                                                     pybind11::arg("B"),
                                                                     pybind11::arg("t_start"),
                                                                     pybind11::arg("t_ramp"))
        .def_property("A", &VariantRamp::getA, &VariantRamp::setA)
        .def_property("B", &VariantRamp::getB, &VariantRamp::setB)
        .def_property("t_start", &VariantRamp::getTStart, &VariantRamp::setTStart)
        .def_property("t_ramp", &VariantRamp::getTRamp, &VariantRamp::setTRamp)
        ;

    pybind11::class_<VariantCycle, Variant, std::shared_ptr<VariantCycle> >(m, "VariantCycle")
        .def(pybind11::init< Scalar,
                             Scalar,
                             uint64_t,
                             uint64_t,
                             uint64_t,
                             uint64_t,
                             uint64_t >(),
            pybind11::arg("A"),
            pybind11::arg("B"),
            pybind11::arg("t_start"),
            pybind11::arg("t_A"),
            pybind11::arg("t_AB"),
            pybind11::arg("t_B"),
            pybind11::arg("t_BA"))

        .def_property("A", &VariantCycle::getA, &VariantCycle::setA)
        .def_property("B", &VariantCycle::getB, &VariantCycle::setB)
        .def_property("t_start", &VariantCycle::getTStart, &VariantCycle::setTStart)
        .def_property("t_A", &VariantCycle::getTA, &VariantCycle::setTA)
        .def_property("t_AB", &VariantCycle::getTAB, &VariantCycle::setTAB)
        .def_property("t_B", &VariantCycle::getTB, &VariantCycle::setTB)
        .def_property("t_BA", &VariantCycle::getTBA, &VariantCycle::setTBA)
        ;

    m.def("_test_variant_call", &testVariantCall);
    }
<|endoftext|>


<commit_before>#ifndef CMDPARSER_HPP
#define CMDPARSER_HPP

#include <vector>
#include <map>
#include <sstream>
#include <stdexcept>
#include "clany_macros.h"

_CLANY_BEGIN
class ParseError : public logic_error {
public:
    ParseError(const std::string& err_msg)
        : logic_error(err_msg) {
    }
};

enum { no_argument = 0, required_argument, optional_argument };

struct LongOption {
    string name;
    int    has_arg;
    int    val;
};

class CmdLineParser {
private:
    struct Option {
        char name;
        int has_arg;
    };

public:
    CmdLineParser(int argc, const char* const argv[], const string& options)
        : arg_vec(argv + 1, argv + argc), pos(0), curr_idx(0, false), next_idx(0) {
        if (argc != 1) initParser(options);
    }

    CmdLineParser(int argc, const char* const argv[],
                  const string& options,
                  const vector<LongOption>& long_options)
        : CmdLineParser(argc, argv, options) {
        // Store user provided long options
        for (const auto& lopt : long_options) {
            long_opt_map.emplace(lopt.name, lopt);
        }
    }

    // Return next option character, if no more option characters available, return -1
    char get() {
        if (pos == opt_idx_vec.size()) return -1;
        curr_idx = opt_idx_vec[pos++];

        string option = arg_vec[curr_idx.first];
        if (!curr_idx.second) {     // Short option
            if (opt_map.find(option[1]) == opt_map.end()) return '?';
            opt_char = option[1];
        } else {                    // Long option
            option = option.substr(2, option.find('=') - 2);
            if (long_opt_map.find(option) == long_opt_map.end()) return '?';
            opt_char = long_opt_map.at(option).val;
        }
        bool next_is_arg = parseArg();
        if (opt_arg == ":") return ':';

        // Update the index of next option to be processed
        if (pos != opt_idx_vec.size()) {
            next_idx = opt_idx_vec[pos].first;
        } else {
            next_idx = opt_idx_vec.back().first + (next_is_arg ? 2 : 1);
        }

        return opt_char;
    }

    // Get current option argument
    template<typename T>
    T getArg() const {
        istringstream ss(opt_arg);
        T parsed_arg;
        ss >> parsed_arg;
        return parsed_arg;
    }

    // Get the index of next option to be processed in argv
    int getIndex() const {
        return next_idx + 1;    // Program name counts 1
    }

    // Get argument of custom option
    template<typename T>
    T parse(const string& name) {
        // Save current state
        auto old_curr_idx = curr_idx;
        auto old_opt_arg  = opt_arg;

        if (!findArg(name)) throw ParseError("Argument not found or not required!");

        istringstream ss(opt_arg);
        T parsed_arg;
        ss >> parsed_arg;

        curr_idx = old_curr_idx;
        opt_arg  = old_opt_arg;

        return parsed_arg;
    }

private:
    void initParser(const string& options) {
        // Get all possible options
        char last_opt;
        for (const auto& opt : options) {
            if (opt != ':') {
                opt_map.emplace(opt, Option {opt, 0}), last_opt = opt;
            } else {
                opt_map.at(last_opt).has_arg++;
            }
        }
        // Extract all options and long options in arg_vec, store index
        for (size_t i = 0; i < arg_vec.size(); ++i) {
            const string& arg = arg_vec[i];
            if (arg[0] == '-') opt_idx_vec.emplace_back(i, arg[1] == '-');
        }
    }

    // Return true if parsed argument is the next argument in argv, else return false
    bool parseArg() {
        string option   = arg_vec[curr_idx.first];
        string next_arg = curr_idx.first + 1 == arg_vec.size() ? "" : arg_vec[curr_idx.first + 1];
        if (curr_idx.second) {    // Long option
            option.erase(0, 2);
            opt_arg = parseArg(option, next_arg, long_opt_map.at(
                               option.substr(0, option.find('='))).has_arg);
        } else {                  // Short option
            option.erase(0, 1);
            opt_arg = parseArg(option, next_arg,
                               opt_map.at(option[0]).has_arg);
        }
        return !opt_arg.empty() && opt_arg == next_arg;
    }

    string parseArg(const string& option, const string& next, int has_argument) const {
        // Option does not require an argument
        if (!has_argument) return NULL_CHARACTER;

        size_t eq_sign_pos = option.find('=');
        string arg = eq_sign_pos == string::npos ? option.substr(1) : option.substr(eq_sign_pos + 1);
        string name = option.substr(0, eq_sign_pos);

        if (!arg.empty()) return arg;
        if (!next.empty() && next[0] != '-') return next;

        // Option require an argument but not found
        if (has_argument == 1) return ":";

        return "";
    }

    bool findArg(const string& name) {
        // Traverse all possible options
        for (const auto& idx : opt_idx_vec) {
            auto option = arg_vec[idx.first];
            // Check if match any short/long option
            if (name == option.substr(1, 1) ||
                name == option.substr(2, option.find('=') - 2)) {
                curr_idx = idx;
                parseArg();
                if (opt_arg == NULL_CHARACTER || opt_arg == ":") return false;
            }
        }
        return true;
    }

private:
    // All commandline arguments
    vector<string> arg_vec;
    // Option index in arg_vec, second element is true if it's a long option
    vector<pair<int, bool>> opt_idx_vec;
    int pos;
    pair<int, bool> curr_idx;

    // Index of next option to be processed
    int next_idx;
    // Current option
    char opt_char;
    // Argument of current option
    string opt_arg;

    // User defined available options
    map<char, Option>       opt_map;
    map<string, LongOption> long_opt_map;

    const string NULL_CHARACTER = string("\0", 1);
};

template<>
inline string CmdLineParser::getArg<string>() const {
    return opt_arg;
}

template<>
inline string CmdLineParser::parse<string>(const string& name) {
    // Save current state
    auto old_curr_idx = curr_idx;
    auto old_opt_arg  = opt_arg;

    if (!findArg(name)) throw ParseError("Argument not found or not required!");
    string parsed_arg = opt_arg;

    curr_idx = old_curr_idx;
    opt_arg  = old_opt_arg;

    return parsed_arg;
}

_CLANY_END

#endif // __CMDPARSER_HPP__
<commit_msg>Use size_t as index<commit_after>#ifndef CMDPARSER_HPP
#define CMDPARSER_HPP

#include <vector>
#include <map>
#include <sstream>
#include <stdexcept>
#include "clany_macros.h"

_CLANY_BEGIN
class ParseError : public logic_error {
public:
    ParseError(const std::string& err_msg)
        : logic_error(err_msg) {
    }
};

enum { no_argument = 0, required_argument, optional_argument };

struct LongOption {
    string name;
    int    has_arg;
    int    val;
};

class CmdLineParser {
private:
    struct Option {
        char name;
        int has_arg;
    };

public:
    CmdLineParser(int argc, const char* const argv[], const string& options)
        : arg_vec(argv + 1, argv + argc), pos(0), curr_idx(0, false), next_idx(0) {
        if (argc != 1) initParser(options);
    }

    CmdLineParser(int argc, const char* const argv[],
                  const string& options,
                  const vector<LongOption>& long_options)
        : CmdLineParser(argc, argv, options) {
        // Store user provided long options
        for (const auto& lopt : long_options) {
            long_opt_map.insert({lopt.name, lopt});
        }
    }

    // Return next option character, if no more option characters available, return -1
    char get() {
        if (pos == opt_idx_vec.size()) return -1;
        curr_idx = opt_idx_vec[pos++];

        string option = arg_vec[curr_idx.first];
        if (!curr_idx.second) {     // Short option
            if (opt_map.find(option[1]) == opt_map.end()) return '?';
            opt_char = option[1];
        } else {                    // Long option
            option = option.substr(2, option.find('=') - 2);
            if (long_opt_map.find(option) == long_opt_map.end()) return '?';
            opt_char = long_opt_map.at(option).val;
        }
        bool next_is_arg = parseArg();
        if (opt_arg == ":") return ':';

        // Update the index of next option to be processed
        if (pos != opt_idx_vec.size()) {
            next_idx = opt_idx_vec[pos].first;
        } else {
            next_idx = opt_idx_vec.back().first + (next_is_arg ? 2 : 1);
        }

        return opt_char;
    }

    // Get current option argument
    template<typename T>
    T getArg() const {
        istringstream ss(opt_arg);
        T parsed_arg;
        ss >> parsed_arg;
        return parsed_arg;
    }

    // Get the index of next option to be processed in argv
    int getIndex() const {
        return next_idx + 1;    // Program name counts 1
    }

    // Get argument of custom option
    template<typename T>
    T parse(const string& name) {
        // Save current state
        auto old_curr_idx = curr_idx;
        auto old_opt_arg  = opt_arg;

        if (!findArg(name)) throw ParseError("Argument not found or not required!");

        istringstream ss(opt_arg);
        T parsed_arg;
        ss >> parsed_arg;

        curr_idx = old_curr_idx;
        opt_arg  = old_opt_arg;

        return parsed_arg;
    }

private:
    void initParser(const string& options) {
        // Get all possible options
        char last_opt;
        for (const auto& opt : options) {
            if (opt != ':') {
                opt_map.insert({opt, Option {opt, 0}}), last_opt = opt;
            } else {
                opt_map.at(last_opt).has_arg++;
            }
        }
        // Extract all options and long options in arg_vec, store index
        for (size_t i = 0; i < arg_vec.size(); ++i) {
            const string& arg = arg_vec[i];
            if (arg[0] == '-') opt_idx_vec.emplace_back(i, arg[1] == '-');
        }
    }

    // Return true if parsed argument is the next argument in argv, else return false
    bool parseArg() {
        string option   = arg_vec[curr_idx.first];
        string next_arg = curr_idx.first + 1 == arg_vec.size() ? "" : arg_vec[curr_idx.first + 1];
        if (curr_idx.second) {    // Long option
            option.erase(0, 2);
            opt_arg = parseArg(option, next_arg, long_opt_map.at(
                               option.substr(0, option.find('='))).has_arg);
        } else {                  // Short option
            option.erase(0, 1);
            opt_arg = parseArg(option, next_arg,
                               opt_map.at(option[0]).has_arg);
        }
        return !opt_arg.empty() && opt_arg == next_arg;
    }

    string parseArg(const string& option, const string& next, int has_argument) const {
        // Option does not require an argument
        if (!has_argument) return NULL_CHARACTER;

        size_t eq_sign_pos = option.find('=');
        string arg = eq_sign_pos == string::npos ? option.substr(1) : option.substr(eq_sign_pos + 1);
        string name = option.substr(0, eq_sign_pos);

        if (!arg.empty()) return arg;
        if (!next.empty() && next[0] != '-') return next;

        // Option require an argument but not found
        if (has_argument == 1) return ":";

        return "";
    }

    bool findArg(const string& name) {
        // Traverse all possible options
        for (const auto& idx : opt_idx_vec) {
            auto option = arg_vec[idx.first];
            // Check if match any short/long option
            if (name == option.substr(1, 1) ||
                name == option.substr(2, option.find('=') - 2)) {
                curr_idx = idx;
                parseArg();
                if (opt_arg == NULL_CHARACTER || opt_arg == ":") return false;
            }
        }
        return true;
    }

private:
    // All commandline arguments
    vector<string> arg_vec;
    // Option index in arg_vec, second element is true if it's a long option
    vector<pair<size_t, bool>> opt_idx_vec;
    size_t pos;
    pair<size_t, bool> curr_idx;

    // Index of next option to be processed
    size_t next_idx;
    // Current option
    char opt_char;
    // Argument of current option
    string opt_arg;

    // User defined available options
    map<char, Option>       opt_map;
    map<string, LongOption> long_opt_map;

    const string NULL_CHARACTER = string("\0", 1);
};

template<>
inline string CmdLineParser::getArg<string>() const {
    return opt_arg;
}

template<>
inline string CmdLineParser::parse<string>(const string& name) {
    // Save current state
    auto old_curr_idx = curr_idx;
    auto old_opt_arg  = opt_arg;

    if (!findArg(name)) throw ParseError("Argument not found or not required!");
    string parsed_arg = opt_arg;

    curr_idx = old_curr_idx;
    opt_arg  = old_opt_arg;

    return parsed_arg;
}

_CLANY_END

#endif // __CMDPARSER_HPP__
<|endoftext|>


<commit_before>// Demonstration of estimating an RC network using MST
// to accompany "Analyzing On-Chip Interconnect with Modern C++"
// Author: Jeff Trull <edaskel@att.net>

/*
Copyright (c) 2014 Jeffrey E. Trull

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.
*/

#include <iostream>
#include <vector>

#include <boost/iterator/counting_iterator.hpp>
#include <boost/graph/prim_minimum_spanning_tree.hpp>
#include <boost/property_map/function_property_map.hpp>

#include "ckt_graph.h"

// Define an implicit graph class
struct pin_distance_graph {
    // For Prim we must model "Vertex List Graph" and "Incidence Graph"
    // Graph requirements
    typedef size_t vertex_descriptor;
    typedef std::pair<size_t, size_t> edge_descriptor;
    typedef boost::undirected_tag directed_category;
    typedef boost::disallow_parallel_edge_tag edge_parallel_category;
    // inherit traversal from both concepts
    struct traversal_category : virtual public boost::vertex_list_graph_tag,
                                virtual public boost::incidence_graph_tag {};

    // Vertex List Graph requirements
    typedef boost::counting_iterator<size_t> vertex_iterator;
    typedef size_t vertices_size_type;

    // Incidence Graph requirements
    // The most complicated part is generating new edges
    // We need to make a standard conforming edge iterator
    // The Boost.Iterator library can help us make a "forward iterator"
    struct  edge_iterator_t : boost::iterator_facade<edge_iterator_t,
                                                     edge_descriptor,
                                                     boost::forward_traversal_tag,
                                                     edge_descriptor const&> {
        // Implementation constrained by two things:
        // 1) We need to provide a default constructor, so sentinel cannot reference graph
        // 2) We need to provide a non-const dereference operator for Prim, so must keep
        //    a true edge descriptor around

        edge_iterator_t()
            : g_(nullptr) {}  // initialize to invalid
        edge_iterator_t(pin_distance_graph const* g, vertex_descriptor u)
            : current_edge_(std::make_pair(u, 0)), g_(g) {
            advance_to_legal();
        }
    private:
        friend class boost::iterator_core_access;
        // iterator_facade requirements:
        void increment() {
            current_edge_.second++;
            advance_to_legal();
        }
        bool equal(edge_iterator_t const& other) const {
            // true if both invalid, or they match source/dest
            if (invalid()) {
                return other.invalid();
            } else {
                return !other.invalid() && (current_edge_ == other.current_edge_);
            }
        }
        edge_descriptor const& dereference() const {
            return current_edge_;
        }
        // implementation details for above:
        bool invalid() const {
            return ((g_ == nullptr) || (current_edge_.second >= num_vertices(*g_)));
        }
        void advance_to_legal() {
            // ensure target and source are not the same
            if (current_edge_.first == current_edge_.second) {
                current_edge_.second++;
            }
        }
        edge_descriptor current_edge_;
        pin_distance_graph const* g_;
    };

    typedef edge_iterator_t out_edge_iterator;
    typedef size_t degree_size_type;

    // Vertex List Graph free functions
    friend std::pair<vertex_iterator, vertex_iterator>
    vertices(pin_distance_graph const& g) {
        return std::make_pair(vertex_iterator(0), vertex_iterator(g.points_.size()));
    }

    friend vertices_size_type num_vertices(pin_distance_graph const& g) {
        return g.points_.size();
    }

    // Incidence Graph free functions
    friend vertex_descriptor source(edge_descriptor const& e, pin_distance_graph const&) {
        return e.first;
    }

    friend vertex_descriptor target(edge_descriptor const& e, pin_distance_graph const&) {
        return e.second;
    }

    friend std::pair<out_edge_iterator, out_edge_iterator>
    out_edges(vertex_descriptor u, pin_distance_graph const& g) {
        return std::make_pair(edge_iterator_t(&g, u), edge_iterator_t());
    }

    friend degree_size_type
    out_degree(vertex_descriptor, pin_distance_graph const& g) {
        return g.points_.size() - 1;  // "complete" graph, every node connected to every other
    }

    // Main functionality
    typedef std::pair<int, int> point_t;
    template<typename PtIter>
    pin_distance_graph(PtIter beg, PtIter end) : points_(beg, end) {}

    // Provide bracket operator a la adjacency_list
    point_t operator[](vertex_descriptor u) const {
        return points_[u];
    }

private:
    std::vector<point_t> points_;

};

// Run Prim on it
// Prim seems vertex-focused, as opposed to the edge-focused Kruskal
// since our edges are implicit I favored the former

int main() {
    using namespace boost;
    using namespace std;

    // Data for implicit graph - a list of points
    vector<pair<int, int>> pinlocs =
        {{-100, -100}, {-100, 100}, {0, 0}, {100, 100}, {100, -100},
         {-50, 0}, {103, 100}, {100, 90}};

    pin_distance_graph pdg(pinlocs.begin(), pinlocs.end());

    // Create Prim requirements (temporary data structures used in algorithm)

    // vertex index map.  Since we use size_t for vertex descriptors, they
    // are immediately usable as indices:
    auto vindex_map = typed_identity_property_map<size_t>();

    // Predecessor map
    vector<pin_distance_graph::vertex_descriptor> predvec(num_vertices(pdg));   // underlying storage
    auto predpmap = make_iterator_property_map(predvec.begin(), vindex_map);

    // Weight Map
    // Another case where we can be "implicit" - the weight of an edge is
    // the Manhattan distance between the vertices
    typedef pin_distance_graph::edge_descriptor edge_t;
    auto weightpmap = make_function_property_map<edge_t>(
        [&pdg](edge_t e) -> int {
            auto coord1 = pdg[source(e, pdg)];
            auto coord2 = pdg[target(e, pdg)];
            return abs(coord1.first - coord2.first) + abs(coord1.second - coord2.second);
        });

    // call Prim
    prim_minimum_spanning_tree(pdg, predpmap,
                               weight_map(weightpmap).
                               vertex_index_map(vindex_map));

    // produce output as SVG
    cout << "<svg xmlns=\"http://www.w3.org/2000/svg\"" << endl;
    cout << "     xmlns:xlink=\"http://www.w3.org/1999/xlink\">" << endl;
    auto vitpair = vertices(pdg);
    for (auto v : make_iterator_range(vitpair.first, vitpair.second)) {
        // SVG wants positive numbers, and the Y coordinate is canvas style (reversed from Cartesian)
        // so we will hack the numbers a bit to make it match our inputs:
        // scale by 3X, mirror Y axis, and add 600 to both
        int x2 = 400 + 2*pdg[v].first;
        int y2 = 400 - 2*pdg[v].second;
        if (predvec[v] == v) {
            // Root.  Make a red circle
            cout << "    <circle cx=\"" << x2 << "\" cy=\"" << y2 << "\" r=\"10\" style=\"fill:#cc0000\"/>" << endl;
        } else {
            // Not Root: a gray one
            cout << "    <circle cx=\"" << x2 << "\" cy=\"" << y2 << "\" r=\"10\" style=\"fill:#cccccc; stroke:#222222\"/>" << endl;
        }
    }
    // Go back and add lines on top of the circles (for visibility)
    for (auto v : make_iterator_range(vitpair.first, vitpair.second)) {
        int x2 = 400 + 2*pdg[v].first;
        int y2 = 400 - 2*pdg[v].second;
        if (predvec[v] != v) {
            int x1 = 400 + 2*pdg[predvec[v]].first;
            int y1 = 400 - 2*pdg[predvec[v]].second;
            // Make a line to the successor node
            cout << "    <line x1=\"" << x1 << "\" y1=\"" << y1 << "\" x2=\"" << x2 << "\" y2=\"" << y2 << "\" style=\"stroke:#666666; stroke-width:3px\"/>" << endl;
        }
    }
    cout << "</svg>" << endl;

    // Future work: turn resulting tree into an implicit graph compatible with ckt_graph_t,
    // with estimated RC values on edges

}
<commit_msg>improve explanation of SVG transform<commit_after>// Demonstration of estimating an RC network using MST
// to accompany "Analyzing On-Chip Interconnect with Modern C++"
// Author: Jeff Trull <edaskel@att.net>

/*
Copyright (c) 2014 Jeffrey E. Trull

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.
*/

#include <iostream>
#include <vector>

#include <boost/iterator/counting_iterator.hpp>
#include <boost/graph/prim_minimum_spanning_tree.hpp>
#include <boost/property_map/function_property_map.hpp>

#include "ckt_graph.h"

// Define an implicit graph class
struct pin_distance_graph {
    // For Prim we must model "Vertex List Graph" and "Incidence Graph"
    // Graph requirements
    typedef size_t vertex_descriptor;
    typedef std::pair<size_t, size_t> edge_descriptor;
    typedef boost::undirected_tag directed_category;
    typedef boost::disallow_parallel_edge_tag edge_parallel_category;
    // inherit traversal from both concepts
    struct traversal_category : virtual public boost::vertex_list_graph_tag,
                                virtual public boost::incidence_graph_tag {};

    // Vertex List Graph requirements
    typedef boost::counting_iterator<size_t> vertex_iterator;
    typedef size_t vertices_size_type;

    // Incidence Graph requirements
    // The most complicated part is generating new edges
    // We need to make a standard conforming edge iterator
    // The Boost.Iterator library can help us make a "forward iterator"
    struct  edge_iterator_t : boost::iterator_facade<edge_iterator_t,
                                                     edge_descriptor,
                                                     boost::forward_traversal_tag,
                                                     edge_descriptor const&> {
        // Implementation constrained by two things:
        // 1) We need to provide a default constructor, so sentinel cannot reference graph
        // 2) We need to provide a non-const dereference operator for Prim, so must keep
        //    a true edge descriptor around

        edge_iterator_t()
            : g_(nullptr) {}  // initialize to invalid
        edge_iterator_t(pin_distance_graph const* g, vertex_descriptor u)
            : current_edge_(std::make_pair(u, 0)), g_(g) {
            advance_to_legal();
        }
    private:
        friend class boost::iterator_core_access;
        // iterator_facade requirements:
        void increment() {
            current_edge_.second++;
            advance_to_legal();
        }
        bool equal(edge_iterator_t const& other) const {
            // true if both invalid, or they match source/dest
            if (invalid()) {
                return other.invalid();
            } else {
                return !other.invalid() && (current_edge_ == other.current_edge_);
            }
        }
        edge_descriptor const& dereference() const {
            return current_edge_;
        }
        // implementation details for above:
        bool invalid() const {
            return ((g_ == nullptr) || (current_edge_.second >= num_vertices(*g_)));
        }
        void advance_to_legal() {
            // ensure target and source are not the same
            if (current_edge_.first == current_edge_.second) {
                current_edge_.second++;
            }
        }
        edge_descriptor current_edge_;
        pin_distance_graph const* g_;
    };

    typedef edge_iterator_t out_edge_iterator;
    typedef size_t degree_size_type;

    // Vertex List Graph free functions
    friend std::pair<vertex_iterator, vertex_iterator>
    vertices(pin_distance_graph const& g) {
        return std::make_pair(vertex_iterator(0), vertex_iterator(g.points_.size()));
    }

    friend vertices_size_type num_vertices(pin_distance_graph const& g) {
        return g.points_.size();
    }

    // Incidence Graph free functions
    friend vertex_descriptor source(edge_descriptor const& e, pin_distance_graph const&) {
        return e.first;
    }

    friend vertex_descriptor target(edge_descriptor const& e, pin_distance_graph const&) {
        return e.second;
    }

    friend std::pair<out_edge_iterator, out_edge_iterator>
    out_edges(vertex_descriptor u, pin_distance_graph const& g) {
        return std::make_pair(edge_iterator_t(&g, u), edge_iterator_t());
    }

    friend degree_size_type
    out_degree(vertex_descriptor, pin_distance_graph const& g) {
        return g.points_.size() - 1;  // "complete" graph, every node connected to every other
    }

    // Main functionality
    typedef std::pair<int, int> point_t;
    template<typename PtIter>
    pin_distance_graph(PtIter beg, PtIter end) : points_(beg, end) {}

    // Provide bracket operator a la adjacency_list
    point_t operator[](vertex_descriptor u) const {
        return points_[u];
    }

private:
    std::vector<point_t> points_;

};

// Run Prim on it
// Prim seems vertex-focused, as opposed to the edge-focused Kruskal
// since our edges are implicit I favored the former

int main() {
    using namespace boost;
    using namespace std;

    // Data for implicit graph - a list of points
    vector<pair<int, int>> pinlocs =
        {{-100, -100}, {-100, 100}, {0, 0}, {100, 100}, {100, -100},
         {-50, 0}, {103, 100}, {100, 90}};

    pin_distance_graph pdg(pinlocs.begin(), pinlocs.end());

    // Create Prim requirements (temporary data structures used in algorithm)

    // vertex index map.  Since we use size_t for vertex descriptors, they
    // are immediately usable as indices:
    auto vindex_map = typed_identity_property_map<size_t>();

    // Predecessor map
    vector<pin_distance_graph::vertex_descriptor> predvec(num_vertices(pdg));   // underlying storage
    auto predpmap = make_iterator_property_map(predvec.begin(), vindex_map);

    // Weight Map
    // Another case where we can be "implicit" - the weight of an edge is
    // the Manhattan distance between the vertices
    typedef pin_distance_graph::edge_descriptor edge_t;
    auto weightpmap = make_function_property_map<edge_t>(
        [&pdg](edge_t e) -> int {
            auto coord1 = pdg[source(e, pdg)];
            auto coord2 = pdg[target(e, pdg)];
            return abs(coord1.first - coord2.first) + abs(coord1.second - coord2.second);
        });

    // call Prim
    prim_minimum_spanning_tree(pdg, predpmap,
                               weight_map(weightpmap).
                               vertex_index_map(vindex_map));

    // produce output as SVG
    cout << "<svg xmlns=\"http://www.w3.org/2000/svg\"" << endl;
    cout << "     xmlns:xlink=\"http://www.w3.org/1999/xlink\">" << endl;
    auto vitpair = vertices(pdg);
    for (auto v : make_iterator_range(vitpair.first, vitpair.second)) {
        // SVG wants positive numbers, and the Y coordinate is canvas style (reversed from Cartesian)
        // so we will transform the data en route to the display for viewability:
        // scale by 2X, mirror Y axis, and add 400 to both
        int x2 = 400 + 2*pdg[v].first;
        int y2 = 400 - 2*pdg[v].second;
        if (predvec[v] == v) {
            // Root.  Make a red circle
            cout << "    <circle cx=\"" << x2 << "\" cy=\"" << y2 << "\" r=\"10\" style=\"fill:#cc0000\"/>" << endl;
        } else {
            // Not Root: a gray one
            cout << "    <circle cx=\"" << x2 << "\" cy=\"" << y2 << "\" r=\"10\" style=\"fill:#cccccc; stroke:#222222\"/>" << endl;
        }
    }
    // Go back and add lines on top of the circles (for visibility)
    for (auto v : make_iterator_range(vitpair.first, vitpair.second)) {
        int x2 = 400 + 2*pdg[v].first;
        int y2 = 400 - 2*pdg[v].second;
        if (predvec[v] != v) {
            int x1 = 400 + 2*pdg[predvec[v]].first;
            int y1 = 400 - 2*pdg[predvec[v]].second;
            // Make a line to the successor node
            cout << "    <line x1=\"" << x1 << "\" y1=\"" << y1 << "\" x2=\"" << x2 << "\" y2=\"" << y2 << "\" style=\"stroke:#666666; stroke-width:3px\"/>" << endl;
        }
    }
    cout << "</svg>" << endl;

    // Future work: turn resulting tree into an implicit graph compatible with ckt_graph_t,
    // with estimated RC values on edges

}
<|endoftext|>


<commit_before><commit_msg>FENNEL:  when CHECKPOINT_DISCARD is used for forceTxns rollback,  don't truncate txn list<commit_after><|endoftext|>


<commit_before>/*
    This file is part of Kontact.
    Copyright (c) 2003 Tobias Koenig <tokoe@kde.org>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

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

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

    As a special exception, permission is given to link this program
    with any edition of Qt, and distribute the resulting executable,
    without including the source code for Qt in the source distribution.
*/

#include <qcursor.h>
#include <qlabel.h>
#include <qlayout.h>
#include <qtooltip.h>

#include <kdialog.h>
#include <kglobal.h>
#include <kiconloader.h>
#include <klocale.h>
#include <kparts/part.h>
#include <kpopupmenu.h>
#include <kstandarddirs.h>
#include <kurllabel.h>
#include <libkcal/event.h>
#include <libkcal/resourcecalendar.h>
#include <libkcal/resourcelocal.h>
#include <libkcal/incidenceformatter.h>
#include <libkdepim/kpimprefs.h>

#include "korganizeriface_stub.h"

#include "core.h"
#include "plugin.h"
#include "korganizerplugin.h"

#include "korganizer/stdcalendar.h"

#include "summarywidget.h"

SummaryWidget::SummaryWidget( KOrganizerPlugin *plugin, QWidget *parent,
                              const char *name )
  : Kontact::Summary( parent, name ), mPlugin( plugin ), mCalendar( 0 )
{
  QVBoxLayout *mainLayout = new QVBoxLayout( this, 3, 3 );

  QPixmap icon = KGlobal::iconLoader()->loadIcon( "kontact_date",
                   KIcon::Desktop, KIcon::SizeMedium );
  QWidget *header = createHeader( this, icon, i18n( "Calendar" ) );
  mainLayout->addWidget( header );

  mLayout = new QGridLayout( mainLayout, 7, 5, 3 );
  mLayout->setRowStretch( 6, 1 );

  mCalendar = KOrg::StdCalendar::self();
  mCalendar->load();

  connect( mCalendar, SIGNAL( calendarChanged() ), SLOT( updateView() ) );
  connect( mPlugin->core(), SIGNAL( dayChanged( const QDate& ) ),
           SLOT( updateView() ) );

  updateView();
}

SummaryWidget::~SummaryWidget()
{
}

void SummaryWidget::updateView()
{
  mLabels.setAutoDelete( true );
  mLabels.clear();
  mLabels.setAutoDelete( false );

  KIconLoader loader( "kdepim" );

  KConfig config( "kcmkorgsummaryrc" );

  config.setGroup( "Calendar" );
  int days = config.readNumEntry( "DaysToShow", 1 );

  QLabel *label = 0;
  int counter = 0;
  QPixmap pm = loader.loadIcon( "appointment", KIcon::Small );
  QPixmap pmb = loader.loadIcon( "calendarbirthday", KIcon::Small );
  QPixmap pma = loader.loadIcon( "calendaranniversary", KIcon::Small );

  QDate dt;
  QDate currentDate = QDate::currentDate();
  for ( dt=currentDate;
        dt<=currentDate.addDays( days - 1 );
        dt=dt.addDays(1) ) {

    KCal::Event *ev;

    KCal::Event::List events = mCalendar->events( dt );
    KCal::Event::List::ConstIterator it = events.begin();

    QDateTime qdt;

    // sort the events for this date by summary
    events = KCal::Calendar::sortEvents( &events,
                                         KCal::EventSortSummary,
                                         KCal::SortDirectionAscending );
    // sort the events for this date by start date
    events = KCal::Calendar::sortEvents( &events,
                                         KCal::EventSortStartDate,
                                         KCal::SortDirectionAscending );

    for ( it=events.begin(); it!=events.end(); ++it ) {
      ev = *it;

      // Count number of days remaining in multiday event
      int span=1; int dayof=1;
      if ( ev->isMultiDay() ) {
        QDate d = ev->dtStart().date();
        if ( d < currentDate ) {
          d = currentDate;
        }
        while ( d < ev->dtEnd().date() ) {
          if ( d < dt ) {
            dayof++;
          }
          span++;
          d=d.addDays( 1 );
        }
      }

      // If this date is part of a floating, multiday event, then we
      // only make a print for the first day of the event.
      if ( ev->isMultiDay() && ev->doesFloat() && dayof != 1 ) continue;

      // Fill Appointment Pixmap Field
      label = new QLabel( this );
      if ( ev->categories().contains( "Birthday" ) ) {
        label->setPixmap( pmb );
      } else if ( ev->categories().contains( "Anniversary" ) ) {
        label->setPixmap( pma );
      } else {
        label->setPixmap( pm );
      }
      label->setMaximumWidth( label->minimumSizeHint().width() );
      label->setAlignment( AlignVCenter );
      mLayout->addWidget( label, counter, 0 );
      mLabels.append( label );

      // Fill Event Date Field
      bool makeBold = false;
      QString datestr;

      // Modify event date for printing
      QDate sD = QDate::QDate( dt.year(), dt.month(), dt.day() );
      if ( ( sD.month() == currentDate.month() ) &&
           ( sD.day()   == currentDate.day() ) ) {
        datestr = i18n( "Today" );
        makeBold = true;
      } else if ( ( sD.month() == currentDate.addDays( 1 ).month() ) &&
                  ( sD.day()   == currentDate.addDays( 1 ).day() ) ) {
        datestr = i18n( "Tomorrow" );
      } else {
        datestr = KGlobal::locale()->formatDate( sD );
      }

      // Print the date span for multiday, floating events, for the
      // first day of the event only.
      if ( ev->isMultiDay() && ev->doesFloat() && dayof == 1 && span > 1 ) {
        datestr = KGlobal::locale()->formatDate( ev->dtStart().date() );
        datestr += " -\n " +
                   KGlobal::locale()->formatDate( sD.addDays( span-1 ) );
      }

      label = new QLabel( datestr, this );
      label->setAlignment( AlignLeft | AlignVCenter );
      if ( makeBold ) {
        QFont font = label->font();
        font.setBold( true );
        label->setFont( font );
      }
      mLayout->addWidget( label, counter, 1 );
      mLabels.append( label );

      // Fill Event Summary Field
      QString newtext = ev->summary();
      if ( ev->isMultiDay() &&  !ev->doesFloat() ) {
        newtext.append( QString(" (%1/%2)").arg( dayof ).arg( span ) );
      }

      KURLLabel *urlLabel = new KURLLabel( this );
      urlLabel->setText( newtext );
      urlLabel->setURL( ev->uid() );
      urlLabel->installEventFilter( this );
      urlLabel->setAlignment( urlLabel->alignment() | Qt::WordBreak );
      mLayout->addWidget( urlLabel, counter, 2 );
      mLabels.append( urlLabel );

      connect( urlLabel, SIGNAL( leftClickedURL( const QString& ) ),
               this, SLOT( viewEvent( const QString& ) ) );
      connect( urlLabel, SIGNAL( rightClickedURL( const QString& ) ),
               this, SLOT( popupMenu( const QString& ) ) );

      QString tipText( KCal::IncidenceFormatter::toolTipStr( mCalendar, ev, dt, true ) );
      if ( !tipText.isEmpty() ) {
        QToolTip::add( urlLabel, tipText );
      }

      // Fill Event Time Range Field (only for non-floating Events)
      if ( !ev->doesFloat() ) {
        QTime sST = ev->dtStart().time();
        QTime sET = ev->dtEnd().time();
        if ( ev->isMultiDay() ) {
          if ( ev->dtStart().date() < dt ) {
            sST = QTime::QTime( 0, 0 );
          }
          if ( ev->dtEnd().date() > dt ) {
            sET = QTime::QTime( 23, 59 );
          }
        }
        datestr = i18n( "Time from - to", "%1 - %2" )
                  .arg( KGlobal::locale()->formatTime( sST ) )
                  .arg( KGlobal::locale()->formatTime( sET ) );
        label = new QLabel( datestr, this );
        label->setAlignment( AlignLeft | AlignVCenter );
        mLayout->addWidget( label, counter, 3 );
        mLabels.append( label );
      }

      counter++;
    }
  }

  if ( !counter ) {
    QLabel *noEvents = new QLabel(
      i18n( "No appointments pending within the next day",
            "No appointments pending within the next %n days",
            days ), this, "nothing to see" );
    noEvents->setAlignment( AlignHCenter | AlignVCenter );
    mLayout->addWidget( noEvents, 0, 2 );
    mLabels.append( noEvents );
  }

  for ( label = mLabels.first(); label; label = mLabels.next() )
    label->show();
}

void SummaryWidget::viewEvent( const QString &uid )
{
  mPlugin->core()->selectPlugin( "kontact_korganizerplugin" ); //ensure loaded
  KOrganizerIface_stub iface( "korganizer", "KOrganizerIface" );
  iface.editIncidence( uid );
}

void SummaryWidget::removeEvent( const QString &uid )
{
  mPlugin->core()->selectPlugin( "kontact_korganizerplugin" ); //ensure loaded
  KOrganizerIface_stub iface( "korganizer", "KOrganizerIface" );
  iface.deleteIncidence( uid, false );
}

void SummaryWidget::popupMenu( const QString &uid )
{
  KPopupMenu popup( this );
  QToolTip::remove( this );
  popup.insertItem( i18n( "&Edit Appointment..." ), 0 );
  popup.insertItem( KGlobal::iconLoader()->loadIcon( "editdelete", KIcon::Small),
                    i18n( "&Delete Appointment" ), 1 );

  switch ( popup.exec( QCursor::pos() ) ) {
    case 0:
      viewEvent( uid );
      break;
    case 1:
      removeEvent( uid );
      break;
  }
}

bool SummaryWidget::eventFilter( QObject *obj, QEvent* e )
{
  if ( obj->inherits( "KURLLabel" ) ) {
    KURLLabel* label = static_cast<KURLLabel*>( obj );
    if ( e->type() == QEvent::Enter )
      emit message( i18n( "Edit Appointment: \"%1\"" ).arg( label->text() ) );
    if ( e->type() == QEvent::Leave )
      emit message( QString::null );
  }

  return Kontact::Summary::eventFilter( obj, e );
}

QStringList SummaryWidget::configModules() const
{
  return QStringList( "kcmkorgsummary.desktop" );
}

#include "summarywidget.moc"
<commit_msg>Do not load the calendar because it's already loaded in Korganizer. Probable fix for kolab/issue3065. MERGE: none (already in e4 and trunk)<commit_after>/*
    This file is part of Kontact.
    Copyright (c) 2003 Tobias Koenig <tokoe@kde.org>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

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

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

    As a special exception, permission is given to link this program
    with any edition of Qt, and distribute the resulting executable,
    without including the source code for Qt in the source distribution.
*/

#include <qcursor.h>
#include <qlabel.h>
#include <qlayout.h>
#include <qtooltip.h>

#include <kdialog.h>
#include <kglobal.h>
#include <kiconloader.h>
#include <klocale.h>
#include <kparts/part.h>
#include <kpopupmenu.h>
#include <kstandarddirs.h>
#include <kurllabel.h>
#include <libkcal/event.h>
#include <libkcal/resourcecalendar.h>
#include <libkcal/resourcelocal.h>
#include <libkcal/incidenceformatter.h>
#include <libkdepim/kpimprefs.h>

#include "korganizeriface_stub.h"

#include "core.h"
#include "plugin.h"
#include "korganizerplugin.h"

#include "korganizer/stdcalendar.h"

#include "summarywidget.h"

SummaryWidget::SummaryWidget( KOrganizerPlugin *plugin, QWidget *parent,
                              const char *name )
  : Kontact::Summary( parent, name ), mPlugin( plugin ), mCalendar( 0 )
{
  QVBoxLayout *mainLayout = new QVBoxLayout( this, 3, 3 );

  QPixmap icon = KGlobal::iconLoader()->loadIcon( "kontact_date",
                   KIcon::Desktop, KIcon::SizeMedium );
  QWidget *header = createHeader( this, icon, i18n( "Calendar" ) );
  mainLayout->addWidget( header );

  mLayout = new QGridLayout( mainLayout, 7, 5, 3 );
  mLayout->setRowStretch( 6, 1 );

  mCalendar = KOrg::StdCalendar::self();

  connect( mCalendar, SIGNAL( calendarChanged() ), SLOT( updateView() ) );
  connect( mPlugin->core(), SIGNAL( dayChanged( const QDate& ) ),
           SLOT( updateView() ) );

  updateView();
}

SummaryWidget::~SummaryWidget()
{
}

void SummaryWidget::updateView()
{
  mLabels.setAutoDelete( true );
  mLabels.clear();
  mLabels.setAutoDelete( false );

  KIconLoader loader( "kdepim" );

  KConfig config( "kcmkorgsummaryrc" );

  config.setGroup( "Calendar" );
  int days = config.readNumEntry( "DaysToShow", 1 );

  QLabel *label = 0;
  int counter = 0;
  QPixmap pm = loader.loadIcon( "appointment", KIcon::Small );
  QPixmap pmb = loader.loadIcon( "calendarbirthday", KIcon::Small );
  QPixmap pma = loader.loadIcon( "calendaranniversary", KIcon::Small );

  QDate dt;
  QDate currentDate = QDate::currentDate();
  for ( dt=currentDate;
        dt<=currentDate.addDays( days - 1 );
        dt=dt.addDays(1) ) {

    KCal::Event::List events = mCalendar->events( dt );

    // sort the events for this date by summary
    events = KCal::Calendar::sortEvents( &events,
                                         KCal::EventSortSummary,
                                         KCal::SortDirectionAscending );
    // sort the events for this date by start date
    events = KCal::Calendar::sortEvents( &events,
                                         KCal::EventSortStartDate,
                                         KCal::SortDirectionAscending );

    KCal::Event::List::ConstIterator it = events.begin();
    for ( it=events.begin(); it!=events.end(); ++it ) {
      KCal::Event *ev = *it;

      // Count number of days remaining in multiday event
      int span=1; int dayof=1;
      if ( ev->isMultiDay() ) {
        QDate d = ev->dtStart().date();
        if ( d < currentDate ) {
          d = currentDate;
        }
        while ( d < ev->dtEnd().date() ) {
          if ( d < dt ) {
            dayof++;
          }
          span++;
          d=d.addDays( 1 );
        }
      }

      // If this date is part of a floating, multiday event, then we
      // only make a print for the first day of the event.
      if ( ev->isMultiDay() && ev->doesFloat() && dayof != 1 ) continue;

      // Fill Appointment Pixmap Field
      label = new QLabel( this );
      if ( ev->categories().contains( "Birthday" ) ) {
        label->setPixmap( pmb );
      } else if ( ev->categories().contains( "Anniversary" ) ) {
        label->setPixmap( pma );
      } else {
        label->setPixmap( pm );
      }
      label->setMaximumWidth( label->minimumSizeHint().width() );
      label->setAlignment( AlignVCenter );
      mLayout->addWidget( label, counter, 0 );
      mLabels.append( label );

      // Fill Event Date Field
      bool makeBold = false;
      QString datestr;

      // Modify event date for printing
      QDate sD = QDate::QDate( dt.year(), dt.month(), dt.day() );
      if ( ( sD.month() == currentDate.month() ) &&
           ( sD.day()   == currentDate.day() ) ) {
        datestr = i18n( "Today" );
        makeBold = true;
      } else if ( ( sD.month() == currentDate.addDays( 1 ).month() ) &&
                  ( sD.day()   == currentDate.addDays( 1 ).day() ) ) {
        datestr = i18n( "Tomorrow" );
      } else {
        datestr = KGlobal::locale()->formatDate( sD );
      }

      // Print the date span for multiday, floating events, for the
      // first day of the event only.
      if ( ev->isMultiDay() && ev->doesFloat() && dayof == 1 && span > 1 ) {
        datestr = KGlobal::locale()->formatDate( ev->dtStart().date() );
        datestr += " -\n " +
                   KGlobal::locale()->formatDate( sD.addDays( span-1 ) );
      }

      label = new QLabel( datestr, this );
      label->setAlignment( AlignLeft | AlignVCenter );
      if ( makeBold ) {
        QFont font = label->font();
        font.setBold( true );
        label->setFont( font );
      }
      mLayout->addWidget( label, counter, 1 );
      mLabels.append( label );

      // Fill Event Summary Field
      QString newtext = ev->summary();
      if ( ev->isMultiDay() &&  !ev->doesFloat() ) {
        newtext.append( QString(" (%1/%2)").arg( dayof ).arg( span ) );
      }

      KURLLabel *urlLabel = new KURLLabel( this );
      urlLabel->setText( newtext );
      urlLabel->setURL( ev->uid() );
      urlLabel->installEventFilter( this );
      urlLabel->setAlignment( urlLabel->alignment() | Qt::WordBreak );
      mLayout->addWidget( urlLabel, counter, 2 );
      mLabels.append( urlLabel );

      connect( urlLabel, SIGNAL( leftClickedURL( const QString& ) ),
               this, SLOT( viewEvent( const QString& ) ) );
      connect( urlLabel, SIGNAL( rightClickedURL( const QString& ) ),
               this, SLOT( popupMenu( const QString& ) ) );

      QString tipText( KCal::IncidenceFormatter::toolTipStr( mCalendar, ev, dt, true ) );
      if ( !tipText.isEmpty() ) {
        QToolTip::add( urlLabel, tipText );
      }

      // Fill Event Time Range Field (only for non-floating Events)
      if ( !ev->doesFloat() ) {
        QTime sST = ev->dtStart().time();
        QTime sET = ev->dtEnd().time();
        if ( ev->isMultiDay() ) {
          if ( ev->dtStart().date() < dt ) {
            sST = QTime::QTime( 0, 0 );
          }
          if ( ev->dtEnd().date() > dt ) {
            sET = QTime::QTime( 23, 59 );
          }
        }
        datestr = i18n( "Time from - to", "%1 - %2" )
                  .arg( KGlobal::locale()->formatTime( sST ) )
                  .arg( KGlobal::locale()->formatTime( sET ) );
        label = new QLabel( datestr, this );
        label->setAlignment( AlignLeft | AlignVCenter );
        mLayout->addWidget( label, counter, 3 );
        mLabels.append( label );
      }

      counter++;
    }
  }

  if ( !counter ) {
    QLabel *noEvents = new QLabel(
      i18n( "No appointments pending within the next day",
            "No appointments pending within the next %n days",
            days ), this, "nothing to see" );
    noEvents->setAlignment( AlignHCenter | AlignVCenter );
    mLayout->addWidget( noEvents, 0, 2 );
    mLabels.append( noEvents );
  }

  for ( label = mLabels.first(); label; label = mLabels.next() )
    label->show();
}

void SummaryWidget::viewEvent( const QString &uid )
{
  mPlugin->core()->selectPlugin( "kontact_korganizerplugin" ); //ensure loaded
  KOrganizerIface_stub iface( "korganizer", "KOrganizerIface" );
  iface.editIncidence( uid );
}

void SummaryWidget::removeEvent( const QString &uid )
{
  mPlugin->core()->selectPlugin( "kontact_korganizerplugin" ); //ensure loaded
  KOrganizerIface_stub iface( "korganizer", "KOrganizerIface" );
  iface.deleteIncidence( uid, false );
}

void SummaryWidget::popupMenu( const QString &uid )
{
  KPopupMenu popup( this );
  QToolTip::remove( this );
  popup.insertItem( i18n( "&Edit Appointment..." ), 0 );
  popup.insertItem( KGlobal::iconLoader()->loadIcon( "editdelete", KIcon::Small),
                    i18n( "&Delete Appointment" ), 1 );

  switch ( popup.exec( QCursor::pos() ) ) {
    case 0:
      viewEvent( uid );
      break;
    case 1:
      removeEvent( uid );
      break;
  }
}

bool SummaryWidget::eventFilter( QObject *obj, QEvent* e )
{
  if ( obj->inherits( "KURLLabel" ) ) {
    KURLLabel* label = static_cast<KURLLabel*>( obj );
    if ( e->type() == QEvent::Enter )
      emit message( i18n( "Edit Appointment: \"%1\"" ).arg( label->text() ) );
    if ( e->type() == QEvent::Leave )
      emit message( QString::null );
  }

  return Kontact::Summary::eventFilter( obj, e );
}

QStringList SummaryWidget::configModules() const
{
  return QStringList( "kcmkorgsummary.desktop" );
}

#include "summarywidget.moc"
<|endoftext|>