<commit_before>/***********************************
   Copyright 2018 Ravishankar Mathur

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

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

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

/** \file CurveArtist.cpp
 * Definitions for the CurveArtist class.
 */

#include <OpenFrames/CurveArtist.hpp>
#include <OpenFrames/DoubleSingleUtils.hpp>
#include <osg/Geometry>
#include <climits>

namespace OpenFrames
{

/** Updates a CurveArtist's internal geometry when its target Trajectory changes. */
class CurveArtistUpdateCallback : public osg::Callback
{
public:
  CurveArtistUpdateCallback()
    : _numPoints(0),
    _dataAdded(true),
    _dataCleared(true)
  {}

  void dataAdded() { _dataAdded = true; }
  void dataCleared() { _dataCleared = true; }

  virtual bool run(osg::Object* object, osg::Object* data)
  {
    // Get the arrays that hold vertex data
    // Note that all object types are known, since this callback is only added to a CurveArtist
    // This is why we don't have to use dynamic_cast
    _ca = static_cast<CurveArtist*>(object);
    _geom = _ca->getDrawable(0)->asGeometry();
    _vertexHigh = static_cast<osg::Vec3Array*>(_geom->getVertexArray());
    _vertexLow = static_cast<osg::Vec3Array*>(_geom->getVertexAttribArray(TrajectoryArtist::OF_VERTEXLOW));
    _drawArrays = static_cast<osg::DrawArrays*>(_geom->getPrimitiveSet(0));

    // Clear data if it is invalid or all points are zero
    if (!_ca->isDataValid() || _ca->isDataZero())
    {
      // Clear all points
      clearVertexData();
      dirtyVertexData(0);
    }

    // Otherwise process trajectory points and update vertex data as needed
    else
    {
      // Clear all points if needed
      if (_dataCleared)
      {
        clearVertexData();
        _numPoints = 0;
        _dataCleared = false;
        _dataAdded = true; // Ensure arrays are marked as dirty
      }

      // Process new data if needed
      if (_dataAdded)
      {
        // Get and lock trajectory so its data doesn't move while we're analyzing it
        _dataAdded = false;
        _traj = _ca->getTrajectory();
        _traj->lockData();

        // Process trajectory points
        unsigned int newNumPoints = _traj->getNumPoints(_ca->getDataSource());
        processPoints(newNumPoints);

        // Unlock trajectory
        _traj->unlockData();

        // Mark data as changed
        dirtyVertexData(newNumPoints);
      }
    }

    // Continue traversing as needed
    return traverse(object, data);
  }

private:
  void clearVertexData()
  {
    _vertexHigh->clear();
    _vertexLow->clear();
  }

  void dirtyVertexData(unsigned int newNumPoints)
  {
    _numPoints = newNumPoints;

    // Dirty vertex arrays to indicate they've changed
    _vertexHigh->dirty();
    _vertexLow->dirty();

    // Update number of points to draw
    if (_numPoints == 0) _geom->setNodeMask(0);
    else
    {
      _geom->setNodeMask(~0);
      _drawArrays->setCount(_numPoints);
      _drawArrays->dirty();
      _geom->dirtyBound();
    }
  }

  void processPoints(unsigned int newNumPoints)
  {
    // Process each new point
    osg::Vec3d newPoint;
    osg::Vec3f high, low;
    for (unsigned int i = _numPoints; i < newNumPoints; ++i)
    {
      _traj->getPoint(i, _ca->getDataSource(), newPoint._v); // Get current point

      // Split point into high and low portions to support GPU-based RTE rendering
      OpenFrames::DS_Split(newPoint, high, low);
      _vertexHigh->push_back(high);
      _vertexLow->push_back(low);
    }
  }

  unsigned int _numPoints;
  bool _dataAdded, _dataCleared;

  osg::Geometry* _geom;
  osg::Vec3Array* _vertexHigh;
  osg::Vec3Array* _vertexLow;
  osg::DrawArrays* _drawArrays;
  const Trajectory* _traj;
  CurveArtist* _ca;
};

CurveArtist::CurveArtist(const Trajectory *traj)
: _dataValid(false), _dataZero(false)
{
	setTrajectory(traj); // Set the specified trajectory

	unsigned int dof = 0;
	if(_traj.valid()) dof = _traj->getDOF();

        // By default, the CurveArtist will draw a line between
        // successive points in the trajectory.
	Trajectory::DataSource data;
        data._src = Trajectory::POSOPT;

	if(dof >= 1)
	{
	  data._element = 0;
          setXData(data);
	}

	if(dof >= 2)
	{
	  data._element = 1;
          setYData(data);
	}

	if(dof >= 3)
	{
	  data._element = 2;
          setZData(data);
	}

  // Initialize line properties
	_lineWidth = new osg::LineWidth;
	_linePattern = new osg::LineStipple;
  osg::StateSet* stateset = getOrCreateStateSet();
	stateset->setAttribute(_lineWidth.get());
	stateset->setAttributeAndModes(_linePattern.get());

  // Initialize colors
  // Currently we use one color for the whole trajectory, but this can be
  // changed later for per-vertex colors
  _lineColors = new osg::Vec4Array(1);
  (*_lineColors)[0] = osg::Vec4(1.0, 1.0, 1.0, 1.0);
  _lineColors->setBinding(osg::Array::BIND_OVERALL);

  // Initialize geometry that will draw line strips
  osg::Geometry *geom = new osg::Geometry;
  geom->setDataVariance(osg::Object::DYNAMIC);
  geom->setUseDisplayList(false);
  geom->setUseVertexBufferObjects(true);
  geom->setVertexArray(new osg::Vec3Array());
  geom->setVertexAttribArray(OF_VERTEXLOW, new osg::Vec3Array(), osg::Array::BIND_PER_VERTEX);
  geom->setColorArray(_lineColors);
  geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINE_STRIP, 0, 0));
  addDrawable(geom);

  // Add callback that updates our geometry when the Trajectory changes
  addUpdateCallback(new CurveArtistUpdateCallback());
}

// Not using the copy constructor
CurveArtist::CurveArtist( const CurveArtist &ca, const osg::CopyOp& copyop )
{}

CurveArtist::~CurveArtist()
{}

void CurveArtist::setTrajectory(const Trajectory *traj)
{
	// Do nothing if this artist is already drawing the given Trajectory
	if(_traj == traj) return;

	// Handle default behavior
	TrajectoryArtist::setTrajectory(traj);

	verifyData();
}

bool CurveArtist::setXData(const Trajectory::DataSource &src)
{
	if(_dataSource[0] == src) return _dataValid;

	_dataSource[0] = src; // Set new source
	verifyData();

	return _dataValid;
}

bool CurveArtist::setYData(const Trajectory::DataSource &src)
{
	if(_dataSource[1] == src) return _dataValid;

	_dataSource[1] = src; // Set new source
	verifyData();

	return _dataValid;
}

bool CurveArtist::setZData(const Trajectory::DataSource &src)
{
	if(_dataSource[2] == src) return _dataValid;

	_dataSource[2] = src; // Set new source
	verifyData();

	return _dataValid;
}

void CurveArtist::setColor( float r, float g, float b)
{
  (*_lineColors)[0].set(r, g, b, 1.0);
  _lineColors->dirty();
}

void CurveArtist::setWidth( float width )
{
	if(width > 0.0)
	{
	  _lineWidth->setWidth(width);
	}
}

void CurveArtist::setPattern( GLint factor, GLushort pattern )
{
	_linePattern->setFactor(factor);
	_linePattern->setPattern(pattern);
}

void CurveArtist::dataCleared(const Trajectory* traj)
{
	verifyData();
  CurveArtistUpdateCallback *cb = static_cast<CurveArtistUpdateCallback*>(getUpdateCallback());
  cb->dataCleared();
}

void CurveArtist::dataAdded(const Trajectory* traj)
{
  CurveArtistUpdateCallback *cb = static_cast<CurveArtistUpdateCallback*>(getUpdateCallback());
  cb->dataAdded();
}

void CurveArtist::verifyData() const
{
	if(_dataSource[0]._src == Trajectory::ZERO &&
	   _dataSource[1]._src == Trajectory::ZERO &&
	   _dataSource[2]._src == Trajectory::ZERO)
	{
	  _dataValid = true;
	  _dataZero = true;
	}
	else if(_traj.valid())
	{
	  _dataValid = _traj->verifyData(_dataSource);
	  _dataZero = false;
	}
	else
	{
	  _dataValid = false;
	  _dataZero = false;
	}
}

}
<commit_msg>Improved CurveArtist performance by adding points in batches CurveArtist updates at fixed rate to reduce overhead in high-FPS applications (e.g. VR)<commit_after>/***********************************
   Copyright 2018 Ravishankar Mathur

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

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

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

/** \file CurveArtist.cpp
 * Definitions for the CurveArtist class.
 */

#include <OpenFrames/CurveArtist.hpp>
#include <OpenFrames/DoubleSingleUtils.hpp>
#include <osg/Geometry>
#include <climits>

namespace OpenFrames
{

/** Updates a CurveArtist's internal geometry when its target Trajectory changes. */
class CurveArtistUpdateCallback : public osg::Callback
{
public:
  CurveArtistUpdateCallback()
    : _dataAdded(true), _dataCleared(true), _batchSize(1000), _lastUpdateTime(0.0), _lastRunTime(0.0)
  {}

  void dataAdded() { _dataAdded = true; }
  void dataCleared() { _dataCleared = true; }

  virtual bool run(osg::Object* object, osg::Object* data)
  {
    // Get the current time
    osg::NodeVisitor* nv = data->asNodeVisitor();
    double currTime = nv->getFrameStamp()->getReferenceTime();

    // Process points at fixed rate to avoid performance bottlenecks in high-FPS applications (e.g. VR)
    if (currTime - _lastRunTime < 0.05) return traverse(object, data);
    else _lastRunTime = currTime;

    // Get the arrays that hold vertex data
    // Note that all object types are known, since this callback is only added to a CurveArtist
    // This is why we don't have to use dynamic_cast
    _ca = static_cast<CurveArtist*>(object);
    _geom = _ca->getDrawable(0)->asGeometry();
    _vertexHigh = static_cast<osg::Vec3Array*>(_geom->getVertexArray());
    _vertexLow = static_cast<osg::Vec3Array*>(_geom->getVertexAttribArray(TrajectoryArtist::OF_VERTEXLOW));
    _drawArrays = static_cast<osg::DrawArrays*>(_geom->getPrimitiveSet(0));

    // Clear data if it is invalid or all points are zero
    if (!_ca->isDataValid() || _ca->isDataZero())
    {
      // Clear all points
      clearVertexData();
      dirtyVertexData();
    }

    // Otherwise process trajectory points and update vertex data as needed
    else if (_dataCleared || _dataAdded)
    {
      // Clear all points if needed
      if (_dataCleared)
      {
        clearVertexData();
        _dataCleared = false;
        _dataAdded = true; // Ensure arrays are marked as dirty
      }

      // Process new data if needed
      if (_dataAdded)
      {
        // Get and lock trajectory so its data doesn't move while we're analyzing it
        _dataAdded = false;
        _lastUpdateTime = currTime;
        _traj = _ca->getTrajectory();
        _traj->lockData();

        // Process trajectory points
        unsigned int newNumPoints = _traj->getNumPoints(_ca->getDataSource());
        processPoints(newNumPoints);

        // Unlock trajectory
        _traj->unlockData();

        // Mark data as changed
        dirtyVertexData();
      }
    }

    // If vertex arrays have not been modified in a while, then shink them
    // to fit the current number of points
    else if ((currTime - _lastUpdateTime >= 1.0) && (_vertexHigh->size() > _drawArrays->getCount()))
    {
      _vertexHigh->resize(_drawArrays->getCount());
      _vertexLow->resize(_drawArrays->getCount());
      _vertexHigh->asVector().shrink_to_fit();
      _vertexLow->asVector().shrink_to_fit();
      _vertexHigh->dirty();
      _vertexLow->dirty();
    }

    // Continue traversing as needed
    return traverse(object, data);
  }

private:
  void clearVertexData()
  {
    _vertexHigh->clear();
    _vertexLow->clear();
    _drawArrays->setCount(0);
  }

  void dirtyVertexData()
  {
    // Dirty arrays to indicate they've changed
    _vertexHigh->dirty();
    _vertexLow->dirty();
    _drawArrays->dirty();
    _geom->dirtyBound();
  }

  void processPoints(unsigned int newNumPoints)
  {
    // Make space for new points
    if (newNumPoints > _vertexHigh->size())
    {
      unsigned int newSize = std::ceil((double)newNumPoints / (double)_batchSize);
      newSize *= _batchSize;
      _vertexHigh->resize(newSize);
      _vertexLow->resize(newSize);
    }

    // Process each new point
    osg::Vec3d newPoint;
    osg::Vec3f high, low;
    for (unsigned int i = _drawArrays->getCount(); i < newNumPoints; ++i)
    {
      _traj->getPoint(i, _ca->getDataSource(), newPoint._v); // Get current point

      // Split point into high and low portions to support GPU-based RTE rendering
      OpenFrames::DS_Split(newPoint, high, low);
      (*_vertexHigh)[i] = high;
      (*_vertexLow)[i] = low;
    }
    _drawArrays->setCount(newNumPoints);
  }

  bool _dataAdded, _dataCleared;
  unsigned int _batchSize;
  double _lastUpdateTime, _lastRunTime;

  osg::Geometry* _geom;
  osg::Vec3Array* _vertexHigh;
  osg::Vec3Array* _vertexLow;
  osg::DrawArrays* _drawArrays;
  const Trajectory* _traj;
  CurveArtist* _ca;
};

CurveArtist::CurveArtist(const Trajectory *traj)
: _dataValid(false), _dataZero(false)
{
	setTrajectory(traj); // Set the specified trajectory

	unsigned int dof = 0;
	if(_traj.valid()) dof = _traj->getDOF();

        // By default, the CurveArtist will draw a line between
        // successive points in the trajectory.
	Trajectory::DataSource data;
        data._src = Trajectory::POSOPT;

	if(dof >= 1)
	{
	  data._element = 0;
          setXData(data);
	}

	if(dof >= 2)
	{
	  data._element = 1;
          setYData(data);
	}

	if(dof >= 3)
	{
	  data._element = 2;
          setZData(data);
	}

  // Initialize line properties
	_lineWidth = new osg::LineWidth;
	_linePattern = new osg::LineStipple;
  osg::StateSet* stateset = getOrCreateStateSet();
	stateset->setAttribute(_lineWidth.get());
	stateset->setAttributeAndModes(_linePattern.get());

  // Initialize colors
  // Currently we use one color for the whole trajectory, but this can be
  // changed later for per-vertex colors
  _lineColors = new osg::Vec4Array(1);
  (*_lineColors)[0] = osg::Vec4(1.0, 1.0, 1.0, 1.0);
  _lineColors->setBinding(osg::Array::BIND_OVERALL);

  // Initialize geometry that will draw line strips
  osg::Geometry *geom = new osg::Geometry;
  geom->setDataVariance(osg::Object::DYNAMIC);
  geom->setUseDisplayList(false);
  geom->setUseVertexBufferObjects(true);
  geom->setVertexArray(new osg::Vec3Array());
  geom->setVertexAttribArray(OF_VERTEXLOW, new osg::Vec3Array(), osg::Array::BIND_PER_VERTEX);
  geom->setColorArray(_lineColors);
  geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINE_STRIP, 0, 0));
  geom->getOrCreateVertexBufferObject()->setUsage(GL_DYNAMIC_DRAW);
  addDrawable(geom);

  // Add callback that updates our geometry when the Trajectory changes
  addUpdateCallback(new CurveArtistUpdateCallback());
}

// Not using the copy constructor
CurveArtist::CurveArtist( const CurveArtist &ca, const osg::CopyOp& copyop )
{}

CurveArtist::~CurveArtist()
{}

void CurveArtist::setTrajectory(const Trajectory *traj)
{
	// Do nothing if this artist is already drawing the given Trajectory
	if(_traj == traj) return;

	// Handle default behavior
	TrajectoryArtist::setTrajectory(traj);

	verifyData();
}

bool CurveArtist::setXData(const Trajectory::DataSource &src)
{
	if(_dataSource[0] == src) return _dataValid;

	_dataSource[0] = src; // Set new source
	verifyData();

	return _dataValid;
}

bool CurveArtist::setYData(const Trajectory::DataSource &src)
{
	if(_dataSource[1] == src) return _dataValid;

	_dataSource[1] = src; // Set new source
	verifyData();

	return _dataValid;
}

bool CurveArtist::setZData(const Trajectory::DataSource &src)
{
	if(_dataSource[2] == src) return _dataValid;

	_dataSource[2] = src; // Set new source
	verifyData();

	return _dataValid;
}

void CurveArtist::setColor( float r, float g, float b)
{
  (*_lineColors)[0].set(r, g, b, 1.0);
  _lineColors->dirty();
}

void CurveArtist::setWidth( float width )
{
	if(width > 0.0)
	{
	  _lineWidth->setWidth(width);
	}
}

void CurveArtist::setPattern( GLint factor, GLushort pattern )
{
	_linePattern->setFactor(factor);
	_linePattern->setPattern(pattern);
}

void CurveArtist::dataCleared(const Trajectory* traj)
{
	verifyData();
  CurveArtistUpdateCallback *cb = static_cast<CurveArtistUpdateCallback*>(getUpdateCallback());
  cb->dataCleared();
}

void CurveArtist::dataAdded(const Trajectory* traj)
{
  CurveArtistUpdateCallback *cb = static_cast<CurveArtistUpdateCallback*>(getUpdateCallback());
  cb->dataAdded();
}

void CurveArtist::verifyData() const
{
	if(_dataSource[0]._src == Trajectory::ZERO &&
	   _dataSource[1]._src == Trajectory::ZERO &&
	   _dataSource[2]._src == Trajectory::ZERO)
	{
	  _dataValid = true;
	  _dataZero = true;
	}
	else if(_traj.valid())
	{
	  _dataValid = _traj->verifyData(_dataSource);
	  _dataZero = false;
	}
	else
	{
	  _dataValid = false;
	  _dataZero = false;
	}
}

}
<|endoftext|>


<commit_before>/* Copyright (C) 2012-2017 IBM Corp.
 * This program is Licensed under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *   http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. See accompanying LICENSE file.
 */
/**
 * @file fft.cpp - computing the canonical embedding and related norms
 **/
#include <complex>
#include <cmath>
#include <numeric> // std::accumulate
#include <NTL/BasicThreadPool.h>
#include "NumbTh.h"
#include "timing.h"
#include "norms.h"
#include "PAlgebra.h"
NTL_CLIENT

constexpr double pi = 4 * std::atan(1);

#ifdef FFT_NATIVE
#warning "canonicalEmbedding implemented via slow DFT, expect very slow key-generation"
// An extremely lame implementation of the canonical embedding

// evaluate poly(x) using Horner's rule
cx_double complexEvalPoly(const zzX& poly, const cx_double& x)
{
  if (lsize(poly)<=0) return cx_double(0.0,0.0);
  cx_double res(double(poly[0]), 0.0);
  for (long i=1; i<lsize(poly); i++) {
    res *= x;
    res += cx_double(double(poly[i]));
  }
  return res;
}

void canonicalEmbedding(std::vector<cx_double>& v, const zzX& f, const PAlgebra& palg)
{
  FHE_TIMER_START;
  long m = palg.getM();
  long phimBy2 = divc(palg.getPhiM(),2);
  vector<long> zmstar(phimBy2); // the first half of Zm*
  for (long i=1, idx=0; i<=m/2; i++)
    if (palg.inZmStar(i)) zmstar[idx++] = i;

  v.resize(phimBy2);
  NTL_EXEC_RANGE(phimBy2, first, last)
  for (long i=first; i < last; ++i) {
    auto rou = std::polar<double>(1.0, -(2*pi*zmstar[i])/m); // root of unity
    v[i] = complexEvalPoly(f,rou);
  }
  NTL_EXEC_RANGE_END
  FHE_TIMER_STOP;
}
void embedInSlots(zzX& f, const std::vector<cx_double>& v,
                  const PAlgebra& palg, bool strictInverse)
{
  NTL::Error("embedInSlots not implemented\n");
}
#else // ifdef FFT_NATIVE
#ifdef FFT_ARMA
#warning "canonicalEmbedding implemented via Armadillo"

#include <armadillo>
void convert(zzX& to, const arma::vec& from)
{
  to.SetLength(from.size());
  NTL_EXEC_RANGE(to.length(), first, last)
  for (long i=first; i<last; i++)
    to[i] = std::round(from[i]);
  NTL_EXEC_RANGE_END
}
void convert(arma::vec& to, const zzX& from)
{
  to.resize(from.length());
  NTL_EXEC_RANGE(from.length(), first, last)
  for (long i=first; i<last; i++)
    to[i] = from[i];
  NTL_EXEC_RANGE_END
}

// Computing the canonical embedding. This function returns in v only
// the first half of the entries, the others are v[phi(m)-i]=conj(v[i])
void canonicalEmbedding(std::vector<cx_double>& v,
                        const zzX& f, const PAlgebra& palg)
{
  FHE_TIMER_START;
  long m = palg.getM();
  long phimBy2 = divc(palg.getPhiM(),2);
  arma::vec av; // convert to vector of doubles
  convert(av, f);
  arma::cx_vec avv = arma::fft(av,m); // compute the full FFT

  v.resize(phimBy2); // the first half of Zm*
  for (long i=1, idx=0; i<=m/2; i++)
    if (palg.inZmStar(i)) v[idx++] = avv[i];
  FHE_TIMER_STOP;
}

// Roughly the inverse of canonicalEmbedding, except for rounding issues.
// Calling embedInSlots(f,v,palg,strictInverse=true) after setting
// canonicalEmbedding(v, f, palg), is sure to recover the same f.
// Calling embedInSlots(f,v,palg,strictInverse=false) when m is
// not a power of two may fail to recover the same f, however.
// When m is a power of two, the strictInverse flag has no effect.
void embedInSlots(zzX& f, const std::vector<cx_double>& v,
                  const PAlgebra& palg, bool strictInverse)
{
  FHE_TIMER_START;
  long m = palg.getM();
  if (0==(m & 1)) strictInverse=true; // m even => power of two
  arma::cx_vec avv(m);
  for (long i=1, idx=0; i<=m/2; i++) {
    if (palg.inZmStar(i)) {
      avv[i] = v[idx++];
      avv[m-i] = std::conj(avv[i]);
    }
    else
      avv[m-i] = avv[i] = std::complex<double>(0.0,0.0);
  }
  arma::vec av = arma::real(arma::ifft(avv,m));

  // If v was obtained by canonicalEmbedding(v,f,palg) then we have
  // the guarantee that m*av is an integral polynomial, and moreover
  // m*av mod Phi_m(x) is in m*Z[X].
  if (strictInverse) av *= m; // scale up by m
  convert(f, av);    // round to an integer polynomial
  reduceModPhimX(f, palg);
  if (strictInverse) f /= m;  // scale down by m
  normalize(f);
}
#endif // ifdef FFT_ARMA
#endif // ifdef FFT_NATIVE
<commit_msg>make Armadillo the default FFT implementation<commit_after>/* Copyright (C) 2012-2017 IBM Corp.
 * This program is Licensed under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *   http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. See accompanying LICENSE file.
 */
/**
 * @file fft.cpp - computing the canonical embedding and related norms
 **/
#include <complex>
#include <cmath>
#include <numeric> // std::accumulate
#include <NTL/BasicThreadPool.h>
#include "NumbTh.h"
#include "timing.h"
#include "norms.h"
#include "PAlgebra.h"
NTL_CLIENT

constexpr double pi = 4 * std::atan(1);

#ifdef FFT_ARMA
#warning "canonicalEmbedding implemented via Armadillo"
#include <armadillo>
void convert(zzX& to, const arma::vec& from)
{
  to.SetLength(from.size());
  NTL_EXEC_RANGE(to.length(), first, last)
  for (long i=first; i<last; i++)
    to[i] = std::round(from[i]);
  NTL_EXEC_RANGE_END
}
void convert(arma::vec& to, const zzX& from)
{
  to.resize(from.length());
  NTL_EXEC_RANGE(from.length(), first, last)
  for (long i=first; i<last; i++)
    to[i] = from[i];
  NTL_EXEC_RANGE_END
}

// Computing the canonical embedding. This function returns in v only
// the first half of the entries, the others are v[phi(m)-i]=conj(v[i])
void canonicalEmbedding(std::vector<cx_double>& v,
                        const zzX& f, const PAlgebra& palg)
{
  FHE_TIMER_START;
  long m = palg.getM();
  long phimBy2 = divc(palg.getPhiM(),2);
  arma::vec av; // convert to vector of doubles
  convert(av, f);
  arma::cx_vec avv = arma::fft(av,m); // compute the full FFT

  v.resize(phimBy2); // the first half of Zm*
  for (long i=1, idx=0; i<=m/2; i++)
    if (palg.inZmStar(i)) v[idx++] = avv[i];
  FHE_TIMER_STOP;
}

// Roughly the inverse of canonicalEmbedding, except for rounding issues.
// Calling embedInSlots(f,v,palg,strictInverse=true) after setting
// canonicalEmbedding(v, f, palg), is sure to recover the same f.
// Calling embedInSlots(f,v,palg,strictInverse=false) when m is
// not a power of two may fail to recover the same f, however.
// When m is a power of two, the strictInverse flag has no effect.
void embedInSlots(zzX& f, const std::vector<cx_double>& v,
                  const PAlgebra& palg, bool strictInverse)
{
  FHE_TIMER_START;
  long m = palg.getM();
  if (0==(m & 1)) strictInverse=true; // m even => power of two
  arma::cx_vec avv(m);
  for (long i=1, idx=0; i<=m/2; i++) {
    if (palg.inZmStar(i)) {
      avv[i] = v[idx++];
      avv[m-i] = std::conj(avv[i]);
    }
    else
      avv[m-i] = avv[i] = std::complex<double>(0.0,0.0);
  }
  arma::vec av = arma::real(arma::ifft(avv,m));

  // If v was obtained by canonicalEmbedding(v,f,palg) then we have
  // the guarantee that m*av is an integral polynomial, and moreover
  // m*av mod Phi_m(x) is in m*Z[X].
  if (strictInverse) av *= m; // scale up by m
  convert(f, av);    // round to an integer polynomial
  reduceModPhimX(f, palg);
  if (strictInverse) f /= m;  // scale down by m
  normalize(f);
}
#else
#ifdef FFT_NATIVE
#warning "canonicalEmbedding implemented via slow DFT, expect very slow key-generation"
// An extremely lame implementation of the canonical embedding

// evaluate poly(x) using Horner's rule
cx_double complexEvalPoly(const zzX& poly, const cx_double& x)
{
  if (lsize(poly)<=0) return cx_double(0.0,0.0);
  cx_double res(double(poly[0]), 0.0);
  for (long i=1; i<lsize(poly); i++) {
    res *= x;
    res += cx_double(double(poly[i]));
  }
  return res;
}

void canonicalEmbedding(std::vector<cx_double>& v, const zzX& f, const PAlgebra& palg)
{
  FHE_TIMER_START;
  long m = palg.getM();
  long phimBy2 = divc(palg.getPhiM(),2);
  vector<long> zmstar(phimBy2); // the first half of Zm*
  for (long i=1, idx=0; i<=m/2; i++)
    if (palg.inZmStar(i)) zmstar[idx++] = i;

  v.resize(phimBy2);
  NTL_EXEC_RANGE(phimBy2, first, last)
  for (long i=first; i < last; ++i) {
    auto rou = std::polar<double>(1.0, -(2*pi*zmstar[i])/m); // root of unity
    v[i] = complexEvalPoly(f,rou);
  }
  NTL_EXEC_RANGE_END
  FHE_TIMER_STOP;
}
void embedInSlots(zzX& f, const std::vector<cx_double>& v,
                  const PAlgebra& palg, bool strictInverse)
{
  NTL::Error("embedInSlots not implemented\n");
}
#endif // ifdef FFT_NATIVE
#endif // ifdef FFT_ARMA
<|endoftext|>


<commit_before>#include "drake/solvers/mosek_solver.h"

#include <iostream>

#include <Eigen/Core>

#include "gtest/gtest.h"

#include "drake/util/testUtil.h"
#include "drake/solvers/mathematical_program.h"
#include "drake/solvers/optimization.h"
#include "drake/util/eigen_matrix_compare.h"
#include "drake/solvers/constraint.h"

using drake::util::MatrixCompareType;
namespace drake {
namespace solvers {
namespace {

GTEST_TEST(testMosek, MosekLinearProgram) {
  // Taken from http://docs.mosek.com/7.1/capi/Linear_optimization.html
  OptimizationProblem prog;
  auto x = prog.AddContinuousVariables(4);
  Eigen::Vector4d A;
  A << 3, 1, 5, 1;
  LinearConstraint obj(A, Eigen::Vector4d::Constant(0),
                       Eigen::Vector4d::Constant(0));
  std::shared_ptr<LinearConstraint> ptrtoobj =
      std::make_shared<LinearConstraint>(obj);

  prog.AddCost(ptrtoobj);
  Eigen::MatrixXd constraint1(1, 4);
  Eigen::MatrixXd constraint2(1, 4);
  constraint1 << 2, 1, 3, 1;
  constraint2 << 0, 2, 0, 3;
  Eigen::MatrixXd lineqconstraint(1, 4);
  lineqconstraint << 3, 1, 2, 0;
  Eigen::MatrixXd lb1(1, 1), ub1(1, 1);
  Eigen::MatrixXd lb2(1, 1), ub2(1, 1);
  lb1 << 15;
  ub1 << +std::numeric_limits<double>::infinity();
  lb2 << -std::numeric_limits<double>::infinity();
  ub2 << 25;
  Eigen::MatrixXd lineqbounds(1, 1);
  lineqbounds << 30;

  prog.AddLinearConstraint(constraint1, lb1, ub1);
  prog.AddLinearConstraint(constraint2, lb2, ub2);
  prog.AddLinearEqualityConstraint(lineqconstraint, lineqbounds);
  Eigen::Vector4d bboxlow, bboxhigh;
  bboxlow << 0, 0, 0, 0;
  bboxhigh << std::numeric_limits<double>::infinity(),
              10,
              std::numeric_limits<double>::infinity(),
              std::numeric_limits<double>::infinity();
  prog.AddBoundingBoxConstraint(bboxlow, bboxhigh);
  prog.SetSolverOption("Mosek", "maxormin", "max");
  prog.SetSolverOption("Mosek", "problemtype", "linear");
  SolutionResult result = SolutionResult::kUnknownError;
  MosekSolver msk;
  ASSERT_NO_THROW(result = msk.Solve(prog));
  EXPECT_EQ(result, SolutionResult::kSolutionFound);
  Eigen::Vector4d solutions;
  solutions << 0, 0, 15, 8.33333333333333333333;
  EXPECT_TRUE(CompareMatrices(solutions, x.value(), 1e-10,
                              MatrixCompareType::absolute));
}

GTEST_TEST(testMosek, MosekQuadraticCost) {
  // http://docs.mosek.com/7.1/capi/Quadratic_optimization.html
  OptimizationProblem prog2;
  auto x = prog2.AddContinuousVariables(3);
  // Build the objective matrix and send it to the program.
  Eigen::Matrix3d Q;
  Q << 2, 0, -1,
       0, 0.2, 0,
       -1, 0, 2;
  Eigen::Vector3d c;
  c << 0, -1, 0;
  prog2.AddCost(std::make_shared<QuadraticConstraint>(Q, c, 0, 0));
  // Build the constraint matrix and send it to the program.
  Eigen::Vector3d linearcon;
  linearcon << 1, 1, 1;
  std::shared_ptr<QuadraticConstraint> ptrtocon =
      std::make_shared<QuadraticConstraint>(
          Eigen::Matrix3d::Constant(0), linearcon, 1,
          std::numeric_limits<double>::infinity());
  prog2.AddConstraint(ptrtocon);
  // Create the bounding box.
  Eigen::Vector3d bboxlow, bboxhigh;
  bboxlow << 0, 0, 0;
  bboxhigh << std::numeric_limits<double>::infinity(),
              std::numeric_limits<double>::infinity(),
              std::numeric_limits<double>::infinity();
  prog2.AddBoundingBoxConstraint(bboxlow, bboxhigh);
  MosekSolver msk;
  SolutionResult result = SolutionResult::kUnknownError;
  prog2.SetSolverOption("Mosek", "maxormin", "min");
  prog2.SetSolverOption("Mosek", "problemtype", "quadratic");
  ASSERT_NO_THROW(result = msk.Solve(prog2));
  EXPECT_EQ(result, SolutionResult::kSolutionFound);
  Eigen::Vector3d solutions;
  solutions << 5.975006e-05, 5, 5.975006e-05;
  EXPECT_TRUE(CompareMatrices(solutions, x.value(), 1e-7,
                              MatrixCompareType::absolute));
}

GTEST_TEST(testMosek, MosekQuadraticConstraintAndCost) {
  // http://docs.mosek.com/7.1/capi/Quadratic_optimization.html
  OptimizationProblem prog2;
  auto x = prog2.AddContinuousVariables(3);
  // Build the objective matrix and send it to the program.
  Eigen::Matrix3d Q;
  Q << 2, 0, -1,
       0, 0.2, 0,
       -1, 0, 2;
  Eigen::Vector3d c;
  c << 0, -1, 0;
  prog2.AddCost(std::make_shared<QuadraticConstraint>(Q, c, 0, 0));
  // Create the constraint matrix, and send it to the program.
  Eigen::Vector3d linearcon;
  linearcon << 1, 1, 1;
  Eigen::Matrix3d quadcon;
  quadcon << -2, 0, 0.2,
              0, -2, 0,
              0.2, 0, -0.2;
  std::shared_ptr<QuadraticConstraint> ptrtocon =
      std::make_shared<QuadraticConstraint>(quadcon, linearcon, 1,
      std::numeric_limits<double>::infinity());
  prog2.AddConstraint(ptrtocon);
  // Create the bounding box.
  Eigen::Vector3d bboxlow, bboxhigh;
  bboxlow << 0, 0, 0;
  bboxhigh << std::numeric_limits<double>::infinity(),
              std::numeric_limits<double>::infinity(),
              std::numeric_limits<double>::infinity();
  prog2.AddBoundingBoxConstraint(bboxlow, bboxhigh);
  MosekSolver msk;
  SolutionResult result = SolutionResult::kUnknownError;
  prog2.SetSolverOption("Mosek", "maxormin", "min");
  prog2.SetSolverOption("Mosek", "problemtype", "quadratic");
  ASSERT_NO_THROW(result = msk.Solve(prog2));
  EXPECT_EQ(result, SolutionResult::kSolutionFound);
  Eigen::Vector3d solutions;
  solutions << 4.487849e-01, 9.319130e-01, 6.741081e-01;
  EXPECT_TRUE(CompareMatrices(solutions, x.value(), 1e-7,
                              MatrixCompareType::absolute));
}

GTEST_TEST(testMosek, MosekSemiDefiniteProgram) {
  // http://docs.mosek.com/7.1/capi/Semidefinite_optimization.html
  OptimizationProblem prog3;
  auto x = prog3.AddContinuousVariables(9);
  prog3.SetSolverOption("Mosek", "numbarvar", 6);
  // Build the objective matrix and send it to the program.
  Eigen::Matrix3d Q;
  Q << 2, 1, 0,
       1, 2, 1,
       0, 1, 2;
  Eigen::Vector3d c;
  c << 1, 0, 0;
  prog3.AddCost(std::make_shared<SemidefiniteConstraint>(Q, c, 0, 0));
  // Create the constraint matrix, and send it to the program.
  Eigen::Vector3d linearcon1;
  linearcon1 << 1, 0, 0;
  Eigen::Matrix3d sdpcon1;
  sdpcon1 << 1, 0, 0,
             0, 1, 0,
             0, 0, 1;
  auto ptrtocon1 = std::make_shared<SemidefiniteConstraint>(sdpcon1, linearcon1,
                                                         1, 1);
  prog3.AddConstraint(ptrtocon1);
  Eigen::Vector3d linearcon2;
  linearcon2 << 0, 1, 1;
  Eigen::Matrix3d sdpcon2;
  sdpcon2 << 1, 1, 1,
             1, 1, 1,
             1, 1, 1;
  auto ptrtocon2 = std::make_shared<SemidefiniteConstraint>(sdpcon2, linearcon2,
                                                         0.5, 0.5);
  prog3.AddConstraint(ptrtocon2);
  // Create the bounding box.
  Eigen::Vector3d bboxlow, bboxhigh;
  bboxlow << -std::numeric_limits<double>::infinity(),
             -std::numeric_limits<double>::infinity(),
             -std::numeric_limits<double>::infinity();
  bboxhigh << std::numeric_limits<double>::infinity(),
              std::numeric_limits<double>::infinity(),
              std::numeric_limits<double>::infinity();
  prog3.AddBoundingBoxConstraint(bboxlow, bboxhigh);
  MosekSolver msk;
  SolutionResult result = SolutionResult::kUnknownError;
  prog3.SetSolverOption("Mosek", "maxormin", "min");
  prog3.SetSolverOption("Mosek", "problemtype", "sdp");
  ASSERT_NO_THROW(result = msk.Solve(prog3));
  EXPECT_EQ(result, SolutionResult::kSolutionFound);
  Eigen::VectorXd solutions(9);
  solutions << 2.543589e-01, 1.798589e-01, 1.798589e-01, 2.172859e-01,
               -2.599827e-01, 2.172859e-01, 3.110694e-01, -2.599827e-01,
               2.172859e-01;
  EXPECT_TRUE(CompareMatrices(solutions, x.value(), 1e-7,
                              MatrixCompareType::absolute));
}

}  // Anonymous namespace
}  // namespace solvers
}  // namespace drake
<commit_msg>Changed the bounding box, as well as the values to check against. (Note that these values were found by modifying the semidefinite mosek example)<commit_after>#include "drake/solvers/mosek_solver.h"

#include <iostream>

#include <Eigen/Core>

#include "gtest/gtest.h"

#include "drake/util/testUtil.h"
#include "drake/solvers/mathematical_program.h"
#include "drake/solvers/optimization.h"
#include "drake/util/eigen_matrix_compare.h"
#include "drake/solvers/constraint.h"

using drake::util::MatrixCompareType;
namespace drake {
namespace solvers {
namespace {

GTEST_TEST(testMosek, MosekLinearProgram) {
  // Taken from http://docs.mosek.com/7.1/capi/Linear_optimization.html
  OptimizationProblem prog;
  auto x = prog.AddContinuousVariables(4);
  Eigen::Vector4d A;
  A << 3, 1, 5, 1;
  LinearConstraint obj(A, Eigen::Vector4d::Constant(0),
                       Eigen::Vector4d::Constant(0));
  std::shared_ptr<LinearConstraint> ptrtoobj =
      std::make_shared<LinearConstraint>(obj);

  prog.AddCost(ptrtoobj);
  Eigen::MatrixXd constraint1(1, 4);
  Eigen::MatrixXd constraint2(1, 4);
  constraint1 << 2, 1, 3, 1;
  constraint2 << 0, 2, 0, 3;
  Eigen::MatrixXd lineqconstraint(1, 4);
  lineqconstraint << 3, 1, 2, 0;
  Eigen::MatrixXd lb1(1, 1), ub1(1, 1);
  Eigen::MatrixXd lb2(1, 1), ub2(1, 1);
  lb1 << 15;
  ub1 << +std::numeric_limits<double>::infinity();
  lb2 << -std::numeric_limits<double>::infinity();
  ub2 << 25;
  Eigen::MatrixXd lineqbounds(1, 1);
  lineqbounds << 30;

  prog.AddLinearConstraint(constraint1, lb1, ub1);
  prog.AddLinearConstraint(constraint2, lb2, ub2);
  prog.AddLinearEqualityConstraint(lineqconstraint, lineqbounds);
  Eigen::Vector4d bboxlow, bboxhigh;
  bboxlow << 0, 0, 0, 0;
  bboxhigh << std::numeric_limits<double>::infinity(),
              10,
              std::numeric_limits<double>::infinity(),
              std::numeric_limits<double>::infinity();
  prog.AddBoundingBoxConstraint(bboxlow, bboxhigh);
  prog.SetSolverOption("Mosek", "maxormin", "max");
  prog.SetSolverOption("Mosek", "problemtype", "linear");
  SolutionResult result = SolutionResult::kUnknownError;
  MosekSolver msk;
  ASSERT_NO_THROW(result = msk.Solve(prog));
  EXPECT_EQ(result, SolutionResult::kSolutionFound);
  Eigen::Vector4d solutions;
  solutions << 0, 0, 15, 8.33333333333333333333;
  EXPECT_TRUE(CompareMatrices(solutions, x.value(), 1e-10,
                              MatrixCompareType::absolute));
}

GTEST_TEST(testMosek, MosekQuadraticCost) {
  // http://docs.mosek.com/7.1/capi/Quadratic_optimization.html
  OptimizationProblem prog2;
  auto x = prog2.AddContinuousVariables(3);
  // Build the objective matrix and send it to the program.
  Eigen::Matrix3d Q;
  Q << 2, 0, -1,
       0, 0.2, 0,
       -1, 0, 2;
  Eigen::Vector3d c;
  c << 0, -1, 0;
  prog2.AddCost(std::make_shared<QuadraticConstraint>(Q, c, 0, 0));
  // Build the constraint matrix and send it to the program.
  Eigen::Vector3d linearcon;
  linearcon << 1, 1, 1;
  std::shared_ptr<QuadraticConstraint> ptrtocon =
      std::make_shared<QuadraticConstraint>(
          Eigen::Matrix3d::Constant(0), linearcon, 1,
          std::numeric_limits<double>::infinity());
  prog2.AddConstraint(ptrtocon);
  // Create the bounding box.
  Eigen::Vector3d bboxlow, bboxhigh;
  bboxlow << 0, 0, 0;
  bboxhigh << std::numeric_limits<double>::infinity(),
              std::numeric_limits<double>::infinity(),
              std::numeric_limits<double>::infinity();
  prog2.AddBoundingBoxConstraint(bboxlow, bboxhigh);
  MosekSolver msk;
  SolutionResult result = SolutionResult::kUnknownError;
  prog2.SetSolverOption("Mosek", "maxormin", "min");
  prog2.SetSolverOption("Mosek", "problemtype", "quadratic");
  ASSERT_NO_THROW(result = msk.Solve(prog2));
  EXPECT_EQ(result, SolutionResult::kSolutionFound);
  Eigen::Vector3d solutions;
  solutions << 5.975006e-05, 5, 5.975006e-05;
  EXPECT_TRUE(CompareMatrices(solutions, x.value(), 1e-7,
                              MatrixCompareType::absolute));
}

GTEST_TEST(testMosek, MosekQuadraticConstraintAndCost) {
  // http://docs.mosek.com/7.1/capi/Quadratic_optimization.html
  OptimizationProblem prog2;
  auto x = prog2.AddContinuousVariables(3);
  // Build the objective matrix and send it to the program.
  Eigen::Matrix3d Q;
  Q << 2, 0, -1,
       0, 0.2, 0,
       -1, 0, 2;
  Eigen::Vector3d c;
  c << 0, -1, 0;
  prog2.AddCost(std::make_shared<QuadraticConstraint>(Q, c, 0, 0));
  // Create the constraint matrix, and send it to the program.
  Eigen::Vector3d linearcon;
  linearcon << 1, 1, 1;
  Eigen::Matrix3d quadcon;
  quadcon << -2, 0, 0.2,
              0, -2, 0,
              0.2, 0, -0.2;
  std::shared_ptr<QuadraticConstraint> ptrtocon =
      std::make_shared<QuadraticConstraint>(quadcon, linearcon, 1,
      std::numeric_limits<double>::infinity());
  prog2.AddConstraint(ptrtocon);
  // Create the bounding box.
  Eigen::Vector3d bboxlow, bboxhigh;
  bboxlow << 0, 0, 0;
  bboxhigh << std::numeric_limits<double>::infinity(),
              std::numeric_limits<double>::infinity(),
              std::numeric_limits<double>::infinity();
  prog2.AddBoundingBoxConstraint(bboxlow, bboxhigh);
  MosekSolver msk;
  SolutionResult result = SolutionResult::kUnknownError;
  prog2.SetSolverOption("Mosek", "maxormin", "min");
  prog2.SetSolverOption("Mosek", "problemtype", "quadratic");
  ASSERT_NO_THROW(result = msk.Solve(prog2));
  EXPECT_EQ(result, SolutionResult::kSolutionFound);
  Eigen::Vector3d solutions;
  solutions << 4.487849e-01, 9.319130e-01, 6.741081e-01;
  EXPECT_TRUE(CompareMatrices(solutions, x.value(), 1e-7,
                              MatrixCompareType::absolute));
}

GTEST_TEST(testMosek, MosekSemiDefiniteProgram) {
  // http://docs.mosek.com/7.1/capi/Semidefinite_optimization.html
  OptimizationProblem prog3;
  auto x = prog3.AddContinuousVariables(9);
  prog3.SetSolverOption("Mosek", "numbarvar", 6);
  // Build the objective matrix and send it to the program.
  Eigen::Matrix3d Q;
  Q << 2, 1, 0,
       1, 2, 1,
       0, 1, 2;
  Eigen::Vector3d c;
  c << 1, 0, 0;
  prog3.AddCost(std::make_shared<SemidefiniteConstraint>(Q, c, 0, 0));
  // Create the constraint matrix, and send it to the program.
  Eigen::Vector3d linearcon1;
  linearcon1 << 1, 0, 0;
  Eigen::Matrix3d sdpcon1;
  sdpcon1 << 1, 0, 0,
             0, 1, 0,
             0, 0, 1;
  auto ptrtocon1 = std::make_shared<SemidefiniteConstraint>(sdpcon1, linearcon1,
                                                         1, 1);
  prog3.AddConstraint(ptrtocon1);
  Eigen::Vector3d linearcon2;
  linearcon2 << 0, 1, 1;
  Eigen::Matrix3d sdpcon2;
  sdpcon2 << 1, 1, 1,
             1, 1, 1,
             1, 1, 1;
  auto ptrtocon2 = std::make_shared<SemidefiniteConstraint>(sdpcon2, linearcon2,
                                                         0.5, 0.5);
  prog3.AddConstraint(ptrtocon2);
  // Create the bounding box.
  Eigen::Vector3d bboxlow, bboxhigh;
  bboxlow << -100,
             -100,
             -std::numeric_limits<double>::infinity();
  bboxhigh << 100,
              std::numeric_limits<double>::infinity(),
              std::numeric_limits<double>::infinity();
  prog3.AddBoundingBoxConstraint(bboxlow, bboxhigh);
  MosekSolver msk;
  SolutionResult result = SolutionResult::kUnknownError;
  prog3.SetSolverOption("Mosek", "maxormin", "min");
  prog3.SetSolverOption("Mosek", "problemtype", "sdp");
  ASSERT_NO_THROW(result = msk.Solve(prog3));
  EXPECT_EQ(result, SolutionResult::kSolutionFound);
  Eigen::VectorXd solutions(9);
  solutions << 2.544931e-01, 1.799843e-01, 1.799233e-01, 2.171910e-01,
               -2.599491e-01, 2.171910e-01, 3.111250e-01, -2.599491e-01,
               2.171910e-01;
  EXPECT_TRUE(CompareMatrices(solutions, x.value(), 1e-7,
                              MatrixCompareType::absolute));
}

}  // Anonymous namespace
}  // namespace solvers
}  // namespace drake
<|endoftext|>


<commit_before>/*
 * Copyright 2014 The Imaging Source Europe GmbH
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <vector>
#include <string>
#include <memory>
#include <algorithm>
#include <cstring>
#include <unistd.h>

#include "utils.h"
#include "logging.h"
#include "DeviceIndex.h"

#include "BackendLoader.h"

using namespace tcam;

DeviceIndex::DeviceIndex ()
    : continue_thread(false),
      wait_period(2),
      have_list(false),
      device_list(std::vector<DeviceInfo>()),
      callbacks(std::vector<callback_data>())
{
    continue_thread = true;
    work_thread = std::thread (&DeviceIndex::run, this);

    work_thread.detach();
}

DeviceIndex::~DeviceIndex ()
{
    continue_thread = false;
    if (work_thread.joinable())
    {
        work_thread.join();
    }
}

void DeviceIndex::register_device_lost (dev_callback c, void* user_data)
{
    tcam_log(TCAM_LOG_DEBUG, "Registered device lost callback");

    std::lock_guard<std::mutex> lock(mtx);
    callbacks.push_back({c, user_data, ""});
}

void DeviceIndex::register_device_lost (dev_callback c,
                                        void* user_data,
                                        const std::string& serial)
{
    tcam_log(TCAM_LOG_DEBUG, "Registered device lost callback for %s", serial.c_str());
    std::lock_guard<std::mutex> lock(mtx);
    callbacks.push_back({c, user_data, serial});
}

void DeviceIndex::remove_device_lost (dev_callback callback)
{
    std::lock_guard<std::mutex> lock(mtx);

    auto it = std::begin(callbacks); //std::begin is a free function in C++11
    for (auto& value : callbacks)
    {

        if (value.callback == callback)
        {
            callbacks.erase(it);
            break;
        }
        it++; //at the end OR make sure you do this in each iteration
    }
}

void DeviceIndex::remove_device_lost (dev_callback callback, const std::string& serial)
{
    std::lock_guard<std::mutex> lock(mtx);

    auto it = std::begin(callbacks); //std::begin is a free function in C++11
    for (auto& value : callbacks)
    {
        if (value.callback == callback && value.serial.compare(serial) == 0)
        {
            callbacks.erase(it);
            break;
        }
        it++; //at the end OR make sure you do this in each iteration
    }
}

void DeviceIndex::update_device_list ()
{
    std::vector<DeviceInfo> tmp_dev_list = std::vector<DeviceInfo>();
    tmp_dev_list.reserve(10);

    auto dev_list = BackendLoader::getInstance().get_device_list_all_backends();
    if (!dev_list.empty())
        tmp_dev_list.insert(tmp_dev_list.end(), dev_list.begin(), dev_list.end());

    tcam_log(TCAM_LOG_DEBUG, "Number of found devices: %d", dev_list.size());

    // check for lost devices
    for (const auto& d : device_list)
    {
        auto f = [&d] (const DeviceInfo& info)
            {
                if (d.get_serial().compare(info.get_serial()) == 0)
                    return true;
                return false;
            };

        auto found = std::find_if(tmp_dev_list.begin(), tmp_dev_list.end(), f);

        if (found == tmp_dev_list.end())
        {
            tcam_log(TCAM_LOG_INFO, "Lost device %s. Contacting callbacks", d.get_name().c_str());
            fire_device_lost(d);
        }
    }
    std::lock_guard<std::mutex> lock(mtx);

    device_list.clear();

    device_list.insert(device_list.end(), tmp_dev_list.begin(), tmp_dev_list.end());

    have_list = true;
    wait_for_list.notify_all();
}

void DeviceIndex::run ()
{
    while (continue_thread)
    {
        update_device_list();
        std::this_thread::sleep_for(std::chrono::seconds(wait_period));
    }
}

void DeviceIndex::fire_device_lost (const DeviceInfo& d)
{
    std::lock_guard<std::mutex> lock(mtx);

    for (auto& c : callbacks)
    {
        if (c.serial.empty() || c.serial.compare(d.get_serial()) == 0)
        {
            c.callback(d, c.data);
        }
    }
}

bool DeviceIndex::fill_device_info (DeviceInfo& info) const
{
    if (!info.get_serial().empty())
    {
        for (const auto& d : device_list)
        {
            if (info.get_serial() == d.get_serial())
            {
                info = d;
                return true;
            }
        }
        return false;
    }

    if (!info.get_identifier().empty())
    {
        for (const auto& d : device_list)
        {
            if (info.get_identifier() == d.get_identifier())
            {
                info = d;
                return true;
            }
        }
    }
    return false;
}

std::vector<DeviceInfo> DeviceIndex::get_device_list () const
{
    std::unique_lock<std::mutex> lock(mtx);

    // wait for work_thread to deliver first valid list
    // since get_aravis_device_list is a blocking function
    // our thread would retrieve an empty list without this wait loop
    if (!have_list)
    {
        wait_for_list.wait_for(lock, std::chrono::seconds(wait_period));
    }

    return device_list;
}

DeviceIndex& DeviceIndex::get_instance()
{
    static DeviceIndex static_instance;

    return static_instance;
}

std::vector<DeviceInfo> tcam::get_device_list ()
{
    return DeviceIndex::get_instance().get_device_list();
}
<commit_msg>Increase DeviceIndex::wait_period to 30 seconds<commit_after>/*
 * Copyright 2014 The Imaging Source Europe GmbH
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <vector>
#include <string>
#include <memory>
#include <algorithm>
#include <cstring>
#include <unistd.h>

#include "utils.h"
#include "logging.h"
#include "DeviceIndex.h"

#include "BackendLoader.h"

using namespace tcam;

DeviceIndex::DeviceIndex ()
    : continue_thread(false),
      wait_period(30),
      have_list(false),
      device_list(std::vector<DeviceInfo>()),
      callbacks(std::vector<callback_data>())
{
    continue_thread = true;
    work_thread = std::thread (&DeviceIndex::run, this);

    work_thread.detach();
}

DeviceIndex::~DeviceIndex ()
{
    continue_thread = false;
    if (work_thread.joinable())
    {
        work_thread.join();
    }
}

void DeviceIndex::register_device_lost (dev_callback c, void* user_data)
{
    tcam_log(TCAM_LOG_DEBUG, "Registered device lost callback");

    std::lock_guard<std::mutex> lock(mtx);
    callbacks.push_back({c, user_data, ""});
}

void DeviceIndex::register_device_lost (dev_callback c,
                                        void* user_data,
                                        const std::string& serial)
{
    tcam_log(TCAM_LOG_DEBUG, "Registered device lost callback for %s", serial.c_str());
    std::lock_guard<std::mutex> lock(mtx);
    callbacks.push_back({c, user_data, serial});
}

void DeviceIndex::remove_device_lost (dev_callback callback)
{
    std::lock_guard<std::mutex> lock(mtx);

    auto it = std::begin(callbacks); //std::begin is a free function in C++11
    for (auto& value : callbacks)
    {

        if (value.callback == callback)
        {
            callbacks.erase(it);
            break;
        }
        it++; //at the end OR make sure you do this in each iteration
    }
}

void DeviceIndex::remove_device_lost (dev_callback callback, const std::string& serial)
{
    std::lock_guard<std::mutex> lock(mtx);

    auto it = std::begin(callbacks); //std::begin is a free function in C++11
    for (auto& value : callbacks)
    {
        if (value.callback == callback && value.serial.compare(serial) == 0)
        {
            callbacks.erase(it);
            break;
        }
        it++; //at the end OR make sure you do this in each iteration
    }
}

void DeviceIndex::update_device_list ()
{
    std::vector<DeviceInfo> tmp_dev_list = std::vector<DeviceInfo>();
    tmp_dev_list.reserve(10);

    auto dev_list = BackendLoader::getInstance().get_device_list_all_backends();
    if (!dev_list.empty())
        tmp_dev_list.insert(tmp_dev_list.end(), dev_list.begin(), dev_list.end());

    tcam_log(TCAM_LOG_DEBUG, "Number of found devices: %d", dev_list.size());

    // check for lost devices
    for (const auto& d : device_list)
    {
        auto f = [&d] (const DeviceInfo& info)
            {
                if (d.get_serial().compare(info.get_serial()) == 0)
                    return true;
                return false;
            };

        auto found = std::find_if(tmp_dev_list.begin(), tmp_dev_list.end(), f);

        if (found == tmp_dev_list.end())
        {
            tcam_log(TCAM_LOG_INFO, "Lost device %s. Contacting callbacks", d.get_name().c_str());
            fire_device_lost(d);
        }
    }
    std::lock_guard<std::mutex> lock(mtx);

    device_list.clear();

    device_list.insert(device_list.end(), tmp_dev_list.begin(), tmp_dev_list.end());

    have_list = true;
    wait_for_list.notify_all();
}

void DeviceIndex::run ()
{
    while (continue_thread)
    {
        update_device_list();
        std::this_thread::sleep_for(std::chrono::seconds(wait_period));
    }
}

void DeviceIndex::fire_device_lost (const DeviceInfo& d)
{
    std::lock_guard<std::mutex> lock(mtx);

    for (auto& c : callbacks)
    {
        if (c.serial.empty() || c.serial.compare(d.get_serial()) == 0)
        {
            c.callback(d, c.data);
        }
    }
}

bool DeviceIndex::fill_device_info (DeviceInfo& info) const
{
    if (!info.get_serial().empty())
    {
        for (const auto& d : device_list)
        {
            if (info.get_serial() == d.get_serial())
            {
                info = d;
                return true;
            }
        }
        return false;
    }

    if (!info.get_identifier().empty())
    {
        for (const auto& d : device_list)
        {
            if (info.get_identifier() == d.get_identifier())
            {
                info = d;
                return true;
            }
        }
    }
    return false;
}

std::vector<DeviceInfo> DeviceIndex::get_device_list () const
{
    std::unique_lock<std::mutex> lock(mtx);

    // wait for work_thread to deliver first valid list
    // since get_aravis_device_list is a blocking function
    // our thread would retrieve an empty list without this wait loop
    if (!have_list)
    {
        wait_for_list.wait_for(lock, std::chrono::seconds(wait_period));
    }

    return device_list;
}

DeviceIndex& DeviceIndex::get_instance()
{
    static DeviceIndex static_instance;

    return static_instance;
}

std::vector<DeviceInfo> tcam::get_device_list ()
{
    return DeviceIndex::get_instance().get_device_list();
}
<|endoftext|>


<commit_before>#include <SFML/Network.hpp>
#include <iostream>

#include "common/terrain.h"
#include "common/player.h"

// Choose a random port for opening sockets (ports < 1024 are reserved)
const unsigned short Port = 28997;

Terrain *terrain;

void sendTerrain(sf::SocketTCP Client) {
    std::ifstream file("server.mcube", std::ios::in | std::ios::binary | std::ios::ate);

    std::ifstream::pos_type size;
    char *memblock;

    size = file.tellg();
    memblock = new char[size];
    file.seekg(0, std::ios::beg);
    file.read(memblock, size);
    file.close();

    sf::Packet Packet;
    Packet << std::string(memblock, size);
    Client.Send(Packet);

    delete[] memblock;
}

void saveKey(std::string key) {
    std::ofstream file("key.txt");
    file.write(key.c_str(), key.length());
    file.close();
}

// TODO: error handling
std::string readKey() {
    std::ifstream file("key.txt");

    if (!file) return ""; // No key is saved

    char memblock[33];
    file.read(memblock, 32);
    file.close();

    return std::string(memblock);
}

sf::Http Http;
void sendBeat() {
    Http.SetHost("mc-heartbeat.heroku.com");

    // Prepare a request to beat the heart
    sf::Http::Request Request;
    Request.SetMethod(sf::Http::Request::Post);
    Request.SetBody("hey=thar");//"port=" << Port);
    Request.SetHttpVersion(1, 0);

    // Load the key (if any)
    std::string key = readKey();
    std::cout << "Key: " << key << std::endl;

    if (key == "")
        Request.SetURI("/beat");
    else
        Request.SetURI("/beat/" + key);

    // Send it and get the response returned by the server
    sf::Http::Response Page = Http.SendRequest(Request);

    if (Page.GetStatus() != 200) {
        std::cout << "Error while beating the heart. (HTTP " << Page.GetStatus() << ")" << std::endl;
        return;
    }

    // Display the response
    std::cout << "The heart has been beat." << std::endl;

    // Save the key
    saveKey(Page.GetBody());
}

std::map<sf::SocketTCP, Player> players();

// Launch a server and receive incoming messages
int main() {
    std::cout << "Beating..." << std::endl;
    sendBeat();

    std::cout << "Setting up terrain..." << std::endl;
    terrain = new Terrain(5, 0, 1,1,1, 50);
    terrain->Regenerate();
    terrain->SaveToFile("server.mcube");

    // Create a socket for listening to incoming connections
    sf::SocketTCP Listener;
    if (!Listener.Listen(Port))
        return EXIT_FAILURE;
    std::cout << "Listening to port " << Port << ", waiting for connections..." << std::endl;

    // Create a selector for handling several sockets (the listener + the socket associated to each client)
    sf::SelectorTCP Selector;

    // Add the listener
    Selector.Add(Listener);

    // Loop while... we close the program :)
    while (true)
    {
        // Get the sockets ready for reading
        unsigned int NbSockets = Selector.Wait();

        // We can read from each returned socket
        for (unsigned int i = 0; i < NbSockets; ++i)
        {
            // Get the current socket
            sf::SocketTCP Socket = Selector.GetSocketReady(i);

            if (Socket == Listener)
            {
                // If the listening socket is ready, it means that we can accept a new connection
                sf::IPAddress Address;
                sf::SocketTCP Client;
                Listener.Accept(Client, &Address);
                std::cout << "Client connected: " << Address << std::endl;

                // Add it to the selector
                Selector.Add(Client);
            }
            else
            {
                // Else, it is a client socket so we can read the data he sent
                sf::Packet Packet;
                if (Socket.Receive(Packet) == sf::Socket::Done)
                {
                    // Extract the message and display it
                    std::string Message;
                    Packet >> Message;

                    if (Message == "Terrain pl0z")
                        sendTerrain(Socket);
                    else
                        std::cout << "A client says: \"" << Message << "\"" << std::endl;
                }
                else
                {
                    // Error: we'd better remove the socket from the selector
                    std::cout << "Client disconnected" << std::endl;
                    Selector.Remove(Socket);
                }
            }
        }
    }

    return EXIT_SUCCESS;
}

<commit_msg>Made some modications in the C++ from scott's suggestions<commit_after>#include <SFML/Network.hpp>
#include <iostream>

#include "common/terrain.h"
#include "common/player.h"

// Choose a random port for opening sockets (ports < 1024 are reserved)
const unsigned short Port = 28997;

Terrain *terrain;

void sendTerrain(sf::SocketTCP Client) {
    std::ifstream file("server.mcube", std::ios::in | std::ios::binary | std::ios::ate);

    std::ifstream::pos_type size;
    char *memblock;

    size = file.tellg();
    memblock = new char[size];
    file.seekg(0, std::ios::beg);
    file.read(memblock, size);
    file.close();

    sf::Packet Packet;
    Packet << std::string(memblock, size);
    Client.Send(Packet);

    delete[] memblock;
}

const int KeySize = 32;

void saveKey(std::string key) {
    if (key.length() != KeySize) return; // TODO: throw error

    std::ofstream file("key.txt");
    file << key;
}

// TODO: error handling
std::string readKey() {
    std::ifstream file("key.txt");

    if (!file) return ""; // No key is saved; it'll grab a new one

    char memblock[KeySize];
    file.read(memblock, KeySize);

    return std::string(memblock, KeySize);
}

sf::Http Http;
void sendBeat() {
    Http.SetHost("mc-heartbeat.heroku.com");

    // Prepare a request to beat the heart
    sf::Http::Request Request;
    Request.SetMethod(sf::Http::Request::Post);
    Request.SetBody("hey=thar");//"port=" << Port);
    Request.SetHttpVersion(1, 0);

    // Load the key (if any)
    std::string key = readKey();
    std::cout << "Key: " << key << std::endl;

    if (key == "")
        Request.SetURI("/beat");
    else
        Request.SetURI("/beat/" + key);

    // Send it and get the response returned by the server
    sf::Http::Response Page = Http.SendRequest(Request);

    if (Page.GetStatus() != 200) {
        std::cout << "Error while beating the heart. (HTTP " << Page.GetStatus() << ")" << std::endl;
        return;
    }

    // Display the response
    std::cout << "The heart has been beat." << std::endl;

    // Save the key
    saveKey(Page.GetBody());
}

std::map<sf::SocketTCP, Player> players();

// Launch a server and receive incoming messages
int main() {
    std::cout << "Beating..." << std::endl;
    sendBeat();

    std::cout << "Setting up terrain..." << std::endl;
    terrain = new Terrain(5, 0, 1,1,1, 50);
    terrain->Regenerate();
    terrain->SaveToFile("server.mcube");

    // Create a socket for listening to incoming connections
    sf::SocketTCP Listener;
    if (!Listener.Listen(Port))
        return EXIT_FAILURE;
    std::cout << "Listening to port " << Port << ", waiting for connections..." << std::endl;

    // Create a selector for handling several sockets (the listener + the socket associated to each client)
    sf::SelectorTCP Selector;

    // Add the listener
    Selector.Add(Listener);

    // Loop while... we close the program :)
    while (true)
    {
        // Get the sockets ready for reading
        unsigned int NbSockets = Selector.Wait();

        // We can read from each returned socket
        for (unsigned int i = 0; i < NbSockets; ++i)
        {
            // Get the current socket
            sf::SocketTCP Socket = Selector.GetSocketReady(i);

            if (Socket == Listener)
            {
                // If the listening socket is ready, it means that we can accept a new connection
                sf::IPAddress Address;
                sf::SocketTCP Client;
                Listener.Accept(Client, &Address);
                std::cout << "Client connected: " << Address << std::endl;

                // Add it to the selector
                Selector.Add(Client);
            }
            else
            {
                // Else, it is a client socket so we can read the data he sent
                sf::Packet Packet;
                if (Socket.Receive(Packet) == sf::Socket::Done)
                {
                    // Extract the message and display it
                    std::string Message;
                    Packet >> Message;

                    if (Message == "Terrain pl0z")
                        sendTerrain(Socket);
                    else
                        std::cout << "A client says: \"" << Message << "\"" << std::endl;
                }
                else
                {
                    // Error: we'd better remove the socket from the selector
                    std::cout << "Client disconnected" << std::endl;
                    Selector.Remove(Socket);
                }
            }
        }
    }

    return EXIT_SUCCESS;
}

<|endoftext|>


<commit_before>#include	"FTPolyGlyph.h"
#include	"FTVectoriser.h"

FTPolyGlyph::FTPolyGlyph( FT_Glyph glyph)
:	FTGlyph(),
	vectoriser(0),
	numPoints(0),
	data(0),
	glList(0)
{
	if( ft_glyph_format_outline != glyph->format)
	{ return;}

	vectoriser = new FTVectoriser( glyph);

	vectoriser->Process();

	vectoriser->MakeMesh();
	numPoints = vectoriser->MeshPoints();

	if ( numPoints < 3)
	{
		delete vectoriser;
		return;
	}

	data = new double[ numPoints * 3];

	vectoriser->GetMesh( data);

	advance = glyph->advance.x >> 16;

	delete vectoriser;

	glList = glGenLists(1);
	int d = 0;

	glNewList( glList, GL_COMPILE);
		int BEPairs = data[0];
		for( int i = 0; i < BEPairs; ++i)
		{
			int polyType = (int)data[d + 1];
			glBegin( polyType);

			int verts = (int)data[d+2];
			d += 3;
			for( int x = 0; x < verts; ++x)
			{
				glVertex3dv( data + d);
				d += 3;
			}
			glEnd();
		}
	glEndList();

	delete [] data; // FIXME
	data = 0;

	// discard glyph image (bitmap or not)
	FT_Done_Glyph( glyph); // Why does this have to be HERE
}

FTPolyGlyph::~FTPolyGlyph()
{
//	if( data)
//		delete [] data; // FIXME
}

float FTPolyGlyph::Render( const FT_Vector& pen)
{
	if( glList)
	{
		glTranslatef( pen.x, pen.y, 0);
			glCallList( glList);
		glTranslatef( -pen.x, -pen.y, 0);
	}

	return advance;
}
<commit_msg>Set the bounding box<commit_after>#include	"FTPolyGlyph.h"
#include	"FTVectoriser.h"

FTPolyGlyph::FTPolyGlyph( FT_Glyph glyph)
:	FTGlyph(),
	vectoriser(0),
	numPoints(0),
	data(0),
	glList(0)
{
	if( ft_glyph_format_outline != glyph->format)
	{
		return;
	}

	vectoriser = new FTVectoriser( glyph);

	vectoriser->Process();

	vectoriser->MakeMesh();
	numPoints = vectoriser->MeshPoints();

	if ( numPoints < 3)
	{
		delete vectoriser;
		return;
	}

	data = new double[ numPoints * 3];
	vectoriser->GetMesh( data);
	delete vectoriser;

	int d = 0;
	glList = glGenLists(1);
	glNewList( glList, GL_COMPILE);
		int BEPairs = data[0];
		for( int i = 0; i < BEPairs; ++i)
		{
			int polyType = (int)data[d + 1];
			glBegin( polyType);

			int verts = (int)data[d+2];
			d += 3;
			for( int x = 0; x < verts; ++x)
			{
				glVertex3dv( data + d);
				d += 3;
			}
			glEnd();
		}
	glEndList();

	delete [] data; // FIXME
	data = 0;

	bBox = FTBBox( glyph);
	advance = glyph->advance.x >> 16;

	// discard glyph image (bitmap or not)
	FT_Done_Glyph( glyph); // Why does this have to be HERE
}

FTPolyGlyph::~FTPolyGlyph()
{
//	if( data)
//		delete [] data; // FIXME
}

float FTPolyGlyph::Render( const FT_Vector& pen)
{
	if( glList)
	{
		glTranslatef( pen.x, pen.y, 0);
			glCallList( glList);
		glTranslatef( -pen.x, -pen.y, 0);
	}

	return advance;
}
<|endoftext|>


<commit_before>// This file is part of Poseidon.
// Copyleft 2022, LH_Mouse. All wrongs reserved.

#ifndef POSEIDON_FWD_
#define POSEIDON_FWD_

#include "version.h"
#include <asteria/fwd.hpp>
#include <rocket/ascii_case.hpp>
#include <rocket/linear_buffer.hpp>
#include <rocket/atomic.hpp>
#include <rocket/mutex.hpp>
#include <rocket/recursive_mutex.hpp>
#include <rocket/condition_variable.hpp>
#include <rocket/once_flag.hpp>
#include <array>
#include <vector>
#include <deque>
#include <unordered_map>

namespace poseidon {
namespace noadl = poseidon;

// Aliases
using ::std::initializer_list;
using ::std::nullptr_t;
using ::std::max_align_t;
using ::std::int8_t;
using ::std::uint8_t;
using ::std::int16_t;
using ::std::uint16_t;
using ::std::int32_t;
using ::std::uint32_t;
using ::std::int64_t;
using ::std::uint64_t;
using ::std::intptr_t;
using ::std::uintptr_t;
using ::std::intmax_t;
using ::std::uintmax_t;
using ::std::ptrdiff_t;
using ::std::size_t;
using ::std::wint_t;
using ::std::exception;
using ::std::exception_ptr;
using ::std::type_info;
using ::std::pair;
using ::std::unique_ptr;
using ::std::shared_ptr;
using ::std::weak_ptr;
using ::std::array;
using ::std::vector;
using ::std::deque;
using ::std::unordered_map;

using ::std::static_pointer_cast;
using ::std::dynamic_pointer_cast;
using ::std::const_pointer_cast;

using ::rocket::linear_buffer;
using ::rocket::atomic;
using ::rocket::atomic_relaxed;
using ::rocket::atomic_acq_rel;
using ::rocket::atomic_seq_cst;
using atomic_signal = ::rocket::atomic_relaxed<int>;
using plain_mutex = ::rocket::mutex;
using ::rocket::recursive_mutex;
using ::rocket::condition_variable;
using ::rocket::once_flag;
using ::rocket::cow_vector;
using ::rocket::cow_hashmap;
using ::rocket::static_vector;
using ::rocket::cow_string;
using ::rocket::cow_u16string;
using ::rocket::cow_u32string;
using ::rocket::tinybuf;
using ::rocket::tinyfmt;
using ::rocket::tinyfmt_str;
using ::rocket::tinyfmt_file;

using ::rocket::begin;
using ::rocket::end;
using ::rocket::swap;
using ::rocket::xswap;
using ::rocket::size;
using ::rocket::nullopt;
using ::rocket::nullopt_t;
using ::rocket::sref;
using ::rocket::optional;
using ::rocket::variant;
using phsh_string = ::rocket::prehashed_string;

#define POSEIDON_HIDDEN_STRUCT(CLASS, MEMBER)  \
  using CLASS##_##MEMBER = MEMBER;  \
  struct __attribute__((__visibility__("hidden")))  \
      CLASS::MEMBER : CLASS##_##MEMBER { }  // no semicolon

// Log levels
// Note each level has a hardcoded name and number.
// Don't change their values or reorder them.
enum Log_Level : uint8_t
  {
    log_level_fatal  = 0,
    log_level_error  = 1,
    log_level_warn   = 2,
    log_level_info   = 3,
    log_level_debug  = 4,
    log_level_trace  = 5,
  };

// Asynchronous function states
enum Async_State : uint8_t
  {
    async_state_pending    = 0,
    async_state_suspended  = 1,
    async_state_running    = 2,
    async_state_finished   = 3,
  };

// Future states
enum Future_State : uint8_t
  {
    future_state_empty      = 0,
    future_state_value      = 1,
    future_state_exception  = 2,
  };

// Core classes
class Config_File;
class Abstract_Timer;
class Abstract_Future;
template<typename ValueT> class Future;
class Abstract_Fiber;

// Manager classes
extern class Main_Config& main_config;
extern class Async_Logger& async_logger;
extern class Timer_Driver& timer_driver;
extern class Fiber_Scheduler& fiber_scheduler;
extern atomic_signal exit_signal;

// Composes a string and submits it to the logger.
// Note that in order to use these macros, you still have to include
// <poseidon/static/async_logger.hpp>; otherwise there may be errors
// about incomplete types.
#define POSEIDON_LOG_GENERIC(LEVEL, TEMPLATE, ...)  \
  (::poseidon::async_logger.enabled(::poseidon::log_level_##LEVEL)  \
   &&  \
   ([=](const char* f5zuNP3w) -> bool  \
       __attribute__((__noinline__, __nothrow__))  \
     {  \
       try {  \
         ::poseidon::Async_Logger::Element iQw3Zbsf;  \
         iQw3Zbsf.level = ::poseidon::log_level_##LEVEL;  \
         iQw3Zbsf.file = __FILE__;  \
         iQw3Zbsf.line = __LINE__;  \
         iQw3Zbsf.func = f5zuNP3w;  \
         \
         try {  \
           iQw3Zbsf.text = ::asteria::format_string(  \
               (::asteria::make_string_template TEMPLATE), ##__VA_ARGS__);  \
         }  \
         catch(::std::exception& xSG022wB) {  \
           iQw3Zbsf.text = ::asteria::format_string(  \
               "Failed to compose log message: $1\n[exception class `$2`]",  \
               xSG022wB, typeid(xSG022wB));  \
         }  \
         ::poseidon::async_logger.enqueue(::std::move(iQw3Zbsf));  \
         \
         if(iQw3Zbsf.level <= ::poseidon::log_level_error)  \
           ::poseidon::async_logger.synchronize();  \
       }  \
       catch(::std::exception& aJHPhv84) {  \
         ::fprintf(stderr,  \
             "%s: Error writing log:\n  %s\n", f5zuNP3w, aJHPhv84.what());  \
       }  \
       return true;  \
     }  \
     (__func__)))

#define POSEIDON_LOG_FATAL(...)   POSEIDON_LOG_GENERIC(fatal, __VA_ARGS__)
#define POSEIDON_LOG_ERROR(...)   POSEIDON_LOG_GENERIC(error, __VA_ARGS__)
#define POSEIDON_LOG_WARN(...)    POSEIDON_LOG_GENERIC(warn,  __VA_ARGS__)
#define POSEIDON_LOG_INFO(...)    POSEIDON_LOG_GENERIC(info,  __VA_ARGS__)
#define POSEIDON_LOG_DEBUG(...)   POSEIDON_LOG_GENERIC(debug, __VA_ARGS__)
#define POSEIDON_LOG_TRACE(...)   POSEIDON_LOG_GENERIC(trace, __VA_ARGS__)

}  // namespace poseidon

#endif
<commit_msg>fwd: Remove duplicate exception type names in log<commit_after>// This file is part of Poseidon.
// Copyleft 2022, LH_Mouse. All wrongs reserved.

#ifndef POSEIDON_FWD_
#define POSEIDON_FWD_

#include "version.h"
#include <asteria/fwd.hpp>
#include <rocket/ascii_case.hpp>
#include <rocket/linear_buffer.hpp>
#include <rocket/atomic.hpp>
#include <rocket/mutex.hpp>
#include <rocket/recursive_mutex.hpp>
#include <rocket/condition_variable.hpp>
#include <rocket/once_flag.hpp>
#include <array>
#include <vector>
#include <deque>
#include <unordered_map>

namespace poseidon {
namespace noadl = poseidon;

// Aliases
using ::std::initializer_list;
using ::std::nullptr_t;
using ::std::max_align_t;
using ::std::int8_t;
using ::std::uint8_t;
using ::std::int16_t;
using ::std::uint16_t;
using ::std::int32_t;
using ::std::uint32_t;
using ::std::int64_t;
using ::std::uint64_t;
using ::std::intptr_t;
using ::std::uintptr_t;
using ::std::intmax_t;
using ::std::uintmax_t;
using ::std::ptrdiff_t;
using ::std::size_t;
using ::std::wint_t;
using ::std::exception;
using ::std::exception_ptr;
using ::std::type_info;
using ::std::pair;
using ::std::unique_ptr;
using ::std::shared_ptr;
using ::std::weak_ptr;
using ::std::array;
using ::std::vector;
using ::std::deque;
using ::std::unordered_map;

using ::std::static_pointer_cast;
using ::std::dynamic_pointer_cast;
using ::std::const_pointer_cast;

using ::rocket::linear_buffer;
using ::rocket::atomic;
using ::rocket::atomic_relaxed;
using ::rocket::atomic_acq_rel;
using ::rocket::atomic_seq_cst;
using atomic_signal = ::rocket::atomic_relaxed<int>;
using plain_mutex = ::rocket::mutex;
using ::rocket::recursive_mutex;
using ::rocket::condition_variable;
using ::rocket::once_flag;
using ::rocket::cow_vector;
using ::rocket::cow_hashmap;
using ::rocket::static_vector;
using ::rocket::cow_string;
using ::rocket::cow_u16string;
using ::rocket::cow_u32string;
using ::rocket::tinybuf;
using ::rocket::tinyfmt;
using ::rocket::tinyfmt_str;
using ::rocket::tinyfmt_file;

using ::rocket::begin;
using ::rocket::end;
using ::rocket::swap;
using ::rocket::xswap;
using ::rocket::size;
using ::rocket::nullopt;
using ::rocket::nullopt_t;
using ::rocket::sref;
using ::rocket::optional;
using ::rocket::variant;
using phsh_string = ::rocket::prehashed_string;

#define POSEIDON_HIDDEN_STRUCT(CLASS, MEMBER)  \
  using CLASS##_##MEMBER = MEMBER;  \
  struct __attribute__((__visibility__("hidden")))  \
      CLASS::MEMBER : CLASS##_##MEMBER { }  // no semicolon

// Log levels
// Note each level has a hardcoded name and number.
// Don't change their values or reorder them.
enum Log_Level : uint8_t
  {
    log_level_fatal  = 0,
    log_level_error  = 1,
    log_level_warn   = 2,
    log_level_info   = 3,
    log_level_debug  = 4,
    log_level_trace  = 5,
  };

// Asynchronous function states
enum Async_State : uint8_t
  {
    async_state_pending    = 0,
    async_state_suspended  = 1,
    async_state_running    = 2,
    async_state_finished   = 3,
  };

// Future states
enum Future_State : uint8_t
  {
    future_state_empty      = 0,
    future_state_value      = 1,
    future_state_exception  = 2,
  };

// Core classes
class Config_File;
class Abstract_Timer;
class Abstract_Future;
template<typename ValueT> class Future;
class Abstract_Fiber;

// Manager classes
extern class Main_Config& main_config;
extern class Async_Logger& async_logger;
extern class Timer_Driver& timer_driver;
extern class Fiber_Scheduler& fiber_scheduler;
extern atomic_signal exit_signal;

// Composes a string and submits it to the logger.
// Note that in order to use these macros, you still have to include
// <poseidon/static/async_logger.hpp>; otherwise there may be errors
// about incomplete types.
#define POSEIDON_LOG_GENERIC(LEVEL, TEMPLATE, ...)  \
  (::poseidon::async_logger.enabled(::poseidon::log_level_##LEVEL)  \
   &&  \
   ([=](const char* f5zuNP3w) -> bool  \
       __attribute__((__noinline__, __nothrow__))  \
     {  \
       try {  \
         ::poseidon::Async_Logger::Element iQw3Zbsf;  \
         iQw3Zbsf.level = ::poseidon::log_level_##LEVEL;  \
         iQw3Zbsf.file = __FILE__;  \
         iQw3Zbsf.line = __LINE__;  \
         iQw3Zbsf.func = f5zuNP3w;  \
         \
         try {  \
           iQw3Zbsf.text = ::asteria::format_string(  \
               (::asteria::make_string_template TEMPLATE), ##__VA_ARGS__);  \
         }  \
         catch(::std::exception& xSG022wB) {  \
           iQw3Zbsf.text = ::asteria::format_string(  \
               "Failed to compose log message: $1", xSG022wB);  \
         }  \
         ::poseidon::async_logger.enqueue(::std::move(iQw3Zbsf));  \
         \
         if(iQw3Zbsf.level <= ::poseidon::log_level_error)  \
           ::poseidon::async_logger.synchronize();  \
       }  \
       catch(::std::exception& aJHPhv84) {  \
         ::fprintf(stderr,  \
             "%s: Error writing log:\n  %s\n", f5zuNP3w, aJHPhv84.what());  \
       }  \
       return true;  \
     }  \
     (__func__)))

#define POSEIDON_LOG_FATAL(...)   POSEIDON_LOG_GENERIC(fatal, __VA_ARGS__)
#define POSEIDON_LOG_ERROR(...)   POSEIDON_LOG_GENERIC(error, __VA_ARGS__)
#define POSEIDON_LOG_WARN(...)    POSEIDON_LOG_GENERIC(warn,  __VA_ARGS__)
#define POSEIDON_LOG_INFO(...)    POSEIDON_LOG_GENERIC(info,  __VA_ARGS__)
#define POSEIDON_LOG_DEBUG(...)   POSEIDON_LOG_GENERIC(debug, __VA_ARGS__)
#define POSEIDON_LOG_TRACE(...)   POSEIDON_LOG_GENERIC(trace, __VA_ARGS__)

}  // namespace poseidon

#endif
<|endoftext|>


<commit_before>#include "GPUInterface.h"

Persistent<Function> GPUInterface::constructor;

GPUInterface::GPUInterface() {
  client = init();
}

GPUInterface::~GPUInterface() {
  destroy(client);
}

void GPUInterface::Init(Handle<Object> exports) {
  NanScope();

  Local<FunctionTemplate> ctor = NanNew<FunctionTemplate>(GPUInterface::New);
  ctor->InstanceTemplate()->SetInternalFieldCount(1);
  ctor->SetClassName(NanNew("GPUInterface"));

  NODE_SET_PROTOTYPE_METHOD(ctor, "viewImageSync", ViewImageSync);
  NODE_SET_PROTOTYPE_METHOD(ctor, "viewImageAsync", ViewImageAsync);
  NODE_SET_PROTOTYPE_METHOD(ctor, "viewVideoSync", ViewVideoSync);
  NODE_SET_PROTOTYPE_METHOD(ctor, "viewVideoAsync", ViewVideoAsync);

  exports->Set(NanNew("GPUInterface"), ctor->GetFunction());
}

NAN_METHOD(GPUInterface::New) {
  NanScope();
  GPUInterface *gpui = new GPUInterface();
  gpui->Wrap(args.This());
  NanReturnValue(args.This());
}

NAN_METHOD(GPUInterface::ViewImageSync) {
  NanScope();
  GPUInterface *gpui = new GPUInterface();

  Local<String> fileName = args[0].As<String>();
  int duration = args[1]->Uint32Value();
  int ret = viewImageFilename(gpui->client, *String::Utf8Value(fileName), duration);

  NanReturnValue(NanNew<Number>(ret));
}

NAN_METHOD(GPUInterface::ViewImageAsync) {
  NanScope();
  GPUInterface *gpui = new GPUInterface();

  string fileName(*NanAsciiString(args[0]));
  int duration = args[1]->Uint32Value();
  NanCallback *callback = new NanCallback(args[2].As<Function>());

  NanAsyncQueueWorker(new AsyncImageViewer(callback, gpui, fileName, duration));
  NanReturnUndefined();
}

NAN_METHOD(GPUInterface::ViewVideoSync) {
  NanScope();
  GPUInterface *gpui = new GPUInterface();

  Local<String> fileName = args[0].As<String>();
  int ret = playVideoFilename(gpui->client, *String::Utf8Value(fileName));

  NanReturnValue(NanNew<Number>(ret));
}

NAN_METHOD(GPUInterface::ViewVideoAsync) {
  NanScope();
  GPUInterface *gpui = new GPUInterface();

  string fileName(*NanAsciiString(args[0]));
  NanCallback *callback = new NanCallback(args[1].As<Function>());

  NanAsyncQueueWorker(new AsyncVideoViewer(callback, gpui, fileName));
  NanReturnUndefined();
}

void AsyncImageViewer::Execute() {
  returnVal = viewImageFilename(gpui->client, const_cast<char*>(filename.c_str()), duration);
}

void AsyncImageViewer::HandleOKCallback() {
  NanScope();

  Local<Value> argv[] = {
    NanNew<Number>(returnVal)
  };

  callback->Call(1, argv);
}

void AsyncVideoViewer::Execute() {
  returnVal = playVideoFilename(gpui->client, const_cast<char*>(filename.c_str()));
}

void AsyncVideoViewer::HandleOKCallback() {
  NanScope();

  Local<Value> argv[] = {
    NanNew<Number>(returnVal)
  };

  callback->Call(1, argv);
}
<commit_msg>fixed creating a new guiinterface object on every function call<commit_after>#include "GPUInterface.h"

Persistent<Function> GPUInterface::constructor;

GPUInterface::GPUInterface() {
  client = init();
}

GPUInterface::~GPUInterface() {
  destroy(client);
}

void GPUInterface::Init(Handle<Object> exports) {
  NanScope();

  Local<FunctionTemplate> ctor = NanNew<FunctionTemplate>(GPUInterface::New);
  ctor->InstanceTemplate()->SetInternalFieldCount(1);
  ctor->SetClassName(NanNew("GPUInterface"));

  NODE_SET_PROTOTYPE_METHOD(ctor, "viewImageSync", ViewImageSync);
  NODE_SET_PROTOTYPE_METHOD(ctor, "viewImageAsync", ViewImageAsync);
  NODE_SET_PROTOTYPE_METHOD(ctor, "viewVideoSync", ViewVideoSync);
  NODE_SET_PROTOTYPE_METHOD(ctor, "viewVideoAsync", ViewVideoAsync);

  exports->Set(NanNew("GPUInterface"), ctor->GetFunction());
}

NAN_METHOD(GPUInterface::New) {
  NanScope();
  GPUInterface *gpui = new GPUInterface();
  gpui->Wrap(args.This());
  NanReturnValue(args.This());
}

NAN_METHOD(GPUInterface::ViewImageSync) {
  NanScope();
  GPUInterface *gpui = ObjectWrap::Unwrap<GPUInterface>(args.This());

  Local<String> fileName = args[0].As<String>();
  int duration = args[1]->Uint32Value();
  int ret = viewImageFilename(gpui->client, *String::Utf8Value(fileName), duration);

  NanReturnValue(NanNew<Number>(ret));
}

NAN_METHOD(GPUInterface::ViewImageAsync) {
  NanScope();
  GPUInterface *gpui = ObjectWrap::Unwrap<GPUInterface>(args.This());

  string fileName(*NanAsciiString(args[0]));
  int duration = args[1]->Uint32Value();
  NanCallback *callback = new NanCallback(args[2].As<Function>());

  NanAsyncQueueWorker(new AsyncImageViewer(callback, gpui, fileName, duration));
  NanReturnUndefined();
}

NAN_METHOD(GPUInterface::ViewVideoSync) {
  NanScope();
  GPUInterface *gpui = ObjectWrap::Unwrap<GPUInterface>(args.This());

  Local<String> fileName = args[0].As<String>();
  int ret = playVideoFilename(gpui->client, *String::Utf8Value(fileName));

  NanReturnValue(NanNew<Number>(ret));
}

NAN_METHOD(GPUInterface::ViewVideoAsync) {
  NanScope();
  GPUInterface *gpui = ObjectWrap::Unwrap<GPUInterface>(args.This());

  string fileName(*NanAsciiString(args[0]));
  NanCallback *callback = new NanCallback(args[1].As<Function>());

  NanAsyncQueueWorker(new AsyncVideoViewer(callback, gpui, fileName));
  NanReturnUndefined();
}

void AsyncImageViewer::Execute() {
  returnVal = viewImageFilename(gpui->client, const_cast<char*>(filename.c_str()), duration);
}

void AsyncImageViewer::HandleOKCallback() {
  NanScope();

  Local<Value> argv[] = {
    NanNew<Number>(returnVal)
  };

  callback->Call(1, argv);
}

void AsyncVideoViewer::Execute() {
  returnVal = playVideoFilename(gpui->client, const_cast<char*>(filename.c_str()));
}

void AsyncVideoViewer::HandleOKCallback() {
  NanScope();

  Local<Value> argv[] = {
    NanNew<Number>(returnVal)
  };

  callback->Call(1, argv);
}
<|endoftext|>


<commit_before>#include "llvm_gen.hpp"
#include <map>
#include <cybozu/option.hpp>

struct Code : public mcl::Generator {
	typedef std::map<int, Function> FunctionMap;
	Operand Void;
	uint32_t unit;
	uint32_t unit2;
	uint32_t bit;
	uint32_t N;
	std::string unitStr;
	Function mulUU;
	Function extractHigh;
	Function mulPos;
	Function makeNIST_P192;
	Function mcl_fpDbl_mod_NIST_P192;
	FunctionMap mcl_fp_addNCM;
	FunctionMap mcl_fp_subNCM;
	FunctionMap mcl_fp_addM;
	FunctionMap mcl_fp_subM;
	Code() : unit(0), unit2(0), bit(0), N(0) { }

	void gen_mulUU()
	{
		resetGlobalIdx();
		Operand z(Int, unit2);
		Operand x(Int, unit);
		Operand y(Int, unit);
		std::string name = "mul";
		name += unitStr + "x" + unitStr;
		mulUU = Function(name, z, x, y);
		beginFunc(mulUU);

		x = zext(x, unit2);
		y = zext(y, unit2);
		z= mul(x, y);
		ret(z);
		endFunc();
	}
	void gen_extractHigh()
	{
		resetGlobalIdx();
		Operand z(Int, unit);
		Operand x(Int, unit2);
		std::string name = "extractHigh";
		name += unitStr;
		extractHigh = Function(name, z, x);
		extractHigh.setPrivate();
		beginFunc(extractHigh);

		x = lshr(x, unit);
		z = trunc(x, unit);
		ret(z);
		endFunc();
	}
	void gen_mulPos()
	{
		resetGlobalIdx();
		Operand xy(Int, unit2);
		Operand px(IntPtr, unit);
		Operand y(Int, unit);
		Operand i(Int, unit);
		std::string name = "mulPos";
		name += unitStr + "x" + unitStr;
		mulPos = Function(name, xy, px, y, i);
		mulPos.setPrivate();
		beginFunc(mulPos);

		px = getelementptr(px, i);
		Operand x = load(px);
		xy = call(mulUU, x, y);
		ret(xy);
		endFunc();
	}
	Operand extract192to64(const Operand& x, uint32_t shift)
	{
		Operand y = lshr(x, shift);
		y = trunc(y, 64);
		return y;
	}
	void gen_makeNIST_P192()
	{
		resetGlobalIdx();
		Operand p(Int, 192);
		Operand p0(Int, 64);
		Operand p1(Int, 64);
		Operand p2(Int, 64);
		Operand _0 = makeImm(64, 0);
		Operand _1 = makeImm(64, 1);
		Operand _2 = makeImm(64, 2);
		makeNIST_P192 = Function("makeNIST_P192", p);
		beginFunc(makeNIST_P192);
		p0 = sub(_0, _1);
		p1 = sub(_0, _2);
		p2 = sub(_0, _1);
		p0 = zext(p0, 192);
		p1 = zext(p1, 192);
		p2 = zext(p2, 192);
		p1 = shl(p1, 64);
		p2 = shl(p2, 128);
		p = add(p0, p1);
		p = add(p, p2);
		ret(p);
		endFunc();
	}
	void gen_mcl_fpDbl_mod_NIST_P192()
	{
		resetGlobalIdx();
		Operand out(IntPtr, 192);
		Operand px(IntPtr, 192);
		mcl_fpDbl_mod_NIST_P192 = Function("mcl_fpDbl_mod_NIST_P192", Void, out, px);
		beginFunc(mcl_fpDbl_mod_NIST_P192);

		Operand L = load(px);
		L = zext(L, 256);

		Operand pH = getelementptr(px, makeImm(32, 1));
		Operand H192 = load(pH);
		Operand H = zext(H192, 256);

		Operand H10 = shl(H192, 64);
		H10 = zext(H10, 256);

		Operand H2 = extract192to64(H192, 128);
		H2 = zext(H2, 256);
		Operand H102 = _or(H10, H2);

		H2 = shl(H2, 64);

		Operand t = add(L, H);
		t = add(t, H102);
		t = add(t, H2);

		Operand e = lshr(t, 192);
		e = trunc(e, 64);
		e = zext(e, 256);
		Operand e2 = shl(e, 64);
		e = _or(e, e2);

		t = trunc(t, 192);
		t = zext(t, 256);

		Operand z = add(t, e);
		Operand p = call(makeNIST_P192);
		p = zext(p, 256);
		Operand zp = sub(z, p);
		Operand c = lshr(zp, 192);
		c = trunc(c, 1);
		z = select(c, z, zp);
		z = trunc(z, 192);
		store(z, out);
		ret(Void);
		endFunc();
	}
	void gen_once()
	{
		gen_mulUU();
		gen_extractHigh();
		gen_mulPos();
		gen_makeNIST_P192();
		gen_mcl_fpDbl_mod_NIST_P192();
	}
	Operand extract(const Operand& x, uint32_t shift)
	{
		Operand t = lshr(x, shift);
		t = trunc(t, unit);
		return t;
	}
	void gen_mcl_fp_addsubNC(bool isAdd)
	{
		resetGlobalIdx();
		Operand pz(IntPtr, bit);
		Operand px(IntPtr, bit);
		Operand py(IntPtr, bit);
		std::string name;
		if (isAdd) {
			name = "mcl_fp_addNC" + cybozu::itoa(bit);
			mcl_fp_addNCM[bit] = Function(name, Void, pz, px, py);
			beginFunc(mcl_fp_addNCM[bit]);
		} else {
			name = "mcl_fp_subNC" + cybozu::itoa(bit);
			mcl_fp_subNCM[bit] = Function(name, Void, pz, px, py);
			beginFunc(mcl_fp_subNCM[bit]);
		}
		Operand x = load(px);
		Operand y = load(py);
		Operand z;
		if (isAdd) {
			add(x, y);
		} else {
			sub(x, y);
		}
		store(z, pz);
		ret(Void);
		endFunc();
	}
	void gen_mcl_fp_add()
	{
		resetGlobalIdx();
		Operand pz(IntPtr, bit);
		Operand px(IntPtr, bit);
		Operand py(IntPtr, bit);
		Operand pp(IntPtr, bit);
		std::string name = "mcl_fp_add" + cybozu::itoa(bit);
		mcl_fp_addM[bit] = Function(name, Void, pz, px, py, pp);
		beginFunc(mcl_fp_addM[bit]);
		Operand x = load(px);
		Operand y = load(py);
		Operand p = load(pp);
		x = zext(x, bit + unit);
		y = zext(y, bit + unit);
		p = zext(p, bit + unit);
		Operand t0 = add(x, y);
		Operand t1 = sub(t0, p);
		Operand t = lshr(t1, bit);
		t = trunc(t, 1);
		t = select(t, t0, t1);
		t = trunc(t, bit);
		store(t, pz);
		ret(Void);
		endFunc();
	}
	void gen_mcl_fp_sub()
	{
		resetGlobalIdx();
		Operand pz(IntPtr, bit);
		Operand px(IntPtr, bit);
		Operand py(IntPtr, bit);
		Operand pp(IntPtr, bit);
		std::string name = "mcl_fp_sub" + cybozu::itoa(bit);
		mcl_fp_subM[bit] = Function(name, Void, pz, px, py, pp);
		beginFunc(mcl_fp_subM[bit]);
		Operand x = load(px);
		Operand y = load(py);
		x = zext(x, bit + unit);
		y = zext(y, bit + unit);
		Operand vc = sub(x, y);
		Operand v = trunc(vc, bit); // v = x - y
		Operand c = lshr(vc, bit + unit - 1);
		c = trunc(c, 1);
		Operand p = load(pp);
		Operand z = select(c, p, makeImm(bit, 0));
		v = add(v, z);
		store(v, pz);
		ret(Void);
		endFunc();
	}
	void gen_all()
	{
		gen_mcl_fp_addsubNC(true);
		gen_mcl_fp_addsubNC(false);
	}
	void gen_short()
	{
		gen_mcl_fp_add();
		gen_mcl_fp_sub();
	}
	void setBit(uint32_t bit)
	{
		this->bit = bit;
		N = bit / unit;
	}
	void setUnit(uint32_t unit)
	{
		this->unit = unit;
		unit2 = unit * 2;
		unitStr = cybozu::itoa(unit);
	}
	void gen()
	{
		gen_once();
		for (int i = 128; i < 256; i += unit) {
//			gen_all();
//			gen_short();
		}
	}
};

int main(int argc, char *argv[])
	try
{
	uint32_t unit;
	cybozu::Option opt;
	opt.appendOpt(&unit, uint32_t(sizeof(void*)) * 8, "u", "unit");
	opt.appendHelp("h");
	if (!opt.parse(argc, argv)) {
		opt.usage();
		return 1;
	}
	Code c;
	c.setUnit(unit);
	c.gen();
} catch (std::exception& e) {
	printf("ERR %s\n", e.what());
	return 1;
}
<commit_msg>unity some expressions<commit_after>#include "llvm_gen.hpp"
#include <map>
#include <cybozu/option.hpp>

struct Code : public mcl::Generator {
	typedef std::map<int, Function> FunctionMap;
	Operand Void;
	uint32_t unit;
	uint32_t unit2;
	uint32_t bit;
	uint32_t N;
	std::string unitStr;
	Function mulUU;
	Function extractHigh;
	Function mulPos;
	Function makeNIST_P192;
	Function mcl_fpDbl_mod_NIST_P192;
	FunctionMap mcl_fp_addNCM;
	FunctionMap mcl_fp_subNCM;
	FunctionMap mcl_fp_addM;
	FunctionMap mcl_fp_subM;
	Code() : unit(0), unit2(0), bit(0), N(0) { }

	void gen_mulUU()
	{
		resetGlobalIdx();
		Operand z(Int, unit2);
		Operand x(Int, unit);
		Operand y(Int, unit);
		std::string name = "mul";
		name += unitStr + "x" + unitStr;
		mulUU = Function(name, z, x, y);
		beginFunc(mulUU);

		x = zext(x, unit2);
		y = zext(y, unit2);
		z= mul(x, y);
		ret(z);
		endFunc();
	}
	void gen_extractHigh()
	{
		resetGlobalIdx();
		Operand z(Int, unit);
		Operand x(Int, unit2);
		std::string name = "extractHigh";
		name += unitStr;
		extractHigh = Function(name, z, x);
		extractHigh.setPrivate();
		beginFunc(extractHigh);

		x = lshr(x, unit);
		z = trunc(x, unit);
		ret(z);
		endFunc();
	}
	void gen_mulPos()
	{
		resetGlobalIdx();
		Operand xy(Int, unit2);
		Operand px(IntPtr, unit);
		Operand y(Int, unit);
		Operand i(Int, unit);
		std::string name = "mulPos";
		name += unitStr + "x" + unitStr;
		mulPos = Function(name, xy, px, y, i);
		mulPos.setPrivate();
		beginFunc(mulPos);

		px = getelementptr(px, i);
		Operand x = load(px);
		xy = call(mulUU, x, y);
		ret(xy);
		endFunc();
	}
	Operand extract192to64(const Operand& x, uint32_t shift)
	{
		Operand y = lshr(x, shift);
		y = trunc(y, 64);
		return y;
	}
	void gen_makeNIST_P192()
	{
		resetGlobalIdx();
		Operand p(Int, 192);
		Operand p0(Int, 64);
		Operand p1(Int, 64);
		Operand p2(Int, 64);
		Operand _0 = makeImm(64, 0);
		Operand _1 = makeImm(64, 1);
		Operand _2 = makeImm(64, 2);
		makeNIST_P192 = Function("makeNIST_P192", p);
		beginFunc(makeNIST_P192);
		p0 = sub(_0, _1);
		p1 = sub(_0, _2);
		p2 = sub(_0, _1);
		p0 = zext(p0, 192);
		p1 = zext(p1, 192);
		p2 = zext(p2, 192);
		p1 = shl(p1, 64);
		p2 = shl(p2, 128);
		p = add(p0, p1);
		p = add(p, p2);
		ret(p);
		endFunc();
	}
	void gen_mcl_fpDbl_mod_NIST_P192()
	{
		resetGlobalIdx();
		Operand out(IntPtr, 192);
		Operand px(IntPtr, 192);
		mcl_fpDbl_mod_NIST_P192 = Function("mcl_fpDbl_mod_NIST_P192", Void, out, px);
		beginFunc(mcl_fpDbl_mod_NIST_P192);

		Operand L = load(px);
		L = zext(L, 256);

		Operand pH = getelementptr(px, makeImm(32, 1));
		Operand H192 = load(pH);
		Operand H = zext(H192, 256);

		Operand H10 = shl(H192, 64);
		H10 = zext(H10, 256);

		Operand H2 = extract192to64(H192, 128);
		H2 = zext(H2, 256);
		Operand H102 = _or(H10, H2);

		H2 = shl(H2, 64);

		Operand t = add(L, H);
		t = add(t, H102);
		t = add(t, H2);

		Operand e = lshr(t, 192);
		e = trunc(e, 64);
		e = zext(e, 256);
		Operand e2 = shl(e, 64);
		e = _or(e, e2);

		t = trunc(t, 192);
		t = zext(t, 256);

		Operand z = add(t, e);
		Operand p = call(makeNIST_P192);
		p = zext(p, 256);
		Operand zp = sub(z, p);
		Operand c = trunc(lshr(zp, 192), 1);
		z = trunc(select(c, z, zp), 192);
		store(z, out);
		ret(Void);
		endFunc();
	}
	void gen_once()
	{
		gen_mulUU();
		gen_extractHigh();
		gen_mulPos();
		gen_makeNIST_P192();
		gen_mcl_fpDbl_mod_NIST_P192();
	}
	Operand extract(const Operand& x, uint32_t shift)
	{
		Operand t = lshr(x, shift);
		t = trunc(t, unit);
		return t;
	}
	void gen_mcl_fp_addsubNC(bool isAdd)
	{
		resetGlobalIdx();
		Operand pz(IntPtr, bit);
		Operand px(IntPtr, bit);
		Operand py(IntPtr, bit);
		std::string name;
		if (isAdd) {
			name = "mcl_fp_addNC" + cybozu::itoa(bit);
			mcl_fp_addNCM[bit] = Function(name, Void, pz, px, py);
			beginFunc(mcl_fp_addNCM[bit]);
		} else {
			name = "mcl_fp_subNC" + cybozu::itoa(bit);
			mcl_fp_subNCM[bit] = Function(name, Void, pz, px, py);
			beginFunc(mcl_fp_subNCM[bit]);
		}
		Operand x = load(px);
		Operand y = load(py);
		Operand z;
		if (isAdd) {
			add(x, y);
		} else {
			sub(x, y);
		}
		store(z, pz);
		ret(Void);
		endFunc();
	}
	void gen_mcl_fp_add()
	{
		resetGlobalIdx();
		Operand pz(IntPtr, bit);
		Operand px(IntPtr, bit);
		Operand py(IntPtr, bit);
		Operand pp(IntPtr, bit);
		std::string name = "mcl_fp_add" + cybozu::itoa(bit);
		mcl_fp_addM[bit] = Function(name, Void, pz, px, py, pp);
		beginFunc(mcl_fp_addM[bit]);
		Operand x = load(px);
		Operand y = load(py);
		Operand p = load(pp);
		x = zext(x, bit + unit);
		y = zext(y, bit + unit);
		p = zext(p, bit + unit);
		Operand t0 = add(x, y);
		Operand t1 = sub(t0, p);
		Operand t = lshr(t1, bit);
		t = trunc(t, 1);
		t = select(t, t0, t1);
		t = trunc(t, bit);
		store(t, pz);
		ret(Void);
		endFunc();
	}
	void gen_mcl_fp_sub()
	{
		resetGlobalIdx();
		Operand pz(IntPtr, bit);
		Operand px(IntPtr, bit);
		Operand py(IntPtr, bit);
		Operand pp(IntPtr, bit);
		std::string name = "mcl_fp_sub" + cybozu::itoa(bit);
		mcl_fp_subM[bit] = Function(name, Void, pz, px, py, pp);
		beginFunc(mcl_fp_subM[bit]);
		Operand x = load(px);
		Operand y = load(py);
		x = zext(x, bit + unit);
		y = zext(y, bit + unit);
		Operand vc = sub(x, y);
		Operand v = trunc(vc, bit); // v = x - y
		Operand c = lshr(vc, bit + unit - 1);
		c = trunc(c, 1);
		Operand p = load(pp);
		Operand z = select(c, p, makeImm(bit, 0));
		v = add(v, z);
		store(v, pz);
		ret(Void);
		endFunc();
	}
	void gen_all()
	{
		gen_mcl_fp_addsubNC(true);
		gen_mcl_fp_addsubNC(false);
	}
	void gen_short()
	{
		gen_mcl_fp_add();
		gen_mcl_fp_sub();
	}
	void setBit(uint32_t bit)
	{
		this->bit = bit;
		N = bit / unit;
	}
	void setUnit(uint32_t unit)
	{
		this->unit = unit;
		unit2 = unit * 2;
		unitStr = cybozu::itoa(unit);
	}
	void gen()
	{
		gen_once();
		for (int i = 128; i < 256; i += unit) {
//			gen_all();
//			gen_short();
		}
	}
};

int main(int argc, char *argv[])
	try
{
	uint32_t unit;
	cybozu::Option opt;
	opt.appendOpt(&unit, uint32_t(sizeof(void*)) * 8, "u", "unit");
	opt.appendHelp("h");
	if (!opt.parse(argc, argv)) {
		opt.usage();
		return 1;
	}
	Code c;
	c.setUnit(unit);
	c.gen();
} catch (std::exception& e) {
	printf("ERR %s\n", e.what());
	return 1;
}
<|endoftext|>