<commit_before>// Copyright 2017 Global Phasing Ltd.
//
// This program analyses PDB or mmCIF files, printing similar things
// as CCP4 RWCONTENTS: weight, Matthews coefficient, etc.

#include <cassert>
#include <cstdio>
#include <gemmi/symmetry.hpp>
#include <gemmi/resinfo.hpp>
#include <gemmi/calculate.hpp>
#include <gemmi/gzread.hpp>
#include <gemmi/polyheur.hpp>  // for setup_entities, calculate_sequence_weight
#include "histogram.h"         // for print_histogram
#define GEMMI_PROG contents
#include "options.h"

using namespace gemmi;
using std::printf;

enum OptionIndex { Dihedrals=4, Bfactors, NoContentInfo };

static const option::Descriptor Usage[] = {
  { NoOp, 0, "", "", Arg::None,
    "Usage:\n " EXE_NAME " [options] INPUT[...]"
    "\nAnalyses content of a PDB or mmCIF."},
  CommonUsage[Help],
  CommonUsage[Version],
  CommonUsage[Verbose],
  { Bfactors, 0, "b", "", Arg::None,
    "  -b  \tPrint statistics of isotropic ADPs (B-factors)." },
  { Dihedrals, 0, "", "dihedrals", Arg::None,
    "  --dihedrals  \tPrint peptide dihedral angles." },
  { NoContentInfo, 0, "n", "", Arg::None,
    "  -n  \tDo not print content (for use with other options)." },
  { 0, 0, 0, 0, 0, 0 }
};

static void print_atoms_on_special_positions(const Structure& st) {
  printf(" Atoms on special positions:");
  bool found = false;
  for (const Chain& chain : st.first_model().chains)
    for (const Residue& res : chain.residues)
      for (const Atom& atom : res.atoms)
        if (int n = st.cell.is_special_position(atom.pos)) {
          found = true;
          SymImage im = st.cell.find_nearest_image(atom.pos, atom.pos,
                                                   Asu::Different);
          printf("\n    %s %4d%c %3s %-3s %c fold=%d  occ=%.2f  d_image=%.4f",
                 chain.name.c_str(), *res.seqid.num, res.seqid.icode,
                 res.name.c_str(), atom.name.c_str(), (atom.altloc | 0x20),
                 n+1, atom.occ, im.dist());
        }
  if (!found)
    printf(" none");
  printf("\n");
}

static void print_content_info(const Structure& st, bool /*verbose*/) {
  printf(" Spacegroup   %s\n", st.spacegroup_hm.c_str());
  int order = 1;
  const SpaceGroup* sg = st.find_spacegroup();
  if (sg) {
    order = sg->operations().order();
    printf("   Group no. %d with %d operations.\n", sg->number, order);
  } else {
    std::fprintf(stderr, "%s space group name! Assuming P1.\n",
                 st.spacegroup_hm.empty() ? "No" : "Unrecognized");
  }
  if (!st.origx.is_identity())
    printf("   The ORIGX matrix is not identity.\n");
  if (st.cell.explicit_matrices)
    printf("   Non-standard fractionalization matrix is given.\n");
  print_atoms_on_special_positions(st);
  double n_molecules = order * st.get_ncs_multiplier();
  printf(" Number of images (symmetry * strict NCS): %5g\n", n_molecules);
  assert(n_molecules == st.cell.images.size() + 1);
  printf(" Cell volume [A^3]: %30.1f\n", st.cell.volume);
  printf(" ASU volume [A^3]:  %30.1f\n", st.cell.volume / order);
  double water_count = 0;
  int residue_count = 0;
  int mol_h_count = 0;
  double mol_weight = 0;
  double mol_atom_count = 0;
  double buffer_atom_count = 0;
  double file_h_count = 0;
  const Model& model = st.first_model();
  for (const Chain& chain : model.chains) {
    for (const Residue& res : chain.residues) {
      ResidueInfo res_info = find_tabulated_residue(res.name);
      bool is_buffer = res_info.is_buffer_or_water();
      if (!is_buffer && chain.is_first_in_group(res)) {
        residue_count++;
        mol_h_count += std::max(res_info.hydrogen_count - 2, 0);
      }
      for (const Atom& atom : res.atoms) {
        if (atom.is_hydrogen()) {
          file_h_count += atom.occ;
        } else if (is_buffer) {
          if (res_info.is_water())
            water_count += atom.occ;
          buffer_atom_count += atom.occ;
        } else {
          mol_atom_count += atom.occ;
          mol_weight += atom.occ * atom.element.weight();
        }
      }
    }
  }
  // add weight of hydrogens
  mol_weight += mol_h_count * Element(El::H).weight();

  printf(" Residue count excl. solvent and buffer: %7d\n", residue_count);
  printf(" Water count: %38.3f\n", water_count);
  printf(" Heavy (not H) atom count: %25.3f\n",
         mol_atom_count + buffer_atom_count);
  printf("     in macromolecules and ligands: %16.3f\n", mol_atom_count);
  printf("     in solvent and buffer: %24.3f\n", buffer_atom_count);
  printf(" Hydrogens in the file: %28.3f\n", file_h_count);
  printf("Solvent content based on the model (excl. solvent and buffer)\n");
  printf(" Estimated hydrogen count: %21d\n", mol_h_count);
  printf(" Estimated molecular weight: %23.3f\n", mol_weight);
  if (st.cell.is_crystal()) {
    double Vm = st.cell.volume_per_image() / mol_weight;
    printf(" Matthews coefficient: %29.3f\n", Vm);
    double Na = 0.602214;  // Avogadro number x 10^-24 (cm^3->A^3)
    // rwcontents uses 1.34, Rupp's papers 1.35
    for (double ro : { 1.35, 1.34 })
      printf(" Solvent %% (for protein density %g): %13.3f\n",
             ro, 100. * (1. - 1. / (ro * Vm * Na)));
  } else {
    printf(" Not a crystal / unit cell not known.\n");
  }
  printf("Solvent content based on SEQRES\n");
  mol_weight = 0.;
  bool missing = false;
  for (const Chain& chain : model.chains)
    if (ConstResidueSpan polymer = chain.get_polymer()) {
      const Entity* entity = st.get_entity_of(polymer);
      if (!entity || entity->full_sequence.empty()) {
        printf(" Missing sequence for chain %s.\n", chain.name.c_str());
        missing = true;
      }
      mol_weight += calculate_sequence_weight(entity->full_sequence, 100.);
    }
  if (missing)
    return;
  printf(" Molecular weight from sequence: %19.3f\n", mol_weight);
  if (st.cell.is_crystal()) {
    double Vm = st.cell.volume_per_image() / mol_weight;
    printf(" Matthews coefficient: %29.3f\n", Vm);
    double Na = 0.602214;  // Avogadro number x 10^-24 (cm^3->A^3)
    // rwcontents uses 1.34, Rupp's papers 1.35
    for (double ro : { 1.35, 1.34 })
      printf(" Solvent %% (for protein density %g): %13.3f\n",
             ro, 100. * (1. - 1. / (ro * Vm * Na)));
  } else {
    printf(" Not a crystal / unit cell not known.\n");
  }
}

static void print_dihedrals(const Structure& st) {
  printf(" Chain Residue      Psi      Phi    Omega\n");
  const Model& model = st.first_model();
  for (const Chain& chain : model.chains) {
    for (const Residue& res : chain.residues) {
      printf("%3s %4d%c %5s", chain.name.c_str(), *res.seqid.num,
                              res.seqid.icode, res.name.c_str());
      const Residue* prev = chain.previous_bonded_aa(res);
      const Residue* next = chain.next_bonded_aa(res);
      double omega = next ? calculate_omega(res, *next) : NAN;
      auto phi_psi = calculate_phi_psi(prev, res, next);
      if (prev || next)
        printf(" % 8.2f % 8.2f % 8.2f\n",
               deg(phi_psi[0]), deg(phi_psi[1]), deg(omega));
      else
        printf("\n");
    }
  }
  printf("\n");
}

static void print_bfactor_info(const gemmi::Model& model) {
  std::vector<double> bfactors;
  for (const Chain& chain : model.chains)
    for (const Residue& res : chain.residues)
      for (const Atom& atom : res.atoms)
        if (atom.occ > 0)
          bfactors.push_back(atom.b_iso);
  gemmi::DataStats stats = gemmi::calculate_data_statistics(bfactors);
  printf("\nIsotropic ADPs: %zu values\n", bfactors.size());
  printf("  min: %.2f  max: %.2f  mean: %.2f  std.dev: %.2f\n",
         stats.dmin, stats.dmax, stats.dmean, stats.rms);
  if (stats.dmin < stats.dmax)
    print_histogram(bfactors, stats.dmin, stats.dmax);
}

int GEMMI_MAIN(int argc, char **argv) {
  OptParser p(EXE_NAME);
  p.simple_parse(argc, argv, Usage);
  p.require_input_files_as_args();
  bool verbose = p.options[Verbose];
  try {
    for (int i = 0; i < p.nonOptionsCount(); ++i) {
      std::string input = p.coordinate_input_file(i);
      if (i > 0)
        std::printf("\n");
      if (verbose || p.nonOptionsCount() > 1)
        std::printf("File: %s\n", input.c_str());
      Structure st = read_structure_gz(input);
      setup_entities(st);
      if (st.models.size() > 1)
        std::fprintf(stderr,
                     "Warning: using only the first model out of %zu.\n",
                     st.models.size());
      if (!p.options[NoContentInfo])
        print_content_info(st, verbose);
      if (p.options[Bfactors])
        print_bfactor_info(st.first_model());
      if (p.options[Dihedrals])
        print_dihedrals(st);
    }
  } catch (std::runtime_error& e) {
    std::fprintf(stderr, "ERROR: %s\n", e.what());
    return 1;
  }
  return 0;
}

// vim:sw=2:ts=2:et:path^=../include,../third_party
<commit_msg>refactoring src/contents.cpp<commit_after>// Copyright 2017 Global Phasing Ltd.
//
// This program analyses PDB or mmCIF files, printing similar things
// as CCP4 RWCONTENTS: weight, Matthews coefficient, etc.

#include <cassert>
#include <cstdio>
#include <gemmi/symmetry.hpp>
#include <gemmi/resinfo.hpp>
#include <gemmi/calculate.hpp>
#include <gemmi/gzread.hpp>
#include <gemmi/polyheur.hpp>  // for setup_entities, calculate_sequence_weight
#include "histogram.h"         // for print_histogram
#define GEMMI_PROG contents
#include "options.h"

using namespace gemmi;
using std::printf;

enum OptionIndex { Dihedrals=4, Bfactors, NoContentInfo };

static const option::Descriptor Usage[] = {
  { NoOp, 0, "", "", Arg::None,
    "Usage:\n " EXE_NAME " [options] INPUT[...]"
    "\nAnalyses content of a PDB or mmCIF."},
  CommonUsage[Help],
  CommonUsage[Version],
  CommonUsage[Verbose],
  { Bfactors, 0, "b", "", Arg::None,
    "  -b  \tPrint statistics of isotropic ADPs (B-factors)." },
  { Dihedrals, 0, "", "dihedrals", Arg::None,
    "  --dihedrals  \tPrint peptide dihedral angles." },
  { NoContentInfo, 0, "n", "", Arg::None,
    "  -n  \tDo not print content (for use with other options)." },
  { 0, 0, 0, 0, 0, 0 }
};

static void print_atoms_on_special_positions(const Structure& st) {
  printf(" Atoms on special positions:");
  bool found = false;
  for (const Chain& chain : st.first_model().chains)
    for (const Residue& res : chain.residues)
      for (const Atom& atom : res.atoms)
        if (int n = st.cell.is_special_position(atom.pos)) {
          found = true;
          SymImage im = st.cell.find_nearest_image(atom.pos, atom.pos,
                                                   Asu::Different);
          printf("\n    %s %4d%c %3s %-3s %c fold=%d  occ=%.2f  d_image=%.4f",
                 chain.name.c_str(), *res.seqid.num, res.seqid.icode,
                 res.name.c_str(), atom.name.c_str(), (atom.altloc | 0x20),
                 n+1, atom.occ, im.dist());
        }
  if (!found)
    printf(" none");
  printf("\n");
}

static void print_solvent_content(const UnitCell& cell, double mol_weight) {
  if (cell.is_crystal()) {
    double Vm = cell.volume_per_image() / mol_weight;
    printf(" Matthews coefficient: %29.3f\n", Vm);
    double Na = 0.602214;  // Avogadro number x 10^-24 (cm^3->A^3)
    // rwcontents uses 1.34, Rupp's papers 1.35
    for (double ro : { 1.35, 1.34 })
      printf(" Solvent %% (for protein density %g): %13.3f\n",
             ro, 100. * (1. - 1. / (ro * Vm * Na)));
  } else {
    printf(" Not a crystal / unit cell not known.\n");
  }
}

static void print_content_info(const Structure& st, bool /*verbose*/) {
  printf(" Spacegroup   %s\n", st.spacegroup_hm.c_str());
  int order = 1;
  const SpaceGroup* sg = st.find_spacegroup();
  if (sg) {
    order = sg->operations().order();
    printf("   Group no. %d with %d operations.\n", sg->number, order);
  } else {
    std::fprintf(stderr, "%s space group name! Assuming P1.\n",
                 st.spacegroup_hm.empty() ? "No" : "Unrecognized");
  }
  if (!st.origx.is_identity())
    printf("   The ORIGX matrix is not identity.\n");
  if (st.cell.explicit_matrices)
    printf("   Non-standard fractionalization matrix is given.\n");
  print_atoms_on_special_positions(st);
  double n_molecules = order * st.get_ncs_multiplier();
  printf(" Number of images (symmetry * strict NCS): %5g\n", n_molecules);
  assert(n_molecules == st.cell.images.size() + 1);
  printf(" Cell volume [A^3]: %30.1f\n", st.cell.volume);
  printf(" ASU volume [A^3]:  %30.1f\n", st.cell.volume / order);
  double water_count = 0;
  int residue_count = 0;
  int mol_h_count = 0;
  double mol_weight = 0;
  double mol_atom_count = 0;
  double buffer_atom_count = 0;
  double file_h_count = 0;
  const Model& model = st.first_model();
  for (const Chain& chain : model.chains) {
    for (const Residue& res : chain.residues) {
      ResidueInfo res_info = find_tabulated_residue(res.name);
      bool is_buffer = res_info.is_buffer_or_water();
      if (!is_buffer && chain.is_first_in_group(res)) {
        residue_count++;
        mol_h_count += std::max(res_info.hydrogen_count - 2, 0);
      }
      for (const Atom& atom : res.atoms) {
        if (atom.is_hydrogen()) {
          file_h_count += atom.occ;
        } else if (is_buffer) {
          if (res_info.is_water())
            water_count += atom.occ;
          buffer_atom_count += atom.occ;
        } else {
          mol_atom_count += atom.occ;
          mol_weight += atom.occ * atom.element.weight();
        }
      }
    }
  }
  // add weight of hydrogens
  mol_weight += mol_h_count * Element(El::H).weight();

  printf(" Residue count excl. solvent and buffer: %7d\n", residue_count);
  printf(" Water count: %38.3f\n", water_count);
  printf(" Heavy (not H) atom count: %25.3f\n",
         mol_atom_count + buffer_atom_count);
  printf("     in macromolecules and ligands: %16.3f\n", mol_atom_count);
  printf("     in solvent and buffer: %24.3f\n", buffer_atom_count);
  printf(" Hydrogens in the file: %28.3f\n", file_h_count);
  printf("Solvent content based on the model (excl. solvent and buffer)\n");
  printf(" Estimated hydrogen count: %21d\n", mol_h_count);
  printf(" Estimated molecular weight: %23.3f\n", mol_weight);
  print_solvent_content(st.cell, mol_weight);
  printf("Solvent content based on SEQRES\n");
  mol_weight = 0.;
  bool missing = false;
  for (const Chain& chain : model.chains)
    if (ConstResidueSpan polymer = chain.get_polymer()) {
      const Entity* entity = st.get_entity_of(polymer);
      if (!entity || entity->full_sequence.empty()) {
        printf(" Missing sequence for chain %s.\n", chain.name.c_str());
        missing = true;
      }
      mol_weight += calculate_sequence_weight(entity->full_sequence, 100.);
    }
  if (missing)
    return;
  printf(" Molecular weight from sequence: %19.3f\n", mol_weight);
  print_solvent_content(st.cell, mol_weight);
}

static void print_dihedrals(const Structure& st) {
  printf(" Chain Residue      Psi      Phi    Omega\n");
  const Model& model = st.first_model();
  for (const Chain& chain : model.chains) {
    for (const Residue& res : chain.residues) {
      printf("%3s %4d%c %5s", chain.name.c_str(), *res.seqid.num,
                              res.seqid.icode, res.name.c_str());
      const Residue* prev = chain.previous_bonded_aa(res);
      const Residue* next = chain.next_bonded_aa(res);
      double omega = next ? calculate_omega(res, *next) : NAN;
      auto phi_psi = calculate_phi_psi(prev, res, next);
      if (prev || next)
        printf(" % 8.2f % 8.2f % 8.2f\n",
               deg(phi_psi[0]), deg(phi_psi[1]), deg(omega));
      else
        printf("\n");
    }
  }
  printf("\n");
}

static void print_bfactor_info(const gemmi::Model& model) {
  std::vector<double> bfactors;
  for (const Chain& chain : model.chains)
    for (const Residue& res : chain.residues)
      for (const Atom& atom : res.atoms)
        if (atom.occ > 0)
          bfactors.push_back(atom.b_iso);
  gemmi::DataStats stats = gemmi::calculate_data_statistics(bfactors);
  printf("\nIsotropic ADPs: %zu values\n", bfactors.size());
  printf("  min: %.2f  max: %.2f  mean: %.2f  std.dev: %.2f\n",
         stats.dmin, stats.dmax, stats.dmean, stats.rms);
  if (stats.dmin < stats.dmax)
    print_histogram(bfactors, stats.dmin, stats.dmax);
}

int GEMMI_MAIN(int argc, char **argv) {
  OptParser p(EXE_NAME);
  p.simple_parse(argc, argv, Usage);
  p.require_input_files_as_args();
  bool verbose = p.options[Verbose];
  try {
    for (int i = 0; i < p.nonOptionsCount(); ++i) {
      std::string input = p.coordinate_input_file(i);
      if (i > 0)
        std::printf("\n");
      if (verbose || p.nonOptionsCount() > 1)
        std::printf("File: %s\n", input.c_str());
      Structure st = read_structure_gz(input);
      setup_entities(st);
      if (st.models.size() > 1)
        std::fprintf(stderr,
                     "Warning: using only the first model out of %zu.\n",
                     st.models.size());
      if (!p.options[NoContentInfo])
        print_content_info(st, verbose);
      if (p.options[Bfactors])
        print_bfactor_info(st.first_model());
      if (p.options[Dihedrals])
        print_dihedrals(st);
    }
  } catch (std::runtime_error& e) {
    std::fprintf(stderr, "ERROR: %s\n", e.what());
    return 1;
  }
  return 0;
}

// vim:sw=2:ts=2:et:path^=../include,../third_party
<|endoftext|>


<commit_before>#include "VertexShader.h"

using namespace Device;
using namespace Rendering::Shader;

VertexShader::VertexShader(ID3DBlob* blob, const std::string& key)
	: ShaderForm(blob, key), _shader(nullptr), _layout(nullptr)
{
	_type = Type::Vertex;
}

VertexShader::~VertexShader(void)
{
	SAFE_RELEASE(_shader);
	SAFE_RELEASE(_layout);
}

bool VertexShader::Create(
	const Device::DirectX* dx,
	const std::vector<D3D11_INPUT_ELEMENT_DESC>& vertexDeclations)
{
	if(_blob == nullptr)
		return false;

	uint count = vertexDeclations.size();
	ID3D11Device* device = dx->GetDevice();

	HRESULT hr = device->CreateVertexShader( _blob->GetBufferPointer(), _blob->GetBufferSize(), nullptr, &_shader );

	if( FAILED( hr ) )
		return false;

	if(vertexDeclations.size() == 0)
		return true;

	hr = device->CreateInputLayout(vertexDeclations.data(), count,
		_blob->GetBufferPointer(), _blob->GetBufferSize(), &_layout);

	_blob->Release();

	if( FAILED( hr ) )
		return false;

	for(unsigned int i=0; i<count; ++i)
	{
		const D3D11_INPUT_ELEMENT_DESC& desc = vertexDeclations[i];

		SemanticInfo info;
		{
			info.name = desc.SemanticName;
			info.semanticIndex = desc.SemanticIndex;

			if( (i+1) != count )
				info.size = vertexDeclations[i+1].AlignedByteOffset - desc.AlignedByteOffset;
		}

		_semanticInfo.push_back(info);
	}

	_semanticInfo.back().size = dx->CalcFormatSize(vertexDeclations[count-1].Format);

	return true;
}

void VertexShader::BindShaderToContext(ID3D11DeviceContext* context)
{
	context->VSSetShader(_shader, nullptr, 0);
}

void VertexShader::BindInputLayoutToContext(ID3D11DeviceContext* context)
{
	context->IASetInputLayout(_layout);
}

void VertexShader::BindResourcesToContext(
				ID3D11DeviceContext* context,
				const std::vector<InputConstBuffer>* constBuffers,
				const std::vector<InputTexture>* textures,
				const std::vector<InputShaderResourceBuffer>* srBuffers)
{
	if(constBuffers)
	{
		for(auto iter = constBuffers->begin(); iter != constBuffers->end(); ++iter)
		{
			ID3D11Buffer* buffer = (*iter).buffer->GetBuffer();
			if(buffer && iter->useVS)
				context->VSSetConstantBuffers( (*iter).bindIndex, 1, &buffer );
		}
	}

	if(textures)
	{
		for(auto iter = textures->begin(); iter != textures->end(); ++iter)
		{
			auto srv = iter->texture->GetShaderResourceView()->GetView();
			if(srv && iter->useVS)
				context->VSSetShaderResources( iter->bindIndex, 1, &srv );
		}
	}

	if(srBuffers)
	{
		for(auto iter = srBuffers->begin(); iter != srBuffers->end(); ++iter)
		{
			auto srv = iter->srBuffer->GetShaderResourceView();
			if(srv && iter->useVS)
				context->VSSetShaderResources( iter->bindIndex, 1, srv );
		}
	}
}

void VertexShader::Clear(
				ID3D11DeviceContext* context,
				const std::vector<InputConstBuffer>* constBuffers,
				const std::vector<InputTexture>* textures,
				const std::vector<InputShaderResourceBuffer>* srBuffers)
{
	if(textures)
	{
		ID3D11ShaderResourceView* nullSrv = nullptr;

		for(auto iter = textures->begin(); iter != textures->end(); ++iter)
		{
			if(iter->useVS)
				context->VSSetShaderResources( iter->bindIndex, 1, &nullSrv );
		}
	}

	if(srBuffers)
	{
		ID3D11ShaderResourceView* nullSrv = nullptr;

		for(auto iter = srBuffers->begin(); iter != srBuffers->end(); ++iter)
		{
			if(iter->useVS)
				context->VSSetShaderResources( iter->bindIndex, 1, &nullSrv );
		}
	}

	if(constBuffers)
	{
		ID3D11Buffer* nullBuffer = nullptr;

		for(auto iter = constBuffers->begin(); iter != constBuffers->end(); ++iter)
		{
			if(iter->useVS)
				context->VSSetConstantBuffers( iter->bindIndex, 1, &nullBuffer );
		}
	}
}

void VertexShader::BindTexture(ID3D11DeviceContext* context, TextureBindIndex bind, const Texture::Texture2D* tex)
{
	ID3D11ShaderResourceView* srv = tex ? tex->GetShaderResourceView()->GetView() : nullptr;
	context->VSSetShaderResources(uint(bind), 1, &srv);
}

void VertexShader::BindSamplerState(ID3D11DeviceContext* context, SamplerStateBindIndex bind, ID3D11SamplerState* samplerState)
{
	context->VSSetSamplers(uint(bind), 1, &samplerState);
}

void VertexShader::BindConstBuffer(ID3D11DeviceContext* context, ConstBufferBindIndex bind, const Buffer::ConstBuffer* cb)
{
	ID3D11Buffer* buf = cb ? cb->GetBuffer() : nullptr;
	context->VSSetConstantBuffers(uint(bind), 1, &buf);
}

void VertexShader::BindShaderResourceBuffer(ID3D11DeviceContext* context, TextureBindIndex bind, const Buffer::ShaderResourceBuffer* srBuffer)
{
	ID3D11ShaderResourceView* srv = srBuffer ? srBuffer->GetShaderResourceView() : nullptr;
	context->VSSetShaderResources(uint(bind), 1, &srv);
}
<commit_msg>VertexShader- BindTexture에서 Texture2D 대신 TextureForm을 사용하도록 수정 #32<commit_after>#include "VertexShader.h"

using namespace Device;
using namespace Rendering::Shader;

VertexShader::VertexShader(ID3DBlob* blob, const std::string& key)
	: ShaderForm(blob, key), _shader(nullptr), _layout(nullptr)
{
	_type = Type::Vertex;
}

VertexShader::~VertexShader(void)
{
	SAFE_RELEASE(_shader);
	SAFE_RELEASE(_layout);
}

bool VertexShader::Create(
	const Device::DirectX* dx,
	const std::vector<D3D11_INPUT_ELEMENT_DESC>& vertexDeclations)
{
	if(_blob == nullptr)
		return false;

	uint count = vertexDeclations.size();
	ID3D11Device* device = dx->GetDevice();

	HRESULT hr = device->CreateVertexShader( _blob->GetBufferPointer(), _blob->GetBufferSize(), nullptr, &_shader );

	if( FAILED( hr ) )
		return false;

	if(vertexDeclations.size() == 0)
		return true;

	hr = device->CreateInputLayout(vertexDeclations.data(), count,
		_blob->GetBufferPointer(), _blob->GetBufferSize(), &_layout);

	_blob->Release();

	if( FAILED( hr ) )
		return false;

	for(unsigned int i=0; i<count; ++i)
	{
		const D3D11_INPUT_ELEMENT_DESC& desc = vertexDeclations[i];

		SemanticInfo info;
		{
			info.name = desc.SemanticName;
			info.semanticIndex = desc.SemanticIndex;

			if( (i+1) != count )
				info.size = vertexDeclations[i+1].AlignedByteOffset - desc.AlignedByteOffset;
		}

		_semanticInfo.push_back(info);
	}

	_semanticInfo.back().size = dx->CalcFormatSize(vertexDeclations[count-1].Format);

	return true;
}

void VertexShader::BindShaderToContext(ID3D11DeviceContext* context)
{
	context->VSSetShader(_shader, nullptr, 0);
}

void VertexShader::BindInputLayoutToContext(ID3D11DeviceContext* context)
{
	context->IASetInputLayout(_layout);
}

void VertexShader::BindResourcesToContext(
				ID3D11DeviceContext* context,
				const std::vector<InputConstBuffer>* constBuffers,
				const std::vector<InputTexture>* textures,
				const std::vector<InputShaderResourceBuffer>* srBuffers)
{
	if(constBuffers)
	{
		for(auto iter = constBuffers->begin(); iter != constBuffers->end(); ++iter)
		{
			ID3D11Buffer* buffer = (*iter).buffer->GetBuffer();
			if(buffer && iter->useVS)
				context->VSSetConstantBuffers( (*iter).bindIndex, 1, &buffer );
		}
	}

	if(textures)
	{
		for(auto iter = textures->begin(); iter != textures->end(); ++iter)
		{
			auto srv = iter->texture->GetShaderResourceView()->GetView();
			if(srv && iter->useVS)
				context->VSSetShaderResources( iter->bindIndex, 1, &srv );
		}
	}

	if(srBuffers)
	{
		for(auto iter = srBuffers->begin(); iter != srBuffers->end(); ++iter)
		{
			auto srv = iter->srBuffer->GetShaderResourceView();
			if(srv && iter->useVS)
				context->VSSetShaderResources( iter->bindIndex, 1, srv );
		}
	}
}

void VertexShader::Clear(
				ID3D11DeviceContext* context,
				const std::vector<InputConstBuffer>* constBuffers,
				const std::vector<InputTexture>* textures,
				const std::vector<InputShaderResourceBuffer>* srBuffers)
{
	if(textures)
	{
		ID3D11ShaderResourceView* nullSrv = nullptr;

		for(auto iter = textures->begin(); iter != textures->end(); ++iter)
		{
			if(iter->useVS)
				context->VSSetShaderResources( iter->bindIndex, 1, &nullSrv );
		}
	}

	if(srBuffers)
	{
		ID3D11ShaderResourceView* nullSrv = nullptr;

		for(auto iter = srBuffers->begin(); iter != srBuffers->end(); ++iter)
		{
			if(iter->useVS)
				context->VSSetShaderResources( iter->bindIndex, 1, &nullSrv );
		}
	}

	if(constBuffers)
	{
		ID3D11Buffer* nullBuffer = nullptr;

		for(auto iter = constBuffers->begin(); iter != constBuffers->end(); ++iter)
		{
			if(iter->useVS)
				context->VSSetConstantBuffers( iter->bindIndex, 1, &nullBuffer );
		}
	}
}

void VertexShader::BindTexture(ID3D11DeviceContext* context, TextureBindIndex bind, const Texture::TextureForm* tex)
{
	ID3D11ShaderResourceView* srv = tex ? tex->GetShaderResourceView()->GetView() : nullptr;
	context->VSSetShaderResources(uint(bind), 1, &srv);
}

void VertexShader::BindSamplerState(ID3D11DeviceContext* context, SamplerStateBindIndex bind, ID3D11SamplerState* samplerState)
{
	context->VSSetSamplers(uint(bind), 1, &samplerState);
}

void VertexShader::BindConstBuffer(ID3D11DeviceContext* context, ConstBufferBindIndex bind, const Buffer::ConstBuffer* cb)
{
	ID3D11Buffer* buf = cb ? cb->GetBuffer() : nullptr;
	context->VSSetConstantBuffers(uint(bind), 1, &buf);
}

void VertexShader::BindShaderResourceBuffer(ID3D11DeviceContext* context, TextureBindIndex bind, const Buffer::ShaderResourceBuffer* srBuffer)
{
	ID3D11ShaderResourceView* srv = srBuffer ? srBuffer->GetShaderResourceView() : nullptr;
	context->VSSetShaderResources(uint(bind), 1, &srv);
}
<|endoftext|>


<commit_before>#include "VertexRenderer.hpp"
#include "FAST/SceneGraph.hpp"

namespace fast {

void VertexRenderer::draw(Matrix4f perspectiveMatrix, Matrix4f viewingMatrix) {
    std::lock_guard<std::mutex> lock(mMutex);
    glEnable(GL_POINT_SPRITE); // Circles created in fragment shader will not work without this
    glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);

    activateShader();
    setShaderUniform("perspectiveTransform", perspectiveMatrix);
    setShaderUniform("viewTransform", viewingMatrix);

    for(auto it : mDataToRender) {
        Mesh::pointer points = it.second;
        float pointSize = mDefaultPointSize;
        if(mInputSizes.count(it.first) > 0) {
            pointSize = mInputSizes[it.first];
        }
        bool useGlobalColor = false;
        Color color = Color::Green();
        if(mInputColors.count(it.first) > 0) {
            color = mInputColors[it.first];
            useGlobalColor = true;
        } else if(mDefaultColorSet) {
            color = mDefaultColor;
            useGlobalColor = true;
        }
        bool drawOnTop;
        if(mInputDrawOnTop.count(it.first) > 0) {
            drawOnTop = mInputDrawOnTop[it.first];
        } else {
            drawOnTop = mDefaultDrawOnTop;
        }
        if(drawOnTop)
            glDisable(GL_DEPTH_TEST);

        AffineTransformation::pointer transform = SceneGraph::getAffineTransformationFromData(points);
        setShaderUniform("transform", transform->getTransform());
        setShaderUniform("pointSize", pointSize);

        VertexBufferObjectAccess::pointer access = points->getVertexBufferObjectAccess(ACCESS_READ);
        GLuint* coordinateVBO = access->getCoordinateVBO();

        // Coordinates buffer
        glBindBuffer(GL_ARRAY_BUFFER, *coordinateVBO);
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
        glEnableVertexAttribArray(0);

        // Color buffer
        if(access->hasColorVBO() && !useGlobalColor) {
            GLuint *colorVBO = access->getColorVBO();
            glBindBuffer(GL_ARRAY_BUFFER, *colorVBO);
            glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
            glEnableVertexAttribArray(1);
        } else {
            useGlobalColor = true;
        }
        setShaderUniform("useGlobalColor", useGlobalColor);
        setShaderUniform("globalColor", color.asVector());

        glDrawArrays(GL_POINTS, 0, points->getNrOfVertices()*3);

        glBindBuffer(GL_ARRAY_BUFFER, 0);

        if(drawOnTop)
            glEnable(GL_DEPTH_TEST);
    }

    deactivateShader();
}

void VertexRenderer::draw2D(
                cl::BufferGL PBO,
                uint width,
                uint height,
                Eigen::Transform<float, 3, Eigen::Affine> pixelToViewportTransform,
                float PBOspacing,
                Vector2f translation
        ) {
    std::lock_guard<std::mutex> lock(mMutex);

    OpenCLDevice::pointer device = getMainDevice();
    cl::CommandQueue queue = device->getCommandQueue();
    std::vector<cl::Memory> v;
    v.push_back(PBO);
    queue.enqueueAcquireGLObjects(&v);

    // Map would probably be better here, but doesn't work on NVIDIA, segfault surprise!
    //float* pixels = (float*)queue.enqueueMapBuffer(PBO, CL_TRUE, CL_MAP_WRITE, 0, width*height*sizeof(float)*4);
    UniquePointer<float[]> pixels(new float[width*height*sizeof(float)*4]);
    queue.enqueueReadBuffer(PBO, CL_TRUE, 0, width*height*4*sizeof(float), pixels.get());

    for(auto it : mDataToRender) {
    	Mesh::pointer points = it.second;

		Color color = mDefaultColor;
        if(mInputColors.count(it.first) > 0) {
            color = mInputColors[it.first];
        }

    	MeshAccess::pointer access = points->getMeshAccess(ACCESS_READ);
        std::vector<MeshVertex> vertices = access->getVertices();

        // Draw each line
        int size = 3;
        for(int i = 0; i < vertices.size(); ++i) {
        	Vector2f position = vertices[i].getPosition().head(2); // In mm
        	Vector2i positinInPixles(
        			round(position.x() / PBOspacing),
        			round(position.y() / PBOspacing)
        	);

        	// Draw the line
        	for(int j = -size; j <= size; ++j) {
        	for(int k = -size; k <= size; ++k) {

        		int x = positinInPixles.x() + j;
        		int y = positinInPixles.y() + k;
        		y = height - 1 - y;
        		if(x < 0 || y < 0 || x >= width || y >= height)
        			continue;

        		pixels[4*(x + y*width)] = color.getRedValue();
        		pixels[4*(x + y*width) + 1] = color.getGreenValue();
        		pixels[4*(x + y*width) + 2] = color.getBlueValue();
        	}}
        }
    }

    //queue.enqueueUnmapMemObject(PBO, pixels);
    queue.enqueueWriteBuffer(PBO, CL_TRUE, 0, width*height*4*sizeof(float), pixels.get());
    queue.enqueueReleaseGLObjects(&v);
}

VertexRenderer::VertexRenderer() {
    mDefaultPointSize = 10;
    mDefaultColorSet = false;
    mDefaultDrawOnTop = false;
    createInputPort<Mesh>(0, false);

    createShaderProgram({
        Config::getKernelSourcePath() + "Visualization/VertexRenderer/VertexRenderer.vert",
        Config::getKernelSourcePath() + "Visualization/VertexRenderer/VertexRenderer.frag",
    });
}

uint VertexRenderer::addInputConnection(DataPort::pointer port) {
    return Renderer::addInputConnection(port);
}

uint VertexRenderer::addInputConnection(DataPort::pointer port, Color color,
        float size) {
    uint nr = addInputConnection(port);
    setColor(nr, color);
    setSize(nr, size);
    return nr;
}

uint VertexRenderer::addInputData(DataObject::pointer data) {
    return Renderer::addInputData(data);
}

uint VertexRenderer::addInputData(Mesh::pointer data, Color color, float size) {
    uint nr = addInputData(data);
    setColor(nr, color);
    setSize(nr, size);
}

void VertexRenderer::setDefaultColor(Color color) {
    mDefaultColor = color;
    mDefaultColorSet = true;
}

void VertexRenderer::setDefaultSize(float size) {
    mDefaultPointSize = size;
}

void VertexRenderer::setDefaultDrawOnTop(bool drawOnTop) {
    mDefaultDrawOnTop = drawOnTop;
}

void VertexRenderer::setDrawOnTop(uint inputNr, bool drawOnTop) {
    mInputDrawOnTop[inputNr] = drawOnTop;
}

void VertexRenderer::setColor(uint inputNr, Color color) {
    mInputColors[inputNr] = color;
}

void VertexRenderer::setSize(uint inputNr, float size) {
    mInputSizes[inputNr] = size;
}

} // end namespace fast
<commit_msg>Function must return a value.<commit_after>#include "VertexRenderer.hpp"
#include "FAST/SceneGraph.hpp"

namespace fast {

void VertexRenderer::draw(Matrix4f perspectiveMatrix, Matrix4f viewingMatrix) {
    std::lock_guard<std::mutex> lock(mMutex);
    glEnable(GL_POINT_SPRITE); // Circles created in fragment shader will not work without this
    glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);

    activateShader();
    setShaderUniform("perspectiveTransform", perspectiveMatrix);
    setShaderUniform("viewTransform", viewingMatrix);

    for(auto it : mDataToRender) {
        Mesh::pointer points = it.second;
        float pointSize = mDefaultPointSize;
        if(mInputSizes.count(it.first) > 0) {
            pointSize = mInputSizes[it.first];
        }
        bool useGlobalColor = false;
        Color color = Color::Green();
        if(mInputColors.count(it.first) > 0) {
            color = mInputColors[it.first];
            useGlobalColor = true;
        } else if(mDefaultColorSet) {
            color = mDefaultColor;
            useGlobalColor = true;
        }
        bool drawOnTop;
        if(mInputDrawOnTop.count(it.first) > 0) {
            drawOnTop = mInputDrawOnTop[it.first];
        } else {
            drawOnTop = mDefaultDrawOnTop;
        }
        if(drawOnTop)
            glDisable(GL_DEPTH_TEST);

        AffineTransformation::pointer transform = SceneGraph::getAffineTransformationFromData(points);
        setShaderUniform("transform", transform->getTransform());
        setShaderUniform("pointSize", pointSize);

        VertexBufferObjectAccess::pointer access = points->getVertexBufferObjectAccess(ACCESS_READ);
        GLuint* coordinateVBO = access->getCoordinateVBO();

        // Coordinates buffer
        glBindBuffer(GL_ARRAY_BUFFER, *coordinateVBO);
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
        glEnableVertexAttribArray(0);

        // Color buffer
        if(access->hasColorVBO() && !useGlobalColor) {
            GLuint *colorVBO = access->getColorVBO();
            glBindBuffer(GL_ARRAY_BUFFER, *colorVBO);
            glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
            glEnableVertexAttribArray(1);
        } else {
            useGlobalColor = true;
        }
        setShaderUniform("useGlobalColor", useGlobalColor);
        setShaderUniform("globalColor", color.asVector());

        glDrawArrays(GL_POINTS, 0, points->getNrOfVertices()*3);

        glBindBuffer(GL_ARRAY_BUFFER, 0);

        if(drawOnTop)
            glEnable(GL_DEPTH_TEST);
    }

    deactivateShader();
}

void VertexRenderer::draw2D(
                cl::BufferGL PBO,
                uint width,
                uint height,
                Eigen::Transform<float, 3, Eigen::Affine> pixelToViewportTransform,
                float PBOspacing,
                Vector2f translation
        ) {
    std::lock_guard<std::mutex> lock(mMutex);

    OpenCLDevice::pointer device = getMainDevice();
    cl::CommandQueue queue = device->getCommandQueue();
    std::vector<cl::Memory> v;
    v.push_back(PBO);
    queue.enqueueAcquireGLObjects(&v);

    // Map would probably be better here, but doesn't work on NVIDIA, segfault surprise!
    //float* pixels = (float*)queue.enqueueMapBuffer(PBO, CL_TRUE, CL_MAP_WRITE, 0, width*height*sizeof(float)*4);
    UniquePointer<float[]> pixels(new float[width*height*sizeof(float)*4]);
    queue.enqueueReadBuffer(PBO, CL_TRUE, 0, width*height*4*sizeof(float), pixels.get());

    for(auto it : mDataToRender) {
    	Mesh::pointer points = it.second;

		Color color = mDefaultColor;
        if(mInputColors.count(it.first) > 0) {
            color = mInputColors[it.first];
        }

    	MeshAccess::pointer access = points->getMeshAccess(ACCESS_READ);
        std::vector<MeshVertex> vertices = access->getVertices();

        // Draw each line
        int size = 3;
        for(int i = 0; i < vertices.size(); ++i) {
        	Vector2f position = vertices[i].getPosition().head(2); // In mm
        	Vector2i positinInPixles(
        			round(position.x() / PBOspacing),
        			round(position.y() / PBOspacing)
        	);

        	// Draw the line
        	for(int j = -size; j <= size; ++j) {
        	for(int k = -size; k <= size; ++k) {

        		int x = positinInPixles.x() + j;
        		int y = positinInPixles.y() + k;
        		y = height - 1 - y;
        		if(x < 0 || y < 0 || x >= width || y >= height)
        			continue;

        		pixels[4*(x + y*width)] = color.getRedValue();
        		pixels[4*(x + y*width) + 1] = color.getGreenValue();
        		pixels[4*(x + y*width) + 2] = color.getBlueValue();
        	}}
        }
    }

    //queue.enqueueUnmapMemObject(PBO, pixels);
    queue.enqueueWriteBuffer(PBO, CL_TRUE, 0, width*height*4*sizeof(float), pixels.get());
    queue.enqueueReleaseGLObjects(&v);
}

VertexRenderer::VertexRenderer() {
    mDefaultPointSize = 10;
    mDefaultColorSet = false;
    mDefaultDrawOnTop = false;
    createInputPort<Mesh>(0, false);

    createShaderProgram({
        Config::getKernelSourcePath() + "Visualization/VertexRenderer/VertexRenderer.vert",
        Config::getKernelSourcePath() + "Visualization/VertexRenderer/VertexRenderer.frag",
    });
}

uint VertexRenderer::addInputConnection(DataPort::pointer port) {
    return Renderer::addInputConnection(port);
}

uint VertexRenderer::addInputConnection(DataPort::pointer port, Color color,
        float size) {
    uint nr = addInputConnection(port);
    setColor(nr, color);
    setSize(nr, size);
    return nr;
}

uint VertexRenderer::addInputData(DataObject::pointer data) {
    return Renderer::addInputData(data);
}

uint VertexRenderer::addInputData(Mesh::pointer data, Color color, float size) {
    uint nr = addInputData(data);
    setColor(nr, color);
    setSize(nr, size);
	return nr;
}

void VertexRenderer::setDefaultColor(Color color) {
    mDefaultColor = color;
    mDefaultColorSet = true;
}

void VertexRenderer::setDefaultSize(float size) {
    mDefaultPointSize = size;
}

void VertexRenderer::setDefaultDrawOnTop(bool drawOnTop) {
    mDefaultDrawOnTop = drawOnTop;
}

void VertexRenderer::setDrawOnTop(uint inputNr, bool drawOnTop) {
    mInputDrawOnTop[inputNr] = drawOnTop;
}

void VertexRenderer::setColor(uint inputNr, Color color) {
    mInputColors[inputNr] = color;
}

void VertexRenderer::setSize(uint inputNr, float size) {
    mInputSizes[inputNr] = size;
}

} // end namespace fast
<|endoftext|>


<commit_before>#include "xchainer/numerical_gradient.h"

#include <algorithm>
#include <functional>
#include <vector>

#ifdef XCHAINER_ENABLE_CUDA
#include <cuda_runtime.h>
#endif  // XCHAINER_ENABLE_CUDA

#include "xchainer/array.h"
#include "xchainer/array_repr.h"
#include "xchainer/error.h"
#include "xchainer/indexable_array.h"
#include "xchainer/indexer.h"
#ifdef XCHAINER_ENABLE_CUDA
#include "xchainer/cuda/cuda_runtime.h"
#endif  // XCHAINER_ENABLE_CUDA
#include "xchainer/device.h"

namespace xchainer {
namespace numerical_gradient_internal {

Array& Subtract(const Array& lhs, const Array& rhs, Array& out) {
    lhs.device().Synchronize();
    rhs.device().Synchronize();
    out.device().Synchronize();

    VisitDtype(lhs.dtype(), [&](auto pt) {
        using T = typename decltype(pt)::type;
        IndexableArray<const T> lhs_iarray{lhs};
        IndexableArray<const T> rhs_iarray{rhs};
        IndexableArray<T> out_iarray{out};
        Indexer<> indexer{lhs.shape()};

        for (int64_t i = 0; i < indexer.total_size(); i++) {
            indexer.Set(i);
            out_iarray[indexer] = lhs_iarray[indexer] - rhs_iarray[indexer];
        }
    });
    return out;
}

Array& Divide(const Array& lhs, const Array& rhs, Array& out) {
    lhs.device().Synchronize();
    rhs.device().Synchronize();
    out.device().Synchronize();

    VisitDtype(lhs.dtype(), [&](auto pt) {
        using T = typename decltype(pt)::type;
        IndexableArray<const T> lhs_iarray{lhs};
        IndexableArray<const T> rhs_iarray{rhs};
        IndexableArray<T> out_iarray{out};
        Indexer<> indexer{lhs.shape()};

        for (int64_t i = 0; i < indexer.total_size(); i++) {
            indexer.Set(i);
            out_iarray[indexer] = lhs_iarray[indexer] / rhs_iarray[indexer];
        }
    });
    return out;
}

Array operator-(const Array& lhs, const Array& rhs) {
    Array out = Array::EmptyLike(lhs);
    Subtract(lhs, rhs, out);
    return out;
}

Array operator/(const Array& lhs, const Array& rhs) {
    Array out = Array::EmptyLike(lhs);
    Divide(lhs, rhs, out);
    return out;
}

Scalar Sum(const Array& array) {
    array.device().Synchronize();

    return VisitDtype(array.dtype(), [&](auto pt) {
        using T = typename decltype(pt)::type;
        IndexableArray<const T> iarray{array};
        Indexer<> indexer{array.shape()};

        T s = 0;
        for (int64_t i = 0; i < indexer.total_size(); i++) {
            indexer.Set(i);
            s += iarray[indexer];
        }
        return Scalar{s};
    });
}

Scalar Norm(const Array& x) {
    Scalar s = Sum(x * x);
    return Scalar(std::sqrt(static_cast<double>(s)), x.dtype());
}

Scalar VectorDot(const Array& x, const Array& y) { return Sum(x * y); }

void Set(Array& out, int64_t flat_index, Scalar value) {
    out.device().Synchronize();

    VisitDtype(out.dtype(), [&](auto pt) {
        using T = typename decltype(pt)::type;
        IndexableArray<T> iarray{out};
        Indexer<> indexer{out.shape()};
        indexer.Set(flat_index);
        iarray[indexer] = static_cast<T>(value);
    });
}

Scalar Get(const Array& out, int64_t flat_index) {
    out.device().Synchronize();

    return VisitDtype(out.dtype(), [&](auto pt) {
        using T = typename decltype(pt)::type;
        IndexableArray<const T> iarray{out};
        Indexer<> indexer{out.shape()};
        indexer.Set(flat_index);
        return Scalar{iarray[indexer]};
    });
}

Arrays CalculateNumericalGradient(
        std::function<Arrays(const Arrays&)> func,
        const Arrays& inputs,
        const Arrays& grad_outputs,
        const Arrays& eps,
        const GraphId& graph_id) {
    // TODO(niboshi): Currently only elementwise functions are supported.
    // TODO(niboshi): Implement arithmetic operations and avoid manual synchronize
    const int nin = inputs.size();
    const int nout = grad_outputs.size();

    if (eps.size() != static_cast<size_t>(nin)) {
        throw XchainerError(
                "Invalid number of eps arrays where number of inputs: " + std::to_string(nin) + ", eps: " + std::to_string(eps.size()));
    }

    for (int i = 0; i < nin; ++i) {
        if (inputs.at(i).shape() != eps.at(i).shape()) {
            throw XchainerError("Invalid eps shape");
        }
        if (inputs.at(i).dtype() != eps.at(i).dtype()) {
            throw XchainerError("Invalid eps dtype");
        }
        // TODO(niboshi): Check: eps must not contain zeros.
    }

    Dtype dtype = inputs[0].dtype();

    auto eval = [&, graph_id](int i_in, int64_t in_flat_index, Scalar eps_scalar, float multiplier) -> Arrays {
        Arrays xs;
        std::transform(inputs.begin(), inputs.end(), std::back_inserter(xs), [graph_id](const Array& x) {
            return x.AsConstant(CopyKind::kCopy).RequireGrad(graph_id);
        });

        Set(xs.at(i_in), in_flat_index, Get(xs.at(i_in), in_flat_index) + Scalar(static_cast<float>(eps_scalar) * multiplier, dtype));
        return func(xs);
    };

    Arrays grads;

    for (int i = 0; i < nin; ++i) {
        Array grad_i = Array::ZerosLike(inputs.at(i));
        int64_t size = grad_i.GetTotalSize();

        for (int64_t in_flat_index = 0; in_flat_index < size; ++in_flat_index) {
            Scalar eps_scalar = Get(eps.at(i), in_flat_index);
            Arrays ys0 = eval(i, in_flat_index, eps_scalar, -1);
            Arrays ys1 = eval(i, in_flat_index, eps_scalar, 1);

            for (int j = 0; j < nout; ++j) {
                Array dy = ys1.at(j) - ys0.at(j);
                Array denom = Array::FullLike(dy, eps_scalar) * Array::FullLike(dy, Scalar(2, dtype));

                Scalar g = VectorDot((ys1.at(j) - ys0.at(j)) / denom, grad_outputs.at(j));
                Scalar g_ij = Get(grad_i, in_flat_index) + g;
                Set(grad_i, in_flat_index, g_ij);
            }
        }
        grads.push_back(grad_i);
    }

    return grads;
}

}  // namespace numerical_gradient_internal
}  // namespace xchainer
<commit_msg>Cosmetics<commit_after>#include "xchainer/numerical_gradient.h"

#include <algorithm>
#include <functional>
#include <vector>

#ifdef XCHAINER_ENABLE_CUDA
#include <cuda_runtime.h>
#endif  // XCHAINER_ENABLE_CUDA

#include "xchainer/array.h"
#include "xchainer/array_repr.h"
#include "xchainer/error.h"
#include "xchainer/indexable_array.h"
#include "xchainer/indexer.h"
#ifdef XCHAINER_ENABLE_CUDA
#include "xchainer/cuda/cuda_runtime.h"
#endif  // XCHAINER_ENABLE_CUDA
#include "xchainer/device.h"

namespace xchainer {
namespace numerical_gradient_internal {

Array& Subtract(const Array& lhs, const Array& rhs, Array& out) {
    lhs.device().Synchronize();
    rhs.device().Synchronize();
    out.device().Synchronize();

    VisitDtype(lhs.dtype(), [&](auto pt) {
        using T = typename decltype(pt)::type;
        IndexableArray<const T> lhs_iarray{lhs};
        IndexableArray<const T> rhs_iarray{rhs};
        IndexableArray<T> out_iarray{out};
        Indexer<> indexer{out.shape()};

        for (int64_t i = 0; i < indexer.total_size(); i++) {
            indexer.Set(i);
            out_iarray[indexer] = lhs_iarray[indexer] - rhs_iarray[indexer];
        }
    });
    return out;
}

Array& Divide(const Array& lhs, const Array& rhs, Array& out) {
    lhs.device().Synchronize();
    rhs.device().Synchronize();
    out.device().Synchronize();

    VisitDtype(lhs.dtype(), [&](auto pt) {
        using T = typename decltype(pt)::type;
        IndexableArray<const T> lhs_iarray{lhs};
        IndexableArray<const T> rhs_iarray{rhs};
        IndexableArray<T> out_iarray{out};
        Indexer<> indexer{out.shape()};

        for (int64_t i = 0; i < indexer.total_size(); i++) {
            indexer.Set(i);
            out_iarray[indexer] = lhs_iarray[indexer] / rhs_iarray[indexer];
        }
    });
    return out;
}

Array operator-(const Array& lhs, const Array& rhs) {
    Array out = Array::EmptyLike(lhs);
    Subtract(lhs, rhs, out);
    return out;
}

Array operator/(const Array& lhs, const Array& rhs) {
    Array out = Array::EmptyLike(lhs);
    Divide(lhs, rhs, out);
    return out;
}

Scalar Sum(const Array& array) {
    array.device().Synchronize();

    return VisitDtype(array.dtype(), [&](auto pt) {
        using T = typename decltype(pt)::type;
        IndexableArray<const T> iarray{array};
        Indexer<> indexer{array.shape()};

        T s = 0;
        for (int64_t i = 0; i < indexer.total_size(); i++) {
            indexer.Set(i);
            s += iarray[indexer];
        }
        return Scalar{s};
    });
}

Scalar Norm(const Array& x) {
    Scalar s = Sum(x * x);
    return Scalar(std::sqrt(static_cast<double>(s)), x.dtype());
}

Scalar VectorDot(const Array& x, const Array& y) { return Sum(x * y); }

void Set(Array& out, int64_t flat_index, Scalar value) {
    out.device().Synchronize();

    VisitDtype(out.dtype(), [&](auto pt) {
        using T = typename decltype(pt)::type;
        IndexableArray<T> iarray{out};
        Indexer<> indexer{out.shape()};
        indexer.Set(flat_index);
        iarray[indexer] = static_cast<T>(value);
    });
}

Scalar Get(const Array& out, int64_t flat_index) {
    out.device().Synchronize();

    return VisitDtype(out.dtype(), [&](auto pt) {
        using T = typename decltype(pt)::type;
        IndexableArray<const T> iarray{out};
        Indexer<> indexer{out.shape()};
        indexer.Set(flat_index);
        return Scalar{iarray[indexer]};
    });
}

Arrays CalculateNumericalGradient(
        std::function<Arrays(const Arrays&)> func,
        const Arrays& inputs,
        const Arrays& grad_outputs,
        const Arrays& eps,
        const GraphId& graph_id) {
    // TODO(niboshi): Currently only elementwise functions are supported.
    // TODO(niboshi): Implement arithmetic operations and avoid manual synchronize
    const int nin = inputs.size();
    const int nout = grad_outputs.size();

    if (eps.size() != static_cast<size_t>(nin)) {
        throw XchainerError(
                "Invalid number of eps arrays where number of inputs: " + std::to_string(nin) + ", eps: " + std::to_string(eps.size()));
    }

    for (int i = 0; i < nin; ++i) {
        if (inputs.at(i).shape() != eps.at(i).shape()) {
            throw XchainerError("Invalid eps shape");
        }
        if (inputs.at(i).dtype() != eps.at(i).dtype()) {
            throw XchainerError("Invalid eps dtype");
        }
        // TODO(niboshi): Check: eps must not contain zeros.
    }

    Dtype dtype = inputs[0].dtype();

    auto eval = [&, graph_id](int i_in, int64_t in_flat_index, Scalar eps_scalar, float multiplier) -> Arrays {
        Arrays xs;
        std::transform(inputs.begin(), inputs.end(), std::back_inserter(xs), [graph_id](const Array& x) {
            return x.AsConstant(CopyKind::kCopy).RequireGrad(graph_id);
        });

        Set(xs.at(i_in), in_flat_index, Get(xs.at(i_in), in_flat_index) + Scalar(static_cast<float>(eps_scalar) * multiplier, dtype));
        return func(xs);
    };

    Arrays grads;
    for (int i = 0; i < nin; ++i) {
        Array grad_i = Array::ZerosLike(inputs.at(i));
        int64_t size = grad_i.GetTotalSize();

        for (int64_t in_flat_index = 0; in_flat_index < size; ++in_flat_index) {
            Scalar eps_scalar = Get(eps.at(i), in_flat_index);
            Arrays ys0 = eval(i, in_flat_index, eps_scalar, -1);
            Arrays ys1 = eval(i, in_flat_index, eps_scalar, 1);

            for (int j = 0; j < nout; ++j) {
                Array dy = ys1.at(j) - ys0.at(j);
                Array denom = Array::FullLike(dy, eps_scalar) * Array::FullLike(dy, Scalar(2, dtype));

                Scalar g = VectorDot((ys1.at(j) - ys0.at(j)) / denom, grad_outputs.at(j));
                Scalar g_ij = Get(grad_i, in_flat_index) + g;
                Set(grad_i, in_flat_index, g_ij);
            }
        }
        grads.push_back(grad_i);
    }

    return grads;
}

}  // namespace numerical_gradient_internal
}  // namespace xchainer
<|endoftext|>


<commit_before>/**
   @file clientapitest.cpp
   @brief Automatic tests for sensor client interfaces

   Copyright (C) 2009-2010 Nokia Corporation

   @author Timo Rongas <ext-timo.2.rongas@nokia.com>
   @author Ustun Ergenoglu <ext-ustun.ergenoglu@nokia.com>

   This file is part of Sensord.

   Sensord is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License
   version 2.1 as published by the Free Software Foundation.

   Sensord 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
   Lesser General Public License for more details.

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

*/

#include "qt-api/sensormanagerinterface.h"

#include "qt-api/orientationsensor_i.h"
#include "qt-api/accelerometersensor_i.h"
#include "qt-api/compasssensor_i.h"
#include "qt-api/tapsensor_i.h"
#include "qt-api/alssensor_i.h"
#include "qt-api/proximitysensor_i.h"
#include "qt-api/rotationsensor_i.h"
#include "qt-api/magnetometersensor_i.h"

#include "clientapitest.h"

void ClientApiTest::initTestCase()
{
    qDBusRegisterMetaType<XYZ>();
    qDBusRegisterMetaType<Compass>();
    qDBusRegisterMetaType<MagneticField>();
    qDBusRegisterMetaType<Tap>();
    qDBusRegisterMetaType<Unsigned>();

    SensorManagerInterface& remoteSensorManager = SensorManagerInterface::instance();
    QVERIFY( remoteSensorManager.isValid() );

    // Load plugins (should test running depend on plug-in load result?)
    qDebug() << remoteSensorManager.loadPlugin("orientationsensor");
    remoteSensorManager.loadPlugin("accelerometersensor");
    remoteSensorManager.loadPlugin("compasssensor");
    remoteSensorManager.loadPlugin("tapsensor");
    remoteSensorManager.loadPlugin("alssensor");
    remoteSensorManager.loadPlugin("proximitysensor");
    remoteSensorManager.loadPlugin("rotationsensor");
    remoteSensorManager.loadPlugin("magnetometersensor");

    // Register interfaces (can this be done inside the plugins?
    remoteSensorManager.registerSensorInterface<OrientationSensorChannelInterface>("orientationsensor");
    remoteSensorManager.registerSensorInterface<AccelerometerSensorChannelInterface>("accelerometersensor");
    remoteSensorManager.registerSensorInterface<CompassSensorChannelInterface>("compasssensor");
    remoteSensorManager.registerSensorInterface<TapSensorChannelInterface>("tapsensor");
    remoteSensorManager.registerSensorInterface<ALSSensorChannelInterface>("alssensor");
    remoteSensorManager.registerSensorInterface<ProximitySensorChannelInterface>("proximitysensor");
    remoteSensorManager.registerSensorInterface<RotationSensorChannelInterface>("rotationsensor");
    remoteSensorManager.registerSensorInterface<MagnetometerSensorChannelInterface>("magnetometersensor");
}

void ClientApiTest::init()
{
    //qDebug() << "Run before each test";
    //TODO: Verify that sensord has not crashed.
}

void ClientApiTest::cleanup()
{
    //qDebug() << "Run after each test";
    //TODO: Verify that sensord has not crashed.
}

void ClientApiTest::cleanupTestCase()
{
    //qDebug() << "Run after all test cases";
}

void ClientApiTest::testOrientationSensor()
{
    QString sensorName("orientationsensor");
    SensorManagerInterface& sm = SensorManagerInterface::instance();
    QVERIFY( sm.isValid() );

    // Get control session
    OrientationSensorChannelInterface* sensorIfc = OrientationSensorChannelInterface::controlInterface(sensorName);
    QVERIFY2(sensorIfc && sensorIfc->isValid(), "Failed to get control session");

    // Attempt to get another control session
    OrientationSensorChannelInterface* failIfc = OrientationSensorChannelInterface::controlInterface(sensorName);
    QVERIFY2(!failIfc, "Got another control session");

    // Get listen session
    const OrientationSensorChannelInterface* listenIfc = OrientationSensorChannelInterface::listenInterface(sensorName);
    QVERIFY2(listenIfc && listenIfc->isValid(), "Failed to get listen session");
    delete listenIfc;

    // Test properties
    sensorIfc->setInterval(100);

    QVERIFY(sensorIfc->orientation() == qvariant_cast<Unsigned>(sensorIfc->property("orientation")));
    QVERIFY(sensorIfc->threshold() == sensorIfc->property("threshold"));
    int offset = 200;
    int threshold = sensorIfc->threshold();
    sensorIfc->setThreshold(threshold + offset);
    QVERIFY(threshold+offset == sensorIfc->threshold());
    sensorIfc->setThreshold(threshold);

    // test start
    QDBusReply<void> reply = sensorIfc->start();
    QVERIFY(reply.isValid());

    // test stop
    reply = sensorIfc->stop();
    QVERIFY(reply.isValid());

    delete sensorIfc;
}

void ClientApiTest::testAccelerometerSensor()
{
    QString sensorName("accelerometersensor");
    SensorManagerInterface& sm = SensorManagerInterface::instance();
    QVERIFY( sm.isValid() );

    // Get control session
    AccelerometerSensorChannelInterface* sensorIfc = AccelerometerSensorChannelInterface::controlInterface(sensorName);
    QVERIFY2(sensorIfc && sensorIfc->isValid(), "Failed to get control session");

    // Attempt to get another control session
    AccelerometerSensorChannelInterface* failIfc = AccelerometerSensorChannelInterface::controlInterface(sensorName);
    QVERIFY2(!failIfc, "Got another control session");

    // Get listen session
    const AccelerometerSensorChannelInterface* listenIfc = AccelerometerSensorChannelInterface::listenInterface(sensorName);
    QVERIFY2(listenIfc && listenIfc->isValid(), "Failed to get listen session");
    delete listenIfc;

    // Test properties
    sensorIfc->setInterval(100);

    // test start
    QDBusReply<void> reply = sensorIfc->start();
    QVERIFY(reply.isValid());

    // test stop
    reply = sensorIfc->stop();
    QVERIFY(reply.isValid());

    XYZ sample1 = sensorIfc->get();
    XYZ sample2 = qvariant_cast<XYZ>(sensorIfc->property("value"));
    QVERIFY(sample1 == sample2);

    delete sensorIfc;
}

void ClientApiTest::testMagnetometerSensor()
{
    QString sensorName("magnetometersensor");
    SensorManagerInterface& sm = SensorManagerInterface::instance();
    QVERIFY( sm.isValid() );

    // Get control session
    MagnetometerSensorChannelInterface* sensorIfc = MagnetometerSensorChannelInterface::controlInterface(sensorName);
    QVERIFY2(sensorIfc && sensorIfc->isValid(), "Failed to get control session");

    // Attempt to get another control session
    MagnetometerSensorChannelInterface* failIfc = MagnetometerSensorChannelInterface::controlInterface(sensorName);
    QVERIFY2(!failIfc, "Got another control session");

    // Get listen session
    const MagnetometerSensorChannelInterface* listenIfc = MagnetometerSensorChannelInterface::listenInterface(sensorName);
    QVERIFY2(listenIfc && listenIfc->isValid(), "Failed to get listen session");
    delete listenIfc;

    // Test properties
    sensorIfc->setInterval(100);

    // Need simulated data to make sensible test ouf of this.
    MagneticField sample1 = sensorIfc->magneticField();
    MagneticField sample2 = qvariant_cast<MagneticField>(sensorIfc->property("magneticField"));
    // Background process keeps magnetometer on -- values are changing, and subsequent
    // calls are likely to give different values. This makes thus no sense.
    // QVERIFY(sample1 == sample2);

    // test start
    QDBusReply<void> reply = sensorIfc->start();
    QVERIFY(reply.isValid());

    // test stop
    reply = sensorIfc->stop();
    QVERIFY(reply.isValid());

    delete sensorIfc;
}

void ClientApiTest::testCompassSensor()
{
    QString sensorName("compasssensor");
    SensorManagerInterface& sm = SensorManagerInterface::instance();
    QVERIFY( sm.isValid() );

    // Get control session
    CompassSensorChannelInterface* sensorIfc = CompassSensorChannelInterface::controlInterface(sensorName);
    QVERIFY2(sensorIfc && sensorIfc->isValid(), "Failed to get control session");

    // Attempt to get another control session
    CompassSensorChannelInterface* failIfc = CompassSensorChannelInterface::controlInterface(sensorName);
    QVERIFY2(!failIfc, "Got another control session");

    // Get listen session
    const CompassSensorChannelInterface* listenIfc = CompassSensorChannelInterface::listenInterface(sensorName);
    QVERIFY2(listenIfc && listenIfc->isValid(), "Failed to get listen session");
    delete listenIfc;

    // Test properties
    sensorIfc->setInterval(100);

    QVERIFY(sensorIfc->get() == qvariant_cast<Compass>(sensorIfc->property("value")));
    QVERIFY(sensorIfc->useDeclination() == sensorIfc->property("usedeclination"));
    QVERIFY(sensorIfc->declinationValue() == sensorIfc->property("declinationvalue"));

    // Check declination on/off
    bool declinationInUse = sensorIfc->useDeclination();
    sensorIfc->setUseDeclination(!declinationInUse);
    QVERIFY(declinationInUse != sensorIfc->useDeclination());
    sensorIfc->setUseDeclination(!declinationInUse);

    // TODO: Compare declination value against value in gconf

    // test start
    QDBusReply<void> reply = sensorIfc->start();
    QVERIFY(reply.isValid());

    // test stop
    reply = sensorIfc->stop();
    QVERIFY(reply.isValid());

    delete sensorIfc;
}

void ClientApiTest::testTapSensor()
{
    QString sensorName("tapsensor");
    SensorManagerInterface& sm = SensorManagerInterface::instance();
    QVERIFY( sm.isValid() );

    // Get control session
    TapSensorChannelInterface* sensorIfc = TapSensorChannelInterface::controlInterface(sensorName);
    QVERIFY2(sensorIfc && sensorIfc->isValid(), "Failed to get control session");

    // Attempt to get another control session
    TapSensorChannelInterface* failIfc = TapSensorChannelInterface::controlInterface(sensorName);
    QVERIFY2(!failIfc, "Got another control session");

    // Get listen session
    const TapSensorChannelInterface* listenIfc = TapSensorChannelInterface::listenInterface(sensorName);
    QVERIFY2(listenIfc && listenIfc->isValid(), "Failed to get listen session");
    delete listenIfc;

    // Test properties
    sensorIfc->setInterval(100);

    // test start
    QDBusReply<void> reply = sensorIfc->start();
    QVERIFY(reply.isValid());

    // test stop
    reply = sensorIfc->stop();
    QVERIFY(reply.isValid());

    delete sensorIfc;
}

void ClientApiTest::testALSSensor()
{
    QString sensorName("alssensor");
    SensorManagerInterface& sm = SensorManagerInterface::instance();
    QVERIFY( sm.isValid() );

    // Get control session
    // TODO: Because context takes control over ALS in sensord::main(),
    //       we can't test this. (applies to all other possible open
    //       sessions too...)
    //ALSSensorChannelInterface* sensorIfc = ALSSensorChannelInterface::controlInterface(sensorName);
    //QVERIFY2(sensorIfc && sensorIfc->isValid(), "Failed to get control session");

    // Attempt to get another control session
    ALSSensorChannelInterface* failIfc = ALSSensorChannelInterface::controlInterface(sensorName);
    QVERIFY2(!failIfc, "Got another control session");

    // Get listen session
    const ALSSensorChannelInterface* listenIfc = ALSSensorChannelInterface::listenInterface(sensorName);
    QVERIFY2(listenIfc && listenIfc->isValid(), "Failed to get listen session");
    delete listenIfc;

    // Test properties
    //sensorIfc->setInterval(100);

    // test start
    //QDBusReply<void> reply = sensorIfc->start();
    //QVERIFY(reply.isValid());

    // test stop
    //reply = sensorIfc->stop();
    //QVERIFY(reply.isValid());

    //delete sensorIfc;
}

void ClientApiTest::testProximitySensor()
{
    QString sensorName("proximitysensor");
    SensorManagerInterface& sm = SensorManagerInterface::instance();
    QVERIFY( sm.isValid() );

    // Get control session
    ProximitySensorChannelInterface* sensorIfc = ProximitySensorChannelInterface::controlInterface(sensorName);
    QVERIFY2(sensorIfc && sensorIfc->isValid(), "Failed to get control session");

    // Attempt to get another control session
    ProximitySensorChannelInterface* failIfc = ProximitySensorChannelInterface::controlInterface(sensorName);
    QVERIFY2(!failIfc, "Got another control session");

    // Get listen session
    const ProximitySensorChannelInterface* listenIfc = ProximitySensorChannelInterface::listenInterface(sensorName);
    QVERIFY2(listenIfc && listenIfc->isValid(), "Failed to get listen session");
    delete listenIfc;

    // Test properties
    sensorIfc->setInterval(100);

    // test start
    QDBusReply<void> reply = sensorIfc->start();
    QVERIFY(reply.isValid());

    // test stop
    reply = sensorIfc->stop();
    QVERIFY(reply.isValid());

    delete sensorIfc;
}

void ClientApiTest::testRotationSensor()
{
    QString sensorName("rotationsensor");
    SensorManagerInterface& sm = SensorManagerInterface::instance();
    QVERIFY( sm.isValid() );

    // Get control session
    RotationSensorChannelInterface* sensorIfc = RotationSensorChannelInterface::controlInterface(sensorName);
    QVERIFY2(sensorIfc && sensorIfc->isValid(), "Failed to get control session");

    // Attempt to get another control session
    RotationSensorChannelInterface* failIfc = RotationSensorChannelInterface::controlInterface(sensorName);
    QVERIFY2(!failIfc, "Got another control session");

    // Get listen session
    const RotationSensorChannelInterface* listenIfc = RotationSensorChannelInterface::listenInterface(sensorName);
    QVERIFY2(listenIfc && listenIfc->isValid(), "Failed to get listen session");
    delete listenIfc;

    // Test properties
    sensorIfc->setInterval(100);

    bool hasz = sensorIfc->hasZ();
    qDebug() << "Z-axis rotation calculations in use:" << hasz;

    // Need simulated data to make sensible test ouf of this.
    XYZ sample1 = sensorIfc->rotation();
    XYZ sample2 = qvariant_cast<XYZ>(sensorIfc->property("rotation"));
    QVERIFY(sample1 == sample2);

    // test start
    QDBusReply<void> reply = sensorIfc->start();
    QVERIFY(reply.isValid());

    // test stop
    reply = sensorIfc->stop();
    QVERIFY(reply.isValid());

    delete sensorIfc;
}

/**
 * Runs two sensors using the same adaptor/chain simultaneously.
 *
 * For the sake of testing, OrientationSensor and AccelerometerSensor are
 * used. AccelerometerChain is shared.
 *
 * @note Sensord hacks starting context framework things might affect this by
 *       adding other sensors that use the same adaptor.
 */
void ClientApiTest::testCommonAdaptorPipeline()
{
    int DELAY = 250;
    OrientationSensorChannelInterface *orientation;
    AccelerometerSensorChannelInterface *accelerometer;

    orientation = OrientationSensorChannelInterface::controlInterface("orientationsensor");
    QVERIFY2(orientation && orientation->isValid(), "Could not get orientation sensor control channel");
    orientation->setInterval(100);

    orientation->start();
    qDebug() << "Orientation sensor started, waiting for" << DELAY << "ms.";
    QTest::qWait(DELAY);

    accelerometer = AccelerometerSensorChannelInterface::controlInterface("accelerometersensor");
    QVERIFY2(accelerometer && accelerometer->isValid(), "Could not get accelerometer sensor control channel");
    accelerometer->setInterval(100);

    accelerometer->start();
    qDebug() << "Accelerometer sensor started, waiting for" << DELAY << "ms.";
    QTest::qWait(DELAY);

    orientation->stop();
    qDebug() << "Orientation sensor stopped, waiting for" << DELAY << "ms.";
    QTest::qWait(DELAY);

    delete orientation;
    qDebug() << "Orientation sensor destroyed, waiting for" << DELAY << "ms.";
    QTest::qWait(DELAY);

    accelerometer->stop();
    qDebug() << "Accelerometer sensor stopped.";

    delete accelerometer;
}

void ClientApiTest::testListenSessionInitiation()
{
    const OrientationSensorChannelInterface *orientation;

    orientation = OrientationSensorChannelInterface::listenInterface("orientationsensor");

    QVERIFY2(orientation && orientation->isValid(), "Could not get orientation sensor listen channel");
}

QTEST_MAIN(ClientApiTest)
<commit_msg>fixed client api tests.<commit_after>/**
   @file clientapitest.cpp
   @brief Automatic tests for sensor client interfaces

   Copyright (C) 2009-2010 Nokia Corporation

   @author Timo Rongas <ext-timo.2.rongas@nokia.com>
   @author Ustun Ergenoglu <ext-ustun.ergenoglu@nokia.com>

   This file is part of Sensord.

   Sensord is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License
   version 2.1 as published by the Free Software Foundation.

   Sensord 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
   Lesser General Public License for more details.

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

*/

#include "qt-api/sensormanagerinterface.h"

#include "qt-api/orientationsensor_i.h"
#include "qt-api/accelerometersensor_i.h"
#include "qt-api/compasssensor_i.h"
#include "qt-api/tapsensor_i.h"
#include "qt-api/alssensor_i.h"
#include "qt-api/proximitysensor_i.h"
#include "qt-api/rotationsensor_i.h"
#include "qt-api/magnetometersensor_i.h"

#include "clientapitest.h"

void ClientApiTest::initTestCase()
{
    qDBusRegisterMetaType<XYZ>();
    qDBusRegisterMetaType<Compass>();
    qDBusRegisterMetaType<MagneticField>();
    qDBusRegisterMetaType<Tap>();
    qDBusRegisterMetaType<Unsigned>();

    SensorManagerInterface& remoteSensorManager = SensorManagerInterface::instance();
    QVERIFY( remoteSensorManager.isValid() );

    // Load plugins (should test running depend on plug-in load result?)
    qDebug() << remoteSensorManager.loadPlugin("orientationsensor");
    remoteSensorManager.loadPlugin("accelerometersensor");
    remoteSensorManager.loadPlugin("compasssensor");
    remoteSensorManager.loadPlugin("tapsensor");
    remoteSensorManager.loadPlugin("alssensor");
    remoteSensorManager.loadPlugin("proximitysensor");
    remoteSensorManager.loadPlugin("rotationsensor");
    remoteSensorManager.loadPlugin("magnetometersensor");

    // Register interfaces (can this be done inside the plugins?
    remoteSensorManager.registerSensorInterface<OrientationSensorChannelInterface>("orientationsensor");
    remoteSensorManager.registerSensorInterface<AccelerometerSensorChannelInterface>("accelerometersensor");
    remoteSensorManager.registerSensorInterface<CompassSensorChannelInterface>("compasssensor");
    remoteSensorManager.registerSensorInterface<TapSensorChannelInterface>("tapsensor");
    remoteSensorManager.registerSensorInterface<ALSSensorChannelInterface>("alssensor");
    remoteSensorManager.registerSensorInterface<ProximitySensorChannelInterface>("proximitysensor");
    remoteSensorManager.registerSensorInterface<RotationSensorChannelInterface>("rotationsensor");
    remoteSensorManager.registerSensorInterface<MagnetometerSensorChannelInterface>("magnetometersensor");
}

void ClientApiTest::init()
{
    //qDebug() << "Run before each test";
    //TODO: Verify that sensord has not crashed.
}

void ClientApiTest::cleanup()
{
    //qDebug() << "Run after each test";
    //TODO: Verify that sensord has not crashed.
}

void ClientApiTest::cleanupTestCase()
{
    //qDebug() << "Run after all test cases";
}

void ClientApiTest::testOrientationSensor()
{
    QString sensorName("orientationsensor");
    SensorManagerInterface& sm = SensorManagerInterface::instance();
    QVERIFY( sm.isValid() );

    // Get control session
    OrientationSensorChannelInterface* sensorIfc = OrientationSensorChannelInterface::controlInterface(sensorName);
    QVERIFY2(sensorIfc && sensorIfc->isValid(), "Failed to get control session");

    // Attempt to get another control session
    OrientationSensorChannelInterface* failIfc = OrientationSensorChannelInterface::controlInterface(sensorName);
    QVERIFY2(!failIfc, "Got another control session");

    // Get listen session
    const OrientationSensorChannelInterface* listenIfc = OrientationSensorChannelInterface::listenInterface(sensorName);
    QVERIFY2(listenIfc && listenIfc->isValid(), "Failed to get listen session");
    delete listenIfc;

    // Test properties
    sensorIfc->setInterval(100);

    QVERIFY(sensorIfc->orientation() == qvariant_cast<Unsigned>(sensorIfc->property("orientation")));

    // test start
    QDBusReply<void> reply = sensorIfc->start();
    QVERIFY(reply.isValid());

    // test stop
    reply = sensorIfc->stop();
    QVERIFY(reply.isValid());

    delete sensorIfc;
}

void ClientApiTest::testAccelerometerSensor()
{
    QString sensorName("accelerometersensor");
    SensorManagerInterface& sm = SensorManagerInterface::instance();
    QVERIFY( sm.isValid() );

    // Get control session
    AccelerometerSensorChannelInterface* sensorIfc = AccelerometerSensorChannelInterface::controlInterface(sensorName);
    QVERIFY2(sensorIfc && sensorIfc->isValid(), "Failed to get control session");

    // Attempt to get another control session
    AccelerometerSensorChannelInterface* failIfc = AccelerometerSensorChannelInterface::controlInterface(sensorName);
    QVERIFY2(!failIfc, "Got another control session");

    // Get listen session
    const AccelerometerSensorChannelInterface* listenIfc = AccelerometerSensorChannelInterface::listenInterface(sensorName);
    QVERIFY2(listenIfc && listenIfc->isValid(), "Failed to get listen session");
    delete listenIfc;

    // Test properties
    sensorIfc->setInterval(100);

    // test start
    QDBusReply<void> reply = sensorIfc->start();
    QVERIFY(reply.isValid());

    // test stop
    reply = sensorIfc->stop();
    QVERIFY(reply.isValid());

    XYZ sample1 = sensorIfc->get();
    XYZ sample2 = qvariant_cast<XYZ>(sensorIfc->property("value"));
    QVERIFY(sample1 == sample2);

    delete sensorIfc;
}

void ClientApiTest::testMagnetometerSensor()
{
    QString sensorName("magnetometersensor");
    SensorManagerInterface& sm = SensorManagerInterface::instance();
    QVERIFY( sm.isValid() );

    // Get control session
    MagnetometerSensorChannelInterface* sensorIfc = MagnetometerSensorChannelInterface::controlInterface(sensorName);
    QVERIFY2(sensorIfc && sensorIfc->isValid(), "Failed to get control session");

    // Attempt to get another control session
    MagnetometerSensorChannelInterface* failIfc = MagnetometerSensorChannelInterface::controlInterface(sensorName);
    QVERIFY2(!failIfc, "Got another control session");

    // Get listen session
    const MagnetometerSensorChannelInterface* listenIfc = MagnetometerSensorChannelInterface::listenInterface(sensorName);
    QVERIFY2(listenIfc && listenIfc->isValid(), "Failed to get listen session");
    delete listenIfc;

    // Test properties
    sensorIfc->setInterval(100);

    // Need simulated data to make sensible test ouf of this.
    MagneticField sample1 = sensorIfc->magneticField();
    MagneticField sample2 = qvariant_cast<MagneticField>(sensorIfc->property("magneticField"));
    // Background process keeps magnetometer on -- values are changing, and subsequent
    // calls are likely to give different values. This makes thus no sense.
    // QVERIFY(sample1 == sample2);

    // test start
    QDBusReply<void> reply = sensorIfc->start();
    QVERIFY(reply.isValid());

    // test stop
    reply = sensorIfc->stop();
    QVERIFY(reply.isValid());

    delete sensorIfc;
}

void ClientApiTest::testCompassSensor()
{
    QString sensorName("compasssensor");
    SensorManagerInterface& sm = SensorManagerInterface::instance();
    QVERIFY( sm.isValid() );

    // Get control session
    CompassSensorChannelInterface* sensorIfc = CompassSensorChannelInterface::controlInterface(sensorName);
    QVERIFY2(sensorIfc && sensorIfc->isValid(), "Failed to get control session");

    // Attempt to get another control session
    CompassSensorChannelInterface* failIfc = CompassSensorChannelInterface::controlInterface(sensorName);
    QVERIFY2(!failIfc, "Got another control session");

    // Get listen session
    const CompassSensorChannelInterface* listenIfc = CompassSensorChannelInterface::listenInterface(sensorName);
    QVERIFY2(listenIfc && listenIfc->isValid(), "Failed to get listen session");
    delete listenIfc;

    // Test properties
    sensorIfc->setInterval(100);

    QVERIFY(sensorIfc->get() == qvariant_cast<Compass>(sensorIfc->property("value")));
    QVERIFY(sensorIfc->useDeclination() == sensorIfc->property("usedeclination"));
    QVERIFY(sensorIfc->declinationValue() == sensorIfc->property("declinationvalue"));

    // Check declination on/off
    bool declinationInUse = sensorIfc->useDeclination();
    sensorIfc->setUseDeclination(!declinationInUse);
    QVERIFY(declinationInUse != sensorIfc->useDeclination());
    sensorIfc->setUseDeclination(!declinationInUse);

    // TODO: Compare declination value against value in gconf

    // test start
    QDBusReply<void> reply = sensorIfc->start();
    QVERIFY(reply.isValid());

    // test stop
    reply = sensorIfc->stop();
    QVERIFY(reply.isValid());

    delete sensorIfc;
}

void ClientApiTest::testTapSensor()
{
    QString sensorName("tapsensor");
    SensorManagerInterface& sm = SensorManagerInterface::instance();
    QVERIFY( sm.isValid() );

    // Get control session
    TapSensorChannelInterface* sensorIfc = TapSensorChannelInterface::controlInterface(sensorName);
    QVERIFY2(sensorIfc && sensorIfc->isValid(), "Failed to get control session");

    // Attempt to get another control session
    TapSensorChannelInterface* failIfc = TapSensorChannelInterface::controlInterface(sensorName);
    QVERIFY2(!failIfc, "Got another control session");

    // Get listen session
    const TapSensorChannelInterface* listenIfc = TapSensorChannelInterface::listenInterface(sensorName);
    QVERIFY2(listenIfc && listenIfc->isValid(), "Failed to get listen session");
    delete listenIfc;

    // Test properties
    sensorIfc->setInterval(100);

    // test start
    QDBusReply<void> reply = sensorIfc->start();
    QVERIFY(reply.isValid());

    // test stop
    reply = sensorIfc->stop();
    QVERIFY(reply.isValid());

    delete sensorIfc;
}

void ClientApiTest::testALSSensor()
{
    QString sensorName("alssensor");
    SensorManagerInterface& sm = SensorManagerInterface::instance();
    QVERIFY( sm.isValid() );

    // Get control session
    // TODO: Because context takes control over ALS in sensord::main(),
    //       we can't test this. (applies to all other possible open
    //       sessions too...)
    //ALSSensorChannelInterface* sensorIfc = ALSSensorChannelInterface::controlInterface(sensorName);
    //QVERIFY2(sensorIfc && sensorIfc->isValid(), "Failed to get control session");

    // Attempt to get another control session
    ALSSensorChannelInterface* failIfc = ALSSensorChannelInterface::controlInterface(sensorName);
    QVERIFY2(!failIfc, "Got another control session");

    // Get listen session
    const ALSSensorChannelInterface* listenIfc = ALSSensorChannelInterface::listenInterface(sensorName);
    QVERIFY2(listenIfc && listenIfc->isValid(), "Failed to get listen session");
    delete listenIfc;

    // Test properties
    //sensorIfc->setInterval(100);

    // test start
    //QDBusReply<void> reply = sensorIfc->start();
    //QVERIFY(reply.isValid());

    // test stop
    //reply = sensorIfc->stop();
    //QVERIFY(reply.isValid());

    //delete sensorIfc;
}

void ClientApiTest::testProximitySensor()
{
    QString sensorName("proximitysensor");
    SensorManagerInterface& sm = SensorManagerInterface::instance();
    QVERIFY( sm.isValid() );

    // Get control session
    ProximitySensorChannelInterface* sensorIfc = ProximitySensorChannelInterface::controlInterface(sensorName);
    QVERIFY2(sensorIfc && sensorIfc->isValid(), "Failed to get control session");

    // Attempt to get another control session
    ProximitySensorChannelInterface* failIfc = ProximitySensorChannelInterface::controlInterface(sensorName);
    QVERIFY2(!failIfc, "Got another control session");

    // Get listen session
    const ProximitySensorChannelInterface* listenIfc = ProximitySensorChannelInterface::listenInterface(sensorName);
    QVERIFY2(listenIfc && listenIfc->isValid(), "Failed to get listen session");
    delete listenIfc;

    // Test properties
    sensorIfc->setInterval(100);

    // test start
    QDBusReply<void> reply = sensorIfc->start();
    QVERIFY(reply.isValid());

    // test stop
    reply = sensorIfc->stop();
    QVERIFY(reply.isValid());

    delete sensorIfc;
}

void ClientApiTest::testRotationSensor()
{
    QString sensorName("rotationsensor");
    SensorManagerInterface& sm = SensorManagerInterface::instance();
    QVERIFY( sm.isValid() );

    // Get control session
    RotationSensorChannelInterface* sensorIfc = RotationSensorChannelInterface::controlInterface(sensorName);
    QVERIFY2(sensorIfc && sensorIfc->isValid(), "Failed to get control session");

    // Attempt to get another control session
    RotationSensorChannelInterface* failIfc = RotationSensorChannelInterface::controlInterface(sensorName);
    QVERIFY2(!failIfc, "Got another control session");

    // Get listen session
    const RotationSensorChannelInterface* listenIfc = RotationSensorChannelInterface::listenInterface(sensorName);
    QVERIFY2(listenIfc && listenIfc->isValid(), "Failed to get listen session");
    delete listenIfc;

    // Test properties
    sensorIfc->setInterval(100);

    bool hasz = sensorIfc->hasZ();
    qDebug() << "Z-axis rotation calculations in use:" << hasz;

    // Need simulated data to make sensible test ouf of this.
    XYZ sample1 = sensorIfc->rotation();
    XYZ sample2 = qvariant_cast<XYZ>(sensorIfc->property("rotation"));
    QVERIFY(sample1 == sample2);

    // test start
    QDBusReply<void> reply = sensorIfc->start();
    QVERIFY(reply.isValid());

    // test stop
    reply = sensorIfc->stop();
    QVERIFY(reply.isValid());

    delete sensorIfc;
}

/**
 * Runs two sensors using the same adaptor/chain simultaneously.
 *
 * For the sake of testing, OrientationSensor and AccelerometerSensor are
 * used. AccelerometerChain is shared.
 *
 * @note Sensord hacks starting context framework things might affect this by
 *       adding other sensors that use the same adaptor.
 */
void ClientApiTest::testCommonAdaptorPipeline()
{
    int DELAY = 250;
    OrientationSensorChannelInterface *orientation;
    AccelerometerSensorChannelInterface *accelerometer;

    orientation = OrientationSensorChannelInterface::controlInterface("orientationsensor");
    QVERIFY2(orientation && orientation->isValid(), "Could not get orientation sensor control channel");
    orientation->setInterval(100);

    orientation->start();
    qDebug() << "Orientation sensor started, waiting for" << DELAY << "ms.";
    QTest::qWait(DELAY);

    accelerometer = AccelerometerSensorChannelInterface::controlInterface("accelerometersensor");
    QVERIFY2(accelerometer && accelerometer->isValid(), "Could not get accelerometer sensor control channel");
    accelerometer->setInterval(100);

    accelerometer->start();
    qDebug() << "Accelerometer sensor started, waiting for" << DELAY << "ms.";
    QTest::qWait(DELAY);

    orientation->stop();
    qDebug() << "Orientation sensor stopped, waiting for" << DELAY << "ms.";
    QTest::qWait(DELAY);

    delete orientation;
    qDebug() << "Orientation sensor destroyed, waiting for" << DELAY << "ms.";
    QTest::qWait(DELAY);

    accelerometer->stop();
    qDebug() << "Accelerometer sensor stopped.";

    delete accelerometer;
}

void ClientApiTest::testListenSessionInitiation()
{
    const OrientationSensorChannelInterface *orientation;

    orientation = OrientationSensorChannelInterface::listenInterface("orientationsensor");

    QVERIFY2(orientation && orientation->isValid(), "Could not get orientation sensor listen channel");
}

QTEST_MAIN(ClientApiTest)
<|endoftext|>


<commit_before>#include <map>

#include <unistd.h>

#include "database.hpp"

#include "cmap.hpp"

#define MAX_COLLECTIONS 100
#define WRITE_INTERVAL  30

using namespace std;

static int backup = -1;

struct Coll {
  pthread_mutex_t mutex;
  string name;
  cmap<int,JSON> documents;
  Coll() : mutex(PTHREAD_MUTEX_INITIALIZER) {}
  void read() { // this function is called only once, by a locked piece of code
    JSON tmp;
    if (!tmp.read_file("database/"+name+".json")) return;
    for (auto& doc : tmp.arr()) documents[doc("_id")] = doc("document");
  }
  void write() {
    JSON tmp(vector<JSON>{});
    pthread_mutex_lock(&mutex);
    for (auto& kv : documents) tmp.emplace_back(move(map<string,JSON>{
      {"_id"      , kv.first},
      {"document" , kv.second}
    }));
    pthread_mutex_unlock(&mutex);
    tmp.write_file("database/"+name+".json");
  }
  int create(JSON&& doc) {
    int id = 1;
    pthread_mutex_lock(&mutex);
    if (documents.size() > 0) id += documents.max_key();
    documents[id] = move(doc);
    pthread_mutex_unlock(&mutex);
    return id;
  }
  bool retrieve(int id, JSON& doc) {
    pthread_mutex_lock(&mutex);
    auto it = documents.find(id);
    if (it == documents.end()) {
      doc.setnull();
      pthread_mutex_unlock(&mutex);
      return false;
    }
    doc = it->second;
    pthread_mutex_unlock(&mutex);
    return true;
  }
  JSON retrieve(const JSON& filter) {
    JSON ans(vector<JSON>{});
    pthread_mutex_lock(&mutex);
    for (auto& kv : documents) if (filter.issubobj(kv.second)) {
      JSON tmp = kv.second;
      tmp["id"] = kv.first;
      ans.push_back(move(tmp));
    }
    pthread_mutex_unlock(&mutex);
    return ans;
  }
  JSON retrieve_page(unsigned p, unsigned ps) {
    JSON ans(vector<JSON>{});
    pthread_mutex_lock(&mutex);
    if (!ps) p = 0, ps = documents.size();
    auto it = documents.at(p*ps);
    for (int i = 0; i < ps && it != documents.end(); i++, it++) {
      JSON tmp = it->second;
      tmp["id"] = it->first;
      ans.push_back(move(tmp));
    }
    pthread_mutex_unlock(&mutex);
    return ans;
  }
  bool update(int id, JSON&& doc) {
    pthread_mutex_lock(&mutex);
    auto it = documents.find(id);
    if (it == documents.end()) {
      pthread_mutex_unlock(&mutex);
      return false;
    }
    it->second = move(doc);
    pthread_mutex_unlock(&mutex);
    return true;
  }
  bool update(const Database::Updater& upd, int id) {
    bool ans = false;
    pthread_mutex_lock(&mutex);
    auto it = documents.find(id);
    if (it != documents.end()) {
      ans = upd(*it);
      pthread_mutex_unlock(&mutex);
      return ans;
    }
    for (auto& kv : documents) ans = ans || upd(kv);
    pthread_mutex_unlock(&mutex);
    return ans;
  }
  bool destroy(int id) {
    pthread_mutex_lock(&mutex);
    auto it = documents.find(id);
    if (it == documents.end()) {
      pthread_mutex_unlock(&mutex);
      return false;
    }
    documents.erase(it);
    pthread_mutex_unlock(&mutex);
    return true;
  }
};
static Coll collection[MAX_COLLECTIONS];
static int ncolls = 0;
static pthread_mutex_t colls_mutex = PTHREAD_MUTEX_INITIALIZER;
static int get(const string& name) {
  static map<string,int> colls;
  int i;
  pthread_mutex_lock(&colls_mutex);
  auto it = colls.find(name);
  if (it != colls.end()) {
    i = it->second;
    pthread_mutex_unlock(&colls_mutex);
    return i;
  }
  i = ncolls++;
  colls[name] = i;
  collection[i].name = name;
  collection[i].read();
  pthread_mutex_unlock(&colls_mutex);
  return i;
}

// thread
static bool quit = false;
static pthread_t db;
static void do_backup() {
  stringstream ss;
  ss << "backup" << backup;
  system(("mkdir -p database/"+ss.str()).c_str());
  system(("cp database/*.json database/"+ss.str()).c_str());
}
static void update() {
  int nc;
  pthread_mutex_lock(&colls_mutex);
  nc = ncolls;
  pthread_mutex_unlock(&colls_mutex);
  for (int i = 0; i < nc; i++) collection[i].write();
  if (backup < 0) return;
  do_backup();
  backup = 1-backup;
}
static void* thread(void*) {
  static time_t upd = 0;
  while (!quit) {
    if (upd <= time(nullptr)) {
      update();
      upd = time(nullptr)+WRITE_INTERVAL;
    }
    usleep(100000);
  }
}

namespace Database {

Collection::Collection(const string& name) : collid(get(name)) {

}

int Collection::create(const JSON& document) {
  JSON tmp(document);
  return collection[collid].create(move(tmp));
}

int Collection::create(JSON&& document) {
  return collection[collid].create(move(document));
}

JSON Collection::retrieve(int docid) {
  JSON ans;
  collection[collid].retrieve(docid,ans);
  return ans;
}

bool Collection::retrieve(int docid, JSON& document) {
  return collection[collid].retrieve(docid,document);
}

JSON Collection::retrieve(const JSON& filter) {
  return collection[collid].retrieve(filter);
}

JSON Collection::retrieve_page(unsigned page,unsigned page_size) {
  return collection[collid].retrieve_page(page,page_size);
}

bool Collection::update(int docid, const JSON& document) {
  JSON tmp(document);
  return collection[collid].update(docid,move(tmp));
}

bool Collection::update(int docid, JSON&& document) {
  return collection[collid].update(docid,move(document));
}

bool Collection::update(const Updater& upd, int docid) {
  return collection[collid].update(upd,docid);
}

bool Collection::destroy(int docid) {
  return collection[collid].destroy(docid);
}

void init(bool backup) {
  if (backup) ::backup = 0;
  system("mkdir -p database");
  pthread_create(&db,nullptr,thread,nullptr);
}

void close() {
  quit = true;
  pthread_join(db,nullptr);
  update();
}

} // namespace Database
<commit_msg>Flushing disk after database write<commit_after>#include <map>

#include <unistd.h>

#include "database.hpp"

#include "cmap.hpp"

#define MAX_COLLECTIONS 100
#define WRITE_INTERVAL  30

using namespace std;

static int backup = -1;

struct Coll {
  pthread_mutex_t mutex;
  string name;
  cmap<int,JSON> documents;
  Coll() : mutex(PTHREAD_MUTEX_INITIALIZER) {}
  void read() { // this function is called only once, by a locked piece of code
    JSON tmp;
    if (!tmp.read_file("database/"+name+".json")) return;
    for (auto& doc : tmp.arr()) documents[doc("_id")] = doc("document");
  }
  void write() {
    JSON tmp(vector<JSON>{});
    pthread_mutex_lock(&mutex);
    for (auto& kv : documents) tmp.emplace_back(move(map<string,JSON>{
      {"_id"      , kv.first},
      {"document" , kv.second}
    }));
    pthread_mutex_unlock(&mutex);
    tmp.write_file("database/"+name+".json");
  }
  int create(JSON&& doc) {
    int id = 1;
    pthread_mutex_lock(&mutex);
    if (documents.size() > 0) id += documents.max_key();
    documents[id] = move(doc);
    pthread_mutex_unlock(&mutex);
    return id;
  }
  bool retrieve(int id, JSON& doc) {
    pthread_mutex_lock(&mutex);
    auto it = documents.find(id);
    if (it == documents.end()) {
      doc.setnull();
      pthread_mutex_unlock(&mutex);
      return false;
    }
    doc = it->second;
    pthread_mutex_unlock(&mutex);
    return true;
  }
  JSON retrieve(const JSON& filter) {
    JSON ans(vector<JSON>{});
    pthread_mutex_lock(&mutex);
    for (auto& kv : documents) if (filter.issubobj(kv.second)) {
      JSON tmp = kv.second;
      tmp["id"] = kv.first;
      ans.push_back(move(tmp));
    }
    pthread_mutex_unlock(&mutex);
    return ans;
  }
  JSON retrieve_page(unsigned p, unsigned ps) {
    JSON ans(vector<JSON>{});
    pthread_mutex_lock(&mutex);
    if (!ps) p = 0, ps = documents.size();
    auto it = documents.at(p*ps);
    for (int i = 0; i < ps && it != documents.end(); i++, it++) {
      JSON tmp = it->second;
      tmp["id"] = it->first;
      ans.push_back(move(tmp));
    }
    pthread_mutex_unlock(&mutex);
    return ans;
  }
  bool update(int id, JSON&& doc) {
    pthread_mutex_lock(&mutex);
    auto it = documents.find(id);
    if (it == documents.end()) {
      pthread_mutex_unlock(&mutex);
      return false;
    }
    it->second = move(doc);
    pthread_mutex_unlock(&mutex);
    return true;
  }
  bool update(const Database::Updater& upd, int id) {
    bool ans = false;
    pthread_mutex_lock(&mutex);
    auto it = documents.find(id);
    if (it != documents.end()) {
      ans = upd(*it);
      pthread_mutex_unlock(&mutex);
      return ans;
    }
    for (auto& kv : documents) ans = ans || upd(kv);
    pthread_mutex_unlock(&mutex);
    return ans;
  }
  bool destroy(int id) {
    pthread_mutex_lock(&mutex);
    auto it = documents.find(id);
    if (it == documents.end()) {
      pthread_mutex_unlock(&mutex);
      return false;
    }
    documents.erase(it);
    pthread_mutex_unlock(&mutex);
    return true;
  }
};
static Coll collection[MAX_COLLECTIONS];
static int ncolls = 0;
static pthread_mutex_t colls_mutex = PTHREAD_MUTEX_INITIALIZER;
static int get(const string& name) {
  static map<string,int> colls;
  int i;
  pthread_mutex_lock(&colls_mutex);
  auto it = colls.find(name);
  if (it != colls.end()) {
    i = it->second;
    pthread_mutex_unlock(&colls_mutex);
    return i;
  }
  i = ncolls++;
  colls[name] = i;
  collection[i].name = name;
  collection[i].read();
  pthread_mutex_unlock(&colls_mutex);
  return i;
}

// thread
static bool quit = false;
static pthread_t db;
static void do_backup() {
  if (backup < 0) return;
  stringstream ss;
  ss << "backup" << backup;
  system(("mkdir -p database/"+ss.str()).c_str());
  system(("cp database/*.json database/"+ss.str()).c_str());
  backup = 1-backup;
}
static void update() {
  int nc;
  pthread_mutex_lock(&colls_mutex);
  nc = ncolls;
  pthread_mutex_unlock(&colls_mutex);
  for (int i = 0; i < nc; i++) collection[i].write();
  do_backup();
  sync();
}
static void* thread(void*) {
  static time_t upd = 0;
  while (!quit) {
    if (upd <= time(nullptr)) {
      update();
      upd = time(nullptr)+WRITE_INTERVAL;
    }
    usleep(100000);
  }
}

namespace Database {

Collection::Collection(const string& name) : collid(get(name)) {

}

int Collection::create(const JSON& document) {
  JSON tmp(document);
  return collection[collid].create(move(tmp));
}

int Collection::create(JSON&& document) {
  return collection[collid].create(move(document));
}

JSON Collection::retrieve(int docid) {
  JSON ans;
  collection[collid].retrieve(docid,ans);
  return ans;
}

bool Collection::retrieve(int docid, JSON& document) {
  return collection[collid].retrieve(docid,document);
}

JSON Collection::retrieve(const JSON& filter) {
  return collection[collid].retrieve(filter);
}

JSON Collection::retrieve_page(unsigned page,unsigned page_size) {
  return collection[collid].retrieve_page(page,page_size);
}

bool Collection::update(int docid, const JSON& document) {
  JSON tmp(document);
  return collection[collid].update(docid,move(tmp));
}

bool Collection::update(int docid, JSON&& document) {
  return collection[collid].update(docid,move(document));
}

bool Collection::update(const Updater& upd, int docid) {
  return collection[collid].update(upd,docid);
}

bool Collection::destroy(int docid) {
  return collection[collid].destroy(docid);
}

void init(bool backup) {
  if (backup) ::backup = 0;
  system("mkdir -p database");
  pthread_create(&db,nullptr,thread,nullptr);
}

void close() {
  quit = true;
  pthread_join(db,nullptr);
  update();
}

} // namespace Database
<|endoftext|>