<commit_before>/**
 * @file cnn_conv_connection.hpp
 * @author Shangtong Zhang
 * @author Marcus Edel
 *
 * Implementation of the convolutional connection between input layer and output
 * layer.
 */
#ifndef __MLPACK_METHODS_ANN_CONNECTIONS_CONV_CONNECTION_HPP
#define __MLPACK_METHODS_ANN_CONNECTIONS_CONV_CONNECTION_HPP

#include <mlpack/core.hpp>
#include <mlpack/methods/ann/init_rules/nguyen_widrow_init.hpp>
#include <mlpack/methods/ann/optimizer/steepest_descent.hpp>
#include <mlpack/methods/ann/convolution_rules/border_modes.hpp>
#include <mlpack/methods/ann/convolution_rules/naive_convolution.hpp>
#include <mlpack/methods/ann/convolution_rules/fft_convolution.hpp>

namespace mlpack{
namespace ann  /** Artificial Neural Network. */{

/**
 * Implementation of the convolutional connection class. The convolutional
 * connection performs convolution between input layer and output layer.
 * Convolution is applied to every neuron in input layer. The kernel used for
 * convolution is stored in @weights.
 *
 * Users can design their own convolution rule (ForwardConvolutionRule)
 * to perform forward process. But once user-defined forward convolution is
 * used, users have to design special BackwardConvolutionRule and
 * GradientConvolutionRule to perform backward process and calculate gradient
 * corresponding to ForwardConvolutionRule, aimed to guarantee the correctness
 * of error flow.
 *
 * @tparam InputLayerType Type of the connected input layer.
 * @tparam OutputLayerType Type of the connected output layer.
 * @tparam OptimizerType Type of the optimizer used to update the weights.
 * @tparam WeightInitRule Rule used to initialize the weights matrix.
 * @tparam ForwardConvolutionRule Convolution to perform forward process.
 * @tparam BackwardConvolutionRule Convolution to perform backward process.
 * @tparam GradientConvolutionRule Convolution to calculate gradient.
 * @tparam DataType Type of data (arma::mat, arma::sp_mat or arma::cube).
 */
template<
    typename InputLayerType,
    typename OutputLayerType,
    template<typename, typename> class OptimizerType = mlpack::ann::RMSPROP,
    class WeightInitRule = NguyenWidrowInitialization,
    typename ForwardConvolutionRule = NaiveConvolution<ValidConvolution>,
    typename BackwardConvolutionRule = NaiveConvolution<FullConvolution>,
    typename GradientConvolutionRule = NaiveConvolution<ValidConvolution>,
    typename DataType = arma::cube
>
class ConvConnection
{
 public:
  /**
   * Create the ConvConnection object using the specified input layer, output
   * layer, filter size, optimizer and weight initialization rule.
   *
   * @param InputLayerType The input layer which is connected with the output
   * layer.
   * @param OutputLayerType The output layer which is connected with the input
   * layer.
   * @param filterSize the size of the filter.
   * @param OptimizerType The optimizer used to update the weight matrix.
   * @param WeightInitRule The weights initialization rule used to initialize
   * the weights matrix.
   */
  ConvConnection(InputLayerType& inputLayer,
                 OutputLayerType& outputLayer,
                 const size_t filterSize,
                 OptimizerType<ConvConnection<InputLayerType,
                                              OutputLayerType,
                                              OptimizerType,
                                              WeightInitRule,
                                              ForwardConvolutionRule,
                                              BackwardConvolutionRule,
                                              GradientConvolutionRule,
                                              DataType>, DataType>& optimizer,
                 WeightInitRule weightInitRule = WeightInitRule()) :
      inputLayer(inputLayer),
      outputLayer(outputLayer),
      optimizer(&optimizer),
      ownsOptimizer(false)
  {
    weightInitRule.Initialize(weights, filterSize, filterSize,
        outputLayer.LayerSlices());
  }

  /**
   * Create the ConvConnection object using the specified input layer, output
   * layer, filter size and weight initialization rule.
   *
   * @param InputLayerType The input layer which is connected with the output
   * layer.
   * @param OutputLayerType The output layer which is connected with the input
   * layer.
   * @param filterSize the size of the filter.
   * @param WeightInitRule The weights initialization rule used to initialize
   * the weights matrix.
   */
  ConvConnection(InputLayerType& inputLayer,
                 OutputLayerType& outputLayer,
                 const size_t filterSize,
                 WeightInitRule weightInitRule = WeightInitRule()) :
      inputLayer(inputLayer),
      outputLayer(outputLayer),
      optimizer(new OptimizerType<ConvConnection<InputLayerType,
                                                 OutputLayerType,
                                                 OptimizerType,
                                                 WeightInitRule,
                                                 ForwardConvolutionRule,
                                                 BackwardConvolutionRule,
                                                 GradientConvolutionRule,
                                                 DataType>, DataType>(*this)),
      ownsOptimizer(true)
  {
    weightInitRule.Initialize(weights, filterSize, filterSize,
        inputLayer.OutputMaps() * outputLayer.OutputMaps());
  }

  /**
   * Delete the conv connection object and its optimizer.
   */
  ~ConvConnection()
  {
    if (ownsOptimizer)
      delete optimizer;
  }

  /**
   * Ordinary feed forward pass of a neural network. Apply convolution to every
   * neuron in input layer and put the output in the output layer.
   *
   * @param input The input activation.
   */
  template<typename InputType>
  void FeedForward(const InputType& input)
  {
    for (size_t outputmap = 0; outputmap < outputLayer.OutputMaps(); outputmap++)
    {
      for (size_t inputmap = 0; inputmap < inputLayer.OutputMaps(); inputmap++)
      {
        InputType inputSlices = input.slices(
            inputmap * inputLayer.LayerSlices(),
            (inputmap * inputLayer.LayerSlices()) +
            inputLayer.LayerSlices() - 1);

        InputType output;
        ForwardConvolutionRule::Convolution(inputSlices,
            weights.slice(inputmap * outputLayer.OutputMaps() +
            outputmap), output);

        outputLayer.InputActivation().slices(
            (outputmap * inputLayer.LayerSlices()),
            (outputmap * inputLayer.LayerSlices()) +
            inputLayer.LayerSlices() - 1) += output;
      }
    }
  }

  /**
   * Ordinary feed backward pass of a neural network. Pass the error from output
   * layer to input layer and calculate the delta of kernel weights.
   *
   * @param error The backpropagated error.
   */
  template<typename eT>
  void FeedBackward(const arma::Cube<eT>& error)
  {
    delta = arma::zeros<arma::Cube<eT>>(inputLayer.InputActivation().n_rows,
                                        inputLayer.InputActivation().n_cols,
                                        inputLayer.InputActivation().n_slices);

    for (size_t outputmap = 0; outputmap < inputLayer.OutputMaps(); outputmap++)
    {
      for (size_t inputmap = 0; inputmap < outputLayer.OutputMaps(); inputmap++)
      {
        arma::Cube<eT> errorSlices = error.slices(inputmap *
            inputLayer.LayerSlices(), (inputmap * inputLayer.LayerSlices()) +
            inputLayer.LayerSlices() - 1);

        arma::Mat<eT> rotatedFilter;
        Rotate180(weights.slice(
            outputmap * outputLayer.OutputMaps() + inputmap), rotatedFilter);

        arma::Cube<eT> output;
        BackwardConvolutionRule::Convolution(errorSlices, rotatedFilter, output);

        delta.slices((outputmap * inputLayer.LayerSlices()),
            (outputmap * inputLayer.LayerSlices()) +
            inputLayer.LayerSlices() - 1) += output;
      }
    }
  }

  /*
   * Calculate the gradient using the output delta and the input activation.
   *
   * @param gradient The calculated gradient.
   */
  template<typename eT>
  void Gradient(arma::Cube<eT>& gradient)
  {
    gradient = arma::zeros<arma::Cube<eT> >(weights.n_rows, weights.n_cols,
        weights.n_slices);

    for (size_t outputmap = 0, s = 0; outputmap < outputLayer.OutputMaps(); outputmap++)
    {
      for (size_t inputmap = 0; inputmap < inputLayer.OutputMaps(); inputmap++, s++)
      {
        arma::Cube<eT> inputSlices = inputLayer.InputActivation().slices(
            inputmap * inputLayer.LayerSlices(), (inputmap + 1) *
            inputLayer.LayerSlices() - 1);

        arma::Cube<eT> deltaSlices = outputLayer.Delta().slices(
            outputmap * inputLayer.LayerSlices(),
            (outputmap + 1) * inputLayer.LayerSlices() - 1);

        arma::Cube<eT> output;
        GradientConvolutionRule::Convolution(inputSlices, deltaSlices, output);

        for (size_t i = 0; i < output.n_slices; i++)
          gradient.slice(s) += output.slice(i);

        gradient.slice(s) /= inputLayer.LayerSlices();
      }
    }

    if (InputLayer().OutputMaps() != 1)
    {
      arma::Cube<eT> temp = arma::zeros<arma::Cube<eT> >(weights.n_rows, weights.n_cols,
        weights.n_slices);

      for (size_t i = 0, g = 0; i < OutputLayer().OutputMaps(); i++)
      {
        for (size_t j = i; j < weights.n_slices; j+= OutputLayer().OutputMaps(), g++)
        {
          temp.slice(j) = gradient.slice(g);
        }
      }

      gradient = temp;
    }
  }

  //! Get the convolution kernel.
  DataType& Weights() const { return weights; }
  //! Modify the convolution kernel.
  DataType& Weights() { return weights; }

  //! Get the input layer.
  InputLayerType& InputLayer() const { return inputLayer; }
  //! Modify the input layer.
  InputLayerType& InputLayer() { return inputLayer; }

  //! Get the output layer.
  OutputLayerType& OutputLayer() const { return outputLayer; }
  //! Modify the output layer.
  OutputLayerType& OutputLayer() { return outputLayer; }

  //! Get the optimzer.
  OptimizerType<ConvConnection<InputLayerType,
                               OutputLayerType,
                               OptimizerType,
                               WeightInitRule,
                               ForwardConvolutionRule,
                               BackwardConvolutionRule,
                               GradientConvolutionRule,
                               DataType>, DataType>& Optimzer() const
  {
    return *optimizer;
  }
  //! Modify the optimzer.
  OptimizerType<ConvConnection<InputLayerType,
                               OutputLayerType,
                               OptimizerType,
                               WeightInitRule,
                               ForwardConvolutionRule,
                               BackwardConvolutionRule,
                               GradientConvolutionRule,
                               DataType>, DataType>& Optimzer()
  {
    return *optimizer;
  }

  //! Get the passed error in backward propagation.
  DataType& Delta() const { return delta; }
  //! Modify the passed error in backward propagation.
  DataType& Delta() { return delta; }

 private:
  /*
   * Rotates a 3rd-order tesor counterclockwise by 180 degrees.
   *
   * @param input The input data to be rotated.
   * @param output The rotated output.
   */
  template<typename eT>
  void Rotate180(const arma::Cube<eT>& input, arma::Cube<eT>& output)
  {
    output = arma::Cube<eT>(input.n_rows, input.n_cols, input.n_slices);

    // * left-right flip, up-down flip */
    for (size_t s = 0; s < output.n_slices; s++)
      output.slice(s) = arma::fliplr(arma::flipud(input.slice(s)));
  }

  /*
   * Rotates a dense matrix counterclockwise by 180 degrees.
   *
   * @param input The input data to be rotated.
   * @param output The rotated output.
   */
  template<typename eT>
  void Rotate180(const arma::Mat<eT>& input, arma::Mat<eT>& output)
  {
    // * left-right flip, up-down flip */
    output = arma::fliplr(arma::flipud(input));
  }

  //! Locally-stored kernel weights.
  DataType weights;

  //! Locally-stored inputlayer.
  InputLayerType& inputLayer;

  //! Locally-stored outputlayer.
  OutputLayerType& outputLayer;

  //! Locally-stored optimizer.
  OptimizerType<ConvConnection<InputLayerType,
                               OutputLayerType,
                               OptimizerType,
                               WeightInitRule,
                               ForwardConvolutionRule,
                               BackwardConvolutionRule,
                               GradientConvolutionRule,
                               DataType>, DataType>* optimizer;

  //! Parameter that indicates if the class owns a optimizer object.
  bool ownsOptimizer;

  //! Locally-stored passed error in backward propagation.
  DataType delta;
};// class ConvConnection

}; // namespace ann
}; // namespace mlpack

#endif<commit_msg>Gradient calculation speedup by iterating over the output maps.<commit_after>/**
 * @file cnn_conv_connection.hpp
 * @author Shangtong Zhang
 * @author Marcus Edel
 *
 * Implementation of the convolutional connection between input layer and output
 * layer.
 */
#ifndef __MLPACK_METHODS_ANN_CONNECTIONS_CONV_CONNECTION_HPP
#define __MLPACK_METHODS_ANN_CONNECTIONS_CONV_CONNECTION_HPP

#include <mlpack/core.hpp>
#include <mlpack/methods/ann/init_rules/nguyen_widrow_init.hpp>
#include <mlpack/methods/ann/optimizer/rmsprop.hpp>
#include <mlpack/methods/ann/convolution_rules/border_modes.hpp>
#include <mlpack/methods/ann/convolution_rules/naive_convolution.hpp>

namespace mlpack{
namespace ann  /** Artificial Neural Network. */{

/**
 * Implementation of the convolutional connection class. The convolutional
 * connection performs convolution between input layer and output layer.
 * Convolution is applied to every neuron in input layer. The kernel used for
 * convolution is stored in @weights.
 *
 * Users can design their own convolution rule (ForwardConvolutionRule)
 * to perform forward process. But once user-defined forward convolution is
 * used, users have to design special BackwardConvolutionRule and
 * GradientConvolutionRule to perform backward process and calculate gradient
 * corresponding to ForwardConvolutionRule, aimed to guarantee the correctness
 * of error flow.
 *
 * @tparam InputLayerType Type of the connected input layer.
 * @tparam OutputLayerType Type of the connected output layer.
 * @tparam OptimizerType Type of the optimizer used to update the weights.
 * @tparam WeightInitRule Rule used to initialize the weights matrix.
 * @tparam ForwardConvolutionRule Convolution to perform forward process.
 * @tparam BackwardConvolutionRule Convolution to perform backward process.
 * @tparam GradientConvolutionRule Convolution to calculate gradient.
 * @tparam DataType Type of data (arma::mat, arma::sp_mat or arma::cube).
 */
template<
    typename InputLayerType,
    typename OutputLayerType,
    template<typename, typename> class OptimizerType = mlpack::ann::RMSPROP,
    class WeightInitRule = NguyenWidrowInitialization,
    typename ForwardConvolutionRule = NaiveConvolution<ValidConvolution>,
    typename BackwardConvolutionRule = NaiveConvolution<FullConvolution>,
    typename GradientConvolutionRule = NaiveConvolution<ValidConvolution>,
    typename DataType = arma::cube
>
class ConvConnection
{
 public:
  /**
   * Create the ConvConnection object using the specified input layer, output
   * layer, filter size, optimizer and weight initialization rule.
   *
   * @param InputLayerType The input layer which is connected with the output
   * layer.
   * @param OutputLayerType The output layer which is connected with the input
   * layer.
   * @param filterSize the size of the filter.
   * @param OptimizerType The optimizer used to update the weight matrix.
   * @param WeightInitRule The weights initialization rule used to initialize
   * the weights matrix.
   */
  ConvConnection(InputLayerType& inputLayer,
                 OutputLayerType& outputLayer,
                 const size_t filterSize,
                 OptimizerType<ConvConnection<InputLayerType,
                                              OutputLayerType,
                                              OptimizerType,
                                              WeightInitRule,
                                              ForwardConvolutionRule,
                                              BackwardConvolutionRule,
                                              GradientConvolutionRule,
                                              DataType>, DataType>& optimizer,
                 WeightInitRule weightInitRule = WeightInitRule()) :
      inputLayer(inputLayer),
      outputLayer(outputLayer),
      optimizer(&optimizer),
      ownsOptimizer(false)
  {
    weightInitRule.Initialize(weights, filterSize, filterSize,
        outputLayer.LayerSlices());
  }

  /**
   * Create the ConvConnection object using the specified input layer, output
   * layer, filter size and weight initialization rule.
   *
   * @param InputLayerType The input layer which is connected with the output
   * layer.
   * @param OutputLayerType The output layer which is connected with the input
   * layer.
   * @param filterSize the size of the filter.
   * @param WeightInitRule The weights initialization rule used to initialize
   * the weights matrix.
   */
  ConvConnection(InputLayerType& inputLayer,
                 OutputLayerType& outputLayer,
                 const size_t filterSize,
                 WeightInitRule weightInitRule = WeightInitRule()) :
      inputLayer(inputLayer),
      outputLayer(outputLayer),
      optimizer(new OptimizerType<ConvConnection<InputLayerType,
                                                 OutputLayerType,
                                                 OptimizerType,
                                                 WeightInitRule,
                                                 ForwardConvolutionRule,
                                                 BackwardConvolutionRule,
                                                 GradientConvolutionRule,
                                                 DataType>, DataType>(*this)),
      ownsOptimizer(true)
  {
    weightInitRule.Initialize(weights, filterSize, filterSize,
        inputLayer.OutputMaps() * outputLayer.OutputMaps());
  }

  /**
   * Delete the conv connection object and its optimizer.
   */
  ~ConvConnection()
  {
    if (ownsOptimizer)
      delete optimizer;
  }

  /**
   * Ordinary feed forward pass of a neural network. Apply convolution to every
   * neuron in input layer and put the output in the output layer.
   *
   * @param input The input activation.
   */
  template<typename eT>
  void FeedForward(const arma::Cube<eT>& input)
  {
    for (size_t outputmap = 0; outputmap < outputLayer.OutputMaps(); outputmap++)
    {
      for (size_t inputmap = 0; inputmap < inputLayer.OutputMaps(); inputmap++)
      {
        arma::Cube<eT> inputSlices = input.slices(
            inputmap * inputLayer.LayerSlices(),
            (inputmap * inputLayer.LayerSlices()) +
            inputLayer.LayerSlices() - 1);

        arma::Cube<eT> output;
        ForwardConvolutionRule::Convolution(inputSlices,
            weights.slice(inputmap * outputLayer.OutputMaps() +
            outputmap), output);

        outputLayer.InputActivation().slices(
            (outputmap * inputLayer.LayerSlices()),
            (outputmap * inputLayer.LayerSlices()) +
            inputLayer.LayerSlices() - 1) += output;
      }
    }
  }

  /**
   * Ordinary feed backward pass of a neural network. Pass the error from output
   * layer to input layer and calculate the delta of kernel weights.
   *
   * @param error The backpropagated error.
   */
  template<typename eT>
  void FeedBackward(const arma::Cube<eT>& error)
  {
    delta = arma::zeros<arma::Cube<eT> >(inputLayer.InputActivation().n_rows,
                                         inputLayer.InputActivation().n_cols,
                                         inputLayer.InputActivation().n_slices);

    for (size_t outputmap = 0; outputmap < inputLayer.OutputMaps(); outputmap++)
    {
      for (size_t inputmap = 0; inputmap < outputLayer.OutputMaps(); inputmap++)
      {
        arma::Cube<eT> errorSlices = error.slices(inputmap *
            inputLayer.LayerSlices(), (inputmap * inputLayer.LayerSlices()) +
            inputLayer.LayerSlices() - 1);

        arma::Mat<eT> rotatedFilter;
        Rotate180(weights.slice(
            outputmap * outputLayer.OutputMaps() + inputmap), rotatedFilter);

        arma::Cube<eT> output;
        BackwardConvolutionRule::Convolution(errorSlices, rotatedFilter, output);

        delta.slices((outputmap * inputLayer.LayerSlices()),
            (outputmap * inputLayer.LayerSlices()) +
            inputLayer.LayerSlices() - 1) += output;
      }
    }
  }

  /*
   * Calculate the gradient using the output delta and the input activation.
   *
   * @param gradient The calculated gradient.
   */
  template<typename eT>
  void Gradient(arma::Cube<eT>& gradient)
  {
    gradient = arma::zeros<arma::Cube<eT> >(weights.n_rows, weights.n_cols,
        weights.n_slices);

    for (size_t outputmap = 0; outputmap < outputLayer.OutputMaps(); outputmap++)
    {
      for (size_t inputmap = 0, s = outputmap; inputmap < inputLayer.OutputMaps();
        inputmap++, s += OutputLayer().OutputMaps())
      {
        arma::Cube<eT> inputSlices = inputLayer.InputActivation().slices(
            inputmap * inputLayer.LayerSlices(), (inputmap + 1) *
            inputLayer.LayerSlices() - 1);

        arma::Cube<eT> deltaSlices = outputLayer.Delta().slices(
            outputmap * inputLayer.LayerSlices(),
            (outputmap + 1) * inputLayer.LayerSlices() - 1);

        arma::Cube<eT> output;
        GradientConvolutionRule::Convolution(inputSlices, deltaSlices, output);

        for (size_t i = 0; i < output.n_slices; i++)
          gradient.slice(s) += output.slice(i);

        gradient.slice(s) /= inputLayer.LayerSlices();
      }
    }
  }

  //! Get the convolution kernel.
  DataType& Weights() const { return weights; }
  //! Modify the convolution kernel.
  DataType& Weights() { return weights; }

  //! Get the input layer.
  InputLayerType& InputLayer() const { return inputLayer; }
  //! Modify the input layer.
  InputLayerType& InputLayer() { return inputLayer; }

  //! Get the output layer.
  OutputLayerType& OutputLayer() const { return outputLayer; }
  //! Modify the output layer.
  OutputLayerType& OutputLayer() { return outputLayer; }

  //! Get the optimzer.
  OptimizerType<ConvConnection<InputLayerType,
                               OutputLayerType,
                               OptimizerType,
                               WeightInitRule,
                               ForwardConvolutionRule,
                               BackwardConvolutionRule,
                               GradientConvolutionRule,
                               DataType>, DataType>& Optimzer() const
  {
    return *optimizer;
  }
  //! Modify the optimzer.
  OptimizerType<ConvConnection<InputLayerType,
                               OutputLayerType,
                               OptimizerType,
                               WeightInitRule,
                               ForwardConvolutionRule,
                               BackwardConvolutionRule,
                               GradientConvolutionRule,
                               DataType>, DataType>& Optimzer()
  {
    return *optimizer;
  }

  //! Get the passed error in backward propagation.
  DataType& Delta() const { return delta; }
  //! Modify the passed error in backward propagation.
  DataType& Delta() { return delta; }

 private:
  /*
   * Rotates a 3rd-order tesor counterclockwise by 180 degrees.
   *
   * @param input The input data to be rotated.
   * @param output The rotated output.
   */
  template<typename eT>
  void Rotate180(const arma::Cube<eT>& input, arma::Cube<eT>& output)
  {
    output = arma::Cube<eT>(input.n_rows, input.n_cols, input.n_slices);

    // * left-right flip, up-down flip */
    for (size_t s = 0; s < output.n_slices; s++)
      output.slice(s) = arma::fliplr(arma::flipud(input.slice(s)));
  }

  /*
   * Rotates a dense matrix counterclockwise by 180 degrees.
   *
   * @param input The input data to be rotated.
   * @param output The rotated output.
   */
  template<typename eT>
  void Rotate180(const arma::Mat<eT>& input, arma::Mat<eT>& output)
  {
    // * left-right flip, up-down flip */
    output = arma::fliplr(arma::flipud(input));
  }

  //! Locally-stored kernel weights.
  DataType weights;

  //! Locally-stored inputlayer.
  InputLayerType& inputLayer;

  //! Locally-stored outputlayer.
  OutputLayerType& outputLayer;

  //! Locally-stored optimizer.
  OptimizerType<ConvConnection<InputLayerType,
                               OutputLayerType,
                               OptimizerType,
                               WeightInitRule,
                               ForwardConvolutionRule,
                               BackwardConvolutionRule,
                               GradientConvolutionRule,
                               DataType>, DataType>* optimizer;

  //! Parameter that indicates if the class owns a optimizer object.
  bool ownsOptimizer;

  //! Locally-stored passed error in backward propagation.
  DataType delta;
};// class ConvConnection

}; // namespace ann
}; // namespace mlpack

#endif<|endoftext|>


<commit_before>#include "../tests.h"

// Test vectors in kernel

#include <sstream>

template <class T>
std::string to_string(const T& t) {
	std::stringstream s;
	s << '(' << t.x() << ',' << t.y() << ',' << t.z() << ')';
	return s.str();
}

bool test10() {

	using namespace cl::sycl;
	using namespace std;

	cpu_selector gpu;
	queue myQueue(gpu);

	const int size = 10;
	cl::sycl::cl_float3 testVector;
	testVector.x() = 1;
	testVector.y() = 2;
	testVector.z() = 3;
	buffer<float3> vectors(size);

	myQueue.submit([&](handler& cgh) {
		auto v = vectors.get_access<access::mode::discard_write>(cgh);

		cgh.parallel_for<class addition>(range<1>(size), [=](id<> i) {
			v[i] = float3(testVector.x(), testVector.y(), testVector.z());
		});
	});

	auto v = vectors.get_access<access::mode::read, access::target::host_buffer>();

	auto floatEqual = [](float& first, float& second) {
		static const double eps = 1e5f;
		return first > second - eps && first < second + eps;
	};

	for(auto i = 0; i < size; ++i) {
		auto vi = v[i];
		if(	!floatEqual(vi.x(), testVector.x()) ||
			!floatEqual(vi.y(), testVector.y()) ||
			!floatEqual(vi.z(), testVector.z())
		) {
			cout << i << " -> expected " << to_string(testVector) << ", got " << to_string(vi) << endl;
			return false;
		}
	}

	return true;
}
<commit_msg>tests - fix comparison interval in test10, add twist to assigning third element of float3<commit_after>#include "../tests.h"

// Test vectors in kernel

#include <sstream>

template <class T>
std::string to_string(const T& t) {
	std::stringstream s;
	s << '(' << t.x() << ',' << t.y() << ',' << t.z() << ')';
	return s.str();
}

bool test10() {

	using namespace cl::sycl;
	using namespace std;

	cpu_selector gpu;
	queue myQueue(gpu);

	const int size = 10;
	cl::sycl::cl_float3 testVector;
	testVector.x() = 1;
	testVector.y() = 2;
	testVector.z() = 3;
	buffer<float3> vectors(size);

	myQueue.submit([&](handler& cgh) {
		auto v = vectors.get_access<access::mode::discard_write>(cgh);

		cgh.parallel_for<class addition>(range<1>(size), [=](id<> i) {
			v[i] = float3(testVector.x(), testVector.y(), 0);
			v[i].z() = testVector.z();
		});
	});

	auto v = vectors.get_access<access::mode::read, access::target::host_buffer>();

	auto floatEqual = [](float& first, float& second) {
		static const double eps = 1e-5f;
		return first > second - eps && first < second + eps;
	};

	for(auto i = 0; i < size; ++i) {
		auto vi = v[i];
		if(	!floatEqual(vi.x(), testVector.x()) ||
			!floatEqual(vi.y(), testVector.y()) ||
			!floatEqual(vi.z(), testVector.z())
		) {
			cout << i << " -> expected " << to_string(testVector) << ", got " << to_string(vi) << endl;
			return false;
		}
	}

	return true;
}
<|endoftext|>


<commit_before>#include "net/AsyncHttp.h"
#include "common/StringConverter.h"
#include "net/URI.h"
#include "common/RhodesApp.h"
#include "common/RhoAppAdapter.h"

namespace rho
{
using namespace rho::common;
namespace net
{

IMPLEMENT_LOGCLASS(CAsyncHttp, "AsyncHttp");
CAsyncHttp* CAsyncHttp::m_pInstance = 0;

/*static*/ CAsyncHttp* CAsyncHttp::Create()
{
    if ( m_pInstance )
        return m_pInstance;

    m_pInstance = new CAsyncHttp( rho_impl_createClassFactory());
    return m_pInstance;
}

/*static*/void CAsyncHttp::Destroy()
{
    if ( m_pInstance )
        delete m_pInstance;

    m_pInstance = 0;
}

CAsyncHttp::CAsyncHttp(common::IRhoClassFactory* factory) : CThreadQueue(factory)
{
    CThreadQueue::setLogCategory(getLogCategory());

    setPollInterval(QUEUE_POLL_INTERVAL_INFINITE);

    m_pCurCmd = null;
}

CAsyncHttp::~CAsyncHttp(void)
{
    cancelRequest("*", true);
    LOG(INFO) + "Thread shutdown";
}

void CAsyncHttp::addQueueCommand(IQueueCommand* pCmd)
{
    if ( ((CHttpCommand*)pCmd)->m_strCallback.length()==0)
        processCommand(pCmd);
    else
    {
        CThreadQueue::addQueueCommand(pCmd);
        start(epLow);
    }
}

void CAsyncHttp::cancelRequest(const char* szCallback, boolean bWait)
{
    if (!szCallback || !*szCallback )
    {
        LOG(INFO) + "Cancel callback should not be empty. Use * for cancel all";
        return;
    }

    if ( m_pCurCmd != null )
        m_pCurCmd->cancel();

    if ( bWait )
        stop(-1);

    //TODO: find command by callback and cancel it if current, remove if it is still in queue
}

void CAsyncHttp::processCommand(IQueueCommand* pCmd)
{
    m_pCurCmd = (CHttpCommand*)pCmd;
    m_pCurCmd->execute();
    m_pCurCmd = null;
}

extern "C" void header_iter(const char* szName, const char* szValue, void* pHash)
{
    ((Hashtable<String,String>*)pHash)->put(szName, szValue);
}

CAsyncHttp::CHttpCommand::CHttpCommand(String strCmd, rho_param *p) : m_params(p)
{
    m_eCmd = translateCommand(strCmd);
    m_strCallback = m_params.getString("callback");
    m_strCallbackParams = m_params.getString("callback_param");

    m_params.getHash("headers", m_mapHeaders);

    m_pNetRequest = CAsyncHttp::getInstance()->getFactory()->createNetRequest();
    m_pNetRequest->sslVerifyPeer(m_params.getBool("ssl_verify_peer"));

}

void CAsyncHttp::CHttpCommand::cancel()
{
    if (m_pNetRequest!=null && !m_pNetRequest->isCancelled() )
        m_pNetRequest->cancel();
}

void CAsyncHttp::CHttpCommand::execute()
{
    INetResponse* resp = null;
    switch( m_eCmd )
    {
    case hcGet:
        resp = m_pNetRequest->doRequest( m_params.getString("http_command", "GET").c_str(),
            m_params.getString("url"), m_params.getString("body"), null, &m_mapHeaders);
        break;
    case hcPost:
        resp = m_pNetRequest->doRequest(m_params.getString("http_command", "POST").c_str(),
            m_params.getString("url"), m_params.getString("body"), null, &m_mapHeaders);
        break;

    case hcDownload:
        resp = m_pNetRequest->pullFile(m_params.getString("url"), m_params.getString("filename"), null, &m_mapHeaders);
        break;

    case hcUpload:
        {
            VectorPtr<net::CMultipartItem*> arMultipartItems;

            CRhoParamArray arParams( m_params, "multipart");
            if ( arParams.size() > 0 )
            {
                for( int i = 0; i < arParams.size(); i++)
                {
                    CRhoParams oItem = arParams.getItem(i);

                    net::CMultipartItem* pItem = new net::CMultipartItem();
                    String strBody = oItem.getString("body");
                    if ( strBody.length() > 0 )
                    {
                        pItem->m_strBody = strBody;
                        pItem->m_strContentType = oItem.getString("content_type", "application/x-www-form-urlencoded");
                    }
                    else
                    {
                        pItem->m_strFilePath = oItem.getString("filename");
                        pItem->m_strContentType = oItem.getString("content_type", "application/octet-stream");
                    }

                    pItem->m_strName = oItem.getString("name");
                    pItem->m_strFileName = oItem.getString("filename_base");
                    arMultipartItems.addElement(pItem);
                }
            }else
            {
                net::CMultipartItem* pItem = new net::CMultipartItem();
                pItem->m_strFilePath = m_params.getString("filename");
                pItem->m_strContentType = m_params.getString("file_content_type", "application/octet-stream");
                pItem->m_strName = m_params.getString("name");
                pItem->m_strFileName = m_params.getString("filename_base");
                arMultipartItems.addElement(pItem);

                String strBody = m_params.getString("body");
                if ( strBody.length() > 0 )
                {
                    net::CMultipartItem* pItem2 = new net::CMultipartItem();
                    pItem2->m_strBody = strBody;
                    pItem2->m_strContentType = m_mapHeaders.get("content-type");
                    arMultipartItems.addElement(pItem2);
                }
            }

            resp = m_pNetRequest->pushMultipartData( m_params.getString("url"), arMultipartItems, null, &m_mapHeaders );
            break;
        }
    }

    if ( !m_pNetRequest->isCancelled())
        callNotify(resp,0);

    m_params.free_params();
}

unsigned long CAsyncHttp::CHttpCommand::getRetValue()
{
    if ( m_strCallback.length() == 0 )
        return rho_ruby_create_string(m_strResBody.c_str());

    return rho_ruby_get_NIL();
}

String CAsyncHttp::CHttpCommand::makeHeadersString()
{
    String strRes = "";

    for ( Hashtable<String,String>::iterator it = m_mapHeaders.begin();  it != m_mapHeaders.end(); ++it )
    {
        if ( strRes.length() > 0)
            strRes += "&";

        strRes += "headers[";
        URI::urlEncode(it->first,strRes);
        strRes += "]=";
        URI::urlEncode(it->second,strRes);
    }

    return strRes;
}

void CAsyncHttp::CHttpCommand::callNotify(rho::net::INetResponse* pResp, int nError )
{
    rho::net::INetResponse& resp = *pResp;

    m_strResBody = "rho_callback=1";
    m_strResBody += "&status=";
    if ( nError > 0 )
    {
    	m_strResBody += "error&error_code=" + nError;
    }else
    {
        if ( resp.isOK() )
    	    m_strResBody += "ok";
        else
        {
    	    m_strResBody += "error&error_code=";
            m_strResBody += convertToStringA(RhoAppAdapter.getErrorFromResponse(resp));
            if ( resp.isResponseRecieved())
	            m_strResBody += "&http_error=" + convertToStringA(resp.getRespCode());
        }

        String cookies = resp.getCookies();
        if (cookies.length()>0)
            m_strResBody += "&cookies=" + URI::urlEncode(cookies);

        String strHeaders = makeHeadersString();
        if (strHeaders.length() > 0 )
            m_strResBody += "&" + strHeaders;

        m_strResBody += "&" + RHODESAPP().addCallbackObject(
            new CAsyncHttpResponse(pResp, m_mapHeaders.get("content-type")), "body");
    }

    if ( m_strCallbackParams.length() > 0 )
        m_strResBody += "&" + m_strCallbackParams;

    if ( m_strCallback.length() > 0 )
    {
        String strFullUrl = m_pNetRequest->resolveUrl(m_strCallback);
        m_pNetRequest->pushData( strFullUrl, m_strResBody, null );
    }
}

CAsyncHttp::CAsyncHttpResponse::~CAsyncHttpResponse(){}

extern "C" VALUE rjson_tokener_parse(const char *str, char** pszError );
unsigned long CAsyncHttp::CAsyncHttpResponse::getObjectValue()
{
    rho::net::INetResponse& resp = *m_pNetResponse;
    if (resp.isOK())
    {
        if ( m_strContentType.find("application/json") != String::npos )
        {
            char* szError = 0;
            unsigned long valBody = rjson_tokener_parse(resp.getCharData(), &szError);
            if ( valBody != 0 )
                return valBody;

            LOG(ERROR) + "Incorrect json body.Error:" + (szError ? szError : "");
            if ( szError )
                free(szError);
        }
    }

    return rho_ruby_create_string(resp.getCharData());
}

} // namespace net
} // namespace rho

extern "C" {

using namespace rho::net;

void rho_asynchttp_cancel(const char* cancel_callback)
{
    if ( CAsyncHttp::getInstance() != null )
        CAsyncHttp::getInstance()->cancelRequest(cancel_callback, false);
}

unsigned long rho_asynchttp_request(const char* command, rho_param *p)
{
    CAsyncHttp::Create();

    CAsyncHttp::CHttpCommand* pHttp = new CAsyncHttp::CHttpCommand( command, p );
    CAsyncHttp::getInstance()->addQueueCommand(pHttp);
    return pHttp->getRetValue();
}

}
<commit_msg>remove default Content-type in multipart request<commit_after>#include "net/AsyncHttp.h"
#include "common/StringConverter.h"
#include "net/URI.h"
#include "common/RhodesApp.h"
#include "common/RhoAppAdapter.h"

namespace rho
{
using namespace rho::common;
namespace net
{

IMPLEMENT_LOGCLASS(CAsyncHttp, "AsyncHttp");
CAsyncHttp* CAsyncHttp::m_pInstance = 0;

/*static*/ CAsyncHttp* CAsyncHttp::Create()
{
    if ( m_pInstance )
        return m_pInstance;

    m_pInstance = new CAsyncHttp( rho_impl_createClassFactory());
    return m_pInstance;
}

/*static*/void CAsyncHttp::Destroy()
{
    if ( m_pInstance )
        delete m_pInstance;

    m_pInstance = 0;
}

CAsyncHttp::CAsyncHttp(common::IRhoClassFactory* factory) : CThreadQueue(factory)
{
    CThreadQueue::setLogCategory(getLogCategory());

    setPollInterval(QUEUE_POLL_INTERVAL_INFINITE);

    m_pCurCmd = null;
}

CAsyncHttp::~CAsyncHttp(void)
{
    cancelRequest("*", true);
    LOG(INFO) + "Thread shutdown";
}

void CAsyncHttp::addQueueCommand(IQueueCommand* pCmd)
{
    if ( ((CHttpCommand*)pCmd)->m_strCallback.length()==0)
        processCommand(pCmd);
    else
    {
        CThreadQueue::addQueueCommand(pCmd);
        start(epLow);
    }
}

void CAsyncHttp::cancelRequest(const char* szCallback, boolean bWait)
{
    if (!szCallback || !*szCallback )
    {
        LOG(INFO) + "Cancel callback should not be empty. Use * for cancel all";
        return;
    }

    if ( m_pCurCmd != null )
        m_pCurCmd->cancel();

    if ( bWait )
        stop(-1);

    //TODO: find command by callback and cancel it if current, remove if it is still in queue
}

void CAsyncHttp::processCommand(IQueueCommand* pCmd)
{
    m_pCurCmd = (CHttpCommand*)pCmd;
    m_pCurCmd->execute();
    m_pCurCmd = null;
}

extern "C" void header_iter(const char* szName, const char* szValue, void* pHash)
{
    ((Hashtable<String,String>*)pHash)->put(szName, szValue);
}

CAsyncHttp::CHttpCommand::CHttpCommand(String strCmd, rho_param *p) : m_params(p)
{
    m_eCmd = translateCommand(strCmd);
    m_strCallback = m_params.getString("callback");
    m_strCallbackParams = m_params.getString("callback_param");

    m_params.getHash("headers", m_mapHeaders);

    m_pNetRequest = CAsyncHttp::getInstance()->getFactory()->createNetRequest();
    m_pNetRequest->sslVerifyPeer(m_params.getBool("ssl_verify_peer"));

}

void CAsyncHttp::CHttpCommand::cancel()
{
    if (m_pNetRequest!=null && !m_pNetRequest->isCancelled() )
        m_pNetRequest->cancel();
}

void CAsyncHttp::CHttpCommand::execute()
{
    INetResponse* resp = null;
    switch( m_eCmd )
    {
    case hcGet:
        resp = m_pNetRequest->doRequest( m_params.getString("http_command", "GET").c_str(),
            m_params.getString("url"), m_params.getString("body"), null, &m_mapHeaders);
        break;
    case hcPost:
        resp = m_pNetRequest->doRequest(m_params.getString("http_command", "POST").c_str(),
            m_params.getString("url"), m_params.getString("body"), null, &m_mapHeaders);
        break;

    case hcDownload:
        resp = m_pNetRequest->pullFile(m_params.getString("url"), m_params.getString("filename"), null, &m_mapHeaders);
        break;

    case hcUpload:
        {
            VectorPtr<net::CMultipartItem*> arMultipartItems;

            CRhoParamArray arParams( m_params, "multipart");
            if ( arParams.size() > 0 )
            {
                for( int i = 0; i < arParams.size(); i++)
                {
                    CRhoParams oItem = arParams.getItem(i);

                    net::CMultipartItem* pItem = new net::CMultipartItem();
                    String strBody = oItem.getString("body");
                    if ( strBody.length() > 0 )
                    {
                        pItem->m_strBody = strBody;
                        pItem->m_strContentType = oItem.getString("content_type", "");
                    }
                    else
                    {
                        pItem->m_strFilePath = oItem.getString("filename");
                        pItem->m_strContentType = oItem.getString("content_type", "application/octet-stream");
                    }

                    pItem->m_strName = oItem.getString("name");
                    pItem->m_strFileName = oItem.getString("filename_base");
                    arMultipartItems.addElement(pItem);
                }
            }else
            {
                net::CMultipartItem* pItem = new net::CMultipartItem();
                pItem->m_strFilePath = m_params.getString("filename");
                pItem->m_strContentType = m_params.getString("file_content_type", "application/octet-stream");
                pItem->m_strName = m_params.getString("name");
                pItem->m_strFileName = m_params.getString("filename_base");
                arMultipartItems.addElement(pItem);

                String strBody = m_params.getString("body");
                if ( strBody.length() > 0 )
                {
                    net::CMultipartItem* pItem2 = new net::CMultipartItem();
                    pItem2->m_strBody = strBody;
                    pItem2->m_strContentType = m_mapHeaders.get("content-type");
                    arMultipartItems.addElement(pItem2);
                }
            }

            resp = m_pNetRequest->pushMultipartData( m_params.getString("url"), arMultipartItems, null, &m_mapHeaders );
            break;
        }
    }

    if ( !m_pNetRequest->isCancelled())
        callNotify(resp,0);

    m_params.free_params();
}

unsigned long CAsyncHttp::CHttpCommand::getRetValue()
{
    if ( m_strCallback.length() == 0 )
        return rho_ruby_create_string(m_strResBody.c_str());

    return rho_ruby_get_NIL();
}

String CAsyncHttp::CHttpCommand::makeHeadersString()
{
    String strRes = "";

    for ( Hashtable<String,String>::iterator it = m_mapHeaders.begin();  it != m_mapHeaders.end(); ++it )
    {
        if ( strRes.length() > 0)
            strRes += "&";

        strRes += "headers[";
        URI::urlEncode(it->first,strRes);
        strRes += "]=";
        URI::urlEncode(it->second,strRes);
    }

    return strRes;
}

void CAsyncHttp::CHttpCommand::callNotify(rho::net::INetResponse* pResp, int nError )
{
    rho::net::INetResponse& resp = *pResp;

    m_strResBody = "rho_callback=1";
    m_strResBody += "&status=";
    if ( nError > 0 )
    {
    	m_strResBody += "error&error_code=" + nError;
    }else
    {
        if ( resp.isOK() )
    	    m_strResBody += "ok";
        else
        {
    	    m_strResBody += "error&error_code=";
            m_strResBody += convertToStringA(RhoAppAdapter.getErrorFromResponse(resp));
            if ( resp.isResponseRecieved())
	            m_strResBody += "&http_error=" + convertToStringA(resp.getRespCode());
        }

        String cookies = resp.getCookies();
        if (cookies.length()>0)
            m_strResBody += "&cookies=" + URI::urlEncode(cookies);

        String strHeaders = makeHeadersString();
        if (strHeaders.length() > 0 )
            m_strResBody += "&" + strHeaders;

        m_strResBody += "&" + RHODESAPP().addCallbackObject(
            new CAsyncHttpResponse(pResp, m_mapHeaders.get("content-type")), "body");
    }

    if ( m_strCallbackParams.length() > 0 )
        m_strResBody += "&" + m_strCallbackParams;

    if ( m_strCallback.length() > 0 )
    {
        String strFullUrl = m_pNetRequest->resolveUrl(m_strCallback);
        m_pNetRequest->pushData( strFullUrl, m_strResBody, null );
    }
}

CAsyncHttp::CAsyncHttpResponse::~CAsyncHttpResponse(){}

extern "C" VALUE rjson_tokener_parse(const char *str, char** pszError );
unsigned long CAsyncHttp::CAsyncHttpResponse::getObjectValue()
{
    rho::net::INetResponse& resp = *m_pNetResponse;
    if (resp.isOK())
    {
        if ( m_strContentType.find("application/json") != String::npos )
        {
            char* szError = 0;
            unsigned long valBody = rjson_tokener_parse(resp.getCharData(), &szError);
            if ( valBody != 0 )
                return valBody;

            LOG(ERROR) + "Incorrect json body.Error:" + (szError ? szError : "");
            if ( szError )
                free(szError);
        }
    }

    return rho_ruby_create_string(resp.getCharData());
}

} // namespace net
} // namespace rho

extern "C" {

using namespace rho::net;

void rho_asynchttp_cancel(const char* cancel_callback)
{
    if ( CAsyncHttp::getInstance() != null )
        CAsyncHttp::getInstance()->cancelRequest(cancel_callback, false);
}

unsigned long rho_asynchttp_request(const char* command, rho_param *p)
{
    CAsyncHttp::Create();

    CAsyncHttp::CHttpCommand* pHttp = new CAsyncHttp::CHttpCommand( command, p );
    CAsyncHttp::getInstance()->addQueueCommand(pHttp);
    return pHttp->getRetValue();
}

}
<|endoftext|>


<commit_before>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/speech/extension_api/tts_extension_api_chromeos.h"

#include <list>

#include "base/bind.h"
#include "base/memory/singleton.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop.h"
#include "base/stl_util.h"
#include "base/string_number_conversions.h"
#include "chrome/browser/speech/extension_api/tts_extension_api_controller.h"
#include "chrome/browser/speech/extension_api/tts_extension_api_platform.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/speech_synthesizer_client.h"

using base::DoubleToString;

namespace {
const int kSpeechCheckDelayIntervalMs = 100;
};

// Very simple queue of utterances that can't be spoken because audio
// isn't initialized yet.
struct QueuedUtterance {
  int utterance_id;
  std::string utterance;
  std::string lang;
  UtteranceContinuousParameters params;
};

class ExtensionTtsPlatformImplChromeOs
    : public ExtensionTtsPlatformImpl {
 public:
  virtual bool PlatformImplAvailable() {
    return true;
  }

  virtual bool Speak(
      int utterance_id,
      const std::string& utterance,
      const std::string& lang,
      const UtteranceContinuousParameters& params);

  virtual bool StopSpeaking();

  virtual bool IsSpeaking();

  virtual bool SendsEvent(TtsEventType event_type);

  // Get the single instance of this class.
  static ExtensionTtsPlatformImplChromeOs* GetInstance();

  // TTS won't begin until this is called.
  void Enable();

 private:
  ExtensionTtsPlatformImplChromeOs();
  virtual ~ExtensionTtsPlatformImplChromeOs() {}

  void PollUntilSpeechFinishes(int utterance_id);
  void ContinuePollingIfSpeechIsNotFinished(int utterance_id, bool result);

  void AppendSpeakOption(std::string key,
                         std::string value,
                         std::string* options);

  int utterance_id_;
  int utterance_length_;
  std::list<QueuedUtterance*> queued_utterances_;
  bool enabled_;
  base::WeakPtrFactory<ExtensionTtsPlatformImplChromeOs> weak_ptr_factory_;

  friend struct DefaultSingletonTraits<ExtensionTtsPlatformImplChromeOs>;

  DISALLOW_COPY_AND_ASSIGN(ExtensionTtsPlatformImplChromeOs);
};

// static
ExtensionTtsPlatformImpl* ExtensionTtsPlatformImpl::GetInstance() {
  return ExtensionTtsPlatformImplChromeOs::GetInstance();
}

ExtensionTtsPlatformImplChromeOs::ExtensionTtsPlatformImplChromeOs()
    : enabled_(false),
      ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) {
}

bool ExtensionTtsPlatformImplChromeOs::Speak(
    int utterance_id,
    const std::string& utterance,
    const std::string& lang,
    const UtteranceContinuousParameters& params) {
  if (!enabled_) {
    QueuedUtterance *queued = new QueuedUtterance();
    queued->utterance_id = utterance_id;
    queued->utterance = utterance;
    queued->lang = lang;
    queued->params = params;
    queued_utterances_.push_back(queued);
    return true;
  }

  utterance_id_ = utterance_id;
  utterance_length_ = utterance.size();

  std::string options;

  if (!lang.empty()) {
    AppendSpeakOption(
        chromeos::SpeechSynthesizerClient::kSpeechPropertyLocale,
        lang,
        &options);
  }

  if (params.rate >= 0.0) {
    AppendSpeakOption(
        chromeos::SpeechSynthesizerClient::kSpeechPropertyRate,
        DoubleToString(params.rate),
        &options);
  }

  if (params.pitch >= 0.0) {
    // The TTS service allows a range of 0 to 2 for speech pitch.
    AppendSpeakOption(
        chromeos::SpeechSynthesizerClient::kSpeechPropertyPitch,
        DoubleToString(params.pitch),
        &options);
  }

  if (params.volume >= 0.0) {
    // The Chrome OS TTS service allows a range of 0 to 5 for speech volume,
    // but 5 clips, so map to a range of 0...4.
    AppendSpeakOption(
        chromeos::SpeechSynthesizerClient::kSpeechPropertyVolume,
        DoubleToString(params.volume * 4),
        &options);
  }

  chromeos::SpeechSynthesizerClient* speech_synthesizer_client =
      chromeos::DBusThreadManager::Get()->GetSpeechSynthesizerClient();

  speech_synthesizer_client->Speak(utterance, options);
  if (utterance_id_ >= 0) {
    ExtensionTtsController* controller = ExtensionTtsController::GetInstance();
    controller->OnTtsEvent(utterance_id_, TTS_EVENT_START, 0, std::string());
    PollUntilSpeechFinishes(utterance_id_);
  }

  return true;
}

bool ExtensionTtsPlatformImplChromeOs::StopSpeaking() {
  // If we haven't been enabled yet, clear the internal queue.
  if (!enabled_) {
    STLDeleteElements(&queued_utterances_);
    return true;
  }

  chromeos::DBusThreadManager::Get()->GetSpeechSynthesizerClient()->
      StopSpeaking();
  return true;
}

bool ExtensionTtsPlatformImplChromeOs::IsSpeaking() {
  // Defers to controller's utterance based detection of is speaking.
  return true;
}

bool ExtensionTtsPlatformImplChromeOs::SendsEvent(TtsEventType event_type) {
  return (event_type == TTS_EVENT_START ||
          event_type == TTS_EVENT_END ||
          event_type == TTS_EVENT_ERROR);
}

void ExtensionTtsPlatformImplChromeOs::Enable() {
  enabled_ = true;
  while (!queued_utterances_.empty()) {
    QueuedUtterance* queued = queued_utterances_.front();
    Speak(queued->utterance_id,
          queued->utterance,
          queued->lang,
          queued->params);
    delete queued;
    queued_utterances_.pop_front();
  }
}

void ExtensionTtsPlatformImplChromeOs::PollUntilSpeechFinishes(
    int utterance_id) {
  if (utterance_id != utterance_id_) {
    // This utterance must have been interrupted or cancelled.
    return;
  }
  chromeos::SpeechSynthesizerClient* speech_synthesizer_client =
      chromeos::DBusThreadManager::Get()->GetSpeechSynthesizerClient();
  speech_synthesizer_client->IsSpeaking(base::Bind(
      &ExtensionTtsPlatformImplChromeOs::ContinuePollingIfSpeechIsNotFinished,
      weak_ptr_factory_.GetWeakPtr(), utterance_id));
}

void ExtensionTtsPlatformImplChromeOs::ContinuePollingIfSpeechIsNotFinished(
    int utterance_id, bool is_speaking) {
  if (utterance_id != utterance_id_) {
    // This utterance must have been interrupted or cancelled.
    return;
  }
  if (!is_speaking) {
    ExtensionTtsController* controller = ExtensionTtsController::GetInstance();
    controller->OnTtsEvent(
        utterance_id_, TTS_EVENT_END, utterance_length_, std::string());
    return;
  }
  // Continue polling.
  MessageLoop::current()->PostDelayedTask(
      FROM_HERE, base::Bind(
          &ExtensionTtsPlatformImplChromeOs::PollUntilSpeechFinishes,
          weak_ptr_factory_.GetWeakPtr(),
          utterance_id),
      base::TimeDelta::FromMilliseconds(kSpeechCheckDelayIntervalMs));
}

void ExtensionTtsPlatformImplChromeOs::AppendSpeakOption(
    std::string key,
    std::string value,
    std::string* options) {
  *options +=
      key +
      chromeos::SpeechSynthesizerClient::kSpeechPropertyEquals +
      value +
      chromeos::SpeechSynthesizerClient::kSpeechPropertyDelimiter;
}

// static
ExtensionTtsPlatformImplChromeOs*
ExtensionTtsPlatformImplChromeOs::GetInstance() {
  return Singleton<ExtensionTtsPlatformImplChromeOs>::get();
}

// global
void EnableChromeOsTts() {
  ExtensionTtsPlatformImplChromeOs::GetInstance()->Enable();
}
<commit_msg>Chrome OS TTS API should not report it's speaking when it's not.<commit_after>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/speech/extension_api/tts_extension_api_chromeos.h"

#include <list>

#include "base/bind.h"
#include "base/memory/singleton.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop.h"
#include "base/stl_util.h"
#include "base/string_number_conversions.h"
#include "chrome/browser/speech/extension_api/tts_extension_api_controller.h"
#include "chrome/browser/speech/extension_api/tts_extension_api_platform.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/speech_synthesizer_client.h"

using base::DoubleToString;

namespace {
const int kSpeechCheckDelayIntervalMs = 100;
};

// Very simple queue of utterances that can't be spoken because audio
// isn't initialized yet.
struct QueuedUtterance {
  int utterance_id;
  std::string utterance;
  std::string lang;
  UtteranceContinuousParameters params;
};

class ExtensionTtsPlatformImplChromeOs
    : public ExtensionTtsPlatformImpl {
 public:
  virtual bool PlatformImplAvailable() {
    return true;
  }

  virtual bool Speak(
      int utterance_id,
      const std::string& utterance,
      const std::string& lang,
      const UtteranceContinuousParameters& params);

  virtual bool StopSpeaking();

  virtual bool IsSpeaking();

  virtual bool SendsEvent(TtsEventType event_type);

  // Get the single instance of this class.
  static ExtensionTtsPlatformImplChromeOs* GetInstance();

  // TTS won't begin until this is called.
  void Enable();

 private:
  ExtensionTtsPlatformImplChromeOs();
  virtual ~ExtensionTtsPlatformImplChromeOs() {}

  void PollUntilSpeechFinishes(int utterance_id);
  void ContinuePollingIfSpeechIsNotFinished(int utterance_id, bool result);

  void AppendSpeakOption(std::string key,
                         std::string value,
                         std::string* options);

  int utterance_id_;
  int utterance_length_;
  std::list<QueuedUtterance*> queued_utterances_;
  bool enabled_;
  base::WeakPtrFactory<ExtensionTtsPlatformImplChromeOs> weak_ptr_factory_;

  friend struct DefaultSingletonTraits<ExtensionTtsPlatformImplChromeOs>;

  DISALLOW_COPY_AND_ASSIGN(ExtensionTtsPlatformImplChromeOs);
};

// static
ExtensionTtsPlatformImpl* ExtensionTtsPlatformImpl::GetInstance() {
  return ExtensionTtsPlatformImplChromeOs::GetInstance();
}

ExtensionTtsPlatformImplChromeOs::ExtensionTtsPlatformImplChromeOs()
    : enabled_(false),
      ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) {
}

bool ExtensionTtsPlatformImplChromeOs::Speak(
    int utterance_id,
    const std::string& utterance,
    const std::string& lang,
    const UtteranceContinuousParameters& params) {
  if (!enabled_) {
    QueuedUtterance *queued = new QueuedUtterance();
    queued->utterance_id = utterance_id;
    queued->utterance = utterance;
    queued->lang = lang;
    queued->params = params;
    queued_utterances_.push_back(queued);
    return true;
  }

  utterance_id_ = utterance_id;
  utterance_length_ = utterance.size();

  std::string options;

  if (!lang.empty()) {
    AppendSpeakOption(
        chromeos::SpeechSynthesizerClient::kSpeechPropertyLocale,
        lang,
        &options);
  }

  if (params.rate >= 0.0) {
    AppendSpeakOption(
        chromeos::SpeechSynthesizerClient::kSpeechPropertyRate,
        DoubleToString(params.rate),
        &options);
  }

  if (params.pitch >= 0.0) {
    // The TTS service allows a range of 0 to 2 for speech pitch.
    AppendSpeakOption(
        chromeos::SpeechSynthesizerClient::kSpeechPropertyPitch,
        DoubleToString(params.pitch),
        &options);
  }

  if (params.volume >= 0.0) {
    // The Chrome OS TTS service allows a range of 0 to 5 for speech volume,
    // but 5 clips, so map to a range of 0...4.
    AppendSpeakOption(
        chromeos::SpeechSynthesizerClient::kSpeechPropertyVolume,
        DoubleToString(params.volume * 4),
        &options);
  }

  chromeos::SpeechSynthesizerClient* speech_synthesizer_client =
      chromeos::DBusThreadManager::Get()->GetSpeechSynthesizerClient();

  speech_synthesizer_client->Speak(utterance, options);
  if (utterance_id_ >= 0) {
    ExtensionTtsController* controller = ExtensionTtsController::GetInstance();
    controller->OnTtsEvent(utterance_id_, TTS_EVENT_START, 0, std::string());
    PollUntilSpeechFinishes(utterance_id_);
  }

  return true;
}

bool ExtensionTtsPlatformImplChromeOs::StopSpeaking() {
  // If we haven't been enabled yet, clear the internal queue.
  if (!enabled_) {
    STLDeleteElements(&queued_utterances_);
    return true;
  }

  chromeos::DBusThreadManager::Get()->GetSpeechSynthesizerClient()->
      StopSpeaking();
  return true;
}

bool ExtensionTtsPlatformImplChromeOs::IsSpeaking() {
  // Defers to controller's utterance based detection of is speaking.
  return false;
}

bool ExtensionTtsPlatformImplChromeOs::SendsEvent(TtsEventType event_type) {
  return (event_type == TTS_EVENT_START ||
          event_type == TTS_EVENT_END ||
          event_type == TTS_EVENT_ERROR);
}

void ExtensionTtsPlatformImplChromeOs::Enable() {
  enabled_ = true;
  while (!queued_utterances_.empty()) {
    QueuedUtterance* queued = queued_utterances_.front();
    Speak(queued->utterance_id,
          queued->utterance,
          queued->lang,
          queued->params);
    delete queued;
    queued_utterances_.pop_front();
  }
}

void ExtensionTtsPlatformImplChromeOs::PollUntilSpeechFinishes(
    int utterance_id) {
  if (utterance_id != utterance_id_) {
    // This utterance must have been interrupted or cancelled.
    return;
  }
  chromeos::SpeechSynthesizerClient* speech_synthesizer_client =
      chromeos::DBusThreadManager::Get()->GetSpeechSynthesizerClient();
  speech_synthesizer_client->IsSpeaking(base::Bind(
      &ExtensionTtsPlatformImplChromeOs::ContinuePollingIfSpeechIsNotFinished,
      weak_ptr_factory_.GetWeakPtr(), utterance_id));
}

void ExtensionTtsPlatformImplChromeOs::ContinuePollingIfSpeechIsNotFinished(
    int utterance_id, bool is_speaking) {
  if (utterance_id != utterance_id_) {
    // This utterance must have been interrupted or cancelled.
    return;
  }
  if (!is_speaking) {
    ExtensionTtsController* controller = ExtensionTtsController::GetInstance();
    controller->OnTtsEvent(
        utterance_id_, TTS_EVENT_END, utterance_length_, std::string());
    return;
  }
  // Continue polling.
  MessageLoop::current()->PostDelayedTask(
      FROM_HERE, base::Bind(
          &ExtensionTtsPlatformImplChromeOs::PollUntilSpeechFinishes,
          weak_ptr_factory_.GetWeakPtr(),
          utterance_id),
      base::TimeDelta::FromMilliseconds(kSpeechCheckDelayIntervalMs));
}

void ExtensionTtsPlatformImplChromeOs::AppendSpeakOption(
    std::string key,
    std::string value,
    std::string* options) {
  *options +=
      key +
      chromeos::SpeechSynthesizerClient::kSpeechPropertyEquals +
      value +
      chromeos::SpeechSynthesizerClient::kSpeechPropertyDelimiter;
}

// static
ExtensionTtsPlatformImplChromeOs*
ExtensionTtsPlatformImplChromeOs::GetInstance() {
  return Singleton<ExtensionTtsPlatformImplChromeOs>::get();
}

// global
void EnableChromeOsTts() {
  ExtensionTtsPlatformImplChromeOs::GetInstance()->Enable();
}
<|endoftext|>


<commit_before>//////////////////////////////////////////////////////////////////////////////
// NoLifeNx - Part of the NoLifeStory project                               //
// Copyright © 2013 Peter Atashian                                          //
//                                                                          //
// This program is free software: you can redistribute it and/or modify     //
// it under the terms of the GNU Affero General Public License as           //
// published by the Free Software Foundation, either version 3 of the       //
// License, or (at your option) any later version.                          //
//                                                                          //
// This program is distributed in the hope that it will be useful,          //
// but WITHOUT ANY WARRANTY; without even the implied warranty of           //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            //
// GNU Affero General Public License for more details.                      //
//                                                                          //
// You should have received a copy of the GNU Affero General Public License //
// along with this program.  If not, see <http://www.gnu.org/licenses/>.    //
//////////////////////////////////////////////////////////////////////////////

#include "file_impl.hpp"
#include "node_impl.hpp"
#ifdef _WIN32
#  include <Windows.h>
#else
#  include <sys/types.h>
#  include <sys/stat.h>
#  include <sys/fcntl.h>
#  include <sys/mman.h>
#  include <unistd.h>
#endif
#include <stdexcept>

namespace nl {
    file::file(std::string name) {
        open(name);
    }
    file::~file() {
        close();
    }
    void file::open(std::string name) {
        close();
        m_data = new data();
#ifdef _WIN32
        m_data->file_handle = ::CreateFileA(name.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, nullptr);
        if (m_data->file_handle == INVALID_HANDLE_VALUE)
            throw std::runtime_error("Failed to open file " + name);
        m_data->map = ::CreateFileMappingA(m_data->file_handle, 0, PAGE_READONLY, 0, 0, nullptr);
        if (!m_data->map)
            throw std::runtime_error("Failed to create file mapping of file " + name);
        m_data->base = ::MapViewOfFile(m_data->map, FILE_MAP_READ, 0, 0, 0);
        if (!m_data->base)
            throw std::runtime_error("Failed to map view of file " + name);
#else
        m_data->file_handle = ::open(name.c_str(), O_RDONLY);
        if (m_data->file_handle == -1)
            throw std::runtime_error("Failed to open file " + name);
        struct stat finfo;
        if (::fstat(m_data->file_handle, &finfo) == -1)
            throw std::runtime_error("Failed to obtain file information of file " + name);
        m_data->size = finfo.st_size;
        m_data->base = ::mmap(nullptr, m_data->size, PROT_READ, MAP_SHARED, m_data->file_handle, 0);
        if (reinterpret_cast<intptr_t>(m_data->base) == -1)
            throw std::runtime_error("Failed to create memory mapping of file " + name);
#endif
        m_data->header = reinterpret_cast<header const *>(m_data->base);
        if (m_data->header->magic != 0x34474B50)
            throw std::runtime_error(name + " is not a PKG4 NX file");
        m_data->node_table = reinterpret_cast<node::data const *>(reinterpret_cast<char const *>(m_data->base) + m_data->header->node_offset);
        m_data->string_table = reinterpret_cast<uint64_t const *>(reinterpret_cast<char const *>(m_data->base) + m_data->header->string_offset);
        m_data->bitmap_table = reinterpret_cast<uint64_t const *>(reinterpret_cast<char const *>(m_data->base) + m_data->header->bitmap_offset);
        m_data->audio_table = reinterpret_cast<uint64_t const *>(reinterpret_cast<char const *>(m_data->base) + m_data->header->audio_offset);
    }
    void file::close() {
        if (!m_data) return;
#ifdef _WIN32
        ::UnmapViewOfFile(m_data->base);
        ::CloseHandle(m_data->map);
        ::CloseHandle(m_data->file_handle);
#else
        ::munmap(const_cast<void *>(m_data->base), m_data->size);
        ::close(m_data->file_handle);
#endif
        delete m_data;
        m_data = nullptr;
    }
    node file::root() const {
        return {m_data->node_table, m_data};
    }
    file::operator node() const {
        return root();
    }
    uint32_t file::string_count() const {
        return m_data->header->string_count;
    }
    uint32_t file::bitmap_count() const {
        return m_data->header->bitmap_count;
    }
    uint32_t file::audio_count() const {
        return m_data->header->audio_count;
    }
    uint32_t file::node_count() const {
        return m_data->header->node_count;
    }
    std::string file::get_string(uint32_t i) const {
        auto const s = reinterpret_cast<char const *>(m_data->base) + m_data->string_table[i];
        return {s + 2, *reinterpret_cast<uint16_t const *>(s)};
    }
}
<commit_msg>nx: Use windows.h if building on MinGW<commit_after>//////////////////////////////////////////////////////////////////////////////
// NoLifeNx - Part of the NoLifeStory project                               //
// Copyright © 2013 Peter Atashian                                          //
//                                                                          //
// This program is free software: you can redistribute it and/or modify     //
// it under the terms of the GNU Affero General Public License as           //
// published by the Free Software Foundation, either version 3 of the       //
// License, or (at your option) any later version.                          //
//                                                                          //
// This program is distributed in the hope that it will be useful,          //
// but WITHOUT ANY WARRANTY; without even the implied warranty of           //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            //
// GNU Affero General Public License for more details.                      //
//                                                                          //
// You should have received a copy of the GNU Affero General Public License //
// along with this program.  If not, see <http://www.gnu.org/licenses/>.    //
//////////////////////////////////////////////////////////////////////////////

#include "file_impl.hpp"
#include "node_impl.hpp"
#ifdef _WIN32
#ifdef __MINGW32__
#  include <windows.h>
#else
#  include <Windows.h>
#endif // __MINGW32__
#else
#  include <sys/types.h>
#  include <sys/stat.h>
#  include <sys/fcntl.h>
#  include <sys/mman.h>
#  include <unistd.h>
#endif
#include <stdexcept>

namespace nl {
    file::file(std::string name) {
        open(name);
    }
    file::~file() {
        close();
    }
    void file::open(std::string name) {
        close();
        m_data = new data();
#ifdef _WIN32
        m_data->file_handle = ::CreateFileA(name.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, nullptr);
        if (m_data->file_handle == INVALID_HANDLE_VALUE)
            throw std::runtime_error("Failed to open file " + name);
        m_data->map = ::CreateFileMappingA(m_data->file_handle, 0, PAGE_READONLY, 0, 0, nullptr);
        if (!m_data->map)
            throw std::runtime_error("Failed to create file mapping of file " + name);
        m_data->base = ::MapViewOfFile(m_data->map, FILE_MAP_READ, 0, 0, 0);
        if (!m_data->base)
            throw std::runtime_error("Failed to map view of file " + name);
#else
        m_data->file_handle = ::open(name.c_str(), O_RDONLY);
        if (m_data->file_handle == -1)
            throw std::runtime_error("Failed to open file " + name);
        struct stat finfo;
        if (::fstat(m_data->file_handle, &finfo) == -1)
            throw std::runtime_error("Failed to obtain file information of file " + name);
        m_data->size = finfo.st_size;
        m_data->base = ::mmap(nullptr, m_data->size, PROT_READ, MAP_SHARED, m_data->file_handle, 0);
        if (reinterpret_cast<intptr_t>(m_data->base) == -1)
            throw std::runtime_error("Failed to create memory mapping of file " + name);
#endif
        m_data->header = reinterpret_cast<header const *>(m_data->base);
        if (m_data->header->magic != 0x34474B50)
            throw std::runtime_error(name + " is not a PKG4 NX file");
        m_data->node_table = reinterpret_cast<node::data const *>(reinterpret_cast<char const *>(m_data->base) + m_data->header->node_offset);
        m_data->string_table = reinterpret_cast<uint64_t const *>(reinterpret_cast<char const *>(m_data->base) + m_data->header->string_offset);
        m_data->bitmap_table = reinterpret_cast<uint64_t const *>(reinterpret_cast<char const *>(m_data->base) + m_data->header->bitmap_offset);
        m_data->audio_table = reinterpret_cast<uint64_t const *>(reinterpret_cast<char const *>(m_data->base) + m_data->header->audio_offset);
    }
    void file::close() {
        if (!m_data) return;
#ifdef _WIN32
        ::UnmapViewOfFile(m_data->base);
        ::CloseHandle(m_data->map);
        ::CloseHandle(m_data->file_handle);
#else
        ::munmap(const_cast<void *>(m_data->base), m_data->size);
        ::close(m_data->file_handle);
#endif
        delete m_data;
        m_data = nullptr;
    }
    node file::root() const {
        return {m_data->node_table, m_data};
    }
    file::operator node() const {
        return root();
    }
    uint32_t file::string_count() const {
        return m_data->header->string_count;
    }
    uint32_t file::bitmap_count() const {
        return m_data->header->bitmap_count;
    }
    uint32_t file::audio_count() const {
        return m_data->header->audio_count;
    }
    uint32_t file::node_count() const {
        return m_data->header->node_count;
    }
    std::string file::get_string(uint32_t i) const {
        auto const s = reinterpret_cast<char const *>(m_data->base) + m_data->string_table[i];
        return {s + 2, *reinterpret_cast<uint16_t const *>(s)};
    }
}
<|endoftext|>


<commit_before>#include "test.hpp"
#include "../plane.hpp"
#include "../random/plane.hpp"

namespace frea {
	namespace test {
		template <class T>
		using VectorD_3 = RVector<T>;
		using TypesD_3 = ToTestTypes_t<types::VectorRangeD_t<types::FReg_t, 3>>;
		TYPED_TEST_CASE(VectorD_3, TypesD_3);

		namespace {
			template <class value_t, class MT>
			value_t Scale(MT& mt) {
				const value_t s = mt.template getUniform<int>({0,1}) == 0 ? -1 : 1;
				return s * mt.template getUniform<value_t>({1e-1, 1e1});
			}
			template <class vec_t, class MT>
			auto GenVecPair(MT& mt) {
				using value_t = typename vec_t::value_t;
				// 同じ方向か、正反対のベクトルの組は避ける
				const auto mtf = mt.template getUniformF<value_t>();
				vec_t v0 = random::GenVecUnit<vec_t>(mtf),
					 v1;
				for(;;) {
					v1 = random::GenVecUnit<vec_t>(mtf);
					const auto d = v0.dot(v1);
					if(std::abs(1.0-d) > 1e-5 && std::abs(d) > 1e-5)
						break;
				}
				const auto c = v0.dot(v1);
				// 適当に倍率を掛ける
				v0 *= Scale<value_t>(mt);
				v1 *= Scale<value_t>(mt);

				struct {
					vec_t	v0, v1;
					value_t	cos;
				} ret{v0, v1, c};
				return ret;
			}
		}
		TYPED_TEST(VectorD_3, Cross) {
			using value_t = typename TestFixture::value_t;
			using vec_t = typename TestFixture::vec_t;

			const auto v = GenVecPair<vec_t>(this->mt());
			const auto s = std::sqrt(1 - v.cos*v.cos);
			const auto v2 = v.v0.cross(v.v1);
			// 外積の長さはv0とv1のそれを掛けた値*sinθ
			constexpr auto Th = Threshold<value_t>(0.7, 0);
			EXPECT_NEAR(v.v0.length()*v.v1.length()*s, v2.length(), Th);
			// 生成元のベクトルと内積を取ると0
			EXPECT_NEAR(v.v0.dot(v2), 0, Th);
			EXPECT_NEAR(v.v1.dot(v2), 0, Th);
			// operator % はcross()と同じ
			const auto v3 = v.v0 % v.v1;
			EXPECT_LE(AbsMax(vec_t(v2-v3)), Threshold<value_t>(0.3, 0));
		}
		TYPED_TEST(VectorD_3, VerticalVector) {
			using value_t = typename TestFixture::value_t;
			using vec_t = typename TestFixture::vec_t;
			const auto mtf = this->mt().template getUniformF<value_t>();
			const auto v0 = random::GenVecUnit<vec_t>(mtf);
			const auto v1 = v0.verticalVector();
			// ゼロベクトルではない
			EXPECT_GT(v1.length(), Threshold<value_t>(0.8,0));
			// 元のベクトルとの内積は0
			constexpr auto Th = Threshold<value_t>(0.2, 0);
			EXPECT_LE(std::abs(v0.dot(v1)), Th);
			// 生成したベクトルと外積を取ると、先の2つのベクトルとどちらも内積が0
			const auto v2 = (v1.cross(v0)).normalization();
			EXPECT_LE(std::abs(v2.dot(v0)), Th);
			EXPECT_LE(std::abs(v2.dot(v1)), Th);
		}
		TYPED_TEST(VectorD_3, Flip) {
			using value_t = typename TestFixture::value_t;
			using vec_t = typename TestFixture::vec_t;
			constexpr Range<value_t> range{-1e2, 1e2};
			const auto mtf = this->mt().template getUniformF<value_t>(range);
			const auto p = random::GenPlane<PlaneT<value_t, false>>(mtf);
			const auto v0 = random::GenVec<vec_t>(mtf);

			// 一回反転すればplaneとの内積が符号反転
			const auto v1 = p.flip(v0);
			const auto d0 = p.dot(v0),
						d1 = p.dot(v1);
			constexpr auto Th = Threshold<value_t>(0.6, 0);
			EXPECT_NEAR(d0, -d1, Th);

			// 二回反転すると元に戻る
			const auto v2 = p.flip(v1);
			const auto d2 = p.dot(v2);
			EXPECT_NEAR(d0, d2, Th);

			// 反転前と後を結ぶ方向は平面の法線と同一、または正対する
			const vec_t dir = (v1 - v0).normalization();
			const auto dd = dir.dot(p.getNormal());
			EXPECT_LE(std::abs(dd)-1, Threshold<value_t>(0.8, 0));
		}
	}
}
<commit_msg>plane::crosspoint()　テストコード<commit_after>#include "test.hpp"
#include "../plane.hpp"
#include "../random/plane.hpp"

namespace frea {
	namespace test {
		template <class T>
		using VectorD_3 = RVector<T>;
		using TypesD_3 = ToTestTypes_t<types::VectorRangeD_t<types::FReg_t, 3>>;
		TYPED_TEST_CASE(VectorD_3, TypesD_3);

		namespace {
			template <class value_t, class MT>
			value_t Scale(MT& mt) {
				const value_t s = mt.template getUniform<int>({0,1}) == 0 ? -1 : 1;
				return s * mt.template getUniform<value_t>({1e-1, 1e1});
			}
			template <class vec_t, class MT>
			auto GenVecPair(MT& mt) {
				using value_t = typename vec_t::value_t;
				// 同じ方向か、正反対のベクトルの組は避ける
				const auto mtf = mt.template getUniformF<value_t>();
				vec_t v0 = random::GenVecUnit<vec_t>(mtf),
					 v1;
				for(;;) {
					v1 = random::GenVecUnit<vec_t>(mtf);
					const auto d = v0.dot(v1);
					if(std::abs(1.0-d) > 1e-5 && std::abs(d) > 1e-5)
						break;
				}
				const auto c = v0.dot(v1);
				// 適当に倍率を掛ける
				v0 *= Scale<value_t>(mt);
				v1 *= Scale<value_t>(mt);

				struct {
					vec_t	v0, v1;
					value_t	cos;
				} ret{v0, v1, c};
				return ret;
			}
		}
		TYPED_TEST(VectorD_3, Cross) {
			using value_t = typename TestFixture::value_t;
			using vec_t = typename TestFixture::vec_t;

			const auto v = GenVecPair<vec_t>(this->mt());
			const auto s = std::sqrt(1 - v.cos*v.cos);
			const auto v2 = v.v0.cross(v.v1);
			// 外積の長さはv0とv1のそれを掛けた値*sinθ
			constexpr auto Th = Threshold<value_t>(0.7, 0);
			EXPECT_NEAR(v.v0.length()*v.v1.length()*s, v2.length(), Th);
			// 生成元のベクトルと内積を取ると0
			EXPECT_NEAR(v.v0.dot(v2), 0, Th);
			EXPECT_NEAR(v.v1.dot(v2), 0, Th);
			// operator % はcross()と同じ
			const auto v3 = v.v0 % v.v1;
			EXPECT_LE(AbsMax(vec_t(v2-v3)), Threshold<value_t>(0.3, 0));
		}
		TYPED_TEST(VectorD_3, VerticalVector) {
			using value_t = typename TestFixture::value_t;
			using vec_t = typename TestFixture::vec_t;
			const auto mtf = this->mt().template getUniformF<value_t>();
			const auto v0 = random::GenVecUnit<vec_t>(mtf);
			const auto v1 = v0.verticalVector();
			// ゼロベクトルではない
			EXPECT_GT(v1.length(), Threshold<value_t>(0.8,0));
			// 元のベクトルとの内積は0
			constexpr auto Th = Threshold<value_t>(0.2, 0);
			EXPECT_LE(std::abs(v0.dot(v1)), Th);
			// 生成したベクトルと外積を取ると、先の2つのベクトルとどちらも内積が0
			const auto v2 = (v1.cross(v0)).normalization();
			EXPECT_LE(std::abs(v2.dot(v0)), Th);
			EXPECT_LE(std::abs(v2.dot(v1)), Th);
		}
		TYPED_TEST(VectorD_3, Flip) {
			using value_t = typename TestFixture::value_t;
			using vec_t = typename TestFixture::vec_t;
			constexpr Range<value_t> range{-1e2, 1e2};
			const auto mtf = this->mt().template getUniformF<value_t>(range);
			const auto p = random::GenPlane<PlaneT<value_t, false>>(mtf);
			const auto v0 = random::GenVec<vec_t>(mtf);

			// 一回反転すればplaneとの内積が符号反転
			const auto v1 = p.flip(v0);
			const auto d0 = p.dot(v0),
						d1 = p.dot(v1);
			constexpr auto Th = Threshold<value_t>(0.6, 0);
			EXPECT_NEAR(d0, -d1, Th);

			// 二回反転すると元に戻る
			const auto v2 = p.flip(v1);
			const auto d2 = p.dot(v2);
			EXPECT_NEAR(d0, d2, Th);

			// 反転前と後を結ぶ方向は平面の法線と同一、または正対する
			const vec_t dir = (v1 - v0).normalization();
			const auto dd = dir.dot(p.getNormal());
			EXPECT_LE(std::abs(dd)-1, Threshold<value_t>(0.8, 0));
		}
		TYPED_TEST(VectorD_3, CrossPoint) {
			using value_t = typename TestFixture::value_t;
			using vec_t = typename TestFixture::vec_t;
			constexpr Range<value_t> range{-1e2, 1e2};
			const auto mtf = this->mt().template getUniformF<value_t>(range);
			const auto p = random::GenPlane<PlaneT<value_t, false>>(mtf);
			constexpr auto Th = Threshold<value_t>(0.6, 0);
			// 平面の両方側に位置するランダム頂点を生成
			vec_t vf, vb;
			// 表側に位置する頂点
			do {
				vf = random::GenVec<vec_t>(mtf);
			} while(p.dot(vf) <= Th);
			// 裏側に位置する頂点
			do {
				vb = random::GenVec<vec_t>(mtf);
			} while(p.dot(vb) >= -Th);
			const auto res0 = p.crosspoint(vf, vb);
			// 交差フラグはTrueである
			ASSERT_TRUE(res0.cross);
			// 交点は面上にある
			ASSERT_LE(std::abs(p.dot(res0.point)), Th);
			// 逆順で計算しても結果は同じ
			const auto res1 = p.crosspoint(vb, vf);
			ASSERT_TRUE(res1.cross);
			// 最初の交点と同じ位置
			ASSERT_LE(AbsMax(vec_t(res1.point - res0.point)), Th);
		}
	}
}
<|endoftext|>


<commit_before>//
//  6522.hpp
//  Clock Signal
//
//  Created by Thomas Harte on 06/06/2016.
//  Copyright © 2016 Thomas Harte. All rights reserved.
//

#ifndef _522_hpp
#define _522_hpp

#include <cstdint>
#include <cstdio>

namespace MOS {

class MOS6522Delegate {
	public:
		virtual void mos6522_did_change_interrupt_status(void *mos6522) = 0;
};

template <class T> class MOS6522 {
	private:
		enum InterruptFlag: uint8_t {
			CA2ActiveEdge	= 1 << 0,
			CA1ActiveEdge	= 1 << 1,
			ShiftRegister	= 1 << 2,
			CB2ActiveEdge	= 1 << 3,
			CB1ActiveEdge	= 1 << 4,
			Timer2			= 1 << 5,
			Timer1			= 1 << 6,
		};

	public:
		void set_register(int address, uint8_t value)
		{
			address &= 0xf;
//			printf("6522 %p: %d <- %02x\n", this, address, value);
			switch(address)
			{
				case 0x00:
					_registers.output[1] = value;
				break;
				case 0x01:
					_registers.output[0] = value;
				break;
				case 0xf:
					_registers.output[0] = value;
				break;
				case 0x02:
					_registers.data_direction[1] = value;
				break;
				case 0x03:
					_registers.data_direction[0] = value;
				break;

				// Timer 1
				case 0x06:	case 0x04:	_registers.timer_latch[0] = (_registers.timer_latch[0]&0xff00) | value;	break;
				case 0x05:	case 0x07:
					_registers.timer_latch[0] = (_registers.timer_latch[0]&0x00ff) | (uint16_t)(value << 8);
					_registers.interrupt_flags &= ~InterruptFlag::Timer1;
					if(address == 0x05)
					{
						_registers.timer[0] = _registers.timer_latch[0];
						_timer_is_running[0] = true;
					}
					reevaluate_interrupts();
				break;

				// Timer 2
				case 0x08:	_registers.timer_latch[1] = value;	break;
				case 0x09:
					_registers.interrupt_flags &= ~InterruptFlag::Timer2;
					_registers.timer[1] = _registers.timer_latch[1] | (uint16_t)(value << 8);
					_timer_is_running[1] = true;
					reevaluate_interrupts();
				break;

				// Shift
				case 0xa:	_registers.shift = value;				break;

				// Control
				case 0xb: _registers.auxiliary_control = value;		break;
				case 0xc: _registers.peripheral_control = value;	break;

				// Interrupt control
				case 0xd:
					_registers.interrupt_flags = value;	// TODO: really?
					reevaluate_interrupts();
				break;
				case 0xe:
					if(value&0x80)
						_registers.interrupt_enable |= value;
					else
						_registers.interrupt_enable &= ~value;
					reevaluate_interrupts();
				break;
			}
		}

		uint8_t get_register(int address)
		{
			address &= 0xf;
//			printf("6522 %p: %d\n", this, address);
			switch(address)
			{
				case 0x00:	return _registers.input[1];
				case 0x01:	return _registers.input[0];
				case 0x02:	return _registers.data_direction[1];
				case 0x03:	return _registers.data_direction[0];
				case 0x0f:	return _registers.input[1];

				// Timer 1
				case 0x04:
					_registers.interrupt_flags &= ~InterruptFlag::Timer1;
					reevaluate_interrupts();
				return _registers.timer[0] & 0x00ff;
				case 0x05:	return _registers.timer[0] >> 8;
				case 0x06:	return _registers.timer_latch[0] & 0x00ff;
				case 0x07:	return _registers.timer_latch[0] >> 8;

				// Timer 2
				case 0x08:
					_registers.interrupt_flags &= ~InterruptFlag::Timer2;
					reevaluate_interrupts();
				return _registers.timer[1] & 0x00ff;
				case 0x09:	return _registers.timer[1] >> 8;

				case 0x0a:	return _registers.shift;

				case 0x0b:	return _registers.auxiliary_control;
				case 0x0c:	return _registers.peripheral_control;

				case 0x0d:	return _registers.interrupt_flags | (get_interrupt_line() ? 0x80 : 0x00);
				case 0x0e:	return _registers.interrupt_enable | 0x80;
			}

			return 0xff;
		}

		void run_for_cycles(unsigned int number_of_cycles)
		{
			_registers.timer[0] -= number_of_cycles;
			_registers.timer[1] -= number_of_cycles;

			if(!_registers.timer[1] && _timer_is_running[1])
			{
				_timer_is_running[1] = false;
				_registers.interrupt_flags |= InterruptFlag::Timer2;
				reevaluate_interrupts();
			}

			if(!_registers.timer[0] && _timer_is_running[0])
			{
				_registers.interrupt_flags |= InterruptFlag::Timer1;
				reevaluate_interrupts();

				// TODO: reload shouldn't occur for a further 1.5 cycles
				if(_registers.auxiliary_control&0x40)
				{
					_registers.timer[0] = _registers.timer_latch[0];
				}
				else
					_timer_is_running[0] = false;
			}
			// TODO: lots of other status effects
		}

		bool get_interrupt_line()
		{
			uint8_t interrupt_status = _registers.interrupt_flags & _registers.interrupt_enable & 0x7f;
			return !!interrupt_status;
		}

		void set_delegate(MOS6522Delegate *delegate)
		{
			_delegate = delegate;
		}

		MOS6522() :
			_timer_is_running{false, false},
			_last_posted_interrupt_status(false)
		{}

	private:
		bool _last_posted_interrupt_status;
		inline void reevaluate_interrupts()
		{
			bool new_interrupt_status = get_interrupt_line();
			if(new_interrupt_status != _last_posted_interrupt_status)
			{
				_last_posted_interrupt_status = new_interrupt_status;
				if(_delegate) _delegate->mos6522_did_change_interrupt_status(this);
			}
		}

		MOS6522Delegate *_delegate;

		struct Registers {
			uint8_t output[2], input[2], data_direction[2];
			uint16_t timer[2], timer_latch[2];
			uint8_t shift;
			uint8_t auxiliary_control, peripheral_control;
			uint8_t interrupt_flags, interrupt_enable;

			Registers() :
				data_direction{0, 0},
				interrupt_enable(0) {}
		} _registers;

		bool _timer_is_running[2];
};

}

#endif /* _522_hpp */
<commit_msg>This is how flags writes are supposed to work, it seems.<commit_after>//
//  6522.hpp
//  Clock Signal
//
//  Created by Thomas Harte on 06/06/2016.
//  Copyright © 2016 Thomas Harte. All rights reserved.
//

#ifndef _522_hpp
#define _522_hpp

#include <cstdint>
#include <cstdio>

namespace MOS {

class MOS6522Delegate {
	public:
		virtual void mos6522_did_change_interrupt_status(void *mos6522) = 0;
};

template <class T> class MOS6522 {
	private:
		enum InterruptFlag: uint8_t {
			CA2ActiveEdge	= 1 << 0,
			CA1ActiveEdge	= 1 << 1,
			ShiftRegister	= 1 << 2,
			CB2ActiveEdge	= 1 << 3,
			CB1ActiveEdge	= 1 << 4,
			Timer2			= 1 << 5,
			Timer1			= 1 << 6,
		};

	public:
		void set_register(int address, uint8_t value)
		{
			address &= 0xf;
//			printf("6522 %p: %d <- %02x\n", this, address, value);
			switch(address)
			{
				case 0x00:
					_registers.output[1] = value;
				break;
				case 0x01:
					_registers.output[0] = value;
				break;
				case 0xf:
					_registers.output[0] = value;
				break;
				case 0x02:
					_registers.data_direction[1] = value;
				break;
				case 0x03:
					_registers.data_direction[0] = value;
				break;

				// Timer 1
				case 0x06:	case 0x04:	_registers.timer_latch[0] = (_registers.timer_latch[0]&0xff00) | value;	break;
				case 0x05:	case 0x07:
					_registers.timer_latch[0] = (_registers.timer_latch[0]&0x00ff) | (uint16_t)(value << 8);
					_registers.interrupt_flags &= ~InterruptFlag::Timer1;
					if(address == 0x05)
					{
						_registers.timer[0] = _registers.timer_latch[0];
						_timer_is_running[0] = true;
					}
					reevaluate_interrupts();
				break;

				// Timer 2
				case 0x08:	_registers.timer_latch[1] = value;	break;
				case 0x09:
					_registers.interrupt_flags &= ~InterruptFlag::Timer2;
					_registers.timer[1] = _registers.timer_latch[1] | (uint16_t)(value << 8);
					_timer_is_running[1] = true;
					reevaluate_interrupts();
				break;

				// Shift
				case 0xa:	_registers.shift = value;				break;

				// Control
				case 0xb: _registers.auxiliary_control = value;		break;
				case 0xc: _registers.peripheral_control = value;	break;

				// Interrupt control
				case 0xd:
					_registers.interrupt_flags &= ~value;
					reevaluate_interrupts();
				break;
				case 0xe:
					if(value&0x80)
						_registers.interrupt_enable |= value;
					else
						_registers.interrupt_enable &= ~value;
					reevaluate_interrupts();
				break;
			}
		}

		uint8_t get_register(int address)
		{
			address &= 0xf;
//			printf("6522 %p: %d\n", this, address);
			switch(address)
			{
				case 0x00:	return _registers.input[1];
				case 0x01:	return _registers.input[0];
				case 0x02:	return _registers.data_direction[1];
				case 0x03:	return _registers.data_direction[0];
				case 0x0f:	return _registers.input[1];

				// Timer 1
				case 0x04:
					_registers.interrupt_flags &= ~InterruptFlag::Timer1;
					reevaluate_interrupts();
				return _registers.timer[0] & 0x00ff;
				case 0x05:	return _registers.timer[0] >> 8;
				case 0x06:	return _registers.timer_latch[0] & 0x00ff;
				case 0x07:	return _registers.timer_latch[0] >> 8;

				// Timer 2
				case 0x08:
					_registers.interrupt_flags &= ~InterruptFlag::Timer2;
					reevaluate_interrupts();
				return _registers.timer[1] & 0x00ff;
				case 0x09:	return _registers.timer[1] >> 8;

				case 0x0a:	return _registers.shift;

				case 0x0b:	return _registers.auxiliary_control;
				case 0x0c:	return _registers.peripheral_control;

				case 0x0d:	return _registers.interrupt_flags | (get_interrupt_line() ? 0x80 : 0x00);
				case 0x0e:	return _registers.interrupt_enable | 0x80;
			}

			return 0xff;
		}

		void run_for_cycles(unsigned int number_of_cycles)
		{
			_registers.timer[0] -= number_of_cycles;
			_registers.timer[1] -= number_of_cycles;

			if(!_registers.timer[1] && _timer_is_running[1])
			{
				_timer_is_running[1] = false;
				_registers.interrupt_flags |= InterruptFlag::Timer2;
				reevaluate_interrupts();
			}

			if(!_registers.timer[0] && _timer_is_running[0])
			{
				_registers.interrupt_flags |= InterruptFlag::Timer1;
				reevaluate_interrupts();

				// TODO: reload shouldn't occur for a further 1.5 cycles
				if(_registers.auxiliary_control&0x40)
				{
					_registers.timer[0] = _registers.timer_latch[0];
				}
				else
					_timer_is_running[0] = false;
			}
			// TODO: lots of other status effects
		}

		bool get_interrupt_line()
		{
			uint8_t interrupt_status = _registers.interrupt_flags & _registers.interrupt_enable & 0x7f;
			return !!interrupt_status;
		}

		void set_delegate(MOS6522Delegate *delegate)
		{
			_delegate = delegate;
		}

		MOS6522() :
			_timer_is_running{false, false},
			_last_posted_interrupt_status(false)
		{}

	private:
		bool _last_posted_interrupt_status;
		inline void reevaluate_interrupts()
		{
			bool new_interrupt_status = get_interrupt_line();
			if(new_interrupt_status != _last_posted_interrupt_status)
			{
				_last_posted_interrupt_status = new_interrupt_status;
				if(_delegate) _delegate->mos6522_did_change_interrupt_status(this);
			}
		}

		MOS6522Delegate *_delegate;

		struct Registers {
			uint8_t output[2], input[2], data_direction[2];
			uint16_t timer[2], timer_latch[2];
			uint8_t shift;
			uint8_t auxiliary_control, peripheral_control;
			uint8_t interrupt_flags, interrupt_enable;

			Registers() :
				data_direction{0, 0},
				interrupt_enable(0) {}
		} _registers;

		bool _timer_is_running[2];
};

}

#endif /* _522_hpp */
<|endoftext|>