<commit_before>// file PWScore.cpp
//-----------------------------------------------------------------------------

#if (defined(_MSC_VER) && (_MSC_VER >= 1300))
#include <fstream>
#include <string>
#include <vector>
#include <iostream>
using namespace std;
#else
#include <fstream.h> // for WritePlaintextFile
#include <vector.h>
#include <iostream.h>
#endif

#include "PWScore.h"
#include "BlowFish.h"

PWScore::PWScore() : m_currfile(_T("")), m_changed(false),
		     m_usedefuser(false), m_defusername(_T("")),
		     m_ReadFileVersion(PWSfile::UNKNOWN_VERSION),
		     m_passkey(NULL), m_passkey_len(0)
{
  int i;

  srand((unsigned)time(NULL));
  CItemData::SetSessionKey(); // per-session initialization
  for (i = 0; i < sizeof(m_session_key); i++)
    m_session_key[i] = newrand();
  for (i = 0; i < sizeof(m_session_salt); i++)
    m_session_salt[i] = newrand();

}

PWScore::~PWScore()
{
  if (m_passkey_len > 0) {
    trashMemory(m_passkey, ((m_passkey_len + 7)/8)*8);
    delete[] m_passkey;
  }
}

void
PWScore::ClearData(void)
{
  if (m_passkey_len > 0) {
    trashMemory(m_passkey, ((m_passkey_len + 7)/8)*8);
    delete[] m_passkey;
    m_passkey_len = 0;
  }
  //Composed of ciphertext, so doesn't need to be overwritten
  m_pwlist.RemoveAll();
}

void
PWScore::NewFile(const CMyString &passkey)
{
   ClearData();
   SetPassKey(passkey);
   m_changed = false;
}

int
PWScore::WriteFile(const CMyString &filename, PWSfile::VERSION version)
{
  PWSfile out(filename, GetPassKey());

  int status;

  status = out.OpenWriteFile(version);

  if (status != PWSfile::SUCCESS)
    return CANT_OPEN_FILE;

  CItemData temp;
  POSITION listPos = m_pwlist.GetHeadPosition();
  while (listPos != NULL)
    {
      temp = m_pwlist.GetAt(listPos);
      out.WriteRecord(temp);
      m_pwlist.GetNext(listPos);
    }
  out.CloseFile();

  m_changed = FALSE;
  m_ReadFileVersion = version; // needed when saving a V17 as V20 1st time [871893]

  return SUCCESS;
}

int
PWScore::WritePlaintextFile(const CMyString &filename)
{
  ofstream of(filename);

  if (!of)
    return CANT_OPEN_FILE;

  CItemData temp;
  POSITION listPos = m_pwlist.GetHeadPosition();
  while (listPos != NULL)
    {
      temp = m_pwlist.GetAt(listPos);
      of << temp.GetPlaintext('\t') << endl;
      m_pwlist.GetNext(listPos);
    }
  of.close();

  return SUCCESS;
}

/*
int
PWScore::WriteXMLFile(const CMyString &filename)
{
  ofstream of(filename);

  if (!of)
    return CANT_OPEN_FILE;

  of << "<?xml version=\"1.0\">" << endl;
  of << "<passwordsafe>" << endl;
  POSITION listPos = m_pwlist.GetHeadPosition();
  while (listPos != NULL)
    {
      CItemData temp = m_pwlist.GetAt(listPos);

      of << "  <entry>" << endl;
      // TODO: need to handle entity escaping of values.
      of << "    <group>" << temp.GetGroup() << "</group>" << endl;
      of << "    <title>" << temp.GetTitle() << "</title>" << endl;
      of << "    <username>" << temp.GetUser() << "</username>" << endl;
      of << "    <password>" << temp.GetPassword() << "</password>" << endl;
      of << "    <notes>" << temp.GetNotes() << "</notes>" << endl;
      of << "  </entry>" << endl;

      m_pwlist.GetNext(listPos);
    }
  of << "</passwordsafe>" << endl;
  of.close();

  return SUCCESS;
}
*/

int
PWScore::ImportPlaintextFile(const CMyString &filename)
{
    static const char *ImportedPrefix = { "Imported" };
    ifstream ifs(filename);

    if (!ifs)
        return CANT_OPEN_FILE;

    for (;;)
    {
        // read a single line.
        string linebuf;
        if (!getline(ifs, linebuf, '\n')) break;

        // remove MS-DOS linebreaks, if needed.
        if (!linebuf.empty() && *(linebuf.end() - 1) == '\r') {
            linebuf.resize(linebuf.size() - 1);
        }

        // tokenize into separate elements
        vector<string> tokens;
        for (int startpos = 0; ; ) {
            int nextchar = linebuf.find_first_of('\t', startpos);
            if (nextchar >= 0 && tokens.size() < 3) {
                tokens.push_back(linebuf.substr(startpos, nextchar - startpos));
                startpos = nextchar + 1;
            } else {
                tokens.push_back(linebuf.substr(startpos));
                break;
            }
        }
        if (tokens.size() != 4) {
            ASSERT(0 && "did not find expected number of tokens");
            break;
        }

        // Start initializing the new record.
        CItemData temp;
        temp.CreateUUID();
        temp.SetUser(CMyString(tokens[1].c_str()));
        temp.SetPassword(CMyString(tokens[2].c_str()));

        // The group and title field are concatenated.
        const string &grouptitle = tokens[0];
        int lastdot = grouptitle.find_last_of('.');
        if (lastdot > 0) {
            CMyString newgroup(ImportedPrefix);
            newgroup += ".";
            newgroup += grouptitle.substr(0, lastdot).c_str();
            temp.SetGroup(newgroup);
            temp.SetTitle(grouptitle.substr(lastdot + 1).c_str());
        } else {
            temp.SetGroup(ImportedPrefix);
            temp.SetTitle(grouptitle.c_str());
        }

        // The notes field begins and ends with a double-quote, with
        // no special escaping of any other internal characters.
        string quotedNotes = tokens[3];
        if (!quotedNotes.empty() &&
            *quotedNotes.begin() == '\"' &&
            *(quotedNotes.end() - 1) == '\"')
        {
            quotedNotes = quotedNotes.substr(1, quotedNotes.size() - 2);
            temp.SetNotes(CMyString(quotedNotes.c_str()));
        }

        AddEntryToTail(temp);
    }
    ifs.close();

    // TODO: maybe return an error if the full end of the file was not reached?

    return SUCCESS;
}

int PWScore::CheckPassword(const CMyString &filename, CMyString& passkey)
{
  PWSfile f(filename, passkey);

  int status = f.CheckPassword();

  switch (status) {
  case PWSfile::SUCCESS:
    return SUCCESS;
  case PWSfile::CANT_OPEN_FILE:
    return CANT_OPEN_FILE;
  case PWSfile::WRONG_PASSWORD:
    return WRONG_PASSWORD;
  default:
    ASSERT(0);
    return status; // should never happen
  }
}

int
PWScore::ReadFile(const CMyString &a_filename,
                   const CMyString &a_passkey)
{
   //That passkey had better be the same one that came from CheckPassword(...)

   PWSfile in(a_filename, a_passkey);

  int status;

  m_ReadFileVersion = in.GetFileVersion();

  if (m_ReadFileVersion == PWSfile::UNKNOWN_VERSION)
    return UNKNOWN_VERSION;

  status = in.OpenReadFile(m_ReadFileVersion);

  if (status != PWSfile::SUCCESS)
    return CANT_OPEN_FILE;

  // prepare handling of pre-2.0 DEFUSERCHR conversion
  if (m_ReadFileVersion == PWSfile::V17)
    in.SetDefUsername(m_defusername);

   ClearData(); //Before overwriting old data, but after opening the file...

   SetPassKey(a_passkey);

   CItemData temp;

   status = in.ReadRecord(temp);

   while (status == PWSfile::SUCCESS)
   {
      m_pwlist.AddTail(temp);
      status = in.ReadRecord(temp);
   }

   in.CloseFile();

   return SUCCESS;
}

int PWScore::RenameFile(const CMyString &oldname, const CMyString &newname)
{
  return PWSfile::RenameFile(oldname, newname);
}

void PWScore::ChangePassword(const CMyString &newPassword)
{

  SetPassKey(newPassword);
  m_changed = TRUE;
}

// Finds stuff based on title & user fields only
POSITION
PWScore::Find(const CMyString &a_title, const CMyString &a_user)
{
   POSITION listPos = m_pwlist.GetHeadPosition();
   CMyString title, user;

   while (listPos != NULL)
   {
      title = m_pwlist.GetAt(listPos).GetTitle();
      user = m_pwlist.GetAt(listPos).GetUser();
      if (title == a_title && user == a_user)
         break;
      else
         m_pwlist.GetNext(listPos);
   }

   return listPos;
}

void PWScore::EncryptPassword(const unsigned char *plaintext, int len,
			      unsigned char *ciphertext) const
{
  // ciphertext is ((len +7)/8)*8 bytes long
  BlowFish *Algorithm = MakeBlowFish(m_session_key, sizeof(m_session_key),
				     m_session_salt, sizeof(m_session_salt));
  int BlockLength = ((len + 7)/8)*8;
  unsigned char curblock[8];

  for (int x=0;x<BlockLength;x+=8) {
    int i;
    if ((len == 0) ||
	((len%8 != 0) && (len - x < 8))) {
      //This is for an uneven last block
      memset(curblock, 0, 8);
      for (i = 0; i < len %8; i++)
	curblock[i] = plaintext[x + i];
      } else
	for (i = 0; i < 8; i++)
	  curblock[i] = plaintext[x + i];
      Algorithm->Encrypt(curblock, curblock);
      memcpy(ciphertext + x, curblock, 8);
   }
   trashMemory(curblock, 8);
  delete Algorithm;
}

void PWScore::SetPassKey(const CMyString &new_passkey)
{
  // if changing, clear old
  if (m_passkey_len > 0) {
    trashMemory(m_passkey, ((m_passkey_len + 7)/8)*8);
    delete[] m_passkey;
  }

  m_passkey_len = new_passkey.GetLength();

  int BlockLength = ((m_passkey_len + 7)/8)*8;
  m_passkey = new unsigned char[BlockLength];
  LPCTSTR plaintext = LPCTSTR(new_passkey);
  EncryptPassword((const unsigned char *)plaintext, m_passkey_len,
		  m_passkey);
}

CMyString PWScore::GetPassKey() const
{
  CMyString retval(_T(""));
  if (m_passkey_len > 0) {
    unsigned int BlockLength = ((m_passkey_len + 7)/8)*8;
    BlowFish *Algorithm = MakeBlowFish(m_session_key, sizeof(m_session_key),
				       m_session_salt, sizeof(m_session_salt));
    unsigned char curblock[8];

    for (unsigned int x = 0; x < BlockLength; x += 8) {
      unsigned int i;
      for (i = 0; i < 8; i++)
	curblock[i] = m_passkey[x + i];
      Algorithm->Decrypt(curblock, curblock);
      for (i = 0; i < 8; i++)
	if (x + i < m_passkey_len)
	  retval += curblock[i];
    }
    trashMemory(curblock, 8);
    delete Algorithm;
  }
  return retval;
}
<commit_msg>Compiles (with warnings!) under VC6.0<commit_after>// file PWScore.cpp
//-----------------------------------------------------------------------------

#include <fstream> // for WritePlaintextFile
#include <iostream>
#include <string>
#include <vector>
using namespace std;

#include "PWScore.h"
#include "BlowFish.h"

PWScore::PWScore() : m_currfile(_T("")), m_changed(false),
		     m_usedefuser(false), m_defusername(_T("")),
		     m_ReadFileVersion(PWSfile::UNKNOWN_VERSION),
		     m_passkey(NULL), m_passkey_len(0)
{
  int i;

  srand((unsigned)time(NULL));
  CItemData::SetSessionKey(); // per-session initialization
  for (i = 0; i < sizeof(m_session_key); i++)
    m_session_key[i] = newrand();
  for (i = 0; i < sizeof(m_session_salt); i++)
    m_session_salt[i] = newrand();

}

PWScore::~PWScore()
{
  if (m_passkey_len > 0) {
    trashMemory(m_passkey, ((m_passkey_len + 7)/8)*8);
    delete[] m_passkey;
  }
}

void
PWScore::ClearData(void)
{
  if (m_passkey_len > 0) {
    trashMemory(m_passkey, ((m_passkey_len + 7)/8)*8);
    delete[] m_passkey;
    m_passkey_len = 0;
  }
  //Composed of ciphertext, so doesn't need to be overwritten
  m_pwlist.RemoveAll();
}

void
PWScore::NewFile(const CMyString &passkey)
{
   ClearData();
   SetPassKey(passkey);
   m_changed = false;
}

int
PWScore::WriteFile(const CMyString &filename, PWSfile::VERSION version)
{
  PWSfile out(filename, GetPassKey());

  int status;

  status = out.OpenWriteFile(version);

  if (status != PWSfile::SUCCESS)
    return CANT_OPEN_FILE;

  CItemData temp;
  POSITION listPos = m_pwlist.GetHeadPosition();
  while (listPos != NULL)
    {
      temp = m_pwlist.GetAt(listPos);
      out.WriteRecord(temp);
      m_pwlist.GetNext(listPos);
    }
  out.CloseFile();

  m_changed = FALSE;
  m_ReadFileVersion = version; // needed when saving a V17 as V20 1st time [871893]

  return SUCCESS;
}

int
PWScore::WritePlaintextFile(const CMyString &filename)
{
  ofstream of(filename);

  if (!of)
    return CANT_OPEN_FILE;

  CItemData temp;
  POSITION listPos = m_pwlist.GetHeadPosition();
  while (listPos != NULL)
    {
      temp = m_pwlist.GetAt(listPos);
      of << temp.GetPlaintext('\t') << endl;
      m_pwlist.GetNext(listPos);
    }
  of.close();

  return SUCCESS;
}

/*
int
PWScore::WriteXMLFile(const CMyString &filename)
{
  ofstream of(filename);

  if (!of)
    return CANT_OPEN_FILE;

  of << "<?xml version=\"1.0\">" << endl;
  of << "<passwordsafe>" << endl;
  POSITION listPos = m_pwlist.GetHeadPosition();
  while (listPos != NULL)
    {
      CItemData temp = m_pwlist.GetAt(listPos);

      of << "  <entry>" << endl;
      // TODO: need to handle entity escaping of values.
      of << "    <group>" << temp.GetGroup() << "</group>" << endl;
      of << "    <title>" << temp.GetTitle() << "</title>" << endl;
      of << "    <username>" << temp.GetUser() << "</username>" << endl;
      of << "    <password>" << temp.GetPassword() << "</password>" << endl;
      of << "    <notes>" << temp.GetNotes() << "</notes>" << endl;
      of << "  </entry>" << endl;

      m_pwlist.GetNext(listPos);
    }
  of << "</passwordsafe>" << endl;
  of.close();

  return SUCCESS;
}
*/

int
PWScore::ImportPlaintextFile(const CMyString &filename)
{
    static const char *ImportedPrefix = { "Imported" };
    ifstream ifs(filename);

    if (!ifs)
        return CANT_OPEN_FILE;

    for (;;)
    {
        // read a single line.
        string linebuf;
        if (!getline(ifs, linebuf, '\n')) break;

        // remove MS-DOS linebreaks, if needed.
        if (!linebuf.empty() && *(linebuf.end() - 1) == '\r') {
            linebuf.resize(linebuf.size() - 1);
        }

        // tokenize into separate elements
        vector<string> tokens;
        for (int startpos = 0; ; ) {
            int nextchar = linebuf.find_first_of('\t', startpos);
            if (nextchar >= 0 && tokens.size() < 3) {
                tokens.push_back(linebuf.substr(startpos, nextchar - startpos));
                startpos = nextchar + 1;
            } else {
                tokens.push_back(linebuf.substr(startpos));
                break;
            }
        }
        if (tokens.size() != 4) {
            ASSERT(0 && "did not find expected number of tokens");
            break;
        }

        // Start initializing the new record.
        CItemData temp;
        temp.CreateUUID();
        temp.SetUser(CMyString(tokens[1].c_str()));
        temp.SetPassword(CMyString(tokens[2].c_str()));

        // The group and title field are concatenated.
        const string &grouptitle = tokens[0];
        int lastdot = grouptitle.find_last_of('.');
        if (lastdot > 0) {
            CMyString newgroup(ImportedPrefix);
            newgroup += ".";
            newgroup += grouptitle.substr(0, lastdot).c_str();
            temp.SetGroup(newgroup);
            temp.SetTitle(grouptitle.substr(lastdot + 1).c_str());
        } else {
            temp.SetGroup(ImportedPrefix);
            temp.SetTitle(grouptitle.c_str());
        }

        // The notes field begins and ends with a double-quote, with
        // no special escaping of any other internal characters.
        string quotedNotes = tokens[3];
        if (!quotedNotes.empty() &&
            *quotedNotes.begin() == '\"' &&
            *(quotedNotes.end() - 1) == '\"')
        {
            quotedNotes = quotedNotes.substr(1, quotedNotes.size() - 2);
            temp.SetNotes(CMyString(quotedNotes.c_str()));
        }

        AddEntryToTail(temp);
    }
    ifs.close();

    // TODO: maybe return an error if the full end of the file was not reached?

    return SUCCESS;
}

int PWScore::CheckPassword(const CMyString &filename, CMyString& passkey)
{
  PWSfile f(filename, passkey);

  int status = f.CheckPassword();

  switch (status) {
  case PWSfile::SUCCESS:
    return SUCCESS;
  case PWSfile::CANT_OPEN_FILE:
    return CANT_OPEN_FILE;
  case PWSfile::WRONG_PASSWORD:
    return WRONG_PASSWORD;
  default:
    ASSERT(0);
    return status; // should never happen
  }
}

int
PWScore::ReadFile(const CMyString &a_filename,
                   const CMyString &a_passkey)
{
   //That passkey had better be the same one that came from CheckPassword(...)

   PWSfile in(a_filename, a_passkey);

  int status;

  m_ReadFileVersion = in.GetFileVersion();

  if (m_ReadFileVersion == PWSfile::UNKNOWN_VERSION)
    return UNKNOWN_VERSION;

  status = in.OpenReadFile(m_ReadFileVersion);

  if (status != PWSfile::SUCCESS)
    return CANT_OPEN_FILE;

  // prepare handling of pre-2.0 DEFUSERCHR conversion
  if (m_ReadFileVersion == PWSfile::V17)
    in.SetDefUsername(m_defusername);

   ClearData(); //Before overwriting old data, but after opening the file...

   SetPassKey(a_passkey);

   CItemData temp;

   status = in.ReadRecord(temp);

   while (status == PWSfile::SUCCESS)
   {
      m_pwlist.AddTail(temp);
      status = in.ReadRecord(temp);
   }

   in.CloseFile();

   return SUCCESS;
}

int PWScore::RenameFile(const CMyString &oldname, const CMyString &newname)
{
  return PWSfile::RenameFile(oldname, newname);
}

void PWScore::ChangePassword(const CMyString &newPassword)
{

  SetPassKey(newPassword);
  m_changed = TRUE;
}

// Finds stuff based on title & user fields only
POSITION
PWScore::Find(const CMyString &a_title, const CMyString &a_user)
{
   POSITION listPos = m_pwlist.GetHeadPosition();
   CMyString title, user;

   while (listPos != NULL)
   {
      title = m_pwlist.GetAt(listPos).GetTitle();
      user = m_pwlist.GetAt(listPos).GetUser();
      if (title == a_title && user == a_user)
         break;
      else
         m_pwlist.GetNext(listPos);
   }

   return listPos;
}

void PWScore::EncryptPassword(const unsigned char *plaintext, int len,
			      unsigned char *ciphertext) const
{
  // ciphertext is ((len +7)/8)*8 bytes long
  BlowFish *Algorithm = MakeBlowFish(m_session_key, sizeof(m_session_key),
				     m_session_salt, sizeof(m_session_salt));
  int BlockLength = ((len + 7)/8)*8;
  unsigned char curblock[8];

  for (int x=0;x<BlockLength;x+=8) {
    int i;
    if ((len == 0) ||
	((len%8 != 0) && (len - x < 8))) {
      //This is for an uneven last block
      memset(curblock, 0, 8);
      for (i = 0; i < len %8; i++)
	curblock[i] = plaintext[x + i];
      } else
	for (i = 0; i < 8; i++)
	  curblock[i] = plaintext[x + i];
      Algorithm->Encrypt(curblock, curblock);
      memcpy(ciphertext + x, curblock, 8);
   }
   trashMemory(curblock, 8);
  delete Algorithm;
}

void PWScore::SetPassKey(const CMyString &new_passkey)
{
  // if changing, clear old
  if (m_passkey_len > 0) {
    trashMemory(m_passkey, ((m_passkey_len + 7)/8)*8);
    delete[] m_passkey;
  }

  m_passkey_len = new_passkey.GetLength();

  int BlockLength = ((m_passkey_len + 7)/8)*8;
  m_passkey = new unsigned char[BlockLength];
  LPCTSTR plaintext = LPCTSTR(new_passkey);
  EncryptPassword((const unsigned char *)plaintext, m_passkey_len,
		  m_passkey);
}

CMyString PWScore::GetPassKey() const
{
  CMyString retval(_T(""));
  if (m_passkey_len > 0) {
    unsigned int BlockLength = ((m_passkey_len + 7)/8)*8;
    BlowFish *Algorithm = MakeBlowFish(m_session_key, sizeof(m_session_key),
				       m_session_salt, sizeof(m_session_salt));
    unsigned char curblock[8];

    for (unsigned int x = 0; x < BlockLength; x += 8) {
      unsigned int i;
      for (i = 0; i < 8; i++)
	curblock[i] = m_passkey[x + i];
      Algorithm->Decrypt(curblock, curblock);
      for (i = 0; i < 8; i++)
	if (x + i < m_passkey_len)
	  retval += curblock[i];
    }
    trashMemory(curblock, 8);
    delete Algorithm;
  }
  return retval;
}
<|endoftext|>


<commit_before>
#include "stdafx.h"
#include "particlesystems.h"

#include <graphics/device.h>
#include <graphics/camera.h>
#include <graphics/shaperenderer2d.h>

#include <particles/parser.h>
#include <particles/instances.h>
#include "../obj.h"
#include "../config/config.h"

#include "ui/ui_render.h"

using namespace gfx;
using namespace particles;

class PartSysExternal : public IPartSysExternal {
public:
	PartSysExternal(ParticleSysSystem &system, WorldCamera &camera)
		: mSystem(system), mCamera(camera) {}

	float GetParticleFidelity() override;
	bool GetObjLocation(ObjHndl obj, Vec3& worldPos) override;
	bool GetObjRotation(ObjHndl obj, float& rotation) override;
	float GetObjRadius(ObjHndl obj) override;
	bool GetBoneWorldMatrix(ObjHndl obj, const std::string& boneName, Matrix4x4& boneMatrix) override;
	int GetBoneCount(ObjHndl obj) override;
	int GetParentChildBonePos(ObjHndl obj, int boneIdx, Vec3& parentPos, Vec3& childPos) override;
	bool GetBonePos(ObjHndl obj, int boneIdx, Vec3& pos) override;
	void WorldToScreen(const Vec3& worldPos, Vec2& screenPos) override;
	bool IsBoxVisible(const Vec2& screenPos, const Box2d& box) override;
private:
	ParticleSysSystem &mSystem;
	WorldCamera &mCamera;
};

ParticleSysSystem::ParticleSysSystem(WorldCamera& camera) {

	PartSysParser parser;
	parser.ParseFile("rules\\partsys0.tab");
	parser.ParseFile("rules\\partsys1.tab");
	parser.ParseFile("rules\\partsys2.tab");

	for (auto &spec : parser) {
		mPartSysByName[spec.first] = spec.second;
		mPartSysByHash[spec.second->GetNameHash()] = spec.second;
	}

	mExternal = std::make_unique<PartSysExternal>(*this, camera);
	IPartSysExternal::SetCurrent(mExternal.get());

	// Register a config for the partsys fidelity
	config.AddVanillaSetting("partsys_fidelity", "100", [=]() {
		SetFidelity(config.GetVanillaInt("partsys_fidelity") / 100.0f);
	});
	SetFidelity(config.GetVanillaInt("partsys_fidelity") / 100.0f);
}

ParticleSysSystem::~ParticleSysSystem() {
	IPartSysExternal::SetCurrent(nullptr);
}

void ParticleSysSystem::AdvanceTime(uint32_t time) {

	// First call
	if (mLastSimTime == 0) {
		mLastSimTime = time;
		return;
	}

	auto sinceLastSim = time - mLastSimTime;
	mLastSimTime = time;

	auto timeInSecs = sinceLastSim / 1000.0f;

	if (timeInSecs > 0.5f) {
		timeInSecs = 0.5f;
	}

	auto it = mActiveSys.begin();
	while (it != mActiveSys.end()) {
		auto& sys = *it->second;

		sys.Simulate(timeInSecs);

		// Remove dead systems
		if (sys.IsDead()) {
			it = mActiveSys.erase(it);
		} else {
			it++;
		}
	}

}

const std::string &ParticleSysSystem::GetName() const {
	static std::string name("ParticleSys");
	return name;
}

int ParticleSysSystem::CreateAt(uint32_t nameHash, XMFLOAT3 pos) {
	auto it = mPartSysByHash.find(nameHash);

	if (it == mPartSysByHash.end()) {
		logger->warn("Unable to spawn unknown particle system: {}", nameHash);
		return -1;
	}

	auto& spec = it->second;

	auto sys(std::make_shared<PartSys>(spec));
	sys->SetWorldPos(mExternal.get(), pos.x, pos.y, pos.z);

	auto assignedId = mNextId++;

	mActiveSys[assignedId] = sys;
	return assignedId;

}

ParticleSysSystem::Handle ParticleSysSystem::CreateAtObj(const std::string &name, objHndl obj) {
	auto it = mPartSysByName.find(tolower(name));

	if (it == mPartSysByName.end()) {
		logger->warn("Unable to spawn unknown particle system: {}", name);
		return -1;
	}

	auto& spec = it->second;

	auto loc = objects.GetLocationFull(obj);
	auto absLoc = loc.ToInches3D(objects.GetOffsetZ(obj));

	auto sys(std::make_shared<PartSys>(spec));
	sys->SetWorldPos(mExternal.get(), absLoc.x, absLoc.y, absLoc.z);
	sys->SetAttachedTo(obj);

	auto assignedId = mNextId++;

	mActiveSys[assignedId] = sys;
	return assignedId;
}

ParticleSysSystem::Handle ParticleSysSystem::CreateAtPos(const std::string &name, XMFLOAT3 pos) {
	auto it = mPartSysByName.find(tolower(name));

	if (it == mPartSysByName.end()) {
		logger->warn("Unable to spawn unknown particle system: {}", name);
		return -1;
	}

	auto& spec = it->second;

	auto sys(std::make_shared<PartSys>(spec));
	sys->SetWorldPos(mExternal.get(), pos.x, pos.y, pos.z);

	auto assignedId = mNextId++;

	mActiveSys[assignedId] = sys;
	return assignedId;
}

bool ParticleSysSystem::DoesNameExist(const std::string & name)
{
	return mPartSysByName.find(tolower(name)) != mPartSysByName.end();
}

bool ParticleSysSystem::DoesNameHashExist(uint32_t nameHash)
{
	return mPartSysByHash.find(nameHash) != mPartSysByHash.end();
}

particles::PartSysPtr ParticleSysSystem::GetByHandle(Handle handle) {
	auto it = mActiveSys.find(handle);
	if (it != mActiveSys.end()) {
		return it->second;
	}
	return nullptr;
}

void ParticleSysSystem::Remove(Handle handle)
{
	mActiveSys.erase(handle);
}

void ParticleSysSystem::End(Handle partsysId) {
	auto partSys = GetByHandle(partsysId);
	if (partSys) {
		partSys->EndPrematurely();
	}
}

void ParticleSysSystem::RemoveAll() {
	mActiveSys.clear();
}

float PartSysExternal::GetParticleFidelity() {
	return mSystem.GetFidelity();
}

bool PartSysExternal::GetObjLocation(ObjHndl obj, Vec3& worldPos) {
	auto locWithOffsets = objects.GetLocationFull(obj);
	auto offsetZ = objects.GetOffsetZ(obj);
	auto center3d(locWithOffsets.ToInches3D(offsetZ));
	worldPos.x = center3d.x;
	worldPos.y = center3d.y;
	worldPos.z = center3d.z;
	return true;
}

bool PartSysExternal::GetObjRotation(ObjHndl obj, float& rotation) {
	rotation = objects.GetRotation(obj);
	return true;
}

float PartSysExternal::GetObjRadius(ObjHndl obj) {
	return objects.GetRadius(obj);
}

bool PartSysExternal::GetBoneWorldMatrix(ObjHndl obj, const std::string& boneName, Matrix4x4& boneMatrix) {
	auto model = objects.GetAnimHandle(obj);
	if (!model) {
		return false;
	}

	auto animParams = objects.GetAnimParams(obj);

	auto objType = objects.GetType(obj);
	if (objType >= obj_t_weapon && objType <= obj_t_generic || objType == obj_t_bag) {
		auto parent = inventory.GetParent(obj);
		if (parent) {
			auto parentModel = objects.GetAnimHandle(parent);
			return parentModel->GetBoneWorldMatrixByNameForChild(
				model, animParams, boneName, &boneMatrix
			);
		}
	}

	return model->GetBoneWorldMatrixByName(animParams, boneName, &boneMatrix);
}

int PartSysExternal::GetBoneCount(ObjHndl obj) {
	auto model = objects.GetAnimHandle(obj);
	if (model) {
		return model->GetBoneCount();
	} else {
		return 0;
	}
}

static bool StrContains(const std::string &str, const char *otherStr) {
	return str.find(otherStr) != std::string::npos;
}

static bool IsIgnoredBone(const std::string &name) {

	if (name[0] == '#') {
		return true; // Cloth bone
	}
	if (tolower(name) == "bip01") {
		return true;
	}

	return StrContains(name, "Pony")
		|| StrContains(name, "Footstep")
		|| StrContains(name, "Origin")
		|| StrContains(name, "Casting_ref")
		|| StrContains(name, "EarthElemental_reg")
		|| StrContains(name, "Casting_ref")
		|| StrContains(name, "origin")
		|| StrContains(name, "Bip01 Footsteps")
		|| StrContains(name, "FootL_ref")
		|| StrContains(name, "FootR_ref")
		|| StrContains(name, "Head_ref")
		|| StrContains(name, "HandL_ref")
		|| StrContains(name, "HandR_ref")
		|| StrContains(name, "Chest_ref")
		|| StrContains(name, "groundParticleRef")
		|| StrContains(name, "effects_ref")
		|| StrContains(name, "trap_ref");

}

int PartSysExternal::GetParentChildBonePos(ObjHndl obj, int boneIdx, Vec3& parentPos, Vec3& childPos) {

	auto model = objects.GetAnimHandle(obj);
	auto aasParams = objects.GetAnimParams(obj);

	auto parentId(model->GetBoneParentId(boneIdx));
	if (parentId < 0) {
		return parentId;
	}

	auto boneName(model->GetBoneName(boneIdx));
	if (boneName.empty()) {
		return -1;
	}

	if (IsIgnoredBone(boneName)) {
		return -1;
	}

	auto parentName(model->GetBoneName(parentId));
	if (parentName.empty()) {
		return -1;
	}

	DirectX::XMFLOAT4X4	worldMatrix;
	if (!model->GetBoneWorldMatrixByName(aasParams, parentName, &worldMatrix)) {
		return -1;
	}

	parentPos.x = worldMatrix._41;
	parentPos.y = worldMatrix._42;
	parentPos.z = worldMatrix._43;

	if (!model->GetBoneWorldMatrixByName(aasParams, boneName, &worldMatrix)) {
		return -1;
	}

	childPos.x = worldMatrix._41;
	childPos.y = worldMatrix._42;
	childPos.z = worldMatrix._43;
	return parentId;
}

bool PartSysExternal::GetBonePos(ObjHndl obj, int boneIdx, Vec3& pos) {
	auto model = objects.GetAnimHandle(obj);
	auto aasParams = objects.GetAnimParams(obj);

	auto boneName(model->GetBoneName(boneIdx));
	if (boneName.empty()) {
		return false;
	}

	DirectX::XMFLOAT4X4	worldMatrix;
	if (!model->GetBoneWorldMatrixByName(aasParams, boneName, &worldMatrix)) {
		return false;
	}

	pos.x = worldMatrix._41;
	pos.y = worldMatrix._42;
	pos.z = worldMatrix._43;
	return true;
}

void PartSysExternal::WorldToScreen(const Vec3& worldPos, Vec2& screenPos) {
	screenPos = mCamera.WorldToScreen(worldPos);
	auto offset2d = mCamera.Get2dTranslation();
	screenPos.x += offset2d.x;
	screenPos.y += offset2d.y;
}

bool PartSysExternal::IsBoxVisible(const Vec2& screenPos, const Box2d& box) {

	return mCamera.IsBoxOnScreen(screenPos,
		box.left, box.top,
		box.right, box.bottom);

}
<commit_msg>Added a missing object rotation offset. Fixes #144<commit_after>
#include "stdafx.h"
#include "particlesystems.h"

#include <graphics/device.h>
#include <graphics/camera.h>
#include <graphics/shaperenderer2d.h>

#include <particles/parser.h>
#include <particles/instances.h>
#include "../obj.h"
#include "../config/config.h"

#include "ui/ui_render.h"

using namespace gfx;
using namespace particles;

class PartSysExternal : public IPartSysExternal {
public:
	PartSysExternal(ParticleSysSystem &system, WorldCamera &camera)
		: mSystem(system), mCamera(camera) {}

	float GetParticleFidelity() override;
	bool GetObjLocation(ObjHndl obj, Vec3& worldPos) override;
	bool GetObjRotation(ObjHndl obj, float& rotation) override;
	float GetObjRadius(ObjHndl obj) override;
	bool GetBoneWorldMatrix(ObjHndl obj, const std::string& boneName, Matrix4x4& boneMatrix) override;
	int GetBoneCount(ObjHndl obj) override;
	int GetParentChildBonePos(ObjHndl obj, int boneIdx, Vec3& parentPos, Vec3& childPos) override;
	bool GetBonePos(ObjHndl obj, int boneIdx, Vec3& pos) override;
	void WorldToScreen(const Vec3& worldPos, Vec2& screenPos) override;
	bool IsBoxVisible(const Vec2& screenPos, const Box2d& box) override;
private:
	ParticleSysSystem &mSystem;
	WorldCamera &mCamera;
};

ParticleSysSystem::ParticleSysSystem(WorldCamera& camera) {

	PartSysParser parser;
	parser.ParseFile("rules\\partsys0.tab");
	parser.ParseFile("rules\\partsys1.tab");
	parser.ParseFile("rules\\partsys2.tab");

	for (auto &spec : parser) {
		mPartSysByName[spec.first] = spec.second;
		mPartSysByHash[spec.second->GetNameHash()] = spec.second;
	}

	mExternal = std::make_unique<PartSysExternal>(*this, camera);
	IPartSysExternal::SetCurrent(mExternal.get());

	// Register a config for the partsys fidelity
	config.AddVanillaSetting("partsys_fidelity", "100", [=]() {
		SetFidelity(config.GetVanillaInt("partsys_fidelity") / 100.0f);
	});
	SetFidelity(config.GetVanillaInt("partsys_fidelity") / 100.0f);
}

ParticleSysSystem::~ParticleSysSystem() {
	IPartSysExternal::SetCurrent(nullptr);
}

void ParticleSysSystem::AdvanceTime(uint32_t time) {

	// First call
	if (mLastSimTime == 0) {
		mLastSimTime = time;
		return;
	}

	auto sinceLastSim = time - mLastSimTime;
	mLastSimTime = time;

	auto timeInSecs = sinceLastSim / 1000.0f;

	if (timeInSecs > 0.5f) {
		timeInSecs = 0.5f;
	}

	auto it = mActiveSys.begin();
	while (it != mActiveSys.end()) {
		auto& sys = *it->second;

		sys.Simulate(timeInSecs);

		// Remove dead systems
		if (sys.IsDead()) {
			it = mActiveSys.erase(it);
		} else {
			it++;
		}
	}

}

const std::string &ParticleSysSystem::GetName() const {
	static std::string name("ParticleSys");
	return name;
}

int ParticleSysSystem::CreateAt(uint32_t nameHash, XMFLOAT3 pos) {
	auto it = mPartSysByHash.find(nameHash);

	if (it == mPartSysByHash.end()) {
		logger->warn("Unable to spawn unknown particle system: {}", nameHash);
		return -1;
	}

	auto& spec = it->second;

	auto sys(std::make_shared<PartSys>(spec));
	sys->SetWorldPos(mExternal.get(), pos.x, pos.y, pos.z);

	auto assignedId = mNextId++;

	mActiveSys[assignedId] = sys;
	return assignedId;

}

ParticleSysSystem::Handle ParticleSysSystem::CreateAtObj(const std::string &name, objHndl obj) {
	auto it = mPartSysByName.find(tolower(name));

	if (it == mPartSysByName.end()) {
		logger->warn("Unable to spawn unknown particle system: {}", name);
		return -1;
	}

	auto& spec = it->second;

	auto loc = objects.GetLocationFull(obj);
	auto absLoc = loc.ToInches3D(objects.GetOffsetZ(obj));

	auto sys(std::make_shared<PartSys>(spec));
	sys->SetWorldPos(mExternal.get(), absLoc.x, absLoc.y, absLoc.z);
	sys->SetAttachedTo(obj);

	auto assignedId = mNextId++;

	mActiveSys[assignedId] = sys;
	return assignedId;
}

ParticleSysSystem::Handle ParticleSysSystem::CreateAtPos(const std::string &name, XMFLOAT3 pos) {
	auto it = mPartSysByName.find(tolower(name));

	if (it == mPartSysByName.end()) {
		logger->warn("Unable to spawn unknown particle system: {}", name);
		return -1;
	}

	auto& spec = it->second;

	auto sys(std::make_shared<PartSys>(spec));
	sys->SetWorldPos(mExternal.get(), pos.x, pos.y, pos.z);

	auto assignedId = mNextId++;

	mActiveSys[assignedId] = sys;
	return assignedId;
}

bool ParticleSysSystem::DoesNameExist(const std::string & name)
{
	return mPartSysByName.find(tolower(name)) != mPartSysByName.end();
}

bool ParticleSysSystem::DoesNameHashExist(uint32_t nameHash)
{
	return mPartSysByHash.find(nameHash) != mPartSysByHash.end();
}

particles::PartSysPtr ParticleSysSystem::GetByHandle(Handle handle) {
	auto it = mActiveSys.find(handle);
	if (it != mActiveSys.end()) {
		return it->second;
	}
	return nullptr;
}

void ParticleSysSystem::Remove(Handle handle)
{
	mActiveSys.erase(handle);
}

void ParticleSysSystem::End(Handle partsysId) {
	auto partSys = GetByHandle(partsysId);
	if (partSys) {
		partSys->EndPrematurely();
	}
}

void ParticleSysSystem::RemoveAll() {
	mActiveSys.clear();
}

float PartSysExternal::GetParticleFidelity() {
	return mSystem.GetFidelity();
}

bool PartSysExternal::GetObjLocation(ObjHndl obj, Vec3& worldPos) {
	auto locWithOffsets = objects.GetLocationFull(obj);
	auto offsetZ = objects.GetOffsetZ(obj);
	auto center3d(locWithOffsets.ToInches3D(offsetZ));
	worldPos.x = center3d.x;
	worldPos.y = center3d.y;
	worldPos.z = center3d.z;
	return true;
}

bool PartSysExternal::GetObjRotation(ObjHndl obj, float& rotation) {
	rotation = objects.GetRotation(obj) + XM_PIDIV4;
	return true;
}

float PartSysExternal::GetObjRadius(ObjHndl obj) {
	return objects.GetRadius(obj);
}

bool PartSysExternal::GetBoneWorldMatrix(ObjHndl obj, const std::string& boneName, Matrix4x4& boneMatrix) {
	auto model = objects.GetAnimHandle(obj);
	if (!model) {
		return false;
	}

	auto animParams = objects.GetAnimParams(obj);

	auto objType = objects.GetType(obj);
	if (objType >= obj_t_weapon && objType <= obj_t_generic || objType == obj_t_bag) {
		auto parent = inventory.GetParent(obj);
		if (parent) {
			auto parentModel = objects.GetAnimHandle(parent);
			return parentModel->GetBoneWorldMatrixByNameForChild(
				model, animParams, boneName, &boneMatrix
			);
		}
	}

	return model->GetBoneWorldMatrixByName(animParams, boneName, &boneMatrix);
}

int PartSysExternal::GetBoneCount(ObjHndl obj) {
	auto model = objects.GetAnimHandle(obj);
	if (model) {
		return model->GetBoneCount();
	} else {
		return 0;
	}
}

static bool StrContains(const std::string &str, const char *otherStr) {
	return str.find(otherStr) != std::string::npos;
}

static bool IsIgnoredBone(const std::string &name) {

	if (name[0] == '#') {
		return true; // Cloth bone
	}
	if (tolower(name) == "bip01") {
		return true;
	}

	return StrContains(name, "Pony")
		|| StrContains(name, "Footstep")
		|| StrContains(name, "Origin")
		|| StrContains(name, "Casting_ref")
		|| StrContains(name, "EarthElemental_reg")
		|| StrContains(name, "Casting_ref")
		|| StrContains(name, "origin")
		|| StrContains(name, "Bip01 Footsteps")
		|| StrContains(name, "FootL_ref")
		|| StrContains(name, "FootR_ref")
		|| StrContains(name, "Head_ref")
		|| StrContains(name, "HandL_ref")
		|| StrContains(name, "HandR_ref")
		|| StrContains(name, "Chest_ref")
		|| StrContains(name, "groundParticleRef")
		|| StrContains(name, "effects_ref")
		|| StrContains(name, "trap_ref");

}

int PartSysExternal::GetParentChildBonePos(ObjHndl obj, int boneIdx, Vec3& parentPos, Vec3& childPos) {

	auto model = objects.GetAnimHandle(obj);
	auto aasParams = objects.GetAnimParams(obj);

	auto parentId(model->GetBoneParentId(boneIdx));
	if (parentId < 0) {
		return parentId;
	}

	auto boneName(model->GetBoneName(boneIdx));
	if (boneName.empty()) {
		return -1;
	}

	if (IsIgnoredBone(boneName)) {
		return -1;
	}

	auto parentName(model->GetBoneName(parentId));
	if (parentName.empty()) {
		return -1;
	}

	DirectX::XMFLOAT4X4	worldMatrix;
	if (!model->GetBoneWorldMatrixByName(aasParams, parentName, &worldMatrix)) {
		return -1;
	}

	parentPos.x = worldMatrix._41;
	parentPos.y = worldMatrix._42;
	parentPos.z = worldMatrix._43;

	if (!model->GetBoneWorldMatrixByName(aasParams, boneName, &worldMatrix)) {
		return -1;
	}

	childPos.x = worldMatrix._41;
	childPos.y = worldMatrix._42;
	childPos.z = worldMatrix._43;
	return parentId;
}

bool PartSysExternal::GetBonePos(ObjHndl obj, int boneIdx, Vec3& pos) {
	auto model = objects.GetAnimHandle(obj);
	auto aasParams = objects.GetAnimParams(obj);

	auto boneName(model->GetBoneName(boneIdx));
	if (boneName.empty()) {
		return false;
	}

	DirectX::XMFLOAT4X4	worldMatrix;
	if (!model->GetBoneWorldMatrixByName(aasParams, boneName, &worldMatrix)) {
		return false;
	}

	pos.x = worldMatrix._41;
	pos.y = worldMatrix._42;
	pos.z = worldMatrix._43;
	return true;
}

void PartSysExternal::WorldToScreen(const Vec3& worldPos, Vec2& screenPos) {
	screenPos = mCamera.WorldToScreen(worldPos);
	auto offset2d = mCamera.Get2dTranslation();
	screenPos.x += offset2d.x;
	screenPos.y += offset2d.y;
}

bool PartSysExternal::IsBoxVisible(const Vec2& screenPos, const Box2d& box) {

	return mCamera.IsBoxOnScreen(screenPos,
		box.left, box.top,
		box.right, box.bottom);

}
<|endoftext|>


<commit_before>/******************************************************************************//**
 * @copyright (c) RDO-Team, 2011
 * @file      braces.cpp
 * @authors
 * @date      16.04.2011
 * @brief     unknown
 * @indent    4T
 *********************************************************************************/

#ifndef _LIB_RUNTIME_CALC_BINARY_H_
#define _LIB_RUNTIME_CALC_BINARY_H_

// **************************************************************************** PCH
#include "rdo_lib/rdo_runtime/pch.h"
// *********************************************************************** INCLUDES
// *********************************************************************** SYNOPSIS
#include "rdo_lib/rdo_runtime/calc/braces.h"
// ********************************************************************************

OPEN_RDO_RUNTIME_NAMESPACE

// ********************************************************************************
// ******************** RDOCalcOpenBrace
// ********************************************************************************
RDOCalcOpenBrace::RDOCalcOpenBrace()
{}

REF(RDOValue) RDOCalcOpenBrace::doCalc(CREF(LPRDORuntime) pRuntime)
{
	LPRDOMemory pLocalMemory = rdo::Factory<RDOMemory>::create();
	pRuntime->getMemoryStack()->push(pLocalMemory);
	return m_value;
}

// ********************************************************************************
// ******************** RDOCalcCloseBrace
// ********************************************************************************
RDOCalcCloseBrace::RDOCalcCloseBrace()
{}

REF(RDOValue) RDOCalcCloseBrace::doCalc(CREF(LPRDORuntime) pRuntime)
{
	pRuntime->getMemoryStack()->pop();
	return m_value;
}

// ********************************************************************************
// ******************** RDOCalcBodyBrace
// ********************************************************************************
RDOCalcBodyBrace::RDOCalcBodyBrace()
{}

void RDOCalcBodyBrace::addCalc(CREF(LPRDOCalc) pCalc)
{
	ASSERT(pCalc);
	m_calcList.push_back(pCalc);
}

REF(RDOValue) RDOCalcBodyBrace::doCalc(CREF(LPRDORuntime) pRuntime)
{
	STL_FOR_ALL(m_calcList, calc_it)
	{
		(*calc_it)->calcValue(pRuntime);
	}

	m_value = RDOValue(m_calcList.size());
	return m_value;
}

// ********************************************************************************
// ******************** RDOCalcFunBodyBrace
// ********************************************************************************
RDOCalcFunBodyBrace::RDOCalcFunBodyBrace()
{}

void RDOCalcFunBodyBrace::addFunCalc(CREF(LPRDOCalc) pCalc)
{
	ASSERT(pCalc);
	m_calcFunList.push_back(pCalc);
}

void RDOCalcFunBodyBrace::addRetCalc(CREF(LPRDOCalc) pCalc)
{
	ASSERT(pCalc);
	LPRDOCalc pCalcReturn = rdo::Factory<rdoRuntime::RDOCalcFunReturn>::create(pCalc);
	m_calcFunList.insert(--m_calcFunList.end(), pCalcReturn);
}

REF(RDOValue) RDOCalcFunBodyBrace::doCalc(CREF(LPRDORuntime) pRuntime)
{
	if (pRuntime->getFunBreakFlag() == RDORuntime::FBF_CONTINUE)
	{
		STL_FOR_ALL(m_calcFunList, calcIt)
		{
			LPRDOCalc pCalc = *calcIt;
			ASSERT(pCalc);
			m_value = pCalc->calcValue(pRuntime);
			if (pRuntime->getFunBreakFlag() == RDORuntime::FBF_BREAK)
			{
				m_calcFunList.back()->calcValue(pRuntime);
				pRuntime->setFunBreakFlag(RDORuntime::FBF_CONTINUE);
				m_value = RDOValue(false); //! break
				return m_value;
			}
			if (pRuntime->getFunBreakFlag() == RDORuntime::FBF_RETURN)
			{
				m_calcFunList.back()->calcValue(pRuntime);
				return m_value;
			}
		}
	}
	return m_value;
}

// ********************************************************************************
// ******************** RDOCalcFunEnd
// ********************************************************************************
RDOCalcFunEnd::RDOCalcFunEnd()
{}

REF(RDOValue) RDOCalcFunEnd::doCalc(CREF(LPRDORuntime) pRuntime)
{
	pRuntime->getMemoryStack()->pop();
	pRuntime->setFunBreakFlag(RDORuntime::FBF_CONTINUE);
	return m_value;
}

CLOSE_RDO_RUNTIME_NAMESPACE
<commit_msg> - repair<commit_after>/******************************************************************************//**
 * @copyright (c) RDO-Team, 2011
 * @file      braces.cpp
 * @authors
 * @date      16.04.2011
 * @brief     unknown
 * @indent    4T
 *********************************************************************************/

// **************************************************************************** PCH
#include "rdo_lib/rdo_runtime/pch.h"
// *********************************************************************** INCLUDES
// *********************************************************************** SYNOPSIS
#include "rdo_lib/rdo_runtime/calc/braces.h"
// ********************************************************************************

OPEN_RDO_RUNTIME_NAMESPACE

// ********************************************************************************
// ******************** RDOCalcOpenBrace
// ********************************************************************************
RDOCalcOpenBrace::RDOCalcOpenBrace()
{}

REF(RDOValue) RDOCalcOpenBrace::doCalc(CREF(LPRDORuntime) pRuntime)
{
	LPRDOMemory pLocalMemory = rdo::Factory<RDOMemory>::create();
	pRuntime->getMemoryStack()->push(pLocalMemory);
	return m_value;
}

// ********************************************************************************
// ******************** RDOCalcCloseBrace
// ********************************************************************************
RDOCalcCloseBrace::RDOCalcCloseBrace()
{}

REF(RDOValue) RDOCalcCloseBrace::doCalc(CREF(LPRDORuntime) pRuntime)
{
	pRuntime->getMemoryStack()->pop();
	return m_value;
}

// ********************************************************************************
// ******************** RDOCalcBodyBrace
// ********************************************************************************
RDOCalcBodyBrace::RDOCalcBodyBrace()
{}

void RDOCalcBodyBrace::addCalc(CREF(LPRDOCalc) pCalc)
{
	ASSERT(pCalc);
	m_calcList.push_back(pCalc);
}

REF(RDOValue) RDOCalcBodyBrace::doCalc(CREF(LPRDORuntime) pRuntime)
{
	STL_FOR_ALL(m_calcList, calc_it)
	{
		(*calc_it)->calcValue(pRuntime);
	}

	m_value = RDOValue(m_calcList.size());
	return m_value;
}

// ********************************************************************************
// ******************** RDOCalcFunBodyBrace
// ********************************************************************************
RDOCalcFunBodyBrace::RDOCalcFunBodyBrace()
{}

void RDOCalcFunBodyBrace::addFunCalc(CREF(LPRDOCalc) pCalc)
{
	ASSERT(pCalc);
	m_calcFunList.push_back(pCalc);
}

void RDOCalcFunBodyBrace::addRetCalc(CREF(LPRDOCalc) pCalc)
{
	ASSERT(pCalc);
	LPRDOCalc pCalcReturn = rdo::Factory<rdoRuntime::RDOCalcFunReturn>::create(pCalc);
	m_calcFunList.insert(--m_calcFunList.end(), pCalcReturn);
}

REF(RDOValue) RDOCalcFunBodyBrace::doCalc(CREF(LPRDORuntime) pRuntime)
{
	if (pRuntime->getFunBreakFlag() == RDORuntime::FBF_CONTINUE)
	{
		STL_FOR_ALL(m_calcFunList, calcIt)
		{
			LPRDOCalc pCalc = *calcIt;
			ASSERT(pCalc);
			m_value = pCalc->calcValue(pRuntime);
			if (pRuntime->getFunBreakFlag() == RDORuntime::FBF_BREAK)
			{
				m_calcFunList.back()->calcValue(pRuntime);
				pRuntime->setFunBreakFlag(RDORuntime::FBF_CONTINUE);
				m_value = RDOValue(false); //! break
				return m_value;
			}
			if (pRuntime->getFunBreakFlag() == RDORuntime::FBF_RETURN)
			{
				m_calcFunList.back()->calcValue(pRuntime);
				return m_value;
			}
		}
	}
	return m_value;
}

// ********************************************************************************
// ******************** RDOCalcFunEnd
// ********************************************************************************
RDOCalcFunEnd::RDOCalcFunEnd()
{}

REF(RDOValue) RDOCalcFunEnd::doCalc(CREF(LPRDORuntime) pRuntime)
{
	pRuntime->getMemoryStack()->pop();
	pRuntime->setFunBreakFlag(RDORuntime::FBF_CONTINUE);
	return m_value;
}

CLOSE_RDO_RUNTIME_NAMESPACE
<|endoftext|>


<commit_before>#include <boost/python.hpp>
#include <boost/python/docstring_options.hpp>
#include <boost/numpy.hpp>
#include <boost/scoped_array.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>
#include <sstream>

#include "zhangsuen.h"

typedef unsigned char uchar_t;

namespace py = boost::python;
namespace np = boost::numpy;

/**
 * ndarray_to_mat(const np::ndarray& array, cv::Mat& result)
 *
 * Args:
 *  array: The input boost::numpy::ndarray. Must be 2D and the data type
 *         should be unsigned char. Also, the inner data alignment should be
 *         row major.
 *
 *  result: The resulting cv::Mat. It is assumed to be initialized to
 *          have appropriate number of rows and columns
 *
 * NOTE: The memory is NOT shared between the input ndarray and the resultant
 *       mat, unlike the reverse ndarray_to_mat() function.
 *
 */
void ndarray_to_mat(const np::ndarray& array, cv::Mat& result)
{
    // Ensure dtype is unsigned char
    if (array.get_dtype() != np::dtype::get_builtin<uchar_t>())
    {
        np::dtype need_dtype = np::dtype::get_builtin<uchar_t>();
        std::stringstream ss;
        ss << "Incorrect data type: "
           << "Expected: " << py::extract<const char*>(py::str(need_dtype))
           << ", Got: " << py::extract<const char*>(py::str(array.get_dtype()));
        PyErr_SetString(PyExc_TypeError, ss.str().c_str());
        py::throw_error_already_set();
    }
    // Ensure the array is 2D
    if (array.get_nd() != 2)
    {
        std::stringstream ss;
        ss << "Expecting a 2D array, got a " << array.get_nd() << " array.";
        PyErr_SetString(PyExc_TypeError, ss.str().c_str());
        py::throw_error_already_set();
    }
    // Ensure the array is row major
    if (!(array.get_flags() & np::ndarray::C_CONTIGUOUS))
    {
        PyErr_SetString(PyExc_TypeError, "Array must be row major contiguous");
        py::throw_error_already_set();
    }
    uchar_t *data = reinterpret_cast<uchar_t*>(array.get_data());
    int row_incr = array.strides(0) / sizeof(*data),
        col_incr = array.strides(1) / sizeof(*data);
    uchar_t *iter;
    int rows = array.shape(0), cols = array.shape(1);
    for (int i = 0; i < rows; i++)
    {
        iter = data + i * row_incr;
        uchar_t *row = result.ptr<uchar_t>(i);
        for (int j = 0; j < cols; j++)
        {
            *(row + j) = *(iter + j * col_incr);
        }
    }
}

// Return an ndarray that holds the same information as the underlying cv::Mat
// NOTE: The memory is shared between the returned ndarray and the input image.
static np::ndarray mat_to_ndarray(const cv::Mat& image, bool share_mem=true)
{
    uchar_t* data = image.data;
    // Our images are all grayscale, so the datatype will be (the numpy
    // equivalent of) uchar_t
    np::dtype type(np::dtype::get_builtin<uchar_t>());
    // This is the shape of the resulting ndarray, the number of rows and the
    // number of columns.
    py::tuple shape = py::make_tuple(image.rows, image.cols);
    // Strides for rows and columns. Given a current position in a 2D array,
    // the row stride is the number of bytes to be traversed from the current
    // position to get to the corresponding column in the next row. The column
    // stride is the number of bytes to be traversed from the current position
    // to get to the next element(column) in the same row.
    py::tuple strides = py::make_tuple(image.step[0] * sizeof(*data),
                                       image.step[1] * sizeof(*data));
    // The owner of the resulting ndarray. Since we are sharing the memory used
    // by the |image| and the resulting ndarray, we need the resultant to hold
    // a reference to this piece of memory. Since the resultant array holds
    // a reference to its owner object, it is prevented from being deallocated
    // by the GC.
    if (share_mem)
    {
        //TODO: This doesn't seem to work as the thinned image, when viewed
        //from Python, is all screwed up.
        np::ndarray array = np::from_data(data, type, shape, strides, py::object());
        return array;
    }
    else
    {
        np::ndarray array(np::zeros(shape, type));
        uchar_t *result_data = reinterpret_cast<uchar_t*>(array.get_data());
        int row_incr = array.strides(0) / sizeof(*data),
            col_incr = array.strides(1) / sizeof(*data);
        int mat_row_incr = image.step[0], mat_col_incr = image.step[1];
        for (int i = 0; i < image.rows; i++)
        {
            uchar_t *result_iter = result_data + i * row_incr;
            uchar_t *mat_iter = data + i * mat_row_incr;
            for (int j = 0; j < image.cols; j++, result_iter += col_incr, mat_iter += mat_col_incr)
            {
                *result_iter = *mat_iter;
            }
        }
        return array;
    }
}

void show(const std::string& window, const cv::Mat& mat)
{
    cv::namedWindow(window, CV_WINDOW_NORMAL);
    cv::imshow(window, mat);
    cv::waitKey(0);
}

void show(const std::string& window, const np::ndarray& array)
{
    cv::Mat img(array.shape(0), array.shape(1), CV_8U);
    ndarray_to_mat(array, img);
    show(window, img);
}

static np::ndarray wrap_thin(const np::ndarray& image,
                             bool need_boundary_smoothing=false,
                             bool need_acute_angle_emphasis=false,
                             bool destair=false)
{
    cv::Mat mat(image.shape(0), image.shape(1), CV_8U);
    ndarray_to_mat(image, mat);
    thin(mat, need_boundary_smoothing, need_acute_angle_emphasis, destair);
    return mat_to_ndarray(mat, false /* don't share memory */);
}

static const std::string docstring_thin(
"Perform Zhang-Suen thinning on `image`.\n\n"
"Args: \n"
"    image(numpy.ndarray): The binarized image to thin. A black foreground on\n"
"                          a lighter background is expected.\n\n"

"    need_boundary_smoothing(bool): Whether to perform the Stentiford boundary\n"
"                                   smoothing operation as a preprocessing step\n"
"                                   or not. This operation is performed to reduce\n"
"                                   line fuzz. Default false.\n\n"

"    need_acute_angle_emphasis(bool): Whether to perform the acute-angle emphasis\n"
"                                     preprocessing operation or not. This\n"
"                                     operation is used to decrease the necking\n"
"                                     effect. Default false.\n\n"

"    destair(bool): Whether to perform the Holt\'s destair post-processing. This\n"
"                   reduces the staircase effect in oblique lines.\n"
"                   Default false.\n");

BOOST_PYTHON_MODULE(zhangsuen)
{
    // Initialize the Python runtime and the Boost.Numpy lib.
    // This is a must! Otherwise we will get a segfault.
    Py_Initialize();
    np::initialize();
    py::docstring_options doc_options;
    doc_options.disable_cpp_signatures();
    py::def("thin", wrap_thin, docstring_thin.c_str());
}
<commit_msg>Breaking free from Boost::Numpy<commit_after>#include "../zhangsuen.h"
#include <Python.h>
#include <boost/python.hpp>
#include <boost/python/docstring_options.hpp>
#include <boost/scoped_array.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/core/core.hpp>
#include "numpy/ndarrayobject.h"
#include <string>
#include <iostream>

namespace py = boost::python;

static const std::string docstring_thin(
"Perform Zhang-Suen thinning on `image`.\n\n"
"Args: \n"
"    image(numpy.ndarray): The binarized image to thin. A black foreground on\n"
"                          a lighter background is expected.\n\n"

"    need_boundary_smoothing(bool): Whether to perform the Stentiford boundary\n"
"                                   smoothing operation as a preprocessing step\n"
"                                   or not. This operation is performed to reduce\n"
"                                   line fuzz. Default false.\n\n"

"    need_acute_angle_emphasis(bool): Whether to perform the acute-angle emphasis\n"
"                                     preprocessing operation or not. This\n"
"                                     operation is used to decrease the necking\n"
"                                     effect. Default false.\n\n"

"    destair(bool): Whether to perform the Holt\'s destair post-processing. This\n"
"                   reduces the staircase effect in oblique lines.\n"
"                   Default false.\n");

// The following conversion functions are taken from OpenCV's cv2.cpp file inside modules/python/src2 folder.
static PyObject* opencv_error = 0;

static int failmsg(const char *fmt, ...)
{
    char str[1000];

    va_list ap;
    va_start(ap, fmt);
    vsnprintf(str, sizeof(str), fmt, ap);
    va_end(ap);

    PyErr_SetString(PyExc_TypeError, str);
    return 0;
}

class PyAllowThreads
{
public:
    PyAllowThreads() : _state(PyEval_SaveThread()) {}
    ~PyAllowThreads()
    {
        PyEval_RestoreThread(_state);
    }
private:
    PyThreadState* _state;
};

class PyEnsureGIL
{
public:
    PyEnsureGIL() : _state(PyGILState_Ensure()) {}
    ~PyEnsureGIL()
    {
        PyGILState_Release(_state);
    }
private:
    PyGILState_STATE _state;
};

#define ERRWRAP2(expr) \
try \
{ \
    PyAllowThreads allowThreads; \
    expr; \
} \
catch (const cv::Exception &e) \
{ \
    PyErr_SetString(opencv_error, e.what()); \
    return 0; \
}

using namespace cv;

static PyObject* failmsgp(const char *fmt, ...)
{
  char str[1000];

  va_list ap;
  va_start(ap, fmt);
  vsnprintf(str, sizeof(str), fmt, ap);
  va_end(ap);

  PyErr_SetString(PyExc_TypeError, str);
  return 0;
}

static size_t REFCOUNT_OFFSET = (size_t)&(((PyObject*)0)->ob_refcnt) +
    (0x12345678 != *(const size_t*)"\x78\x56\x34\x12\0\0\0\0\0")*sizeof(int);

static inline PyObject* pyObjectFromRefcount(const int* refcount)
{
    return (PyObject*)((size_t)refcount - REFCOUNT_OFFSET);
}

static inline int* refcountFromPyObject(const PyObject* obj)
{
    return (int*)((size_t)obj + REFCOUNT_OFFSET);
}

class NumpyAllocator : public MatAllocator
{
public:
    NumpyAllocator() {}
    ~NumpyAllocator() {}

    void allocate(int dims, const int* sizes, int type, int*& refcount,
                  uchar*& datastart, uchar*& data, size_t* step)
    {
        PyEnsureGIL gil;

        int depth = CV_MAT_DEPTH(type);
        int cn = CV_MAT_CN(type);
        const int f = (int)(sizeof(size_t)/8);
        int typenum = depth == CV_8U ? NPY_UBYTE : depth == CV_8S ? NPY_BYTE :
                      depth == CV_16U ? NPY_USHORT : depth == CV_16S ? NPY_SHORT :
                      depth == CV_32S ? NPY_INT : depth == CV_32F ? NPY_FLOAT :
                      depth == CV_64F ? NPY_DOUBLE : f*NPY_ULONGLONG + (f^1)*NPY_UINT;
        int i;
        npy_intp _sizes[CV_MAX_DIM+1];
        for( i = 0; i < dims; i++ )
        {
            _sizes[i] = sizes[i];
        }

        if( cn > 1 )
        {
            /*if( _sizes[dims-1] == 1 )
                _sizes[dims-1] = cn;
            else*/
                _sizes[dims++] = cn;
        }

        PyObject* o = PyArray_SimpleNew(dims, _sizes, typenum);

        if(!o)
        {
            CV_Error_(CV_StsError, ("The numpy array of typenum=%d, ndims=%d can not be created", typenum, dims));
        }
        refcount = refcountFromPyObject(o);

        npy_intp* _strides = PyArray_STRIDES(o);
        for( i = 0; i < dims - (cn > 1); i++ )
            step[i] = (size_t)_strides[i];
        datastart = data = (uchar*)PyArray_DATA(o);
    }

    void deallocate(int* refcount, uchar*, uchar*)
    {
        PyEnsureGIL gil;
        if( !refcount )
            return;
        PyObject* o = pyObjectFromRefcount(refcount);
        Py_INCREF(o);
        Py_DECREF(o);
    }
};

NumpyAllocator g_numpyAllocator;

enum { ARG_NONE = 0, ARG_MAT = 1, ARG_SCALAR = 2 };

static int pyopencv_to(const PyObject* o, Mat& m, const char* name = "<unknown>", bool allowND=true)
{
    //NumpyAllocator g_numpyAllocator;
    if(!o || o == Py_None)
    {
        if( !m.data )
            m.allocator = &g_numpyAllocator;
        return true;
    }

    if( !PyArray_Check(o) )
    {
        failmsg("%s is not a numpy array", name);
        return false;
    }

    int typenum = PyArray_TYPE(o);
    int type = typenum == NPY_UBYTE ? CV_8U : typenum == NPY_BYTE ? CV_8S :
               typenum == NPY_USHORT ? CV_16U : typenum == NPY_SHORT ? CV_16S :
               typenum == NPY_INT || typenum == NPY_LONG ? CV_32S :
               typenum == NPY_FLOAT ? CV_32F :
               typenum == NPY_DOUBLE ? CV_64F : -1;

    if( type < 0 )
    {
        failmsg("%s data type = %d is not supported", name, typenum);
        return false;
    }

    int ndims = PyArray_NDIM(o);
    if(ndims >= CV_MAX_DIM)
    {
        failmsg("%s dimensionality (=%d) is too high", name, ndims);
        return false;
    }

    int size[CV_MAX_DIM+1];
    size_t step[CV_MAX_DIM+1], elemsize = CV_ELEM_SIZE1(type);
    const npy_intp* _sizes = PyArray_DIMS(o);
    const npy_intp* _strides = PyArray_STRIDES(o);
    bool transposed = false;

    for(int i = 0; i < ndims; i++)
    {
        size[i] = (int)_sizes[i];
        step[i] = (size_t)_strides[i];
    }

    if( ndims == 0 || step[ndims-1] > elemsize ) {
        size[ndims] = 1;
        step[ndims] = elemsize;
        ndims++;
    }

    if( ndims >= 2 && step[0] < step[1] )
    {
        std::swap(size[0], size[1]);
        std::swap(step[0], step[1]);
        transposed = true;
    }

    if( ndims == 3 && size[2] <= CV_CN_MAX && step[1] == elemsize*size[2] )
    {
        ndims--;
        type |= CV_MAKETYPE(0, size[2]);
    }

    if( ndims > 2 && !allowND )
    {
        failmsg("%s has more than 2 dimensions", name);
        return false;
    }

    m = Mat(ndims, size, type, PyArray_DATA(o), step);

    if( m.data )
    {
        m.refcount = refcountFromPyObject(o);
        m.addref(); // protect the original numpy array from deallocation
                    // (since Mat destructor will decrement the reference counter)
    };
    m.allocator = &g_numpyAllocator;

    if( transposed )
    {
        Mat tmp;
        tmp.allocator = &g_numpyAllocator;
        transpose(m, tmp);
        m = tmp;
    }
    return true;
}

static PyObject* pyopencv_from(const Mat& m)
{
    if( !m.data )
        Py_RETURN_NONE;
    Mat temp, *p = (Mat*)&m;
    if(!p->refcount || p->allocator != &g_numpyAllocator)
    {
        temp.allocator = &g_numpyAllocator;
        m.copyTo(temp);
        p = &temp;
    }
    p->addref();
    return pyObjectFromRefcount(p->refcount);
}

// The conversions functions above are taken from OpenCV. The following function is
// what we define to access the C++ code we are interested in.
PyObject* wrap_thin(PyObject* image, bool need_boundary_smoothing,
                         bool need_acute_angle_emphasis, bool destair)
{
    std::cout << "Here" << std::endl;
    cv::Mat cvImage;
    pyopencv_to(image, cvImage); // From OpenCV's source
    std::cout << "Converted to Mat" << std::endl;
    thin(cvImage, need_boundary_smoothing, need_acute_angle_emphasis, destair);
    std::cout << "Thinning done" << std::endl;
    PyObject *ret = pyopencv_from(cvImage);
    std::cout << "Converted to PyObject*" << std::endl;
    return ret;
}

static void init()
{
    Py_Initialize();
    import_array();
}

BOOST_PYTHON_MODULE(zhangsuen)
{
    init();
    py::docstring_options doc_options;
    doc_options.disable_cpp_signatures();
    py::def("thin", wrap_thin, docstring_thin.c_str());
}
<|endoftext|>


<commit_before>#include <algorithm>  /* max, min, next_permutation, prev_permutation */
#include <functional>  /* greater */
#include <list>
#include <sstream>  /* istringstream, ostringstream */
#include <stdexcept>  /* logic_error, invalid_argument */
#include <string>
#include <vector>
#include "Permutation.hpp"
#include "Groups/util.hpp"  /* lcm, factorial */
using namespace std;

Permutation::Permutation() : pmap(0), _even(1), _order(1), _lehmer(0) { }

Permutation::Permutation(const vector<int>& mapping, int ev, int ord, int lehm)
  : pmap(mapping), _even(ev), _order(ord), _lehmer(lehm) {
 while (!pmap.empty() && pmap.back() == (int) pmap.size()) pmap.pop_back();
}

Permutation Permutation::identity() {return Permutation(); }

int Permutation::operator()(int i) const {
 if (0 < i && i <= (int) pmap.size()) return pmap[i-1];
 else return i;
}

Permutation Permutation::operator*(const Permutation& other) const {
 int newdeg = max(degree(), other.degree());
 vector<int> newmap(newdeg);
 for (int i=0; i<newdeg; i++) newmap[i] = (*this)(other(i+1));
 return Permutation(newmap, _even != -1 && other._even != -1 ? _even == other._even : -1);
}

Permutation& Permutation::operator*=(const Permutation& other) {
 *this = *this * other;
 return *this;
}

Permutation::operator string() const {
 vector< vector<int> > cycles = toCycles();
 if (cycles.empty()) return string("1");
 else {
  ostringstream out;
  for (size_t i=0; i<cycles.size(); i++) {
   out << '(';
   for (size_t j=0; j<cycles[i].size(); j++) {
    if (j>0) out << ' ';
    out << cycles[i][j];
   }
   out << ')';
  }
  return out.str();
 }
}

Permutation::operator bool() const {return !pmap.empty(); }

int Permutation::degree() const {return pmap.size(); }

Permutation Permutation::inverse() const {
 vector<int> newmap(pmap.size());
 for (size_t i=0; i<pmap.size(); i++) newmap[pmap[i]-1] = i+1;
 return Permutation(newmap, _even, _order);
}

int Permutation::order() const {
 if (_order == -1) {
  _order = 1;
  vector< vector<int> > cycles = toCycles();
  for (size_t i=0; i<cycles.size(); i++) {
   _order = Groups::lcm(_order, cycles[i].size());
  }
 }
 return _order;
}

bool Permutation::isEven() const {
 if (_even == -1) {
  size_t lensum = 0;
  vector< vector<int> > cycles = toCycles();
  for (size_t i=0; i<cycles.size(); i++) {
   lensum += cycles[i].size() - 1;
  }
  _even = lensum % 2 == 0;
 }
 return bool(_even);
}

bool Permutation::isOdd() const {return !isEven(); }

int Permutation::sign() const {return isEven() ? 1 : -1; }

int Permutation::lehmer() const {
 if (_lehmer == -1) {
  _lehmer = 0;
  list<int> left;
  for (int i=degree(); i>0; i--) left.push_back(i);
  for (int x=degree(); x>0; x--) {
   int y = (*this)(x);
   list<int>::iterator iter = left.begin();
   int i=0;
   while (*iter != y) {iter++; i++; }
   left.erase(iter);
   _lehmer = _lehmer * x + i;
  }
 }
 return _lehmer;
}

Permutation Permutation::fromLehmer(int x) {
 int x0 = x;
 vector<int> mapping;
 vector<int>::iterator iter;
 for (int f=1; x>0; x /= f++) {
  int c = x % f;
  for (iter = mapping.begin(); iter != mapping.end(); iter++) {
   if (*iter >= c) (*iter)++;
  }
  mapping.push_back(c);
 }
 for (iter = mapping.begin(); iter != mapping.end(); iter++) {
  *iter = mapping.size() - *iter;
 }
 return Permutation(mapping, -1, -1, max(x0,0));
}

vector< vector<int> > Permutation::toCycles() const {
 vector<int> cmap(pmap);
 vector< vector<int> > cycles;
 for (int i=0; i<(int)cmap.size(); i++) {
  if (cmap[i] != 0 && cmap[i] != i+1) {
   int x = i+1;
   vector<int> cyke(1, x);
   int y = cmap[x-1];
   cmap[x-1] = 0;
   while (y != x) {
    cyke.push_back(y);
    int next = cmap[y-1];
    cmap[y-1] = 0;
    y = next;
   }
   cycles.push_back(cyke);
  }
 }
 return cycles;
}

/* Returns the permutation representing the transposition of the positive
 * integers `a` and `b` */
Permutation Permutation::transposition(int a, int b) {
 if (a < 1 || b < 1) throw invalid_argument("Permutation::transposition: arguments must be positive");
 if (a == b) return Permutation();
 else {
  int big = max(a,b), small = min(a,b);
  vector<int> mapping(big);
  for (int i=0; i<big; i++)
   mapping[i] = i+1 == a ? b : i+1 == b ? a : i+1;
  /* For $a<b$, $Lehmer((a b)) = (b-a) (b-1)! + \sum_{i=a}^{b-2} i!$ */
  int lehmer = 0, fac = Groups::factorial(small);
  for (int i=small; i<big-1; i++) {
   lehmer += fac;
   fac *= i+1;
  }
  lehmer += fac * (big-small);
  return Permutation(mapping, 0, 2, lehmer);
 }
}

Permutation Permutation::firstOfDegree(int n) {
 if (n <= 1) return identity();
 else return transposition(n, n-1);
}

/* This comparison method produces the same ordering as the modified Lehmer
 * codes. */
int Permutation::cmp(const Permutation& other) const {
 if (degree() < other.degree()) return -1;
 if (degree() > other.degree()) return  1;
 for (int i=degree()-1; i>=0; i--) {
  if (pmap[i] > other.pmap[i]) return -1;
  if (pmap[i] < other.pmap[i]) return 1;
 }
 return 0;
}

Permutation& Permutation::operator++() {
 if (degree() == 0) {
  *this = transposition(1,2);
 } else if (!next_permutation(pmap.rbegin(), pmap.rend(), greater<int>())) {
  *this = firstOfDegree(degree()+1);
 } else {
  _even = -1;
  _order = -1;
  if (_lehmer != -1) _lehmer++;
 }
 return *this;
}

Permutation Permutation::operator++(int) {
 Permutation tmp(*this);
 ++*this;
 return tmp;
}

Permutation& Permutation::operator--() {
 if (degree() == 0)
  throw logic_error("Permutation::operator--: cannot decrement identity");
 prev_permutation(pmap.rbegin(), pmap.rend(), greater<int>());
 while (!pmap.empty() && pmap.back() == (int) pmap.size()) pmap.pop_back();
 _even = -1;
 _order = -1;
 if (_lehmer != -1) _lehmer--;
 return *this;
}

Permutation Permutation::operator--(int) {
 Permutation tmp(*this);
 --*this;
 return tmp;
}

bool Permutation::disjoint(const Permutation& other) const {
 for (int i=0; i < min(degree(), other.degree()); i++) {
  if (i+1 != pmap[i] && i+1 != other.pmap[i]) return false;
 }
 return true;
}

vector<Permutation> Permutation::s_n(int degree) {
 vector<Permutation> elems(Groups::factorial(degree), identity());
 vector<Permutation>::iterator iter = elems.begin();
 Permutation p = *iter;
 for (iter++, p++; iter != elems.end(); iter++, p++) {
  *iter = p;
 }
 return elems;
}

vector<int> Permutation::toImage() const {return pmap; }

Permutation Permutation::fromImage(const vector<int>& img) {
 vector<bool> used(img.size(), false);
 vector<int>::const_iterator iter;
 for (iter = img.begin(); iter != img.end(); iter++) {
  if (*iter < 1)
   throw invalid_argument("Permutation::fromImage: values must be positive");
  if (*iter > (int) img.size())
   throw invalid_argument("Permutation::fromImage: value missing from input");
  if (used[*iter-1])
   throw invalid_argument("Permutation::fromImage: value repeated in input");
  used[*iter-1] = true;
 }
 return Permutation(img);
}

Permutation Permutation::parse(const string& s) {
 istringstream in(s);
 char c;
 in >> ws;
 if (in >> c) {
  if (c == '(') {
   vector< vector<int> > cycles;
   for (;;) {
    vector<int> cyc;
    int i;
    while (in >> i) cyc.push_back(i);
    if (cyc.empty()) throw invalid_argument("Permutation::parse");
    in >> ws;
    if ((in >> c) && c == ')') {
     cycles.push_back(cyc);
     in >> ws;
     if (ws.eof()) return fromCycles<vector< vector<int> >::const_iterator>(cycles.begin(), cycles.end());
     else if ((in >> c) && c == '(') continue;
     else throw invalid_argument("Permutation::parse");
    } else throw invalid_argument("Permutation::parse");
   }
  } else if (c == '1') {
   in >> ws;
   if (in.eof()) return identity();
   else throw invalid_argument("Permutation::parse");
  } else throw invalid_argument("Permutation::parse");
 } else throw invalid_argument("Permutation::parse");
}
<commit_msg>Fixed compilation error.  Stupid ambiguous compiler messages.<commit_after>#include <algorithm>  /* max, min, next_permutation, prev_permutation */
#include <functional>  /* greater */
#include <list>
#include <sstream>  /* istringstream, ostringstream */
#include <stdexcept>  /* logic_error, invalid_argument */
#include <string>
#include <vector>
#include "Permutation.hpp"
#include "Groups/util.hpp"  /* lcm, factorial */
using namespace std;

Permutation::Permutation() : pmap(0), _even(1), _order(1), _lehmer(0) { }

Permutation::Permutation(const vector<int>& mapping, int ev, int ord, int lehm)
  : pmap(mapping), _even(ev), _order(ord), _lehmer(lehm) {
 while (!pmap.empty() && pmap.back() == (int) pmap.size()) pmap.pop_back();
}

Permutation Permutation::identity() {return Permutation(); }

int Permutation::operator()(int i) const {
 if (0 < i && i <= (int) pmap.size()) return pmap[i-1];
 else return i;
}

Permutation Permutation::operator*(const Permutation& other) const {
 int newdeg = max(degree(), other.degree());
 vector<int> newmap(newdeg);
 for (int i=0; i<newdeg; i++) newmap[i] = (*this)(other(i+1));
 return Permutation(newmap, _even != -1 && other._even != -1 ? _even == other._even : -1);
}

Permutation& Permutation::operator*=(const Permutation& other) {
 *this = *this * other;
 return *this;
}

Permutation::operator string() const {
 vector< vector<int> > cycles = toCycles();
 if (cycles.empty()) return string("1");
 else {
  ostringstream out;
  for (size_t i=0; i<cycles.size(); i++) {
   out << '(';
   for (size_t j=0; j<cycles[i].size(); j++) {
    if (j>0) out << ' ';
    out << cycles[i][j];
   }
   out << ')';
  }
  return out.str();
 }
}

Permutation::operator bool() const {return !pmap.empty(); }

int Permutation::degree() const {return pmap.size(); }

Permutation Permutation::inverse() const {
 vector<int> newmap(pmap.size());
 for (size_t i=0; i<pmap.size(); i++) newmap[pmap[i]-1] = i+1;
 return Permutation(newmap, _even, _order);
}

int Permutation::order() const {
 if (_order == -1) {
  _order = 1;
  vector< vector<int> > cycles = toCycles();
  for (size_t i=0; i<cycles.size(); i++) {
   _order = Groups::lcm(_order, cycles[i].size());
  }
 }
 return _order;
}

bool Permutation::isEven() const {
 if (_even == -1) {
  size_t lensum = 0;
  vector< vector<int> > cycles = toCycles();
  for (size_t i=0; i<cycles.size(); i++) {
   lensum += cycles[i].size() - 1;
  }
  _even = lensum % 2 == 0;
 }
 return bool(_even);
}

bool Permutation::isOdd() const {return !isEven(); }

int Permutation::sign() const {return isEven() ? 1 : -1; }

int Permutation::lehmer() const {
 if (_lehmer == -1) {
  _lehmer = 0;
  list<int> left;
  for (int i=degree(); i>0; i--) left.push_back(i);
  for (int x=degree(); x>0; x--) {
   int y = (*this)(x);
   list<int>::iterator iter = left.begin();
   int i=0;
   while (*iter != y) {iter++; i++; }
   left.erase(iter);
   _lehmer = _lehmer * x + i;
  }
 }
 return _lehmer;
}

Permutation Permutation::fromLehmer(int x) {
 int x0 = x;
 vector<int> mapping;
 vector<int>::iterator iter;
 for (int f=1; x>0; x /= f++) {
  int c = x % f;
  for (iter = mapping.begin(); iter != mapping.end(); iter++) {
   if (*iter >= c) (*iter)++;
  }
  mapping.push_back(c);
 }
 for (iter = mapping.begin(); iter != mapping.end(); iter++) {
  *iter = mapping.size() - *iter;
 }
 return Permutation(mapping, -1, -1, max(x0,0));
}

vector< vector<int> > Permutation::toCycles() const {
 vector<int> cmap(pmap);
 vector< vector<int> > cycles;
 for (int i=0; i<(int)cmap.size(); i++) {
  if (cmap[i] != 0 && cmap[i] != i+1) {
   int x = i+1;
   vector<int> cyke(1, x);
   int y = cmap[x-1];
   cmap[x-1] = 0;
   while (y != x) {
    cyke.push_back(y);
    int next = cmap[y-1];
    cmap[y-1] = 0;
    y = next;
   }
   cycles.push_back(cyke);
  }
 }
 return cycles;
}

/* Returns the permutation representing the transposition of the positive
 * integers `a` and `b` */
Permutation Permutation::transposition(int a, int b) {
 if (a < 1 || b < 1) throw invalid_argument("Permutation::transposition: arguments must be positive");
 if (a == b) return Permutation();
 else {
  int big = max(a,b), small = min(a,b);
  vector<int> mapping(big);
  for (int i=0; i<big; i++)
   mapping[i] = i+1 == a ? b : i+1 == b ? a : i+1;
  /* For $a<b$, $Lehmer((a b)) = (b-a) (b-1)! + \sum_{i=a}^{b-2} i!$ */
  int lehmer = 0, fac = Groups::factorial(small);
  for (int i=small; i<big-1; i++) {
   lehmer += fac;
   fac *= i+1;
  }
  lehmer += fac * (big-small);
  return Permutation(mapping, 0, 2, lehmer);
 }
}

Permutation Permutation::firstOfDegree(int n) {
 if (n <= 1) return identity();
 else return transposition(n, n-1);
}

/* This comparison method produces the same ordering as the modified Lehmer
 * codes. */
int Permutation::cmp(const Permutation& other) const {
 if (degree() < other.degree()) return -1;
 if (degree() > other.degree()) return  1;
 for (int i=degree()-1; i>=0; i--) {
  if (pmap[i] > other.pmap[i]) return -1;
  if (pmap[i] < other.pmap[i]) return 1;
 }
 return 0;
}

Permutation& Permutation::operator++() {
 if (degree() == 0) {
  *this = transposition(1,2);
 } else if (!next_permutation(pmap.rbegin(), pmap.rend(), greater<int>())) {
  *this = firstOfDegree(degree()+1);
 } else {
  _even = -1;
  _order = -1;
  if (_lehmer != -1) _lehmer++;
 }
 return *this;
}

Permutation Permutation::operator++(int) {
 Permutation tmp(*this);
 ++*this;
 return tmp;
}

Permutation& Permutation::operator--() {
 if (degree() == 0)
  throw logic_error("Permutation::operator--: cannot decrement identity");
 prev_permutation(pmap.rbegin(), pmap.rend(), greater<int>());
 while (!pmap.empty() && pmap.back() == (int) pmap.size()) pmap.pop_back();
 _even = -1;
 _order = -1;
 if (_lehmer != -1) _lehmer--;
 return *this;
}

Permutation Permutation::operator--(int) {
 Permutation tmp(*this);
 --*this;
 return tmp;
}

bool Permutation::disjoint(const Permutation& other) const {
 for (int i=0; i < min(degree(), other.degree()); i++) {
  if (i+1 != pmap[i] && i+1 != other.pmap[i]) return false;
 }
 return true;
}

vector<Permutation> Permutation::s_n(int degree) {
 vector<Permutation> elems(Groups::factorial(degree), identity());
 vector<Permutation>::iterator iter = elems.begin();
 Permutation p = *iter;
 for (iter++, p++; iter != elems.end(); iter++, p++) {
  *iter = p;
 }
 return elems;
}

vector<int> Permutation::toImage() const {return pmap; }

Permutation Permutation::fromImage(const vector<int>& img) {
 vector<bool> used(img.size(), false);
 vector<int>::const_iterator iter;
 for (iter = img.begin(); iter != img.end(); iter++) {
  if (*iter < 1)
   throw invalid_argument("Permutation::fromImage: values must be positive");
  if (*iter > (int) img.size())
   throw invalid_argument("Permutation::fromImage: value missing from input");
  if (used[*iter-1])
   throw invalid_argument("Permutation::fromImage: value repeated in input");
  used[*iter-1] = true;
 }
 return Permutation(img);
}

Permutation Permutation::parse(const string& s) {
 istringstream in(s);
 char c;
 in >> ws;
 if (in >> c) {
  if (c == '(') {
   vector< vector<int> > cycles;
   for (;;) {
    vector<int> cyc;
    int i;
    while (in >> i) cyc.push_back(i);
    if (cyc.empty()) throw invalid_argument("Permutation::parse");
    in >> ws;
    if ((in >> c) && c == ')') {
     cycles.push_back(cyc);
     in >> ws;
     if (in.eof()) return fromCycles(cycles.begin(), cycles.end());
     else if ((in >> c) && c == '(') continue;
     else throw invalid_argument("Permutation::parse");
    } else throw invalid_argument("Permutation::parse");
   }
  } else if (c == '1') {
   in >> ws;
   if (in.eof()) return identity();
   else throw invalid_argument("Permutation::parse");
  } else throw invalid_argument("Permutation::parse");
 } else throw invalid_argument("Permutation::parse");
}
<|endoftext|>


<commit_before>#ifndef OPENBFME_STRING_HPP
#define OPENBFME_STRING_HPP

#include "types.hpp"
#include <vector>

namespace OpenBFME {

struct Printable{
    // TODO - support more stuffs.
    enum Type{
        Integer, Decimal, Character, String
    };
    Type type;
    union {
        integer num;
        decimal dec;
        character ch;
        cstring str;
    };

    template<typename T> Printable(T n, typename std::enable_if<std::is_integral<T>::value>::type* = 0) : type(Integer), num(n) {}
    template<typename T> Printable(T d, typename std::enable_if<std::is_floating_point<T>::value>::type* = 0) : type(Decimal), dec(d) {}
    inline Printable(char c) : type(Character), ch(c) {}
    inline Printable(const std::string& s) : type(String), str(s.c_str()) {}
    inline Printable(cstring s) : type(String), str(s) {}
};

string to_base(unsigned int value, unsigned int base, char start = 'a');

string format(const string& fmt, std::vector<Printable> args);

/*!
 * \brief Formats the string with a subset of printf syntax.
 * \todo Support more printf options.
 * \todo Make it more safe.
 */
template<typename... Args> inline string format(const string& fmt, Args... args){
    return format(fmt, std::vector<Printable>{ args... });
}

}

#endif //OPENBFME_STRING_HPP
<commit_msg>added an empty format function for when format is called with no additional arguments.<commit_after>#ifndef OPENBFME_STRING_HPP
#define OPENBFME_STRING_HPP

#include "types.hpp"
#include <vector>

namespace OpenBFME {

struct Printable{
    // TODO - support more stuffs.
    enum Type{
        Integer, Decimal, Character, String
    };
    Type type;
    union {
        integer num;
        decimal dec;
        character ch;
        cstring str;
    };

    template<typename T> Printable(T n, typename std::enable_if<std::is_integral<T>::value>::type* = 0) : type(Integer), num(n) {}
    template<typename T> Printable(T d, typename std::enable_if<std::is_floating_point<T>::value>::type* = 0) : type(Decimal), dec(d) {}
    inline Printable(char c) : type(Character), ch(c) {}
    inline Printable(const std::string& s) : type(String), str(s.c_str()) {}
    inline Printable(cstring s) : type(String), str(s) {}
};

string to_base(unsigned int value, unsigned int base, char start = 'a');

inline string format(const string& fmt){ return fmt; }

string format(const string& fmt, std::vector<Printable> args);

/*!
 * \brief Formats the string with a subset of printf syntax.
 * \todo Support more printf options.
 * \todo Make it more safe.
 */
template<typename... Args> inline string format(const string& fmt, Args... args){
    return format(fmt, std::vector<Printable>{ args... });
}

}

#endif //OPENBFME_STRING_HPP
<|endoftext|>


<commit_before>/** \file    add_synonyms.cc
 *  \brief   Generic version for augmenting title data with synonyms found
             in the authority data
 *  \author  Johannes Riedl
 */

/*
    Copyright (C) 2016, Library of the University of Tübingen

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/*  We offer a list of tags and subfields where the primary data resides along
    with a list of tags and subfields where the synonym data is found and
    a list of unused fields in the title data where the synonyms can be stored
*/

#include <iostream>
#include <map>
#include <vector>
#include <cstdlib>
#include "Compiler.h"
#include "FileUtil.h"
#include "MarcUtil.h"
#include "MarcXmlWriter.h"
#include "MediaTypeUtil.h"
#include "StringUtil.h"
#include "Subfields.h"
#include "util.h"

static unsigned modified_count(0);
static unsigned record_count(0);

void Usage() {
    std::cerr << "Usage: " << ::progname << " master_marc_input norm_data_marc_input marc_output\n";
    std::exit(EXIT_FAILURE);
}

std::string GetTag(const std::string &tag_and_subfields_spec) {
    return tag_and_subfields_spec.substr(0, 3);
}

std::string GetSubfields(const std::string &tag_and_subfields_spec) {
    return tag_and_subfields_spec.substr(3);
}

void ExtractSynonyms(File * const norm_data_marc_input, const std::set<std::string> &primary_tags_and_subfield_codes,
                     const std::set<std::string> &synonym_tags_and_subfield_codes,  std::vector<std::map<std::string, std::string>> * const synonym_maps)
{
    while (const MarcUtil::Record record = MarcUtil::Record::XmlFactory(norm_data_marc_input)) {
        std::set<std::string>::const_iterator primary;
        std::set<std::string>::const_iterator synonym;
        unsigned int i(0);
        for (primary = primary_tags_and_subfield_codes.begin(), synonym = synonym_tags_and_subfield_codes.begin();
            primary != primary_tags_and_subfield_codes.end();
            ++primary, ++synonym, ++i)
        {
            // Fill maps with synonyms
            std::vector<std::string> primary_values;
            std::vector<std::string> synonym_values;

            if (record.extractSubfields(GetTag(*primary), GetSubfields(*primary), &primary_values) and
                record.extractSubfields(GetTag(*synonym), GetSubfields(*synonym), &synonym_values))
                    (*synonym_maps)[i].emplace(StringUtil::Join(primary_values, ','), StringUtil::Join(synonym_values, ','));
        }
    }
}

void ProcessRecord(MarcUtil::Record * const record, const std::vector<std::map<std::string, std::string>> &synonym_maps,
                   const std::set<std::string> &primary_tags_and_subfield_codes,
                   const std::set<std::string> &output_tags_and_subfield_codes)
{
    std::set<std::string>::const_iterator primary;
    std::set<std::string>::const_iterator output;
    unsigned int i(0);

    if (primary_tags_and_subfield_codes.size() == output_tags_and_subfield_codes.size()) {

        for (primary = primary_tags_and_subfield_codes.begin(), output = output_tags_and_subfield_codes.begin();
            primary != primary_tags_and_subfield_codes.end();
            ++primary, ++output, ++i)
        {
            std::vector<std::string> primary_values;
            std::string synonyms;
            if (record->extractSubfields(GetTag(*primary), GetSubfields(*primary), &primary_values)) {

                // First case: Look up synonyms only in one category
                if (i < synonym_maps.size()) {
                    const auto synonym_map(synonym_maps[i]);
                    synonyms = synonym_map.find(StringUtil::Join(primary_values, ','))->second;
                }

                // Second case: Look up synonyms in all categories
                else {
                    for (auto sm : synonym_maps)
                       synonyms += sm[StringUtil::Join(primary_values, ',')];
                }

                if (synonyms.empty())
                    continue;

                // Insert synonyms
                // Abort if field is already populated
                std::string tag(GetTag(*output));
                if (record->getFieldIndex(tag) != -1)
                    Error("Field with tag " + tag + " is not empty for PPN " + record->getControlNumber() + '\n');
                std::string subfield_spec = GetSubfields(*output);
                if (subfield_spec.size() != 1)
                    Error("We currently only support a single subfield and thus specifying " + subfield_spec + " as output subfield is not valid\n");
                Subfields subfields(' ', ' '); // <- indicators must be set explicitly although empty
                subfields.addSubfield(subfield_spec.at(0), synonyms);
                if (not(record->insertField(tag, subfields.toString())))
                    Warning("Could not insert field " + tag + " for PPN " + record->getControlNumber() + '\n');
                ++modified_count;
            }
        }
    }
}

void InsertSynonyms(File * const marc_input, File * marc_output, const std::set<std::string> &primary_tags_and_subfield_codes,
                    const std::set<std::string> &output_tags_and_subfield_codes, std::vector<std::map<std::string, std::string>> &synonym_maps)
{
    MarcXmlWriter xml_writer(marc_output);

    while (MarcUtil::Record record = MarcUtil::Record::XmlFactory(marc_input)) {
        ProcessRecord(&record, synonym_maps, primary_tags_and_subfield_codes, output_tags_and_subfield_codes);
        record.write(&xml_writer);
        ++record_count;
    }

    std::cerr << "Modified " << modified_count << " of " << record_count << " record(s).\n";
}

int main(int argc, char **argv) {
    ::progname = argv[0];

    if (argc != 4)
        Usage();

    const std::string marc_input_filename(argv[1]);
    std::unique_ptr<File> marc_input(FileUtil::OpenInputFileOrDie(marc_input_filename));

    const std::string norm_data_marc_input_filename(argv[2]);
    std::unique_ptr<File> norm_data_marc_input(FileUtil::OpenInputFileOrDie(norm_data_marc_input_filename));

    const std::string marc_output_filename(argv[3]);
    if (unlikely(marc_input_filename == marc_output_filename))
        Error("Title data input file name equals output file name!");
    if (unlikely(norm_data_marc_input_filename == marc_output_filename))
        Error("Authority data input file name equals output file name!");

    File marc_output(marc_output_filename, "w");
    if (not marc_output)
        Error("can't open \"" + marc_output_filename + "\" for writing!");

    try {
        // Determine possible mappings
        const std::string AUTHORITY_DATA_PRIMARY_SPEC("110abcd:111abcd:130abcd:150abcd:151abcd");
        const std::string AUTHORITY_DATA_SYNONYM_SPEC("410abcd:411abcd:430abcd:450abcd:451abcd");
        const std::string TITLE_DATA_PRIMARY_SPEC("610abcd:611abcd:630abcd:650abcd:651abcd:689abcd");
        const std::string TITLE_DATA_UNUSED_FIELDS_FOR_SYNONYMS("180a:181a:182a:183a:184a:185a");

        // Determine fields to handle
        std::set<std::string> primary_tags_and_subfield_codes;
        std::set<std::string> synonym_tags_and_subfield_codes;
        std::set<std::string> input_tags_and_subfield_codes;
        std::set<std::string> output_tags_and_subfield_codes;

        if (unlikely(StringUtil::Split(AUTHORITY_DATA_PRIMARY_SPEC, ":", &primary_tags_and_subfield_codes) < 1))
            Error("Need at least one primary field");

        if (unlikely(StringUtil::Split(AUTHORITY_DATA_SYNONYM_SPEC, ":", &synonym_tags_and_subfield_codes) < 1))
            Error("Need at least one synonym field");

        if (unlikely(StringUtil::Split(TITLE_DATA_PRIMARY_SPEC, ":", &input_tags_and_subfield_codes) < 1))
            Error("Need at least one input field");

        if (unlikely(StringUtil::Split(TITLE_DATA_UNUSED_FIELDS_FOR_SYNONYMS, ":", &output_tags_and_subfield_codes) < 1))
            Error("Need at least one output field");

        unsigned num_of_authority_entries(primary_tags_and_subfield_codes.size());

        if (synonym_tags_and_subfield_codes.size() != num_of_authority_entries)
            Error("Number of authority primary specs must match number of synonym specs");
        if (input_tags_and_subfield_codes.size() != output_tags_and_subfield_codes.size())
            Error("Number of fields title entry specs must match number of output specs");

        std::vector<std::map<std::string, std::string>> synonym_maps(num_of_authority_entries, std::map<std::string, std::string>());

        // Extract the synonyms from authority data
        ExtractSynonyms(norm_data_marc_input.get(), primary_tags_and_subfield_codes, synonym_tags_and_subfield_codes, &synonym_maps);

        // Iterate over the title data
        InsertSynonyms(marc_input.get(), &marc_output, input_tags_and_subfield_codes, output_tags_and_subfield_codes, synonym_maps);

    } catch (const std::exception &x) {
        Error("caught exception: " + std::string(x.what()));
    }
}
<commit_msg>Update add_synonyms.cc<commit_after>/** \file    add_synonyms.cc
 *  \brief   Generic version for augmenting title data with synonyms found
             in the authority data
 *  \author  Johannes Riedl
 */

/*
    Copyright (C) 2016, Library of the University of Tübingen

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/*  We offer a list of tags and subfields where the primary data resides along
    with a list of tags and subfields where the synonym data is found and
    a list of unused fields in the title data where the synonyms can be stored
*/

#include <iostream>
#include <map>
#include <vector>
#include <cstdlib>
#include "Compiler.h"
#include "FileUtil.h"
#include "MarcUtil.h"
#include "MarcXmlWriter.h"
#include "MediaTypeUtil.h"
#include "StringUtil.h"
#include "Subfields.h"
#include "util.h"

static unsigned modified_count(0);
static unsigned record_count(0);

void Usage() {
    std::cerr << "Usage: " << ::progname << " master_marc_input norm_data_marc_input marc_output\n";
    std::exit(EXIT_FAILURE);
}

std::string GetTag(const std::string &tag_and_subfields_spec) {
    return tag_and_subfields_spec.substr(0, 3);
}

std::string GetSubfieldCodes(const std::string &tag_and_subfields_spec) {
    return tag_and_subfields_spec.substr(3);
}

void ExtractSynonyms(File * const norm_data_marc_input, const std::set<std::string> &primary_tags_and_subfield_codes,
                     const std::set<std::string> &synonym_tags_and_subfield_codes,  std::vector<std::map<std::string, std::string>> * const synonym_maps)
{
    while (const MarcUtil::Record record = MarcUtil::Record::XmlFactory(norm_data_marc_input)) {
        std::set<std::string>::const_iterator primary;
        std::set<std::string>::const_iterator synonym;
        unsigned int i(0);
        for (primary = primary_tags_and_subfield_codes.begin(), synonym = synonym_tags_and_subfield_codes.begin();
            primary != primary_tags_and_subfield_codes.end();
            ++primary, ++synonym, ++i)
        {
            // Fill maps with synonyms
            std::vector<std::string> primary_values;
            std::vector<std::string> synonym_values;

            if (record.extractSubfields(GetTag(*primary), GetSubfieldCodes(*primary), &primary_values) and
                record.extractSubfields(GetTag(*synonym), GetSubfieldCodes(*synonym), &synonym_values))
                    (*synonym_maps)[i].emplace(StringUtil::Join(primary_values, ','), StringUtil::Join(synonym_values, ','));
        }
    }
}

void ProcessRecord(MarcUtil::Record * const record, const std::vector<std::map<std::string, std::string>> &synonym_maps,
                   const std::set<std::string> &primary_tags_and_subfield_codes,
                   const std::set<std::string> &output_tags_and_subfield_codes)
{
    std::set<std::string>::const_iterator primary;
    std::set<std::string>::const_iterator output;
    unsigned int i(0);

    if (primary_tags_and_subfield_codes.size() == output_tags_and_subfield_codes.size()) {
        for (primary = primary_tags_and_subfield_codes.begin(), output = output_tags_and_subfield_codes.begin();
            primary != primary_tags_and_subfield_codes.end();
            ++primary, ++output, ++i)
        {
            std::vector<std::string> primary_values;
            std::string synonyms;
            if (record->extractSubfields(GetTag(*primary), GetSubfieldCodes(*primary), &primary_values)) {
                // First case: Look up synonyms only in one category
                if (i < synonym_maps.size()) {
                    const auto synonym_map(synonym_maps[i]);
                    synonyms = synonym_map.find(StringUtil::Join(primary_values, ','))->second;
                }

                // Second case: Look up synonyms in all categories
                else {
                    for (auto sm : synonym_maps)
                       synonyms += sm[StringUtil::Join(primary_values, ',')];
                }

                if (synonyms.empty())
                    continue;

                // Insert synonyms
                // Abort if field is already populated
                std::string tag(GetTag(*output));
                if (record->getFieldIndex(tag) != -1)
                    Error("Field with tag " + tag + " is not empty for PPN " + record->getControlNumber() + '\n');
                std::string subfield_spec = GetSubfieldCodes(*output);
                if (subfield_spec.size() != 1)
                    Error("We currently only support a single subfield and thus specifying " + subfield_spec + " as output subfield is not valid\n");
                Subfields subfields(' ', ' '); // <- indicators must be set explicitly although empty
                subfields.addSubfield(subfield_spec.at(0), synonyms);
                if (not(record->insertField(tag, subfields.toString())))
                    Warning("Could not insert field " + tag + " for PPN " + record->getControlNumber() + '\n');
                ++modified_count;
            }
        }
    }
}

void InsertSynonyms(File * const marc_input, File * marc_output, const std::set<std::string> &primary_tags_and_subfield_codes,
                    const std::set<std::string> &output_tags_and_subfield_codes, std::vector<std::map<std::string, std::string>> &synonym_maps)
{
    MarcXmlWriter xml_writer(marc_output);

    while (MarcUtil::Record record = MarcUtil::Record::XmlFactory(marc_input)) {
        ProcessRecord(&record, synonym_maps, primary_tags_and_subfield_codes, output_tags_and_subfield_codes);
        record.write(&xml_writer);
        ++record_count;
    }

    std::cerr << "Modified " << modified_count << " of " << record_count << " record(s).\n";
}

int main(int argc, char **argv) {
    ::progname = argv[0];

    if (argc != 4)
        Usage();

    const std::string marc_input_filename(argv[1]);
    std::unique_ptr<File> marc_input(FileUtil::OpenInputFileOrDie(marc_input_filename));

    const std::string norm_data_marc_input_filename(argv[2]);
    std::unique_ptr<File> norm_data_marc_input(FileUtil::OpenInputFileOrDie(norm_data_marc_input_filename));

    const std::string marc_output_filename(argv[3]);
    if (unlikely(marc_input_filename == marc_output_filename))
        Error("Title data input file name equals output file name!");
    if (unlikely(norm_data_marc_input_filename == marc_output_filename))
        Error("Authority data input file name equals output file name!");

    File marc_output(marc_output_filename, "w");
    if (not marc_output)
        Error("can't open \"" + marc_output_filename + "\" for writing!");

    try {
        // Determine possible mappings
        const std::string AUTHORITY_DATA_PRIMARY_SPEC("110abcd:111abcd:130abcd:150abcd:151abcd");
        const std::string AUTHORITY_DATA_SYNONYM_SPEC("410abcd:411abcd:430abcd:450abcd:451abcd");
        const std::string TITLE_DATA_PRIMARY_SPEC("610abcd:611abcd:630abcd:650abcd:651abcd:689abcd");
        const std::string TITLE_DATA_UNUSED_FIELDS_FOR_SYNONYMS("180a:181a:182a:183a:184a:185a");

        // Determine fields to handle
        std::set<std::string> primary_tags_and_subfield_codes;
        std::set<std::string> synonym_tags_and_subfield_codes;
        std::set<std::string> input_tags_and_subfield_codes;
        std::set<std::string> output_tags_and_subfield_codes;

        if (unlikely(StringUtil::Split(AUTHORITY_DATA_PRIMARY_SPEC, ":", &primary_tags_and_subfield_codes) < 1))
            Error("Need at least one primary field");

        if (unlikely(StringUtil::Split(AUTHORITY_DATA_SYNONYM_SPEC, ":", &synonym_tags_and_subfield_codes) < 1))
            Error("Need at least one synonym field");

        if (unlikely(StringUtil::Split(TITLE_DATA_PRIMARY_SPEC, ":", &input_tags_and_subfield_codes) < 1))
            Error("Need at least one input field");

        if (unlikely(StringUtil::Split(TITLE_DATA_UNUSED_FIELDS_FOR_SYNONYMS, ":", &output_tags_and_subfield_codes) < 1))
            Error("Need at least one output field");

        unsigned num_of_authority_entries(primary_tags_and_subfield_codes.size());

        if (synonym_tags_and_subfield_codes.size() != num_of_authority_entries)
            Error("Number of authority primary specs must match number of synonym specs");
        if (input_tags_and_subfield_codes.size() != output_tags_and_subfield_codes.size())
            Error("Number of fields title entry specs must match number of output specs");

        std::vector<std::map<std::string, std::string>> synonym_maps(num_of_authority_entries, std::map<std::string, std::string>());

        // Extract the synonyms from authority data
        ExtractSynonyms(norm_data_marc_input.get(), primary_tags_and_subfield_codes, synonym_tags_and_subfield_codes, &synonym_maps);

        // Iterate over the title data
        InsertSynonyms(marc_input.get(), &marc_output, input_tags_and_subfield_codes, output_tags_and_subfield_codes, synonym_maps);

    } catch (const std::exception &x) {
        Error("caught exception: " + std::string(x.what()));
    }
}
<|endoftext|>